Branch libreoffice-5-0-4
[LibreOffice.git] / vcl / source / bitmap / BitmapFilterStackBlur.cxx
blob975f3ad0fa00a495c715794d0998c4c229acbd07
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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/.
9 */
11 #include <vcl/BitmapFilterStackBlur.hxx>
12 #include <vcl/bmpacc.hxx>
14 namespace
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
57 class BlurSharedData
59 public:
60 long mnRadius;
61 long mnComponentWidth;
62 long mnColorChannels;
63 long mnDiv;
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)
73 : mnRadius(aRadius)
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]);
100 long getShiftValue()
102 return static_cast<long>(constShiftTable[mnRadius]);
106 struct SumFunction24
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;
165 struct SumFunction8
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;
232 long nXPosition;
233 long nStackIndex;
234 long nStackIndexStart;
235 long nWeight;
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);
263 if (i - nRadius > 0)
265 SumFunction::add(nInSum, pSourcePointer);
267 else
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)
297 nXPosition++;
298 pSourcePointer = pReadAccess->GetScanline(y) + nComponentWidth * nXPosition;
301 SumFunction::assignPtr(pStackPtr, pSourcePointer);
303 SumFunction::add(nInSum, pSourcePointer);
305 SumFunction::add(nSum, nInSum);
307 nStackIndex++;
308 if (nStackIndex >= nDiv)
310 nStackIndex = 0;
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;
345 long nYPosition;
346 long nStackIndex;
347 long nStackIndexStart;
348 long nWeight;
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);
376 if (i - nRadius > 0)
378 SumFunction::add(nInSum, pSourcePointer);
380 else
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)
411 nYPosition++;
412 pSourcePointer = pReadAccess->GetScanline(nYPosition) + nComponentWidth * x;
415 SumFunction::assignPtr(pStackPtr, pSourcePointer);
417 SumFunction::add(nInSum, pSourcePointer);
419 SumFunction::add(nSum, nInSum);
421 nStackIndex++;
422 if (nStackIndex >= nDiv)
424 nStackIndex = 0;
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)
438 // Limit radius
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)
460 // Limit radius
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);
504 else
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)
538 : mnRadius(nRadius)
539 , mbExtend(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;
559 if (mbExtend)
561 centerExtendBitmap(rBitmap, mnRadius, COL_WHITE);
564 stackBlur24(rBitmap, mnRadius, nComponentWidth);
566 else if (nScanlineFormat == BMP_FORMAT_8BIT_PAL)
568 int nComponentWidth = 1;
570 if (mbExtend)
572 centerExtendBitmap(rBitmap, mnRadius, COL_WHITE);
575 stackBlur8(rBitmap, mnRadius, nComponentWidth);
578 return true;
581 bool BitmapFilterStackBlur::filter(BitmapEx& rBitmapEx)
583 Bitmap aBitmap = rBitmapEx.GetBitmap();
584 filter(aBitmap);
585 rBitmapEx = BitmapEx(aBitmap);
587 return true;
590 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */