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