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 
17 namespace
18 {
19 const 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 
36 const 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 
50 struct 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 
71 struct 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 
114 typedef void (*BlurRangeFn)(BlurSharedData const& rShared, sal_Int32 nStartY, sal_Int32 nEndY);
115 
116 class BlurTask : public comphelper::ThreadTask
117 {
118  BlurRangeFn mpBlurFunction;
119  BlurSharedData& mrShared;
120  sal_Int32 mnStartY;
121  sal_Int32 mnEndY;
122 
123 public:
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 
138 struct 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 
198 struct 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 
236 template <typename SumFunction>
237 void 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 
356 template <typename SumFunction>
357 void 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 
473 constexpr sal_Int32 nThreadStrip = 16;
474 
475 void 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 
562 void 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 
576 void 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->GetScanlineFormat();
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: */
tools::Long Height() const
Bitmap filter(Bitmap const &rBitmap) const
virtual void doWork()=0
float x
void waitUntilDone(const std::shared_ptr< ThreadTaskTag > &, bool bJoin=true)
ScanlineFormat
Definition: Scanline.hxx:29
Scanline GetScanline(tools::Long nY) const
float y
static ThreadPool & getSharedOptimalPool()
sal_uInt8 * Scanline
Definition: Scanline.hxx:26
int i
void pushTask(std::unique_ptr< ThreadTask > pTask)
ScanlineFormat GetScanlineFormat() const
tools::Long Width() const
BitmapFilterStackBlur(sal_Int32 nRadius)
Implementation of stack blur - a fast Gaussian blur approximation.
virtual BitmapEx execute(BitmapEx const &rBitmap) const override
Bitmap GetBitmap(Color aTransparentReplaceColor) const
Definition: BitmapEx.cxx:203
AlphaMask GetAlpha() const
Definition: BitmapEx.cxx:215
unsigned char sal_uInt8
static std::shared_ptr< ThreadTaskTag > createThreadTaskTag()
Any result
#define SAL_WARN(area, stream)
void set(css::uno::UnoInterfaceReference const &value)
typedef void(CALLTYPE *GetFuncDataPtr)(sal_uInt16 &nNo