1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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/.
11 #include <vcl/BitmapFilterStackBlur.hxx>
12 #include <vcl/bitmapaccess.hxx>
13 #include <bitmapwriteaccess.hxx>
14 #include <sal/log.hxx>
16 #include <comphelper/threadpool.hxx>
20 const sal_Int16 constMultiplyTable
[255]
21 = { 512, 512, 456, 512, 328, 456, 335, 512, 405, 328, 271, 456, 388, 335, 292, 512, 454,
22 405, 364, 328, 298, 271, 496, 456, 420, 388, 360, 335, 312, 292, 273, 512, 482, 454,
23 428, 405, 383, 364, 345, 328, 312, 298, 284, 271, 259, 496, 475, 456, 437, 420, 404,
24 388, 374, 360, 347, 335, 323, 312, 302, 292, 282, 273, 265, 512, 497, 482, 468, 454,
25 441, 428, 417, 405, 394, 383, 373, 364, 354, 345, 337, 328, 320, 312, 305, 298, 291,
26 284, 278, 271, 265, 259, 507, 496, 485, 475, 465, 456, 446, 437, 428, 420, 412, 404,
27 396, 388, 381, 374, 367, 360, 354, 347, 341, 335, 329, 323, 318, 312, 307, 302, 297,
28 292, 287, 282, 278, 273, 269, 265, 261, 512, 505, 497, 489, 482, 475, 468, 461, 454,
29 447, 441, 435, 428, 422, 417, 411, 405, 399, 394, 389, 383, 378, 373, 368, 364, 359,
30 354, 350, 345, 341, 337, 332, 328, 324, 320, 316, 312, 309, 305, 301, 298, 294, 291,
31 287, 284, 281, 278, 274, 271, 268, 265, 262, 259, 257, 507, 501, 496, 491, 485, 480,
32 475, 470, 465, 460, 456, 451, 446, 442, 437, 433, 428, 424, 420, 416, 412, 408, 404,
33 400, 396, 392, 388, 385, 381, 377, 374, 370, 367, 363, 360, 357, 354, 350, 347, 344,
34 341, 338, 335, 332, 329, 326, 323, 320, 318, 315, 312, 310, 307, 304, 302, 299, 297,
35 294, 292, 289, 287, 285, 282, 280, 278, 275, 273, 271, 269, 267, 265, 263, 261, 259 };
37 const sal_Int16 constShiftTable
[255]
38 = { 9, 11, 12, 13, 13, 14, 14, 15, 15, 15, 15, 16, 16, 16, 16, 17, 17, 17, 17, 17, 17, 17,
39 18, 18, 18, 18, 18, 18, 18, 18, 18, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
40 19, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 21, 21, 21,
41 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21,
42 21, 21, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22,
43 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 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, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23,
46 23, 23, 23, 23, 23, 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, 24, 24, 24, 24, 24, 24, 24, 24, 24,
49 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24 };
53 BitmapReadAccess
* mpReadAccess
;
54 BitmapWriteAccess
* mpWriteAccess
;
56 tools::Long mnComponentWidth
;
58 tools::Long mnColorChannels
;
60 BlurSharedData(BitmapReadAccess
* pReadAccess
, BitmapWriteAccess
* pWriteAccess
,
61 tools::Long aRadius
, tools::Long nComponentWidth
, tools::Long nColorChannels
)
62 : mpReadAccess(pReadAccess
)
63 , mpWriteAccess(pWriteAccess
)
65 , mnComponentWidth(nComponentWidth
)
66 , mnDiv(aRadius
+ aRadius
+ 1)
67 , mnColorChannels(nColorChannels
)
74 BlurSharedData maShared
;
76 std::vector
<sal_uInt8
> maStackBuffer
;
77 std::vector
<tools::Long
> maPositionTable
;
78 std::vector
<tools::Long
> maWeightTable
;
80 std::vector
<tools::Long
> mnSumVector
;
81 std::vector
<tools::Long
> mnInSumVector
;
82 std::vector
<tools::Long
> mnOutSumVector
;
84 BlurArrays(BlurSharedData
const& rShared
)
86 , maStackBuffer(maShared
.mnDiv
* maShared
.mnComponentWidth
)
87 , maPositionTable(maShared
.mnDiv
)
88 , maWeightTable(maShared
.mnDiv
)
89 , mnSumVector(maShared
.mnColorChannels
)
90 , mnInSumVector(maShared
.mnColorChannels
)
91 , mnOutSumVector(maShared
.mnColorChannels
)
95 void initializeWeightAndPositions(tools::Long nLastIndex
)
97 for (tools::Long i
= 0; i
< maShared
.mnDiv
; i
++)
99 maPositionTable
[i
] = std::clamp(i
- maShared
.mnRadius
, tools::Long(0), nLastIndex
);
100 maWeightTable
[i
] = maShared
.mnRadius
+ 1 - std::abs(i
- maShared
.mnRadius
);
104 tools::Long
getMultiplyValue() const
106 return static_cast<tools::Long
>(constMultiplyTable
[maShared
.mnRadius
]);
109 tools::Long
getShiftValue() const
111 return static_cast<tools::Long
>(constShiftTable
[maShared
.mnRadius
]);
115 typedef void (*BlurRangeFn
)(BlurSharedData
const& rShared
, tools::Long nStartY
, tools::Long nEndY
);
117 class BlurTask
: public comphelper::ThreadTask
119 BlurRangeFn mpBlurFunction
;
120 BlurSharedData
& mrShared
;
121 tools::Long mnStartY
;
125 explicit BlurTask(const std::shared_ptr
<comphelper::ThreadTaskTag
>& pTag
,
126 BlurRangeFn pBlurFunction
, BlurSharedData
& rShared
, tools::Long nStartY
,
128 : comphelper::ThreadTask(pTag
)
129 , mpBlurFunction(pBlurFunction
)
136 virtual void doWork() override
{ mpBlurFunction(mrShared
, mnStartY
, mnEndY
); }
141 static inline void add(tools::Long
*& pValue1
, tools::Long nConstant
)
143 pValue1
[0] += nConstant
;
144 pValue1
[1] += nConstant
;
145 pValue1
[2] += nConstant
;
148 static inline void set(tools::Long
*& pValue1
, tools::Long nConstant
)
150 pValue1
[0] = nConstant
;
151 pValue1
[1] = nConstant
;
152 pValue1
[2] = nConstant
;
155 static inline void add(tools::Long
*& pValue1
, const sal_uInt8
* pValue2
)
157 pValue1
[0] += pValue2
[0];
158 pValue1
[1] += pValue2
[1];
159 pValue1
[2] += pValue2
[2];
162 static inline void add(tools::Long
*& pValue1
, const tools::Long
* pValue2
)
164 pValue1
[0] += pValue2
[0];
165 pValue1
[1] += pValue2
[1];
166 pValue1
[2] += pValue2
[2];
169 static inline void sub(tools::Long
*& pValue1
, const sal_uInt8
* pValue2
)
171 pValue1
[0] -= pValue2
[0];
172 pValue1
[1] -= pValue2
[1];
173 pValue1
[2] -= pValue2
[2];
176 static inline void sub(tools::Long
*& pValue1
, const tools::Long
* pValue2
)
178 pValue1
[0] -= pValue2
[0];
179 pValue1
[1] -= pValue2
[1];
180 pValue1
[2] -= pValue2
[2];
183 static inline void assignPtr(sal_uInt8
*& pValue1
, const sal_uInt8
* pValue2
)
185 pValue1
[0] = pValue2
[0];
186 pValue1
[1] = pValue2
[1];
187 pValue1
[2] = pValue2
[2];
190 static inline void assignMulAndShr(sal_uInt8
*& result
, const tools::Long
* sum
,
191 tools::Long multiply
, tools::Long shift
)
193 result
[0] = (multiply
* sum
[0]) >> shift
;
194 result
[1] = (multiply
* sum
[1]) >> shift
;
195 result
[2] = (multiply
* sum
[2]) >> shift
;
201 static inline void add(tools::Long
*& pValue1
, tools::Long nConstant
)
203 pValue1
[0] += nConstant
;
206 static inline void set(tools::Long
*& pValue1
, tools::Long nConstant
) { pValue1
[0] = nConstant
; }
208 static inline void add(tools::Long
*& pValue1
, const sal_uInt8
* pValue2
)
210 pValue1
[0] += pValue2
[0];
213 static inline void add(tools::Long
*& pValue1
, const tools::Long
* pValue2
)
215 pValue1
[0] += pValue2
[0];
218 static inline void sub(tools::Long
*& pValue1
, const sal_uInt8
* pValue2
)
220 pValue1
[0] -= pValue2
[0];
223 static inline void sub(tools::Long
*& pValue1
, const tools::Long
* pValue2
)
225 pValue1
[0] -= pValue2
[0];
228 static inline void assignPtr(sal_uInt8
*& pValue1
, const sal_uInt8
* pValue2
)
230 pValue1
[0] = pValue2
[0];
233 static inline void assignMulAndShr(sal_uInt8
*& result
, const tools::Long
* sum
,
234 tools::Long multiply
, tools::Long shift
)
236 result
[0] = (multiply
* sum
[0]) >> shift
;
240 template <typename SumFunction
>
241 void stackBlurHorizontal(BlurSharedData
const& rShared
, tools::Long nStart
, tools::Long nEnd
)
243 BitmapReadAccess
* pReadAccess
= rShared
.mpReadAccess
;
244 BitmapWriteAccess
* pWriteAccess
= rShared
.mpWriteAccess
;
246 BlurArrays
aArrays(rShared
);
248 sal_uInt8
* pStack
= aArrays
.maStackBuffer
.data();
249 sal_uInt8
* pStackPtr
;
251 tools::Long nWidth
= pReadAccess
->Width();
252 tools::Long nLastIndexX
= nWidth
- 1;
254 tools::Long nMultiplyValue
= aArrays
.getMultiplyValue();
255 tools::Long nShiftValue
= aArrays
.getShiftValue();
257 tools::Long nRadius
= rShared
.mnRadius
;
258 tools::Long nComponentWidth
= rShared
.mnComponentWidth
;
259 tools::Long nDiv
= rShared
.mnDiv
;
261 Scanline pSourcePointer
;
262 Scanline pDestinationPointer
;
264 tools::Long nXPosition
;
265 tools::Long nStackIndex
;
266 tools::Long nStackIndexStart
;
269 aArrays
.initializeWeightAndPositions(nLastIndexX
);
271 tools::Long
* nSum
= aArrays
.mnSumVector
.data();
272 tools::Long
* nInSum
= aArrays
.mnInSumVector
.data();
273 tools::Long
* nOutSum
= aArrays
.mnOutSumVector
.data();
275 tools::Long
* pPositionPointer
= aArrays
.maPositionTable
.data();
276 tools::Long
* pWeightPointer
= aArrays
.maWeightTable
.data();
278 for (tools::Long y
= nStart
; y
<= nEnd
; y
++)
280 SumFunction::set(nSum
, 0L);
281 SumFunction::set(nInSum
, 0L);
282 SumFunction::set(nOutSum
, 0L);
284 // Pre-initialize blur data for first pixel.
285 // aArrays.maPositionTable contains values like (for radius of 5): [0,0,0,0,0,0,1,2,3,4,5],
286 // which are used as pixels indices in the current row that we use to prepare information
287 // for the first pixel; aArrays.maWeightTable has [1,2,3,4,5,6,5,4,3,2,1]. Before looking at
288 // the first row pixel, we pretend to have processed fake previous pixels, as if the row was
289 // extended to the left with the same color as that of the first pixel.
290 for (tools::Long i
= 0; i
< nDiv
; i
++)
292 pSourcePointer
= pReadAccess
->GetScanline(y
) + nComponentWidth
* pPositionPointer
[i
];
294 pStackPtr
= &pStack
[nComponentWidth
* i
];
296 SumFunction::assignPtr(pStackPtr
, pSourcePointer
);
298 nWeight
= pWeightPointer
[i
];
300 SumFunction::add(nSum
, pSourcePointer
[0] * nWeight
);
304 SumFunction::add(nInSum
, pSourcePointer
);
308 SumFunction::add(nOutSum
, pSourcePointer
);
312 nStackIndex
= nRadius
;
313 nXPosition
= std::min(nRadius
, nLastIndexX
);
315 pSourcePointer
= pReadAccess
->GetScanline(y
) + nComponentWidth
* nXPosition
;
317 for (tools::Long x
= 0; x
< nWidth
; x
++)
319 pDestinationPointer
= pWriteAccess
->GetScanline(y
) + nComponentWidth
* x
;
321 SumFunction::assignMulAndShr(pDestinationPointer
, nSum
, nMultiplyValue
, nShiftValue
);
323 SumFunction::sub(nSum
, nOutSum
);
325 nStackIndexStart
= nStackIndex
+ nDiv
- nRadius
;
326 if (nStackIndexStart
>= nDiv
)
328 nStackIndexStart
-= nDiv
;
330 pStackPtr
= &pStack
[nComponentWidth
* nStackIndexStart
];
332 SumFunction::sub(nOutSum
, pStackPtr
);
334 if (nXPosition
< nLastIndexX
)
337 pSourcePointer
= pReadAccess
->GetScanline(y
) + nComponentWidth
* nXPosition
;
340 SumFunction::assignPtr(pStackPtr
, pSourcePointer
);
342 SumFunction::add(nInSum
, pSourcePointer
);
344 SumFunction::add(nSum
, nInSum
);
347 if (nStackIndex
>= nDiv
)
352 pStackPtr
= &pStack
[nStackIndex
* nComponentWidth
];
354 SumFunction::add(nOutSum
, pStackPtr
);
355 SumFunction::sub(nInSum
, pStackPtr
);
360 template <typename SumFunction
>
361 void stackBlurVertical(BlurSharedData
const& rShared
, tools::Long nStart
, tools::Long nEnd
)
363 BitmapReadAccess
* pReadAccess
= rShared
.mpReadAccess
;
364 BitmapWriteAccess
* pWriteAccess
= rShared
.mpWriteAccess
;
366 BlurArrays
aArrays(rShared
);
368 sal_uInt8
* pStack
= aArrays
.maStackBuffer
.data();
369 sal_uInt8
* pStackPtr
;
371 tools::Long nHeight
= pReadAccess
->Height();
372 tools::Long nLastIndexY
= nHeight
- 1;
374 tools::Long nMultiplyValue
= aArrays
.getMultiplyValue();
375 tools::Long nShiftValue
= aArrays
.getShiftValue();
377 tools::Long nRadius
= rShared
.mnRadius
;
378 tools::Long nComponentWidth
= rShared
.mnComponentWidth
;
379 tools::Long nDiv
= rShared
.mnDiv
;
381 Scanline pSourcePointer
;
382 Scanline pDestinationPointer
;
384 tools::Long nYPosition
;
385 tools::Long nStackIndex
;
386 tools::Long nStackIndexStart
;
389 aArrays
.initializeWeightAndPositions(nLastIndexY
);
391 tools::Long
* nSum
= aArrays
.mnSumVector
.data();
392 tools::Long
* nInSum
= aArrays
.mnInSumVector
.data();
393 tools::Long
* nOutSum
= aArrays
.mnOutSumVector
.data();
394 tools::Long
* pPositionPointer
= aArrays
.maPositionTable
.data();
395 tools::Long
* pWeightPointer
= aArrays
.maWeightTable
.data();
397 for (tools::Long x
= nStart
; x
<= nEnd
; x
++)
399 SumFunction::set(nSum
, 0L);
400 SumFunction::set(nInSum
, 0L);
401 SumFunction::set(nOutSum
, 0L);
403 // Pre-initialize blur data for first pixel.
404 // aArrays.maPositionTable contains values like (for radius of 5): [0,0,0,0,0,0,1,2,3,4,5],
405 // which are used as pixels indices in the current column that we use to prepare information
406 // for the first pixel; aArrays.maWeightTable has [1,2,3,4,5,6,5,4,3,2,1]. Before looking at
407 // the first column pixels, we pretend to have processed fake previous pixels, as if the
408 // column was extended to the top with the same color as that of the first pixel.
409 for (tools::Long i
= 0; i
< nDiv
; i
++)
411 pSourcePointer
= pReadAccess
->GetScanline(pPositionPointer
[i
]) + nComponentWidth
* x
;
413 pStackPtr
= &pStack
[nComponentWidth
* i
];
415 SumFunction::assignPtr(pStackPtr
, pSourcePointer
);
417 nWeight
= pWeightPointer
[i
];
419 SumFunction::add(nSum
, pSourcePointer
[0] * nWeight
);
423 SumFunction::add(nInSum
, pSourcePointer
);
427 SumFunction::add(nOutSum
, pSourcePointer
);
431 nStackIndex
= nRadius
;
432 nYPosition
= std::min(nRadius
, nLastIndexY
);
434 pSourcePointer
= pReadAccess
->GetScanline(nYPosition
) + nComponentWidth
* x
;
436 for (tools::Long y
= 0; y
< nHeight
; y
++)
438 pDestinationPointer
= pWriteAccess
->GetScanline(y
) + nComponentWidth
* x
;
440 SumFunction::assignMulAndShr(pDestinationPointer
, nSum
, nMultiplyValue
, nShiftValue
);
442 SumFunction::sub(nSum
, nOutSum
);
444 nStackIndexStart
= nStackIndex
+ nDiv
- nRadius
;
445 if (nStackIndexStart
>= nDiv
)
447 nStackIndexStart
-= nDiv
;
449 pStackPtr
= &pStack
[nComponentWidth
* nStackIndexStart
];
451 SumFunction::sub(nOutSum
, pStackPtr
);
453 if (nYPosition
< nLastIndexY
)
456 pSourcePointer
= pReadAccess
->GetScanline(nYPosition
) + nComponentWidth
* x
;
459 SumFunction::assignPtr(pStackPtr
, pSourcePointer
);
460 SumFunction::add(nInSum
, pSourcePointer
);
461 SumFunction::add(nSum
, nInSum
);
464 if (nStackIndex
>= nDiv
)
469 pStackPtr
= &pStack
[nStackIndex
* nComponentWidth
];
471 SumFunction::add(nOutSum
, pStackPtr
);
472 SumFunction::sub(nInSum
, pStackPtr
);
477 constexpr tools::Long nThreadStrip
= 16;
479 void runStackBlur(Bitmap
& rBitmap
, const tools::Long nRadius
, const tools::Long nComponentWidth
,
480 const tools::Long nColorChannels
, BlurRangeFn pBlurHorizontalFn
,
481 BlurRangeFn pBlurVerticalFn
, const bool bParallel
)
487 comphelper::ThreadPool
& rShared
= comphelper::ThreadPool::getSharedOptimalPool();
488 auto pTag
= comphelper::ThreadPool::createThreadTaskTag();
491 Bitmap::ScopedReadAccess
pReadAccess(rBitmap
);
492 BitmapScopedWriteAccess
pWriteAccess(rBitmap
);
493 BlurSharedData
aSharedData(pReadAccess
.get(), pWriteAccess
.get(), nRadius
,
494 nComponentWidth
, nColorChannels
);
496 const tools::Long nFirstIndex
= 0;
497 const tools::Long nLastIndex
= pReadAccess
->Height() - 1;
499 vcl::bitmap::generateStripRanges
<nThreadStrip
>(
500 nFirstIndex
, nLastIndex
,
501 [&](tools::Long
const nStart
, tools::Long
const nEnd
, bool const bLast
) {
504 auto pTask(std::make_unique
<BlurTask
>(pTag
, pBlurHorizontalFn
,
505 aSharedData
, nStart
, nEnd
));
506 rShared
.pushTask(std::move(pTask
));
509 pBlurHorizontalFn(aSharedData
, nStart
, nEnd
);
511 rShared
.waitUntilDone(pTag
);
514 Bitmap::ScopedReadAccess
pReadAccess(rBitmap
);
515 BitmapScopedWriteAccess
pWriteAccess(rBitmap
);
516 BlurSharedData
aSharedData(pReadAccess
.get(), pWriteAccess
.get(), nRadius
,
517 nComponentWidth
, nColorChannels
);
519 const tools::Long nFirstIndex
= 0;
520 const tools::Long nLastIndex
= pReadAccess
->Width() - 1;
522 vcl::bitmap::generateStripRanges
<nThreadStrip
>(
523 nFirstIndex
, nLastIndex
,
524 [&](tools::Long
const nStart
, tools::Long
const nEnd
, bool const bLast
) {
527 auto pTask(std::make_unique
<BlurTask
>(pTag
, pBlurVerticalFn
,
528 aSharedData
, nStart
, nEnd
));
529 rShared
.pushTask(std::move(pTask
));
532 pBlurVerticalFn(aSharedData
, nStart
, nEnd
);
535 rShared
.waitUntilDone(pTag
);
540 SAL_WARN("vcl.gdi", "threaded bitmap blurring failed");
546 Bitmap::ScopedReadAccess
pReadAccess(rBitmap
);
547 BitmapScopedWriteAccess
pWriteAccess(rBitmap
);
548 BlurSharedData
aSharedData(pReadAccess
.get(), pWriteAccess
.get(), nRadius
,
549 nComponentWidth
, nColorChannels
);
550 tools::Long nFirstIndex
= 0;
551 tools::Long nLastIndex
= pReadAccess
->Height() - 1;
552 pBlurHorizontalFn(aSharedData
, nFirstIndex
, nLastIndex
);
555 Bitmap::ScopedReadAccess
pReadAccess(rBitmap
);
556 BitmapScopedWriteAccess
pWriteAccess(rBitmap
);
557 BlurSharedData
aSharedData(pReadAccess
.get(), pWriteAccess
.get(), nRadius
,
558 nComponentWidth
, nColorChannels
);
559 tools::Long nFirstIndex
= 0;
560 tools::Long nLastIndex
= pReadAccess
->Width() - 1;
561 pBlurVerticalFn(aSharedData
, nFirstIndex
, nLastIndex
);
566 void stackBlur24(Bitmap
& rBitmap
, sal_Int32 nRadius
, sal_Int32 nComponentWidth
)
568 const bool bParallel
= true;
570 nRadius
= std::clamp
<sal_Int32
>(nRadius
, 2, 254);
571 const tools::Long nColorChannels
= 3; // 3 color channel
573 BlurRangeFn pBlurHorizontalFn
= stackBlurHorizontal
<SumFunction24
>;
574 BlurRangeFn pBlurVerticalFn
= stackBlurVertical
<SumFunction24
>;
576 runStackBlur(rBitmap
, nRadius
, nComponentWidth
, nColorChannels
, pBlurHorizontalFn
,
577 pBlurVerticalFn
, bParallel
);
580 void stackBlur8(Bitmap
& rBitmap
, sal_Int32 nRadius
, sal_Int32 nComponentWidth
)
582 const bool bParallel
= true;
584 nRadius
= std::clamp
<sal_Int32
>(nRadius
, 2, 254);
585 const tools::Long nColorChannels
= 1; // 1 color channel
587 BlurRangeFn pBlurHorizontalFn
= stackBlurHorizontal
<SumFunction8
>;
588 BlurRangeFn pBlurVerticalFn
= stackBlurVertical
<SumFunction8
>;
590 runStackBlur(rBitmap
, nRadius
, nComponentWidth
, nColorChannels
, pBlurHorizontalFn
,
591 pBlurVerticalFn
, bParallel
);
594 } // end anonymous namespace
597 * Implementation of stack blur - a fast Gaussian blur approximation.
598 * nRadius - blur radius, valid values are between 2 and 254
599 * bExtend - extend the bitmap in all directions by the radius
601 * Stack Blur Algorithm by Mario Klingemann <mario@quasimondo.com>
602 * (http://www.quasimondo.com/StackBlurForCanvas/StackBlurDemo.html)
604 * Additionally references and implementations:
605 * - Blur.js by Jacob Kelley
606 * (http://www.blurjs.com)
607 * - BlurEffectForAndroidDesign by Nicolas Pomepuy
608 * (https://github.com/PomepuyN/BlurEffectForAndroidDesign)
609 * - StackBluriOS by Thomas Landspurg
610 * (https://github.com/tomsoft1/StackBluriOS)
611 * - stackblur.cpp by Benjamin Yates
612 * (https://gist.github.com/benjamin9999/3809142)
613 * - stack blur in fog 2D graphic library by Petr Kobalicek
614 * (https://code.google.com/p/fog/)
617 BitmapFilterStackBlur::BitmapFilterStackBlur(sal_Int32 nRadius
)
622 BitmapFilterStackBlur::~BitmapFilterStackBlur() {}
624 BitmapEx
BitmapFilterStackBlur::execute(BitmapEx
const& rBitmapEx
) const
626 Bitmap aBitmap
= rBitmapEx
.GetBitmap();
627 Bitmap result
= filter(aBitmap
);
628 return BitmapEx(result
, rBitmapEx
.GetMask());
631 Bitmap
BitmapFilterStackBlur::filter(Bitmap
const& rBitmap
) const
633 Bitmap
bitmapCopy(rBitmap
);
634 ScanlineFormat nScanlineFormat
;
636 Bitmap::ScopedReadAccess
pReadAccess(bitmapCopy
);
637 nScanlineFormat
= pReadAccess
->GetScanlineFormat();
640 if (nScanlineFormat
== ScanlineFormat::N24BitTcRgb
641 || nScanlineFormat
== ScanlineFormat::N24BitTcBgr
642 || nScanlineFormat
== ScanlineFormat::N32BitTcMask
643 || nScanlineFormat
== ScanlineFormat::N32BitTcBgra
)
645 int nComponentWidth
= (nScanlineFormat
== ScanlineFormat::N32BitTcMask
646 || nScanlineFormat
== ScanlineFormat::N32BitTcBgra
)
650 stackBlur24(bitmapCopy
, mnRadius
, nComponentWidth
);
652 else if (nScanlineFormat
== ScanlineFormat::N8BitPal
)
654 int nComponentWidth
= 1;
656 stackBlur8(bitmapCopy
, mnRadius
, nComponentWidth
);
662 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */