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/bmpacc.hxx>
17 static const sal_Int16 constMultiplyTable
[255] =
19 512, 512, 456, 512, 328, 456, 335, 512, 405, 328, 271, 456, 388, 335, 292, 512,
20 454, 405, 364, 328, 298, 271, 496, 456, 420, 388, 360, 335, 312, 292, 273, 512,
21 482, 454, 428, 405, 383, 364, 345, 328, 312, 298, 284, 271, 259, 496, 475, 456,
22 437, 420, 404, 388, 374, 360, 347, 335, 323, 312, 302, 292, 282, 273, 265, 512,
23 497, 482, 468, 454, 441, 428, 417, 405, 394, 383, 373, 364, 354, 345, 337, 328,
24 320, 312, 305, 298, 291, 284, 278, 271, 265, 259, 507, 496, 485, 475, 465, 456,
25 446, 437, 428, 420, 412, 404, 396, 388, 381, 374, 367, 360, 354, 347, 341, 335,
26 329, 323, 318, 312, 307, 302, 297, 292, 287, 282, 278, 273, 269, 265, 261, 512,
27 505, 497, 489, 482, 475, 468, 461, 454, 447, 441, 435, 428, 422, 417, 411, 405,
28 399, 394, 389, 383, 378, 373, 368, 364, 359, 354, 350, 345, 341, 337, 332, 328,
29 324, 320, 316, 312, 309, 305, 301, 298, 294, 291, 287, 284, 281, 278, 274, 271,
30 268, 265, 262, 259, 257, 507, 501, 496, 491, 485, 480, 475, 470, 465, 460, 456,
31 451, 446, 442, 437, 433, 428, 424, 420, 416, 412, 408, 404, 400, 396, 392, 388,
32 385, 381, 377, 374, 370, 367, 363, 360, 357, 354, 350, 347, 344, 341, 338, 335,
33 332, 329, 326, 323, 320, 318, 315, 312, 310, 307, 304, 302, 299, 297, 294, 292,
34 289, 287, 285, 282, 280, 278, 275, 273, 271, 269, 267, 265, 263, 261, 259
37 static const sal_Int16 constShiftTable
[255] =
39 9, 11, 12, 13, 13, 14, 14, 15, 15, 15, 15, 16, 16, 16, 16, 17,
40 17, 17, 17, 17, 17, 17, 18, 18, 18, 18, 18, 18, 18, 18, 18, 19,
41 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 20, 20, 20,
42 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 21,
43 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21,
44 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 22, 22, 22, 22, 22, 22,
45 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22,
46 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 23,
47 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23,
48 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23,
49 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23,
50 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
51 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
52 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
53 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
54 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24
61 long mnComponentWidth
;
64 std::vector
<sal_uInt8
> maStackBuffer
;
65 std::vector
<long> maPositionTable
;
66 std::vector
<long> maWeightTable
;
68 std::vector
<long> mnSumVector
;
69 std::vector
<long> mnInSumVector
;
70 std::vector
<long> mnOutSumVector
;
72 BlurSharedData(long aRadius
, long nComponentWidth
, long nColorChannels
)
74 , mnComponentWidth(nComponentWidth
)
75 , mnColorChannels(nColorChannels
)
76 , mnDiv(aRadius
+ aRadius
+ 1)
77 , maStackBuffer(mnDiv
* mnComponentWidth
)
78 , maPositionTable(mnDiv
)
79 , maWeightTable(mnDiv
)
80 , mnSumVector(mnColorChannels
)
81 , mnInSumVector(mnColorChannels
)
82 , mnOutSumVector(mnColorChannels
)
86 void calculateWeightAndPositions(long nLastIndex
)
88 for (long i
= 0; i
< mnDiv
; i
++)
90 maPositionTable
[i
] = std::min(nLastIndex
, std::max(0L, i
- mnRadius
));
91 maWeightTable
[i
] = mnRadius
+ 1 - std::abs(i
- mnRadius
);
95 long getMultiplyValue()
97 return static_cast<long>(constMultiplyTable
[mnRadius
]);
102 return static_cast<long>(constShiftTable
[mnRadius
]);
108 static inline void add(long*& pValue1
, long nConstant
)
110 pValue1
[0] += nConstant
;
111 pValue1
[1] += nConstant
;
112 pValue1
[2] += nConstant
;
115 static inline void set(long*& pValue1
, long nConstant
)
117 pValue1
[0] = nConstant
;
118 pValue1
[1] = nConstant
;
119 pValue1
[2] = nConstant
;
122 static inline void add(long*& pValue1
, sal_uInt8
*& pValue2
)
124 pValue1
[0] += pValue2
[0];
125 pValue1
[1] += pValue2
[1];
126 pValue1
[2] += pValue2
[2];
129 static inline void add(long*& pValue1
, long*& pValue2
)
131 pValue1
[0] += pValue2
[0];
132 pValue1
[1] += pValue2
[1];
133 pValue1
[2] += pValue2
[2];
136 static inline void sub(long*& pValue1
, sal_uInt8
*& pValue2
)
138 pValue1
[0] -= pValue2
[0];
139 pValue1
[1] -= pValue2
[1];
140 pValue1
[2] -= pValue2
[2];
143 static inline void sub(long*& pValue1
, long*& pValue2
)
145 pValue1
[0] -= pValue2
[0];
146 pValue1
[1] -= pValue2
[1];
147 pValue1
[2] -= pValue2
[2];
150 static inline void assignPtr(sal_uInt8
*& pValue1
, sal_uInt8
*& pValue2
)
152 pValue1
[0] = pValue2
[0];
153 pValue1
[1] = pValue2
[1];
154 pValue1
[2] = pValue2
[2];
157 static inline void assignMulAndShr(sal_uInt8
*& result
, long*& sum
, long multiply
, long shift
)
159 result
[0] = (multiply
* sum
[0]) >> shift
;
160 result
[1] = (multiply
* sum
[1]) >> shift
;
161 result
[2] = (multiply
* sum
[2]) >> shift
;
167 static inline void add(long*& pValue1
, long nConstant
)
169 pValue1
[0] += nConstant
;
172 static inline void set(long*& pValue1
, long nConstant
)
174 pValue1
[0] = nConstant
;
177 static inline void add(long*& pValue1
, sal_uInt8
*& pValue2
)
179 pValue1
[0] += pValue2
[0];
182 static inline void add(long*& pValue1
, long*& pValue2
)
184 pValue1
[0] += pValue2
[0];
187 static inline void sub(long*& pValue1
, sal_uInt8
*& pValue2
)
189 pValue1
[0] -= pValue2
[0];
192 static inline void sub(long*& pValue1
, long*& pValue2
)
194 pValue1
[0] -= pValue2
[0];
197 static inline void assignPtr(sal_uInt8
*& pValue1
, sal_uInt8
*& pValue2
)
199 pValue1
[0] = pValue2
[0];
202 static inline void assignMulAndShr(sal_uInt8
*& result
, long*& sum
, long multiply
, long shift
)
204 result
[0] = (multiply
* sum
[0]) >> shift
;
208 template<typename SumFunction
>
209 void stackBlurHorizontal(
210 BitmapReadAccess
* pReadAccess
,
211 BitmapWriteAccess
* pWriteAccess
,
212 BlurSharedData
& rShared
)
214 long nWidth
= pReadAccess
->Width();
215 long nHeight
= pReadAccess
->Height();
217 sal_uInt8
* pStack
= rShared
.maStackBuffer
.data();
218 sal_uInt8
* pStackPtr
;
220 long nLastIndexX
= nWidth
- 1;
222 long nMultiplyValue
= rShared
.getMultiplyValue();
223 long nShiftValue
= rShared
.getShiftValue();
225 long nRadius
= rShared
.mnRadius
;
226 long nComponentWidth
= rShared
.mnComponentWidth
;
227 long nDiv
= rShared
.mnDiv
;
229 Scanline pSourcePointer
;
230 Scanline pDestinationPointer
;
234 long nStackIndexStart
;
237 long* nSum
= rShared
.mnSumVector
.data();
238 long* nInSum
= rShared
.mnInSumVector
.data();
239 long* nOutSum
= rShared
.mnOutSumVector
.data();
241 rShared
.calculateWeightAndPositions(nLastIndexX
);
242 long* pPositionPointer
= rShared
.maPositionTable
.data();
243 long* pWeightPointer
= rShared
.maWeightTable
.data();
245 for (long y
= 0; y
< nHeight
; y
++)
247 SumFunction::set(nSum
, 0L);
248 SumFunction::set(nInSum
, 0L);
249 SumFunction::set(nOutSum
, 0L);
251 for (long i
= 0; i
< nDiv
; i
++)
253 pSourcePointer
= pReadAccess
->GetScanline(pPositionPointer
[i
]);
255 pStackPtr
= &pStack
[nComponentWidth
* i
];
257 SumFunction::assignPtr(pStackPtr
, pSourcePointer
);
259 nWeight
= pWeightPointer
[i
];
261 SumFunction::add(nSum
, pSourcePointer
[0] * nWeight
);
265 SumFunction::add(nInSum
, pSourcePointer
);
269 SumFunction::add(nOutSum
, pSourcePointer
);
273 nStackIndex
= nRadius
;
274 nXPosition
= std::min(nRadius
, nLastIndexX
);
276 pSourcePointer
= pReadAccess
->GetScanline(y
) + nComponentWidth
* nXPosition
;
278 for (long x
= 0; x
< nWidth
; x
++)
280 pDestinationPointer
= pWriteAccess
->GetScanline(y
) + nComponentWidth
* x
;
282 SumFunction::assignMulAndShr(pDestinationPointer
, nSum
, nMultiplyValue
, nShiftValue
);
284 SumFunction::sub(nSum
, nOutSum
);
286 nStackIndexStart
= nStackIndex
+ nDiv
- nRadius
;
287 if (nStackIndexStart
>= nDiv
)
289 nStackIndexStart
-= nDiv
;
291 pStackPtr
= &pStack
[nComponentWidth
* nStackIndexStart
];
293 SumFunction::sub(nOutSum
, pStackPtr
);
295 if (nXPosition
< nLastIndexX
)
298 pSourcePointer
= pReadAccess
->GetScanline(y
) + nComponentWidth
* nXPosition
;
301 SumFunction::assignPtr(pStackPtr
, pSourcePointer
);
303 SumFunction::add(nInSum
, pSourcePointer
);
305 SumFunction::add(nSum
, nInSum
);
308 if (nStackIndex
>= nDiv
)
313 pStackPtr
= &pStack
[nStackIndex
* nComponentWidth
];
315 SumFunction::add(nOutSum
, pStackPtr
);
316 SumFunction::sub(nInSum
, pStackPtr
);
321 template<typename SumFunction
>
322 void stackBlurVertical(
323 BitmapReadAccess
* pReadAccess
,
324 BitmapWriteAccess
* pWriteAccess
,
325 BlurSharedData
& rShared
)
327 long nWidth
= pReadAccess
->Width();
328 long nHeight
= pReadAccess
->Height();
330 sal_uInt8
* pStack
= rShared
.maStackBuffer
.data();
331 sal_uInt8
* pStackPtr
;
333 long nLastIndexY
= nHeight
- 1;
335 long nMultiplyValue
= rShared
.getMultiplyValue();
336 long nShiftValue
= rShared
.getShiftValue();
338 long nRadius
= rShared
.mnRadius
;
339 long nComponentWidth
= rShared
.mnComponentWidth
;
340 long nDiv
= rShared
.mnDiv
;
342 Scanline pSourcePointer
;
343 Scanline pDestinationPointer
;
347 long nStackIndexStart
;
350 long* nSum
= rShared
.mnSumVector
.data();
351 long* nInSum
= rShared
.mnInSumVector
.data();
352 long* nOutSum
= rShared
.mnOutSumVector
.data();
354 rShared
.calculateWeightAndPositions(nLastIndexY
);
355 long* pPositionPointer
= rShared
.maPositionTable
.data();
356 long* pWeightPointer
= rShared
.maWeightTable
.data();
358 for (long x
= 0; x
< nWidth
; x
++)
360 SumFunction::set(nSum
, 0L);
361 SumFunction::set(nInSum
, 0L);
362 SumFunction::set(nOutSum
, 0L);
364 for (long i
= 0; i
< nDiv
; i
++)
366 pSourcePointer
= pReadAccess
->GetScanline(pPositionPointer
[i
]);
368 pStackPtr
= &pStack
[nComponentWidth
* i
];
370 SumFunction::assignPtr(pStackPtr
, pSourcePointer
);
372 nWeight
= pWeightPointer
[i
];
374 SumFunction::add(nSum
, pSourcePointer
[0] * nWeight
);
378 SumFunction::add(nInSum
, pSourcePointer
);
382 SumFunction::add(nOutSum
, pSourcePointer
);
386 nStackIndex
= nRadius
;
387 nYPosition
= std::min(nRadius
, nLastIndexY
);
389 pSourcePointer
= pReadAccess
->GetScanline(nYPosition
) + nComponentWidth
* x
;
391 for (long y
= 0; y
< nHeight
; y
++)
393 pDestinationPointer
= pWriteAccess
->GetScanline(y
) + nComponentWidth
* x
;
395 SumFunction::assignMulAndShr(pDestinationPointer
, nSum
, nMultiplyValue
, nShiftValue
);
397 SumFunction::sub(nSum
, nOutSum
);
400 nStackIndexStart
= nStackIndex
+ nDiv
- nRadius
;
401 if (nStackIndexStart
>= nDiv
)
403 nStackIndexStart
-= nDiv
;
405 pStackPtr
= &pStack
[nComponentWidth
* nStackIndexStart
];
407 SumFunction::sub(nOutSum
, pStackPtr
);
409 if (nYPosition
< nLastIndexY
)
412 pSourcePointer
= pReadAccess
->GetScanline(nYPosition
) + nComponentWidth
* x
;
415 SumFunction::assignPtr(pStackPtr
, pSourcePointer
);
417 SumFunction::add(nInSum
, pSourcePointer
);
419 SumFunction::add(nSum
, nInSum
);
422 if (nStackIndex
>= nDiv
)
427 pStackPtr
= &pStack
[nStackIndex
* nComponentWidth
];
429 SumFunction::add(nOutSum
, pStackPtr
);
431 SumFunction::sub(nInSum
, pStackPtr
);
436 void stackBlur24(Bitmap
& rBitmap
, sal_Int32 nRadius
, sal_Int32 nComponentWidth
)
439 nRadius
= std::min
<sal_Int32
>(254, std::max
<sal_Int32
>(2, nRadius
));
440 const long nColorChannels
= 3; // 3 color channel
441 BlurSharedData
aData(nRadius
, nComponentWidth
, nColorChannels
);
444 Bitmap::ScopedReadAccess
pReadAccess(rBitmap
);
445 Bitmap::ScopedWriteAccess
pWriteAccess(rBitmap
);
447 stackBlurHorizontal
<SumFunction24
>(pReadAccess
.get(), pWriteAccess
.get(), aData
);
451 Bitmap::ScopedReadAccess
pReadAccess(rBitmap
);
452 Bitmap::ScopedWriteAccess
pWriteAccess(rBitmap
);
454 stackBlurVertical
<SumFunction24
>(pReadAccess
.get(), pWriteAccess
.get(), aData
);
458 void stackBlur8(Bitmap
& rBitmap
, sal_Int32 nRadius
, sal_Int32 nComponentWidth
)
461 nRadius
= std::min
<sal_Int32
>(254, std::max
<sal_Int32
>(2, nRadius
));
462 const long nColorChannels
= 1; // 1 color channel
463 BlurSharedData
aData(nRadius
, nComponentWidth
, nColorChannels
);
466 Bitmap::ScopedReadAccess
pReadAccess(rBitmap
);
467 Bitmap::ScopedWriteAccess
pWriteAccess(rBitmap
);
469 stackBlurHorizontal
<SumFunction8
>(pReadAccess
.get(), pWriteAccess
.get(), aData
);
473 Bitmap::ScopedReadAccess
pReadAccess(rBitmap
);
474 Bitmap::ScopedWriteAccess
pWriteAccess(rBitmap
);
476 stackBlurVertical
<SumFunction8
>(pReadAccess
.get(), pWriteAccess
.get(), aData
);
480 void centerExtendBitmap(Bitmap
& rBitmap
, sal_Int32 nExtendSize
, Color aColor
)
482 const Size
& rSize
= rBitmap
.GetSizePixel();
483 const Size
aNewSize(rSize
.Width() + nExtendSize
* 2,
484 rSize
.Height() + nExtendSize
* 2);
486 Bitmap
aNewBitmap(aNewSize
, rBitmap
.GetBitCount());
489 Bitmap::ScopedReadAccess
pReadAccess(rBitmap
);
490 Bitmap::ScopedWriteAccess
pWriteAccess(aNewBitmap
);
492 long nWidthBorder
= nExtendSize
+ rSize
.Width();
493 long nHeightBorder
= nExtendSize
+ rSize
.Height();
495 for (int y
= 0; y
< aNewSize
.Height(); y
++)
497 for (int x
= 0; x
< aNewSize
.Width(); x
++)
499 if (y
< nExtendSize
|| y
>= nHeightBorder
500 || x
< nExtendSize
|| x
>= nWidthBorder
)
502 pWriteAccess
->SetPixel(y
, x
, aColor
);
506 pWriteAccess
->SetPixel(y
, x
, pReadAccess
->GetPixel(y
- nExtendSize
, x
- nExtendSize
));
511 rBitmap
= aNewBitmap
;
514 } // end anonymous namespace
517 * Implementation of stack blur - a fast Gaussian blur approximation.
518 * nRadius - blur radious, valid values are between 2 and 254
519 * bExtend - extend the bitmap in all directions by the radius
521 * Stack Blur Algorithm by Mario Klingemann <mario@quasimondo.com>
522 * (http://www.quasimondo.com/StackBlurForCanvas/StackBlurDemo.html)
524 * Additionally eferences and implementations:
525 * - Blur.js by Jacob Kelley
526 * (http://www.blurjs.com)
527 * - BlurEffectForAndroidDesign by Nicolas Pomepuy
528 * (https://github.com/PomepuyN/BlurEffectForAndroidDesign)
529 * - StackBluriOS by Thomas Landspurg
530 * (https://github.com/tomsoft1/StackBluriOS)
531 * - stackblur.cpp by Benjamin Yates
532 * (https://gist.github.com/benjamin9999/3809142)
533 * - stack blur in fog 2D graphic library by Petr Kobalicek
534 * (https://code.google.com/p/fog/)
537 BitmapFilterStackBlur::BitmapFilterStackBlur(sal_Int32 nRadius
, bool bExtend
)
542 BitmapFilterStackBlur::~BitmapFilterStackBlur()
545 bool BitmapFilterStackBlur::filter(Bitmap
& rBitmap
)
547 sal_uLong nScanlineFormat
;
549 Bitmap::ScopedReadAccess
pReadAccess(rBitmap
);
550 nScanlineFormat
= pReadAccess
->GetScanlineFormat();
553 if (nScanlineFormat
== BMP_FORMAT_24BIT_TC_RGB
||
554 nScanlineFormat
== BMP_FORMAT_24BIT_TC_BGR
||
555 nScanlineFormat
== BMP_FORMAT_32BIT_TC_MASK
)
557 int nComponentWidth
= (nScanlineFormat
== BMP_FORMAT_32BIT_TC_MASK
) ? 4 : 3;
561 centerExtendBitmap(rBitmap
, mnRadius
, COL_WHITE
);
564 stackBlur24(rBitmap
, mnRadius
, nComponentWidth
);
566 else if (nScanlineFormat
== BMP_FORMAT_8BIT_PAL
)
568 int nComponentWidth
= 1;
572 centerExtendBitmap(rBitmap
, mnRadius
, COL_WHITE
);
575 stackBlur8(rBitmap
, mnRadius
, nComponentWidth
);
581 bool BitmapFilterStackBlur::filter(BitmapEx
& rBitmapEx
)
583 Bitmap aBitmap
= rBitmapEx
.GetBitmap();
585 rBitmapEx
= BitmapEx(aBitmap
);
590 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */