LibreOffice Module vcl (master) 1
BitmapFilterStackBlur.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
13#include <sal/log.hxx>
14
16
17namespace
18{
19const sal_Int16 constMultiplyTable[255]
20 = { 512, 512, 456, 512, 328, 456, 335, 512, 405, 328, 271, 456, 388, 335, 292, 512, 454,
21 405, 364, 328, 298, 271, 496, 456, 420, 388, 360, 335, 312, 292, 273, 512, 482, 454,
22 428, 405, 383, 364, 345, 328, 312, 298, 284, 271, 259, 496, 475, 456, 437, 420, 404,
23 388, 374, 360, 347, 335, 323, 312, 302, 292, 282, 273, 265, 512, 497, 482, 468, 454,
24 441, 428, 417, 405, 394, 383, 373, 364, 354, 345, 337, 328, 320, 312, 305, 298, 291,
25 284, 278, 271, 265, 259, 507, 496, 485, 475, 465, 456, 446, 437, 428, 420, 412, 404,
26 396, 388, 381, 374, 367, 360, 354, 347, 341, 335, 329, 323, 318, 312, 307, 302, 297,
27 292, 287, 282, 278, 273, 269, 265, 261, 512, 505, 497, 489, 482, 475, 468, 461, 454,
28 447, 441, 435, 428, 422, 417, 411, 405, 399, 394, 389, 383, 378, 373, 368, 364, 359,
29 354, 350, 345, 341, 337, 332, 328, 324, 320, 316, 312, 309, 305, 301, 298, 294, 291,
30 287, 284, 281, 278, 274, 271, 268, 265, 262, 259, 257, 507, 501, 496, 491, 485, 480,
31 475, 470, 465, 460, 456, 451, 446, 442, 437, 433, 428, 424, 420, 416, 412, 408, 404,
32 400, 396, 392, 388, 385, 381, 377, 374, 370, 367, 363, 360, 357, 354, 350, 347, 344,
33 341, 338, 335, 332, 329, 326, 323, 320, 318, 315, 312, 310, 307, 304, 302, 299, 297,
34 294, 292, 289, 287, 285, 282, 280, 278, 275, 273, 271, 269, 267, 265, 263, 261, 259 };
35
36const sal_Int16 constShiftTable[255]
37 = { 9, 11, 12, 13, 13, 14, 14, 15, 15, 15, 15, 16, 16, 16, 16, 17, 17, 17, 17, 17, 17, 17,
38 18, 18, 18, 18, 18, 18, 18, 18, 18, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
39 19, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 21, 21, 21,
40 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21,
41 21, 21, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22,
42 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 23, 23, 23, 23, 23,
43 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23,
44 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23,
45 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
46 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
47 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
48 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24 };
49
50struct BlurSharedData
51{
52 BitmapReadAccess* mpReadAccess;
53 BitmapWriteAccess* mpWriteAccess;
54 sal_Int32 mnRadius;
55 sal_Int32 mnComponentWidth;
56 sal_Int32 mnDiv;
57 sal_Int32 mnColorChannels;
58
59 BlurSharedData(BitmapReadAccess* pReadAccess, BitmapWriteAccess* pWriteAccess,
60 sal_Int32 aRadius, sal_Int32 nComponentWidth, sal_Int32 nColorChannels)
61 : mpReadAccess(pReadAccess)
62 , mpWriteAccess(pWriteAccess)
63 , mnRadius(aRadius)
64 , mnComponentWidth(nComponentWidth)
65 , mnDiv(aRadius + aRadius + 1)
66 , mnColorChannels(nColorChannels)
67 {
68 }
69};
70
71struct BlurArrays
72{
73 BlurSharedData maShared;
74
75 std::vector<sal_uInt8> maStackBuffer;
76 std::vector<sal_Int32> maPositionTable;
77 std::vector<sal_Int32> maWeightTable;
78
79 std::vector<sal_Int32> mnSumVector;
80 std::vector<sal_Int32> mnInSumVector;
81 std::vector<sal_Int32> mnOutSumVector;
82
83 BlurArrays(BlurSharedData const& rShared)
84 : maShared(rShared)
85 , maStackBuffer(maShared.mnDiv * maShared.mnComponentWidth)
86 , maPositionTable(maShared.mnDiv)
87 , maWeightTable(maShared.mnDiv)
88 , mnSumVector(maShared.mnColorChannels)
89 , mnInSumVector(maShared.mnColorChannels)
90 , mnOutSumVector(maShared.mnColorChannels)
91 {
92 }
93
94 void initializeWeightAndPositions(sal_Int32 nLastIndex)
95 {
96 for (sal_Int32 i = 0; i < maShared.mnDiv; i++)
97 {
98 maPositionTable[i] = std::clamp(i - maShared.mnRadius, sal_Int32(0), nLastIndex);
99 maWeightTable[i] = maShared.mnRadius + 1 - std::abs(i - maShared.mnRadius);
100 }
101 }
102
103 sal_Int32 getMultiplyValue() const
104 {
105 return static_cast<sal_Int32>(constMultiplyTable[maShared.mnRadius]);
106 }
107
108 sal_Int32 getShiftValue() const
109 {
110 return static_cast<sal_Int32>(constShiftTable[maShared.mnRadius]);
111 }
112};
113
114typedef void (*BlurRangeFn)(BlurSharedData const& rShared, sal_Int32 nStartY, sal_Int32 nEndY);
115
116class BlurTask : public comphelper::ThreadTask
117{
118 BlurRangeFn mpBlurFunction;
119 BlurSharedData& mrShared;
120 sal_Int32 mnStartY;
121 sal_Int32 mnEndY;
122
123public:
124 explicit BlurTask(const std::shared_ptr<comphelper::ThreadTaskTag>& pTag,
125 BlurRangeFn pBlurFunction, BlurSharedData& rShared, sal_Int32 nStartY,
126 sal_Int32 nEndY)
127 : comphelper::ThreadTask(pTag)
128 , mpBlurFunction(pBlurFunction)
129 , mrShared(rShared)
130 , mnStartY(nStartY)
131 , mnEndY(nEndY)
132 {
133 }
134
135 virtual void doWork() override { mpBlurFunction(mrShared, mnStartY, mnEndY); }
136};
137
138struct SumFunction24
139{
140 static inline void add(sal_Int32*& pValue1, sal_Int32 nConstant)
141 {
142 pValue1[0] += nConstant;
143 pValue1[1] += nConstant;
144 pValue1[2] += nConstant;
145 }
146
147 static inline void set(sal_Int32*& pValue1, sal_Int32 nConstant)
148 {
149 pValue1[0] = nConstant;
150 pValue1[1] = nConstant;
151 pValue1[2] = nConstant;
152 }
153
154 static inline void add(sal_Int32*& pValue1, const sal_uInt8* pValue2)
155 {
156 pValue1[0] += pValue2[0];
157 pValue1[1] += pValue2[1];
158 pValue1[2] += pValue2[2];
159 }
160
161 static inline void add(sal_Int32*& pValue1, const sal_Int32* pValue2)
162 {
163 pValue1[0] += pValue2[0];
164 pValue1[1] += pValue2[1];
165 pValue1[2] += pValue2[2];
166 }
167
168 static inline void sub(sal_Int32*& pValue1, const sal_uInt8* pValue2)
169 {
170 pValue1[0] -= pValue2[0];
171 pValue1[1] -= pValue2[1];
172 pValue1[2] -= pValue2[2];
173 }
174
175 static inline void sub(sal_Int32*& pValue1, const sal_Int32* pValue2)
176 {
177 pValue1[0] -= pValue2[0];
178 pValue1[1] -= pValue2[1];
179 pValue1[2] -= pValue2[2];
180 }
181
182 static inline void assignPtr(sal_uInt8*& pValue1, const sal_uInt8* pValue2)
183 {
184 pValue1[0] = pValue2[0];
185 pValue1[1] = pValue2[1];
186 pValue1[2] = pValue2[2];
187 }
188
189 static inline void assignMulAndShr(sal_uInt8*& result, const sal_Int32* sum, sal_Int32 multiply,
190 sal_Int32 shift)
191 {
192 result[0] = (multiply * sum[0]) >> shift;
193 result[1] = (multiply * sum[1]) >> shift;
194 result[2] = (multiply * sum[2]) >> shift;
195 }
196};
197
198struct SumFunction8
199{
200 static inline void add(sal_Int32*& pValue1, sal_Int32 nConstant) { pValue1[0] += nConstant; }
201
202 static inline void set(sal_Int32*& pValue1, sal_Int32 nConstant) { pValue1[0] = nConstant; }
203
204 static inline void add(sal_Int32*& pValue1, const sal_uInt8* pValue2)
205 {
206 pValue1[0] += pValue2[0];
207 }
208
209 static inline void add(sal_Int32*& pValue1, const sal_Int32* pValue2)
210 {
211 pValue1[0] += pValue2[0];
212 }
213
214 static inline void sub(sal_Int32*& pValue1, const sal_uInt8* pValue2)
215 {
216 pValue1[0] -= pValue2[0];
217 }
218
219 static inline void sub(sal_Int32*& pValue1, const sal_Int32* pValue2)
220 {
221 pValue1[0] -= pValue2[0];
222 }
223
224 static inline void assignPtr(sal_uInt8*& pValue1, const sal_uInt8* pValue2)
225 {
226 pValue1[0] = pValue2[0];
227 }
228
229 static inline void assignMulAndShr(sal_uInt8*& result, const sal_Int32* sum, sal_Int32 multiply,
230 sal_Int32 shift)
231 {
232 result[0] = (multiply * sum[0]) >> shift;
233 }
234};
235
236template <typename SumFunction>
237void stackBlurHorizontal(BlurSharedData const& rShared, sal_Int32 nStart, sal_Int32 nEnd)
238{
239 BitmapReadAccess* pReadAccess = rShared.mpReadAccess;
240 BitmapWriteAccess* pWriteAccess = rShared.mpWriteAccess;
241
242 BlurArrays aArrays(rShared);
243
244 sal_uInt8* pStack = aArrays.maStackBuffer.data();
245 sal_uInt8* pStackPtr;
246
247 sal_Int32 nWidth = pReadAccess->Width();
248 sal_Int32 nLastIndexX = nWidth - 1;
249
250 sal_Int32 nMultiplyValue = aArrays.getMultiplyValue();
251 sal_Int32 nShiftValue = aArrays.getShiftValue();
252
253 sal_Int32 nRadius = rShared.mnRadius;
254 sal_Int32 nComponentWidth = rShared.mnComponentWidth;
255 sal_Int32 nDiv = rShared.mnDiv;
256
257 Scanline pSourcePointer;
258 Scanline pDestinationPointer;
259
260 sal_Int32 nXPosition;
261 sal_Int32 nStackIndex;
262 sal_Int32 nStackIndexStart;
263 sal_Int32 nWeight;
264
265 aArrays.initializeWeightAndPositions(nLastIndexX);
266
267 sal_Int32* nSum = aArrays.mnSumVector.data();
268 sal_Int32* nInSum = aArrays.mnInSumVector.data();
269 sal_Int32* nOutSum = aArrays.mnOutSumVector.data();
270
271 sal_Int32* pPositionPointer = aArrays.maPositionTable.data();
272 sal_Int32* pWeightPointer = aArrays.maWeightTable.data();
273
274 for (sal_Int32 y = nStart; y <= nEnd; y++)
275 {
276 SumFunction::set(nSum, 0);
277 SumFunction::set(nInSum, 0);
278 SumFunction::set(nOutSum, 0);
279
280 // Pre-initialize blur data for first pixel.
281 // aArrays.maPositionTable contains values like (for radius of 5): [0,0,0,0,0,0,1,2,3,4,5],
282 // which are used as pixels indices in the current row that we use to prepare information
283 // for the first pixel; aArrays.maWeightTable has [1,2,3,4,5,6,5,4,3,2,1]. Before looking at
284 // the first row pixel, we pretend to have processed fake previous pixels, as if the row was
285 // extended to the left with the same color as that of the first pixel.
286 for (sal_Int32 i = 0; i < nDiv; i++)
287 {
288 pSourcePointer = pReadAccess->GetScanline(y) + nComponentWidth * pPositionPointer[i];
289
290 pStackPtr = &pStack[nComponentWidth * i];
291
292 SumFunction::assignPtr(pStackPtr, pSourcePointer);
293
294 nWeight = pWeightPointer[i];
295
296 SumFunction::add(nSum, pSourcePointer[0] * nWeight);
297
298 if (i - nRadius > 0)
299 {
300 SumFunction::add(nInSum, pSourcePointer);
301 }
302 else
303 {
304 SumFunction::add(nOutSum, pSourcePointer);
305 }
306 }
307
308 nStackIndex = nRadius;
309 nXPosition = std::min(nRadius, nLastIndexX);
310
311 pSourcePointer = pReadAccess->GetScanline(y) + nComponentWidth * nXPosition;
312
313 for (sal_Int32 x = 0; x < nWidth; x++)
314 {
315 pDestinationPointer = pWriteAccess->GetScanline(y) + nComponentWidth * x;
316
317 SumFunction::assignMulAndShr(pDestinationPointer, nSum, nMultiplyValue, nShiftValue);
318
319 SumFunction::sub(nSum, nOutSum);
320
321 nStackIndexStart = nStackIndex + nDiv - nRadius;
322 if (nStackIndexStart >= nDiv)
323 {
324 nStackIndexStart -= nDiv;
325 }
326 pStackPtr = &pStack[nComponentWidth * nStackIndexStart];
327
328 SumFunction::sub(nOutSum, pStackPtr);
329
330 if (nXPosition < nLastIndexX)
331 {
332 nXPosition++;
333 pSourcePointer = pReadAccess->GetScanline(y) + nComponentWidth * nXPosition;
334 }
335
336 SumFunction::assignPtr(pStackPtr, pSourcePointer);
337
338 SumFunction::add(nInSum, pSourcePointer);
339
340 SumFunction::add(nSum, nInSum);
341
342 nStackIndex++;
343 if (nStackIndex >= nDiv)
344 {
345 nStackIndex = 0;
346 }
347
348 pStackPtr = &pStack[nStackIndex * nComponentWidth];
349
350 SumFunction::add(nOutSum, pStackPtr);
351 SumFunction::sub(nInSum, pStackPtr);
352 }
353 }
354}
355
356template <typename SumFunction>
357void stackBlurVertical(BlurSharedData const& rShared, sal_Int32 nStart, sal_Int32 nEnd)
358{
359 BitmapReadAccess* pReadAccess = rShared.mpReadAccess;
360 BitmapWriteAccess* pWriteAccess = rShared.mpWriteAccess;
361
362 BlurArrays aArrays(rShared);
363
364 sal_uInt8* pStack = aArrays.maStackBuffer.data();
365 sal_uInt8* pStackPtr;
366
367 sal_Int32 nHeight = pReadAccess->Height();
368 sal_Int32 nLastIndexY = nHeight - 1;
369
370 sal_Int32 nMultiplyValue = aArrays.getMultiplyValue();
371 sal_Int32 nShiftValue = aArrays.getShiftValue();
372
373 sal_Int32 nRadius = rShared.mnRadius;
374 sal_Int32 nComponentWidth = rShared.mnComponentWidth;
375 sal_Int32 nDiv = rShared.mnDiv;
376
377 Scanline pSourcePointer;
378 Scanline pDestinationPointer;
379
380 sal_Int32 nYPosition;
381 sal_Int32 nStackIndex;
382 sal_Int32 nStackIndexStart;
383 sal_Int32 nWeight;
384
385 aArrays.initializeWeightAndPositions(nLastIndexY);
386
387 sal_Int32* nSum = aArrays.mnSumVector.data();
388 sal_Int32* nInSum = aArrays.mnInSumVector.data();
389 sal_Int32* nOutSum = aArrays.mnOutSumVector.data();
390 sal_Int32* pPositionPointer = aArrays.maPositionTable.data();
391 sal_Int32* pWeightPointer = aArrays.maWeightTable.data();
392
393 for (sal_Int32 x = nStart; x <= nEnd; x++)
394 {
395 SumFunction::set(nSum, 0);
396 SumFunction::set(nInSum, 0);
397 SumFunction::set(nOutSum, 0);
398
399 // Pre-initialize blur data for first pixel.
400 // aArrays.maPositionTable contains values like (for radius of 5): [0,0,0,0,0,0,1,2,3,4,5],
401 // which are used as pixels indices in the current column that we use to prepare information
402 // for the first pixel; aArrays.maWeightTable has [1,2,3,4,5,6,5,4,3,2,1]. Before looking at
403 // the first column pixels, we pretend to have processed fake previous pixels, as if the
404 // column was extended to the top with the same color as that of the first pixel.
405 for (sal_Int32 i = 0; i < nDiv; i++)
406 {
407 pSourcePointer = pReadAccess->GetScanline(pPositionPointer[i]) + nComponentWidth * x;
408
409 pStackPtr = &pStack[nComponentWidth * i];
410
411 SumFunction::assignPtr(pStackPtr, pSourcePointer);
412
413 nWeight = pWeightPointer[i];
414
415 SumFunction::add(nSum, pSourcePointer[0] * nWeight);
416
417 if (i - nRadius > 0)
418 {
419 SumFunction::add(nInSum, pSourcePointer);
420 }
421 else
422 {
423 SumFunction::add(nOutSum, pSourcePointer);
424 }
425 }
426
427 nStackIndex = nRadius;
428 nYPosition = std::min(nRadius, nLastIndexY);
429
430 pSourcePointer = pReadAccess->GetScanline(nYPosition) + nComponentWidth * x;
431
432 for (sal_Int32 y = 0; y < nHeight; y++)
433 {
434 pDestinationPointer = pWriteAccess->GetScanline(y) + nComponentWidth * x;
435
436 SumFunction::assignMulAndShr(pDestinationPointer, nSum, nMultiplyValue, nShiftValue);
437
438 SumFunction::sub(nSum, nOutSum);
439
440 nStackIndexStart = nStackIndex + nDiv - nRadius;
441 if (nStackIndexStart >= nDiv)
442 {
443 nStackIndexStart -= nDiv;
444 }
445 pStackPtr = &pStack[nComponentWidth * nStackIndexStart];
446
447 SumFunction::sub(nOutSum, pStackPtr);
448
449 if (nYPosition < nLastIndexY)
450 {
451 nYPosition++;
452 pSourcePointer = pReadAccess->GetScanline(nYPosition) + nComponentWidth * x;
453 }
454
455 SumFunction::assignPtr(pStackPtr, pSourcePointer);
456 SumFunction::add(nInSum, pSourcePointer);
457 SumFunction::add(nSum, nInSum);
458
459 nStackIndex++;
460 if (nStackIndex >= nDiv)
461 {
462 nStackIndex = 0;
463 }
464
465 pStackPtr = &pStack[nStackIndex * nComponentWidth];
466
467 SumFunction::add(nOutSum, pStackPtr);
468 SumFunction::sub(nInSum, pStackPtr);
469 }
470 }
471}
472
473constexpr sal_Int32 nThreadStrip = 16;
474
475void runStackBlur(Bitmap& rBitmap, const sal_Int32 nRadius, const sal_Int32 nComponentWidth,
476 const sal_Int32 nColorChannels, BlurRangeFn pBlurHorizontalFn,
477 BlurRangeFn pBlurVerticalFn, const bool bParallel)
478{
479 if (bParallel)
480 {
481 try
482 {
485
486 {
487 Bitmap::ScopedReadAccess pReadAccess(rBitmap);
488 BitmapScopedWriteAccess pWriteAccess(rBitmap);
489 BlurSharedData aSharedData(pReadAccess.get(), pWriteAccess.get(), nRadius,
490 nComponentWidth, nColorChannels);
491
492 const sal_Int32 nFirstIndex = 0;
493 const sal_Int32 nLastIndex = pReadAccess->Height() - 1;
494
495 vcl::bitmap::generateStripRanges<nThreadStrip>(
496 nFirstIndex, nLastIndex,
497 [&](sal_Int32 const nStart, sal_Int32 const nEnd, bool const bLast) {
498 if (!bLast)
499 {
500 auto pTask(std::make_unique<BlurTask>(pTag, pBlurHorizontalFn,
501 aSharedData, nStart, nEnd));
502 rShared.pushTask(std::move(pTask));
503 }
504 else
505 pBlurHorizontalFn(aSharedData, nStart, nEnd);
506 });
507 rShared.waitUntilDone(pTag);
508 }
509 {
510 Bitmap::ScopedReadAccess pReadAccess(rBitmap);
511 BitmapScopedWriteAccess pWriteAccess(rBitmap);
512 BlurSharedData aSharedData(pReadAccess.get(), pWriteAccess.get(), nRadius,
513 nComponentWidth, nColorChannels);
514
515 const sal_Int32 nFirstIndex = 0;
516 const sal_Int32 nLastIndex = pReadAccess->Width() - 1;
517
518 vcl::bitmap::generateStripRanges<nThreadStrip>(
519 nFirstIndex, nLastIndex,
520 [&](sal_Int32 const nStart, sal_Int32 const nEnd, bool const bLast) {
521 if (!bLast)
522 {
523 auto pTask(std::make_unique<BlurTask>(pTag, pBlurVerticalFn,
524 aSharedData, nStart, nEnd));
525 rShared.pushTask(std::move(pTask));
526 }
527 else
528 pBlurVerticalFn(aSharedData, nStart, nEnd);
529 });
530
531 rShared.waitUntilDone(pTag);
532 }
533 }
534 catch (...)
535 {
536 SAL_WARN("vcl.gdi", "threaded bitmap blurring failed");
537 }
538 }
539 else
540 {
541 {
542 Bitmap::ScopedReadAccess pReadAccess(rBitmap);
543 BitmapScopedWriteAccess pWriteAccess(rBitmap);
544 BlurSharedData aSharedData(pReadAccess.get(), pWriteAccess.get(), nRadius,
545 nComponentWidth, nColorChannels);
546 sal_Int32 nFirstIndex = 0;
547 sal_Int32 nLastIndex = pReadAccess->Height() - 1;
548 pBlurHorizontalFn(aSharedData, nFirstIndex, nLastIndex);
549 }
550 {
551 Bitmap::ScopedReadAccess pReadAccess(rBitmap);
552 BitmapScopedWriteAccess pWriteAccess(rBitmap);
553 BlurSharedData aSharedData(pReadAccess.get(), pWriteAccess.get(), nRadius,
554 nComponentWidth, nColorChannels);
555 sal_Int32 nFirstIndex = 0;
556 sal_Int32 nLastIndex = pReadAccess->Width() - 1;
557 pBlurVerticalFn(aSharedData, nFirstIndex, nLastIndex);
558 }
559 }
560}
561
562void stackBlur24(Bitmap& rBitmap, sal_Int32 nRadius, sal_Int32 nComponentWidth)
563{
564 const bool bParallel = true;
565 // Limit radius
566 nRadius = std::clamp<sal_Int32>(nRadius, 2, 254);
567 const sal_Int32 nColorChannels = 3; // 3 color channel
568
569 BlurRangeFn pBlurHorizontalFn = stackBlurHorizontal<SumFunction24>;
570 BlurRangeFn pBlurVerticalFn = stackBlurVertical<SumFunction24>;
571
572 runStackBlur(rBitmap, nRadius, nComponentWidth, nColorChannels, pBlurHorizontalFn,
573 pBlurVerticalFn, bParallel);
574}
575
576void stackBlur8(Bitmap& rBitmap, sal_Int32 nRadius, sal_Int32 nComponentWidth)
577{
578 const bool bParallel = true;
579 // Limit radius
580 nRadius = std::clamp<sal_Int32>(nRadius, 2, 254);
581 const sal_Int32 nColorChannels = 1; // 1 color channel
582
583 BlurRangeFn pBlurHorizontalFn = stackBlurHorizontal<SumFunction8>;
584 BlurRangeFn pBlurVerticalFn = stackBlurVertical<SumFunction8>;
585
586 runStackBlur(rBitmap, nRadius, nComponentWidth, nColorChannels, pBlurHorizontalFn,
587 pBlurVerticalFn, bParallel);
588}
589
590} // end anonymous namespace
591
614 : mnRadius(nRadius)
615{
616}
617
619
621{
622 Bitmap aBitmap = rBitmapEx.GetBitmap();
623 Bitmap result = filter(aBitmap);
624 return BitmapEx(result, rBitmapEx.GetAlpha());
625}
626
628{
629 Bitmap bitmapCopy(rBitmap);
630 ScanlineFormat nScanlineFormat;
631 {
632 Bitmap::ScopedReadAccess pReadAccess(bitmapCopy);
633 nScanlineFormat = pReadAccess ? pReadAccess->GetScanlineFormat() : ScanlineFormat::NONE;
634 }
635
636 if (nScanlineFormat == ScanlineFormat::N24BitTcRgb
637 || nScanlineFormat == ScanlineFormat::N24BitTcBgr
638 || nScanlineFormat == ScanlineFormat::N32BitTcMask
639 || nScanlineFormat == ScanlineFormat::N32BitTcBgra)
640 {
641 int nComponentWidth = (nScanlineFormat == ScanlineFormat::N32BitTcMask
642 || nScanlineFormat == ScanlineFormat::N32BitTcBgra)
643 ? 4
644 : 3;
645
646 stackBlur24(bitmapCopy, mnRadius, nComponentWidth);
647 }
648 else if (nScanlineFormat == ScanlineFormat::N8BitPal)
649 {
650 int nComponentWidth = 1;
651
652 stackBlur8(bitmapCopy, mnRadius, nComponentWidth);
653 }
654
655 return bitmapCopy;
656}
657
658/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
sal_uInt8 * Scanline
Definition: Scanline.hxx:26
ScanlineFormat
Definition: Scanline.hxx:29
AlphaMask GetAlpha() const
Definition: BitmapEx.cxx:215
Bitmap GetBitmap(Color aTransparentReplaceColor) const
Definition: BitmapEx.cxx:203
virtual BitmapEx execute(BitmapEx const &rBitmap) const override
BitmapFilterStackBlur(sal_Int32 nRadius)
Implementation of stack blur - a fast Gaussian blur approximation.
Bitmap filter(Bitmap const &rBitmap) const
tools::Long Height() const
tools::Long Width() const
ScanlineFormat GetScanlineFormat() const
Scanline GetScanline(tools::Long nY) const
static ThreadPool & getSharedOptimalPool()
void waitUntilDone(const std::shared_ptr< ThreadTaskTag > &, bool bJoin=true)
static std::shared_ptr< ThreadTaskTag > createThreadTaskTag()
void pushTask(std::unique_ptr< ThreadTask > pTask)
virtual void doWork()=0
float y
float x
#define SAL_WARN(area, stream)
void set(css::uno::UnoInterfaceReference const &value)
int i
unsigned char sal_uInt8
Any result