Kaydet (Commit) f9473a8d authored tarafından Chris Sherlock's avatar Chris Sherlock Kaydeden (comit) Tomaž Vajngerl

vcl: ImplSeparableBlurFilter() -> BitmapGaussianSeparableBlurFilter

Change-Id: I996c9fcb0524e14e0093142be0749f0e5836426b
Reviewed-on: https://gerrit.libreoffice.org/53071Tested-by: 's avatarJenkins <ci@libreoffice.org>
Reviewed-by: 's avatarTomaž Vajngerl <quikee@gmail.com>
üst d4a01820
/* -*- 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: */
......@@ -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:
......
......@@ -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 \
......
/* -*- 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: */
......@@ -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: */
......@@ -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;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment