tdf#130857 qt weld: Implement QtInstanceWidget::get_text_height
[LibreOffice.git] / vcl / source / bitmap / BitmapGaussianSeparableBlurFilter.cxx
blob8489162ab8ca7b5e4cf5b31fd01ac86c0ef1cb0a
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/BitmapGaussianSeparableBlurFilter.hxx>
14 #include <vcl/BitmapWriteAccess.hxx>
16 BitmapEx BitmapGaussianSeparableBlurFilter::execute(BitmapEx const& rBitmapEx) const
18 Bitmap aBitmap(rBitmapEx.GetBitmap());
20 const sal_Int32 nWidth = aBitmap.GetSizePixel().Width();
21 const sal_Int32 nHeight = aBitmap.GetSizePixel().Height();
23 // Prepare Blur Vector
24 int aNumberOfContributions;
25 std::vector<double> aBlurVector(makeBlurKernel(mfRadius, aNumberOfContributions));
26 std::vector<double> aWeights;
27 std::vector<int> aPixels;
28 std::vector<int> aCounts;
30 // Do horizontal filtering
31 blurContributions(nWidth, aNumberOfContributions, aBlurVector, aWeights, aPixels, aCounts);
33 BitmapScopedReadAccess pReadAcc(aBitmap);
35 // switch coordinates as convolution pass transposes result
36 Bitmap aNewBitmap(Size(nHeight, nWidth), vcl::PixelFormat::N24_BPP);
38 bool bResult = convolutionPass(aBitmap, aNewBitmap, pReadAcc.get(), aNumberOfContributions,
39 aWeights.data(), aPixels.data(), aCounts.data());
41 // Cleanup
42 pReadAcc.reset();
43 aWeights.clear();
44 aPixels.clear();
45 aCounts.clear();
47 if (!bResult)
49 aBlurVector.clear();
51 else
53 // Swap current bitmap with new bitmap
54 aBitmap.ReassignWithSize(aNewBitmap);
56 // Do vertical filtering
57 blurContributions(nHeight, aNumberOfContributions, aBlurVector, aWeights, aPixels, aCounts);
59 pReadAcc = aBitmap;
60 aNewBitmap = Bitmap(Size(nWidth, nHeight), vcl::PixelFormat::N24_BPP);
61 bResult = convolutionPass(aBitmap, aNewBitmap, pReadAcc.get(), aNumberOfContributions,
62 aWeights.data(), aPixels.data(), aCounts.data());
64 // Cleanup
65 pReadAcc.reset();
66 aWeights.clear();
67 aCounts.clear();
68 aPixels.clear();
69 aBlurVector.clear();
71 if (bResult)
72 aBitmap.ReassignWithSize(aNewBitmap); // swap current bitmap with new bitmap
75 if (bResult)
76 return BitmapEx(aBitmap);
78 return BitmapEx();
81 bool BitmapGaussianSeparableBlurFilter::convolutionPass(const Bitmap& rBitmap, Bitmap& aNewBitmap,
82 BitmapReadAccess const* pReadAcc,
83 int aNumberOfContributions,
84 const double* pWeights, int const* pPixels,
85 const int* pCount)
87 if (!pReadAcc)
88 return false;
90 BitmapScopedWriteAccess pWriteAcc(aNewBitmap);
91 if (!pWriteAcc)
92 return false;
94 const int nHeight = rBitmap.GetSizePixel().Height();
95 assert(rBitmap.GetSizePixel().Height() == aNewBitmap.GetSizePixel().Width());
96 const int nWidth = rBitmap.GetSizePixel().Width();
97 assert(rBitmap.GetSizePixel().Width() == aNewBitmap.GetSizePixel().Height());
99 BitmapColor aColor;
100 double aValueRed, aValueGreen, aValueBlue;
101 double aSum, aWeight;
102 int aBaseIndex, aIndex;
104 for (int nSourceY = 0; nSourceY < nHeight; ++nSourceY)
106 for (int nSourceX = 0; nSourceX < nWidth; ++nSourceX)
108 aBaseIndex = nSourceX * aNumberOfContributions;
109 aSum = aValueRed = aValueGreen = aValueBlue = 0.0;
111 for (int j = 0; j < pCount[nSourceX]; ++j)
113 aIndex = aBaseIndex + j;
114 aWeight = pWeights[aIndex];
115 aSum += aWeight;
117 aColor = pReadAcc->GetColor(nSourceY, pPixels[aIndex]);
119 aValueRed += aWeight * aColor.GetRed();
120 aValueGreen += aWeight * aColor.GetGreen();
121 aValueBlue += aWeight * aColor.GetBlue();
124 BitmapColor aResultColor(
125 static_cast<sal_uInt8>(std::clamp(aValueRed / aSum, 0.0, 255.0)),
126 static_cast<sal_uInt8>(std::clamp(aValueGreen / aSum, 0.0, 255.0)),
127 static_cast<sal_uInt8>(std::clamp(aValueBlue / aSum, 0.0, 255.0)));
129 int nDestX = nSourceY;
130 int nDestY = nSourceX;
132 pWriteAcc->SetPixel(nDestY, nDestX, aResultColor);
135 return true;
138 std::vector<double> BitmapGaussianSeparableBlurFilter::makeBlurKernel(const double radius,
139 int& rows)
141 int intRadius = static_cast<int>(radius + 1.0);
142 rows = intRadius * 2 + 1;
143 std::vector<double> matrix(rows);
145 double sigma = radius / 3;
146 double radius2 = radius * radius;
147 int index = 0;
148 for (int row = -intRadius; row <= intRadius; row++)
150 double distance = row * row;
151 if (distance > radius2)
153 matrix[index] = 0.0;
155 else
157 matrix[index] = exp(-distance / (2.0 * sigma * sigma)) / sqrt(2.0 * M_PI * sigma);
159 index++;
161 return matrix;
164 void BitmapGaussianSeparableBlurFilter::blurContributions(
165 const int aSize, const int aNumberOfContributions, const std::vector<double>& rBlurVector,
166 std::vector<double>& rWeights, std::vector<int>& rPixels, std::vector<int>& rCounts)
168 rWeights.resize(aSize * aNumberOfContributions);
169 rPixels.resize(aSize * aNumberOfContributions);
170 rCounts.resize(aSize);
172 int aLeft, aRight, aCurrentCount, aPixelIndex;
173 double aWeight;
175 for (int i = 0; i < aSize; i++)
177 aLeft = i - aNumberOfContributions / 2;
178 aRight = i + aNumberOfContributions / 2;
179 aCurrentCount = 0;
180 for (int j = aLeft; j <= aRight; j++)
182 aWeight = rBlurVector[aCurrentCount];
184 // Mirror edges
185 if (j < 0)
187 aPixelIndex = -j;
189 else if (j >= aSize)
191 aPixelIndex = (aSize - j) + aSize - 1;
193 else
195 aPixelIndex = j;
198 // Edge case for small bitmaps
199 if (aPixelIndex < 0 || aPixelIndex >= aSize)
201 aWeight = 0.0;
204 rWeights[i * aNumberOfContributions + aCurrentCount] = aWeight;
205 rPixels[i * aNumberOfContributions + aCurrentCount] = aPixelIndex;
207 aCurrentCount++;
209 rCounts[i] = aCurrentCount;
213 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */