diff options
Diffstat (limited to 'vcl/source/bitmap/BitmapFilterStackBlur.cxx')
-rw-r--r-- | vcl/source/bitmap/BitmapFilterStackBlur.cxx | 658 |
1 files changed, 658 insertions, 0 deletions
diff --git a/vcl/source/bitmap/BitmapFilterStackBlur.cxx b/vcl/source/bitmap/BitmapFilterStackBlur.cxx new file mode 100644 index 000000000..eeb5810f5 --- /dev/null +++ b/vcl/source/bitmap/BitmapFilterStackBlur.cxx @@ -0,0 +1,658 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + */ + +#include <vcl/BitmapFilterStackBlur.hxx> +#include <bitmap/BitmapWriteAccess.hxx> +#include <sal/log.hxx> + +#include <comphelper/threadpool.hxx> + +namespace +{ +const sal_Int16 constMultiplyTable[255] + = { 512, 512, 456, 512, 328, 456, 335, 512, 405, 328, 271, 456, 388, 335, 292, 512, 454, + 405, 364, 328, 298, 271, 496, 456, 420, 388, 360, 335, 312, 292, 273, 512, 482, 454, + 428, 405, 383, 364, 345, 328, 312, 298, 284, 271, 259, 496, 475, 456, 437, 420, 404, + 388, 374, 360, 347, 335, 323, 312, 302, 292, 282, 273, 265, 512, 497, 482, 468, 454, + 441, 428, 417, 405, 394, 383, 373, 364, 354, 345, 337, 328, 320, 312, 305, 298, 291, + 284, 278, 271, 265, 259, 507, 496, 485, 475, 465, 456, 446, 437, 428, 420, 412, 404, + 396, 388, 381, 374, 367, 360, 354, 347, 341, 335, 329, 323, 318, 312, 307, 302, 297, + 292, 287, 282, 278, 273, 269, 265, 261, 512, 505, 497, 489, 482, 475, 468, 461, 454, + 447, 441, 435, 428, 422, 417, 411, 405, 399, 394, 389, 383, 378, 373, 368, 364, 359, + 354, 350, 345, 341, 337, 332, 328, 324, 320, 316, 312, 309, 305, 301, 298, 294, 291, + 287, 284, 281, 278, 274, 271, 268, 265, 262, 259, 257, 507, 501, 496, 491, 485, 480, + 475, 470, 465, 460, 456, 451, 446, 442, 437, 433, 428, 424, 420, 416, 412, 408, 404, + 400, 396, 392, 388, 385, 381, 377, 374, 370, 367, 363, 360, 357, 354, 350, 347, 344, + 341, 338, 335, 332, 329, 326, 323, 320, 318, 315, 312, 310, 307, 304, 302, 299, 297, + 294, 292, 289, 287, 285, 282, 280, 278, 275, 273, 271, 269, 267, 265, 263, 261, 259 }; + +const sal_Int16 constShiftTable[255] + = { 9, 11, 12, 13, 13, 14, 14, 15, 15, 15, 15, 16, 16, 16, 16, 17, 17, 17, 17, 17, 17, 17, + 18, 18, 18, 18, 18, 18, 18, 18, 18, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 23, 23, 23, 23, 23, + 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, + 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, + 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24 }; + +struct BlurSharedData +{ + BitmapReadAccess* mpReadAccess; + BitmapWriteAccess* mpWriteAccess; + sal_Int32 mnRadius; + sal_Int32 mnComponentWidth; + sal_Int32 mnDiv; + sal_Int32 mnColorChannels; + + BlurSharedData(BitmapReadAccess* pReadAccess, BitmapWriteAccess* pWriteAccess, + sal_Int32 aRadius, sal_Int32 nComponentWidth, sal_Int32 nColorChannels) + : mpReadAccess(pReadAccess) + , mpWriteAccess(pWriteAccess) + , mnRadius(aRadius) + , mnComponentWidth(nComponentWidth) + , mnDiv(aRadius + aRadius + 1) + , mnColorChannels(nColorChannels) + { + } +}; + +struct BlurArrays +{ + BlurSharedData maShared; + + std::vector<sal_uInt8> maStackBuffer; + std::vector<sal_Int32> maPositionTable; + std::vector<sal_Int32> maWeightTable; + + std::vector<sal_Int32> mnSumVector; + std::vector<sal_Int32> mnInSumVector; + std::vector<sal_Int32> mnOutSumVector; + + BlurArrays(BlurSharedData const& rShared) + : maShared(rShared) + , maStackBuffer(maShared.mnDiv * maShared.mnComponentWidth) + , maPositionTable(maShared.mnDiv) + , maWeightTable(maShared.mnDiv) + , mnSumVector(maShared.mnColorChannels) + , mnInSumVector(maShared.mnColorChannels) + , mnOutSumVector(maShared.mnColorChannels) + { + } + + void initializeWeightAndPositions(sal_Int32 nLastIndex) + { + for (sal_Int32 i = 0; i < maShared.mnDiv; i++) + { + maPositionTable[i] = std::clamp(i - maShared.mnRadius, sal_Int32(0), nLastIndex); + maWeightTable[i] = maShared.mnRadius + 1 - std::abs(i - maShared.mnRadius); + } + } + + sal_Int32 getMultiplyValue() const + { + return static_cast<sal_Int32>(constMultiplyTable[maShared.mnRadius]); + } + + sal_Int32 getShiftValue() const + { + return static_cast<sal_Int32>(constShiftTable[maShared.mnRadius]); + } +}; + +typedef void (*BlurRangeFn)(BlurSharedData const& rShared, sal_Int32 nStartY, sal_Int32 nEndY); + +class BlurTask : public comphelper::ThreadTask +{ + BlurRangeFn mpBlurFunction; + BlurSharedData& mrShared; + sal_Int32 mnStartY; + sal_Int32 mnEndY; + +public: + explicit BlurTask(const std::shared_ptr<comphelper::ThreadTaskTag>& pTag, + BlurRangeFn pBlurFunction, BlurSharedData& rShared, sal_Int32 nStartY, + sal_Int32 nEndY) + : comphelper::ThreadTask(pTag) + , mpBlurFunction(pBlurFunction) + , mrShared(rShared) + , mnStartY(nStartY) + , mnEndY(nEndY) + { + } + + virtual void doWork() override { mpBlurFunction(mrShared, mnStartY, mnEndY); } +}; + +struct SumFunction24 +{ + static inline void add(sal_Int32*& pValue1, sal_Int32 nConstant) + { + pValue1[0] += nConstant; + pValue1[1] += nConstant; + pValue1[2] += nConstant; + } + + static inline void set(sal_Int32*& pValue1, sal_Int32 nConstant) + { + pValue1[0] = nConstant; + pValue1[1] = nConstant; + pValue1[2] = nConstant; + } + + static inline void add(sal_Int32*& pValue1, const sal_uInt8* pValue2) + { + pValue1[0] += pValue2[0]; + pValue1[1] += pValue2[1]; + pValue1[2] += pValue2[2]; + } + + static inline void add(sal_Int32*& pValue1, const sal_Int32* pValue2) + { + pValue1[0] += pValue2[0]; + pValue1[1] += pValue2[1]; + pValue1[2] += pValue2[2]; + } + + static inline void sub(sal_Int32*& pValue1, const sal_uInt8* pValue2) + { + pValue1[0] -= pValue2[0]; + pValue1[1] -= pValue2[1]; + pValue1[2] -= pValue2[2]; + } + + static inline void sub(sal_Int32*& pValue1, const sal_Int32* pValue2) + { + pValue1[0] -= pValue2[0]; + pValue1[1] -= pValue2[1]; + pValue1[2] -= pValue2[2]; + } + + static inline void assignPtr(sal_uInt8*& pValue1, const sal_uInt8* pValue2) + { + pValue1[0] = pValue2[0]; + pValue1[1] = pValue2[1]; + pValue1[2] = pValue2[2]; + } + + static inline void assignMulAndShr(sal_uInt8*& result, const sal_Int32* sum, sal_Int32 multiply, + sal_Int32 shift) + { + result[0] = (multiply * sum[0]) >> shift; + result[1] = (multiply * sum[1]) >> shift; + result[2] = (multiply * sum[2]) >> shift; + } +}; + +struct SumFunction8 +{ + static inline void add(sal_Int32*& pValue1, sal_Int32 nConstant) { pValue1[0] += nConstant; } + + static inline void set(sal_Int32*& pValue1, sal_Int32 nConstant) { pValue1[0] = nConstant; } + + static inline void add(sal_Int32*& pValue1, const sal_uInt8* pValue2) + { + pValue1[0] += pValue2[0]; + } + + static inline void add(sal_Int32*& pValue1, const sal_Int32* pValue2) + { + pValue1[0] += pValue2[0]; + } + + static inline void sub(sal_Int32*& pValue1, const sal_uInt8* pValue2) + { + pValue1[0] -= pValue2[0]; + } + + static inline void sub(sal_Int32*& pValue1, const sal_Int32* pValue2) + { + pValue1[0] -= pValue2[0]; + } + + static inline void assignPtr(sal_uInt8*& pValue1, const sal_uInt8* pValue2) + { + pValue1[0] = pValue2[0]; + } + + static inline void assignMulAndShr(sal_uInt8*& result, const sal_Int32* sum, sal_Int32 multiply, + sal_Int32 shift) + { + result[0] = (multiply * sum[0]) >> shift; + } +}; + +template <typename SumFunction> +void stackBlurHorizontal(BlurSharedData const& rShared, sal_Int32 nStart, sal_Int32 nEnd) +{ + BitmapReadAccess* pReadAccess = rShared.mpReadAccess; + BitmapWriteAccess* pWriteAccess = rShared.mpWriteAccess; + + BlurArrays aArrays(rShared); + + sal_uInt8* pStack = aArrays.maStackBuffer.data(); + sal_uInt8* pStackPtr; + + sal_Int32 nWidth = pReadAccess->Width(); + sal_Int32 nLastIndexX = nWidth - 1; + + sal_Int32 nMultiplyValue = aArrays.getMultiplyValue(); + sal_Int32 nShiftValue = aArrays.getShiftValue(); + + sal_Int32 nRadius = rShared.mnRadius; + sal_Int32 nComponentWidth = rShared.mnComponentWidth; + sal_Int32 nDiv = rShared.mnDiv; + + Scanline pSourcePointer; + Scanline pDestinationPointer; + + sal_Int32 nXPosition; + sal_Int32 nStackIndex; + sal_Int32 nStackIndexStart; + sal_Int32 nWeight; + + aArrays.initializeWeightAndPositions(nLastIndexX); + + sal_Int32* nSum = aArrays.mnSumVector.data(); + sal_Int32* nInSum = aArrays.mnInSumVector.data(); + sal_Int32* nOutSum = aArrays.mnOutSumVector.data(); + + sal_Int32* pPositionPointer = aArrays.maPositionTable.data(); + sal_Int32* pWeightPointer = aArrays.maWeightTable.data(); + + for (sal_Int32 y = nStart; y <= nEnd; y++) + { + SumFunction::set(nSum, 0); + SumFunction::set(nInSum, 0); + SumFunction::set(nOutSum, 0); + + // Pre-initialize blur data for first pixel. + // aArrays.maPositionTable contains values like (for radius of 5): [0,0,0,0,0,0,1,2,3,4,5], + // which are used as pixels indices in the current row that we use to prepare information + // for the first pixel; aArrays.maWeightTable has [1,2,3,4,5,6,5,4,3,2,1]. Before looking at + // the first row pixel, we pretend to have processed fake previous pixels, as if the row was + // extended to the left with the same color as that of the first pixel. + for (sal_Int32 i = 0; i < nDiv; i++) + { + pSourcePointer = pReadAccess->GetScanline(y) + nComponentWidth * pPositionPointer[i]; + + pStackPtr = &pStack[nComponentWidth * i]; + + SumFunction::assignPtr(pStackPtr, pSourcePointer); + + nWeight = pWeightPointer[i]; + + SumFunction::add(nSum, pSourcePointer[0] * nWeight); + + if (i - nRadius > 0) + { + SumFunction::add(nInSum, pSourcePointer); + } + else + { + SumFunction::add(nOutSum, pSourcePointer); + } + } + + nStackIndex = nRadius; + nXPosition = std::min(nRadius, nLastIndexX); + + pSourcePointer = pReadAccess->GetScanline(y) + nComponentWidth * nXPosition; + + for (sal_Int32 x = 0; x < nWidth; x++) + { + pDestinationPointer = pWriteAccess->GetScanline(y) + nComponentWidth * x; + + SumFunction::assignMulAndShr(pDestinationPointer, nSum, nMultiplyValue, nShiftValue); + + SumFunction::sub(nSum, nOutSum); + + nStackIndexStart = nStackIndex + nDiv - nRadius; + if (nStackIndexStart >= nDiv) + { + nStackIndexStart -= nDiv; + } + pStackPtr = &pStack[nComponentWidth * nStackIndexStart]; + + SumFunction::sub(nOutSum, pStackPtr); + + if (nXPosition < nLastIndexX) + { + nXPosition++; + pSourcePointer = pReadAccess->GetScanline(y) + nComponentWidth * nXPosition; + } + + SumFunction::assignPtr(pStackPtr, pSourcePointer); + + SumFunction::add(nInSum, pSourcePointer); + + SumFunction::add(nSum, nInSum); + + nStackIndex++; + if (nStackIndex >= nDiv) + { + nStackIndex = 0; + } + + pStackPtr = &pStack[nStackIndex * nComponentWidth]; + + SumFunction::add(nOutSum, pStackPtr); + SumFunction::sub(nInSum, pStackPtr); + } + } +} + +template <typename SumFunction> +void stackBlurVertical(BlurSharedData const& rShared, sal_Int32 nStart, sal_Int32 nEnd) +{ + BitmapReadAccess* pReadAccess = rShared.mpReadAccess; + BitmapWriteAccess* pWriteAccess = rShared.mpWriteAccess; + + BlurArrays aArrays(rShared); + + sal_uInt8* pStack = aArrays.maStackBuffer.data(); + sal_uInt8* pStackPtr; + + sal_Int32 nHeight = pReadAccess->Height(); + sal_Int32 nLastIndexY = nHeight - 1; + + sal_Int32 nMultiplyValue = aArrays.getMultiplyValue(); + sal_Int32 nShiftValue = aArrays.getShiftValue(); + + sal_Int32 nRadius = rShared.mnRadius; + sal_Int32 nComponentWidth = rShared.mnComponentWidth; + sal_Int32 nDiv = rShared.mnDiv; + + Scanline pSourcePointer; + Scanline pDestinationPointer; + + sal_Int32 nYPosition; + sal_Int32 nStackIndex; + sal_Int32 nStackIndexStart; + sal_Int32 nWeight; + + aArrays.initializeWeightAndPositions(nLastIndexY); + + sal_Int32* nSum = aArrays.mnSumVector.data(); + sal_Int32* nInSum = aArrays.mnInSumVector.data(); + sal_Int32* nOutSum = aArrays.mnOutSumVector.data(); + sal_Int32* pPositionPointer = aArrays.maPositionTable.data(); + sal_Int32* pWeightPointer = aArrays.maWeightTable.data(); + + for (sal_Int32 x = nStart; x <= nEnd; x++) + { + SumFunction::set(nSum, 0); + SumFunction::set(nInSum, 0); + SumFunction::set(nOutSum, 0); + + // Pre-initialize blur data for first pixel. + // aArrays.maPositionTable contains values like (for radius of 5): [0,0,0,0,0,0,1,2,3,4,5], + // which are used as pixels indices in the current column that we use to prepare information + // for the first pixel; aArrays.maWeightTable has [1,2,3,4,5,6,5,4,3,2,1]. Before looking at + // the first column pixels, we pretend to have processed fake previous pixels, as if the + // column was extended to the top with the same color as that of the first pixel. + for (sal_Int32 i = 0; i < nDiv; i++) + { + pSourcePointer = pReadAccess->GetScanline(pPositionPointer[i]) + nComponentWidth * x; + + pStackPtr = &pStack[nComponentWidth * i]; + + SumFunction::assignPtr(pStackPtr, pSourcePointer); + + nWeight = pWeightPointer[i]; + + SumFunction::add(nSum, pSourcePointer[0] * nWeight); + + if (i - nRadius > 0) + { + SumFunction::add(nInSum, pSourcePointer); + } + else + { + SumFunction::add(nOutSum, pSourcePointer); + } + } + + nStackIndex = nRadius; + nYPosition = std::min(nRadius, nLastIndexY); + + pSourcePointer = pReadAccess->GetScanline(nYPosition) + nComponentWidth * x; + + for (sal_Int32 y = 0; y < nHeight; y++) + { + pDestinationPointer = pWriteAccess->GetScanline(y) + nComponentWidth * x; + + SumFunction::assignMulAndShr(pDestinationPointer, nSum, nMultiplyValue, nShiftValue); + + SumFunction::sub(nSum, nOutSum); + + nStackIndexStart = nStackIndex + nDiv - nRadius; + if (nStackIndexStart >= nDiv) + { + nStackIndexStart -= nDiv; + } + pStackPtr = &pStack[nComponentWidth * nStackIndexStart]; + + SumFunction::sub(nOutSum, pStackPtr); + + if (nYPosition < nLastIndexY) + { + nYPosition++; + pSourcePointer = pReadAccess->GetScanline(nYPosition) + nComponentWidth * x; + } + + SumFunction::assignPtr(pStackPtr, pSourcePointer); + SumFunction::add(nInSum, pSourcePointer); + SumFunction::add(nSum, nInSum); + + nStackIndex++; + if (nStackIndex >= nDiv) + { + nStackIndex = 0; + } + + pStackPtr = &pStack[nStackIndex * nComponentWidth]; + + SumFunction::add(nOutSum, pStackPtr); + SumFunction::sub(nInSum, pStackPtr); + } + } +} + +constexpr sal_Int32 nThreadStrip = 16; + +void runStackBlur(Bitmap& rBitmap, const sal_Int32 nRadius, const sal_Int32 nComponentWidth, + const sal_Int32 nColorChannels, BlurRangeFn pBlurHorizontalFn, + BlurRangeFn pBlurVerticalFn, const bool bParallel) +{ + if (bParallel) + { + try + { + comphelper::ThreadPool& rShared = comphelper::ThreadPool::getSharedOptimalPool(); + auto pTag = comphelper::ThreadPool::createThreadTaskTag(); + + { + Bitmap::ScopedReadAccess pReadAccess(rBitmap); + BitmapScopedWriteAccess pWriteAccess(rBitmap); + BlurSharedData aSharedData(pReadAccess.get(), pWriteAccess.get(), nRadius, + nComponentWidth, nColorChannels); + + const sal_Int32 nFirstIndex = 0; + const sal_Int32 nLastIndex = pReadAccess->Height() - 1; + + vcl::bitmap::generateStripRanges<nThreadStrip>( + nFirstIndex, nLastIndex, + [&](sal_Int32 const nStart, sal_Int32 const nEnd, bool const bLast) { + if (!bLast) + { + auto pTask(std::make_unique<BlurTask>(pTag, pBlurHorizontalFn, + aSharedData, nStart, nEnd)); + rShared.pushTask(std::move(pTask)); + } + else + pBlurHorizontalFn(aSharedData, nStart, nEnd); + }); + rShared.waitUntilDone(pTag); + } + { + Bitmap::ScopedReadAccess pReadAccess(rBitmap); + BitmapScopedWriteAccess pWriteAccess(rBitmap); + BlurSharedData aSharedData(pReadAccess.get(), pWriteAccess.get(), nRadius, + nComponentWidth, nColorChannels); + + const sal_Int32 nFirstIndex = 0; + const sal_Int32 nLastIndex = pReadAccess->Width() - 1; + + vcl::bitmap::generateStripRanges<nThreadStrip>( + nFirstIndex, nLastIndex, + [&](sal_Int32 const nStart, sal_Int32 const nEnd, bool const bLast) { + if (!bLast) + { + auto pTask(std::make_unique<BlurTask>(pTag, pBlurVerticalFn, + aSharedData, nStart, nEnd)); + rShared.pushTask(std::move(pTask)); + } + else + pBlurVerticalFn(aSharedData, nStart, nEnd); + }); + + rShared.waitUntilDone(pTag); + } + } + catch (...) + { + SAL_WARN("vcl.gdi", "threaded bitmap blurring failed"); + } + } + else + { + { + Bitmap::ScopedReadAccess pReadAccess(rBitmap); + BitmapScopedWriteAccess pWriteAccess(rBitmap); + BlurSharedData aSharedData(pReadAccess.get(), pWriteAccess.get(), nRadius, + nComponentWidth, nColorChannels); + sal_Int32 nFirstIndex = 0; + sal_Int32 nLastIndex = pReadAccess->Height() - 1; + pBlurHorizontalFn(aSharedData, nFirstIndex, nLastIndex); + } + { + Bitmap::ScopedReadAccess pReadAccess(rBitmap); + BitmapScopedWriteAccess pWriteAccess(rBitmap); + BlurSharedData aSharedData(pReadAccess.get(), pWriteAccess.get(), nRadius, + nComponentWidth, nColorChannels); + sal_Int32 nFirstIndex = 0; + sal_Int32 nLastIndex = pReadAccess->Width() - 1; + pBlurVerticalFn(aSharedData, nFirstIndex, nLastIndex); + } + } +} + +void stackBlur24(Bitmap& rBitmap, sal_Int32 nRadius, sal_Int32 nComponentWidth) +{ + const bool bParallel = true; + // Limit radius + nRadius = std::clamp<sal_Int32>(nRadius, 2, 254); + const sal_Int32 nColorChannels = 3; // 3 color channel + + BlurRangeFn pBlurHorizontalFn = stackBlurHorizontal<SumFunction24>; + BlurRangeFn pBlurVerticalFn = stackBlurVertical<SumFunction24>; + + runStackBlur(rBitmap, nRadius, nComponentWidth, nColorChannels, pBlurHorizontalFn, + pBlurVerticalFn, bParallel); +} + +void stackBlur8(Bitmap& rBitmap, sal_Int32 nRadius, sal_Int32 nComponentWidth) +{ + const bool bParallel = true; + // Limit radius + nRadius = std::clamp<sal_Int32>(nRadius, 2, 254); + const sal_Int32 nColorChannels = 1; // 1 color channel + + BlurRangeFn pBlurHorizontalFn = stackBlurHorizontal<SumFunction8>; + BlurRangeFn pBlurVerticalFn = stackBlurVertical<SumFunction8>; + + runStackBlur(rBitmap, nRadius, nComponentWidth, nColorChannels, pBlurHorizontalFn, + pBlurVerticalFn, bParallel); +} + +} // end anonymous namespace + +/** + * Implementation of stack blur - a fast Gaussian blur approximation. + * nRadius - blur radius, valid values are between 2 and 254 + * bExtend - extend the bitmap in all directions by the radius + * + * Stack Blur Algorithm by Mario Klingemann <mario@quasimondo.com> + * (http://www.quasimondo.com/StackBlurForCanvas/StackBlurDemo.html) + * + * Additionally references and implementations: + * - Blur.js by Jacob Kelley + * (http://www.blurjs.com) + * - BlurEffectForAndroidDesign by Nicolas Pomepuy + * (https://github.com/PomepuyN/BlurEffectForAndroidDesign) + * - StackBluriOS by Thomas Landspurg + * (https://github.com/tomsoft1/StackBluriOS) + * - stackblur.cpp by Benjamin Yates + * (https://gist.github.com/benjamin9999/3809142) + * - stack blur in fog 2D graphic library by Petr Kobalicek + * (https://code.google.com/p/fog/) + * + */ +BitmapFilterStackBlur::BitmapFilterStackBlur(sal_Int32 nRadius) + : mnRadius(nRadius) +{ +} + +BitmapFilterStackBlur::~BitmapFilterStackBlur() {} + +BitmapEx BitmapFilterStackBlur::execute(BitmapEx const& rBitmapEx) const +{ + Bitmap aBitmap = rBitmapEx.GetBitmap(); + Bitmap result = filter(aBitmap); + return BitmapEx(result, rBitmapEx.GetAlpha()); +} + +Bitmap BitmapFilterStackBlur::filter(Bitmap const& rBitmap) const +{ + Bitmap bitmapCopy(rBitmap); + ScanlineFormat nScanlineFormat; + { + Bitmap::ScopedReadAccess pReadAccess(bitmapCopy); + nScanlineFormat = pReadAccess ? pReadAccess->GetScanlineFormat() : ScanlineFormat::NONE; + } + + if (nScanlineFormat == ScanlineFormat::N24BitTcRgb + || nScanlineFormat == ScanlineFormat::N24BitTcBgr + || nScanlineFormat == ScanlineFormat::N32BitTcMask + || nScanlineFormat == ScanlineFormat::N32BitTcBgra) + { + int nComponentWidth = (nScanlineFormat == ScanlineFormat::N32BitTcMask + || nScanlineFormat == ScanlineFormat::N32BitTcBgra) + ? 4 + : 3; + + stackBlur24(bitmapCopy, mnRadius, nComponentWidth); + } + else if (nScanlineFormat == ScanlineFormat::N8BitPal) + { + int nComponentWidth = 1; + + stackBlur8(bitmapCopy, mnRadius, nComponentWidth); + } + + return bitmapCopy; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |