include/vcl/BitmapGaussianSeparableBlurFilter.hxx | 50 +++ include/vcl/bitmap.hxx | 16 - vcl/Library_vcl.mk | 1 vcl/source/bitmap/BitmapGaussianSeparableBlurFilter.cxx | 217 ++++++++++++++++ vcl/source/gdi/bitmap3.cxx | 52 --- vcl/source/gdi/bitmap4.cxx | 154 ----------- 6 files changed, 280 insertions(+), 210 deletions(-)
New commits: commit f9473a8d2ea6740d325ac35da74fec16476820f0 Author: Chris Sherlock <chris.sherloc...@gmail.com> Date: Wed Apr 18 07:51:50 2018 +1000 vcl: ImplSeparableBlurFilter() -> BitmapGaussianSeparableBlurFilter Change-Id: I996c9fcb0524e14e0093142be0749f0e5836426b Reviewed-on: https://gerrit.libreoffice.org/53071 Tested-by: Jenkins <c...@libreoffice.org> Reviewed-by: Tomaž Vajngerl <qui...@gmail.com> diff --git a/include/vcl/BitmapGaussianSeparableBlurFilter.hxx b/include/vcl/BitmapGaussianSeparableBlurFilter.hxx new file mode 100644 index 000000000000..6e6a3a09e6f0 --- /dev/null +++ b/include/vcl/BitmapGaussianSeparableBlurFilter.hxx @@ -0,0 +1,50 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + */ + +#ifndef INCLUDED_VCL_BITMAPGAUSSIANSEPARABLEBLURFILTER_HXX +#define INCLUDED_VCL_BITMAPGAUSSIANSEPARABLEBLURFILTER_HXX + +#include <vcl/BitmapFilter.hxx> + +class BitmapEx; + +class VCL_DLLPUBLIC BitmapGaussianSeparableBlurFilter : public BitmapFilter +{ +public: + BitmapGaussianSeparableBlurFilter(double fRadius) + : mfRadius(fRadius) + { + } + + /** Separable Gaussian Blur filter and accepts a blur radius + as a parameter so the user can change the strength of the blur. + Radius of 1.0 is 3 * standard deviation of gauss function. + + Separable Blur implementation uses 2x separable 1D convolution + to process the image. + */ + virtual BitmapEx execute(BitmapEx const& rBitmapEx) override; + +private: + double mfRadius; + + bool convolutionPass(Bitmap& rBitmap, Bitmap& aNewBitmap, BitmapReadAccess const* pReadAcc, + int aNumberOfContributions, const double* pWeights, int const* pPixels, + const int* pCount); + + static double* makeBlurKernel(const double radius, int& rows); + static void blurContributions(const int aSize, const int aNumberOfContributions, + const double* pBlurVector, double*& pWeights, int*& pPixels, + int*& pCount); +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/include/vcl/bitmap.hxx b/include/vcl/bitmap.hxx index cc50a6dc9bcc..6370a63d116f 100644 --- a/include/vcl/bitmap.hxx +++ b/include/vcl/bitmap.hxx @@ -676,14 +676,6 @@ public: SAL_DLLPRIVATE bool ImplScaleFast( const double& rScaleX, const double& rScaleY ); SAL_DLLPRIVATE bool ImplScaleInterpolate( const double& rScaleX, const double& rScaleY ); - SAL_DLLPRIVATE bool ImplConvolutionPass( - Bitmap& aNewBitmap, - BitmapReadAccess const * pReadAcc, - int aNumberOfContributions, - const double* pWeights, - int const * pPixels, - const int* pCount ); - SAL_DLLPRIVATE bool ImplMakeGreyscales( sal_uInt16 nGreyscales ); SAL_DLLPRIVATE bool ImplDitherMatrix(); SAL_DLLPRIVATE bool ImplDitherFloyd(); @@ -707,16 +699,8 @@ public: SAL_DLLPRIVATE bool ImplMosaic( const BmpFilterParam* pFilterParam ); SAL_DLLPRIVATE bool ImplPopArt(); - SAL_DLLPRIVATE bool ImplSeparableBlurFilter( const double aRadius ); SAL_DLLPRIVATE bool ImplSeparableUnsharpenFilter( const double aRadius ); SAL_DLLPRIVATE bool ImplDuotoneFilter( const sal_uLong nColorOne, sal_uLong nColorTwo ); - SAL_DLLPRIVATE static void ImplBlurContributions( - const int aSize, - const int aNumberOfContributions, - const double* pBlurVector, - double*& pWeights, - int*& pPixels, - int*& pCount ); public: diff --git a/vcl/Library_vcl.mk b/vcl/Library_vcl.mk index 128541e5d0fd..03c705aa5544 100644 --- a/vcl/Library_vcl.mk +++ b/vcl/Library_vcl.mk @@ -317,6 +317,7 @@ $(eval $(call gb_Library_add_exception_objects,vcl,\ vcl/source/bitmap/BitmapDisabledImageFilter \ vcl/source/bitmap/BitmapColorizeFilter \ vcl/source/bitmap/bitmappaint \ + vcl/source/bitmap/BitmapGaussianSeparableBlurFilter \ vcl/source/bitmap/BitmapFastScaleFilter \ vcl/source/bitmap/BitmapScaleSuperFilter \ vcl/source/bitmap/BitmapScaleConvolutionFilter \ diff --git a/vcl/source/bitmap/BitmapGaussianSeparableBlurFilter.cxx b/vcl/source/bitmap/BitmapGaussianSeparableBlurFilter.cxx new file mode 100644 index 000000000000..b28ce99e8d19 --- /dev/null +++ b/vcl/source/bitmap/BitmapGaussianSeparableBlurFilter.cxx @@ -0,0 +1,217 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + */ + +#include <basegfx/color/bcolortools.hxx> + +#include <vcl/bitmap.hxx> +#include <vcl/bitmapex.hxx> +#include <vcl/bitmapaccess.hxx> +#include <vcl/BitmapGaussianSeparableBlurFilter.hxx> + +#include <bitmapwriteaccess.hxx> + +BitmapEx BitmapGaussianSeparableBlurFilter::execute(BitmapEx const& rBitmapEx) +{ + Bitmap aBitmap(rBitmapEx.GetBitmap()); + + const long nWidth = aBitmap.GetSizePixel().Width(); + const long nHeight = aBitmap.GetSizePixel().Height(); + + // Prepare Blur Vector + int aNumberOfContributions; + double* pBlurVector = makeBlurKernel(mfRadius, aNumberOfContributions); + + double* pWeights; + int* pPixels; + int* pCount; + + // Do horizontal filtering + blurContributions(nWidth, aNumberOfContributions, pBlurVector, pWeights, pPixels, pCount); + + Bitmap::ScopedReadAccess pReadAcc(aBitmap); + + // switch coordinates as convolution pass transposes result + Bitmap aNewBitmap(Size(nHeight, nWidth), 24); + + bool bResult = convolutionPass(aBitmap, aNewBitmap, pReadAcc.get(), aNumberOfContributions, + pWeights, pPixels, pCount); + + // Cleanup + pReadAcc.reset(); + delete[] pWeights; + delete[] pPixels; + delete[] pCount; + + if (!bResult) + { + delete[] pBlurVector; + } + else + { + // Swap current bitmap with new bitmap + aBitmap.ReassignWithSize(aNewBitmap); + + // Do vertical filtering + blurContributions(nHeight, aNumberOfContributions, pBlurVector, pWeights, pPixels, pCount); + + pReadAcc = Bitmap::ScopedReadAccess(aBitmap); + aNewBitmap = Bitmap(Size(nWidth, nHeight), 24); + bResult = convolutionPass(aBitmap, aNewBitmap, pReadAcc.get(), aNumberOfContributions, + pWeights, pPixels, pCount); + + // Cleanup + pReadAcc.reset(); + delete[] pWeights; + delete[] pCount; + delete[] pPixels; + delete[] pBlurVector; + + if (bResult) + aBitmap.ReassignWithSize(aNewBitmap); // swap current bitmap with new bitmap + } + + if (bResult) + return BitmapEx(aBitmap); + + return BitmapEx(); +} + +bool BitmapGaussianSeparableBlurFilter::convolutionPass(Bitmap& rBitmap, Bitmap& aNewBitmap, + BitmapReadAccess const* pReadAcc, + int aNumberOfContributions, + const double* pWeights, int const* pPixels, + const int* pCount) +{ + if (!pReadAcc) + return false; + + BitmapScopedWriteAccess pWriteAcc(aNewBitmap); + if (!pWriteAcc) + return false; + + const int nHeight = rBitmap.GetSizePixel().Height(); + assert(rBitmap.GetSizePixel().Height() == aNewBitmap.GetSizePixel().Width()); + const int nWidth = rBitmap.GetSizePixel().Width(); + assert(rBitmap.GetSizePixel().Width() == aNewBitmap.GetSizePixel().Height()); + + BitmapColor aColor; + double aValueRed, aValueGreen, aValueBlue; + double aSum, aWeight; + int aBaseIndex, aIndex; + + for (int nSourceY = 0; nSourceY < nHeight; ++nSourceY) + { + for (int nSourceX = 0; nSourceX < nWidth; ++nSourceX) + { + aBaseIndex = nSourceX * aNumberOfContributions; + aSum = aValueRed = aValueGreen = aValueBlue = 0.0; + + for (int j = 0; j < pCount[nSourceX]; ++j) + { + aIndex = aBaseIndex + j; + aSum += aWeight = pWeights[aIndex]; + + aColor = pReadAcc->GetColor(nSourceY, pPixels[aIndex]); + + aValueRed += aWeight * aColor.GetRed(); + aValueGreen += aWeight * aColor.GetGreen(); + aValueBlue += aWeight * aColor.GetBlue(); + } + + BitmapColor aResultColor(static_cast<sal_uInt8>(MinMax(aValueRed / aSum, 0, 255)), + static_cast<sal_uInt8>(MinMax(aValueGreen / aSum, 0, 255)), + static_cast<sal_uInt8>(MinMax(aValueBlue / aSum, 0, 255))); + + int nDestX = nSourceY; + int nDestY = nSourceX; + + pWriteAcc->SetPixel(nDestY, nDestX, aResultColor); + } + } + return true; +} + +double* BitmapGaussianSeparableBlurFilter::makeBlurKernel(const double radius, int& rows) +{ + int intRadius = static_cast<int>(radius + 1.0); + rows = intRadius * 2 + 1; + double* matrix = new double[rows]; + + double sigma = radius / 3; + double radius2 = radius * radius; + int index = 0; + for (int row = -intRadius; row <= intRadius; row++) + { + double distance = row * row; + if (distance > radius2) + { + matrix[index] = 0.0; + } + else + { + matrix[index] = exp(-distance / (2.0 * sigma * sigma)) / sqrt(2.0 * M_PI * sigma); + } + index++; + } + return matrix; +} + +void BitmapGaussianSeparableBlurFilter::blurContributions(const int aSize, + const int aNumberOfContributions, + const double* pBlurVector, + double*& pWeights, int*& pPixels, + int*& pCount) +{ + pWeights = new double[aSize * aNumberOfContributions]; + pPixels = new int[aSize * aNumberOfContributions]; + pCount = new int[aSize]; + + int aLeft, aRight, aCurrentCount, aPixelIndex; + double aWeight; + + for (int i = 0; i < aSize; i++) + { + aLeft = i - aNumberOfContributions / 2; + aRight = i + aNumberOfContributions / 2; + aCurrentCount = 0; + for (int j = aLeft; j <= aRight; j++) + { + aWeight = pBlurVector[aCurrentCount]; + + // Mirror edges + if (j < 0) + { + aPixelIndex = -j; + } + else if (j >= aSize) + { + aPixelIndex = (aSize - j) + aSize - 1; + } + else + { + aPixelIndex = j; + } + + // Edge case for small bitmaps + if (aPixelIndex < 0 || aPixelIndex >= aSize) + { + aWeight = 0.0; + } + + pWeights[i * aNumberOfContributions + aCurrentCount] = aWeight; + pPixels[i * aNumberOfContributions + aCurrentCount] = aPixelIndex; + + aCurrentCount++; + } + pCount[i] = aCurrentCount; + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/source/gdi/bitmap3.cxx b/vcl/source/gdi/bitmap3.cxx index b553130c6538..3bf068c3444a 100644 --- a/vcl/source/gdi/bitmap3.cxx +++ b/vcl/source/gdi/bitmap3.cxx @@ -1854,56 +1854,4 @@ bool Bitmap::Adjust( short nLuminancePercent, short nContrastPercent, return bRet; } -bool Bitmap::ImplConvolutionPass(Bitmap& aNewBitmap, BitmapReadAccess const * pReadAcc, int aNumberOfContributions, const double* pWeights, int const * pPixels, const int* pCount) -{ - if (!pReadAcc) - return false; - - BitmapScopedWriteAccess pWriteAcc(aNewBitmap); - if (!pWriteAcc) - return false; - - const int nHeight = GetSizePixel().Height(); - assert(GetSizePixel().Height() == aNewBitmap.GetSizePixel().Width()); - const int nWidth = GetSizePixel().Width(); - assert(GetSizePixel().Width() == aNewBitmap.GetSizePixel().Height()); - - BitmapColor aColor; - double aValueRed, aValueGreen, aValueBlue; - double aSum, aWeight; - int aBaseIndex, aIndex; - - for (int nSourceY = 0; nSourceY < nHeight; ++nSourceY) - { - for (int nSourceX = 0; nSourceX < nWidth; ++nSourceX) - { - aBaseIndex = nSourceX * aNumberOfContributions; - aSum = aValueRed = aValueGreen = aValueBlue = 0.0; - - for (int j = 0; j < pCount[nSourceX]; ++j) - { - aIndex = aBaseIndex + j; - aSum += aWeight = pWeights[ aIndex ]; - - aColor = pReadAcc->GetColor(nSourceY, pPixels[aIndex]); - - aValueRed += aWeight * aColor.GetRed(); - aValueGreen += aWeight * aColor.GetGreen(); - aValueBlue += aWeight * aColor.GetBlue(); - } - - BitmapColor aResultColor( - static_cast<sal_uInt8>(MinMax( aValueRed / aSum, 0, 255 )), - static_cast<sal_uInt8>(MinMax( aValueGreen / aSum, 0, 255 )), - static_cast<sal_uInt8>(MinMax( aValueBlue / aSum, 0, 255 )) ); - - int nDestX = nSourceY; - int nDestY = nSourceX; - - pWriteAcc->SetPixel(nDestY, nDestX, aResultColor); - } - } - return true; -} - /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/source/gdi/bitmap4.cxx b/vcl/source/gdi/bitmap4.cxx index 71fd484170ae..e48a2edae9b3 100644 --- a/vcl/source/gdi/bitmap4.cxx +++ b/vcl/source/gdi/bitmap4.cxx @@ -17,13 +17,16 @@ * the License at http://www.apache.org/licenses/LICENSE-2.0 . */ -#include <memory> -#include <stdlib.h> #include <osl/diagnose.h> #include <vcl/bitmapaccess.hxx> #include <vcl/bitmap.hxx> +#include <vcl/BitmapGaussianSeparableBlurFilter.hxx> + #include <bitmapwriteaccess.hxx> +#include <memory> +#include <stdlib.h> + #define S2(a,b) { long t; if( ( t = b - a ) < 0 ) { a += t; b -= t; } } #define MN3(a,b,c) S2(a,b); S2(a,c); #define MX3(a,b,c) S2(b,c); S2(a,c); @@ -51,7 +54,9 @@ bool Bitmap::Filter( BmpFilter eFilter, const BmpFilterParam* pFilterParam ) // Blur for positive values of mnRadius if (pFilterParam->mnRadius > 0.0) { - bRet = ImplSeparableBlurFilter(pFilterParam->mnRadius); + BitmapEx aBmpEx(*this); + bRet = BitmapFilter::Filter(aBmpEx, BitmapGaussianSeparableBlurFilter(pFilterParam->mnRadius)); + *this = aBmpEx.GetBitmap(); } // Unsharpen Mask for negative values of mnRadius else if (pFilterParam->mnRadius < 0.0) @@ -1062,144 +1067,6 @@ bool Bitmap::ImplPopArt() return bRet; } -double* MakeBlurKernel(const double radius, int& rows) { - int intRadius = static_cast<int>(radius + 1.0); - rows = intRadius * 2 + 1; - double* matrix = new double[rows]; - - double sigma = radius / 3; - double radius2 = radius * radius; - int index = 0; - for (int row = -intRadius; row <= intRadius; row++) - { - double distance = row*row; - if (distance > radius2) { - matrix[index] = 0.0; - }else { - matrix[index] = exp( -distance / (2.0 * sigma * sigma) ) / sqrt( 2.0 * M_PI * sigma ); - } - index++; - } - return matrix; -} - -void Bitmap::ImplBlurContributions( const int aSize, const int aNumberOfContributions, - const double* pBlurVector, double*& pWeights, int*& pPixels, int*& pCount ) -{ - pWeights = new double[ aSize*aNumberOfContributions ]; - pPixels = new int[ aSize*aNumberOfContributions ]; - pCount = new int[ aSize ]; - - int aLeft, aRight, aCurrentCount, aPixelIndex; - double aWeight; - - for ( int i = 0; i < aSize; i++ ) - { - aLeft = i - aNumberOfContributions / 2; - aRight = i + aNumberOfContributions / 2; - aCurrentCount = 0; - for ( int j = aLeft; j <= aRight; j++ ) - { - aWeight = pBlurVector[aCurrentCount]; - - // Mirror edges - if (j < 0) - { - aPixelIndex = -j; - } - else if ( j >= aSize ) - { - aPixelIndex = (aSize - j) + aSize - 1; - } - else - { - aPixelIndex = j; - } - - // Edge case for small bitmaps - if ( aPixelIndex < 0 || aPixelIndex >= aSize ) - { - aWeight = 0.0; - } - - pWeights[ i*aNumberOfContributions + aCurrentCount ] = aWeight; - pPixels[ i*aNumberOfContributions + aCurrentCount ] = aPixelIndex; - - aCurrentCount++; - } - pCount[ i ] = aCurrentCount; - } -} - -// Separable Gaussian Blur - -// Separable Gaussian Blur filter and accepts a blur radius -// as a parameter so the user can change the strength of the blur. -// Radius of 1.0 is 3 * standard deviation of gauss function. - -// Separable Blur implementation uses 2x separable 1D convolution -// to process the image. -bool Bitmap::ImplSeparableBlurFilter(const double radius) -{ - const long nWidth = GetSizePixel().Width(); - const long nHeight = GetSizePixel().Height(); - - // Prepare Blur Vector - int aNumberOfContributions; - double* pBlurVector = MakeBlurKernel(radius, aNumberOfContributions); - - double* pWeights; - int* pPixels; - int* pCount; - - // Do horizontal filtering - ImplBlurContributions( nWidth, aNumberOfContributions, pBlurVector, pWeights, pPixels, pCount); - - ScopedReadAccess pReadAcc(*this); - - // switch coordinates as convolution pass transposes result - Bitmap aNewBitmap( Size( nHeight, nWidth ), 24 ); - - bool bResult = ImplConvolutionPass( aNewBitmap, pReadAcc.get(), aNumberOfContributions, pWeights, pPixels, pCount ); - - // Cleanup - pReadAcc.reset(); - delete[] pWeights; - delete[] pPixels; - delete[] pCount; - - if ( !bResult ) - { - delete[] pBlurVector; - return bResult; - } - - // Swap current bitmap with new bitmap - ReassignWithSize(aNewBitmap); - - // Do vertical filtering - ImplBlurContributions(nHeight, aNumberOfContributions, pBlurVector, pWeights, pPixels, pCount ); - - pReadAcc = ScopedReadAccess(*this); - aNewBitmap = Bitmap( Size( nWidth, nHeight ), 24 ); - bResult = ImplConvolutionPass( aNewBitmap, pReadAcc.get(), aNumberOfContributions, pWeights, pPixels, pCount ); - - // Cleanup - pReadAcc.reset(); - delete[] pWeights; - delete[] pCount; - delete[] pPixels; - delete[] pBlurVector; - - if ( !bResult ) - return bResult; - - // Swap current bitmap with new bitmap - ReassignWithSize(aNewBitmap); - - return true; -} - // Separable Unsharpen Mask filter is actually a subtracted blurred // image from the original image. bool Bitmap::ImplSeparableUnsharpenFilter(const double radius) { @@ -1207,7 +1074,10 @@ bool Bitmap::ImplSeparableUnsharpenFilter(const double radius) { const long nHeight = GetSizePixel().Height(); Bitmap aBlur( *this ); - aBlur.ImplSeparableBlurFilter(-radius); + BitmapEx aBlurEx(aBlur); + + BitmapFilter::Filter(aBlurEx, BitmapGaussianSeparableBlurFilter(-radius)); + aBlur = aBlurEx.GetBitmap(); // Amount of unsharpening effect on image - currently set to a fixed value double aAmount = 2.0; _______________________________________________ Libreoffice-commits mailing list libreoffice-comm...@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/libreoffice-commits