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 };
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 };
55 sal_Int32 mnComponentWidth;
57 sal_Int32 mnColorChannels;
60 sal_Int32 aRadius, sal_Int32 nComponentWidth, sal_Int32 nColorChannels)
61 : mpReadAccess(pReadAccess)
62 , mpWriteAccess(pWriteAccess)
64 , mnComponentWidth(nComponentWidth)
65 , mnDiv(aRadius + aRadius + 1)
66 , mnColorChannels(nColorChannels)
73 BlurSharedData maShared;
75 std::vector<sal_uInt8> maStackBuffer;
76 std::vector<sal_Int32> maPositionTable;
77 std::vector<sal_Int32> maWeightTable;
79 std::vector<sal_Int32> mnSumVector;
80 std::vector<sal_Int32> mnInSumVector;
81 std::vector<sal_Int32> mnOutSumVector;
83 BlurArrays(BlurSharedData
const& 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)
94 void initializeWeightAndPositions(sal_Int32 nLastIndex)
96 for (sal_Int32 i = 0;
i < maShared.mnDiv;
i++)
98 maPositionTable[
i] = std::clamp(i - maShared.mnRadius, sal_Int32(0), nLastIndex);
99 maWeightTable[
i] = maShared.mnRadius + 1 - std::abs(i - maShared.mnRadius);
103 sal_Int32 getMultiplyValue()
const
105 return static_cast<sal_Int32
>(constMultiplyTable[maShared.mnRadius]);
108 sal_Int32 getShiftValue()
const
110 return static_cast<sal_Int32
>(constShiftTable[maShared.mnRadius]);
114typedef void (*BlurRangeFn)(BlurSharedData
const& rShared, sal_Int32 nStartY, sal_Int32 nEndY);
118 BlurRangeFn mpBlurFunction;
119 BlurSharedData& mrShared;
124 explicit BlurTask(
const std::shared_ptr<comphelper::ThreadTaskTag>& pTag,
125 BlurRangeFn pBlurFunction, BlurSharedData& rShared, sal_Int32 nStartY,
128 , mpBlurFunction(pBlurFunction)
135 virtual void doWork()
override { mpBlurFunction(mrShared, mnStartY, mnEndY); }
140 static inline void add(sal_Int32*& pValue1, sal_Int32 nConstant)
142 pValue1[0] += nConstant;
143 pValue1[1] += nConstant;
144 pValue1[2] += nConstant;
147 static inline void set(sal_Int32*& pValue1, sal_Int32 nConstant)
149 pValue1[0] = nConstant;
150 pValue1[1] = nConstant;
151 pValue1[2] = nConstant;
154 static inline void add(sal_Int32*& pValue1,
const sal_uInt8* pValue2)
156 pValue1[0] += pValue2[0];
157 pValue1[1] += pValue2[1];
158 pValue1[2] += pValue2[2];
161 static inline void add(sal_Int32*& pValue1,
const sal_Int32* pValue2)
163 pValue1[0] += pValue2[0];
164 pValue1[1] += pValue2[1];
165 pValue1[2] += pValue2[2];
168 static inline void sub(sal_Int32*& pValue1,
const sal_uInt8* pValue2)
170 pValue1[0] -= pValue2[0];
171 pValue1[1] -= pValue2[1];
172 pValue1[2] -= pValue2[2];
175 static inline void sub(sal_Int32*& pValue1,
const sal_Int32* pValue2)
177 pValue1[0] -= pValue2[0];
178 pValue1[1] -= pValue2[1];
179 pValue1[2] -= pValue2[2];
184 pValue1[0] = pValue2[0];
185 pValue1[1] = pValue2[1];
186 pValue1[2] = pValue2[2];
189 static inline void assignMulAndShr(
sal_uInt8*& result,
const sal_Int32* sum, sal_Int32 multiply,
192 result[0] = (multiply * sum[0]) >> shift;
193 result[1] = (multiply * sum[1]) >> shift;
194 result[2] = (multiply * sum[2]) >> shift;
200 static inline void add(sal_Int32*& pValue1, sal_Int32 nConstant) { pValue1[0] += nConstant; }
202 static inline void set(sal_Int32*& pValue1, sal_Int32 nConstant) { pValue1[0] = nConstant; }
204 static inline void add(sal_Int32*& pValue1,
const sal_uInt8* pValue2)
206 pValue1[0] += pValue2[0];
209 static inline void add(sal_Int32*& pValue1,
const sal_Int32* pValue2)
211 pValue1[0] += pValue2[0];
214 static inline void sub(sal_Int32*& pValue1,
const sal_uInt8* pValue2)
216 pValue1[0] -= pValue2[0];
219 static inline void sub(sal_Int32*& pValue1,
const sal_Int32* pValue2)
221 pValue1[0] -= pValue2[0];
226 pValue1[0] = pValue2[0];
229 static inline void assignMulAndShr(
sal_uInt8*& result,
const sal_Int32* sum, sal_Int32 multiply,
232 result[0] = (multiply * sum[0]) >> shift;
236template <
typename SumFunction>
237void stackBlurHorizontal(BlurSharedData
const& rShared, sal_Int32 nStart, sal_Int32 nEnd)
242 BlurArrays aArrays(rShared);
244 sal_uInt8* pStack = aArrays.maStackBuffer.data();
247 sal_Int32 nWidth = pReadAccess->
Width();
248 sal_Int32 nLastIndexX = nWidth - 1;
250 sal_Int32 nMultiplyValue = aArrays.getMultiplyValue();
251 sal_Int32 nShiftValue = aArrays.getShiftValue();
253 sal_Int32 nRadius = rShared.mnRadius;
254 sal_Int32 nComponentWidth = rShared.mnComponentWidth;
255 sal_Int32 nDiv = rShared.mnDiv;
260 sal_Int32 nXPosition;
261 sal_Int32 nStackIndex;
262 sal_Int32 nStackIndexStart;
265 aArrays.initializeWeightAndPositions(nLastIndexX);
267 sal_Int32* nSum = aArrays.mnSumVector.data();
268 sal_Int32* nInSum = aArrays.mnInSumVector.data();
269 sal_Int32* nOutSum = aArrays.mnOutSumVector.data();
271 sal_Int32* pPositionPointer = aArrays.maPositionTable.data();
272 sal_Int32* pWeightPointer = aArrays.maWeightTable.data();
274 for (sal_Int32 y = nStart;
y <= nEnd;
y++)
276 SumFunction::set(nSum, 0);
277 SumFunction::set(nInSum, 0);
278 SumFunction::set(nOutSum, 0);
286 for (sal_Int32 i = 0;
i < nDiv;
i++)
288 pSourcePointer = pReadAccess->
GetScanline(y) + nComponentWidth * pPositionPointer[
i];
290 pStackPtr = &pStack[nComponentWidth *
i];
292 SumFunction::assignPtr(pStackPtr, pSourcePointer);
294 nWeight = pWeightPointer[
i];
296 SumFunction::add(nSum, pSourcePointer[0] * nWeight);
300 SumFunction::add(nInSum, pSourcePointer);
304 SumFunction::add(nOutSum, pSourcePointer);
308 nStackIndex = nRadius;
309 nXPosition = std::min(nRadius, nLastIndexX);
311 pSourcePointer = pReadAccess->
GetScanline(y) + nComponentWidth * nXPosition;
313 for (sal_Int32 x = 0;
x < nWidth;
x++)
315 pDestinationPointer = pWriteAccess->
GetScanline(y) + nComponentWidth *
x;
317 SumFunction::assignMulAndShr(pDestinationPointer, nSum, nMultiplyValue, nShiftValue);
319 SumFunction::sub(nSum, nOutSum);
321 nStackIndexStart = nStackIndex + nDiv - nRadius;
322 if (nStackIndexStart >= nDiv)
324 nStackIndexStart -= nDiv;
326 pStackPtr = &pStack[nComponentWidth * nStackIndexStart];
328 SumFunction::sub(nOutSum, pStackPtr);
330 if (nXPosition < nLastIndexX)
333 pSourcePointer = pReadAccess->
GetScanline(y) + nComponentWidth * nXPosition;
336 SumFunction::assignPtr(pStackPtr, pSourcePointer);
338 SumFunction::add(nInSum, pSourcePointer);
340 SumFunction::add(nSum, nInSum);
343 if (nStackIndex >= nDiv)
348 pStackPtr = &pStack[nStackIndex * nComponentWidth];
350 SumFunction::add(nOutSum, pStackPtr);
351 SumFunction::sub(nInSum, pStackPtr);
356template <
typename SumFunction>
357void stackBlurVertical(BlurSharedData
const& rShared, sal_Int32 nStart, sal_Int32 nEnd)
362 BlurArrays aArrays(rShared);
364 sal_uInt8* pStack = aArrays.maStackBuffer.data();
367 sal_Int32 nHeight = pReadAccess->
Height();
368 sal_Int32 nLastIndexY = nHeight - 1;
370 sal_Int32 nMultiplyValue = aArrays.getMultiplyValue();
371 sal_Int32 nShiftValue = aArrays.getShiftValue();
373 sal_Int32 nRadius = rShared.mnRadius;
374 sal_Int32 nComponentWidth = rShared.mnComponentWidth;
375 sal_Int32 nDiv = rShared.mnDiv;
380 sal_Int32 nYPosition;
381 sal_Int32 nStackIndex;
382 sal_Int32 nStackIndexStart;
385 aArrays.initializeWeightAndPositions(nLastIndexY);
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();
393 for (sal_Int32 x = nStart;
x <= nEnd;
x++)
395 SumFunction::set(nSum, 0);
396 SumFunction::set(nInSum, 0);
397 SumFunction::set(nOutSum, 0);
405 for (sal_Int32 i = 0;
i < nDiv;
i++)
407 pSourcePointer = pReadAccess->
GetScanline(pPositionPointer[i]) + nComponentWidth *
x;
409 pStackPtr = &pStack[nComponentWidth *
i];
411 SumFunction::assignPtr(pStackPtr, pSourcePointer);
413 nWeight = pWeightPointer[
i];
415 SumFunction::add(nSum, pSourcePointer[0] * nWeight);
419 SumFunction::add(nInSum, pSourcePointer);
423 SumFunction::add(nOutSum, pSourcePointer);
427 nStackIndex = nRadius;
428 nYPosition = std::min(nRadius, nLastIndexY);
430 pSourcePointer = pReadAccess->
GetScanline(nYPosition) + nComponentWidth *
x;
432 for (sal_Int32 y = 0;
y < nHeight;
y++)
434 pDestinationPointer = pWriteAccess->
GetScanline(y) + nComponentWidth *
x;
436 SumFunction::assignMulAndShr(pDestinationPointer, nSum, nMultiplyValue, nShiftValue);
438 SumFunction::sub(nSum, nOutSum);
440 nStackIndexStart = nStackIndex + nDiv - nRadius;
441 if (nStackIndexStart >= nDiv)
443 nStackIndexStart -= nDiv;
445 pStackPtr = &pStack[nComponentWidth * nStackIndexStart];
447 SumFunction::sub(nOutSum, pStackPtr);
449 if (nYPosition < nLastIndexY)
452 pSourcePointer = pReadAccess->
GetScanline(nYPosition) + nComponentWidth *
x;
455 SumFunction::assignPtr(pStackPtr, pSourcePointer);
456 SumFunction::add(nInSum, pSourcePointer);
457 SumFunction::add(nSum, nInSum);
460 if (nStackIndex >= nDiv)
465 pStackPtr = &pStack[nStackIndex * nComponentWidth];
467 SumFunction::add(nOutSum, pStackPtr);
468 SumFunction::sub(nInSum, pStackPtr);
473constexpr sal_Int32 nThreadStrip = 16;
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)
489 BlurSharedData aSharedData(pReadAccess.get(), pWriteAccess.get(), nRadius,
490 nComponentWidth, nColorChannels);
492 const sal_Int32 nFirstIndex = 0;
493 const sal_Int32 nLastIndex = pReadAccess->
Height() - 1;
495 vcl::bitmap::generateStripRanges<nThreadStrip>(
496 nFirstIndex, nLastIndex,
497 [&](sal_Int32
const nStart, sal_Int32
const nEnd,
bool const bLast) {
500 auto pTask(std::make_unique<BlurTask>(pTag, pBlurHorizontalFn,
501 aSharedData, nStart, nEnd));
505 pBlurHorizontalFn(aSharedData, nStart, nEnd);
512 BlurSharedData aSharedData(pReadAccess.get(), pWriteAccess.get(), nRadius,
513 nComponentWidth, nColorChannels);
515 const sal_Int32 nFirstIndex = 0;
516 const sal_Int32 nLastIndex = pReadAccess->
Width() - 1;
518 vcl::bitmap::generateStripRanges<nThreadStrip>(
519 nFirstIndex, nLastIndex,
520 [&](sal_Int32
const nStart, sal_Int32
const nEnd,
bool const bLast) {
523 auto pTask(std::make_unique<BlurTask>(pTag, pBlurVerticalFn,
524 aSharedData, nStart, nEnd));
528 pBlurVerticalFn(aSharedData, nStart, nEnd);
536 SAL_WARN(
"vcl.gdi",
"threaded bitmap blurring failed");
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);
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);
562void stackBlur24(
Bitmap& rBitmap, sal_Int32 nRadius, sal_Int32 nComponentWidth)
564 const bool bParallel =
true;
566 nRadius = std::clamp<sal_Int32>(nRadius, 2, 254);
567 const sal_Int32 nColorChannels = 3;
569 BlurRangeFn pBlurHorizontalFn = stackBlurHorizontal<SumFunction24>;
570 BlurRangeFn pBlurVerticalFn = stackBlurVertical<SumFunction24>;
572 runStackBlur(rBitmap, nRadius, nComponentWidth, nColorChannels, pBlurHorizontalFn,
573 pBlurVerticalFn, bParallel);
576void stackBlur8(
Bitmap& rBitmap, sal_Int32 nRadius, sal_Int32 nComponentWidth)
578 const bool bParallel =
true;
580 nRadius = std::clamp<sal_Int32>(nRadius, 2, 254);
581 const sal_Int32 nColorChannels = 1;
583 BlurRangeFn pBlurHorizontalFn = stackBlurHorizontal<SumFunction8>;
584 BlurRangeFn pBlurVerticalFn = stackBlurVertical<SumFunction8>;
586 runStackBlur(rBitmap, nRadius, nComponentWidth, nColorChannels, pBlurHorizontalFn,
587 pBlurVerticalFn, bParallel);
629 Bitmap bitmapCopy(rBitmap);
646 stackBlur24(bitmapCopy,
mnRadius, nComponentWidth);
650 int nComponentWidth = 1;
652 stackBlur8(bitmapCopy,
mnRadius, nComponentWidth);
const AlphaMask & GetAlphaMask() const
Bitmap GetBitmap(Color aTransparentReplaceColor) const
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
virtual ~BitmapFilterStackBlur()
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)
#define SAL_WARN(area, stream)
void set(css::uno::UnoInterfaceReference const &value)