LibreOffice Module vcl (master) 1
BitmapGaussianSeparableBlurFilter.cxx
Go to the documentation of this file.
1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2/*
3 * This file is part of the LibreOffice project.
4 *
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 *
9 */
10
11#include <tools/helpers.hxx>
12
13#include <vcl/bitmap.hxx>
14#include <vcl/bitmapex.hxx>
16
18
20{
21 Bitmap aBitmap(rBitmapEx.GetBitmap());
22
23 const sal_Int32 nWidth = aBitmap.GetSizePixel().Width();
24 const sal_Int32 nHeight = aBitmap.GetSizePixel().Height();
25
26 // Prepare Blur Vector
27 int aNumberOfContributions;
28 std::vector<double> aBlurVector(makeBlurKernel(mfRadius, aNumberOfContributions));
29 std::vector<double> aWeights;
30 std::vector<int> aPixels;
31 std::vector<int> aCounts;
32
33 // Do horizontal filtering
34 blurContributions(nWidth, aNumberOfContributions, aBlurVector, aWeights, aPixels, aCounts);
35
36 Bitmap::ScopedReadAccess pReadAcc(aBitmap);
37
38 // switch coordinates as convolution pass transposes result
39 Bitmap aNewBitmap(Size(nHeight, nWidth), vcl::PixelFormat::N24_BPP);
40
41 bool bResult = convolutionPass(aBitmap, aNewBitmap, pReadAcc.get(), aNumberOfContributions,
42 aWeights.data(), aPixels.data(), aCounts.data());
43
44 // Cleanup
45 pReadAcc.reset();
46 aWeights.clear();
47 aPixels.clear();
48 aCounts.clear();
49
50 if (!bResult)
51 {
52 aBlurVector.clear();
53 }
54 else
55 {
56 // Swap current bitmap with new bitmap
57 aBitmap.ReassignWithSize(aNewBitmap);
58
59 // Do vertical filtering
60 blurContributions(nHeight, aNumberOfContributions, aBlurVector, aWeights, aPixels, aCounts);
61
62 pReadAcc = Bitmap::ScopedReadAccess(aBitmap);
63 aNewBitmap = Bitmap(Size(nWidth, nHeight), vcl::PixelFormat::N24_BPP);
64 bResult = convolutionPass(aBitmap, aNewBitmap, pReadAcc.get(), aNumberOfContributions,
65 aWeights.data(), aPixels.data(), aCounts.data());
66
67 // Cleanup
68 pReadAcc.reset();
69 aWeights.clear();
70 aCounts.clear();
71 aPixels.clear();
72 aBlurVector.clear();
73
74 if (bResult)
75 aBitmap.ReassignWithSize(aNewBitmap); // swap current bitmap with new bitmap
76 }
77
78 if (bResult)
79 return BitmapEx(aBitmap);
80
81 return BitmapEx();
82}
83
85 BitmapReadAccess const* pReadAcc,
86 int aNumberOfContributions,
87 const double* pWeights, int const* pPixels,
88 const int* pCount)
89{
90 if (!pReadAcc)
91 return false;
92
93 BitmapScopedWriteAccess pWriteAcc(aNewBitmap);
94 if (!pWriteAcc)
95 return false;
96
97 const int nHeight = rBitmap.GetSizePixel().Height();
98 assert(rBitmap.GetSizePixel().Height() == aNewBitmap.GetSizePixel().Width());
99 const int nWidth = rBitmap.GetSizePixel().Width();
100 assert(rBitmap.GetSizePixel().Width() == aNewBitmap.GetSizePixel().Height());
101
102 BitmapColor aColor;
103 double aValueRed, aValueGreen, aValueBlue;
104 double aSum, aWeight;
105 int aBaseIndex, aIndex;
106
107 for (int nSourceY = 0; nSourceY < nHeight; ++nSourceY)
108 {
109 for (int nSourceX = 0; nSourceX < nWidth; ++nSourceX)
110 {
111 aBaseIndex = nSourceX * aNumberOfContributions;
112 aSum = aValueRed = aValueGreen = aValueBlue = 0.0;
113
114 for (int j = 0; j < pCount[nSourceX]; ++j)
115 {
116 aIndex = aBaseIndex + j;
117 aWeight = pWeights[aIndex];
118 aSum += aWeight;
119
120 aColor = pReadAcc->GetColor(nSourceY, pPixels[aIndex]);
121
122 aValueRed += aWeight * aColor.GetRed();
123 aValueGreen += aWeight * aColor.GetGreen();
124 aValueBlue += aWeight * aColor.GetBlue();
125 }
126
127 BitmapColor aResultColor(static_cast<sal_uInt8>(MinMax(aValueRed / aSum, 0, 255)),
128 static_cast<sal_uInt8>(MinMax(aValueGreen / aSum, 0, 255)),
129 static_cast<sal_uInt8>(MinMax(aValueBlue / aSum, 0, 255)));
130
131 int nDestX = nSourceY;
132 int nDestY = nSourceX;
133
134 pWriteAcc->SetPixel(nDestY, nDestX, aResultColor);
135 }
136 }
137 return true;
138}
139
140std::vector<double> BitmapGaussianSeparableBlurFilter::makeBlurKernel(const double radius,
141 int& rows)
142{
143 int intRadius = static_cast<int>(radius + 1.0);
144 rows = intRadius * 2 + 1;
145 std::vector<double> matrix(rows);
146
147 double sigma = radius / 3;
148 double radius2 = radius * radius;
149 int index = 0;
150 for (int row = -intRadius; row <= intRadius; row++)
151 {
152 double distance = row * row;
153 if (distance > radius2)
154 {
155 matrix[index] = 0.0;
156 }
157 else
158 {
159 matrix[index] = exp(-distance / (2.0 * sigma * sigma)) / sqrt(2.0 * M_PI * sigma);
160 }
161 index++;
162 }
163 return matrix;
164}
165
167 const int aSize, const int aNumberOfContributions, const std::vector<double>& rBlurVector,
168 std::vector<double>& rWeights, std::vector<int>& rPixels, std::vector<int>& rCounts)
169{
170 rWeights.resize(aSize * aNumberOfContributions);
171 rPixels.resize(aSize * aNumberOfContributions);
172 rCounts.resize(aSize);
173
174 int aLeft, aRight, aCurrentCount, aPixelIndex;
175 double aWeight;
176
177 for (int i = 0; i < aSize; i++)
178 {
179 aLeft = i - aNumberOfContributions / 2;
180 aRight = i + aNumberOfContributions / 2;
181 aCurrentCount = 0;
182 for (int j = aLeft; j <= aRight; j++)
183 {
184 aWeight = rBlurVector[aCurrentCount];
185
186 // Mirror edges
187 if (j < 0)
188 {
189 aPixelIndex = -j;
190 }
191 else if (j >= aSize)
192 {
193 aPixelIndex = (aSize - j) + aSize - 1;
194 }
195 else
196 {
197 aPixelIndex = j;
198 }
199
200 // Edge case for small bitmaps
201 if (aPixelIndex < 0 || aPixelIndex >= aSize)
202 {
203 aWeight = 0.0;
204 }
205
206 rWeights[i * aNumberOfContributions + aCurrentCount] = aWeight;
207 rPixels[i * aNumberOfContributions + aCurrentCount] = aPixelIndex;
208
209 aCurrentCount++;
210 }
211 rCounts[i] = aCurrentCount;
212 }
213}
214
215/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
Bitmap GetBitmap(Color aTransparentReplaceColor) const
Definition: BitmapEx.cxx:203
static bool convolutionPass(const Bitmap &rBitmap, Bitmap &aNewBitmap, BitmapReadAccess const *pReadAcc, int aNumberOfContributions, const double *pWeights, int const *pPixels, const int *pCount)
virtual BitmapEx execute(BitmapEx const &rBitmapEx) const override
Separable Gaussian Blur filter and accepts a blur radius as a parameter so the user can change the st...
static std::vector< double > makeBlurKernel(const double radius, int &rows)
static void blurContributions(const int aSize, const int aNumberOfContributions, const std::vector< double > &rBlurVector, std::vector< double > &rWeights, std::vector< int > &rPixels, std::vector< int > &rCounts)
BitmapColor GetColor(tools::Long nY, tools::Long nX) const
SAL_DLLPRIVATE void ReassignWithSize(const Bitmap &rBitmap)
ReassignWithSize and recalculate bitmap.
Size GetSizePixel() const
vcl::ScopedBitmapAccess< BitmapReadAccess, Bitmap, &Bitmap::AcquireReadAccess > ScopedReadAccess
sal_uInt8 GetBlue() const
sal_uInt8 GetRed() const
sal_uInt8 GetGreen() const
constexpr tools::Long Height() const
constexpr tools::Long Width() const
std::deque< AttacherIndex_Impl > aIndex
std::enable_if< std::is_signed< T >::value||std::is_floating_point< T >::value, long >::type MinMax(T nVal, tools::Long nMin, tools::Long nMax)
double distance
int i
index
unsigned char sal_uInt8