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 <bitmap/BitmapWriteAccess.hxx>
13 #include <sal/log.hxx>
15 #include <comphelper/threadpool.hxx>
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 };
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 };
52 BitmapReadAccess
* mpReadAccess
;
53 BitmapWriteAccess
* mpWriteAccess
;
55 tools::Long mnComponentWidth
;
57 tools::Long mnColorChannels
;
59 BlurSharedData(BitmapReadAccess
* pReadAccess
, BitmapWriteAccess
* pWriteAccess
,
60 tools::Long aRadius
, tools::Long nComponentWidth
, tools::Long 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
<tools::Long
> maPositionTable
;
77 std::vector
<tools::Long
> maWeightTable
;
79 std::vector
<tools::Long
> mnSumVector
;
80 std::vector
<tools::Long
> mnInSumVector
;
81 std::vector
<tools::Long
> 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(tools::Long nLastIndex
)
96 for (tools::Long i
= 0; i
< maShared
.mnDiv
; i
++)
98 maPositionTable
[i
] = std::clamp(i
- maShared
.mnRadius
, tools::Long(0), nLastIndex
);
99 maWeightTable
[i
] = maShared
.mnRadius
+ 1 - std::abs(i
- maShared
.mnRadius
);
103 tools::Long
getMultiplyValue() const
105 return static_cast<tools::Long
>(constMultiplyTable
[maShared
.mnRadius
]);
108 tools::Long
getShiftValue() const
110 return static_cast<tools::Long
>(constShiftTable
[maShared
.mnRadius
]);
114 typedef void (*BlurRangeFn
)(BlurSharedData
const& rShared
, tools::Long nStartY
, tools::Long nEndY
);
116 class BlurTask
: public comphelper::ThreadTask
118 BlurRangeFn mpBlurFunction
;
119 BlurSharedData
& mrShared
;
120 tools::Long mnStartY
;
124 explicit BlurTask(const std::shared_ptr
<comphelper::ThreadTaskTag
>& pTag
,
125 BlurRangeFn pBlurFunction
, BlurSharedData
& rShared
, tools::Long nStartY
,
127 : comphelper::ThreadTask(pTag
)
128 , mpBlurFunction(pBlurFunction
)
135 virtual void doWork() override
{ mpBlurFunction(mrShared
, mnStartY
, mnEndY
); }
140 static inline void add(tools::Long
*& pValue1
, tools::Long nConstant
)
142 pValue1
[0] += nConstant
;
143 pValue1
[1] += nConstant
;
144 pValue1
[2] += nConstant
;
147 static inline void set(tools::Long
*& pValue1
, tools::Long nConstant
)
149 pValue1
[0] = nConstant
;
150 pValue1
[1] = nConstant
;
151 pValue1
[2] = nConstant
;
154 static inline void add(tools::Long
*& 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(tools::Long
*& pValue1
, const tools::Long
* pValue2
)
163 pValue1
[0] += pValue2
[0];
164 pValue1
[1] += pValue2
[1];
165 pValue1
[2] += pValue2
[2];
168 static inline void sub(tools::Long
*& 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(tools::Long
*& pValue1
, const tools::Long
* pValue2
)
177 pValue1
[0] -= pValue2
[0];
178 pValue1
[1] -= pValue2
[1];
179 pValue1
[2] -= pValue2
[2];
182 static inline void assignPtr(sal_uInt8
*& pValue1
, const sal_uInt8
* pValue2
)
184 pValue1
[0] = pValue2
[0];
185 pValue1
[1] = pValue2
[1];
186 pValue1
[2] = pValue2
[2];
189 static inline void assignMulAndShr(sal_uInt8
*& result
, const tools::Long
* sum
,
190 tools::Long multiply
, tools::Long shift
)
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(tools::Long
*& pValue1
, tools::Long nConstant
)
202 pValue1
[0] += nConstant
;
205 static inline void set(tools::Long
*& pValue1
, tools::Long nConstant
) { pValue1
[0] = nConstant
; }
207 static inline void add(tools::Long
*& pValue1
, const sal_uInt8
* pValue2
)
209 pValue1
[0] += pValue2
[0];
212 static inline void add(tools::Long
*& pValue1
, const tools::Long
* pValue2
)
214 pValue1
[0] += pValue2
[0];
217 static inline void sub(tools::Long
*& pValue1
, const sal_uInt8
* pValue2
)
219 pValue1
[0] -= pValue2
[0];
222 static inline void sub(tools::Long
*& pValue1
, const tools::Long
* pValue2
)
224 pValue1
[0] -= pValue2
[0];
227 static inline void assignPtr(sal_uInt8
*& pValue1
, const sal_uInt8
* pValue2
)
229 pValue1
[0] = pValue2
[0];
232 static inline void assignMulAndShr(sal_uInt8
*& result
, const tools::Long
* sum
,
233 tools::Long multiply
, tools::Long shift
)
235 result
[0] = (multiply
* sum
[0]) >> shift
;
239 template <typename SumFunction
>
240 void stackBlurHorizontal(BlurSharedData
const& rShared
, tools::Long nStart
, tools::Long nEnd
)
242 BitmapReadAccess
* pReadAccess
= rShared
.mpReadAccess
;
243 BitmapWriteAccess
* pWriteAccess
= rShared
.mpWriteAccess
;
245 BlurArrays
aArrays(rShared
);
247 sal_uInt8
* pStack
= aArrays
.maStackBuffer
.data();
248 sal_uInt8
* pStackPtr
;
250 tools::Long nWidth
= pReadAccess
->Width();
251 tools::Long nLastIndexX
= nWidth
- 1;
253 tools::Long nMultiplyValue
= aArrays
.getMultiplyValue();
254 tools::Long nShiftValue
= aArrays
.getShiftValue();
256 tools::Long nRadius
= rShared
.mnRadius
;
257 tools::Long nComponentWidth
= rShared
.mnComponentWidth
;
258 tools::Long nDiv
= rShared
.mnDiv
;
260 Scanline pSourcePointer
;
261 Scanline pDestinationPointer
;
263 tools::Long nXPosition
;
264 tools::Long nStackIndex
;
265 tools::Long nStackIndexStart
;
268 aArrays
.initializeWeightAndPositions(nLastIndexX
);
270 tools::Long
* nSum
= aArrays
.mnSumVector
.data();
271 tools::Long
* nInSum
= aArrays
.mnInSumVector
.data();
272 tools::Long
* nOutSum
= aArrays
.mnOutSumVector
.data();
274 tools::Long
* pPositionPointer
= aArrays
.maPositionTable
.data();
275 tools::Long
* pWeightPointer
= aArrays
.maWeightTable
.data();
277 for (tools::Long y
= nStart
; y
<= nEnd
; y
++)
279 SumFunction::set(nSum
, 0);
280 SumFunction::set(nInSum
, 0);
281 SumFunction::set(nOutSum
, 0);
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
++)
291 pSourcePointer
= pReadAccess
->GetScanline(y
) + nComponentWidth
* pPositionPointer
[i
];
293 pStackPtr
= &pStack
[nComponentWidth
* i
];
295 SumFunction::assignPtr(pStackPtr
, pSourcePointer
);
297 nWeight
= pWeightPointer
[i
];
299 SumFunction::add(nSum
, pSourcePointer
[0] * nWeight
);
303 SumFunction::add(nInSum
, pSourcePointer
);
307 SumFunction::add(nOutSum
, pSourcePointer
);
311 nStackIndex
= nRadius
;
312 nXPosition
= std::min(nRadius
, nLastIndexX
);
314 pSourcePointer
= pReadAccess
->GetScanline(y
) + nComponentWidth
* nXPosition
;
316 for (tools::Long x
= 0; x
< nWidth
; x
++)
318 pDestinationPointer
= pWriteAccess
->GetScanline(y
) + nComponentWidth
* x
;
320 SumFunction::assignMulAndShr(pDestinationPointer
, nSum
, nMultiplyValue
, nShiftValue
);
322 SumFunction::sub(nSum
, nOutSum
);
324 nStackIndexStart
= nStackIndex
+ nDiv
- nRadius
;
325 if (nStackIndexStart
>= nDiv
)
327 nStackIndexStart
-= nDiv
;
329 pStackPtr
= &pStack
[nComponentWidth
* nStackIndexStart
];
331 SumFunction::sub(nOutSum
, pStackPtr
);
333 if (nXPosition
< nLastIndexX
)
336 pSourcePointer
= pReadAccess
->GetScanline(y
) + nComponentWidth
* nXPosition
;
339 SumFunction::assignPtr(pStackPtr
, pSourcePointer
);
341 SumFunction::add(nInSum
, pSourcePointer
);
343 SumFunction::add(nSum
, nInSum
);
346 if (nStackIndex
>= nDiv
)
351 pStackPtr
= &pStack
[nStackIndex
* nComponentWidth
];
353 SumFunction::add(nOutSum
, pStackPtr
);
354 SumFunction::sub(nInSum
, pStackPtr
);
359 template <typename SumFunction
>
360 void stackBlurVertical(BlurSharedData
const& rShared
, tools::Long nStart
, tools::Long nEnd
)
362 BitmapReadAccess
* pReadAccess
= rShared
.mpReadAccess
;
363 BitmapWriteAccess
* pWriteAccess
= rShared
.mpWriteAccess
;
365 BlurArrays
aArrays(rShared
);
367 sal_uInt8
* pStack
= aArrays
.maStackBuffer
.data();
368 sal_uInt8
* pStackPtr
;
370 tools::Long nHeight
= pReadAccess
->Height();
371 tools::Long nLastIndexY
= nHeight
- 1;
373 tools::Long nMultiplyValue
= aArrays
.getMultiplyValue();
374 tools::Long nShiftValue
= aArrays
.getShiftValue();
376 tools::Long nRadius
= rShared
.mnRadius
;
377 tools::Long nComponentWidth
= rShared
.mnComponentWidth
;
378 tools::Long nDiv
= rShared
.mnDiv
;
380 Scanline pSourcePointer
;
381 Scanline pDestinationPointer
;
383 tools::Long nYPosition
;
384 tools::Long nStackIndex
;
385 tools::Long nStackIndexStart
;
388 aArrays
.initializeWeightAndPositions(nLastIndexY
);
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();
396 for (tools::Long x
= nStart
; x
<= nEnd
; x
++)
398 SumFunction::set(nSum
, 0);
399 SumFunction::set(nInSum
, 0);
400 SumFunction::set(nOutSum
, 0);
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
++)
410 pSourcePointer
= pReadAccess
->GetScanline(pPositionPointer
[i
]) + nComponentWidth
* x
;
412 pStackPtr
= &pStack
[nComponentWidth
* i
];
414 SumFunction::assignPtr(pStackPtr
, pSourcePointer
);
416 nWeight
= pWeightPointer
[i
];
418 SumFunction::add(nSum
, pSourcePointer
[0] * nWeight
);
422 SumFunction::add(nInSum
, pSourcePointer
);
426 SumFunction::add(nOutSum
, pSourcePointer
);
430 nStackIndex
= nRadius
;
431 nYPosition
= std::min(nRadius
, nLastIndexY
);
433 pSourcePointer
= pReadAccess
->GetScanline(nYPosition
) + nComponentWidth
* x
;
435 for (tools::Long y
= 0; y
< nHeight
; y
++)
437 pDestinationPointer
= pWriteAccess
->GetScanline(y
) + nComponentWidth
* x
;
439 SumFunction::assignMulAndShr(pDestinationPointer
, nSum
, nMultiplyValue
, nShiftValue
);
441 SumFunction::sub(nSum
, nOutSum
);
443 nStackIndexStart
= nStackIndex
+ nDiv
- nRadius
;
444 if (nStackIndexStart
>= nDiv
)
446 nStackIndexStart
-= nDiv
;
448 pStackPtr
= &pStack
[nComponentWidth
* nStackIndexStart
];
450 SumFunction::sub(nOutSum
, pStackPtr
);
452 if (nYPosition
< nLastIndexY
)
455 pSourcePointer
= pReadAccess
->GetScanline(nYPosition
) + nComponentWidth
* x
;
458 SumFunction::assignPtr(pStackPtr
, pSourcePointer
);
459 SumFunction::add(nInSum
, pSourcePointer
);
460 SumFunction::add(nSum
, nInSum
);
463 if (nStackIndex
>= nDiv
)
468 pStackPtr
= &pStack
[nStackIndex
* nComponentWidth
];
470 SumFunction::add(nOutSum
, pStackPtr
);
471 SumFunction::sub(nInSum
, pStackPtr
);
476 constexpr tools::Long nThreadStrip
= 16;
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
)
486 comphelper::ThreadPool
& rShared
= comphelper::ThreadPool::getSharedOptimalPool();
487 auto pTag
= comphelper::ThreadPool::createThreadTaskTag();
490 Bitmap::ScopedReadAccess
pReadAccess(rBitmap
);
491 BitmapScopedWriteAccess
pWriteAccess(rBitmap
);
492 BlurSharedData
aSharedData(pReadAccess
.get(), pWriteAccess
.get(), nRadius
,
493 nComponentWidth
, nColorChannels
);
495 const tools::Long nFirstIndex
= 0;
496 const tools::Long nLastIndex
= pReadAccess
->Height() - 1;
498 vcl::bitmap::generateStripRanges
<nThreadStrip
>(
499 nFirstIndex
, nLastIndex
,
500 [&](tools::Long
const nStart
, tools::Long
const nEnd
, bool const bLast
) {
503 auto pTask(std::make_unique
<BlurTask
>(pTag
, pBlurHorizontalFn
,
504 aSharedData
, nStart
, nEnd
));
505 rShared
.pushTask(std::move(pTask
));
508 pBlurHorizontalFn(aSharedData
, nStart
, nEnd
);
510 rShared
.waitUntilDone(pTag
);
513 Bitmap::ScopedReadAccess
pReadAccess(rBitmap
);
514 BitmapScopedWriteAccess
pWriteAccess(rBitmap
);
515 BlurSharedData
aSharedData(pReadAccess
.get(), pWriteAccess
.get(), nRadius
,
516 nComponentWidth
, nColorChannels
);
518 const tools::Long nFirstIndex
= 0;
519 const tools::Long nLastIndex
= pReadAccess
->Width() - 1;
521 vcl::bitmap::generateStripRanges
<nThreadStrip
>(
522 nFirstIndex
, nLastIndex
,
523 [&](tools::Long
const nStart
, tools::Long
const nEnd
, bool const bLast
) {
526 auto pTask(std::make_unique
<BlurTask
>(pTag
, pBlurVerticalFn
,
527 aSharedData
, nStart
, nEnd
));
528 rShared
.pushTask(std::move(pTask
));
531 pBlurVerticalFn(aSharedData
, nStart
, nEnd
);
534 rShared
.waitUntilDone(pTag
);
539 SAL_WARN("vcl.gdi", "threaded bitmap blurring failed");
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
);
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
);
565 void stackBlur24(Bitmap
& rBitmap
, sal_Int32 nRadius
, sal_Int32 nComponentWidth
)
567 const bool bParallel
= true;
569 nRadius
= std::clamp
<sal_Int32
>(nRadius
, 2, 254);
570 const tools::Long nColorChannels
= 3; // 3 color channel
572 BlurRangeFn pBlurHorizontalFn
= stackBlurHorizontal
<SumFunction24
>;
573 BlurRangeFn pBlurVerticalFn
= stackBlurVertical
<SumFunction24
>;
575 runStackBlur(rBitmap
, nRadius
, nComponentWidth
, nColorChannels
, pBlurHorizontalFn
,
576 pBlurVerticalFn
, bParallel
);
579 void stackBlur8(Bitmap
& rBitmap
, sal_Int32 nRadius
, sal_Int32 nComponentWidth
)
581 const bool bParallel
= true;
583 nRadius
= std::clamp
<sal_Int32
>(nRadius
, 2, 254);
584 const tools::Long nColorChannels
= 1; // 1 color channel
586 BlurRangeFn pBlurHorizontalFn
= stackBlurHorizontal
<SumFunction8
>;
587 BlurRangeFn pBlurVerticalFn
= stackBlurVertical
<SumFunction8
>;
589 runStackBlur(rBitmap
, nRadius
, nComponentWidth
, nColorChannels
, pBlurHorizontalFn
,
590 pBlurVerticalFn
, bParallel
);
593 } // end anonymous namespace
596 * Implementation of stack blur - a fast Gaussian blur approximation.
597 * nRadius - blur radius, valid values are between 2 and 254
598 * bExtend - extend the bitmap in all directions by the radius
600 * Stack Blur Algorithm by Mario Klingemann <mario@quasimondo.com>
601 * (http://www.quasimondo.com/StackBlurForCanvas/StackBlurDemo.html)
603 * Additionally references and implementations:
604 * - Blur.js by Jacob Kelley
605 * (http://www.blurjs.com)
606 * - BlurEffectForAndroidDesign by Nicolas Pomepuy
607 * (https://github.com/PomepuyN/BlurEffectForAndroidDesign)
608 * - StackBluriOS by Thomas Landspurg
609 * (https://github.com/tomsoft1/StackBluriOS)
610 * - stackblur.cpp by Benjamin Yates
611 * (https://gist.github.com/benjamin9999/3809142)
612 * - stack blur in fog 2D graphic library by Petr Kobalicek
613 * (https://code.google.com/p/fog/)
616 BitmapFilterStackBlur::BitmapFilterStackBlur(sal_Int32 nRadius
)
621 BitmapFilterStackBlur::~BitmapFilterStackBlur() {}
623 BitmapEx
BitmapFilterStackBlur::execute(BitmapEx
const& rBitmapEx
) const
625 Bitmap aBitmap
= rBitmapEx
.GetBitmap();
626 Bitmap result
= filter(aBitmap
);
627 return BitmapEx(result
, rBitmapEx
.GetAlpha());
630 Bitmap
BitmapFilterStackBlur::filter(Bitmap
const& rBitmap
) const
632 Bitmap
bitmapCopy(rBitmap
);
633 ScanlineFormat nScanlineFormat
;
635 Bitmap::ScopedReadAccess
pReadAccess(bitmapCopy
);
636 nScanlineFormat
= pReadAccess
->GetScanlineFormat();
639 if (nScanlineFormat
== ScanlineFormat::N24BitTcRgb
640 || nScanlineFormat
== ScanlineFormat::N24BitTcBgr
641 || nScanlineFormat
== ScanlineFormat::N32BitTcMask
642 || nScanlineFormat
== ScanlineFormat::N32BitTcBgra
)
644 int nComponentWidth
= (nScanlineFormat
== ScanlineFormat::N32BitTcMask
645 || nScanlineFormat
== ScanlineFormat::N32BitTcBgra
)
649 stackBlur24(bitmapCopy
, mnRadius
, nComponentWidth
);
651 else if (nScanlineFormat
== ScanlineFormat::N8BitPal
)
653 int nComponentWidth
= 1;
655 stackBlur8(bitmapCopy
, mnRadius
, nComponentWidth
);
661 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */