bump product version to 7.2.5.1
[LibreOffice.git] / vcl / source / bitmap / BitmapGaussianSeparableBlurFilter.cxx
blob93c3399be493269963ada60d4cf33ffe65fe8287
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
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/.
9 */
11 #include <tools/helpers.hxx>
13 #include <vcl/bitmap.hxx>
14 #include <vcl/bitmapex.hxx>
15 #include <vcl/BitmapGaussianSeparableBlurFilter.hxx>
17 #include <bitmap/BitmapWriteAccess.hxx>
19 BitmapEx BitmapGaussianSeparableBlurFilter::execute(BitmapEx const& rBitmapEx) const
21 Bitmap aBitmap(rBitmapEx.GetBitmap());
23 const tools::Long nWidth = aBitmap.GetSizePixel().Width();
24 const tools::Long nHeight = aBitmap.GetSizePixel().Height();
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;
33 // Do horizontal filtering
34 blurContributions(nWidth, aNumberOfContributions, aBlurVector, aWeights, aPixels, aCounts);
36 Bitmap::ScopedReadAccess pReadAcc(aBitmap);
38 // switch coordinates as convolution pass transposes result
39 Bitmap aNewBitmap(Size(nHeight, nWidth), vcl::PixelFormat::N24_BPP);
41 bool bResult = convolutionPass(aBitmap, aNewBitmap, pReadAcc.get(), aNumberOfContributions,
42 aWeights.data(), aPixels.data(), aCounts.data());
44 // Cleanup
45 pReadAcc.reset();
46 aWeights.clear();
47 aPixels.clear();
48 aCounts.clear();
50 if (!bResult)
52 aBlurVector.clear();
54 else
56 // Swap current bitmap with new bitmap
57 aBitmap.ReassignWithSize(aNewBitmap);
59 // Do vertical filtering
60 blurContributions(nHeight, aNumberOfContributions, aBlurVector, aWeights, aPixels, aCounts);
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());
67 // Cleanup
68 pReadAcc.reset();
69 aWeights.clear();
70 aCounts.clear();
71 aPixels.clear();
72 aBlurVector.clear();
74 if (bResult)
75 aBitmap.ReassignWithSize(aNewBitmap); // swap current bitmap with new bitmap
78 if (bResult)
79 return BitmapEx(aBitmap);
81 return BitmapEx();
84 bool BitmapGaussianSeparableBlurFilter::convolutionPass(const Bitmap& rBitmap, Bitmap& aNewBitmap,
85 BitmapReadAccess const* pReadAcc,
86 int aNumberOfContributions,
87 const double* pWeights, int const* pPixels,
88 const int* pCount)
90 if (!pReadAcc)
91 return false;
93 BitmapScopedWriteAccess pWriteAcc(aNewBitmap);
94 if (!pWriteAcc)
95 return false;
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());
102 BitmapColor aColor;
103 double aValueRed, aValueGreen, aValueBlue;
104 double aSum, aWeight;
105 int aBaseIndex, aIndex;
107 for (int nSourceY = 0; nSourceY < nHeight; ++nSourceY)
109 for (int nSourceX = 0; nSourceX < nWidth; ++nSourceX)
111 aBaseIndex = nSourceX * aNumberOfContributions;
112 aSum = aValueRed = aValueGreen = aValueBlue = 0.0;
114 for (int j = 0; j < pCount[nSourceX]; ++j)
116 aIndex = aBaseIndex + j;
117 aWeight = pWeights[aIndex];
118 aSum += aWeight;
120 aColor = pReadAcc->GetColor(nSourceY, pPixels[aIndex]);
122 aValueRed += aWeight * aColor.GetRed();
123 aValueGreen += aWeight * aColor.GetGreen();
124 aValueBlue += aWeight * aColor.GetBlue();
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)));
131 int nDestX = nSourceY;
132 int nDestY = nSourceX;
134 pWriteAcc->SetPixel(nDestY, nDestX, aResultColor);
137 return true;
140 std::vector<double> BitmapGaussianSeparableBlurFilter::makeBlurKernel(const double radius,
141 int& rows)
143 int intRadius = static_cast<int>(radius + 1.0);
144 rows = intRadius * 2 + 1;
145 std::vector<double> matrix(rows);
147 double sigma = radius / 3;
148 double radius2 = radius * radius;
149 int index = 0;
150 for (int row = -intRadius; row <= intRadius; row++)
152 double distance = row * row;
153 if (distance > radius2)
155 matrix[index] = 0.0;
157 else
159 matrix[index] = exp(-distance / (2.0 * sigma * sigma)) / sqrt(2.0 * M_PI * sigma);
161 index++;
163 return matrix;
166 void BitmapGaussianSeparableBlurFilter::blurContributions(
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)
170 rWeights.resize(aSize * aNumberOfContributions);
171 rPixels.resize(aSize * aNumberOfContributions);
172 rCounts.resize(aSize);
174 int aLeft, aRight, aCurrentCount, aPixelIndex;
175 double aWeight;
177 for (int i = 0; i < aSize; i++)
179 aLeft = i - aNumberOfContributions / 2;
180 aRight = i + aNumberOfContributions / 2;
181 aCurrentCount = 0;
182 for (int j = aLeft; j <= aRight; j++)
184 aWeight = rBlurVector[aCurrentCount];
186 // Mirror edges
187 if (j < 0)
189 aPixelIndex = -j;
191 else if (j >= aSize)
193 aPixelIndex = (aSize - j) + aSize - 1;
195 else
197 aPixelIndex = j;
200 // Edge case for small bitmaps
201 if (aPixelIndex < 0 || aPixelIndex >= aSize)
203 aWeight = 0.0;
206 rWeights[i * aNumberOfContributions + aCurrentCount] = aWeight;
207 rPixels[i * aNumberOfContributions + aCurrentCount] = aPixelIndex;
209 aCurrentCount++;
211 rCounts[i] = aCurrentCount;
215 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */