summaryrefslogtreecommitdiffstats
path: root/vcl/source/bitmap
diff options
context:
space:
mode:
Diffstat (limited to 'vcl/source/bitmap')
-rw-r--r--vcl/source/bitmap/BitmapAlphaClampFilter.cxx45
-rw-r--r--vcl/source/bitmap/BitmapBasicMorphologyFilter.cxx360
-rw-r--r--vcl/source/bitmap/BitmapColorQuantizationFilter.cxx232
-rw-r--r--vcl/source/bitmap/BitmapColorizeFilter.cxx92
-rw-r--r--vcl/source/bitmap/BitmapConvolutionMatrixFilter.cxx208
-rw-r--r--vcl/source/bitmap/BitmapDisabledImageFilter.cxx61
-rw-r--r--vcl/source/bitmap/BitmapDuoToneFilter.cxx65
-rw-r--r--vcl/source/bitmap/BitmapEmbossGreyFilter.cxx158
-rw-r--r--vcl/source/bitmap/BitmapFastScaleFilter.cxx131
-rw-r--r--vcl/source/bitmap/BitmapFilterStackBlur.cxx629
-rw-r--r--vcl/source/bitmap/BitmapGaussianSeparableBlurFilter.cxx215
-rw-r--r--vcl/source/bitmap/BitmapInterpolateScaleFilter.cxx237
-rw-r--r--vcl/source/bitmap/BitmapLightenFilter.cxx69
-rw-r--r--vcl/source/bitmap/BitmapMedianFilter.cxx218
-rw-r--r--vcl/source/bitmap/BitmapMonochromeFilter.cxx101
-rw-r--r--vcl/source/bitmap/BitmapMosaicFilter.cxx185
-rw-r--r--vcl/source/bitmap/BitmapPopArtFilter.cxx97
-rw-r--r--vcl/source/bitmap/BitmapScaleConvolutionFilter.cxx391
-rw-r--r--vcl/source/bitmap/BitmapScaleSuperFilter.cxx1202
-rw-r--r--vcl/source/bitmap/BitmapSeparableUnsharpenFilter.cxx75
-rw-r--r--vcl/source/bitmap/BitmapSepiaFilter.cxx111
-rw-r--r--vcl/source/bitmap/BitmapSimpleColorQuantizationFilter.cxx113
-rw-r--r--vcl/source/bitmap/BitmapSmoothenFilter.cxx32
-rw-r--r--vcl/source/bitmap/BitmapSobelGreyFilter.cxx170
-rw-r--r--vcl/source/bitmap/BitmapSolarizeFilter.cxx71
-rw-r--r--vcl/source/bitmap/BitmapSymmetryCheck.cxx84
-rw-r--r--vcl/source/bitmap/BitmapTools.cxx1131
-rw-r--r--vcl/source/bitmap/Octree.cxx288
-rw-r--r--vcl/source/bitmap/bitmap.cxx892
-rw-r--r--vcl/source/bitmap/bitmapfilter.cxx58
-rw-r--r--vcl/source/bitmap/bitmappaint.cxx1146
-rw-r--r--vcl/source/bitmap/checksum.cxx154
-rw-r--r--vcl/source/bitmap/salbmp.cxx225
33 files changed, 9246 insertions, 0 deletions
diff --git a/vcl/source/bitmap/BitmapAlphaClampFilter.cxx b/vcl/source/bitmap/BitmapAlphaClampFilter.cxx
new file mode 100644
index 000000000..3db6b43e2
--- /dev/null
+++ b/vcl/source/bitmap/BitmapAlphaClampFilter.cxx
@@ -0,0 +1,45 @@
+/* -*- 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/bitmapex.hxx>
+#include <vcl/BitmapAlphaClampFilter.hxx>
+
+#include <bitmapwriteaccess.hxx>
+
+BitmapEx BitmapAlphaClampFilter::execute(BitmapEx const& rBitmapEx) const
+{
+ if (!rBitmapEx.IsTransparent())
+ return rBitmapEx;
+
+ AlphaMask aBitmapAlpha(rBitmapEx.GetAlpha());
+ {
+ AlphaScopedWriteAccess pWriteAlpha(aBitmapAlpha);
+ const Size aSize(rBitmapEx.GetSizePixel());
+
+ for (long nY = 0; nY < aSize.Height(); ++nY)
+ {
+ Scanline pScanAlpha = pWriteAlpha->GetScanline(nY);
+
+ for (long nX = 0; nX < aSize.Width(); ++nX)
+ {
+ BitmapColor aBitmapAlphaValue(pWriteAlpha->GetPixelFromData(pScanAlpha, nX));
+ if (aBitmapAlphaValue.GetIndex() > mcThreshold)
+ {
+ aBitmapAlphaValue.SetIndex(255);
+ pWriteAlpha->SetPixelOnData(pScanAlpha, nX, aBitmapAlphaValue);
+ }
+ }
+ }
+ }
+
+ return BitmapEx(rBitmapEx.GetBitmap(), aBitmapAlpha);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/bitmap/BitmapBasicMorphologyFilter.cxx b/vcl/source/bitmap/BitmapBasicMorphologyFilter.cxx
new file mode 100644
index 000000000..fe4f63f90
--- /dev/null
+++ b/vcl/source/bitmap/BitmapBasicMorphologyFilter.cxx
@@ -0,0 +1,360 @@
+/* -*- 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 <sal/config.h>
+
+#include <comphelper/threadpool.hxx>
+#include <sal/log.hxx>
+#include <vcl/bitmapaccess.hxx>
+#include <vcl/BitmapBasicMorphologyFilter.hxx>
+
+#include <bitmapwriteaccess.hxx>
+
+#include <algorithm>
+
+namespace
+{
+struct FilterSharedData
+{
+ BitmapReadAccess* mpReadAccess;
+ BitmapWriteAccess* mpWriteAccess;
+ long mnRadius;
+ sal_uInt8 mnOutsideVal;
+ Color maOutsideColor;
+
+ FilterSharedData(Bitmap::ScopedReadAccess& rReadAccess, BitmapScopedWriteAccess& rWriteAccess,
+ long nRadius, sal_uInt8 nOutsideVal)
+ : mpReadAccess(rReadAccess.get())
+ , mpWriteAccess(rWriteAccess.get())
+ , mnRadius(nRadius)
+ , mnOutsideVal(nOutsideVal)
+ , maOutsideColor(nOutsideVal, nOutsideVal, nOutsideVal, nOutsideVal)
+ {
+ }
+};
+
+// Black is foreground, white is background
+
+struct ErodeOp
+{
+ static sal_uInt8 apply(sal_uInt8 v1, sal_uInt8 v2) { return std::max(v1, v2); }
+ static constexpr sal_uInt8 initVal = 0;
+};
+
+struct DilateOp
+{
+ static sal_uInt8 apply(sal_uInt8 v1, sal_uInt8 v2) { return std::min(v1, v2); }
+ static constexpr sal_uInt8 initVal = SAL_MAX_UINT8;
+};
+
+// 8 bit per channel case
+
+template <typename MorphologyOp, int nComponentWidth> struct Value
+{
+ static constexpr int nWidthBytes = nComponentWidth / 8;
+ static_assert(nWidthBytes * 8 == nComponentWidth);
+
+ sal_uInt8 aResult[nWidthBytes];
+
+ // If we are at the start or at the end of the line, consider outside value
+ Value(FilterSharedData const& rShared, bool bLookOutside)
+ {
+ std::fill_n(aResult, nWidthBytes,
+ bLookOutside ? rShared.mnOutsideVal : MorphologyOp::initVal);
+ }
+
+ void apply(BitmapReadAccess* pReadAccess, long x, long y, sal_uInt8* pHint = nullptr)
+ {
+ sal_uInt8* pSource = (pHint ? pHint : pReadAccess->GetScanline(y)) + nWidthBytes * x;
+ std::transform(pSource, pSource + nWidthBytes, aResult, aResult, MorphologyOp::apply);
+ }
+
+ void copy(BitmapWriteAccess* pWriteAccess, long x, long y, sal_uInt8* pHint = nullptr)
+ {
+ sal_uInt8* pDest = (pHint ? pHint : pWriteAccess->GetScanline(y)) + nWidthBytes * x;
+ std::copy_n(aResult, nWidthBytes, pDest);
+ }
+};
+
+// Partial specializations for nComponentWidth == 0, using access' GetColor/SetPixel
+
+template <typename MorphologyOp> struct Value<MorphologyOp, 0>
+{
+ static constexpr Color initColor{ MorphologyOp::initVal, MorphologyOp::initVal,
+ MorphologyOp::initVal, MorphologyOp::initVal };
+
+ Color aResult;
+
+ // If we are at the start or at the end of the line, consider outside value
+ Value(FilterSharedData const& rShared, bool bLookOutside)
+ : aResult(bLookOutside ? rShared.maOutsideColor : initColor)
+ {
+ }
+
+ void apply(BitmapReadAccess* pReadAccess, long x, long y, sal_uInt8* /*pHint*/ = nullptr)
+ {
+ const auto& rSource = pReadAccess->GetColor(y, x);
+ aResult = Color(MorphologyOp::apply(rSource.GetTransparency(), aResult.GetTransparency()),
+ MorphologyOp::apply(rSource.GetRed(), aResult.GetRed()),
+ MorphologyOp::apply(rSource.GetGreen(), aResult.GetGreen()),
+ MorphologyOp::apply(rSource.GetBlue(), aResult.GetBlue()));
+ }
+
+ void copy(BitmapWriteAccess* pWriteAccess, long x, long y, sal_uInt8* /*pHint*/ = nullptr)
+ {
+ pWriteAccess->SetPixel(y, x, aResult);
+ }
+};
+
+bool GetMinMax(long nCenter, long nRadius, long nMaxLimit, long& nMin, long& nMax)
+{
+ nMin = nCenter - nRadius;
+ nMax = nCenter + nRadius;
+ bool bLookOutside = false;
+ if (nMin < 0)
+ {
+ bLookOutside = true;
+ nMin = 0;
+ }
+ if (nMax > nMaxLimit)
+ {
+ bLookOutside = true;
+ nMax = nMaxLimit;
+ }
+ return bLookOutside;
+}
+
+template <typename MorphologyOp, int nComponentWidth> struct pass
+{
+ static void Horizontal(FilterSharedData const& rShared, const long nStart, const long nEnd)
+ {
+ BitmapReadAccess* pReadAccess = rShared.mpReadAccess;
+ BitmapWriteAccess* pWriteAccess = rShared.mpWriteAccess;
+
+ const long nLastIndex = pReadAccess->Width() - 1;
+
+ for (long y = nStart; y <= nEnd; y++)
+ {
+ // Optimization
+ sal_uInt8* const pSourceHint = pReadAccess->GetScanline(y);
+ sal_uInt8* const pDestHint = pWriteAccess->GetScanline(y);
+ for (long x = 0; x <= nLastIndex; x++)
+ {
+ // This processes [nRadius * 2 + 1] pixels of source per resulting pixel
+ // TODO: try to optimize this to not process same pixels repeatedly
+ long iMin, iMax;
+ const bool bLookOutside = GetMinMax(x, rShared.mnRadius, nLastIndex, iMin, iMax);
+ Value<MorphologyOp, nComponentWidth> aResult(rShared, bLookOutside);
+ for (long i = iMin; i <= iMax; ++i)
+ aResult.apply(pReadAccess, i, y, pSourceHint);
+
+ aResult.copy(pWriteAccess, x, y, pDestHint);
+ }
+ }
+ }
+
+ static void Vertical(FilterSharedData const& rShared, const long nStart, const long nEnd)
+ {
+ BitmapReadAccess* pReadAccess = rShared.mpReadAccess;
+ BitmapWriteAccess* pWriteAccess = rShared.mpWriteAccess;
+
+ const long nLastIndex = pReadAccess->Height() - 1;
+
+ for (long x = nStart; x <= nEnd; x++)
+ {
+ for (long y = 0; y <= nLastIndex; y++)
+ {
+ // This processes [nRadius * 2 + 1] pixels of source per resulting pixel
+ // TODO: try to optimize this to not process same pixels repeatedly
+ long iMin, iMax;
+ const bool bLookOutside = GetMinMax(y, rShared.mnRadius, nLastIndex, iMin, iMax);
+ Value<MorphologyOp, nComponentWidth> aResult(rShared, bLookOutside);
+ for (long i = iMin; i <= iMax; ++i)
+ aResult.apply(pReadAccess, x, i);
+
+ aResult.copy(pWriteAccess, x, y);
+ }
+ }
+ }
+};
+
+typedef void (*passFn)(FilterSharedData const& rShared, long nStart, long nEnd);
+
+class FilterTask : public comphelper::ThreadTask
+{
+ passFn mpFunction;
+ FilterSharedData& mrShared;
+ long mnStart;
+ long mnEnd;
+
+public:
+ explicit FilterTask(const std::shared_ptr<comphelper::ThreadTaskTag>& pTag, passFn pFunction,
+ FilterSharedData& rShared, long nStart, long nEnd)
+ : comphelper::ThreadTask(pTag)
+ , mpFunction(pFunction)
+ , mrShared(rShared)
+ , mnStart(nStart)
+ , mnEnd(nEnd)
+ {
+ }
+
+ virtual void doWork() override { mpFunction(mrShared, mnStart, mnEnd); }
+};
+
+constexpr long nThreadStrip = 16;
+
+template <typename MorphologyOp, int nComponentWidth>
+void runFilter(Bitmap& rBitmap, const long nRadius, const bool bParallel, bool bUseValueOutside,
+ sal_uInt8 nValueOutside)
+{
+ using myPass = pass<MorphologyOp, nComponentWidth>;
+ const sal_uInt8 nOutsideVal = bUseValueOutside ? nValueOutside : MorphologyOp::initVal;
+ if (bParallel)
+ {
+ try
+ {
+ comphelper::ThreadPool& rShared = comphelper::ThreadPool::getSharedOptimalPool();
+ auto pTag = comphelper::ThreadPool::createThreadTaskTag();
+
+ {
+ Bitmap::ScopedReadAccess pReadAccess(rBitmap);
+ BitmapScopedWriteAccess pWriteAccess(rBitmap);
+ FilterSharedData aSharedData(pReadAccess, pWriteAccess, nRadius, nOutsideVal);
+
+ const long nLastIndex = pReadAccess->Height() - 1;
+ long nStripStart = 0;
+ for (; nStripStart < nLastIndex - nThreadStrip; nStripStart += nThreadStrip)
+ {
+ long nStripEnd = nStripStart + nThreadStrip - 1;
+ auto pTask(std::make_unique<FilterTask>(pTag, myPass::Horizontal, aSharedData,
+ nStripStart, nStripEnd));
+ rShared.pushTask(std::move(pTask));
+ }
+ // Do the last (or the only) strip in main thread without threading overhead
+ myPass::Horizontal(aSharedData, nStripStart, nLastIndex);
+ rShared.waitUntilDone(pTag);
+ }
+ {
+ Bitmap::ScopedReadAccess pReadAccess(rBitmap);
+ BitmapScopedWriteAccess pWriteAccess(rBitmap);
+ FilterSharedData aSharedData(pReadAccess, pWriteAccess, nRadius, nOutsideVal);
+
+ const long nLastIndex = pReadAccess->Width() - 1;
+ long nStripStart = 0;
+ for (; nStripStart < nLastIndex - nThreadStrip; nStripStart += nThreadStrip)
+ {
+ long nStripEnd = nStripStart + nThreadStrip - 1;
+ auto pTask(std::make_unique<FilterTask>(pTag, myPass::Vertical, aSharedData,
+ nStripStart, nStripEnd));
+ rShared.pushTask(std::move(pTask));
+ }
+ // Do the last (or the only) strip in main thread without threading overhead
+ myPass::Vertical(aSharedData, nStripStart, nLastIndex);
+ rShared.waitUntilDone(pTag);
+ }
+ }
+ catch (...)
+ {
+ SAL_WARN("vcl.gdi", "threaded bitmap blurring failed");
+ }
+ }
+ else
+ {
+ {
+ Bitmap::ScopedReadAccess pReadAccess(rBitmap);
+ BitmapScopedWriteAccess pWriteAccess(rBitmap);
+ FilterSharedData aSharedData(pReadAccess, pWriteAccess, nRadius, nOutsideVal);
+ long nFirstIndex = 0;
+ long nLastIndex = pReadAccess->Height() - 1;
+ myPass::Horizontal(aSharedData, nFirstIndex, nLastIndex);
+ }
+ {
+ Bitmap::ScopedReadAccess pReadAccess(rBitmap);
+ BitmapScopedWriteAccess pWriteAccess(rBitmap);
+ FilterSharedData aSharedData(pReadAccess, pWriteAccess, nRadius, nOutsideVal);
+ long nFirstIndex = 0;
+ long nLastIndex = pReadAccess->Width() - 1;
+ myPass::Vertical(aSharedData, nFirstIndex, nLastIndex);
+ }
+ }
+}
+
+template <int nComponentWidth>
+void runFilter(Bitmap& rBitmap, BasicMorphologyOp op, sal_Int32 nRadius, bool bUseValueOutside,
+ sal_uInt8 nValueOutside)
+{
+ const bool bParallel = true;
+
+ if (op == BasicMorphologyOp::erode)
+ runFilter<ErodeOp, nComponentWidth>(rBitmap, nRadius, bParallel, bUseValueOutside,
+ nValueOutside);
+ else if (op == BasicMorphologyOp::dilate)
+ runFilter<DilateOp, nComponentWidth>(rBitmap, nRadius, bParallel, bUseValueOutside,
+ nValueOutside);
+}
+
+} // end anonymous namespace
+
+BitmapBasicMorphologyFilter::BitmapBasicMorphologyFilter(BasicMorphologyOp op, sal_Int32 nRadius)
+ : m_eOp(op)
+ , m_nRadius(nRadius)
+{
+}
+
+BitmapBasicMorphologyFilter::BitmapBasicMorphologyFilter(BasicMorphologyOp op, sal_Int32 nRadius,
+ sal_uInt8 nValueOutside)
+ : m_eOp(op)
+ , m_nRadius(nRadius)
+ , m_nValueOutside(nValueOutside)
+ , m_bUseValueOutside(true)
+{
+}
+
+BitmapBasicMorphologyFilter::~BitmapBasicMorphologyFilter() = default;
+
+BitmapEx BitmapBasicMorphologyFilter::execute(BitmapEx const& rBitmapEx) const
+{
+ Bitmap result = filter(rBitmapEx.GetBitmap());
+ return BitmapEx(result, rBitmapEx.GetMask());
+}
+
+Bitmap BitmapBasicMorphologyFilter::filter(Bitmap const& rBitmap) const
+{
+ Bitmap bitmapCopy(rBitmap);
+ ScanlineFormat nScanlineFormat;
+ {
+ Bitmap::ScopedReadAccess pReadAccess(bitmapCopy);
+ nScanlineFormat = pReadAccess->GetScanlineFormat();
+ }
+
+ switch (nScanlineFormat)
+ {
+ case ScanlineFormat::N24BitTcRgb:
+ case ScanlineFormat::N24BitTcBgr:
+ runFilter<24>(bitmapCopy, m_eOp, m_nRadius, m_bUseValueOutside, m_nValueOutside);
+ break;
+ case ScanlineFormat::N32BitTcMask:
+ case ScanlineFormat::N32BitTcBgra:
+ runFilter<32>(bitmapCopy, m_eOp, m_nRadius, m_bUseValueOutside, m_nValueOutside);
+ break;
+ case ScanlineFormat::N8BitPal:
+ runFilter<8>(bitmapCopy, m_eOp, m_nRadius, m_bUseValueOutside, m_nValueOutside);
+ break;
+ // TODO: handle 1-bit images
+ default:
+ // Use access' GetColor/SetPixel fallback
+ runFilter<0>(bitmapCopy, m_eOp, m_nRadius, m_bUseValueOutside, m_nValueOutside);
+ break;
+ }
+
+ return bitmapCopy;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/bitmap/BitmapColorQuantizationFilter.cxx b/vcl/source/bitmap/BitmapColorQuantizationFilter.cxx
new file mode 100644
index 000000000..390319d8a
--- /dev/null
+++ b/vcl/source/bitmap/BitmapColorQuantizationFilter.cxx
@@ -0,0 +1,232 @@
+/* -*- 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 <sal/config.h>
+
+#include <algorithm>
+
+#include <vcl/bitmap.hxx>
+#include <vcl/bitmapex.hxx>
+#include <vcl/BitmapColorQuantizationFilter.hxx>
+#include <vcl/bitmapaccess.hxx>
+
+#include <bitmapwriteaccess.hxx>
+
+#include <cstdlib>
+
+BitmapEx BitmapColorQuantizationFilter::execute(BitmapEx const& aBitmapEx) const
+{
+ Bitmap aBitmap = aBitmapEx.GetBitmap();
+
+ bool bRet = false;
+
+ if (aBitmap.GetColorCount() <= sal_Int64(mnNewColorCount))
+ {
+ bRet = true;
+ }
+ else
+ {
+ Bitmap::ScopedReadAccess pRAcc(aBitmap);
+ sal_uInt16 nBitCount;
+
+ auto const cappedNewColorCount = std::min(mnNewColorCount, sal_uInt16(256));
+
+ if (cappedNewColorCount < 17)
+ nBitCount = 4;
+ else
+ nBitCount = 8;
+
+ if (pRAcc)
+ {
+ const sal_uInt32 nValidBits = 4;
+ const sal_uInt32 nRightShiftBits = 8 - nValidBits;
+ const sal_uInt32 nLeftShiftBits1 = nValidBits;
+ const sal_uInt32 nLeftShiftBits2 = nValidBits << 1;
+ const sal_uInt32 nColorsPerComponent = 1 << nValidBits;
+ const sal_uInt32 nColorOffset = 256 / nColorsPerComponent;
+ const sal_uInt32 nTotalColors
+ = nColorsPerComponent * nColorsPerComponent * nColorsPerComponent;
+ const long nWidth = pRAcc->Width();
+ const long nHeight = pRAcc->Height();
+ std::unique_ptr<PopularColorCount[]> pCountTable(new PopularColorCount[nTotalColors]);
+
+ memset(pCountTable.get(), 0, nTotalColors * sizeof(PopularColorCount));
+
+ for (long nR = 0, nIndex = 0; nR < 256; nR += nColorOffset)
+ {
+ for (long nG = 0; nG < 256; nG += nColorOffset)
+ {
+ for (long nB = 0; nB < 256; nB += nColorOffset)
+ {
+ pCountTable[nIndex].mnIndex = nIndex;
+ nIndex++;
+ }
+ }
+ }
+
+ if (pRAcc->HasPalette())
+ {
+ for (long nY = 0; nY < nHeight; nY++)
+ {
+ Scanline pScanlineRead = pRAcc->GetScanline(nY);
+ for (long nX = 0; nX < nWidth; nX++)
+ {
+ const BitmapColor& rCol
+ = pRAcc->GetPaletteColor(pRAcc->GetIndexFromData(pScanlineRead, nX));
+ pCountTable[((static_cast<sal_uInt32>(rCol.GetRed()) >> nRightShiftBits)
+ << nLeftShiftBits2)
+ | ((static_cast<sal_uInt32>(rCol.GetGreen()) >> nRightShiftBits)
+ << nLeftShiftBits1)
+ | (static_cast<sal_uInt32>(rCol.GetBlue()) >> nRightShiftBits)]
+ .mnCount++;
+ }
+ }
+ }
+ else
+ {
+ for (long nY = 0; nY < nHeight; nY++)
+ {
+ Scanline pScanlineRead = pRAcc->GetScanline(nY);
+ for (long nX = 0; nX < nWidth; nX++)
+ {
+ const BitmapColor aCol(pRAcc->GetPixelFromData(pScanlineRead, nX));
+ pCountTable[((static_cast<sal_uInt32>(aCol.GetRed()) >> nRightShiftBits)
+ << nLeftShiftBits2)
+ | ((static_cast<sal_uInt32>(aCol.GetGreen()) >> nRightShiftBits)
+ << nLeftShiftBits1)
+ | (static_cast<sal_uInt32>(aCol.GetBlue()) >> nRightShiftBits)]
+ .mnCount++;
+ }
+ }
+ }
+
+ BitmapPalette aNewPal(cappedNewColorCount);
+
+ std::qsort(pCountTable.get(), nTotalColors, sizeof(PopularColorCount),
+ [](const void* p1, const void* p2) {
+ int nRet;
+
+ if (static_cast<PopularColorCount const*>(p1)->mnCount
+ < static_cast<PopularColorCount const*>(p2)->mnCount)
+ nRet = 1;
+ else if (static_cast<PopularColorCount const*>(p1)->mnCount
+ == static_cast<PopularColorCount const*>(p2)->mnCount)
+ nRet = 0;
+ else
+ nRet = -1;
+
+ return nRet;
+ });
+
+ for (sal_uInt16 n = 0; n < cappedNewColorCount; n++)
+ {
+ const PopularColorCount& rPop = pCountTable[n];
+ aNewPal[n] = BitmapColor(
+ static_cast<sal_uInt8>((rPop.mnIndex >> nLeftShiftBits2) << nRightShiftBits),
+ static_cast<sal_uInt8>(
+ ((rPop.mnIndex >> nLeftShiftBits1) & (nColorsPerComponent - 1))
+ << nRightShiftBits),
+ static_cast<sal_uInt8>((rPop.mnIndex & (nColorsPerComponent - 1))
+ << nRightShiftBits));
+ }
+
+ Bitmap aNewBmp(aBitmap.GetSizePixel(), nBitCount, &aNewPal);
+ BitmapScopedWriteAccess pWAcc(aNewBmp);
+
+ if (pWAcc)
+ {
+ BitmapColor aDstCol(sal_uInt8(0));
+ std::unique_ptr<sal_uInt8[]> pIndexMap(new sal_uInt8[nTotalColors]);
+
+ for (long nR = 0, nIndex = 0; nR < 256; nR += nColorOffset)
+ {
+ for (long nG = 0; nG < 256; nG += nColorOffset)
+ {
+ for (long nB = 0; nB < 256; nB += nColorOffset)
+ {
+ pIndexMap[nIndex++] = static_cast<sal_uInt8>(aNewPal.GetBestIndex(
+ BitmapColor(static_cast<sal_uInt8>(nR), static_cast<sal_uInt8>(nG),
+ static_cast<sal_uInt8>(nB))));
+ }
+ }
+ }
+
+ if (pRAcc->HasPalette())
+ {
+ for (long nY = 0; nY < nHeight; nY++)
+ {
+ Scanline pScanline = pWAcc->GetScanline(nY);
+ Scanline pScanlineRead = pRAcc->GetScanline(nY);
+ for (long nX = 0; nX < nWidth; nX++)
+ {
+ const BitmapColor& rCol = pRAcc->GetPaletteColor(
+ pRAcc->GetIndexFromData(pScanlineRead, nX));
+ aDstCol.SetIndex(pIndexMap[((static_cast<sal_uInt32>(rCol.GetRed())
+ >> nRightShiftBits)
+ << nLeftShiftBits2)
+ | ((static_cast<sal_uInt32>(rCol.GetGreen())
+ >> nRightShiftBits)
+ << nLeftShiftBits1)
+ | (static_cast<sal_uInt32>(rCol.GetBlue())
+ >> nRightShiftBits)]);
+ pWAcc->SetPixelOnData(pScanline, nX, aDstCol);
+ }
+ }
+ }
+ else
+ {
+ for (long nY = 0; nY < nHeight; nY++)
+ {
+ Scanline pScanline = pWAcc->GetScanline(nY);
+ Scanline pScanlineRead = pRAcc->GetScanline(nY);
+
+ for (long nX = 0; nX < nWidth; nX++)
+ {
+ const BitmapColor aCol(pRAcc->GetPixelFromData(pScanlineRead, nX));
+ aDstCol.SetIndex(pIndexMap[((static_cast<sal_uInt32>(aCol.GetRed())
+ >> nRightShiftBits)
+ << nLeftShiftBits2)
+ | ((static_cast<sal_uInt32>(aCol.GetGreen())
+ >> nRightShiftBits)
+ << nLeftShiftBits1)
+ | (static_cast<sal_uInt32>(aCol.GetBlue())
+ >> nRightShiftBits)]);
+ pWAcc->SetPixelOnData(pScanline, nX, aDstCol);
+ }
+ }
+ }
+
+ pWAcc.reset();
+ bRet = true;
+ }
+
+ pCountTable.reset();
+ pRAcc.reset();
+
+ if (bRet)
+ {
+ const MapMode aMap(aBitmap.GetPrefMapMode());
+ const Size aSize(aBitmap.GetPrefSize());
+
+ aBitmap = aNewBmp;
+
+ aBitmap.SetPrefMapMode(aMap);
+ aBitmap.SetPrefSize(aSize);
+ }
+ }
+ }
+
+ if (bRet)
+ return BitmapEx(aBitmap);
+
+ return BitmapEx();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/bitmap/BitmapColorizeFilter.cxx b/vcl/source/bitmap/BitmapColorizeFilter.cxx
new file mode 100644
index 000000000..343642b77
--- /dev/null
+++ b/vcl/source/bitmap/BitmapColorizeFilter.cxx
@@ -0,0 +1,92 @@
+/* -*- 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 <tools/color.hxx>
+#include <tools/helpers.hxx>
+
+#include <bitmapwriteaccess.hxx>
+#include <BitmapColorizeFilter.hxx>
+
+BitmapEx BitmapColorizeFilter::execute(BitmapEx const& rBitmapEx) const
+{
+ Bitmap aBitmap = rBitmapEx.GetBitmap();
+ BitmapScopedWriteAccess pWriteAccess(aBitmap);
+
+ if (!pWriteAccess)
+ return rBitmapEx;
+
+ BitmapColor aBitmapColor;
+ const long nW = pWriteAccess->Width();
+ const long nH = pWriteAccess->Height();
+ std::vector<sal_uInt8> aMapR(256);
+ std::vector<sal_uInt8> aMapG(256);
+ std::vector<sal_uInt8> aMapB(256);
+ long nX;
+ long nY;
+
+ const sal_uInt8 cR = maColor.GetRed();
+ const sal_uInt8 cG = maColor.GetGreen();
+ const sal_uInt8 cB = maColor.GetBlue();
+
+ for (nX = 0; nX < 256; ++nX)
+ {
+ aMapR[nX] = MinMax((nX + cR) / 2, 0, 255);
+ aMapG[nX] = MinMax((nX + cG) / 2, 0, 255);
+ aMapB[nX] = MinMax((nX + cB) / 2, 0, 255);
+ }
+
+ if (pWriteAccess->HasPalette())
+ {
+ for (sal_uInt16 i = 0, nCount = pWriteAccess->GetPaletteEntryCount(); i < nCount; i++)
+ {
+ const BitmapColor& rCol = pWriteAccess->GetPaletteColor(i);
+ aBitmapColor.SetRed(aMapR[rCol.GetRed()]);
+ aBitmapColor.SetGreen(aMapG[rCol.GetGreen()]);
+ aBitmapColor.SetBlue(aMapB[rCol.GetBlue()]);
+ pWriteAccess->SetPaletteColor(i, aBitmapColor);
+ }
+ }
+ else if (pWriteAccess->GetScanlineFormat() == ScanlineFormat::N24BitTcBgr)
+ {
+ for (nY = 0; nY < nH; ++nY)
+ {
+ Scanline pScan = pWriteAccess->GetScanline(nY);
+
+ for (nX = 0; nX < nW; ++nX)
+ {
+ *pScan = aMapB[*pScan];
+ pScan++;
+ *pScan = aMapG[*pScan];
+ pScan++;
+ *pScan = aMapR[*pScan];
+ pScan++;
+ }
+ }
+ }
+ else
+ {
+ for (nY = 0; nY < nH; ++nY)
+ {
+ Scanline pScanline = pWriteAccess->GetScanline(nY);
+ for (nX = 0; nX < nW; ++nX)
+ {
+ aBitmapColor = pWriteAccess->GetPixelFromData(pScanline, nX);
+ aBitmapColor.SetRed(aMapR[aBitmapColor.GetRed()]);
+ aBitmapColor.SetGreen(aMapG[aBitmapColor.GetGreen()]);
+ aBitmapColor.SetBlue(aMapB[aBitmapColor.GetBlue()]);
+ pWriteAccess->SetPixelOnData(pScanline, nX, aBitmapColor);
+ }
+ }
+ }
+
+ return rBitmapEx;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/bitmap/BitmapConvolutionMatrixFilter.cxx b/vcl/source/bitmap/BitmapConvolutionMatrixFilter.cxx
new file mode 100644
index 000000000..94e0c6e02
--- /dev/null
+++ b/vcl/source/bitmap/BitmapConvolutionMatrixFilter.cxx
@@ -0,0 +1,208 @@
+/* -*- 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 <tools/helpers.hxx>
+#include <vcl/bitmap.hxx>
+#include <vcl/bitmapex.hxx>
+#include <vcl/bitmapaccess.hxx>
+#include <vcl/BitmapConvolutionMatrixFilter.hxx>
+#include <vcl/BitmapSharpenFilter.hxx>
+
+#include <bitmapwriteaccess.hxx>
+#include <array>
+
+BitmapEx BitmapConvolutionMatrixFilter::execute(BitmapEx const& rBitmapEx) const
+{
+ Bitmap aBitmap(rBitmapEx.GetBitmap());
+
+ const long nDivisor = 8;
+ Bitmap::ScopedReadAccess pReadAcc(aBitmap);
+ bool bRet = false;
+
+ if (pReadAcc)
+ {
+ Bitmap aNewBmp(aBitmap.GetSizePixel(), 24);
+ BitmapScopedWriteAccess pWriteAcc(aNewBmp);
+
+ if (pWriteAcc)
+ {
+ const long nWidth = pWriteAcc->Width(), nWidth2 = nWidth + 2;
+ const long nHeight = pWriteAcc->Height(), nHeight2 = nHeight + 2;
+ std::unique_ptr<long[]> pColm(new long[nWidth2]);
+ std::unique_ptr<long[]> pRows(new long[nHeight2]);
+ std::unique_ptr<BitmapColor[]> pColRow1(new BitmapColor[nWidth2]);
+ std::unique_ptr<BitmapColor[]> pColRow2(new BitmapColor[nWidth2]);
+ std::unique_ptr<BitmapColor[]> pColRow3(new BitmapColor[nWidth2]);
+ BitmapColor* pRowTmp1 = pColRow1.get();
+ BitmapColor* pRowTmp2 = pColRow2.get();
+ BitmapColor* pRowTmp3 = pColRow3.get();
+ BitmapColor* pColor;
+ long nY, nX, i, nSumR, nSumG, nSumB, nMatrixVal, nTmp;
+ std::array<std::array<long, 256>, 9> aKoeff;
+ long* pTmp;
+
+ // create LUT of products of matrix value and possible color component values
+ for (nY = 0; nY < 9; nY++)
+ {
+ for (nX = nTmp = 0, nMatrixVal = mrMatrix[nY]; nX < 256; nX++, nTmp += nMatrixVal)
+ {
+ aKoeff[nY][nX] = nTmp;
+ }
+ }
+
+ // create column LUT
+ for (i = 0; i < nWidth2; i++)
+ {
+ pColm[i] = (i > 0) ? (i - 1) : 0;
+ }
+
+ pColm[nWidth + 1] = pColm[nWidth];
+
+ // create row LUT
+ for (i = 0; i < nHeight2; i++)
+ {
+ pRows[i] = (i > 0) ? (i - 1) : 0;
+ }
+
+ pRows[nHeight + 1] = pRows[nHeight];
+
+ // read first three rows of bitmap color
+ for (i = 0; i < nWidth2; i++)
+ {
+ pColRow1[i] = pReadAcc->GetColor(pRows[0], pColm[i]);
+ pColRow2[i] = pReadAcc->GetColor(pRows[1], pColm[i]);
+ pColRow3[i] = pReadAcc->GetColor(pRows[2], pColm[i]);
+ }
+
+ // do convolution
+ for (nY = 0; nY < nHeight;)
+ {
+ Scanline pScanline = pWriteAcc->GetScanline(nY);
+ for (nX = 0; nX < nWidth; nX++)
+ {
+ // first row
+ pTmp = aKoeff[0].data();
+ pColor = pRowTmp1 + nX;
+ nSumR = pTmp[pColor->GetRed()];
+ nSumG = pTmp[pColor->GetGreen()];
+ nSumB = pTmp[pColor->GetBlue()];
+
+ pTmp = aKoeff[1].data();
+ nSumR += pTmp[(++pColor)->GetRed()];
+ nSumG += pTmp[pColor->GetGreen()];
+ nSumB += pTmp[pColor->GetBlue()];
+
+ pTmp = aKoeff[2].data();
+ nSumR += pTmp[(++pColor)->GetRed()];
+ nSumG += pTmp[pColor->GetGreen()];
+ nSumB += pTmp[pColor->GetBlue()];
+
+ // second row
+ pTmp = aKoeff[3].data();
+ pColor = pRowTmp2 + nX;
+ nSumR += pTmp[pColor->GetRed()];
+ nSumG += pTmp[pColor->GetGreen()];
+ nSumB += pTmp[pColor->GetBlue()];
+
+ pTmp = aKoeff[4].data();
+ nSumR += pTmp[(++pColor)->GetRed()];
+ nSumG += pTmp[pColor->GetGreen()];
+ nSumB += pTmp[pColor->GetBlue()];
+
+ pTmp = aKoeff[5].data();
+ nSumR += pTmp[(++pColor)->GetRed()];
+ nSumG += pTmp[pColor->GetGreen()];
+ nSumB += pTmp[pColor->GetBlue()];
+
+ // third row
+ pTmp = aKoeff[6].data();
+ pColor = pRowTmp3 + nX;
+ nSumR += pTmp[pColor->GetRed()];
+ nSumG += pTmp[pColor->GetGreen()];
+ nSumB += pTmp[pColor->GetBlue()];
+
+ pTmp = aKoeff[7].data();
+ nSumR += pTmp[(++pColor)->GetRed()];
+ nSumG += pTmp[pColor->GetGreen()];
+ nSumB += pTmp[pColor->GetBlue()];
+
+ pTmp = aKoeff[8].data();
+ nSumR += pTmp[(++pColor)->GetRed()];
+ nSumG += pTmp[pColor->GetGreen()];
+ nSumB += pTmp[pColor->GetBlue()];
+
+ // calculate destination color
+ pWriteAcc->SetPixelOnData(
+ pScanline, nX,
+ BitmapColor(static_cast<sal_uInt8>(MinMax(nSumR / nDivisor, 0, 255)),
+ static_cast<sal_uInt8>(MinMax(nSumG / nDivisor, 0, 255)),
+ static_cast<sal_uInt8>(MinMax(nSumB / nDivisor, 0, 255))));
+ }
+
+ if (++nY < nHeight)
+ {
+ if (pRowTmp1 == pColRow1.get())
+ {
+ pRowTmp1 = pColRow2.get();
+ pRowTmp2 = pColRow3.get();
+ pRowTmp3 = pColRow1.get();
+ }
+ else if (pRowTmp1 == pColRow2.get())
+ {
+ pRowTmp1 = pColRow3.get();
+ pRowTmp2 = pColRow1.get();
+ pRowTmp3 = pColRow2.get();
+ }
+ else
+ {
+ pRowTmp1 = pColRow1.get();
+ pRowTmp2 = pColRow2.get();
+ pRowTmp3 = pColRow3.get();
+ }
+
+ for (i = 0; i < nWidth2; i++)
+ {
+ pRowTmp3[i] = pReadAcc->GetColor(pRows[nY + 2], pColm[i]);
+ }
+ }
+ }
+
+ pWriteAcc.reset();
+
+ bRet = true;
+ }
+
+ pReadAcc.reset();
+
+ if (bRet)
+ {
+ const MapMode aMap(aBitmap.GetPrefMapMode());
+ const Size aPrefSize(aBitmap.GetPrefSize());
+
+ aBitmap = aNewBmp;
+
+ aBitmap.SetPrefMapMode(aMap);
+ aBitmap.SetPrefSize(aPrefSize);
+ }
+ }
+
+ if (bRet)
+ return BitmapEx(aBitmap);
+
+ return BitmapEx();
+}
+
+static const long g_SharpenMatrix[] = { -1, -1, -1, -1, 16, -1, -1, -1, -1 };
+
+BitmapSharpenFilter::BitmapSharpenFilter()
+ : BitmapConvolutionMatrixFilter(g_SharpenMatrix)
+{
+}
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/bitmap/BitmapDisabledImageFilter.cxx b/vcl/source/bitmap/BitmapDisabledImageFilter.cxx
new file mode 100644
index 000000000..5ade2451a
--- /dev/null
+++ b/vcl/source/bitmap/BitmapDisabledImageFilter.cxx
@@ -0,0 +1,61 @@
+/* -*- 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/bitmapaccess.hxx>
+
+#include <bitmapwriteaccess.hxx>
+#include <BitmapDisabledImageFilter.hxx>
+
+BitmapEx BitmapDisabledImageFilter::execute(BitmapEx const& rBitmapEx) const
+{
+ const Size aSize(rBitmapEx.GetSizePixel());
+
+ // keep disable image at same depth as original where possible, otherwise
+ // use 8 bit
+ sal_uInt16 nBitCount = rBitmapEx.GetBitCount();
+ if (nBitCount < 8)
+ nBitCount = 8;
+
+ const BitmapPalette* pPal = nBitCount == 8 ? &Bitmap::GetGreyPalette(256) : nullptr;
+ Bitmap aGrey(aSize, nBitCount, pPal);
+ BitmapScopedWriteAccess pGrey(aGrey);
+
+ BitmapEx aReturnBitmap;
+ Bitmap aReadBitmap(rBitmapEx.GetBitmap());
+ Bitmap::ScopedReadAccess pRead(aReadBitmap);
+ if (pRead && pGrey)
+ {
+ for (long nY = 0; nY < aSize.Height(); ++nY)
+ {
+ Scanline pGreyScan = pGrey->GetScanline(nY);
+ Scanline pReadScan = pRead->GetScanline(nY);
+
+ for (long nX = 0; nX < aSize.Width(); ++nX)
+ {
+ // Get the luminance from RGB color and remap the value from 0-255 to 160-224
+ const BitmapColor aColor = pRead->GetPixelFromData(pReadScan, nX);
+ sal_uInt8 nLum(aColor.GetLuminance() / 4 + 160);
+ BitmapColor aGreyValue(nLum, nLum, nLum, aColor.GetAlpha());
+ pGrey->SetPixelOnData(pGreyScan, nX, aGreyValue);
+ }
+ }
+ }
+
+ if (rBitmapEx.IsTransparent())
+ {
+ aReturnBitmap = BitmapEx(aGrey, rBitmapEx.GetAlpha());
+ }
+ else
+ aReturnBitmap = BitmapEx(aGrey);
+
+ return aReturnBitmap;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/bitmap/BitmapDuoToneFilter.cxx b/vcl/source/bitmap/BitmapDuoToneFilter.cxx
new file mode 100644
index 000000000..65d4b3f41
--- /dev/null
+++ b/vcl/source/bitmap/BitmapDuoToneFilter.cxx
@@ -0,0 +1,65 @@
+/* -*- 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/bitmap.hxx>
+#include <vcl/bitmapex.hxx>
+#include <vcl/BitmapDuoToneFilter.hxx>
+#include <vcl/bitmapaccess.hxx>
+
+#include <bitmapwriteaccess.hxx>
+
+static sal_uInt8 lcl_getDuotoneColorComponent(sal_uInt8 base, sal_uInt16 color1, sal_uInt16 color2)
+{
+ color2 = color2 * base / 0xFF;
+ color1 = color1 * (0xFF - base) / 0xFF;
+
+ return static_cast<sal_uInt8>(color1 + color2);
+}
+
+BitmapEx BitmapDuoToneFilter::execute(BitmapEx const& rBitmapEx) const
+{
+ Bitmap aBitmap(rBitmapEx.GetBitmap());
+
+ const long nWidth = aBitmap.GetSizePixel().Width();
+ const long nHeight = aBitmap.GetSizePixel().Height();
+
+ Bitmap aResultBitmap(aBitmap.GetSizePixel(), 24);
+ Bitmap::ScopedReadAccess pReadAcc(aBitmap);
+ BitmapScopedWriteAccess pWriteAcc(aResultBitmap);
+ const BitmapColor aColorOne(static_cast<sal_uInt8>(mnColorOne >> 16),
+ static_cast<sal_uInt8>(mnColorOne >> 8),
+ static_cast<sal_uInt8>(mnColorOne));
+ const BitmapColor aColorTwo(static_cast<sal_uInt8>(mnColorTwo >> 16),
+ static_cast<sal_uInt8>(mnColorTwo >> 8),
+ static_cast<sal_uInt8>(mnColorTwo));
+
+ for (long x = 0; x < nWidth; x++)
+ {
+ for (long y = 0; y < nHeight; y++)
+ {
+ BitmapColor aColor = pReadAcc->GetColor(y, x);
+ sal_uInt8 nLuminance = aColor.GetLuminance();
+ BitmapColor aResultColor(
+ lcl_getDuotoneColorComponent(nLuminance, aColorOne.GetRed(), aColorTwo.GetRed()),
+ lcl_getDuotoneColorComponent(nLuminance, aColorOne.GetGreen(),
+ aColorTwo.GetGreen()),
+ lcl_getDuotoneColorComponent(nLuminance, aColorOne.GetBlue(), aColorTwo.GetBlue()));
+ pWriteAcc->SetPixel(y, x, aResultColor);
+ }
+ }
+
+ pWriteAcc.reset();
+ pReadAcc.reset();
+ aBitmap.ReassignWithSize(aResultBitmap);
+
+ return BitmapEx(aBitmap);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/bitmap/BitmapEmbossGreyFilter.cxx b/vcl/source/bitmap/BitmapEmbossGreyFilter.cxx
new file mode 100644
index 000000000..4ea239b1a
--- /dev/null
+++ b/vcl/source/bitmap/BitmapEmbossGreyFilter.cxx
@@ -0,0 +1,158 @@
+/* -*- 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 <sal/config.h>
+
+#include <algorithm>
+
+#include <tools/helpers.hxx>
+#include <vcl/bitmap.hxx>
+#include <vcl/bitmapex.hxx>
+#include <vcl/bitmapaccess.hxx>
+#include <vcl/BitmapEmbossGreyFilter.hxx>
+
+#include <bitmapwriteaccess.hxx>
+
+BitmapEx BitmapEmbossGreyFilter::execute(BitmapEx const& rBitmapEx) const
+{
+ Bitmap aBitmap(rBitmapEx.GetBitmap());
+
+ bool bRet = aBitmap.ImplMakeGreyscales(256);
+
+ if (bRet)
+ {
+ bRet = false;
+
+ Bitmap::ScopedReadAccess pReadAcc(aBitmap);
+
+ if (pReadAcc)
+ {
+ Bitmap aNewBmp(aBitmap.GetSizePixel(), 8, &pReadAcc->GetPalette());
+ BitmapScopedWriteAccess pWriteAcc(aNewBmp);
+
+ if (pWriteAcc)
+ {
+ BitmapColor aGrey(sal_uInt8(0));
+ const long nWidth = pWriteAcc->Width();
+ const long nHeight = pWriteAcc->Height();
+ long nGrey11, nGrey12, nGrey13;
+ long nGrey21, nGrey22, nGrey23;
+ long nGrey31, nGrey32, nGrey33;
+ double fAzim = basegfx::deg2rad(mnAzimuthAngle100 * 0.01);
+ double fElev = basegfx::deg2rad(mnElevationAngle100 * 0.01);
+ std::unique_ptr<long[]> pHMap(new long[nWidth + 2]);
+ std::unique_ptr<long[]> pVMap(new long[nHeight + 2]);
+ long nX, nY, nNx, nNy, nDotL;
+ const long nLx = FRound(cos(fAzim) * cos(fElev) * 255.0);
+ const long nLy = FRound(sin(fAzim) * cos(fElev) * 255.0);
+ const long nLz = FRound(sin(fElev) * 255.0);
+ const auto nZ2 = ((6 * 255) / 4) * ((6 * 255) / 4);
+ const long nNzLz = ((6 * 255) / 4) * nLz;
+ const sal_uInt8 cLz = static_cast<sal_uInt8>(std::clamp(nLz, 0L, 255L));
+
+ // fill mapping tables
+ pHMap[0] = 0;
+
+ for (nX = 1; nX <= nWidth; nX++)
+ {
+ pHMap[nX] = nX - 1;
+ }
+
+ pHMap[nWidth + 1] = nWidth - 1;
+
+ pVMap[0] = 0;
+
+ for (nY = 1; nY <= nHeight; nY++)
+ {
+ pVMap[nY] = nY - 1;
+ }
+
+ pVMap[nHeight + 1] = nHeight - 1;
+
+ for (nY = 0; nY < nHeight; nY++)
+ {
+ nGrey11 = pReadAcc->GetPixel(pVMap[nY], pHMap[0]).GetIndex();
+ nGrey12 = pReadAcc->GetPixel(pVMap[nY], pHMap[1]).GetIndex();
+ nGrey13 = pReadAcc->GetPixel(pVMap[nY], pHMap[2]).GetIndex();
+ nGrey21 = pReadAcc->GetPixel(pVMap[nY + 1], pHMap[0]).GetIndex();
+ nGrey22 = pReadAcc->GetPixel(pVMap[nY + 1], pHMap[1]).GetIndex();
+ nGrey23 = pReadAcc->GetPixel(pVMap[nY + 1], pHMap[2]).GetIndex();
+ nGrey31 = pReadAcc->GetPixel(pVMap[nY + 2], pHMap[0]).GetIndex();
+ nGrey32 = pReadAcc->GetPixel(pVMap[nY + 2], pHMap[1]).GetIndex();
+ nGrey33 = pReadAcc->GetPixel(pVMap[nY + 2], pHMap[2]).GetIndex();
+
+ Scanline pScanline = pWriteAcc->GetScanline(nY);
+ for (nX = 0; nX < nWidth; nX++)
+ {
+ nNx = nGrey11 + nGrey21 + nGrey31 - nGrey13 - nGrey23 - nGrey33;
+ nNy = nGrey31 + nGrey32 + nGrey33 - nGrey11 - nGrey12 - nGrey13;
+
+ if (!nNx && !nNy)
+ {
+ aGrey.SetIndex(cLz);
+ }
+ else if ((nDotL = nNx * nLx + nNy * nLy + nNzLz) < 0)
+ {
+ aGrey.SetIndex(0);
+ }
+ else
+ {
+ const double fGrey
+ = nDotL / sqrt(static_cast<double>(nNx * nNx + nNy * nNy + nZ2));
+ aGrey.SetIndex(static_cast<sal_uInt8>(std::clamp(fGrey, 0.0, 255.0)));
+ }
+
+ pWriteAcc->SetPixelOnData(pScanline, nX, aGrey);
+
+ if (nX < (nWidth - 1))
+ {
+ const long nNextX = pHMap[nX + 3];
+
+ nGrey11 = nGrey12;
+ nGrey12 = nGrey13;
+ nGrey13 = pReadAcc->GetPixel(pVMap[nY], nNextX).GetIndex();
+ nGrey21 = nGrey22;
+ nGrey22 = nGrey23;
+ nGrey23 = pReadAcc->GetPixel(pVMap[nY + 1], nNextX).GetIndex();
+ nGrey31 = nGrey32;
+ nGrey32 = nGrey33;
+ nGrey33 = pReadAcc->GetPixel(pVMap[nY + 2], nNextX).GetIndex();
+ }
+ }
+ }
+
+ pHMap.reset();
+ pVMap.reset();
+ pWriteAcc.reset();
+ bRet = true;
+ }
+
+ pReadAcc.reset();
+
+ if (bRet)
+ {
+ const MapMode aMap(aBitmap.GetPrefMapMode());
+ const Size aPrefSize(aBitmap.GetPrefSize());
+
+ aBitmap = aNewBmp;
+
+ aBitmap.SetPrefMapMode(aMap);
+ aBitmap.SetPrefSize(aPrefSize);
+ }
+ }
+ }
+
+ if (bRet)
+ return BitmapEx(aBitmap);
+
+ return BitmapEx();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/bitmap/BitmapFastScaleFilter.cxx b/vcl/source/bitmap/BitmapFastScaleFilter.cxx
new file mode 100644
index 000000000..cff8f3148
--- /dev/null
+++ b/vcl/source/bitmap/BitmapFastScaleFilter.cxx
@@ -0,0 +1,131 @@
+/* -*- 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <tools/helpers.hxx>
+
+#include <vcl/bitmapex.hxx>
+#include <vcl/bitmapaccess.hxx>
+
+#include <bitmapwriteaccess.hxx>
+#include <BitmapFastScaleFilter.hxx>
+#include <sal/log.hxx>
+
+BitmapEx BitmapFastScaleFilter::execute(BitmapEx const& rBitmapEx) const
+{
+ SAL_INFO("vcl.gdi", "BitmapFastScaleFilter::execute()");
+
+ Bitmap aBitmap(rBitmapEx.GetBitmap());
+
+ const Size aSizePix(aBitmap.GetSizePixel());
+ const long nNewWidth = FRound(aSizePix.Width() * mfScaleX);
+ const long nNewHeight = FRound(aSizePix.Height() * mfScaleY);
+ bool bRet = false;
+
+ SAL_INFO("vcl.gdi", "New width: " << nNewWidth << "\nNew height: " << nNewHeight);
+
+ if (nNewWidth && nNewHeight)
+ {
+ Bitmap::ScopedReadAccess pReadAcc(aBitmap);
+
+ if (pReadAcc)
+ {
+ Bitmap aNewBmp(Size(nNewWidth, nNewHeight), aBitmap.GetBitCount(),
+ &pReadAcc->GetPalette());
+ BitmapScopedWriteAccess pWriteAcc(aNewBmp);
+
+ if (pWriteAcc)
+ {
+ const long nScanlineSize = pWriteAcc->GetScanlineSize();
+ const long nNewWidth1 = nNewWidth - 1;
+ const long nNewHeight1 = nNewHeight - 1;
+
+ if (nNewWidth1 && nNewHeight1)
+ {
+ const double nWidth = pReadAcc->Width();
+ const double nHeight = pReadAcc->Height();
+ std::unique_ptr<long[]> pLutX(new long[nNewWidth]);
+ std::unique_ptr<long[]> pLutY(new long[nNewHeight]);
+
+ for (long nX = 0; nX < nNewWidth; nX++)
+ {
+ pLutX[nX] = long(nX * nWidth / nNewWidth);
+ }
+
+ for (long nY = 0; nY < nNewHeight; nY++)
+ {
+ pLutY[nY] = long(nY * nHeight / nNewHeight);
+ }
+
+ long nActY = 0;
+ while (nActY < nNewHeight)
+ {
+ long nMapY = pLutY[nActY];
+ Scanline pScanline = pWriteAcc->GetScanline(nActY);
+ Scanline pScanlineRead = pReadAcc->GetScanline(nMapY);
+
+ for (long nX = 0; nX < nNewWidth; nX++)
+ {
+ pWriteAcc->SetPixelOnData(
+ pScanline, nX,
+ pReadAcc->GetPixelFromData(pScanlineRead, pLutX[nX]));
+ }
+
+ while ((nActY < nNewHeight1) && (pLutY[nActY + 1] == nMapY))
+ {
+ memcpy(pWriteAcc->GetScanline(nActY + 1), pWriteAcc->GetScanline(nActY),
+ nScanlineSize);
+ nActY++;
+ }
+ nActY++;
+ }
+
+ bRet = true;
+ }
+
+ pWriteAcc.reset();
+ }
+ pReadAcc.reset();
+
+ if (bRet)
+ {
+ aBitmap.ReassignWithSize(aNewBmp);
+ SAL_INFO("vcl.gdi", "Bitmap size: " << aBitmap.GetSizePixel());
+ }
+ else
+ {
+ SAL_WARN("vcl.gdi", "no resize");
+ }
+ }
+ }
+
+ Bitmap aMask(rBitmapEx.GetMask());
+
+ if (bRet && (rBitmapEx.GetTransparentType() == TransparentType::Bitmap) && !aMask.IsEmpty())
+ bRet = aMask.Scale(maSize, BmpScaleFlag::Fast);
+
+ SAL_WARN_IF(!aMask.IsEmpty() && aBitmap.GetSizePixel() != aMask.GetSizePixel(), "vcl",
+ "BitmapEx::Scale(): size mismatch for bitmap and alpha mask.");
+
+ if (bRet)
+ return BitmapEx(aBitmap, aMask);
+
+ return BitmapEx();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/bitmap/BitmapFilterStackBlur.cxx b/vcl/source/bitmap/BitmapFilterStackBlur.cxx
new file mode 100644
index 000000000..da51daedf
--- /dev/null
+++ b/vcl/source/bitmap/BitmapFilterStackBlur.cxx
@@ -0,0 +1,629 @@
+/* -*- 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 <vcl/bitmapaccess.hxx>
+#include <bitmapwriteaccess.hxx>
+#include <sal/log.hxx>
+
+#include <comphelper/threadpool.hxx>
+
+namespace
+{
+static 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 };
+
+static 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;
+ long mnRadius;
+ long mnComponentWidth;
+ long mnDiv;
+ long mnColorChannels;
+
+ BlurSharedData(BitmapReadAccess* pReadAccess, BitmapWriteAccess* pWriteAccess, long aRadius,
+ long nComponentWidth, long 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<long> maPositionTable;
+ std::vector<long> maWeightTable;
+
+ std::vector<long> mnSumVector;
+ std::vector<long> mnInSumVector;
+ std::vector<long> 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(long nLastIndex)
+ {
+ for (long i = 0; i < maShared.mnDiv; i++)
+ {
+ maPositionTable[i] = std::clamp(i - maShared.mnRadius, 0L, nLastIndex);
+ maWeightTable[i] = maShared.mnRadius + 1 - std::abs(i - maShared.mnRadius);
+ }
+ }
+
+ long getMultiplyValue() { return static_cast<long>(constMultiplyTable[maShared.mnRadius]); }
+
+ long getShiftValue() { return static_cast<long>(constShiftTable[maShared.mnRadius]); }
+};
+
+typedef void (*BlurRangeFn)(BlurSharedData const& rShared, long nStartY, long nEndY);
+
+class BlurTask : public comphelper::ThreadTask
+{
+ BlurRangeFn mpBlurFunction;
+ BlurSharedData& mrShared;
+ long mnStartY;
+ long mnEndY;
+
+public:
+ explicit BlurTask(const std::shared_ptr<comphelper::ThreadTaskTag>& pTag,
+ BlurRangeFn pBlurFunction, BlurSharedData& rShared, long nStartY, long 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(long*& pValue1, long nConstant)
+ {
+ pValue1[0] += nConstant;
+ pValue1[1] += nConstant;
+ pValue1[2] += nConstant;
+ }
+
+ static inline void set(long*& pValue1, long nConstant)
+ {
+ pValue1[0] = nConstant;
+ pValue1[1] = nConstant;
+ pValue1[2] = nConstant;
+ }
+
+ static inline void add(long*& pValue1, sal_uInt8*& pValue2)
+ {
+ pValue1[0] += pValue2[0];
+ pValue1[1] += pValue2[1];
+ pValue1[2] += pValue2[2];
+ }
+
+ static inline void add(long*& pValue1, long*& pValue2)
+ {
+ pValue1[0] += pValue2[0];
+ pValue1[1] += pValue2[1];
+ pValue1[2] += pValue2[2];
+ }
+
+ static inline void sub(long*& pValue1, sal_uInt8*& pValue2)
+ {
+ pValue1[0] -= pValue2[0];
+ pValue1[1] -= pValue2[1];
+ pValue1[2] -= pValue2[2];
+ }
+
+ static inline void sub(long*& pValue1, long*& pValue2)
+ {
+ pValue1[0] -= pValue2[0];
+ pValue1[1] -= pValue2[1];
+ pValue1[2] -= pValue2[2];
+ }
+
+ static inline void assignPtr(sal_uInt8*& pValue1, sal_uInt8*& pValue2)
+ {
+ pValue1[0] = pValue2[0];
+ pValue1[1] = pValue2[1];
+ pValue1[2] = pValue2[2];
+ }
+
+ static inline void assignMulAndShr(sal_uInt8*& result, long*& sum, long multiply, long 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(long*& pValue1, long nConstant) { pValue1[0] += nConstant; }
+
+ static inline void set(long*& pValue1, long nConstant) { pValue1[0] = nConstant; }
+
+ static inline void add(long*& pValue1, sal_uInt8*& pValue2) { pValue1[0] += pValue2[0]; }
+
+ static inline void add(long*& pValue1, long*& pValue2) { pValue1[0] += pValue2[0]; }
+
+ static inline void sub(long*& pValue1, sal_uInt8*& pValue2) { pValue1[0] -= pValue2[0]; }
+
+ static inline void sub(long*& pValue1, long*& pValue2) { pValue1[0] -= pValue2[0]; }
+
+ static inline void assignPtr(sal_uInt8*& pValue1, sal_uInt8*& pValue2)
+ {
+ pValue1[0] = pValue2[0];
+ }
+
+ static inline void assignMulAndShr(sal_uInt8*& result, long*& sum, long multiply, long shift)
+ {
+ result[0] = (multiply * sum[0]) >> shift;
+ }
+};
+
+template <typename SumFunction>
+void stackBlurHorizontal(BlurSharedData const& rShared, long nStart, long nEnd)
+{
+ BitmapReadAccess* pReadAccess = rShared.mpReadAccess;
+ BitmapWriteAccess* pWriteAccess = rShared.mpWriteAccess;
+
+ BlurArrays aArrays(rShared);
+
+ sal_uInt8* pStack = aArrays.maStackBuffer.data();
+ sal_uInt8* pStackPtr;
+
+ long nWidth = pReadAccess->Width();
+ long nLastIndexX = nWidth - 1;
+
+ long nMultiplyValue = aArrays.getMultiplyValue();
+ long nShiftValue = aArrays.getShiftValue();
+
+ long nRadius = rShared.mnRadius;
+ long nComponentWidth = rShared.mnComponentWidth;
+ long nDiv = rShared.mnDiv;
+
+ Scanline pSourcePointer;
+ Scanline pDestinationPointer;
+
+ long nXPosition;
+ long nStackIndex;
+ long nStackIndexStart;
+ long nWeight;
+
+ aArrays.initializeWeightAndPositions(nLastIndexX);
+
+ long* nSum = aArrays.mnSumVector.data();
+ long* nInSum = aArrays.mnInSumVector.data();
+ long* nOutSum = aArrays.mnOutSumVector.data();
+
+ long* pPositionPointer = aArrays.maPositionTable.data();
+ long* pWeightPointer = aArrays.maWeightTable.data();
+
+ for (long y = nStart; y <= nEnd; y++)
+ {
+ SumFunction::set(nSum, 0L);
+ SumFunction::set(nInSum, 0L);
+ SumFunction::set(nOutSum, 0L);
+
+ // 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 (long 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 (long 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, long nStart, long nEnd)
+{
+ BitmapReadAccess* pReadAccess = rShared.mpReadAccess;
+ BitmapWriteAccess* pWriteAccess = rShared.mpWriteAccess;
+
+ BlurArrays aArrays(rShared);
+
+ sal_uInt8* pStack = aArrays.maStackBuffer.data();
+ sal_uInt8* pStackPtr;
+
+ long nHeight = pReadAccess->Height();
+ long nLastIndexY = nHeight - 1;
+
+ long nMultiplyValue = aArrays.getMultiplyValue();
+ long nShiftValue = aArrays.getShiftValue();
+
+ long nRadius = rShared.mnRadius;
+ long nComponentWidth = rShared.mnComponentWidth;
+ long nDiv = rShared.mnDiv;
+
+ Scanline pSourcePointer;
+ Scanline pDestinationPointer;
+
+ long nYPosition;
+ long nStackIndex;
+ long nStackIndexStart;
+ long nWeight;
+
+ aArrays.initializeWeightAndPositions(nLastIndexY);
+
+ long* nSum = aArrays.mnSumVector.data();
+ long* nInSum = aArrays.mnInSumVector.data();
+ long* nOutSum = aArrays.mnOutSumVector.data();
+ long* pPositionPointer = aArrays.maPositionTable.data();
+ long* pWeightPointer = aArrays.maWeightTable.data();
+
+ for (long x = nStart; x <= nEnd; x++)
+ {
+ SumFunction::set(nSum, 0L);
+ SumFunction::set(nInSum, 0L);
+ SumFunction::set(nOutSum, 0L);
+
+ // 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 (long 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 (long 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 long nThreadStrip = 16;
+
+void runStackBlur(Bitmap& rBitmap, const long nRadius, const long nComponentWidth,
+ const long 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 long nLastIndex = pReadAccess->Height() - 1;
+ long nStripStart = 0;
+ for (; nStripStart < nLastIndex - nThreadStrip; nStripStart += nThreadStrip)
+ {
+ long nStripEnd = nStripStart + nThreadStrip - 1;
+ auto pTask(std::make_unique<BlurTask>(pTag, pBlurHorizontalFn, aSharedData,
+ nStripStart, nStripEnd));
+ rShared.pushTask(std::move(pTask));
+ }
+ // Do the last (or the only) strip in main thread without threading overhead
+ pBlurHorizontalFn(aSharedData, nStripStart, nLastIndex);
+ rShared.waitUntilDone(pTag);
+ }
+ {
+ Bitmap::ScopedReadAccess pReadAccess(rBitmap);
+ BitmapScopedWriteAccess pWriteAccess(rBitmap);
+ BlurSharedData aSharedData(pReadAccess.get(), pWriteAccess.get(), nRadius,
+ nComponentWidth, nColorChannels);
+
+ const long nLastIndex = pReadAccess->Width() - 1;
+ long nStripStart = 0;
+ for (; nStripStart < nLastIndex - nThreadStrip; nStripStart += nThreadStrip)
+ {
+ long nStripEnd = nStripStart + nThreadStrip - 1;
+ auto pTask(std::make_unique<BlurTask>(pTag, pBlurVerticalFn, aSharedData,
+ nStripStart, nStripEnd));
+ rShared.pushTask(std::move(pTask));
+ }
+ // Do the last (or the only) strip in main thread without threading overhead
+ pBlurVerticalFn(aSharedData, nStripStart, nLastIndex);
+ 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);
+ long nFirstIndex = 0;
+ long nLastIndex = pReadAccess->Height() - 1;
+ pBlurHorizontalFn(aSharedData, nFirstIndex, nLastIndex);
+ }
+ {
+ Bitmap::ScopedReadAccess pReadAccess(rBitmap);
+ BitmapScopedWriteAccess pWriteAccess(rBitmap);
+ BlurSharedData aSharedData(pReadAccess.get(), pWriteAccess.get(), nRadius,
+ nComponentWidth, nColorChannels);
+ long nFirstIndex = 0;
+ long 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 long 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 long 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.GetMask());
+}
+
+Bitmap BitmapFilterStackBlur::filter(Bitmap const& rBitmap) const
+{
+ Bitmap bitmapCopy(rBitmap);
+ ScanlineFormat nScanlineFormat;
+ {
+ Bitmap::ScopedReadAccess pReadAccess(bitmapCopy);
+ nScanlineFormat = pReadAccess->GetScanlineFormat();
+ }
+
+ 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: */
diff --git a/vcl/source/bitmap/BitmapGaussianSeparableBlurFilter.cxx b/vcl/source/bitmap/BitmapGaussianSeparableBlurFilter.cxx
new file mode 100644
index 000000000..c264dcd13
--- /dev/null
+++ b/vcl/source/bitmap/BitmapGaussianSeparableBlurFilter.cxx
@@ -0,0 +1,215 @@
+/* -*- 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 <tools/helpers.hxx>
+#include <vcl/bitmap.hxx>
+#include <vcl/bitmapex.hxx>
+#include <vcl/bitmapaccess.hxx>
+#include <vcl/BitmapGaussianSeparableBlurFilter.hxx>
+
+#include <bitmapwriteaccess.hxx>
+
+BitmapEx BitmapGaussianSeparableBlurFilter::execute(BitmapEx const& rBitmapEx) const
+{
+ Bitmap aBitmap(rBitmapEx.GetBitmap());
+
+ const long nWidth = aBitmap.GetSizePixel().Width();
+ const long nHeight = aBitmap.GetSizePixel().Height();
+
+ // Prepare Blur Vector
+ int aNumberOfContributions;
+ std::vector<double> aBlurVector(makeBlurKernel(mfRadius, aNumberOfContributions));
+ std::vector<double> aWeights;
+ std::vector<int> aPixels;
+ std::vector<int> aCounts;
+
+ // Do horizontal filtering
+ blurContributions(nWidth, aNumberOfContributions, aBlurVector, aWeights, aPixels, aCounts);
+
+ Bitmap::ScopedReadAccess pReadAcc(aBitmap);
+
+ // switch coordinates as convolution pass transposes result
+ Bitmap aNewBitmap(Size(nHeight, nWidth), 24);
+
+ bool bResult = convolutionPass(aBitmap, aNewBitmap, pReadAcc.get(), aNumberOfContributions,
+ aWeights.data(), aPixels.data(), aCounts.data());
+
+ // Cleanup
+ pReadAcc.reset();
+ aWeights.clear();
+ aPixels.clear();
+ aCounts.clear();
+
+ if (!bResult)
+ {
+ aBlurVector.clear();
+ }
+ else
+ {
+ // Swap current bitmap with new bitmap
+ aBitmap.ReassignWithSize(aNewBitmap);
+
+ // Do vertical filtering
+ blurContributions(nHeight, aNumberOfContributions, aBlurVector, aWeights, aPixels, aCounts);
+
+ pReadAcc = Bitmap::ScopedReadAccess(aBitmap);
+ aNewBitmap = Bitmap(Size(nWidth, nHeight), 24);
+ bResult = convolutionPass(aBitmap, aNewBitmap, pReadAcc.get(), aNumberOfContributions,
+ aWeights.data(), aPixels.data(), aCounts.data());
+
+ // Cleanup
+ pReadAcc.reset();
+ aWeights.clear();
+ aCounts.clear();
+ aPixels.clear();
+ aBlurVector.clear();
+
+ if (bResult)
+ aBitmap.ReassignWithSize(aNewBitmap); // swap current bitmap with new bitmap
+ }
+
+ if (bResult)
+ return BitmapEx(aBitmap);
+
+ return BitmapEx();
+}
+
+bool BitmapGaussianSeparableBlurFilter::convolutionPass(const Bitmap& rBitmap, Bitmap& aNewBitmap,
+ BitmapReadAccess const* pReadAcc,
+ int aNumberOfContributions,
+ const double* pWeights, int const* pPixels,
+ const int* pCount)
+{
+ if (!pReadAcc)
+ return false;
+
+ BitmapScopedWriteAccess pWriteAcc(aNewBitmap);
+ if (!pWriteAcc)
+ return false;
+
+ const int nHeight = rBitmap.GetSizePixel().Height();
+ assert(rBitmap.GetSizePixel().Height() == aNewBitmap.GetSizePixel().Width());
+ const int nWidth = rBitmap.GetSizePixel().Width();
+ assert(rBitmap.GetSizePixel().Width() == aNewBitmap.GetSizePixel().Height());
+
+ BitmapColor aColor;
+ double aValueRed, aValueGreen, aValueBlue;
+ double aSum, aWeight;
+ int aBaseIndex, aIndex;
+
+ for (int nSourceY = 0; nSourceY < nHeight; ++nSourceY)
+ {
+ for (int nSourceX = 0; nSourceX < nWidth; ++nSourceX)
+ {
+ aBaseIndex = nSourceX * aNumberOfContributions;
+ aSum = aValueRed = aValueGreen = aValueBlue = 0.0;
+
+ for (int j = 0; j < pCount[nSourceX]; ++j)
+ {
+ aIndex = aBaseIndex + j;
+ aWeight = pWeights[aIndex];
+ aSum += aWeight;
+
+ aColor = pReadAcc->GetColor(nSourceY, pPixels[aIndex]);
+
+ aValueRed += aWeight * aColor.GetRed();
+ aValueGreen += aWeight * aColor.GetGreen();
+ aValueBlue += aWeight * aColor.GetBlue();
+ }
+
+ BitmapColor aResultColor(static_cast<sal_uInt8>(MinMax(aValueRed / aSum, 0, 255)),
+ static_cast<sal_uInt8>(MinMax(aValueGreen / aSum, 0, 255)),
+ static_cast<sal_uInt8>(MinMax(aValueBlue / aSum, 0, 255)));
+
+ int nDestX = nSourceY;
+ int nDestY = nSourceX;
+
+ pWriteAcc->SetPixel(nDestY, nDestX, aResultColor);
+ }
+ }
+ return true;
+}
+
+std::vector<double> BitmapGaussianSeparableBlurFilter::makeBlurKernel(const double radius,
+ int& rows)
+{
+ int intRadius = static_cast<int>(radius + 1.0);
+ rows = intRadius * 2 + 1;
+ std::vector<double> matrix(rows);
+
+ double sigma = radius / 3;
+ double radius2 = radius * radius;
+ int index = 0;
+ for (int row = -intRadius; row <= intRadius; row++)
+ {
+ double distance = row * row;
+ if (distance > radius2)
+ {
+ matrix[index] = 0.0;
+ }
+ else
+ {
+ matrix[index] = exp(-distance / (2.0 * sigma * sigma)) / sqrt(2.0 * M_PI * sigma);
+ }
+ index++;
+ }
+ return matrix;
+}
+
+void BitmapGaussianSeparableBlurFilter::blurContributions(
+ const int aSize, const int aNumberOfContributions, const std::vector<double>& rBlurVector,
+ std::vector<double>& rWeights, std::vector<int>& rPixels, std::vector<int>& rCounts)
+{
+ rWeights.resize(aSize * aNumberOfContributions);
+ rPixels.resize(aSize * aNumberOfContributions);
+ rCounts.resize(aSize);
+
+ int aLeft, aRight, aCurrentCount, aPixelIndex;
+ double aWeight;
+
+ for (int i = 0; i < aSize; i++)
+ {
+ aLeft = i - aNumberOfContributions / 2;
+ aRight = i + aNumberOfContributions / 2;
+ aCurrentCount = 0;
+ for (int j = aLeft; j <= aRight; j++)
+ {
+ aWeight = rBlurVector[aCurrentCount];
+
+ // Mirror edges
+ if (j < 0)
+ {
+ aPixelIndex = -j;
+ }
+ else if (j >= aSize)
+ {
+ aPixelIndex = (aSize - j) + aSize - 1;
+ }
+ else
+ {
+ aPixelIndex = j;
+ }
+
+ // Edge case for small bitmaps
+ if (aPixelIndex < 0 || aPixelIndex >= aSize)
+ {
+ aWeight = 0.0;
+ }
+
+ rWeights[i * aNumberOfContributions + aCurrentCount] = aWeight;
+ rPixels[i * aNumberOfContributions + aCurrentCount] = aPixelIndex;
+
+ aCurrentCount++;
+ }
+ rCounts[i] = aCurrentCount;
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/bitmap/BitmapInterpolateScaleFilter.cxx b/vcl/source/bitmap/BitmapInterpolateScaleFilter.cxx
new file mode 100644
index 000000000..73f521480
--- /dev/null
+++ b/vcl/source/bitmap/BitmapInterpolateScaleFilter.cxx
@@ -0,0 +1,237 @@
+/* -*- 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <tools/helpers.hxx>
+#include <osl/diagnose.h>
+
+#include <vcl/bitmapex.hxx>
+#include <vcl/bitmapaccess.hxx>
+
+#include <bitmapwriteaccess.hxx>
+#include <BitmapFastScaleFilter.hxx>
+#include <BitmapInterpolateScaleFilter.hxx>
+
+BitmapEx BitmapInterpolateScaleFilter::execute(BitmapEx const& rBitmapEx) const
+{
+ Bitmap aBitmap(rBitmapEx.GetBitmap());
+
+ const Size aSizePix(aBitmap.GetSizePixel());
+ const long nNewWidth = FRound(aSizePix.Width() * mfScaleX);
+ const long nNewHeight = FRound(aSizePix.Height() * mfScaleY);
+ bool bRet = false;
+
+ if ((nNewWidth > 1) && (nNewHeight > 1))
+ {
+ Bitmap::ScopedReadAccess pReadAcc(aBitmap);
+ if (pReadAcc)
+ {
+ long nWidth = pReadAcc->Width();
+ long nHeight = pReadAcc->Height();
+ Bitmap aNewBmp(Size(nNewWidth, nHeight), 24);
+ BitmapScopedWriteAccess pWriteAcc(aNewBmp);
+
+ if (pWriteAcc)
+ {
+ const long nNewWidth1 = nNewWidth - 1;
+ const long nWidth1 = pReadAcc->Width() - 1;
+ const double fRevScaleX = static_cast<double>(nWidth1) / nNewWidth1;
+
+ std::unique_ptr<long[]> pLutInt(new long[nNewWidth]);
+ std::unique_ptr<long[]> pLutFrac(new long[nNewWidth]);
+
+ for (long nX = 0, nTemp = nWidth - 2; nX < nNewWidth; nX++)
+ {
+ double fTemp = nX * fRevScaleX;
+ pLutInt[nX] = MinMax(static_cast<long>(fTemp), 0, nTemp);
+ fTemp -= pLutInt[nX];
+ pLutFrac[nX] = static_cast<long>(fTemp * 1024.);
+ }
+
+ for (long nY = 0; nY < nHeight; nY++)
+ {
+ Scanline pScanlineRead = pReadAcc->GetScanline(nY);
+ if (1 == nWidth)
+ {
+ BitmapColor aCol0;
+ if (pReadAcc->HasPalette())
+ {
+ aCol0 = pReadAcc->GetPaletteColor(
+ pReadAcc->GetIndexFromData(pScanlineRead, 0));
+ }
+ else
+ {
+ aCol0 = pReadAcc->GetPixelFromData(pScanlineRead, 0);
+ }
+
+ Scanline pScanline = pWriteAcc->GetScanline(nY);
+ for (long nX = 0; nX < nNewWidth; nX++)
+ {
+ pWriteAcc->SetPixelOnData(pScanline, nX, aCol0);
+ }
+ }
+ else
+ {
+ Scanline pScanline = pWriteAcc->GetScanline(nY);
+ for (long nX = 0; nX < nNewWidth; nX++)
+ {
+ long nTemp = pLutInt[nX];
+
+ BitmapColor aCol0, aCol1;
+ if (pReadAcc->HasPalette())
+ {
+ aCol0 = pReadAcc->GetPaletteColor(
+ pReadAcc->GetIndexFromData(pScanlineRead, nTemp++));
+ aCol1 = pReadAcc->GetPaletteColor(
+ pReadAcc->GetIndexFromData(pScanlineRead, nTemp));
+ }
+ else
+ {
+ aCol0 = pReadAcc->GetPixelFromData(pScanlineRead, nTemp++);
+ aCol1 = pReadAcc->GetPixelFromData(pScanlineRead, nTemp);
+ }
+
+ nTemp = pLutFrac[nX];
+
+ long lXR0 = aCol0.GetRed();
+ long lXG0 = aCol0.GetGreen();
+ long lXB0 = aCol0.GetBlue();
+ long lXR1 = aCol1.GetRed() - lXR0;
+ long lXG1 = aCol1.GetGreen() - lXG0;
+ long lXB1 = aCol1.GetBlue() - lXB0;
+
+ aCol0.SetRed(
+ static_cast<sal_uInt8>((lXR1 * nTemp + (lXR0 << 10)) >> 10));
+ aCol0.SetGreen(
+ static_cast<sal_uInt8>((lXG1 * nTemp + (lXG0 << 10)) >> 10));
+ aCol0.SetBlue(
+ static_cast<sal_uInt8>((lXB1 * nTemp + (lXB0 << 10)) >> 10));
+
+ pWriteAcc->SetPixelOnData(pScanline, nX, aCol0);
+ }
+ }
+ }
+
+ bRet = true;
+ }
+
+ pReadAcc.reset();
+ pWriteAcc.reset();
+
+ if (bRet)
+ {
+ bRet = false;
+ const Bitmap aOriginal(aBitmap);
+ aBitmap = aNewBmp;
+ aNewBmp = Bitmap(Size(nNewWidth, nNewHeight), 24);
+ pReadAcc = Bitmap::ScopedReadAccess(aBitmap);
+ pWriteAcc = BitmapScopedWriteAccess(aNewBmp);
+
+ if (pReadAcc && pWriteAcc)
+ {
+ const long nNewHeight1 = nNewHeight - 1;
+ const long nHeight1 = pReadAcc->Height() - 1;
+ const double fRevScaleY = static_cast<double>(nHeight1) / nNewHeight1;
+
+ std::unique_ptr<long[]> pLutInt(new long[nNewHeight]);
+ std::unique_ptr<long[]> pLutFrac(new long[nNewHeight]);
+
+ for (long nY = 0, nTemp = nHeight - 2; nY < nNewHeight; nY++)
+ {
+ double fTemp = nY * fRevScaleY;
+ pLutInt[nY] = MinMax(static_cast<long>(fTemp), 0, nTemp);
+ fTemp -= pLutInt[nY];
+ pLutFrac[nY] = static_cast<long>(fTemp * 1024.);
+ }
+
+ // after 1st step, bitmap *is* 24bit format (see above)
+ OSL_ENSURE(!pReadAcc->HasPalette(), "OOps, somehow ImplScaleInterpolate "
+ "in-between format has palette, should not "
+ "happen (!)");
+
+ for (long nX = 0; nX < nNewWidth; nX++)
+ {
+ if (1 == nHeight)
+ {
+ BitmapColor aCol0 = pReadAcc->GetPixel(0, nX);
+
+ for (long nY = 0; nY < nNewHeight; nY++)
+ {
+ pWriteAcc->SetPixel(nY, nX, aCol0);
+ }
+ }
+ else
+ {
+ for (long nY = 0; nY < nNewHeight; nY++)
+ {
+ long nTemp = pLutInt[nY];
+
+ BitmapColor aCol0 = pReadAcc->GetPixel(nTemp++, nX);
+ BitmapColor aCol1 = pReadAcc->GetPixel(nTemp, nX);
+
+ nTemp = pLutFrac[nY];
+
+ long lXR0 = aCol0.GetRed();
+ long lXG0 = aCol0.GetGreen();
+ long lXB0 = aCol0.GetBlue();
+ long lXR1 = aCol1.GetRed() - lXR0;
+ long lXG1 = aCol1.GetGreen() - lXG0;
+ long lXB1 = aCol1.GetBlue() - lXB0;
+
+ aCol0.SetRed(
+ static_cast<sal_uInt8>((lXR1 * nTemp + (lXR0 << 10)) >> 10));
+ aCol0.SetGreen(
+ static_cast<sal_uInt8>((lXG1 * nTemp + (lXG0 << 10)) >> 10));
+ aCol0.SetBlue(
+ static_cast<sal_uInt8>((lXB1 * nTemp + (lXB0 << 10)) >> 10));
+
+ pWriteAcc->SetPixel(nY, nX, aCol0);
+ }
+ }
+ }
+
+ bRet = true;
+ }
+
+ pReadAcc.reset();
+ pWriteAcc.reset();
+
+ if (bRet)
+ {
+ aOriginal.AdaptBitCount(aNewBmp);
+ aBitmap = aNewBmp;
+ }
+ }
+ }
+ }
+
+ if (!bRet)
+ {
+ // fallback to fast scale filter
+ BitmapEx aBmpEx(aBitmap);
+ bRet = BitmapFilter::Filter(aBmpEx, BitmapFastScaleFilter(mfScaleX, mfScaleY));
+ aBitmap = aBmpEx.GetBitmap();
+ }
+
+ if (bRet)
+ return BitmapEx(aBitmap);
+
+ return BitmapEx();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/bitmap/BitmapLightenFilter.cxx b/vcl/source/bitmap/BitmapLightenFilter.cxx
new file mode 100644
index 000000000..45013b143
--- /dev/null
+++ b/vcl/source/bitmap/BitmapLightenFilter.cxx
@@ -0,0 +1,69 @@
+/* -*- 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 <basegfx/color/bcolortools.hxx>
+
+#include <vcl/bitmapaccess.hxx>
+
+#include <bitmapwriteaccess.hxx>
+#include <BitmapLightenFilter.hxx>
+
+BitmapEx BitmapLightenFilter::execute(BitmapEx const& rBitmapEx) const
+{
+ const Size aSize(rBitmapEx.GetSizePixel());
+
+ Bitmap aBitmap(rBitmapEx.GetBitmap());
+ Bitmap aDarkBitmap(aSize, 24);
+
+ Bitmap::ScopedReadAccess pRead(aBitmap);
+ BitmapScopedWriteAccess pWrite(aDarkBitmap);
+
+ if (pRead && pWrite)
+ {
+ for (long nY = 0; nY < aSize.Height(); ++nY)
+ {
+ Scanline pScanline = pWrite->GetScanline(nY);
+ Scanline pScanlineRead = pRead->GetScanline(nY);
+ for (long nX = 0; nX < aSize.Width(); ++nX)
+ {
+ BitmapColor aBmpColor
+ = pRead->HasPalette()
+ ? pRead->GetPaletteColor(pRead->GetIndexFromData(pScanlineRead, nX))
+ : pRead->GetPixelFromData(pScanlineRead, nX);
+ aBmpColor.Invert();
+ basegfx::BColor aBColor(aBmpColor.getBColor());
+ aBColor = basegfx::utils::rgb2hsl(aBColor);
+
+ double fHue = aBColor.getRed();
+ fHue += 180.0;
+
+ while (fHue > 360.0)
+ {
+ fHue -= 360.0;
+ }
+
+ aBColor.setRed(fHue);
+
+ aBColor = basegfx::utils::hsl2rgb(aBColor);
+ aBmpColor.SetRed((aBColor.getRed() * 255.0) + 0.5);
+ aBmpColor.SetGreen((aBColor.getGreen() * 255.0) + 0.5);
+ aBmpColor.SetBlue((aBColor.getBlue() * 255.0) + 0.5);
+
+ pWrite->SetPixelOnData(pScanline, nX, aBmpColor);
+ }
+ }
+ }
+ pWrite.reset();
+ pRead.reset();
+
+ return BitmapEx(aDarkBitmap, rBitmapEx.GetAlpha());
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/bitmap/BitmapMedianFilter.cxx b/vcl/source/bitmap/BitmapMedianFilter.cxx
new file mode 100644
index 000000000..60edc557a
--- /dev/null
+++ b/vcl/source/bitmap/BitmapMedianFilter.cxx
@@ -0,0 +1,218 @@
+/* -*- 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/bitmap.hxx>
+#include <vcl/bitmapex.hxx>
+#include <vcl/bitmapaccess.hxx>
+#include <vcl/BitmapMedianFilter.hxx>
+
+#include <bitmapwriteaccess.hxx>
+
+#define S2(a, b) \
+ { \
+ long t; \
+ if ((t = b - a) < 0) \
+ { \
+ a += t; \
+ b -= t; \
+ } \
+ }
+#define MN3(a, b, c) \
+ S2(a, b); \
+ S2(a, c);
+#define MX3(a, b, c) \
+ S2(b, c); \
+ S2(a, c);
+#define MNMX3(a, b, c) \
+ MX3(a, b, c); \
+ S2(a, b);
+#define MNMX4(a, b, c, d) \
+ S2(a, b); \
+ S2(c, d); \
+ S2(a, c); \
+ S2(b, d);
+#define MNMX5(a, b, c, d, e) \
+ S2(a, b); \
+ S2(c, d); \
+ MN3(a, c, e); \
+ MX3(b, d, e);
+#define MNMX6(a, b, c, d, e, f) \
+ S2(a, d); \
+ S2(b, e); \
+ S2(c, f); \
+ MN3(a, b, c); \
+ MX3(d, e, f);
+
+BitmapEx BitmapMedianFilter::execute(BitmapEx const& rBitmapEx) const
+{
+ Bitmap aBitmap(rBitmapEx.GetBitmap());
+
+ Bitmap::ScopedReadAccess pReadAcc(aBitmap);
+ bool bRet = false;
+
+ if (pReadAcc)
+ {
+ Bitmap aNewBmp(aBitmap.GetSizePixel(), 24);
+ BitmapScopedWriteAccess pWriteAcc(aNewBmp);
+
+ if (pWriteAcc)
+ {
+ const long nWidth = pWriteAcc->Width(), nWidth2 = nWidth + 2;
+ const long nHeight = pWriteAcc->Height(), nHeight2 = nHeight + 2;
+ std::unique_ptr<long[]> pColm(new long[nWidth2]);
+ std::unique_ptr<long[]> pRows(new long[nHeight2]);
+ std::unique_ptr<BitmapColor[]> pColRow1(new BitmapColor[nWidth2]);
+ std::unique_ptr<BitmapColor[]> pColRow2(new BitmapColor[nWidth2]);
+ std::unique_ptr<BitmapColor[]> pColRow3(new BitmapColor[nWidth2]);
+ BitmapColor* pRowTmp1 = pColRow1.get();
+ BitmapColor* pRowTmp2 = pColRow2.get();
+ BitmapColor* pRowTmp3 = pColRow3.get();
+ BitmapColor* pColor;
+ long nY, nX, i;
+ long nR1, nR2, nR3, nR4, nR5, nR6, nR7, nR8, nR9;
+ long nG1, nG2, nG3, nG4, nG5, nG6, nG7, nG8, nG9;
+ long nB1, nB2, nB3, nB4, nB5, nB6, nB7, nB8, nB9;
+
+ // create column LUT
+ for (i = 0; i < nWidth2; i++)
+ pColm[i] = (i > 0) ? (i - 1) : 0;
+
+ pColm[nWidth + 1] = pColm[nWidth];
+
+ // create row LUT
+ for (i = 0; i < nHeight2; i++)
+ pRows[i] = (i > 0) ? (i - 1) : 0;
+
+ pRows[nHeight + 1] = pRows[nHeight];
+
+ // read first three rows of bitmap color
+ if (nHeight2 > 2)
+ {
+ for (i = 0; i < nWidth2; i++)
+ {
+ pColRow1[i] = pReadAcc->GetColor(pRows[0], pColm[i]);
+ pColRow2[i] = pReadAcc->GetColor(pRows[1], pColm[i]);
+ pColRow3[i] = pReadAcc->GetColor(pRows[2], pColm[i]);
+ }
+ }
+
+ // do median filtering
+ for (nY = 0; nY < nHeight;)
+ {
+ Scanline pScanline = pWriteAcc->GetScanline(nY);
+ for (nX = 0; nX < nWidth; nX++)
+ {
+ pColor = pRowTmp1 + nX;
+ nR1 = pColor->GetRed();
+ nG1 = pColor->GetGreen();
+ nB1 = pColor->GetBlue();
+ nR2 = (++pColor)->GetRed();
+ nG2 = pColor->GetGreen();
+ nB2 = pColor->GetBlue();
+ nR3 = (++pColor)->GetRed();
+ nG3 = pColor->GetGreen();
+ nB3 = pColor->GetBlue();
+
+ pColor = pRowTmp2 + nX;
+ nR4 = pColor->GetRed();
+ nG4 = pColor->GetGreen();
+ nB4 = pColor->GetBlue();
+ nR5 = (++pColor)->GetRed();
+ nG5 = pColor->GetGreen();
+ nB5 = pColor->GetBlue();
+ nR6 = (++pColor)->GetRed();
+ nG6 = pColor->GetGreen();
+ nB6 = pColor->GetBlue();
+
+ pColor = pRowTmp3 + nX;
+ nR7 = pColor->GetRed();
+ nG7 = pColor->GetGreen();
+ nB7 = pColor->GetBlue();
+ nR8 = (++pColor)->GetRed();
+ nG8 = pColor->GetGreen();
+ nB8 = pColor->GetBlue();
+ nR9 = (++pColor)->GetRed();
+ nG9 = pColor->GetGreen();
+ nB9 = pColor->GetBlue();
+
+ MNMX6(nR1, nR2, nR3, nR4, nR5, nR6);
+ MNMX5(nR7, nR2, nR3, nR4, nR5);
+ MNMX4(nR8, nR2, nR3, nR4);
+ MNMX3(nR9, nR2, nR3);
+
+ MNMX6(nG1, nG2, nG3, nG4, nG5, nG6);
+ MNMX5(nG7, nG2, nG3, nG4, nG5);
+ MNMX4(nG8, nG2, nG3, nG4);
+ MNMX3(nG9, nG2, nG3);
+
+ MNMX6(nB1, nB2, nB3, nB4, nB5, nB6);
+ MNMX5(nB7, nB2, nB3, nB4, nB5);
+ MNMX4(nB8, nB2, nB3, nB4);
+ MNMX3(nB9, nB2, nB3);
+
+ // set destination color
+ pWriteAcc->SetPixelOnData(pScanline, nX,
+ BitmapColor(static_cast<sal_uInt8>(nR2),
+ static_cast<sal_uInt8>(nG2),
+ static_cast<sal_uInt8>(nB2)));
+ }
+
+ if (++nY < nHeight)
+ {
+ if (pRowTmp1 == pColRow1.get())
+ {
+ pRowTmp1 = pColRow2.get();
+ pRowTmp2 = pColRow3.get();
+ pRowTmp3 = pColRow1.get();
+ }
+ else if (pRowTmp1 == pColRow2.get())
+ {
+ pRowTmp1 = pColRow3.get();
+ pRowTmp2 = pColRow1.get();
+ pRowTmp3 = pColRow2.get();
+ }
+ else
+ {
+ pRowTmp1 = pColRow1.get();
+ pRowTmp2 = pColRow2.get();
+ pRowTmp3 = pColRow3.get();
+ }
+
+ for (i = 0; i < nWidth2; i++)
+ pRowTmp3[i] = pReadAcc->GetColor(pRows[nY + 2], pColm[i]);
+ }
+ }
+
+ pWriteAcc.reset();
+
+ bRet = true;
+ }
+
+ pReadAcc.reset();
+
+ if (bRet)
+ {
+ const MapMode aMap(aBitmap.GetPrefMapMode());
+ const Size aPrefSize(aBitmap.GetPrefSize());
+
+ aBitmap = aNewBmp;
+
+ aBitmap.SetPrefMapMode(aMap);
+ aBitmap.SetPrefSize(aPrefSize);
+ }
+ }
+
+ if (bRet)
+ return BitmapEx(aBitmap);
+
+ return BitmapEx();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/bitmap/BitmapMonochromeFilter.cxx b/vcl/source/bitmap/BitmapMonochromeFilter.cxx
new file mode 100644
index 000000000..72bacf849
--- /dev/null
+++ b/vcl/source/bitmap/BitmapMonochromeFilter.cxx
@@ -0,0 +1,101 @@
+/* -*- 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/bitmap.hxx>
+#include <vcl/bitmapex.hxx>
+#include <vcl/BitmapMonochromeFilter.hxx>
+#include <vcl/bitmapaccess.hxx>
+
+#include <bitmapwriteaccess.hxx>
+
+BitmapEx BitmapMonochromeFilter::execute(BitmapEx const& aBitmapEx) const
+{
+ Bitmap aBitmap = aBitmapEx.GetBitmap();
+ Bitmap::ScopedReadAccess pReadAcc(aBitmap);
+ bool bRet = false;
+
+ if (pReadAcc)
+ {
+ Bitmap aNewBmp(aBitmap.GetSizePixel(), 1);
+ BitmapScopedWriteAccess pWriteAcc(aNewBmp);
+
+ if (pWriteAcc)
+ {
+ const BitmapColor aBlack(pWriteAcc->GetBestMatchingColor(COL_BLACK));
+ const BitmapColor aWhite(pWriteAcc->GetBestMatchingColor(COL_WHITE));
+ const long nWidth = pWriteAcc->Width();
+ const long nHeight = pWriteAcc->Height();
+
+ if (pReadAcc->HasPalette())
+ {
+ for (long nY = 0; nY < nHeight; nY++)
+ {
+ Scanline pScanline = pWriteAcc->GetScanline(nY);
+ Scanline pScanlineRead = pReadAcc->GetScanline(nY);
+ for (long nX = 0; nX < nWidth; nX++)
+ {
+ const sal_uInt8 cIndex = pReadAcc->GetIndexFromData(pScanlineRead, nX);
+ if (pReadAcc->GetPaletteColor(cIndex).GetLuminance() >= mcThreshold)
+ {
+ pWriteAcc->SetPixelOnData(pScanline, nX, aWhite);
+ }
+ else
+ {
+ pWriteAcc->SetPixelOnData(pScanline, nX, aBlack);
+ }
+ }
+ }
+ }
+ else
+ {
+ for (long nY = 0; nY < nHeight; nY++)
+ {
+ Scanline pScanline = pWriteAcc->GetScanline(nY);
+ Scanline pScanlineRead = pReadAcc->GetScanline(nY);
+ for (long nX = 0; nX < nWidth; nX++)
+ {
+ if (pReadAcc->GetPixelFromData(pScanlineRead, nX).GetLuminance()
+ >= mcThreshold)
+ {
+ pWriteAcc->SetPixelOnData(pScanline, nX, aWhite);
+ }
+ else
+ {
+ pWriteAcc->SetPixelOnData(pScanline, nX, aBlack);
+ }
+ }
+ }
+ }
+
+ pWriteAcc.reset();
+ bRet = true;
+ }
+
+ pReadAcc.reset();
+
+ if (bRet)
+ {
+ const MapMode aMap(aBitmap.GetPrefMapMode());
+ const Size aSize(aBitmap.GetPrefSize());
+
+ aBitmap = aNewBmp;
+
+ aBitmap.SetPrefMapMode(aMap);
+ aBitmap.SetPrefSize(aSize);
+ }
+ }
+
+ if (bRet)
+ return BitmapEx(aBitmap);
+
+ return BitmapEx();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/bitmap/BitmapMosaicFilter.cxx b/vcl/source/bitmap/BitmapMosaicFilter.cxx
new file mode 100644
index 000000000..ee0d10275
--- /dev/null
+++ b/vcl/source/bitmap/BitmapMosaicFilter.cxx
@@ -0,0 +1,185 @@
+/* -*- 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/bitmap.hxx>
+#include <vcl/bitmapex.hxx>
+#include <vcl/bitmapaccess.hxx>
+#include <vcl/BitmapMosaicFilter.hxx>
+
+#include <bitmapwriteaccess.hxx>
+
+BitmapEx BitmapMosaicFilter::execute(BitmapEx const& rBitmapEx) const
+{
+ Bitmap aBitmap(rBitmapEx.GetBitmap());
+
+ bool bRet = false;
+
+ if (mnTileWidth > 1 || mnTileHeight > 1)
+ {
+ std::unique_ptr<Bitmap> pNewBmp;
+ BitmapReadAccess* pReadAcc;
+ BitmapWriteAccess* pWriteAcc;
+
+ if (aBitmap.GetBitCount() > 8)
+ {
+ pReadAcc = pWriteAcc = aBitmap.AcquireWriteAccess();
+ }
+ else
+ {
+ pNewBmp.reset(new Bitmap(aBitmap.GetSizePixel(), 24));
+ pReadAcc = aBitmap.AcquireReadAccess();
+ pWriteAcc = pNewBmp->AcquireWriteAccess();
+ }
+
+ bool bConditionsMet = false;
+ long nWidth(0);
+ long nHeight(0);
+ if (pReadAcc && pWriteAcc)
+ {
+ nWidth = pReadAcc->Width();
+ nHeight = pReadAcc->Height();
+ bConditionsMet = (nWidth > 0 && nHeight > 0);
+ }
+
+ if (bConditionsMet)
+ {
+ BitmapColor aCol;
+ long nX, nY, nX1, nX2, nY1, nY2, nSumR, nSumG, nSumB;
+ double fArea_1;
+
+ nY1 = 0;
+ nY2 = mnTileHeight - 1;
+
+ if (nY2 >= nHeight)
+ nY2 = nHeight - 1;
+
+ do
+ {
+ nX1 = 0;
+ nX2 = mnTileWidth - 1;
+
+ if (nX2 >= nWidth)
+ nX2 = nWidth - 1;
+
+ fArea_1 = 1.0 / ((nX2 - nX1 + 1) * (nY2 - nY1 + 1));
+
+ if (!pNewBmp)
+ {
+ do
+ {
+ for (nY = nY1, nSumR = nSumG = nSumB = 0; nY <= nY2; nY++)
+ {
+ Scanline pScanlineRead = pReadAcc->GetScanline(nY);
+ for (nX = nX1; nX <= nX2; nX++)
+ {
+ aCol = pReadAcc->GetPixelFromData(pScanlineRead, nX);
+ nSumR += aCol.GetRed();
+ nSumG += aCol.GetGreen();
+ nSumB += aCol.GetBlue();
+ }
+ }
+
+ aCol.SetRed(static_cast<sal_uInt8>(nSumR * fArea_1));
+ aCol.SetGreen(static_cast<sal_uInt8>(nSumG * fArea_1));
+ aCol.SetBlue(static_cast<sal_uInt8>(nSumB * fArea_1));
+
+ for (nY = nY1; nY <= nY2; nY++)
+ {
+ Scanline pScanline = pWriteAcc->GetScanline(nY);
+ for (nX = nX1; nX <= nX2; nX++)
+ pWriteAcc->SetPixelOnData(pScanline, nX, aCol);
+ }
+
+ nX1 += mnTileWidth;
+ nX2 += mnTileWidth;
+
+ if (nX2 >= nWidth)
+ {
+ nX2 = nWidth - 1;
+ fArea_1 = 1.0 / ((nX2 - nX1 + 1) * (nY2 - nY1 + 1));
+ }
+ } while (nX1 < nWidth);
+ }
+ else
+ {
+ do
+ {
+ for (nY = nY1, nSumR = nSumG = nSumB = 0; nY <= nY2; nY++)
+ {
+ Scanline pScanlineRead = pReadAcc->GetScanline(nY);
+ for (nX = nX1; nX <= nX2; nX++)
+ {
+ const BitmapColor& rCol = pReadAcc->GetPaletteColor(
+ pReadAcc->GetIndexFromData(pScanlineRead, nX));
+ nSumR += rCol.GetRed();
+ nSumG += rCol.GetGreen();
+ nSumB += rCol.GetBlue();
+ }
+ }
+
+ aCol.SetRed(static_cast<sal_uInt8>(nSumR * fArea_1));
+ aCol.SetGreen(static_cast<sal_uInt8>(nSumG * fArea_1));
+ aCol.SetBlue(static_cast<sal_uInt8>(nSumB * fArea_1));
+
+ for (nY = nY1; nY <= nY2; nY++)
+ {
+ Scanline pScanline = pWriteAcc->GetScanline(nY);
+ for (nX = nX1; nX <= nX2; nX++)
+ pWriteAcc->SetPixelOnData(pScanline, nX, aCol);
+ }
+
+ nX1 += mnTileWidth;
+ nX2 += mnTileWidth;
+
+ if (nX2 >= nWidth)
+ {
+ nX2 = nWidth - 1;
+ fArea_1 = 1.0 / ((nX2 - nX1 + 1) * (nY2 - nY1 + 1));
+ }
+ } while (nX1 < nWidth);
+ }
+
+ nY1 += mnTileHeight;
+ nY2 += mnTileHeight;
+
+ if (nY2 >= nHeight)
+ nY2 = nHeight - 1;
+
+ } while (nY1 < nHeight);
+
+ bRet = true;
+ }
+
+ Bitmap::ReleaseAccess(pReadAcc);
+
+ if (pNewBmp)
+ {
+ Bitmap::ReleaseAccess(pWriteAcc);
+
+ if (bRet)
+ {
+ const MapMode aMap(aBitmap.GetPrefMapMode());
+ const Size aPrefSize(aBitmap.GetPrefSize());
+
+ aBitmap = *pNewBmp;
+
+ aBitmap.SetPrefMapMode(aMap);
+ aBitmap.SetPrefSize(aPrefSize);
+ }
+ }
+ }
+
+ if (bRet)
+ return BitmapEx(aBitmap);
+
+ return BitmapEx();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/bitmap/BitmapPopArtFilter.cxx b/vcl/source/bitmap/BitmapPopArtFilter.cxx
new file mode 100644
index 000000000..a335f04fb
--- /dev/null
+++ b/vcl/source/bitmap/BitmapPopArtFilter.cxx
@@ -0,0 +1,97 @@
+/* -*- 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/bitmap.hxx>
+#include <vcl/bitmapex.hxx>
+#include <vcl/BitmapPopArtFilter.hxx>
+
+#include <bitmapwriteaccess.hxx>
+
+BitmapEx BitmapPopArtFilter::execute(BitmapEx const& rBitmapEx) const
+{
+ Bitmap aBitmap(rBitmapEx.GetBitmap());
+
+ bool bRet = (aBitmap.GetBitCount() <= 8) || aBitmap.Convert(BmpConversion::N8BitColors);
+
+ if (bRet)
+ {
+ bRet = false;
+
+ BitmapScopedWriteAccess pWriteAcc(aBitmap);
+
+ if (pWriteAcc)
+ {
+ const long nWidth = pWriteAcc->Width();
+ const long nHeight = pWriteAcc->Height();
+ const int nEntryCount = 1 << pWriteAcc->GetBitCount();
+ int n = 0;
+ std::vector<PopArtEntry> aPopArtTable(nEntryCount);
+
+ for (n = 0; n < nEntryCount; n++)
+ {
+ PopArtEntry& rEntry = aPopArtTable[n];
+ rEntry.mnIndex = static_cast<sal_uInt16>(n);
+ rEntry.mnCount = 0;
+ }
+
+ // get pixel count for each palette entry
+ for (long nY = 0; nY < nHeight; nY++)
+ {
+ Scanline pScanline = pWriteAcc->GetScanline(nY);
+ for (long nX = 0; nX < nWidth; nX++)
+ {
+ aPopArtTable[pWriteAcc->GetIndexFromData(pScanline, nX)].mnCount++;
+ }
+ }
+
+ // sort table
+ std::sort(aPopArtTable.begin(), aPopArtTable.end(),
+ [](const PopArtEntry& lhs, const PopArtEntry& rhs) {
+ return lhs.mnCount < rhs.mnCount;
+ });
+
+ // get last used entry
+ sal_uLong nFirstEntry;
+ sal_uLong nLastEntry = 0;
+
+ for (n = 0; n < nEntryCount; n++)
+ {
+ if (aPopArtTable[n].mnCount)
+ nLastEntry = n;
+ }
+
+ // rotate palette (one entry)
+ const BitmapColor aFirstCol(pWriteAcc->GetPaletteColor(
+ sal::static_int_cast<sal_uInt16>(aPopArtTable[0].mnIndex)));
+
+ for (nFirstEntry = 0; nFirstEntry < nLastEntry; nFirstEntry++)
+ {
+ pWriteAcc->SetPaletteColor(
+ sal::static_int_cast<sal_uInt16>(aPopArtTable[nFirstEntry].mnIndex),
+ pWriteAcc->GetPaletteColor(
+ sal::static_int_cast<sal_uInt16>(aPopArtTable[nFirstEntry + 1].mnIndex)));
+ }
+
+ pWriteAcc->SetPaletteColor(
+ sal::static_int_cast<sal_uInt16>(aPopArtTable[nLastEntry].mnIndex), aFirstCol);
+
+ // cleanup
+ pWriteAcc.reset();
+ bRet = true;
+ }
+ }
+
+ if (bRet)
+ return BitmapEx(aBitmap);
+
+ return BitmapEx();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/bitmap/BitmapScaleConvolutionFilter.cxx b/vcl/source/bitmap/BitmapScaleConvolutionFilter.cxx
new file mode 100644
index 000000000..b679e172d
--- /dev/null
+++ b/vcl/source/bitmap/BitmapScaleConvolutionFilter.cxx
@@ -0,0 +1,391 @@
+/* -*- 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <osl/diagnose.h>
+
+#include <tools/helpers.hxx>
+#include <vcl/bitmapaccess.hxx>
+
+#include <bitmapwriteaccess.hxx>
+#include <BitmapScaleConvolutionFilter.hxx>
+
+#include <algorithm>
+#include <memory>
+
+namespace vcl
+{
+
+namespace
+{
+
+void ImplCalculateContributions(
+ const long aSourceSize,
+ const long aDestinationSize,
+ long& aNumberOfContributions,
+ std::vector<sal_Int16>& rWeights,
+ std::vector<sal_Int32>& rPixels,
+ std::vector<sal_Int32>& rCounts,
+ const Kernel& aKernel)
+{
+ const double fSamplingRadius(aKernel.GetWidth());
+ const double fScale(aDestinationSize / static_cast< double >(aSourceSize));
+ const double fScaledRadius((fScale < 1.0) ? fSamplingRadius / fScale : fSamplingRadius);
+ const double fFilterFactor(std::min(fScale, 1.0));
+
+ aNumberOfContributions = (long(fabs(ceil(fScaledRadius))) * 2) + 1;
+ const long nAllocSize(aDestinationSize * aNumberOfContributions);
+ rWeights.resize(nAllocSize);
+ rPixels.resize(nAllocSize);
+ rCounts.resize(aDestinationSize);
+
+ for(long i(0); i < aDestinationSize; i++)
+ {
+ const long aIndex(i * aNumberOfContributions);
+ const double aCenter(i / fScale);
+ const sal_Int32 aLeft(static_cast< sal_Int32 >(floor(aCenter - fScaledRadius)));
+ const sal_Int32 aRight(static_cast< sal_Int32 >(ceil(aCenter + fScaledRadius)));
+ long aCurrentCount(0);
+
+ for(sal_Int32 j(aLeft); j <= aRight; j++)
+ {
+ const double aWeight(aKernel.Calculate(fFilterFactor * (aCenter - static_cast< double>(j))));
+
+ // Reduce calculations with ignoring weights of 0.0
+ if(fabs(aWeight) < 0.0001)
+ {
+ continue;
+ }
+
+ // Handling on edges
+ const long aPixelIndex(MinMax(j, 0, aSourceSize - 1));
+ const long nIndex(aIndex + aCurrentCount);
+
+ // scale the weight by 255 since we're converting from float to int
+ rWeights[nIndex] = aWeight * 255;
+ rPixels[nIndex] = aPixelIndex;
+
+ aCurrentCount++;
+ }
+
+ rCounts[i] = aCurrentCount;
+ }
+}
+
+bool ImplScaleConvolutionHor(Bitmap& rSource, Bitmap& rTarget, const double& rScaleX, const Kernel& aKernel)
+{
+ // Do horizontal filtering
+ OSL_ENSURE(rScaleX > 0.0, "Error in scaling: Mirror given in non-mirror-capable method (!)");
+ const long nWidth(rSource.GetSizePixel().Width());
+ const long nNewWidth(FRound(nWidth * rScaleX));
+
+ if(nWidth == nNewWidth)
+ {
+ return true;
+ }
+
+ Bitmap::ScopedReadAccess pReadAcc(rSource);
+
+ if(pReadAcc)
+ {
+ std::vector<sal_Int16> aWeights;
+ std::vector<sal_Int32> aPixels;
+ std::vector<sal_Int32> aCounts;
+ long aNumberOfContributions(0);
+
+ const long nHeight(rSource.GetSizePixel().Height());
+ ImplCalculateContributions(nWidth, nNewWidth, aNumberOfContributions, aWeights, aPixels, aCounts, aKernel);
+ rTarget = Bitmap(Size(nNewWidth, nHeight), 24);
+ BitmapScopedWriteAccess pWriteAcc(rTarget);
+ bool bResult(pWriteAcc);
+
+ if(bResult)
+ {
+ for(long y(0); y < nHeight; y++)
+ {
+ Scanline pScanline = pWriteAcc->GetScanline( y );
+ Scanline pScanlineRead = pReadAcc->GetScanline( y );
+ for(long x(0); x < nNewWidth; x++)
+ {
+ const long aBaseIndex(x * aNumberOfContributions);
+ sal_Int32 aSum(0);
+ sal_Int32 aValueRed(0);
+ sal_Int32 aValueGreen(0);
+ sal_Int32 aValueBlue(0);
+
+ for(long j(0); j < aCounts[x]; j++)
+ {
+ const long aIndex(aBaseIndex + j);
+ const sal_Int16 aWeight(aWeights[aIndex]);
+ BitmapColor aColor;
+
+ aSum += aWeight;
+
+ if(pReadAcc->HasPalette())
+ {
+ aColor = pReadAcc->GetPaletteColor(pReadAcc->GetIndexFromData(pScanlineRead, aPixels[aIndex]));
+ }
+ else
+ {
+ aColor = pReadAcc->GetPixelFromData(pScanlineRead, aPixels[aIndex]);
+ }
+
+ aValueRed += aWeight * aColor.GetRed();
+ aValueGreen += aWeight * aColor.GetGreen();
+ aValueBlue += aWeight * aColor.GetBlue();
+ }
+
+ assert(aSum != 0);
+
+ const BitmapColor aResultColor(
+ static_cast< sal_uInt8 >(MinMax(static_cast< sal_Int32 >(aValueRed / aSum), 0, 255)),
+ static_cast< sal_uInt8 >(MinMax(static_cast< sal_Int32 >(aValueGreen / aSum), 0, 255)),
+ static_cast< sal_uInt8 >(MinMax(static_cast< sal_Int32 >(aValueBlue / aSum), 0, 255)));
+
+ pWriteAcc->SetPixelOnData(pScanline, x, aResultColor);
+ }
+ }
+
+ pWriteAcc.reset();
+ }
+
+ aWeights.clear();
+ aCounts.clear();
+ aPixels.clear();
+
+ if(bResult)
+ {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool ImplScaleConvolutionVer(Bitmap& rSource, Bitmap& rTarget, const double& rScaleY, const Kernel& aKernel)
+{
+ // Do vertical filtering
+ OSL_ENSURE(rScaleY > 0.0, "Error in scaling: Mirror given in non-mirror-capable method (!)");
+ const long nHeight(rSource.GetSizePixel().Height());
+ const long nNewHeight(FRound(nHeight * rScaleY));
+
+ if(nHeight == nNewHeight)
+ {
+ return true;
+ }
+
+ Bitmap::ScopedReadAccess pReadAcc(rSource);
+
+ if(pReadAcc)
+ {
+ std::vector<sal_Int16> aWeights;
+ std::vector<sal_Int32> aPixels;
+ std::vector<sal_Int32> aCounts;
+ long aNumberOfContributions(0);
+
+ const long nWidth(rSource.GetSizePixel().Width());
+ ImplCalculateContributions(nHeight, nNewHeight, aNumberOfContributions, aWeights, aPixels, aCounts, aKernel);
+ rTarget = Bitmap(Size(nWidth, nNewHeight), 24);
+ BitmapScopedWriteAccess pWriteAcc(rTarget);
+ bool bResult(pWriteAcc);
+
+ if(pWriteAcc)
+ {
+ std::vector<BitmapColor> aScanline(nHeight);
+ for(long x(0); x < nWidth; x++)
+ {
+ for(long y(0); y < nHeight; y++)
+ if(pReadAcc->HasPalette())
+ aScanline[y] = pReadAcc->GetPaletteColor(pReadAcc->GetPixelIndex(y, x));
+ else
+ aScanline[y] = pReadAcc->GetPixel(y, x);
+ for(long y(0); y < nNewHeight; y++)
+ {
+ const long aBaseIndex(y * aNumberOfContributions);
+ sal_Int32 aSum(0);
+ sal_Int32 aValueRed(0);
+ sal_Int32 aValueGreen(0);
+ sal_Int32 aValueBlue(0);
+
+ for(long j(0); j < aCounts[y]; j++)
+ {
+ const long aIndex(aBaseIndex + j);
+ const sal_Int16 aWeight(aWeights[aIndex]);
+ aSum += aWeight;
+ const BitmapColor & aColor = aScanline[aPixels[aIndex]];
+ aValueRed += aWeight * aColor.GetRed();
+ aValueGreen += aWeight * aColor.GetGreen();
+ aValueBlue += aWeight * aColor.GetBlue();
+ }
+
+ assert(aSum != 0);
+
+ const BitmapColor aResultColor(
+ static_cast< sal_uInt8 >(MinMax(static_cast< sal_Int32 >(aValueRed / aSum), 0, 255)),
+ static_cast< sal_uInt8 >(MinMax(static_cast< sal_Int32 >(aValueGreen / aSum), 0, 255)),
+ static_cast< sal_uInt8 >(MinMax(static_cast< sal_Int32 >(aValueBlue / aSum), 0, 255)));
+
+ if(pWriteAcc->HasPalette())
+ {
+ pWriteAcc->SetPixelIndex(y, x, static_cast< sal_uInt8 >(pWriteAcc->GetBestPaletteIndex(aResultColor)));
+ }
+ else
+ {
+ pWriteAcc->SetPixel(y, x, aResultColor);
+ }
+ }
+ }
+ }
+
+ aWeights.clear();
+ aCounts.clear();
+ aPixels.clear();
+
+ if(bResult)
+ {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool ImplScaleConvolution(Bitmap& rBitmap, const double& rScaleX, const double& rScaleY, const Kernel& aKernel)
+{
+ const bool bMirrorHor(rScaleX < 0.0);
+ const bool bMirrorVer(rScaleY < 0.0);
+ const double fScaleX(bMirrorHor ? -rScaleX : rScaleX);
+ const double fScaleY(bMirrorVer ? -rScaleY : rScaleY);
+ const long nWidth(rBitmap.GetSizePixel().Width());
+ const long nHeight(rBitmap.GetSizePixel().Height());
+ const long nNewWidth(FRound(nWidth * fScaleX));
+ const long nNewHeight(FRound(nHeight * fScaleY));
+ const bool bScaleHor(nWidth != nNewWidth);
+ const bool bScaleVer(nHeight != nNewHeight);
+ const bool bMirror(bMirrorHor || bMirrorVer);
+
+ if (!bMirror && !bScaleHor && !bScaleVer)
+ {
+ return true;
+ }
+
+ bool bResult(true);
+ BmpMirrorFlags nMirrorFlags(BmpMirrorFlags::NONE);
+ bool bMirrorAfter(false);
+
+ if (bMirror)
+ {
+ if(bMirrorHor)
+ {
+ nMirrorFlags |= BmpMirrorFlags::Horizontal;
+ }
+
+ if(bMirrorVer)
+ {
+ nMirrorFlags |= BmpMirrorFlags::Vertical;
+ }
+
+ const long nStartSize(nWidth * nHeight);
+ const long nEndSize(nNewWidth * nNewHeight);
+
+ bMirrorAfter = nStartSize > nEndSize;
+
+ if(!bMirrorAfter)
+ {
+ bResult = rBitmap.Mirror(nMirrorFlags);
+ }
+ }
+
+ Bitmap aResult;
+
+ if (bResult)
+ {
+ const long nInBetweenSizeHorFirst(nHeight * nNewWidth);
+ const long nInBetweenSizeVerFirst(nNewHeight * nWidth);
+ Bitmap aSource(rBitmap);
+
+ if(nInBetweenSizeHorFirst < nInBetweenSizeVerFirst)
+ {
+ if(bScaleHor)
+ {
+ bResult = ImplScaleConvolutionHor(aSource, aResult, fScaleX, aKernel);
+ }
+
+ if(bResult && bScaleVer)
+ {
+ if(bScaleHor)
+ {
+ // copy partial result, independent of color depth
+ aSource = aResult;
+ }
+
+ bResult = ImplScaleConvolutionVer(aSource, aResult, fScaleY, aKernel);
+ }
+ }
+ else
+ {
+ if(bScaleVer)
+ {
+ bResult = ImplScaleConvolutionVer(aSource, aResult, fScaleY, aKernel);
+ }
+
+ if(bResult && bScaleHor)
+ {
+ if(bScaleVer)
+ {
+ // copy partial result, independent of color depth
+ aSource = aResult;
+ }
+
+ bResult = ImplScaleConvolutionHor(aSource, aResult, fScaleX, aKernel);
+ }
+ }
+ }
+
+ if(bResult && bMirrorAfter)
+ {
+ bResult = aResult.Mirror(nMirrorFlags);
+ }
+
+ if(bResult)
+ {
+ rBitmap.AdaptBitCount(aResult);
+ rBitmap = aResult;
+ }
+
+ return bResult;
+}
+
+} // end anonymous namespace
+
+BitmapEx BitmapScaleConvolutionFilter::execute(BitmapEx const& rBitmapEx) const
+{
+ bool bRetval = false;
+ Bitmap aBitmap(rBitmapEx.GetBitmap());
+
+ bRetval = ImplScaleConvolution(aBitmap, mrScaleX, mrScaleY, *mxKernel);
+
+ if (bRetval)
+ return BitmapEx(aBitmap);
+
+ return BitmapEx();
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/bitmap/BitmapScaleSuperFilter.cxx b/vcl/source/bitmap/BitmapScaleSuperFilter.cxx
new file mode 100644
index 000000000..ff808ddcd
--- /dev/null
+++ b/vcl/source/bitmap/BitmapScaleSuperFilter.cxx
@@ -0,0 +1,1202 @@
+/* -*- 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <comphelper/threadpool.hxx>
+
+#include <tools/helpers.hxx>
+#include <vcl/bitmapaccess.hxx>
+
+#include <bitmapwriteaccess.hxx>
+#include <BitmapScaleSuperFilter.hxx>
+
+#include <algorithm>
+#include <memory>
+#include <svdata.hxx>
+#include <sal/log.hxx>
+
+namespace {
+
+#define MAP_PRECISION 7
+
+typedef sal_Int32 BilinearWeightType;
+
+constexpr BilinearWeightType lclMaxWeight()
+{
+ return BilinearWeightType(1) << MAP_PRECISION;
+}
+
+constexpr sal_uInt8 MAP(sal_uInt8 cVal0, sal_uInt8 cVal1, BilinearWeightType nFrac)
+{
+ return sal_uInt8(((BilinearWeightType(cVal0) << MAP_PRECISION) + nFrac * (BilinearWeightType(cVal1) - BilinearWeightType(cVal0))) >> MAP_PRECISION);
+}
+
+struct ScaleContext
+{
+ BitmapReadAccess* mpSrc;
+ BitmapWriteAccess* mpDest;
+ long mnDestW;
+ bool mbHMirr;
+ bool mbVMirr;
+ std::vector<long> maMapIX;
+ std::vector<long> maMapIY;
+ std::vector<BilinearWeightType> maMapFX;
+ std::vector<BilinearWeightType> maMapFY;
+
+ ScaleContext( BitmapReadAccess *pSrc,
+ BitmapWriteAccess *pDest,
+ long nSrcW, long nDestW,
+ long nSrcH, long nDestH,
+ bool bHMirr, bool bVMirr)
+ : mpSrc(pSrc)
+ , mpDest(pDest)
+ , mnDestW(nDestW)
+ , mbHMirr(bHMirr)
+ , mbVMirr(bVMirr)
+ , maMapIX(nDestW)
+ , maMapIY(nDestH)
+ , maMapFX(nDestW)
+ , maMapFY(nDestH)
+ {
+ generateMap(nSrcW, nDestW, bHMirr, maMapIX, maMapFX);
+ generateMap(nSrcH, nDestH, bVMirr, maMapIY, maMapFY);
+ }
+
+ static void generateMap(long nSourceLength, long nDestinationLength, bool bMirrored,
+ std::vector<long> & rMapIX, std::vector<BilinearWeightType> & rMapFX)
+ {
+ const double fRevScale = (nDestinationLength > 1) ? double(nSourceLength - 1) / (nDestinationLength - 1) : 0.0;
+
+ long nTemp = nSourceLength - 2;
+ long nTempX = nSourceLength - 1;
+
+ for (long i = 0; i < nDestinationLength; i++)
+ {
+ double fTemp = i * fRevScale;
+ if (bMirrored)
+ fTemp = nTempX - fTemp;
+ rMapIX[i] = MinMax(long(fTemp), 0, nTemp);
+ rMapFX[i] = BilinearWeightType((fTemp - rMapIX[i]) * (BilinearWeightType(1) << MAP_PRECISION));
+ }
+ }
+};
+
+constexpr long constScaleThreadStrip = 32;
+
+typedef void (*ScaleRangeFn)(ScaleContext &rContext, long nStartY, long nEndY);
+
+class ScaleTask : public comphelper::ThreadTask
+{
+ ScaleRangeFn mpScaleRangeFunction;
+ ScaleContext& mrContext;
+ long mnStartY;
+ long mnEndY;
+
+public:
+ explicit ScaleTask(const std::shared_ptr<comphelper::ThreadTaskTag>& pTag,
+ ScaleRangeFn pScaleRangeFunction,
+ ScaleContext& rContext,
+ long nStartY, long nEndY)
+ : comphelper::ThreadTask(pTag)
+ , mpScaleRangeFunction(pScaleRangeFunction)
+ , mrContext(rContext)
+ , mnStartY(nStartY)
+ , mnEndY(nEndY)
+ {}
+
+ virtual void doWork() override
+ {
+ mpScaleRangeFunction(mrContext, mnStartY, mnEndY);
+ }
+};
+
+void scaleUp32bit(ScaleContext &rCtx, long nStartY, long nEndY)
+{
+ const int nColorComponents = 4;
+
+ const long nStartX = 0;
+ const long nEndX = rCtx.mnDestW - 1;
+
+ for (long nY = nStartY; nY <= nEndY; nY++)
+ {
+ long nTempY = rCtx.maMapIY[nY];
+ BilinearWeightType nTempFY = rCtx.maMapFY[nY];
+
+ Scanline pLine0 = rCtx.mpSrc->GetScanline(nTempY+0);
+ Scanline pLine1 = rCtx.mpSrc->GetScanline(nTempY+1);
+ Scanline pScanDest = rCtx.mpDest->GetScanline(nY);
+
+ sal_uInt8 nComponent1[nColorComponents];
+ sal_uInt8 nComponent2[nColorComponents];
+
+ Scanline pColorPtr0;
+ Scanline pColorPtr1;
+
+ for (long nX = nStartX; nX <= nEndX; nX++)
+ {
+ long nTempX = rCtx.maMapIX[nX];
+ BilinearWeightType nTempFX = rCtx.maMapFX[nX];
+
+ pColorPtr0 = pLine0 + nTempX * nColorComponents;
+ pColorPtr1 = pColorPtr0 + nColorComponents;
+
+ nComponent1[0] = MAP(*pColorPtr0, *pColorPtr1, nTempFX);
+ pColorPtr0++; pColorPtr1++;
+ nComponent1[1] = MAP(*pColorPtr0, *pColorPtr1, nTempFX);
+ pColorPtr0++; pColorPtr1++;
+ nComponent1[2] = MAP(*pColorPtr0, *pColorPtr1, nTempFX);
+ pColorPtr0++; pColorPtr1++;
+ nComponent1[3] = MAP(*pColorPtr0, *pColorPtr1, nTempFX);
+
+ pColorPtr0 = pLine1 + nTempX * nColorComponents;
+ pColorPtr1 = pColorPtr0 + nColorComponents;
+
+ nComponent2[0] = MAP(*pColorPtr0, *pColorPtr1, nTempFX);
+ pColorPtr0++; pColorPtr1++;
+ nComponent2[1] = MAP(*pColorPtr0, *pColorPtr1, nTempFX);
+ pColorPtr0++; pColorPtr1++;
+ nComponent2[2] = MAP(*pColorPtr0, *pColorPtr1, nTempFX);
+ pColorPtr0++; pColorPtr1++;
+ nComponent2[3] = MAP(*pColorPtr0, *pColorPtr1, nTempFX);
+
+ *pScanDest = MAP(nComponent1[0], nComponent2[0], nTempFY);
+ pScanDest++;
+ *pScanDest = MAP(nComponent1[1], nComponent2[1], nTempFY);
+ pScanDest++;
+ *pScanDest = MAP(nComponent1[2], nComponent2[2], nTempFY);
+ pScanDest++;
+ *pScanDest = MAP(nComponent1[3], nComponent2[3], nTempFY);
+ pScanDest++;
+ }
+ }
+}
+
+void scaleUpPalette8bit(ScaleContext &rCtx, long nStartY, long nEndY)
+{
+ const long nStartX = 0, nEndX = rCtx.mnDestW - 1;
+
+ for( long nY = nStartY; nY <= nEndY; nY++ )
+ {
+ long nTempY = rCtx.maMapIY[ nY ];
+ BilinearWeightType nTempFY = rCtx.maMapFY[ nY ];
+ Scanline pLine0 = rCtx.mpSrc->GetScanline( nTempY );
+ Scanline pLine1 = rCtx.mpSrc->GetScanline( ++nTempY );
+ Scanline pScanDest = rCtx.mpDest->GetScanline( nY );
+
+ for(long nX = nStartX, nXDst = 0; nX <= nEndX; nX++ )
+ {
+ long nTempX = rCtx.maMapIX[ nX ];
+ BilinearWeightType nTempFX = rCtx.maMapFX[ nX ];
+
+ const BitmapColor& rCol0 = rCtx.mpSrc->GetPaletteColor( pLine0[ nTempX ] );
+ const BitmapColor& rCol2 = rCtx.mpSrc->GetPaletteColor( pLine1[ nTempX ] );
+ const BitmapColor& rCol1 = rCtx.mpSrc->GetPaletteColor( pLine0[ ++nTempX ] );
+ const BitmapColor& rCol3 = rCtx.mpSrc->GetPaletteColor( pLine1[ nTempX ] );
+
+ sal_uInt8 cR0 = MAP( rCol0.GetRed(), rCol1.GetRed(), nTempFX );
+ sal_uInt8 cG0 = MAP( rCol0.GetGreen(), rCol1.GetGreen(), nTempFX );
+ sal_uInt8 cB0 = MAP( rCol0.GetBlue(), rCol1.GetBlue(), nTempFX );
+
+ sal_uInt8 cR1 = MAP( rCol2.GetRed(), rCol3.GetRed(), nTempFX );
+ sal_uInt8 cG1 = MAP( rCol2.GetGreen(), rCol3.GetGreen(), nTempFX );
+ sal_uInt8 cB1 = MAP( rCol2.GetBlue(), rCol3.GetBlue(), nTempFX );
+
+ BitmapColor aColRes( MAP( cR0, cR1, nTempFY ),
+ MAP( cG0, cG1, nTempFY ),
+ MAP( cB0, cB1, nTempFY ) );
+ rCtx.mpDest->SetPixelOnData( pScanDest, nXDst++, aColRes );
+ }
+ }
+}
+
+void scaleUpPaletteGeneral(ScaleContext &rCtx, long nStartY, long nEndY)
+{
+ const long nStartX = 0, nEndX = rCtx.mnDestW - 1;
+
+ for( long nY = nStartY; nY <= nEndY; nY++ )
+ {
+ long nTempY = rCtx.maMapIY[ nY ];
+ BilinearWeightType nTempFY = rCtx.maMapFY[ nY ];
+ Scanline pScanline = rCtx.mpDest->GetScanline( nY );
+
+ for( long nX = nStartX, nXDst = 0; nX <= nEndX; nX++ )
+ {
+ long nTempX = rCtx.maMapIX[ nX ];
+ BilinearWeightType nTempFX = rCtx.maMapFX[ nX ];
+
+ BitmapColor aCol0 = rCtx.mpSrc->GetPaletteColor( rCtx.mpSrc->GetPixelIndex( nTempY, nTempX ) );
+ BitmapColor aCol1 = rCtx.mpSrc->GetPaletteColor( rCtx.mpSrc->GetPixelIndex( nTempY, ++nTempX ) );
+ sal_uInt8 cR0 = MAP( aCol0.GetRed(), aCol1.GetRed(), nTempFX );
+ sal_uInt8 cG0 = MAP( aCol0.GetGreen(), aCol1.GetGreen(), nTempFX );
+ sal_uInt8 cB0 = MAP( aCol0.GetBlue(), aCol1.GetBlue(), nTempFX );
+
+ aCol1 = rCtx.mpSrc->GetPaletteColor( rCtx.mpSrc->GetPixelIndex( ++nTempY, nTempX ) );
+ aCol0 = rCtx.mpSrc->GetPaletteColor( rCtx.mpSrc->GetPixelIndex( nTempY--, --nTempX ) );
+ sal_uInt8 cR1 = MAP( aCol0.GetRed(), aCol1.GetRed(), nTempFX );
+ sal_uInt8 cG1 = MAP( aCol0.GetGreen(), aCol1.GetGreen(), nTempFX );
+ sal_uInt8 cB1 = MAP( aCol0.GetBlue(), aCol1.GetBlue(), nTempFX );
+
+ BitmapColor aColRes( MAP( cR0, cR1, nTempFY ),
+ MAP( cG0, cG1, nTempFY ),
+ MAP( cB0, cB1, nTempFY ) );
+ rCtx.mpDest->SetPixelOnData( pScanline, nXDst++, aColRes );
+ }
+ }
+}
+
+void scaleUp24bit(ScaleContext &rCtx, long nStartY, long nEndY)
+{
+ const int nColorComponents = 3;
+
+ const long nStartX = 0;
+ const long nEndX = rCtx.mnDestW - 1;
+
+ for (long nY = nStartY; nY <= nEndY; nY++)
+ {
+ long nTempY = rCtx.maMapIY[nY];
+ BilinearWeightType nTempFY = rCtx.maMapFY[nY];
+
+ Scanline pLine0 = rCtx.mpSrc->GetScanline(nTempY+0);
+ Scanline pLine1 = rCtx.mpSrc->GetScanline(nTempY+1);
+ Scanline pScanDest = rCtx.mpDest->GetScanline(nY);
+
+ sal_uInt8 nComponent1[nColorComponents];
+ sal_uInt8 nComponent2[nColorComponents];
+
+ Scanline pColorPtr0;
+ Scanline pColorPtr1;
+
+ for (long nX = nStartX; nX <= nEndX; nX++)
+ {
+ long nTempX = rCtx.maMapIX[nX];
+ BilinearWeightType nTempFX = rCtx.maMapFX[nX];
+
+ pColorPtr0 = pLine0 + nTempX * nColorComponents;
+ pColorPtr1 = pColorPtr0 + nColorComponents;
+
+ nComponent1[0] = MAP(*pColorPtr0, *pColorPtr1, nTempFX);
+ pColorPtr0++; pColorPtr1++;
+ nComponent1[1] = MAP(*pColorPtr0, *pColorPtr1, nTempFX);
+ pColorPtr0++; pColorPtr1++;
+ nComponent1[2] = MAP(*pColorPtr0, *pColorPtr1, nTempFX);
+
+ pColorPtr0 = pLine1 + nTempX * nColorComponents;
+ pColorPtr1 = pColorPtr0 + nColorComponents;
+
+ nComponent2[0] = MAP(*pColorPtr0, *pColorPtr1, nTempFX);
+ pColorPtr0++; pColorPtr1++;
+ nComponent2[1] = MAP(*pColorPtr0, *pColorPtr1, nTempFX);
+ pColorPtr0++; pColorPtr1++;
+ nComponent2[2] = MAP(*pColorPtr0, *pColorPtr1, nTempFX);
+
+ *pScanDest = MAP(nComponent1[0], nComponent2[0], nTempFY);
+ pScanDest++;
+ *pScanDest = MAP(nComponent1[1], nComponent2[1], nTempFY);
+ pScanDest++;
+ *pScanDest = MAP(nComponent1[2], nComponent2[2], nTempFY);
+ pScanDest++;
+ }
+ }
+}
+
+void scaleUpNonPaletteGeneral(ScaleContext &rCtx, long nStartY, long nEndY)
+{
+ const long nStartX = 0, nEndX = rCtx.mnDestW - 1;
+
+ for( long nY = nStartY; nY <= nEndY; nY++ )
+ {
+ long nTempY = rCtx.maMapIY[ nY ];
+ BilinearWeightType nTempFY = rCtx.maMapFY[ nY ];
+ Scanline pScanDest = rCtx.mpDest->GetScanline( nY );
+
+ for( long nX = nStartX, nXDst = 0; nX <= nEndX; nX++ )
+ {
+ long nTempX = rCtx.maMapIX[ nX ];
+ BilinearWeightType nTempFX = rCtx.maMapFX[ nX ];
+
+ BitmapColor aCol0 = rCtx.mpSrc->GetPixel( nTempY, nTempX );
+ BitmapColor aCol1 = rCtx.mpSrc->GetPixel( nTempY, ++nTempX );
+ sal_uInt8 cR0 = MAP( aCol0.GetRed(), aCol1.GetRed(), nTempFX );
+ sal_uInt8 cG0 = MAP( aCol0.GetGreen(), aCol1.GetGreen(), nTempFX );
+ sal_uInt8 cB0 = MAP( aCol0.GetBlue(), aCol1.GetBlue(), nTempFX );
+
+ aCol1 = rCtx.mpSrc->GetPixel( ++nTempY, nTempX );
+ aCol0 = rCtx.mpSrc->GetPixel( nTempY--, --nTempX );
+ sal_uInt8 cR1 = MAP( aCol0.GetRed(), aCol1.GetRed(), nTempFX );
+ sal_uInt8 cG1 = MAP( aCol0.GetGreen(), aCol1.GetGreen(), nTempFX );
+ sal_uInt8 cB1 = MAP( aCol0.GetBlue(), aCol1.GetBlue(), nTempFX );
+
+ BitmapColor aColRes( MAP( cR0, cR1, nTempFY ),
+ MAP( cG0, cG1, nTempFY ),
+ MAP( cB0, cB1, nTempFY ) );
+ rCtx.mpDest->SetPixelOnData( pScanDest, nXDst++, aColRes );
+ }
+ }
+}
+
+void scaleDown32bit(ScaleContext &rCtx, long nStartY, long nEndY)
+{
+ const int constColorComponents = 4;
+
+ const long nStartX = 0;
+ const long nEndX = rCtx.mnDestW - 1;
+
+ for (long nY = nStartY; nY <= nEndY; nY++)
+ {
+ long nTop = rCtx.mbVMirr ? (nY + 1) : nY;
+ long nBottom = rCtx.mbVMirr ? nY : (nY + 1);
+
+ long nLineStart;
+ long nLineRange;
+ if (nY == nEndY)
+ {
+ nLineStart = rCtx.maMapIY[nY];
+ nLineRange = 0;
+ }
+ else
+ {
+ nLineStart = rCtx.maMapIY[nTop];
+ nLineRange = (rCtx.maMapIY[nBottom] == rCtx.maMapIY[nTop]) ?
+ 1 : (rCtx.maMapIY[nBottom] - rCtx.maMapIY[nTop]);
+ }
+
+ Scanline pScanDest = rCtx.mpDest->GetScanline(nY);
+ for (long nX = nStartX; nX <= nEndX; nX++)
+ {
+ long nLeft = rCtx.mbHMirr ? (nX + 1) : nX;
+ long nRight = rCtx.mbHMirr ? nX : (nX + 1);
+
+ long nRowStart;
+ long nRowRange;
+ if (nX == nEndX)
+ {
+ nRowStart = rCtx.maMapIX[nX];
+ nRowRange = 0;
+ }
+ else
+ {
+ nRowStart = rCtx.maMapIX[nLeft];
+ nRowRange = (rCtx.maMapIX[nRight] == rCtx.maMapIX[nLeft]) ?
+ 1 : (rCtx.maMapIX[nRight] - rCtx.maMapIX[nLeft]);
+ }
+
+ int nSum1 = 0;
+ int nSum2 = 0;
+ int nSum3 = 0;
+ int nSum4 = 0;
+ BilinearWeightType nTotalWeightY = 0;
+
+ for (long i = 0; i<= nLineRange; i++)
+ {
+ Scanline pTmpY = rCtx.mpSrc->GetScanline(nLineStart + i);
+ Scanline pTmpX = pTmpY + constColorComponents * nRowStart;
+
+ int nSumRow1 = 0;
+ int nSumRow2 = 0;
+ int nSumRow3 = 0;
+ int nSumRow4 = 0;
+ BilinearWeightType nTotalWeightX = 0;
+
+ for (long j = 0; j <= nRowRange; j++)
+ {
+ if (nX == nEndX)
+ {
+ nSumRow1 += (*pTmpX) << MAP_PRECISION; pTmpX++;
+ nSumRow2 += (*pTmpX) << MAP_PRECISION; pTmpX++;
+ nSumRow3 += (*pTmpX) << MAP_PRECISION; pTmpX++;
+ nSumRow4 += (*pTmpX) << MAP_PRECISION; pTmpX++;
+ nTotalWeightX += lclMaxWeight();
+ }
+ else if(j == 0)
+ {
+ BilinearWeightType nWeightX = lclMaxWeight() - rCtx.maMapFX[nLeft];
+ nSumRow1 += (nWeightX * (*pTmpX)); pTmpX++;
+ nSumRow2 += (nWeightX * (*pTmpX)); pTmpX++;
+ nSumRow3 += (nWeightX * (*pTmpX)); pTmpX++;
+ nSumRow4 += (nWeightX * (*pTmpX)); pTmpX++;
+ nTotalWeightX += nWeightX;
+ }
+ else if ( nRowRange == j )
+ {
+ BilinearWeightType nWeightX = rCtx.maMapFX[ nRight ] ;
+ nSumRow1 += (nWeightX * (*pTmpX)); pTmpX++;
+ nSumRow2 += (nWeightX * (*pTmpX)); pTmpX++;
+ nSumRow3 += (nWeightX * (*pTmpX)); pTmpX++;
+ nSumRow4 += (nWeightX * (*pTmpX)); pTmpX++;
+ nTotalWeightX += nWeightX;
+ }
+ else
+ {
+ nSumRow1 += (*pTmpX) << MAP_PRECISION; pTmpX++;
+ nSumRow2 += (*pTmpX) << MAP_PRECISION; pTmpX++;
+ nSumRow3 += (*pTmpX) << MAP_PRECISION; pTmpX++;
+ nSumRow4 += (*pTmpX) << MAP_PRECISION; pTmpX++;
+ nTotalWeightX += lclMaxWeight();
+ }
+ }
+
+ BilinearWeightType nWeightY = lclMaxWeight();
+ if (nY == nEndY)
+ nWeightY = lclMaxWeight();
+ else if (i == 0)
+ nWeightY = lclMaxWeight() - rCtx.maMapFY[nTop];
+ else if (nLineRange == 1)
+ nWeightY = rCtx.maMapFY[nTop];
+ else if (nLineRange == i)
+ nWeightY = rCtx.maMapFY[nBottom];
+
+ if (nTotalWeightX)
+ {
+ nSumRow1 /= nTotalWeightX;
+ nSumRow2 /= nTotalWeightX;
+ nSumRow3 /= nTotalWeightX;
+ nSumRow4 /= nTotalWeightX;
+ }
+ nSum1 += nWeightY * nSumRow1;
+ nSum2 += nWeightY * nSumRow2;
+ nSum3 += nWeightY * nSumRow3;
+ nSum4 += nWeightY * nSumRow4;
+ nTotalWeightY += nWeightY;
+ }
+
+ if (nTotalWeightY)
+ {
+ nSum1 /= nTotalWeightY;
+ nSum2 /= nTotalWeightY;
+ nSum3 /= nTotalWeightY;
+ nSum4 /= nTotalWeightY;
+ }
+
+ // Write the calculated color components to the destination
+ *pScanDest = nSum1; pScanDest++;
+ *pScanDest = nSum2; pScanDest++;
+ *pScanDest = nSum3; pScanDest++;
+ *pScanDest = nSum4; pScanDest++;
+ }
+ }
+}
+
+void scaleDownPalette8bit(ScaleContext &rCtx, long nStartY, long nEndY)
+{
+ const long nStartX = 0, nEndX = rCtx.mnDestW - 1;
+
+ for( long nY = nStartY; nY <= nEndY; nY++ )
+ {
+ long nTop = rCtx.mbVMirr ? ( nY + 1 ) : nY;
+ long nBottom = rCtx.mbVMirr ? nY : ( nY + 1 ) ;
+
+ long nLineStart, nLineRange;
+ if( nY == nEndY )
+ {
+ nLineStart = rCtx.maMapIY[ nY ];
+ nLineRange = 0;
+ }
+ else
+ {
+ nLineStart = rCtx.maMapIY[ nTop ] ;
+ nLineRange = ( rCtx.maMapIY[ nBottom ] == rCtx.maMapIY[ nTop ] ) ? 1 :( rCtx.maMapIY[ nBottom ] - rCtx.maMapIY[ nTop ] );
+ }
+
+ Scanline pScanDest = rCtx.mpDest->GetScanline( nY );
+ for( long nX = nStartX , nXDst = 0; nX <= nEndX; nX++ )
+ {
+ long nLeft = rCtx.mbHMirr ? ( nX + 1 ) : nX;
+ long nRight = rCtx.mbHMirr ? nX : ( nX + 1 ) ;
+
+ long nRowStart;
+ long nRowRange;
+ if( nX == nEndX )
+ {
+ nRowStart = rCtx.maMapIX[ nX ];
+ nRowRange = 0;
+ }
+ else
+ {
+ nRowStart = rCtx.maMapIX[ nLeft ];
+ nRowRange = ( rCtx.maMapIX[ nRight ] == rCtx.maMapIX[ nLeft ] )? 1 : ( rCtx.maMapIX[ nRight ] - rCtx.maMapIX[ nLeft ] );
+ }
+
+ int nSumR = 0;
+ int nSumG = 0;
+ int nSumB = 0;
+ BilinearWeightType nTotalWeightY = 0;
+
+ for(long i = 0; i<= nLineRange; i++)
+ {
+ Scanline pTmpY = rCtx.mpSrc->GetScanline( nLineStart + i );
+ int nSumRowR = 0;
+ int nSumRowG = 0;
+ int nSumRowB = 0;
+ BilinearWeightType nTotalWeightX = 0;
+
+ for(long j = 0; j <= nRowRange; j++)
+ {
+ const BitmapColor& rCol = rCtx.mpSrc->GetPaletteColor( pTmpY[ nRowStart + j ] );
+
+ if(nX == nEndX )
+ {
+ nSumRowB += rCol.GetBlue() << MAP_PRECISION;
+ nSumRowG += rCol.GetGreen() << MAP_PRECISION;
+ nSumRowR += rCol.GetRed() << MAP_PRECISION;
+ nTotalWeightX += lclMaxWeight();
+ }
+ else if( j == 0 )
+ {
+ BilinearWeightType nWeightX = lclMaxWeight() - rCtx.maMapFX[ nLeft ];
+ nSumRowB += ( nWeightX *rCol.GetBlue()) ;
+ nSumRowG += ( nWeightX *rCol.GetGreen()) ;
+ nSumRowR += ( nWeightX *rCol.GetRed()) ;
+ nTotalWeightX += nWeightX;
+ }
+ else if ( nRowRange == j )
+ {
+ BilinearWeightType nWeightX = rCtx.maMapFX[ nRight ] ;
+ nSumRowB += ( nWeightX *rCol.GetBlue() );
+ nSumRowG += ( nWeightX *rCol.GetGreen() );
+ nSumRowR += ( nWeightX *rCol.GetRed() );
+ nTotalWeightX += nWeightX;
+ }
+ else
+ {
+ nSumRowB += rCol.GetBlue() << MAP_PRECISION;
+ nSumRowG += rCol.GetGreen() << MAP_PRECISION;
+ nSumRowR += rCol.GetRed() << MAP_PRECISION;
+ nTotalWeightX += lclMaxWeight();
+ }
+ }
+
+ BilinearWeightType nWeightY = lclMaxWeight();
+ if( nY == nEndY )
+ nWeightY = lclMaxWeight();
+ else if( i == 0 )
+ nWeightY = lclMaxWeight() - rCtx.maMapFY[ nTop ];
+ else if( nLineRange == 1 )
+ nWeightY = rCtx.maMapFY[ nTop ];
+ else if ( nLineRange == i )
+ nWeightY = rCtx.maMapFY[ nBottom ];
+
+ if (nTotalWeightX)
+ {
+ nSumRowB /= nTotalWeightX;
+ nSumRowG /= nTotalWeightX;
+ nSumRowR /= nTotalWeightX;
+ }
+
+ nSumB += nWeightY * nSumRowB;
+ nSumG += nWeightY * nSumRowG;
+ nSumR += nWeightY * nSumRowR;
+ nTotalWeightY += nWeightY;
+ }
+
+ if (nTotalWeightY)
+ {
+ nSumR /= nTotalWeightY;
+ nSumG /= nTotalWeightY;
+ nSumB /= nTotalWeightY;
+ }
+
+ BitmapColor aColRes(static_cast<sal_uInt8>(nSumR), static_cast<sal_uInt8>(nSumG), static_cast<sal_uInt8>(nSumB));
+ rCtx.mpDest->SetPixelOnData( pScanDest, nXDst++, aColRes );
+ }
+ }
+}
+
+void scaleDownPaletteGeneral(ScaleContext &rCtx, long nStartY, long nEndY)
+{
+ const long nStartX = 0, nEndX = rCtx.mnDestW - 1;
+
+ for( long nY = nStartY; nY <= nEndY; nY++ )
+ {
+ long nTop = rCtx.mbVMirr ? ( nY + 1 ) : nY;
+ long nBottom = rCtx.mbVMirr ? nY : ( nY + 1 ) ;
+
+ long nLineStart, nLineRange;
+ if( nY ==nEndY )
+ {
+ nLineStart = rCtx.maMapIY[ nY ];
+ nLineRange = 0;
+ }
+ else
+ {
+ nLineStart = rCtx.maMapIY[ nTop ] ;
+ nLineRange = ( rCtx.maMapIY[ nBottom ] == rCtx.maMapIY[ nTop ] ) ? 1 :( rCtx.maMapIY[ nBottom ] - rCtx.maMapIY[ nTop ] );
+ }
+
+ Scanline pScanDest = rCtx.mpDest->GetScanline( nY );
+ for( long nX = nStartX , nXDst = 0; nX <= nEndX; nX++ )
+ {
+ long nLeft = rCtx.mbHMirr ? ( nX + 1 ) : nX;
+ long nRight = rCtx.mbHMirr ? nX : ( nX + 1 ) ;
+
+ long nRowStart, nRowRange;
+ if( nX == nEndX )
+ {
+ nRowStart = rCtx.maMapIX[ nX ];
+ nRowRange = 0;
+ }
+ else
+ {
+ nRowStart = rCtx.maMapIX[ nLeft ];
+ nRowRange = ( rCtx.maMapIX[ nRight ] == rCtx.maMapIX[ nLeft ] )? 1 : ( rCtx.maMapIX[ nRight ] - rCtx.maMapIX[ nLeft ] );
+ }
+
+ int nSumR = 0;
+ int nSumG = 0;
+ int nSumB = 0;
+ BilinearWeightType nTotalWeightY = 0;
+
+ for(long i = 0; i<= nLineRange; i++)
+ {
+ int nSumRowR = 0;
+ int nSumRowG = 0;
+ int nSumRowB = 0;
+ BilinearWeightType nTotalWeightX = 0;
+
+ Scanline pScanlineSrc = rCtx.mpSrc->GetScanline( nLineStart + i );
+ for(long j = 0; j <= nRowRange; j++)
+ {
+ BitmapColor aCol0 = rCtx.mpSrc->GetPaletteColor ( rCtx.mpSrc->GetIndexFromData( pScanlineSrc, nRowStart + j ) );
+
+ if(nX == nEndX )
+ {
+
+ nSumRowB += aCol0.GetBlue() << MAP_PRECISION;
+ nSumRowG += aCol0.GetGreen() << MAP_PRECISION;
+ nSumRowR += aCol0.GetRed() << MAP_PRECISION;
+ nTotalWeightX += lclMaxWeight();
+ }
+ else if( j == 0 )
+ {
+
+ BilinearWeightType nWeightX = lclMaxWeight() - rCtx.maMapFX[ nLeft ];
+ nSumRowB += ( nWeightX *aCol0.GetBlue()) ;
+ nSumRowG += ( nWeightX *aCol0.GetGreen()) ;
+ nSumRowR += ( nWeightX *aCol0.GetRed()) ;
+ nTotalWeightX += nWeightX;
+ }
+ else if ( nRowRange == j )
+ {
+
+ BilinearWeightType nWeightX = rCtx.maMapFX[ nRight ] ;
+ nSumRowB += ( nWeightX *aCol0.GetBlue() );
+ nSumRowG += ( nWeightX *aCol0.GetGreen() );
+ nSumRowR += ( nWeightX *aCol0.GetRed() );
+ nTotalWeightX += nWeightX;
+ }
+ else
+ {
+
+ nSumRowB += aCol0.GetBlue() << MAP_PRECISION;
+ nSumRowG += aCol0.GetGreen() << MAP_PRECISION;
+ nSumRowR += aCol0.GetRed() << MAP_PRECISION;
+ nTotalWeightX += lclMaxWeight();
+ }
+ }
+
+ long nWeightY = lclMaxWeight();
+ if( nY == nEndY )
+ nWeightY = lclMaxWeight();
+ else if( i == 0 )
+ nWeightY = lclMaxWeight() - rCtx.maMapFY[ nTop ];
+ else if( nLineRange == 1 )
+ nWeightY = rCtx.maMapFY[ nTop ];
+ else if ( nLineRange == i )
+ nWeightY = rCtx.maMapFY[ nBottom ];
+
+ if (nTotalWeightX)
+ {
+ nSumRowB /= nTotalWeightX;
+ nSumRowG /= nTotalWeightX;
+ nSumRowR /= nTotalWeightX;
+ }
+
+ nSumB += nWeightY * nSumRowB;
+ nSumG += nWeightY * nSumRowG;
+ nSumR += nWeightY * nSumRowR;
+ nTotalWeightY += nWeightY;
+ }
+
+ if (nTotalWeightY)
+ {
+ nSumR /= nTotalWeightY;
+ nSumG /= nTotalWeightY;
+ nSumB /= nTotalWeightY;
+ }
+
+ BitmapColor aColRes(static_cast<sal_uInt8>(nSumR), static_cast<sal_uInt8>(nSumG), static_cast<sal_uInt8>(nSumB));
+ rCtx.mpDest->SetPixelOnData( pScanDest, nXDst++, aColRes );
+ }
+ }
+}
+
+void scaleDown24bit(ScaleContext &rCtx, long nStartY, long nEndY)
+{
+ const int constColorComponents = 3;
+
+ const long nStartX = 0;
+ const long nEndX = rCtx.mnDestW - 1;
+
+ for (long nY = nStartY; nY <= nEndY; nY++)
+ {
+ long nTop = rCtx.mbVMirr ? (nY + 1) : nY;
+ long nBottom = rCtx.mbVMirr ? nY : (nY + 1);
+
+ long nLineStart;
+ long nLineRange;
+ if (nY == nEndY)
+ {
+ nLineStart = rCtx.maMapIY[nY];
+ nLineRange = 0;
+ }
+ else
+ {
+ nLineStart = rCtx.maMapIY[nTop];
+ nLineRange = (rCtx.maMapIY[nBottom] == rCtx.maMapIY[nTop]) ?
+ 1 : (rCtx.maMapIY[nBottom] - rCtx.maMapIY[nTop]);
+ }
+
+ Scanline pScanDest = rCtx.mpDest->GetScanline(nY);
+ for (long nX = nStartX; nX <= nEndX; nX++)
+ {
+ long nLeft = rCtx.mbHMirr ? (nX + 1) : nX;
+ long nRight = rCtx.mbHMirr ? nX : (nX + 1);
+
+ long nRowStart;
+ long nRowRange;
+ if (nX == nEndX)
+ {
+ nRowStart = rCtx.maMapIX[nX];
+ nRowRange = 0;
+ }
+ else
+ {
+ nRowStart = rCtx.maMapIX[nLeft];
+ nRowRange = (rCtx.maMapIX[nRight] == rCtx.maMapIX[nLeft]) ?
+ 1 : (rCtx.maMapIX[nRight] - rCtx.maMapIX[nLeft]);
+ }
+
+ int nSum1 = 0;
+ int nSum2 = 0;
+ int nSum3 = 0;
+ BilinearWeightType nTotalWeightY = 0;
+
+ for (long i = 0; i<= nLineRange; i++)
+ {
+ Scanline pTmpY = rCtx.mpSrc->GetScanline(nLineStart + i);
+ Scanline pTmpX = pTmpY + constColorComponents * nRowStart;
+
+ int nSumRow1 = 0;
+ int nSumRow2 = 0;
+ int nSumRow3 = 0;
+ BilinearWeightType nTotalWeightX = 0;
+
+ for (long j = 0; j <= nRowRange; j++)
+ {
+ if (nX == nEndX)
+ {
+ nSumRow1 += (*pTmpX) << MAP_PRECISION; pTmpX++;
+ nSumRow2 += (*pTmpX) << MAP_PRECISION; pTmpX++;
+ nSumRow3 += (*pTmpX) << MAP_PRECISION; pTmpX++;
+ nTotalWeightX += lclMaxWeight();
+ }
+ else if(j == 0)
+ {
+ BilinearWeightType nWeightX = lclMaxWeight() - rCtx.maMapFX[nLeft];
+ nSumRow1 += (nWeightX * (*pTmpX)); pTmpX++;
+ nSumRow2 += (nWeightX * (*pTmpX)); pTmpX++;
+ nSumRow3 += (nWeightX * (*pTmpX)); pTmpX++;
+ nTotalWeightX += nWeightX;
+ }
+ else if ( nRowRange == j )
+ {
+ BilinearWeightType nWeightX = rCtx.maMapFX[ nRight ] ;
+ nSumRow1 += (nWeightX * (*pTmpX)); pTmpX++;
+ nSumRow2 += (nWeightX * (*pTmpX)); pTmpX++;
+ nSumRow3 += (nWeightX * (*pTmpX)); pTmpX++;
+ nTotalWeightX += nWeightX;
+ }
+ else
+ {
+ nSumRow1 += (*pTmpX) << MAP_PRECISION; pTmpX++;
+ nSumRow2 += (*pTmpX) << MAP_PRECISION; pTmpX++;
+ nSumRow3 += (*pTmpX) << MAP_PRECISION; pTmpX++;
+ nTotalWeightX += lclMaxWeight();
+ }
+ }
+
+ BilinearWeightType nWeightY = lclMaxWeight();
+ if (nY == nEndY)
+ nWeightY = lclMaxWeight();
+ else if (i == 0)
+ nWeightY = lclMaxWeight() - rCtx.maMapFY[nTop];
+ else if (nLineRange == 1)
+ nWeightY = rCtx.maMapFY[nTop];
+ else if (nLineRange == i)
+ nWeightY = rCtx.maMapFY[nBottom];
+
+ if (nTotalWeightX)
+ {
+ nSumRow1 /= nTotalWeightX;
+ nSumRow2 /= nTotalWeightX;
+ nSumRow3 /= nTotalWeightX;
+ }
+ nSum1 += nWeightY * nSumRow1;
+ nSum2 += nWeightY * nSumRow2;
+ nSum3 += nWeightY * nSumRow3;
+ nTotalWeightY += nWeightY;
+ }
+
+ if (nTotalWeightY)
+ {
+ nSum1 /= nTotalWeightY;
+ nSum2 /= nTotalWeightY;
+ nSum3 /= nTotalWeightY;
+ }
+
+ // Write the calculated color components to the destination
+ *pScanDest = nSum1; pScanDest++;
+ *pScanDest = nSum2; pScanDest++;
+ *pScanDest = nSum3; pScanDest++;
+ }
+ }
+}
+
+void scaleDownNonPaletteGeneral(ScaleContext &rCtx, long nStartY, long nEndY)
+{
+ const long nStartX = 0, nEndX = rCtx.mnDestW - 1;
+
+ for( long nY = nStartY; nY <= nEndY; nY++ )
+ {
+ long nTop = rCtx.mbVMirr ? ( nY + 1 ) : nY;
+ long nBottom = rCtx.mbVMirr ? nY : ( nY + 1 ) ;
+
+ long nLineStart, nLineRange;
+ if( nY ==nEndY )
+ {
+ nLineStart = rCtx.maMapIY[ nY ];
+ nLineRange = 0;
+ }
+ else
+ {
+ nLineStart = rCtx.maMapIY[ nTop ] ;
+ nLineRange = ( rCtx.maMapIY[ nBottom ] == rCtx.maMapIY[ nTop ] ) ? 1 :( rCtx.maMapIY[ nBottom ] - rCtx.maMapIY[ nTop ] );
+ }
+
+ Scanline pScanDest = rCtx.mpDest->GetScanline( nY );
+ for( long nX = nStartX , nXDst = 0; nX <= nEndX; nX++ )
+ {
+ long nLeft = rCtx.mbHMirr ? ( nX + 1 ) : nX;
+ long nRight = rCtx.mbHMirr ? nX : ( nX + 1 ) ;
+
+ long nRowStart, nRowRange;
+ if( nX == nEndX )
+ {
+ nRowStart = rCtx.maMapIX[ nX ];
+ nRowRange = 0;
+ }
+ else
+ {
+ nRowStart = rCtx.maMapIX[ nLeft ];
+ nRowRange = ( rCtx.maMapIX[ nRight ] == rCtx.maMapIX[ nLeft ] )? 1 : ( rCtx.maMapIX[ nRight ] - rCtx.maMapIX[ nLeft ] );
+ }
+
+ int nSumR = 0;
+ int nSumG = 0;
+ int nSumB = 0;
+ BilinearWeightType nTotalWeightY = 0;
+
+ for(long i = 0; i<= nLineRange; i++)
+ {
+ int nSumRowR = 0;
+ int nSumRowG = 0;
+ int nSumRowB = 0;
+ BilinearWeightType nTotalWeightX = 0;
+
+ Scanline pScanlineSrc = rCtx.mpSrc->GetScanline( nLineStart + i );
+ for(long j = 0; j <= nRowRange; j++)
+ {
+ BitmapColor aCol0 = rCtx.mpSrc->GetPixelFromData( pScanlineSrc, nRowStart + j );
+
+ if(nX == nEndX )
+ {
+
+ nSumRowB += aCol0.GetBlue() << MAP_PRECISION;
+ nSumRowG += aCol0.GetGreen() << MAP_PRECISION;
+ nSumRowR += aCol0.GetRed() << MAP_PRECISION;
+ nTotalWeightX += lclMaxWeight();
+ }
+ else if( j == 0 )
+ {
+
+ BilinearWeightType nWeightX = lclMaxWeight() - rCtx.maMapFX[ nLeft ];
+ nSumRowB += ( nWeightX *aCol0.GetBlue()) ;
+ nSumRowG += ( nWeightX *aCol0.GetGreen()) ;
+ nSumRowR += ( nWeightX *aCol0.GetRed()) ;
+ nTotalWeightX += nWeightX;
+ }
+ else if ( nRowRange == j )
+ {
+
+ BilinearWeightType nWeightX = rCtx.maMapFX[ nRight ] ;
+ nSumRowB += ( nWeightX *aCol0.GetBlue() );
+ nSumRowG += ( nWeightX *aCol0.GetGreen() );
+ nSumRowR += ( nWeightX *aCol0.GetRed() );
+ nTotalWeightX += nWeightX;
+ }
+ else
+ {
+ nSumRowB += aCol0.GetBlue() << MAP_PRECISION;
+ nSumRowG += aCol0.GetGreen() << MAP_PRECISION;
+ nSumRowR += aCol0.GetRed() << MAP_PRECISION;
+ nTotalWeightX += lclMaxWeight();
+ }
+ }
+
+ BilinearWeightType nWeightY = lclMaxWeight();
+ if( nY == nEndY )
+ nWeightY = lclMaxWeight();
+ else if( i == 0 )
+ nWeightY = lclMaxWeight() - rCtx.maMapFY[ nTop ];
+ else if( nLineRange == 1 )
+ nWeightY = rCtx.maMapFY[ nTop ];
+ else if ( nLineRange == i )
+ nWeightY = rCtx.maMapFY[ nBottom ];
+
+ if (nTotalWeightX)
+ {
+ nSumRowB /= nTotalWeightX;
+ nSumRowG /= nTotalWeightX;
+ nSumRowR /= nTotalWeightX;
+ }
+
+ nSumB += nWeightY * nSumRowB;
+ nSumG += nWeightY * nSumRowG;
+ nSumR += nWeightY * nSumRowR;
+ nTotalWeightY += nWeightY;
+ }
+
+ if (nTotalWeightY)
+ {
+ nSumR /= nTotalWeightY;
+ nSumG /= nTotalWeightY;
+ nSumB /= nTotalWeightY;
+ }
+
+ BitmapColor aColRes(static_cast<sal_uInt8>(nSumR), static_cast<sal_uInt8>(nSumG), static_cast<sal_uInt8>(nSumB));
+ rCtx.mpDest->SetPixelOnData( pScanDest, nXDst++, aColRes );
+ }
+ }
+}
+
+} // end anonymous namespace
+
+BitmapScaleSuperFilter::BitmapScaleSuperFilter(const double& rScaleX, const double& rScaleY) :
+ mrScaleX(rScaleX),
+ mrScaleY(rScaleY)
+{}
+
+BitmapScaleSuperFilter::~BitmapScaleSuperFilter()
+{}
+
+BitmapEx BitmapScaleSuperFilter::execute(BitmapEx const& rBitmap) const
+{
+ Bitmap aBitmap(rBitmap.GetBitmap());
+ bool bRet = false;
+
+ const Size aSizePix(rBitmap.GetSizePixel());
+
+ bool bHMirr = mrScaleX < 0;
+ bool bVMirr = mrScaleY < 0;
+
+ double fScaleX = std::fabs(mrScaleX);
+ double fScaleY = std::fabs(mrScaleY);
+
+ const long nDstW = FRound(aSizePix.Width() * fScaleX);
+ const long nDstH = FRound(aSizePix.Height() * fScaleY);
+
+ const double fScaleThresh = 0.6;
+
+ if (nDstW <= 1 || nDstH <= 1)
+ return BitmapEx();
+
+ // check cache for a previously scaled version of this
+ ScaleCacheKey aKey(aBitmap.ImplGetSalBitmap().get(),
+ Size(nDstW, nDstH));
+
+ ImplSVData* pSVData = ImplGetSVData();
+ auto& rCache = pSVData->maGDIData.maScaleCache;
+ auto aFind = rCache.find(aKey);
+ if (aFind != rCache.end())
+ {
+ if (aFind->second.GetSizePixel().Width() == nDstW && aFind->second.GetSizePixel().Height() == nDstH)
+ return aFind->second;
+ else
+ SAL_WARN("vcl.gdi", "Error: size mismatch in scale cache");
+ }
+
+ {
+ Bitmap::ScopedReadAccess pReadAccess(aBitmap);
+
+ sal_uInt16 nSourceBitcount = aBitmap.GetBitCount();
+
+ Bitmap aOutBmp(Size(nDstW, nDstH), std::max(nSourceBitcount, sal_uInt16(24)));
+ Size aOutSize = aOutBmp.GetSizePixel();
+ sal_uInt16 nTargetBitcount = aOutBmp.GetBitCount();
+
+ if (!aOutSize.Width() || !aOutSize.Height())
+ {
+ SAL_WARN("vcl.gdi", "bmp creation failed");
+ return BitmapEx();
+ }
+
+ BitmapScopedWriteAccess pWriteAccess(aOutBmp);
+
+ const long nStartY = 0;
+ const long nEndY = nDstH - 1;
+
+ if (pReadAccess && pWriteAccess)
+ {
+ ScaleRangeFn pScaleRangeFn;
+ ScaleContext aContext( pReadAccess.get(),
+ pWriteAccess.get(),
+ pReadAccess->Width(),
+ pWriteAccess->Width(),
+ pReadAccess->Height(),
+ pWriteAccess->Height(),
+ bVMirr, bHMirr );
+
+ bool bScaleUp = fScaleX >= fScaleThresh && fScaleY >= fScaleThresh;
+ // If we have a source bitmap with a palette the scaling converts
+ // from up to 8 bit image -> 24 bit non-palette, which is then
+ // adapted back to the same type as original.
+ if (pReadAccess->HasPalette())
+ {
+ switch( pReadAccess->GetScanlineFormat() )
+ {
+ case ScanlineFormat::N8BitPal:
+ pScaleRangeFn = bScaleUp ? scaleUpPalette8bit
+ : scaleDownPalette8bit;
+ break;
+ default:
+ pScaleRangeFn = bScaleUp ? scaleUpPaletteGeneral
+ : scaleDownPaletteGeneral;
+ break;
+ }
+ }
+ // Here we know that we are dealing with a non-palette source bitmap.
+ // The target is either 24 or 32 bit, depending on the image and
+ // the capabilities of the backend. If for some reason the destination
+ // is not the same bit-depth as the source, then we can't use
+ // a fast path, so we always need to process with a general scaler.
+ else if (nSourceBitcount != nTargetBitcount)
+ {
+ pScaleRangeFn = bScaleUp ? scaleUpNonPaletteGeneral : scaleDownNonPaletteGeneral;
+ }
+ // If we get here then we can only use a fast path, but let's
+ // still keep the fallback to the general scaler alive.
+ else
+ {
+ switch( pReadAccess->GetScanlineFormat() )
+ {
+ case ScanlineFormat::N24BitTcBgr:
+ case ScanlineFormat::N24BitTcRgb:
+ pScaleRangeFn = bScaleUp ? scaleUp24bit : scaleDown24bit;
+ break;
+ case ScanlineFormat::N32BitTcRgba:
+ case ScanlineFormat::N32BitTcBgra:
+ case ScanlineFormat::N32BitTcArgb:
+ case ScanlineFormat::N32BitTcAbgr:
+ pScaleRangeFn = bScaleUp ? scaleUp32bit : scaleDown32bit;
+ break;
+ default:
+ pScaleRangeFn = bScaleUp ? scaleUpNonPaletteGeneral
+ : scaleDownNonPaletteGeneral;
+ break;
+ }
+ }
+
+ // We want to thread - only if there is a lot of work to do:
+ // We work hard when there is a large destination image, or
+ // A large source image.
+ bool bHorizontalWork = pReadAccess->Height() >= 512 && pReadAccess->Width() >= 512;
+ bool bUseThreads = true;
+
+ static bool bDisableThreadedScaling = getenv ("VCL_NO_THREAD_SCALE");
+ if (bDisableThreadedScaling || !bHorizontalWork)
+ {
+ SAL_INFO("vcl.gdi", "Scale in main thread");
+ bUseThreads = false;
+ }
+
+ if (bUseThreads)
+ {
+ try
+ {
+ // partition and queue work
+ comphelper::ThreadPool &rShared = comphelper::ThreadPool::getSharedOptimalPool();
+ std::shared_ptr<comphelper::ThreadTaskTag> pTag = comphelper::ThreadPool::createThreadTaskTag();
+
+ long nStripYStart = nStartY;
+ long nStripYEnd = nStripYStart + constScaleThreadStrip - 1;
+
+ while (nStripYEnd < nEndY)
+ {
+ std::unique_ptr<ScaleTask> pTask(new ScaleTask(pTag, pScaleRangeFn, aContext, nStripYStart, nStripYEnd));
+ rShared.pushTask(std::move(pTask));
+ nStripYStart += constScaleThreadStrip;
+ nStripYEnd += constScaleThreadStrip;
+ }
+ if (nStripYStart <= nEndY)
+ {
+ std::unique_ptr<ScaleTask> pTask(new ScaleTask(pTag, pScaleRangeFn, aContext, nStripYStart, nEndY));
+ rShared.pushTask(std::move(pTask));
+ }
+ rShared.waitUntilDone(pTag);
+ SAL_INFO("vcl.gdi", "All threaded scaling tasks complete");
+ }
+ catch (...)
+ {
+ SAL_WARN("vcl.gdi", "threaded bitmap scaling failed");
+ bUseThreads = false;
+ }
+ }
+
+ if (!bUseThreads)
+ pScaleRangeFn( aContext, nStartY, nEndY );
+
+ pWriteAccess.reset();
+ bRet = true;
+ aBitmap.AdaptBitCount(aOutBmp);
+ aBitmap = aOutBmp;
+ }
+ }
+
+ if (bRet)
+ {
+ tools::Rectangle aRect(Point(0, 0), Point(nDstW, nDstH));
+ aBitmap.Crop(aRect);
+ BitmapEx aRet(aBitmap);
+ rCache.insert(std::make_pair(aKey, aRet));
+ return aRet;
+ }
+
+ return BitmapEx();
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/bitmap/BitmapSeparableUnsharpenFilter.cxx b/vcl/source/bitmap/BitmapSeparableUnsharpenFilter.cxx
new file mode 100644
index 000000000..58d5e3810
--- /dev/null
+++ b/vcl/source/bitmap/BitmapSeparableUnsharpenFilter.cxx
@@ -0,0 +1,75 @@
+/* -*- 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 <tools/helpers.hxx>
+#include <vcl/bitmap.hxx>
+#include <vcl/bitmapex.hxx>
+#include <vcl/bitmapaccess.hxx>
+#include <vcl/BitmapGaussianSeparableBlurFilter.hxx>
+#include <vcl/BitmapSeparableUnsharpenFilter.hxx>
+
+#include <bitmapwriteaccess.hxx>
+
+BitmapEx BitmapSeparableUnsharpenFilter::execute(BitmapEx const& rBitmapEx) const
+{
+ Bitmap aBitmap(rBitmapEx.GetBitmap());
+
+ const long nWidth = aBitmap.GetSizePixel().Width();
+ const long nHeight = aBitmap.GetSizePixel().Height();
+
+ Bitmap aBlur(aBitmap);
+ BitmapEx aBlurEx(aBlur);
+
+ BitmapFilter::Filter(aBlurEx, BitmapGaussianSeparableBlurFilter(-mfRadius));
+ aBlur = aBlurEx.GetBitmap();
+
+ // Amount of unsharpening effect on image - currently set to a fixed value
+ double aAmount = 2.0;
+
+ Bitmap aResultBitmap(Size(nWidth, nHeight), 24);
+
+ Bitmap::ScopedReadAccess pReadAccBlur(aBlur);
+ Bitmap::ScopedReadAccess pReadAcc(aBitmap);
+ BitmapScopedWriteAccess pWriteAcc(aResultBitmap);
+
+ BitmapColor aColor, aColorBlur;
+
+ // For all pixels in original image subtract pixels values from blurred image
+ for (long y = 0; y < nHeight; y++)
+ {
+ Scanline pScanline = pWriteAcc->GetScanline(y);
+ for (long x = 0; x < nWidth; x++)
+ {
+ aColorBlur = pReadAccBlur->GetColor(y, x);
+ aColor = pReadAcc->GetColor(y, x);
+
+ BitmapColor aResultColor(
+ static_cast<sal_uInt8>(MinMax(
+ aColor.GetRed() + (aColor.GetRed() - aColorBlur.GetRed()) * aAmount, 0, 255)),
+ static_cast<sal_uInt8>(MinMax(
+ aColor.GetGreen() + (aColor.GetGreen() - aColorBlur.GetGreen()) * aAmount, 0,
+ 255)),
+ static_cast<sal_uInt8>(
+ MinMax(aColor.GetBlue() + (aColor.GetBlue() - aColorBlur.GetBlue()) * aAmount,
+ 0, 255)));
+
+ pWriteAcc->SetPixelOnData(pScanline, x, aResultColor);
+ }
+ }
+
+ pWriteAcc.reset();
+ pReadAcc.reset();
+ pReadAccBlur.reset();
+ aBitmap.ReassignWithSize(aResultBitmap);
+
+ return BitmapEx(aBitmap);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/bitmap/BitmapSepiaFilter.cxx b/vcl/source/bitmap/BitmapSepiaFilter.cxx
new file mode 100644
index 000000000..5123bf130
--- /dev/null
+++ b/vcl/source/bitmap/BitmapSepiaFilter.cxx
@@ -0,0 +1,111 @@
+/* -*- 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 <sal/config.h>
+
+#include <algorithm>
+
+#include <vcl/bitmap.hxx>
+#include <vcl/bitmapex.hxx>
+#include <vcl/bitmapaccess.hxx>
+#include <vcl/BitmapSepiaFilter.hxx>
+
+#include <bitmapwriteaccess.hxx>
+
+BitmapEx BitmapSepiaFilter::execute(BitmapEx const& rBitmapEx) const
+{
+ Bitmap aBitmap(rBitmapEx.GetBitmap());
+ Bitmap::ScopedReadAccess pReadAcc(aBitmap);
+ bool bRet = false;
+
+ if (pReadAcc)
+ {
+ const long nSepia
+ = 10000 - 100 * std::clamp(mnSepiaPercent, sal_uInt16(0), sal_uInt16(100));
+ BitmapPalette aSepiaPal(256);
+
+ for (sal_uInt16 i = 0; i < 256; i++)
+ {
+ BitmapColor& rCol = aSepiaPal[i];
+ const sal_uInt8 cSepiaValue = static_cast<sal_uInt8>(nSepia * i / 10000);
+
+ rCol.SetRed(static_cast<sal_uInt8>(i));
+ rCol.SetGreen(cSepiaValue);
+ rCol.SetBlue(cSepiaValue);
+ }
+
+ Bitmap aNewBmp(aBitmap.GetSizePixel(), 8, &aSepiaPal);
+ BitmapScopedWriteAccess pWriteAcc(aNewBmp);
+
+ if (pWriteAcc)
+ {
+ BitmapColor aCol(sal_uInt8(0));
+ const long nWidth = pWriteAcc->Width();
+ const long nHeight = pWriteAcc->Height();
+
+ if (pReadAcc->HasPalette())
+ {
+ const sal_uInt16 nPalCount = pReadAcc->GetPaletteEntryCount();
+ std::unique_ptr<sal_uInt8[]> pIndexMap(new sal_uInt8[nPalCount]);
+ for (sal_uInt16 i = 0; i < nPalCount; i++)
+ {
+ pIndexMap[i] = pReadAcc->GetPaletteColor(i).GetLuminance();
+ }
+
+ for (long nY = 0; nY < nHeight; nY++)
+ {
+ Scanline pScanline = pWriteAcc->GetScanline(nY);
+ Scanline pScanlineRead = pReadAcc->GetScanline(nY);
+ for (long nX = 0; nX < nWidth; nX++)
+ {
+ aCol.SetIndex(pIndexMap[pReadAcc->GetIndexFromData(pScanlineRead, nX)]);
+ pWriteAcc->SetPixelOnData(pScanline, nX, aCol);
+ }
+ }
+ }
+ else
+ {
+ for (long nY = 0; nY < nHeight; nY++)
+ {
+ Scanline pScanline = pWriteAcc->GetScanline(nY);
+ Scanline pScanlineRead = pReadAcc->GetScanline(nY);
+ for (long nX = 0; nX < nWidth; nX++)
+ {
+ aCol.SetIndex(pReadAcc->GetPixelFromData(pScanlineRead, nX).GetLuminance());
+ pWriteAcc->SetPixelOnData(pScanline, nX, aCol);
+ }
+ }
+ }
+
+ pWriteAcc.reset();
+ bRet = true;
+ }
+
+ pReadAcc.reset();
+
+ if (bRet)
+ {
+ const MapMode aMap(aBitmap.GetPrefMapMode());
+ const Size aPrefSize(aBitmap.GetPrefSize());
+
+ aBitmap = aNewBmp;
+
+ aBitmap.SetPrefMapMode(aMap);
+ aBitmap.SetPrefSize(aPrefSize);
+ }
+ }
+
+ if (bRet)
+ return rBitmapEx;
+
+ return BitmapEx();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/bitmap/BitmapSimpleColorQuantizationFilter.cxx b/vcl/source/bitmap/BitmapSimpleColorQuantizationFilter.cxx
new file mode 100644
index 000000000..1d21ed9e1
--- /dev/null
+++ b/vcl/source/bitmap/BitmapSimpleColorQuantizationFilter.cxx
@@ -0,0 +1,113 @@
+/* -*- 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/bitmap.hxx>
+#include <vcl/bitmapex.hxx>
+#include <vcl/bitmapaccess.hxx>
+#include <vcl/BitmapSimpleColorQuantizationFilter.hxx>
+
+#include <bitmapwriteaccess.hxx>
+#include <bitmap/Octree.hxx>
+
+BitmapEx BitmapSimpleColorQuantizationFilter::execute(BitmapEx const& aBitmapEx) const
+{
+ Bitmap aBitmap = aBitmapEx.GetBitmap();
+
+ bool bRet = false;
+
+ if (aBitmap.GetColorCount() <= sal_Int64(mnNewColorCount))
+ {
+ bRet = true;
+ }
+ else
+ {
+ Bitmap aNewBmp;
+ Bitmap::ScopedReadAccess pRAcc(aBitmap);
+ const sal_uInt16 nColorCount = std::min(mnNewColorCount, sal_uInt16(256));
+ sal_uInt16 nBitCount = 0;
+
+ if (nColorCount <= 2)
+ nBitCount = 1;
+ else if (nColorCount <= 16)
+ nBitCount = 4;
+ else
+ nBitCount = 8;
+
+ if (pRAcc)
+ {
+ Octree aOct(*pRAcc, nColorCount);
+ const BitmapPalette& rPal = aOct.GetPalette();
+
+ aNewBmp = Bitmap(aBitmap.GetSizePixel(), nBitCount, &rPal);
+ BitmapScopedWriteAccess pWAcc(aNewBmp);
+
+ if (pWAcc)
+ {
+ const long nWidth = pRAcc->Width();
+ const long nHeight = pRAcc->Height();
+
+ if (pRAcc->HasPalette())
+ {
+ for (long nY = 0; nY < nHeight; nY++)
+ {
+ Scanline pScanline = pWAcc->GetScanline(nY);
+ Scanline pScanlineRead = pRAcc->GetScanline(nY);
+ for (long nX = 0; nX < nWidth; nX++)
+ {
+ auto c = pRAcc->GetPaletteColor(
+ pRAcc->GetIndexFromData(pScanlineRead, nX));
+ pWAcc->SetPixelOnData(
+ pScanline, nX,
+ BitmapColor(static_cast<sal_uInt8>(aOct.GetBestPaletteIndex(c))));
+ }
+ }
+ }
+ else
+ {
+ for (long nY = 0; nY < nHeight; nY++)
+ {
+ Scanline pScanline = pWAcc->GetScanline(nY);
+ Scanline pScanlineRead = pRAcc->GetScanline(nY);
+ for (long nX = 0; nX < nWidth; nX++)
+ {
+ auto c = pRAcc->GetPixelFromData(pScanlineRead, nX);
+ pWAcc->SetPixelOnData(
+ pScanline, nX,
+ BitmapColor(static_cast<sal_uInt8>(aOct.GetBestPaletteIndex(c))));
+ }
+ }
+ }
+
+ pWAcc.reset();
+ bRet = true;
+ }
+
+ pRAcc.reset();
+ }
+
+ if (bRet)
+ {
+ const MapMode aMap(aBitmap.GetPrefMapMode());
+ const Size aSize(aBitmap.GetPrefSize());
+
+ aBitmap = aNewBmp;
+
+ aBitmap.SetPrefMapMode(aMap);
+ aBitmap.SetPrefSize(aSize);
+ }
+ }
+
+ if (bRet)
+ return BitmapEx(aBitmap);
+
+ return BitmapEx();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/bitmap/BitmapSmoothenFilter.cxx b/vcl/source/bitmap/BitmapSmoothenFilter.cxx
new file mode 100644
index 000000000..67cea0cb3
--- /dev/null
+++ b/vcl/source/bitmap/BitmapSmoothenFilter.cxx
@@ -0,0 +1,32 @@
+/* -*- 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/bitmapex.hxx>
+#include <vcl/BitmapGaussianSeparableBlurFilter.hxx>
+#include <vcl/BitmapSeparableUnsharpenFilter.hxx>
+#include <vcl/BitmapSmoothenFilter.hxx>
+
+BitmapEx BitmapSmoothenFilter::execute(BitmapEx const& rBitmapEx) const
+{
+ BitmapEx aBitmapEx(rBitmapEx);
+ bool bRet = false;
+
+ if (mfRadius > 0.0) // Blur for positive values of mnRadius
+ bRet = BitmapFilter::Filter(aBitmapEx, BitmapGaussianSeparableBlurFilter(mfRadius));
+ else if (mfRadius < 0.0) // Unsharpen mask for negative values of mnRadius
+ bRet = BitmapFilter::Filter(aBitmapEx, BitmapSeparableUnsharpenFilter(mfRadius));
+
+ if (bRet)
+ return rBitmapEx;
+
+ return BitmapEx();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/bitmap/BitmapSobelGreyFilter.cxx b/vcl/source/bitmap/BitmapSobelGreyFilter.cxx
new file mode 100644
index 000000000..5ce4987a2
--- /dev/null
+++ b/vcl/source/bitmap/BitmapSobelGreyFilter.cxx
@@ -0,0 +1,170 @@
+/* -*- 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 <sal/config.h>
+
+#include <algorithm>
+
+#include <vcl/bitmap.hxx>
+#include <vcl/bitmapex.hxx>
+#include <vcl/bitmapaccess.hxx>
+#include <vcl/BitmapSobelGreyFilter.hxx>
+
+#include <bitmapwriteaccess.hxx>
+
+BitmapEx BitmapSobelGreyFilter::execute(BitmapEx const& rBitmapEx) const
+{
+ Bitmap aBitmap(rBitmapEx.GetBitmap());
+
+ bool bRet = aBitmap.ImplMakeGreyscales(256);
+
+ if (bRet)
+ {
+ bRet = false;
+
+ Bitmap::ScopedReadAccess pReadAcc(aBitmap);
+
+ if (pReadAcc)
+ {
+ Bitmap aNewBmp(aBitmap.GetSizePixel(), 8, &pReadAcc->GetPalette());
+ BitmapScopedWriteAccess pWriteAcc(aNewBmp);
+
+ if (pWriteAcc)
+ {
+ BitmapColor aGrey(sal_uInt8(0));
+ const long nWidth = pWriteAcc->Width();
+ const long nHeight = pWriteAcc->Height();
+ const long nMask111 = -1, nMask121 = 0, nMask131 = 1;
+ const long nMask211 = -2, nMask221 = 0, nMask231 = 2;
+ const long nMask311 = -1, nMask321 = 0, nMask331 = 1;
+ const long nMask112 = 1, nMask122 = 2, nMask132 = 1;
+ const long nMask212 = 0, nMask222 = 0, nMask232 = 0;
+ const long nMask312 = -1, nMask322 = -2, nMask332 = -1;
+ long nGrey11, nGrey12, nGrey13;
+ long nGrey21, nGrey22, nGrey23;
+ long nGrey31, nGrey32, nGrey33;
+ std::unique_ptr<long[]> pHMap(new long[nWidth + 2]);
+ std::unique_ptr<long[]> pVMap(new long[nHeight + 2]);
+ long nX, nY, nSum1, nSum2;
+
+ // fill mapping tables
+ pHMap[0] = 0;
+
+ for (nX = 1; nX <= nWidth; nX++)
+ {
+ pHMap[nX] = nX - 1;
+ }
+
+ pHMap[nWidth + 1] = nWidth - 1;
+
+ pVMap[0] = 0;
+
+ for (nY = 1; nY <= nHeight; nY++)
+ {
+ pVMap[nY] = nY - 1;
+ }
+
+ pVMap[nHeight + 1] = nHeight - 1;
+
+ for (nY = 0; nY < nHeight; nY++)
+ {
+ nGrey11 = pReadAcc->GetPixel(pVMap[nY], pHMap[0]).GetIndex();
+ nGrey12 = pReadAcc->GetPixel(pVMap[nY], pHMap[1]).GetIndex();
+ nGrey13 = pReadAcc->GetPixel(pVMap[nY], pHMap[2]).GetIndex();
+ nGrey21 = pReadAcc->GetPixel(pVMap[nY + 1], pHMap[0]).GetIndex();
+ nGrey22 = pReadAcc->GetPixel(pVMap[nY + 1], pHMap[1]).GetIndex();
+ nGrey23 = pReadAcc->GetPixel(pVMap[nY + 1], pHMap[2]).GetIndex();
+ nGrey31 = pReadAcc->GetPixel(pVMap[nY + 2], pHMap[0]).GetIndex();
+ nGrey32 = pReadAcc->GetPixel(pVMap[nY + 2], pHMap[1]).GetIndex();
+ nGrey33 = pReadAcc->GetPixel(pVMap[nY + 2], pHMap[2]).GetIndex();
+
+ Scanline pScanline = pWriteAcc->GetScanline(nY);
+ for (nX = 0; nX < nWidth; nX++)
+ {
+ nSum1 = nSum2 = 0;
+
+ nSum1 += nMask111 * nGrey11;
+ nSum2 += nMask112 * nGrey11;
+
+ nSum1 += nMask121 * nGrey12;
+ nSum2 += nMask122 * nGrey12;
+
+ nSum1 += nMask131 * nGrey13;
+ nSum2 += nMask132 * nGrey13;
+
+ nSum1 += nMask211 * nGrey21;
+ nSum2 += nMask212 * nGrey21;
+
+ nSum1 += nMask221 * nGrey22;
+ nSum2 += nMask222 * nGrey22;
+
+ nSum1 += nMask231 * nGrey23;
+ nSum2 += nMask232 * nGrey23;
+
+ nSum1 += nMask311 * nGrey31;
+ nSum2 += nMask312 * nGrey31;
+
+ nSum1 += nMask321 * nGrey32;
+ nSum2 += nMask322 * nGrey32;
+
+ nSum1 += nMask331 * nGrey33;
+ nSum2 += nMask332 * nGrey33;
+
+ nSum1 = static_cast<long>(
+ sqrt(static_cast<double>(nSum1 * nSum1 + nSum2 * nSum2)));
+
+ aGrey.SetIndex(~static_cast<sal_uInt8>(std::clamp(nSum1, 0L, 255L)));
+ pWriteAcc->SetPixelOnData(pScanline, nX, aGrey);
+
+ if (nX < (nWidth - 1))
+ {
+ const long nNextX = pHMap[nX + 3];
+
+ nGrey11 = nGrey12;
+ nGrey12 = nGrey13;
+ nGrey13 = pReadAcc->GetPixel(pVMap[nY], nNextX).GetIndex();
+ nGrey21 = nGrey22;
+ nGrey22 = nGrey23;
+ nGrey23 = pReadAcc->GetPixel(pVMap[nY + 1], nNextX).GetIndex();
+ nGrey31 = nGrey32;
+ nGrey32 = nGrey33;
+ nGrey33 = pReadAcc->GetPixel(pVMap[nY + 2], nNextX).GetIndex();
+ }
+ }
+ }
+
+ pHMap.reset();
+ pVMap.reset();
+ pWriteAcc.reset();
+ bRet = true;
+ }
+
+ pReadAcc.reset();
+
+ if (bRet)
+ {
+ const MapMode aMap(aBitmap.GetPrefMapMode());
+ const Size aPrefSize(aBitmap.GetPrefSize());
+
+ aBitmap = aNewBmp;
+
+ aBitmap.SetPrefMapMode(aMap);
+ aBitmap.SetPrefSize(aPrefSize);
+ }
+ }
+ }
+
+ if (bRet)
+ return BitmapEx(aBitmap);
+
+ return BitmapEx();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/bitmap/BitmapSolarizeFilter.cxx b/vcl/source/bitmap/BitmapSolarizeFilter.cxx
new file mode 100644
index 000000000..bd7518b4c
--- /dev/null
+++ b/vcl/source/bitmap/BitmapSolarizeFilter.cxx
@@ -0,0 +1,71 @@
+/* -*- 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/bitmap.hxx>
+#include <vcl/bitmapex.hxx>
+#include <vcl/BitmapSolarizeFilter.hxx>
+
+#include <bitmapwriteaccess.hxx>
+
+BitmapEx BitmapSolarizeFilter::execute(BitmapEx const& rBitmapEx) const
+{
+ Bitmap aBitmap(rBitmapEx.GetBitmap());
+ bool bRet = false;
+ BitmapScopedWriteAccess pWriteAcc(aBitmap);
+
+ if (pWriteAcc)
+ {
+ if (pWriteAcc->HasPalette())
+ {
+ const BitmapPalette& rPal = pWriteAcc->GetPalette();
+
+ for (sal_uInt16 i = 0, nCount = rPal.GetEntryCount(); i < nCount; i++)
+ {
+ if (rPal[i].GetLuminance() >= mcSolarGreyThreshold)
+ {
+ BitmapColor aCol(rPal[i]);
+ aCol.Invert();
+ pWriteAcc->SetPaletteColor(i, aCol);
+ }
+ }
+ }
+ else
+ {
+ BitmapColor aCol;
+ const long nWidth = pWriteAcc->Width();
+ const long nHeight = pWriteAcc->Height();
+
+ for (long nY = 0; nY < nHeight; nY++)
+ {
+ Scanline pScanline = pWriteAcc->GetScanline(nY);
+ for (long nX = 0; nX < nWidth; nX++)
+ {
+ aCol = pWriteAcc->GetPixelFromData(pScanline, nX);
+
+ if (aCol.GetLuminance() >= mcSolarGreyThreshold)
+ {
+ aCol.Invert();
+ pWriteAcc->SetPixelOnData(pScanline, nX, aCol);
+ }
+ }
+ }
+ }
+
+ pWriteAcc.reset();
+ bRet = true;
+ }
+
+ if (bRet)
+ return rBitmapEx;
+
+ return BitmapEx();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/bitmap/BitmapSymmetryCheck.cxx b/vcl/source/bitmap/BitmapSymmetryCheck.cxx
new file mode 100644
index 000000000..9abb48086
--- /dev/null
+++ b/vcl/source/bitmap/BitmapSymmetryCheck.cxx
@@ -0,0 +1,84 @@
+/* -*- 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 <BitmapSymmetryCheck.hxx>
+
+BitmapSymmetryCheck::BitmapSymmetryCheck()
+{}
+
+BitmapSymmetryCheck::~BitmapSymmetryCheck()
+{}
+
+bool BitmapSymmetryCheck::check(Bitmap& rBitmap)
+{
+ Bitmap::ScopedReadAccess aReadAccess(rBitmap);
+ return checkImpl(aReadAccess.get());
+}
+
+bool BitmapSymmetryCheck::checkImpl(BitmapReadAccess const * pReadAccess)
+{
+ long nHeight = pReadAccess->Height();
+ long nWidth = pReadAccess->Width();
+
+ long nHeightHalf = nHeight / 2;
+ long nWidthHalf = nWidth / 2;
+
+ bool bHeightEven = (nHeight % 2) == 0;
+ bool bWidthEven = (nWidth % 2) == 0;
+
+ for (long y = 0; y < nHeightHalf; ++y)
+ {
+ Scanline pScanlineRead = pReadAccess->GetScanline( y );
+ Scanline pScanlineRead2 = pReadAccess->GetScanline( nHeight - y - 1 );
+ for (long x = 0; x < nWidthHalf; ++x)
+ {
+ if (pReadAccess->GetPixelFromData(pScanlineRead, x) != pReadAccess->GetPixelFromData(pScanlineRead2, x))
+ {
+ return false;
+ }
+ if (pReadAccess->GetPixelFromData(pScanlineRead, x) != pReadAccess->GetPixelFromData(pScanlineRead, nWidth - x - 1))
+ {
+ return false;
+ }
+ if (pReadAccess->GetPixelFromData(pScanlineRead, x) != pReadAccess->GetPixelFromData(pScanlineRead2, nWidth - x - 1))
+ {
+ return false;
+ }
+ }
+ }
+
+ if (bWidthEven)
+ {
+ for (long y = 0; y < nHeightHalf; ++y)
+ {
+ if (pReadAccess->GetPixel(y, nWidthHalf) != pReadAccess->GetPixel(nHeight - y - 1, nWidthHalf))
+ {
+ return false;
+ }
+ }
+ }
+
+ if (bHeightEven)
+ {
+ Scanline pScanlineRead = pReadAccess->GetScanline( nHeightHalf );
+ for (long x = 0; x < nWidthHalf; ++x)
+ {
+ if (pReadAccess->GetPixelFromData(pScanlineRead, x) != pReadAccess->GetPixelFromData(pScanlineRead, nWidth - x - 1))
+ {
+ return false;
+ }
+ }
+ }
+
+ return true;
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/bitmap/BitmapTools.cxx b/vcl/source/bitmap/BitmapTools.cxx
new file mode 100644
index 000000000..3f0226047
--- /dev/null
+++ b/vcl/source/bitmap/BitmapTools.cxx
@@ -0,0 +1,1131 @@
+/* -*- 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/BitmapTools.hxx>
+
+#include <sal/log.hxx>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/seqstream.hxx>
+#include <vcl/canvastools.hxx>
+#include <basegfx/matrix/b2dhommatrix.hxx>
+
+#include <com/sun/star/graphic/SvgTools.hpp>
+#include <com/sun/star/graphic/Primitive2DTools.hpp>
+
+#include <drawinglayer/primitive2d/baseprimitive2d.hxx>
+
+#include <com/sun/star/rendering/XIntegerReadOnlyBitmap.hpp>
+
+#include <vcl/dibtools.hxx>
+#include <vcl/settings.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/bitmapaccess.hxx>
+#include <vcl/virdev.hxx>
+#if ENABLE_CAIRO_CANVAS
+#include <cairo.h>
+#endif
+#include <tools/diagnose_ex.h>
+#include <tools/fract.hxx>
+#include <tools/stream.hxx>
+#include <bitmapwriteaccess.hxx>
+
+using namespace css;
+
+using drawinglayer::primitive2d::Primitive2DSequence;
+using drawinglayer::primitive2d::Primitive2DReference;
+
+namespace vcl::bitmap
+{
+
+BitmapEx loadFromName(const OUString& rFileName, const ImageLoadFlags eFlags)
+{
+ bool bSuccess = true;
+ OUString aIconTheme;
+ BitmapEx aBitmapEx;
+ try
+ {
+ aIconTheme = Application::GetSettings().GetStyleSettings().DetermineIconTheme();
+ ImageTree::get().loadImage(rFileName, aIconTheme, aBitmapEx, true, eFlags);
+ }
+ catch (...)
+ {
+ bSuccess = false;
+ }
+
+ SAL_WARN_IF(!bSuccess, "vcl", "vcl::bitmap::loadFromName : could not load image " << rFileName << " via icon theme " << aIconTheme);
+
+ return aBitmapEx;
+}
+
+void loadFromSvg(SvStream& rStream, const OUString& sPath, BitmapEx& rBitmapEx, double fScalingFactor)
+{
+ uno::Reference<uno::XComponentContext> xContext(comphelper::getProcessComponentContext());
+ const uno::Reference<graphic::XSvgParser> xSvgParser = graphic::SvgTools::create(xContext);
+
+ std::size_t nSize = rStream.remainingSize();
+ std::vector<sal_Int8> aBuffer(nSize + 1);
+ rStream.ReadBytes(aBuffer.data(), nSize);
+ aBuffer[nSize] = 0;
+
+ uno::Sequence<sal_Int8> aData(aBuffer.data(), nSize + 1);
+ uno::Reference<io::XInputStream> aInputStream(new comphelper::SequenceInputStream(aData));
+
+ const Primitive2DSequence aPrimitiveSequence = xSvgParser->getDecomposition(aInputStream, sPath);
+
+ if (!aPrimitiveSequence.hasElements())
+ return;
+
+ uno::Sequence<beans::PropertyValue> aViewParameters;
+
+ geometry::RealRectangle2D aRealRect;
+ basegfx::B2DRange aRange;
+ for (Primitive2DReference const & xReference : aPrimitiveSequence)
+ {
+ if (xReference.is())
+ {
+ aRealRect = xReference->getRange(aViewParameters);
+ aRange.expand(basegfx::B2DRange(aRealRect.X1, aRealRect.Y1, aRealRect.X2, aRealRect.Y2));
+ }
+ }
+
+ aRealRect.X1 = aRange.getMinX();
+ aRealRect.Y1 = aRange.getMinY();
+ aRealRect.X2 = aRange.getMaxX();
+ aRealRect.Y2 = aRange.getMaxY();
+
+ double nDPI = 96 * fScalingFactor;
+
+ const css::uno::Reference<css::graphic::XPrimitive2DRenderer> xPrimitive2DRenderer = css::graphic::Primitive2DTools::create(xContext);
+ const css::uno::Reference<css::rendering::XBitmap> xBitmap(
+ xPrimitive2DRenderer->rasterize(aPrimitiveSequence, aViewParameters, nDPI, nDPI, aRealRect, 256*256));
+
+ if (xBitmap.is())
+ {
+ const css::uno::Reference<css::rendering::XIntegerReadOnlyBitmap> xIntBmp(xBitmap, uno::UNO_QUERY_THROW);
+ rBitmapEx = vcl::unotools::bitmapExFromXBitmap(xIntBmp);
+ }
+
+}
+
+/** Copy block of image data into the bitmap.
+ Assumes that the Bitmap has been constructed with the desired size.
+
+ @param pData
+ The block of data to copy
+ @param nStride
+ The number of bytes in a scanline, must >= (width * nBitCount / 8)
+*/
+BitmapEx CreateFromData( sal_uInt8 const *pData, sal_Int32 nWidth, sal_Int32 nHeight, sal_Int32 nStride, sal_uInt16 nBitCount )
+{
+ assert(nStride >= (nWidth * nBitCount / 8));
+ assert( nBitCount == 1 || nBitCount == 24 || nBitCount == 32);
+ Bitmap aBmp( Size( nWidth, nHeight ), nBitCount );
+
+ BitmapScopedWriteAccess pWrite(aBmp);
+ assert(pWrite.get());
+ if( !pWrite )
+ return BitmapEx();
+ std::unique_ptr<AlphaMask> pAlphaMask;
+ AlphaScopedWriteAccess xMaskAcc;
+ if (nBitCount == 32)
+ {
+ pAlphaMask.reset( new AlphaMask( Size(nWidth, nHeight) ) );
+ xMaskAcc = AlphaScopedWriteAccess(*pAlphaMask);
+ }
+ if (nBitCount == 1)
+ {
+ for( long y = 0; y < nHeight; ++y )
+ {
+ Scanline pScanline = pWrite->GetScanline(y);
+ for (long x = 0; x < nWidth; ++x)
+ {
+ sal_uInt8 const *p = pData + y * nStride / 8;
+ int bitIndex = (y * nStride) % 8;
+ pWrite->SetPixelOnData(pScanline, x, BitmapColor((*p >> bitIndex) & 1));
+ }
+ }
+ }
+ else
+ {
+ for( long y = 0; y < nHeight; ++y )
+ {
+ sal_uInt8 const *p = pData + (y * nStride);
+ Scanline pScanline = pWrite->GetScanline(y);
+ for (long x = 0; x < nWidth; ++x)
+ {
+ BitmapColor col(p[0], p[1], p[2]);
+ pWrite->SetPixelOnData(pScanline, x, col);
+ p += nBitCount/8;
+ }
+ if (nBitCount == 32)
+ {
+ p = pData + (y * nStride) + 3;
+ Scanline pMaskScanLine = xMaskAcc->GetScanline(y);
+ for (long x = 0; x < nWidth; ++x)
+ {
+ xMaskAcc->SetPixelOnData(pMaskScanLine, x, BitmapColor(*p));
+ p += 4;
+ }
+ }
+ }
+ }
+ if (nBitCount == 32)
+ return BitmapEx(aBmp, *pAlphaMask);
+ else
+ return BitmapEx(aBmp);
+}
+
+/** Copy block of image data into the bitmap.
+ Assumes that the Bitmap has been constructed with the desired size.
+*/
+BitmapEx CreateFromData( RawBitmap&& rawBitmap )
+{
+ auto nBitCount = rawBitmap.GetBitCount();
+ assert( nBitCount == 24 || nBitCount == 32);
+ Bitmap aBmp( rawBitmap.maSize, nBitCount );
+
+ BitmapScopedWriteAccess pWrite(aBmp);
+ assert(pWrite.get());
+ if( !pWrite )
+ return BitmapEx();
+ std::unique_ptr<AlphaMask> pAlphaMask;
+ AlphaScopedWriteAccess xMaskAcc;
+ if (nBitCount == 32)
+ {
+ pAlphaMask.reset( new AlphaMask( rawBitmap.maSize ) );
+ xMaskAcc = AlphaScopedWriteAccess(*pAlphaMask);
+ }
+
+ auto nHeight = rawBitmap.maSize.getHeight();
+ auto nWidth = rawBitmap.maSize.getWidth();
+ auto nStride = nWidth * nBitCount / 8;
+ for( long y = 0; y < nHeight; ++y )
+ {
+ sal_uInt8 const *p = rawBitmap.mpData.get() + (y * nStride);
+ Scanline pScanline = pWrite->GetScanline(y);
+ for (long x = 0; x < nWidth; ++x)
+ {
+ BitmapColor col(p[0], p[1], p[2]);
+ pWrite->SetPixelOnData(pScanline, x, col);
+ p += nBitCount/8;
+ }
+ if (nBitCount == 32)
+ {
+ p = rawBitmap.mpData.get() + (y * nStride) + 3;
+ Scanline pMaskScanLine = xMaskAcc->GetScanline(y);
+ for (long x = 0; x < nWidth; ++x)
+ {
+ xMaskAcc->SetPixelOnData(pMaskScanLine, x, BitmapColor(*p));
+ p += 4;
+ }
+ }
+ }
+ if (nBitCount == 32)
+ return BitmapEx(aBmp, *pAlphaMask);
+ else
+ return BitmapEx(aBmp);
+}
+
+#if ENABLE_CAIRO_CANVAS
+BitmapEx* CreateFromCairoSurface(Size aSize, cairo_surface_t * pSurface)
+{
+ // FIXME: if we could teach VCL/ about cairo handles, life could
+ // be significantly better here perhaps.
+
+#if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 12, 0)
+ cairo_surface_t *pPixels = cairo_surface_create_similar_image(pSurface,
+#else
+ cairo_surface_t *pPixels = cairo_image_surface_create(
+#endif
+ CAIRO_FORMAT_ARGB32, aSize.Width(), aSize.Height());
+ cairo_t *pCairo = cairo_create( pPixels );
+ if( !pPixels || !pCairo || cairo_status(pCairo) != CAIRO_STATUS_SUCCESS )
+ return nullptr;
+
+ // suck ourselves from the X server to this buffer so then we can fiddle with
+ // Alpha to turn it into the ultra-lame vcl required format and then push it
+ // all back again later at vast expense [ urgh ]
+ cairo_set_source_surface( pCairo, pSurface, 0, 0 );
+ cairo_set_operator( pCairo, CAIRO_OPERATOR_SOURCE );
+ cairo_paint( pCairo );
+
+ ::Bitmap aRGB( aSize, 24 );
+ ::AlphaMask aMask( aSize );
+
+ BitmapScopedWriteAccess pRGBWrite(aRGB);
+ assert(pRGBWrite);
+ if (!pRGBWrite)
+ return nullptr;
+
+ AlphaScopedWriteAccess pMaskWrite(aMask);
+ assert(pMaskWrite);
+ if (!pMaskWrite)
+ return nullptr;
+
+ cairo_surface_flush(pPixels);
+ unsigned char *pSrc = cairo_image_surface_get_data( pPixels );
+ unsigned int nStride = cairo_image_surface_get_stride( pPixels );
+ vcl::bitmap::lookup_table unpremultiply_table = vcl::bitmap::get_unpremultiply_table();
+ for( long y = 0; y < aSize.Height(); y++ )
+ {
+ sal_uInt32 *pPix = reinterpret_cast<sal_uInt32 *>(pSrc + nStride * y);
+ for( long x = 0; x < aSize.Width(); x++ )
+ {
+#if defined OSL_BIGENDIAN
+ sal_uInt8 nB = (*pPix >> 24);
+ sal_uInt8 nG = (*pPix >> 16) & 0xff;
+ sal_uInt8 nR = (*pPix >> 8) & 0xff;
+ sal_uInt8 nAlpha = *pPix & 0xff;
+#else
+ sal_uInt8 nAlpha = (*pPix >> 24);
+ sal_uInt8 nR = (*pPix >> 16) & 0xff;
+ sal_uInt8 nG = (*pPix >> 8) & 0xff;
+ sal_uInt8 nB = *pPix & 0xff;
+#endif
+ if( nAlpha != 0 && nAlpha != 255 )
+ {
+ // Cairo uses pre-multiplied alpha - we do not => re-multiply
+ nR = unpremultiply_table[nAlpha][nR];
+ nG = unpremultiply_table[nAlpha][nG];
+ nB = unpremultiply_table[nAlpha][nB];
+ }
+ pRGBWrite->SetPixel( y, x, BitmapColor( nR, nG, nB ) );
+ pMaskWrite->SetPixelIndex( y, x, 255 - nAlpha );
+ pPix++;
+ }
+ }
+
+ // ignore potential errors above. will get caller a
+ // uniformly white bitmap, but not that there would
+ // be error handling in calling code ...
+ ::BitmapEx *pBitmapEx = new ::BitmapEx( aRGB, aMask );
+
+ cairo_destroy( pCairo );
+ cairo_surface_destroy( pPixels );
+ return pBitmapEx;
+}
+#endif
+
+BitmapEx CanvasTransformBitmap( const BitmapEx& rBitmap,
+ const ::basegfx::B2DHomMatrix& rTransform,
+ ::basegfx::B2DRectangle const & rDestRect,
+ ::basegfx::B2DHomMatrix const & rLocalTransform )
+{
+ const Size aBmpSize( rBitmap.GetSizePixel() );
+ Bitmap aSrcBitmap( rBitmap.GetBitmap() );
+ Bitmap aSrcAlpha;
+
+ // differentiate mask and alpha channel (on-off
+ // vs. multi-level transparency)
+ if( rBitmap.IsTransparent() )
+ {
+ if( rBitmap.IsAlpha() )
+ aSrcAlpha = rBitmap.GetAlpha().GetBitmap();
+ else
+ aSrcAlpha = rBitmap.GetMask();
+ }
+
+ Bitmap::ScopedReadAccess pReadAccess( aSrcBitmap );
+ Bitmap::ScopedReadAccess pAlphaReadAccess( rBitmap.IsTransparent() ?
+ aSrcAlpha.AcquireReadAccess() :
+ nullptr,
+ aSrcAlpha );
+
+ if( pReadAccess.get() == nullptr ||
+ (pAlphaReadAccess.get() == nullptr && rBitmap.IsTransparent()) )
+ {
+ // TODO(E2): Error handling!
+ ENSURE_OR_THROW( false,
+ "transformBitmap(): could not access source bitmap" );
+ }
+
+ // mapping table, to translate pAlphaReadAccess' pixel
+ // values into destination alpha values (needed e.g. for
+ // paletted 1-bit masks).
+ sal_uInt8 aAlphaMap[256];
+
+ if( rBitmap.IsTransparent() )
+ {
+ if( rBitmap.IsAlpha() )
+ {
+ // source already has alpha channel - 1:1 mapping,
+ // i.e. aAlphaMap[0]=0,...,aAlphaMap[255]=255.
+ sal_uInt8 val=0;
+ sal_uInt8* pCur=aAlphaMap;
+ sal_uInt8* const pEnd=&aAlphaMap[256];
+ while(pCur != pEnd)
+ *pCur++ = val++;
+ }
+ else
+ {
+ // mask transparency - determine used palette colors
+ const BitmapColor& rCol0( pAlphaReadAccess->GetPaletteColor( 0 ) );
+ const BitmapColor& rCol1( pAlphaReadAccess->GetPaletteColor( 1 ) );
+
+ // shortcut for true luminance calculation
+ // (assumes that palette is grey-level)
+ aAlphaMap[0] = rCol0.GetRed();
+ aAlphaMap[1] = rCol1.GetRed();
+ }
+ }
+ // else: mapping table is not used
+
+ const Size aDestBmpSize( ::basegfx::fround( rDestRect.getWidth() ),
+ ::basegfx::fround( rDestRect.getHeight() ) );
+
+ if( aDestBmpSize.IsEmpty() )
+ return BitmapEx();
+
+ Bitmap aDstBitmap( aDestBmpSize, aSrcBitmap.GetBitCount(), &pReadAccess->GetPalette() );
+ Bitmap aDstAlpha( AlphaMask( aDestBmpSize ).GetBitmap() );
+
+ {
+ // just to be on the safe side: let the
+ // ScopedAccessors get destructed before
+ // copy-constructing the resulting bitmap. This will
+ // rule out the possibility that cached accessor data
+ // is not yet written back.
+ BitmapScopedWriteAccess pWriteAccess( aDstBitmap );
+ BitmapScopedWriteAccess pAlphaWriteAccess( aDstAlpha );
+
+
+ if( pWriteAccess.get() != nullptr &&
+ pAlphaWriteAccess.get() != nullptr &&
+ rTransform.isInvertible() )
+ {
+ // we're doing inverse mapping here, i.e. mapping
+ // points from the destination bitmap back to the
+ // source
+ ::basegfx::B2DHomMatrix aTransform( rLocalTransform );
+ aTransform.invert();
+
+ // for the time being, always read as ARGB
+ for( long y=0; y<aDestBmpSize.Height(); ++y )
+ {
+ // differentiate mask and alpha channel (on-off
+ // vs. multi-level transparency)
+ if( rBitmap.IsTransparent() )
+ {
+ Scanline pScan = pWriteAccess->GetScanline( y );
+ Scanline pScanAlpha = pAlphaWriteAccess->GetScanline( y );
+ // Handling alpha and mask just the same...
+ for( long x=0; x<aDestBmpSize.Width(); ++x )
+ {
+ ::basegfx::B2DPoint aPoint(x,y);
+ aPoint *= aTransform;
+
+ const int nSrcX( ::basegfx::fround( aPoint.getX() ) );
+ const int nSrcY( ::basegfx::fround( aPoint.getY() ) );
+ if( nSrcX < 0 || nSrcX >= aBmpSize.Width() ||
+ nSrcY < 0 || nSrcY >= aBmpSize.Height() )
+ {
+ pAlphaWriteAccess->SetPixelOnData( pScanAlpha, x, BitmapColor(255) );
+ }
+ else
+ {
+ const sal_uInt8 cAlphaIdx = pAlphaReadAccess->GetPixelIndex( nSrcY, nSrcX );
+ pAlphaWriteAccess->SetPixelOnData( pScanAlpha, x, BitmapColor(aAlphaMap[ cAlphaIdx ]) );
+ pWriteAccess->SetPixelOnData( pScan, x, pReadAccess->GetPixel( nSrcY, nSrcX ) );
+ }
+ }
+ }
+ else
+ {
+ Scanline pScan = pWriteAccess->GetScanline( y );
+ Scanline pScanAlpha = pAlphaWriteAccess->GetScanline( y );
+ for( long x=0; x<aDestBmpSize.Width(); ++x )
+ {
+ ::basegfx::B2DPoint aPoint(x,y);
+ aPoint *= aTransform;
+
+ const int nSrcX( ::basegfx::fround( aPoint.getX() ) );
+ const int nSrcY( ::basegfx::fround( aPoint.getY() ) );
+ if( nSrcX < 0 || nSrcX >= aBmpSize.Width() ||
+ nSrcY < 0 || nSrcY >= aBmpSize.Height() )
+ {
+ pAlphaWriteAccess->SetPixelOnData( pScanAlpha, x, BitmapColor(255) );
+ }
+ else
+ {
+ pAlphaWriteAccess->SetPixelOnData( pScanAlpha, x, BitmapColor(0) );
+ pWriteAccess->SetPixelOnData( pScan, x, pReadAccess->GetPixel( nSrcY,
+ nSrcX ) );
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ // TODO(E2): Error handling!
+ ENSURE_OR_THROW( false,
+ "transformBitmap(): could not access bitmap" );
+ }
+ }
+
+ return BitmapEx(aDstBitmap, AlphaMask(aDstAlpha));
+}
+
+
+void DrawAlphaBitmapAndAlphaGradient(BitmapEx & rBitmapEx, bool bFixedTransparence, float fTransparence, AlphaMask & rNewMask)
+{
+ // mix existing and new alpha mask
+ AlphaMask aOldMask;
+
+ if(rBitmapEx.IsAlpha())
+ {
+ aOldMask = rBitmapEx.GetAlpha();
+ }
+ else if(TransparentType::Bitmap == rBitmapEx.GetTransparentType())
+ {
+ aOldMask = rBitmapEx.GetMask();
+ }
+ else if(TransparentType::Color == rBitmapEx.GetTransparentType())
+ {
+ aOldMask = rBitmapEx.GetBitmap().CreateMask(rBitmapEx.GetTransparentColor());
+ }
+
+ {
+ AlphaScopedWriteAccess pOld(aOldMask);
+
+ assert(pOld && "Got no access to old alpha mask (!)");
+
+ const double fFactor(1.0 / 255.0);
+
+ if(bFixedTransparence)
+ {
+ const double fOpNew(1.0 - fTransparence);
+
+ for(long y(0); y < pOld->Height(); y++)
+ {
+ Scanline pScanline = pOld->GetScanline( y );
+ for(long x(0); x < pOld->Width(); x++)
+ {
+ const double fOpOld(1.0 - (pOld->GetIndexFromData(pScanline, x) * fFactor));
+ const sal_uInt8 aCol(basegfx::fround((1.0 - (fOpOld * fOpNew)) * 255.0));
+
+ pOld->SetPixelOnData(pScanline, x, BitmapColor(aCol));
+ }
+ }
+ }
+ else
+ {
+ AlphaMask::ScopedReadAccess pNew(rNewMask);
+
+ assert(pNew && "Got no access to new alpha mask (!)");
+
+ assert(pOld->Width() == pNew->Width() && pOld->Height() == pNew->Height() &&
+ "Alpha masks have different sizes (!)");
+
+ for(long y(0); y < pOld->Height(); y++)
+ {
+ Scanline pScanline = pOld->GetScanline( y );
+ for(long x(0); x < pOld->Width(); x++)
+ {
+ const double fOpOld(1.0 - (pOld->GetIndexFromData(pScanline, x) * fFactor));
+ const double fOpNew(1.0 - (pNew->GetIndexFromData(pScanline, x) * fFactor));
+ const sal_uInt8 aCol(basegfx::fround((1.0 - (fOpOld * fOpNew)) * 255.0));
+
+ pOld->SetPixelOnData(pScanline, x, BitmapColor(aCol));
+ }
+ }
+ }
+
+ }
+
+ // apply combined bitmap as mask
+ rBitmapEx = BitmapEx(rBitmapEx.GetBitmap(), aOldMask);
+}
+
+
+void DrawAndClipBitmap(const Point& rPos, const Size& rSize, const BitmapEx& rBitmap, BitmapEx & aBmpEx, basegfx::B2DPolyPolygon const & rClipPath)
+{
+ ScopedVclPtrInstance< VirtualDevice > pVDev;
+ MapMode aMapMode( MapUnit::Map100thMM );
+ aMapMode.SetOrigin( Point( -rPos.X(), -rPos.Y() ) );
+ const Size aOutputSizePixel( pVDev->LogicToPixel( rSize, aMapMode ) );
+ const Size aSizePixel( rBitmap.GetSizePixel() );
+ if ( aOutputSizePixel.Width() && aOutputSizePixel.Height() )
+ {
+ aMapMode.SetScaleX( Fraction( aSizePixel.Width(), aOutputSizePixel.Width() ) );
+ aMapMode.SetScaleY( Fraction( aSizePixel.Height(), aOutputSizePixel.Height() ) );
+ }
+ pVDev->SetMapMode( aMapMode );
+ pVDev->SetOutputSizePixel( aSizePixel );
+ pVDev->SetFillColor( COL_BLACK );
+ const tools::PolyPolygon aClip( rClipPath );
+ pVDev->DrawPolyPolygon( aClip );
+
+ // #i50672# Extract whole VDev content (to match size of rBitmap)
+ pVDev->EnableMapMode( false );
+ const Bitmap aVDevMask(pVDev->GetBitmap(Point(), aSizePixel));
+
+ if(aBmpEx.IsTransparent())
+ {
+ // bitmap already uses a Mask or Alpha, we need to blend that with
+ // the new masking in pVDev
+ if(aBmpEx.IsAlpha())
+ {
+ // need to blend in AlphaMask quality (8Bit)
+ AlphaMask fromVDev(aVDevMask);
+ AlphaMask fromBmpEx(aBmpEx.GetAlpha());
+ AlphaMask::ScopedReadAccess pR(fromVDev);
+ AlphaScopedWriteAccess pW(fromBmpEx);
+
+ if(pR && pW)
+ {
+ const long nWidth(std::min(pR->Width(), pW->Width()));
+ const long nHeight(std::min(pR->Height(), pW->Height()));
+
+ for(long nY(0); nY < nHeight; nY++)
+ {
+ Scanline pScanlineR = pR->GetScanline( nY );
+ Scanline pScanlineW = pW->GetScanline( nY );
+ for(long nX(0); nX < nWidth; nX++)
+ {
+ const sal_uInt8 nIndR(pR->GetIndexFromData(pScanlineR, nX));
+ const sal_uInt8 nIndW(pW->GetIndexFromData(pScanlineW, nX));
+
+ // these values represent transparency (0 == no, 255 == fully transparent),
+ // so to blend these we have to multiply the inverse (opacity)
+ // and re-invert the result to transparence
+ const sal_uInt8 nCombined(0x00ff - (((0x00ff - nIndR) * (0x00ff - nIndW)) >> 8));
+
+ pW->SetPixelOnData(pScanlineW, nX, BitmapColor(nCombined));
+ }
+ }
+ }
+
+ pR.reset();
+ pW.reset();
+ aBmpEx = BitmapEx(aBmpEx.GetBitmap(), fromBmpEx);
+ }
+ else
+ {
+ // need to blend in Mask quality (1Bit)
+ Bitmap aMask(aVDevMask.CreateMask(COL_WHITE));
+
+ if ( rBitmap.GetTransparentColor() == COL_WHITE )
+ {
+ aMask.CombineSimple( rBitmap.GetMask(), BmpCombine::Or );
+ }
+ else
+ {
+ aMask.CombineSimple( rBitmap.GetMask(), BmpCombine::And );
+ }
+
+ aBmpEx = BitmapEx( rBitmap.GetBitmap(), aMask );
+ }
+ }
+ else
+ {
+ // no mask yet, create and add new mask. For better quality, use Alpha,
+ // this allows the drawn mask being processed with AntiAliasing (AAed)
+ aBmpEx = BitmapEx(rBitmap.GetBitmap(), aVDevMask);
+ }
+}
+
+
+css::uno::Sequence< sal_Int8 > GetMaskDIB(BitmapEx const & aBmpEx)
+{
+ if ( aBmpEx.IsAlpha() )
+ {
+ SvMemoryStream aMem;
+ WriteDIB(aBmpEx.GetAlpha().GetBitmap(), aMem, false, true);
+ return css::uno::Sequence< sal_Int8 >( static_cast<sal_Int8 const *>(aMem.GetData()), aMem.Tell() );
+ }
+ else if ( aBmpEx.IsTransparent() )
+ {
+ SvMemoryStream aMem;
+ WriteDIB(aBmpEx.GetMask(), aMem, false, true);
+ return css::uno::Sequence< sal_Int8 >( static_cast<sal_Int8 const *>(aMem.GetData()), aMem.Tell() );
+ }
+
+ return css::uno::Sequence< sal_Int8 >();
+}
+
+static bool readAlpha( BitmapReadAccess const * pAlphaReadAcc, long nY, const long nWidth, unsigned char* data, long nOff )
+{
+ bool bIsAlpha = false;
+ long nX;
+ int nAlpha;
+ Scanline pReadScan;
+
+ nOff += 3;
+
+ switch( pAlphaReadAcc->GetScanlineFormat() )
+ {
+ case ScanlineFormat::N8BitTcMask:
+ pReadScan = pAlphaReadAcc->GetScanline( nY );
+ for( nX = 0; nX < nWidth; nX++ )
+ {
+ nAlpha = data[ nOff ] = 255 - ( *pReadScan++ );
+ if( nAlpha != 255 )
+ bIsAlpha = true;
+ nOff += 4;
+ }
+ break;
+ case ScanlineFormat::N8BitPal:
+ pReadScan = pAlphaReadAcc->GetScanline( nY );
+ for( nX = 0; nX < nWidth; nX++ )
+ {
+ BitmapColor const& rColor(
+ pAlphaReadAcc->GetPaletteColor(*pReadScan));
+ pReadScan++;
+ nAlpha = data[ nOff ] = 255 - rColor.GetIndex();
+ if( nAlpha != 255 )
+ bIsAlpha = true;
+ nOff += 4;
+ }
+ break;
+ default:
+ SAL_INFO( "canvas.cairo", "fallback to GetColor for alpha - slow, format: " << static_cast<int>(pAlphaReadAcc->GetScanlineFormat()) );
+ for( nX = 0; nX < nWidth; nX++ )
+ {
+ nAlpha = data[ nOff ] = 255 - pAlphaReadAcc->GetColor( nY, nX ).GetIndex();
+ if( nAlpha != 255 )
+ bIsAlpha = true;
+ nOff += 4;
+ }
+ }
+
+ return bIsAlpha;
+}
+
+
+
+/**
+ * @param data will be filled with alpha data, if xBitmap is alpha/transparent image
+ * @param bHasAlpha will be set to true if resulting surface has alpha
+ **/
+void CanvasCairoExtractBitmapData( BitmapEx const & aBmpEx, Bitmap & aBitmap, unsigned char*& data, bool& bHasAlpha, long& rnWidth, long& rnHeight )
+{
+ AlphaMask aAlpha = aBmpEx.GetAlpha();
+
+ ::BitmapReadAccess* pBitmapReadAcc = aBitmap.AcquireReadAccess();
+ ::BitmapReadAccess* pAlphaReadAcc = nullptr;
+ const long nWidth = rnWidth = pBitmapReadAcc->Width();
+ const long nHeight = rnHeight = pBitmapReadAcc->Height();
+ long nX, nY;
+ bool bIsAlpha = false;
+
+ if( aBmpEx.IsTransparent() || aBmpEx.IsAlpha() )
+ pAlphaReadAcc = aAlpha.AcquireReadAccess();
+
+ data = static_cast<unsigned char*>(malloc( nWidth*nHeight*4 ));
+
+ long nOff = 0;
+ ::Color aColor;
+ unsigned int nAlpha = 255;
+
+ vcl::bitmap::lookup_table premultiply_table = vcl::bitmap::get_premultiply_table();
+ for( nY = 0; nY < nHeight; nY++ )
+ {
+ ::Scanline pReadScan;
+
+ switch( pBitmapReadAcc->GetScanlineFormat() )
+ {
+ case ScanlineFormat::N8BitPal:
+ pReadScan = pBitmapReadAcc->GetScanline( nY );
+ if( pAlphaReadAcc )
+ if( readAlpha( pAlphaReadAcc, nY, nWidth, data, nOff ) )
+ bIsAlpha = true;
+
+ for( nX = 0; nX < nWidth; nX++ )
+ {
+#ifdef OSL_BIGENDIAN
+ if( pAlphaReadAcc )
+ nAlpha = data[ nOff++ ];
+ else
+ nAlpha = data[ nOff++ ] = 255;
+#else
+ if( pAlphaReadAcc )
+ nAlpha = data[ nOff + 3 ];
+ else
+ nAlpha = data[ nOff + 3 ] = 255;
+#endif
+ aColor = pBitmapReadAcc->GetPaletteColor(*pReadScan++);
+
+#ifdef OSL_BIGENDIAN
+ data[ nOff++ ] = premultiply_table[nAlpha][aColor.GetRed()];
+ data[ nOff++ ] = premultiply_table[nAlpha][aColor.GetGreen()];
+ data[ nOff++ ] = premultiply_table[nAlpha][aColor.GetBlue()];
+#else
+ data[ nOff++ ] = premultiply_table[nAlpha][aColor.GetBlue()];
+ data[ nOff++ ] = premultiply_table[nAlpha][aColor.GetGreen()];
+ data[ nOff++ ] = premultiply_table[nAlpha][aColor.GetRed()];
+ nOff++;
+#endif
+ }
+ break;
+ case ScanlineFormat::N24BitTcBgr:
+ pReadScan = pBitmapReadAcc->GetScanline( nY );
+ if( pAlphaReadAcc )
+ if( readAlpha( pAlphaReadAcc, nY, nWidth, data, nOff ) )
+ bIsAlpha = true;
+
+ for( nX = 0; nX < nWidth; nX++ )
+ {
+#ifdef OSL_BIGENDIAN
+ if( pAlphaReadAcc )
+ nAlpha = data[ nOff ];
+ else
+ nAlpha = data[ nOff ] = 255;
+ data[ nOff + 3 ] = premultiply_table[nAlpha][*pReadScan++];
+ data[ nOff + 2 ] = premultiply_table[nAlpha][*pReadScan++];
+ data[ nOff + 1 ] = premultiply_table[nAlpha][*pReadScan++];
+ nOff += 4;
+#else
+ if( pAlphaReadAcc )
+ nAlpha = data[ nOff + 3 ];
+ else
+ nAlpha = data[ nOff + 3 ] = 255;
+ data[ nOff++ ] = premultiply_table[nAlpha][*pReadScan++];
+ data[ nOff++ ] = premultiply_table[nAlpha][*pReadScan++];
+ data[ nOff++ ] = premultiply_table[nAlpha][*pReadScan++];
+ nOff++;
+#endif
+ }
+ break;
+ case ScanlineFormat::N24BitTcRgb:
+ pReadScan = pBitmapReadAcc->GetScanline( nY );
+ if( pAlphaReadAcc )
+ if( readAlpha( pAlphaReadAcc, nY, nWidth, data, nOff ) )
+ bIsAlpha = true;
+
+ for( nX = 0; nX < nWidth; nX++ )
+ {
+#ifdef OSL_BIGENDIAN
+ if( pAlphaReadAcc )
+ nAlpha = data[ nOff++ ];
+ else
+ nAlpha = data[ nOff++ ] = 255;
+ data[ nOff++ ] = premultiply_table[nAlpha][*pReadScan++];
+ data[ nOff++ ] = premultiply_table[nAlpha][*pReadScan++];
+ data[ nOff++ ] = premultiply_table[nAlpha][*pReadScan++];
+#else
+ if( pAlphaReadAcc )
+ nAlpha = data[ nOff + 3 ];
+ else
+ nAlpha = data[ nOff + 3 ] = 255;
+ data[ nOff++ ] = premultiply_table[nAlpha][pReadScan[ 2 ]];
+ data[ nOff++ ] = premultiply_table[nAlpha][pReadScan[ 1 ]];
+ data[ nOff++ ] = premultiply_table[nAlpha][pReadScan[ 0 ]];
+ pReadScan += 3;
+ nOff++;
+#endif
+ }
+ break;
+ case ScanlineFormat::N32BitTcBgra:
+ pReadScan = pBitmapReadAcc->GetScanline( nY );
+ if( pAlphaReadAcc )
+ if( readAlpha( pAlphaReadAcc, nY, nWidth, data, nOff ) )
+ bIsAlpha = true;
+
+ for( nX = 0; nX < nWidth; nX++ )
+ {
+#ifdef OSL_BIGENDIAN
+ if( pAlphaReadAcc )
+ nAlpha = data[ nOff++ ];
+ else
+ nAlpha = data[ nOff++ ] = 255;
+ data[ nOff++ ] = premultiply_table[nAlpha][pReadScan[ 2 ]];
+ data[ nOff++ ] = premultiply_table[nAlpha][pReadScan[ 1 ]];
+ data[ nOff++ ] = premultiply_table[nAlpha][pReadScan[ 0 ]];
+ pReadScan += 4;
+#else
+ if( pAlphaReadAcc )
+ nAlpha = data[ nOff + 3 ];
+ else
+ nAlpha = data[ nOff + 3 ] = 255;
+ data[ nOff++ ] = premultiply_table[nAlpha][*pReadScan++];
+ data[ nOff++ ] = premultiply_table[nAlpha][*pReadScan++];
+ data[ nOff++ ] = premultiply_table[nAlpha][*pReadScan++];
+ pReadScan++;
+ nOff++;
+#endif
+ }
+ break;
+ case ScanlineFormat::N32BitTcRgba:
+ pReadScan = pBitmapReadAcc->GetScanline( nY );
+ if( pAlphaReadAcc )
+ if( readAlpha( pAlphaReadAcc, nY, nWidth, data, nOff ) )
+ bIsAlpha = true;
+
+ for( nX = 0; nX < nWidth; nX++ )
+ {
+#ifdef OSL_BIGENDIAN
+ if( pAlphaReadAcc )
+ nAlpha = data[ nOff ++ ];
+ else
+ nAlpha = data[ nOff ++ ] = 255;
+ data[ nOff++ ] = premultiply_table[nAlpha][*pReadScan++];
+ data[ nOff++ ] = premultiply_table[nAlpha][*pReadScan++];
+ data[ nOff++ ] = premultiply_table[nAlpha][*pReadScan++];
+ pReadScan++;
+#else
+ if( pAlphaReadAcc )
+ nAlpha = data[ nOff + 3 ];
+ else
+ nAlpha = data[ nOff + 3 ] = 255;
+ data[ nOff++ ] = premultiply_table[nAlpha][pReadScan[ 2 ]];
+ data[ nOff++ ] = premultiply_table[nAlpha][pReadScan[ 1 ]];
+ data[ nOff++ ] = premultiply_table[nAlpha][pReadScan[ 0 ]];
+ pReadScan += 4;
+ nOff++;
+#endif
+ }
+ break;
+ default:
+ SAL_INFO( "canvas.cairo", "fallback to GetColor - slow, format: " << static_cast<int>(pBitmapReadAcc->GetScanlineFormat()) );
+
+ if( pAlphaReadAcc )
+ if( readAlpha( pAlphaReadAcc, nY, nWidth, data, nOff ) )
+ bIsAlpha = true;
+
+ for( nX = 0; nX < nWidth; nX++ )
+ {
+ aColor = pBitmapReadAcc->GetColor( nY, nX );
+
+ // cairo need premultiplied color values
+ // TODO(rodo) handle endianness
+#ifdef OSL_BIGENDIAN
+ if( pAlphaReadAcc )
+ nAlpha = data[ nOff++ ];
+ else
+ nAlpha = data[ nOff++ ] = 255;
+ data[ nOff++ ] = premultiply_table[nAlpha][aColor.GetRed()];
+ data[ nOff++ ] = premultiply_table[nAlpha][aColor.GetGreen()];
+ data[ nOff++ ] = premultiply_table[nAlpha][aColor.GetBlue()];
+#else
+ if( pAlphaReadAcc )
+ nAlpha = data[ nOff + 3 ];
+ else
+ nAlpha = data[ nOff + 3 ] = 255;
+ data[ nOff++ ] = premultiply_table[nAlpha][aColor.GetBlue()];
+ data[ nOff++ ] = premultiply_table[nAlpha][aColor.GetGreen()];
+ data[ nOff++ ] = premultiply_table[nAlpha][aColor.GetRed()];
+ nOff ++;
+#endif
+ }
+ }
+ }
+
+ ::Bitmap::ReleaseAccess( pBitmapReadAcc );
+ if( pAlphaReadAcc )
+ aAlpha.ReleaseAccess( pAlphaReadAcc );
+
+ bHasAlpha = bIsAlpha;
+
+}
+
+ uno::Sequence< sal_Int8 > CanvasExtractBitmapData(BitmapEx const & rBitmapEx, const geometry::IntegerRectangle2D& rect)
+ {
+ Bitmap aBitmap( rBitmapEx.GetBitmap() );
+ Bitmap aAlpha( rBitmapEx.GetAlpha().GetBitmap() );
+
+ Bitmap::ScopedReadAccess pReadAccess( aBitmap );
+ Bitmap::ScopedReadAccess pAlphaReadAccess( aAlpha.IsEmpty() ?
+ nullptr : aAlpha.AcquireReadAccess(),
+ aAlpha );
+
+ assert( pReadAccess );
+
+ // TODO(F1): Support more formats.
+ const Size aBmpSize( aBitmap.GetSizePixel() );
+
+ // for the time being, always return as BGRA
+ uno::Sequence< sal_Int8 > aRes( 4*aBmpSize.Width()*aBmpSize.Height() );
+ sal_Int8* pRes = aRes.getArray();
+
+ int nCurrPos(0);
+ for( long y=rect.Y1;
+ y<aBmpSize.Height() && y<rect.Y2;
+ ++y )
+ {
+ if( pAlphaReadAccess.get() != nullptr )
+ {
+ Scanline pScanlineReadAlpha = pAlphaReadAccess->GetScanline( y );
+ for( long x=rect.X1;
+ x<aBmpSize.Width() && x<rect.X2;
+ ++x )
+ {
+ pRes[ nCurrPos++ ] = pReadAccess->GetColor( y, x ).GetRed();
+ pRes[ nCurrPos++ ] = pReadAccess->GetColor( y, x ).GetGreen();
+ pRes[ nCurrPos++ ] = pReadAccess->GetColor( y, x ).GetBlue();
+ pRes[ nCurrPos++ ] = pAlphaReadAccess->GetIndexFromData( pScanlineReadAlpha, x );
+ }
+ }
+ else
+ {
+ for( long x=rect.X1;
+ x<aBmpSize.Width() && x<rect.X2;
+ ++x )
+ {
+ pRes[ nCurrPos++ ] = pReadAccess->GetColor( y, x ).GetRed();
+ pRes[ nCurrPos++ ] = pReadAccess->GetColor( y, x ).GetGreen();
+ pRes[ nCurrPos++ ] = pReadAccess->GetColor( y, x ).GetBlue();
+ pRes[ nCurrPos++ ] = sal_uInt8(255);
+ }
+ }
+ }
+ return aRes;
+ }
+
+ BitmapEx createHistorical8x8FromArray(std::array<sal_uInt8,64> const & pArray, Color aColorPix, Color aColorBack)
+ {
+ BitmapPalette aPalette(2);
+
+ aPalette[0] = BitmapColor(aColorBack);
+ aPalette[1] = BitmapColor(aColorPix);
+
+ Bitmap aBitmap(Size(8, 8), 1, &aPalette);
+ BitmapScopedWriteAccess pContent(aBitmap);
+
+ for(sal_uInt16 a(0); a < 8; a++)
+ {
+ for(sal_uInt16 b(0); b < 8; b++)
+ {
+ if(pArray[(a * 8) + b])
+ {
+ pContent->SetPixelIndex(a, b, 1);
+ }
+ else
+ {
+ pContent->SetPixelIndex(a, b, 0);
+ }
+ }
+ }
+
+ return BitmapEx(aBitmap);
+ }
+
+ bool isHistorical8x8(const BitmapEx& rBitmapEx, Color& o_rBack, Color& o_rFront)
+ {
+ bool bRet(false);
+
+ if(!rBitmapEx.IsTransparent())
+ {
+ Bitmap aBitmap(rBitmapEx.GetBitmap());
+
+ if(8 == aBitmap.GetSizePixel().Width() && 8 == aBitmap.GetSizePixel().Height())
+ {
+ if(2 == aBitmap.GetColorCount())
+ {
+ BitmapReadAccess* pRead = aBitmap.AcquireReadAccess();
+
+ if(pRead)
+ {
+ if(pRead->HasPalette() && 2 == pRead->GetPaletteEntryCount())
+ {
+ const BitmapPalette& rPalette = pRead->GetPalette();
+
+ // #i123564# background and foreground were exchanged; of course
+ // rPalette[0] is the background color
+ o_rFront = rPalette[1];
+ o_rBack = rPalette[0];
+
+ bRet = true;
+ }
+
+ Bitmap::ReleaseAccess(pRead);
+ }
+ }
+ }
+ }
+
+ return bRet;
+ }
+
+ sal_uInt8 unpremultiply(sal_uInt8 c, sal_uInt8 a)
+ {
+ return (a == 0) ? 0 : (c * 255 + a / 2) / a;
+ }
+
+ sal_uInt8 premultiply(sal_uInt8 c, sal_uInt8 a)
+ {
+ return (c * a + 127) / 255;
+ }
+
+ lookup_table get_unpremultiply_table()
+ {
+ static bool inited;
+ static sal_uInt8 unpremultiply_table[256][256];
+
+ if (!inited)
+ {
+ for (int a = 0; a < 256; ++a)
+ for (int c = 0; c < 256; ++c)
+ unpremultiply_table[a][c] = unpremultiply(c, a);
+ inited = true;
+ }
+
+ return unpremultiply_table;
+ }
+
+ lookup_table get_premultiply_table()
+ {
+ static bool inited;
+ static sal_uInt8 premultiply_table[256][256];
+
+ if (!inited)
+ {
+ for (int a = 0; a < 256; ++a)
+ for (int c = 0; c < 256; ++c)
+ premultiply_table[a][c] = premultiply(c, a);
+ inited = true;
+ }
+
+ return premultiply_table;
+ }
+
+bool convertBitmap32To24Plus8(BitmapEx const & rInput, BitmapEx & rResult)
+{
+ Bitmap aBitmap(rInput.GetBitmap());
+ if (aBitmap.GetBitCount() != 32)
+ return false;
+
+ Size aSize = aBitmap.GetSizePixel();
+ Bitmap aResultBitmap(aSize, 24);
+ AlphaMask aResultAlpha(aSize);
+ {
+ BitmapScopedWriteAccess pResultBitmapAccess(aResultBitmap);
+ AlphaScopedWriteAccess pResultAlphaAccess(aResultAlpha);
+
+ Bitmap::ScopedReadAccess pReadAccess(aBitmap);
+
+ for (long nY = 0; nY < aSize.Height(); ++nY)
+ {
+ Scanline aResultScan = pResultBitmapAccess->GetScanline(nY);
+ Scanline aResultScanAlpha = pResultAlphaAccess->GetScanline(nY);
+
+ Scanline aReadScan = pReadAccess->GetScanline(nY);
+
+ for (long nX = 0; nX < aSize.Width(); ++nX)
+ {
+ const BitmapColor aColor = pReadAccess->GetPixelFromData(aReadScan, nX);
+ BitmapColor aResultColor(aColor.GetRed(), aColor.GetGreen(), aColor.GetBlue());
+ BitmapColor aResultColorAlpha(aColor.GetAlpha(), aColor.GetAlpha(), aColor.GetAlpha());
+
+ pResultBitmapAccess->SetPixelOnData(aResultScan, nX, aResultColor);
+ pResultAlphaAccess->SetPixelOnData(aResultScanAlpha, nX, aResultColorAlpha);
+ }
+ }
+ }
+ if (rInput.IsTransparent())
+ rResult = BitmapEx(aResultBitmap, rInput.GetAlpha());
+ else
+ rResult = BitmapEx(aResultBitmap, aResultAlpha);
+ return true;
+}
+
+} // end vcl::bitmap
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/bitmap/Octree.cxx b/vcl/source/bitmap/Octree.cxx
new file mode 100644
index 000000000..44f911ef0
--- /dev/null
+++ b/vcl/source/bitmap/Octree.cxx
@@ -0,0 +1,288 @@
+/* -*- 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <vcl/bitmapaccess.hxx>
+
+#include <bitmap/Octree.hxx>
+
+namespace
+{
+constexpr size_t OCTREE_BITS = 5;
+constexpr size_t OCTREE_BITS_1 = 10;
+
+constexpr sal_uLong gnBits = 8 - OCTREE_BITS;
+
+} // end anonymous namespace
+
+Octree::Octree(const BitmapReadAccess& rReadAcc, sal_uLong nColors)
+ : mnLeafCount(0)
+ , mnLevel(0)
+ , mpReduce(OCTREE_BITS + 1, nullptr)
+ , mpColor(nullptr)
+ , mnPalIndex(0)
+{
+ const BitmapReadAccess* pAccess = &rReadAcc;
+ sal_uLong nMax(nColors);
+
+ if (!!*pAccess)
+ {
+ const long nWidth = pAccess->Width();
+ const long nHeight = pAccess->Height();
+
+ if (pAccess->HasPalette())
+ {
+ for (long nY = 0; nY < nHeight; nY++)
+ {
+ Scanline pScanline = pAccess->GetScanline(nY);
+ for (long nX = 0; nX < nWidth; nX++)
+ {
+ mpColor = &pAccess->GetPaletteColor(pAccess->GetIndexFromData(pScanline, nX));
+ mnLevel = 0;
+ add(pTree);
+
+ while (mnLeafCount > nMax)
+ reduce();
+ }
+ }
+ }
+ else
+ {
+ BitmapColor aColor;
+
+ mpColor = &aColor;
+
+ for (long nY = 0; nY < nHeight; nY++)
+ {
+ Scanline pScanline = pAccess->GetScanline(nY);
+ for (long nX = 0; nX < nWidth; nX++)
+ {
+ aColor = pAccess->GetPixelFromData(pScanline, nX);
+ mnLevel = 0;
+ add(pTree);
+
+ while (mnLeafCount > nMax)
+ reduce();
+ }
+ }
+ }
+ }
+}
+
+Octree::~Octree() {}
+
+void Octree::add(std::unique_ptr<OctreeNode>& rpNode)
+{
+ // possibly generate new nodes
+ if (!rpNode)
+ {
+ rpNode.reset(new OctreeNode);
+ rpNode->bLeaf = (OCTREE_BITS == mnLevel);
+
+ if (rpNode->bLeaf)
+ mnLeafCount++;
+ else
+ {
+ rpNode->pNext = mpReduce[mnLevel];
+ mpReduce[mnLevel] = rpNode.get();
+ }
+ }
+
+ if (rpNode->bLeaf)
+ {
+ rpNode->nCount++;
+ rpNode->nRed += mpColor->GetRed();
+ rpNode->nGreen += mpColor->GetGreen();
+ rpNode->nBlue += mpColor->GetBlue();
+ }
+ else
+ {
+ const sal_uLong nShift = 7 - mnLevel;
+ const sal_uInt8 cMask = 0x80 >> mnLevel;
+ const sal_uLong nIndex = (((mpColor->GetRed() & cMask) >> nShift) << 2)
+ | (((mpColor->GetGreen() & cMask) >> nShift) << 1)
+ | ((mpColor->GetBlue() & cMask) >> nShift);
+
+ mnLevel++;
+ add(rpNode->pChild[nIndex]);
+ }
+}
+
+void Octree::reduce()
+{
+ OctreeNode* pNode;
+ sal_uLong nRedSum = 0;
+ sal_uLong nGreenSum = 0;
+ sal_uLong nBlueSum = 0;
+ sal_uLong nChildren = 0;
+
+ sal_uLong nIndex = OCTREE_BITS - 1;
+ while (nIndex > 0 && !mpReduce[nIndex])
+ {
+ nIndex--;
+ }
+
+ pNode = mpReduce[nIndex];
+ mpReduce[nIndex] = pNode->pNext;
+
+ for (unsigned int i = 0; i < 8; i++)
+ {
+ if (pNode->pChild[i])
+ {
+ OctreeNode* pChild = pNode->pChild[i].get();
+
+ nRedSum += pChild->nRed;
+ nGreenSum += pChild->nGreen;
+ nBlueSum += pChild->nBlue;
+ pNode->nCount += pChild->nCount;
+
+ pNode->pChild[i].reset();
+ nChildren++;
+ }
+ }
+
+ pNode->bLeaf = true;
+ pNode->nRed = nRedSum;
+ pNode->nGreen = nGreenSum;
+ pNode->nBlue = nBlueSum;
+ mnLeafCount -= --nChildren;
+}
+
+void Octree::CreatePalette(OctreeNode* pNode)
+{
+ if (pNode->bLeaf)
+ {
+ pNode->nPalIndex = mnPalIndex;
+ maPalette[mnPalIndex++] = BitmapColor(sal_uInt8(double(pNode->nRed) / pNode->nCount),
+ sal_uInt8(double(pNode->nGreen) / pNode->nCount),
+ sal_uInt8(double(pNode->nBlue) / pNode->nCount));
+ }
+ else
+ {
+ for (auto const& i : pNode->pChild)
+ {
+ if (i)
+ {
+ CreatePalette(i.get());
+ }
+ }
+ }
+}
+
+void Octree::GetPalIndex(const OctreeNode* pNode)
+{
+ if (pNode->bLeaf)
+ mnPalIndex = pNode->nPalIndex;
+ else
+ {
+ const sal_uLong nShift = 7 - mnLevel;
+ const sal_uInt8 cMask = 0x80 >> mnLevel;
+ mnLevel++;
+ const sal_uLong nIndex = (((mpColor->GetRed() & cMask) >> nShift) << 2)
+ | (((mpColor->GetGreen() & cMask) >> nShift) << 1)
+ | ((mpColor->GetBlue() & cMask) >> nShift);
+
+ GetPalIndex(pNode->pChild[nIndex].get());
+ }
+}
+
+const BitmapPalette& Octree::GetPalette()
+{
+ maPalette.SetEntryCount(sal_uInt16(mnLeafCount));
+ mnPalIndex = 0;
+ CreatePalette(pTree.get());
+ return maPalette;
+}
+
+sal_uInt16 Octree::GetBestPaletteIndex(const BitmapColor& rColor)
+{
+ mpColor = &rColor;
+ mnPalIndex = 65535;
+ mnLevel = 0;
+ GetPalIndex(pTree.get());
+ return mnPalIndex;
+}
+
+constexpr int nColorMax = 1 << OCTREE_BITS;
+
+InverseColorMap::InverseColorMap(const BitmapPalette& rPal)
+{
+ const unsigned long xsqr = 1 << (gnBits << 1);
+ const unsigned long xsqr2 = xsqr << 1;
+ const int nColors = rPal.GetEntryCount();
+ const long x = 1 << gnBits;
+ const long x2 = x >> 1;
+ sal_uLong r, g, b;
+ long rxx, gxx, bxx;
+
+ ImplCreateBuffers();
+
+ for (int nIndex = 0; nIndex < nColors; nIndex++)
+ {
+ const BitmapColor& rColor = rPal[static_cast<sal_uInt16>(nIndex)];
+ const long cRed = rColor.GetRed();
+ const long cGreen = rColor.GetGreen();
+ const long cBlue = rColor.GetBlue();
+
+ long rdist = cRed - x2;
+ long gdist = cGreen - x2;
+ long bdist = cBlue - x2;
+ rdist = rdist * rdist + gdist * gdist + bdist * bdist;
+
+ const long crinc = (xsqr - (cRed << gnBits)) << 1;
+ const long cginc = (xsqr - (cGreen << gnBits)) << 1;
+ const long cbinc = (xsqr - (cBlue << gnBits)) << 1;
+
+ sal_uLong* cdp = reinterpret_cast<sal_uLong*>(mpBuffer.data());
+ sal_uInt8* crgbp = mpMap.data();
+
+ for (r = 0, rxx = crinc; r < nColorMax; rdist += rxx, r++, rxx += xsqr2)
+ {
+ for (g = 0, gdist = rdist, gxx = cginc; g < nColorMax; gdist += gxx, g++, gxx += xsqr2)
+ {
+ for (b = 0, bdist = gdist, bxx = cbinc; b < nColorMax;
+ bdist += bxx, b++, cdp++, crgbp++, bxx += xsqr2)
+ if (!nIndex || static_cast<long>(*cdp) > bdist)
+ {
+ *cdp = bdist;
+ *crgbp = static_cast<sal_uInt8>(nIndex);
+ }
+ }
+ }
+ }
+}
+
+InverseColorMap::~InverseColorMap() {}
+
+void InverseColorMap::ImplCreateBuffers()
+{
+ const sal_uLong nCount = nColorMax * nColorMax * nColorMax;
+ const sal_uLong nSize = nCount * sizeof(sal_uLong);
+
+ mpMap.resize(nCount, 0x00);
+ mpBuffer.resize(nSize, 0xff);
+}
+
+sal_uInt16 InverseColorMap::GetBestPaletteIndex(const BitmapColor& rColor)
+{
+ return mpMap[((static_cast<sal_uLong>(rColor.GetRed()) >> gnBits) << OCTREE_BITS_1)
+ | ((static_cast<sal_uLong>(rColor.GetGreen()) >> gnBits) << OCTREE_BITS)
+ | (static_cast<sal_uLong>(rColor.GetBlue()) >> gnBits)];
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/bitmap/bitmap.cxx b/vcl/source/bitmap/bitmap.cxx
new file mode 100644
index 000000000..af5a6f440
--- /dev/null
+++ b/vcl/source/bitmap/bitmap.cxx
@@ -0,0 +1,892 @@
+/* -*- 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <osl/diagnose.h>
+#include <tools/helpers.hxx>
+#include <vcl/bitmap.hxx>
+#include <vcl/bitmapaccess.hxx>
+#include <vcl/outdev.hxx>
+
+#include <svdata.hxx>
+#include <salinst.hxx>
+#include <salbmp.hxx>
+#include <bitmapwriteaccess.hxx>
+
+#include <algorithm>
+#include <memory>
+
+Bitmap::Bitmap()
+{
+}
+
+Bitmap::Bitmap(const Bitmap& rBitmap)
+ : mxSalBmp(rBitmap.mxSalBmp)
+ , maPrefMapMode(rBitmap.maPrefMapMode)
+ , maPrefSize(rBitmap.maPrefSize)
+{
+}
+
+Bitmap::Bitmap(std::shared_ptr<SalBitmap> const & pSalBitmap)
+ : mxSalBmp(pSalBitmap)
+ , maPrefMapMode(MapMode(MapUnit::MapPixel))
+ , maPrefSize(mxSalBmp->GetSize())
+{
+}
+
+Bitmap::Bitmap( const Size& rSizePixel, sal_uInt16 nBitCount, const BitmapPalette* pPal )
+{
+ if (rSizePixel.Width() && rSizePixel.Height())
+ {
+ BitmapPalette aPal;
+ BitmapPalette* pRealPal = nullptr;
+
+ if( nBitCount <= 8 )
+ {
+ if( !pPal )
+ {
+ if( 1 == nBitCount )
+ {
+ aPal.SetEntryCount( 2 );
+ aPal[ 0 ] = COL_BLACK;
+ aPal[ 1 ] = COL_WHITE;
+ }
+ else if( ( 4 == nBitCount ) || ( 8 == nBitCount ) )
+ {
+ aPal.SetEntryCount( 1 << nBitCount );
+ aPal[ 0 ] = COL_BLACK;
+ aPal[ 1 ] = COL_BLUE;
+ aPal[ 2 ] = COL_GREEN;
+ aPal[ 3 ] = COL_CYAN;
+ aPal[ 4 ] = COL_RED;
+ aPal[ 5 ] = COL_MAGENTA;
+ aPal[ 6 ] = COL_BROWN;
+ aPal[ 7 ] = COL_GRAY;
+ aPal[ 8 ] = COL_LIGHTGRAY;
+ aPal[ 9 ] = COL_LIGHTBLUE;
+ aPal[ 10 ] = COL_LIGHTGREEN;
+ aPal[ 11 ] = COL_LIGHTCYAN;
+ aPal[ 12 ] = COL_LIGHTRED;
+ aPal[ 13 ] = COL_LIGHTMAGENTA;
+ aPal[ 14 ] = COL_YELLOW;
+ aPal[ 15 ] = COL_WHITE;
+
+ // Create dither palette
+ if( 8 == nBitCount )
+ {
+ sal_uInt16 nActCol = 16;
+
+ for( sal_uInt16 nB = 0; nB < 256; nB += 51 )
+ for( sal_uInt16 nG = 0; nG < 256; nG += 51 )
+ for( sal_uInt16 nR = 0; nR < 256; nR += 51 )
+ aPal[ nActCol++ ] = BitmapColor( static_cast<sal_uInt8>(nR), static_cast<sal_uInt8>(nG), static_cast<sal_uInt8>(nB) );
+
+ // Set standard Office colors
+ aPal[ nActCol++ ] = BitmapColor( 0, 184, 255 );
+ }
+ }
+ }
+ else
+ pRealPal = const_cast<BitmapPalette*>(pPal);
+ }
+
+ mxSalBmp = ImplGetSVData()->mpDefInst->CreateSalBitmap();
+ mxSalBmp->Create( rSizePixel, nBitCount, pRealPal ? *pRealPal : aPal );
+ }
+}
+
+Bitmap::~Bitmap()
+{
+}
+
+const BitmapPalette& Bitmap::GetGreyPalette( int nEntries )
+{
+ // Create greyscale palette with 2, 4, 16 or 256 entries
+ switch (nEntries)
+ {
+ case 2:
+ {
+ static const BitmapPalette aGreyPalette2 = [] {
+ BitmapPalette aPalette(2);
+ aPalette[0] = BitmapColor(0, 0, 0);
+ aPalette[1] = BitmapColor(255, 255, 255);
+ return aPalette;
+ }();
+
+ return aGreyPalette2;
+ }
+ case 4:
+ {
+ static const BitmapPalette aGreyPalette4 = [] {
+ BitmapPalette aPalette(4);
+ aPalette[0] = BitmapColor(0, 0, 0);
+ aPalette[1] = BitmapColor(85, 85, 85);
+ aPalette[2] = BitmapColor(170, 170, 170);
+ aPalette[3] = BitmapColor(255, 255, 255);
+ return aPalette;
+ }();
+
+ return aGreyPalette4;
+ }
+ case 16:
+ {
+ static const BitmapPalette aGreyPalette16 = [] {
+ sal_uInt8 cGrey = 0;
+ sal_uInt8 const cGreyInc = 17;
+
+ BitmapPalette aPalette(16);
+
+ for (sal_uInt16 i = 0; i < 16; ++i, cGrey += cGreyInc)
+ aPalette[i] = BitmapColor(cGrey, cGrey, cGrey);
+
+ return aPalette;
+ }();
+
+ return aGreyPalette16;
+ }
+ case 256:
+ {
+ static const BitmapPalette aGreyPalette256 = [] {
+ BitmapPalette aPalette(256);
+
+ for (sal_uInt16 i = 0; i < 256; ++i)
+ aPalette[i] = BitmapColor(static_cast<sal_uInt8>(i), static_cast<sal_uInt8>(i),
+ static_cast<sal_uInt8>(i));
+
+ return aPalette;
+ }();
+
+ return aGreyPalette256;
+ }
+ }
+ OSL_FAIL("Bitmap::GetGreyPalette: invalid entry count (2/4/16/256 allowed)");
+ return GetGreyPalette(2);
+}
+
+bool BitmapPalette::IsGreyPaletteAny() const
+{
+ const int nEntryCount = GetEntryCount();
+ if( !nEntryCount ) // NOTE: an empty palette means 1:1 mapping
+ return true;
+ // See above: only certain entry values will result in a valid call to GetGreyPalette
+ if( nEntryCount == 2 || nEntryCount == 4 || nEntryCount == 16 || nEntryCount == 256 )
+ {
+ const BitmapPalette& rGreyPalette = Bitmap::GetGreyPalette( nEntryCount );
+ if( rGreyPalette == *this )
+ return true;
+ }
+
+ bool bRet = false;
+ // TODO: is it worth to compare the entries for the general case?
+ if (nEntryCount == 2)
+ {
+ const BitmapColor& rCol0(maBitmapColor[0]);
+ const BitmapColor& rCol1(maBitmapColor[1]);
+ bRet = rCol0.GetRed() == rCol0.GetGreen() && rCol0.GetRed() == rCol0.GetBlue() &&
+ rCol1.GetRed() == rCol1.GetGreen() && rCol1.GetRed() == rCol1.GetBlue();
+ }
+ return bRet;
+}
+
+bool BitmapPalette::IsGreyPalette8Bit() const
+{
+ const int nEntryCount = GetEntryCount();
+ if( !nEntryCount ) // NOTE: an empty palette means 1:1 mapping
+ return true;
+ if( nEntryCount != 256 )
+ return false;
+ for (sal_uInt16 i = 0; i < 256; ++i)
+ {
+ if( maBitmapColor[i] != BitmapColor(i, i, i))
+ return false;
+ }
+ return true;
+}
+
+Bitmap& Bitmap::operator=( const Bitmap& rBitmap )
+{
+ if (this == &rBitmap)
+ return *this;
+
+ maPrefSize = rBitmap.maPrefSize;
+ maPrefMapMode = rBitmap.maPrefMapMode;
+ mxSalBmp = rBitmap.mxSalBmp;
+
+ return *this;
+}
+
+Bitmap& Bitmap::operator=( Bitmap&& rBitmap ) noexcept
+{
+ maPrefSize = std::move(rBitmap.maPrefSize);
+ maPrefMapMode = std::move(rBitmap.maPrefMapMode);
+ mxSalBmp = std::move(rBitmap.mxSalBmp);
+
+ return *this;
+}
+
+bool Bitmap::operator==( const Bitmap& rBmp ) const
+{
+ if (rBmp.mxSalBmp == mxSalBmp) // Includes both are nullptr
+ return true;
+ if (!rBmp.mxSalBmp || !mxSalBmp)
+ return false;
+ if (rBmp.mxSalBmp->GetSize() != mxSalBmp->GetSize() ||
+ rBmp.mxSalBmp->GetBitCount() != mxSalBmp->GetBitCount())
+ return false;
+ BitmapChecksum aChecksum1, aChecksum2;
+ rBmp.mxSalBmp->GetChecksum(aChecksum1);
+ mxSalBmp->GetChecksum(aChecksum2);
+ // If the bitmaps can't calculate a checksum, best to regard them as different.
+ if (aChecksum1 == 0 || aChecksum2 == 0)
+ return false;
+ return aChecksum1 == aChecksum2;
+}
+
+void Bitmap::SetEmpty()
+{
+ maPrefMapMode = MapMode();
+ maPrefSize = Size();
+ mxSalBmp.reset();
+}
+
+Size Bitmap::GetSizePixel() const
+{
+ return( mxSalBmp ? mxSalBmp->GetSize() : Size() );
+}
+
+sal_uInt16 Bitmap::GetBitCount() const
+{
+ if (!mxSalBmp)
+ return 0;
+
+ sal_uInt16 nBitCount = mxSalBmp->GetBitCount();
+ if (nBitCount <= 1)
+ return 1;
+ if (nBitCount <= 4)
+ return 4;
+ if (nBitCount <= 8)
+ return 8;
+ if (nBitCount <= 24)
+ return 24;
+ if (nBitCount <= 32)
+ return 32;
+ return 0;
+}
+
+bool Bitmap::HasGreyPaletteAny() const
+{
+ const sal_uInt16 nBitCount = GetBitCount();
+ bool bRet = nBitCount == 1;
+
+ ScopedInfoAccess pIAcc(const_cast<Bitmap&>(*this));
+
+ if( pIAcc )
+ {
+ bRet = pIAcc->HasPalette() && pIAcc->GetPalette().IsGreyPaletteAny();
+ }
+
+ return bRet;
+}
+
+bool Bitmap::HasGreyPalette8Bit() const
+{
+ bool bRet = false;
+ ScopedInfoAccess pIAcc(const_cast<Bitmap&>(*this));
+
+ if( pIAcc )
+ {
+ bRet = pIAcc->HasPalette() && pIAcc->GetPalette().IsGreyPalette8Bit();
+ }
+
+ return bRet;
+}
+
+BitmapChecksum Bitmap::GetChecksum() const
+{
+ BitmapChecksum nRet = 0;
+
+ if( mxSalBmp )
+ {
+ mxSalBmp->GetChecksum(nRet);
+
+ if (!nRet)
+ {
+ // nRet == 0 => probably, we were not able to acquire
+ // the buffer in SalBitmap::updateChecksum;
+ // so, we need to update the imp bitmap for this bitmap instance
+ // as we do in BitmapInfoAccess::ImplCreate
+ std::shared_ptr<SalBitmap> xNewImpBmp(ImplGetSVData()->mpDefInst->CreateSalBitmap());
+ if (xNewImpBmp->Create(*mxSalBmp, GetBitCount()))
+ {
+ Bitmap* pThis = const_cast<Bitmap*>(this);
+ pThis->mxSalBmp = xNewImpBmp;
+ mxSalBmp->GetChecksum(nRet);
+ }
+ }
+ }
+
+ return nRet;
+}
+
+void Bitmap::ImplMakeUnique()
+{
+ if (mxSalBmp && mxSalBmp.use_count() > 1)
+ {
+ std::shared_ptr<SalBitmap> xOldImpBmp = mxSalBmp;
+ mxSalBmp = ImplGetSVData()->mpDefInst->CreateSalBitmap();
+ (void)mxSalBmp->Create(*xOldImpBmp);
+ }
+}
+
+void Bitmap::ReassignWithSize(const Bitmap& rBitmap)
+{
+ const Size aOldSizePix(GetSizePixel());
+ const Size aNewSizePix(rBitmap.GetSizePixel());
+ const MapMode aOldMapMode(maPrefMapMode);
+ Size aNewPrefSize;
+
+ if ((aOldSizePix != aNewSizePix) && aOldSizePix.Width() && aOldSizePix.Height())
+ {
+ aNewPrefSize.setWidth(FRound(maPrefSize.Width() * aNewSizePix.Width() / aOldSizePix.Width()));
+ aNewPrefSize.setHeight(FRound(maPrefSize.Height() * aNewSizePix.Height() / aOldSizePix.Height()));
+ }
+ else
+ {
+ aNewPrefSize = maPrefSize;
+ }
+
+ *this = rBitmap;
+
+ maPrefSize = aNewPrefSize;
+ maPrefMapMode = aOldMapMode;
+}
+
+
+void Bitmap::ImplSetSalBitmap(const std::shared_ptr<SalBitmap>& xImpBmp)
+{
+ mxSalBmp = xImpBmp;
+}
+
+BitmapInfoAccess* Bitmap::AcquireInfoAccess()
+{
+ std::unique_ptr<BitmapInfoAccess> pInfoAccess(new BitmapInfoAccess( *this ));
+
+ if( !*pInfoAccess )
+ {
+ return nullptr;
+ }
+
+ return pInfoAccess.release();
+}
+
+BitmapReadAccess* Bitmap::AcquireReadAccess()
+{
+ std::unique_ptr<BitmapReadAccess> pReadAccess(new BitmapReadAccess( *this ));
+
+ if( !*pReadAccess )
+ {
+ return nullptr;
+ }
+
+ return pReadAccess.release();
+}
+
+BitmapWriteAccess* Bitmap::AcquireWriteAccess()
+{
+ std::unique_ptr<BitmapWriteAccess> pWriteAccess(new BitmapWriteAccess( *this ));
+
+ if( !*pWriteAccess )
+ {
+ return nullptr;
+ }
+
+ return pWriteAccess.release();
+}
+
+void Bitmap::ReleaseAccess( BitmapInfoAccess* pBitmapAccess )
+{
+ delete pBitmapAccess;
+}
+
+bool Bitmap::Crop( const tools::Rectangle& rRectPixel )
+{
+ const Size aSizePix( GetSizePixel() );
+ tools::Rectangle aRect( rRectPixel );
+ bool bRet = false;
+
+ aRect.Intersection( tools::Rectangle( Point(), aSizePix ) );
+
+ if( !aRect.IsEmpty() && aSizePix != aRect.GetSize())
+ {
+ ScopedReadAccess pReadAcc(*this);
+
+ if( pReadAcc )
+ {
+ const tools::Rectangle aNewRect( Point(), aRect.GetSize() );
+ Bitmap aNewBmp( aNewRect.GetSize(), GetBitCount(), &pReadAcc->GetPalette() );
+ BitmapScopedWriteAccess pWriteAcc(aNewBmp);
+
+ if( pWriteAcc )
+ {
+ const long nOldX = aRect.Left();
+ const long nOldY = aRect.Top();
+ const long nNewWidth = aNewRect.GetWidth();
+ const long nNewHeight = aNewRect.GetHeight();
+
+ for( long nY = 0, nY2 = nOldY; nY < nNewHeight; nY++, nY2++ )
+ {
+ Scanline pScanline = pWriteAcc->GetScanline(nY);
+ Scanline pScanlineRead = pReadAcc->GetScanline(nY2);
+ for( long nX = 0, nX2 = nOldX; nX < nNewWidth; nX++, nX2++ )
+ pWriteAcc->SetPixelOnData( pScanline, nX, pReadAcc->GetPixelFromData( pScanlineRead, nX2 ) );
+ }
+
+ pWriteAcc.reset();
+ bRet = true;
+ }
+
+ pReadAcc.reset();
+
+ if( bRet )
+ ReassignWithSize( aNewBmp );
+ }
+ }
+
+ return bRet;
+};
+
+bool Bitmap::CopyPixel( const tools::Rectangle& rRectDst,
+ const tools::Rectangle& rRectSrc, const Bitmap* pBmpSrc )
+{
+ const Size aSizePix( GetSizePixel() );
+ tools::Rectangle aRectDst( rRectDst );
+ bool bRet = false;
+
+ aRectDst.Intersection( tools::Rectangle( Point(), aSizePix ) );
+
+ if( !aRectDst.IsEmpty() )
+ {
+ if( pBmpSrc && ( pBmpSrc->mxSalBmp != mxSalBmp ) )
+ {
+ Bitmap* pSrc = const_cast<Bitmap*>(pBmpSrc);
+ const Size aCopySizePix( pSrc->GetSizePixel() );
+ tools::Rectangle aRectSrc( rRectSrc );
+ const sal_uInt16 nSrcBitCount = pBmpSrc->GetBitCount();
+ const sal_uInt16 nDstBitCount = GetBitCount();
+
+ if( nSrcBitCount > nDstBitCount )
+ {
+ int nNextIndex = 0;
+
+ if (nSrcBitCount == 24)
+ Convert( BmpConversion::N24Bit );
+ else if (nSrcBitCount == 8)
+ {
+ Convert( BmpConversion::N8BitColors );
+ nNextIndex = 16;
+ }
+ else if (nSrcBitCount == 4)
+ {
+ Convert( BmpConversion::N4BitColors );
+ nNextIndex = 2;
+ }
+
+ if( nNextIndex )
+ {
+ ScopedReadAccess pSrcAcc(*pSrc);
+ BitmapScopedWriteAccess pDstAcc(*this);
+
+ if( pSrcAcc && pDstAcc )
+ {
+ const int nSrcCount = pDstAcc->GetPaletteEntryCount();
+ const int nDstCount = 1 << nDstBitCount;
+
+ for (int i = 0; ( i < nSrcCount ) && ( nNextIndex < nSrcCount ); ++i)
+ {
+ const BitmapColor& rSrcCol = pSrcAcc->GetPaletteColor( static_cast<sal_uInt16>(i) );
+
+ bool bFound = false;
+
+ for (int j = 0; j < nDstCount; ++j)
+ {
+ if( rSrcCol == pDstAcc->GetPaletteColor( static_cast<sal_uInt16>(j) ) )
+ {
+ bFound = true;
+ break;
+ }
+ }
+
+ if( !bFound )
+ pDstAcc->SetPaletteColor( static_cast<sal_uInt16>(nNextIndex++), rSrcCol );
+ }
+ }
+ }
+ }
+
+ aRectSrc.Intersection( tools::Rectangle( Point(), aCopySizePix ) );
+
+ if( !aRectSrc.IsEmpty() )
+ {
+ ScopedReadAccess pReadAcc(*pSrc);
+
+ if( pReadAcc )
+ {
+ BitmapScopedWriteAccess pWriteAcc(*this);
+
+ if( pWriteAcc )
+ {
+ const long nWidth = std::min( aRectSrc.GetWidth(), aRectDst.GetWidth() );
+ const long nHeight = std::min( aRectSrc.GetHeight(), aRectDst.GetHeight() );
+ const long nSrcEndX = aRectSrc.Left() + nWidth;
+ const long nSrcEndY = aRectSrc.Top() + nHeight;
+ long nDstY = aRectDst.Top();
+
+ if( pReadAcc->HasPalette() && pWriteAcc->HasPalette() )
+ {
+ const sal_uInt16 nCount = pReadAcc->GetPaletteEntryCount();
+ std::unique_ptr<sal_uInt8[]> pMap(new sal_uInt8[ nCount ]);
+
+ // Create index map for the color table, as the bitmap should be copied
+ // retaining it's color information relatively well
+ for( sal_uInt16 i = 0; i < nCount; i++ )
+ pMap[ i ] = static_cast<sal_uInt8>(pWriteAcc->GetBestPaletteIndex( pReadAcc->GetPaletteColor( i ) ));
+
+ for( long nSrcY = aRectSrc.Top(); nSrcY < nSrcEndY; nSrcY++, nDstY++ )
+ {
+ Scanline pScanline = pWriteAcc->GetScanline(nDstY);
+ Scanline pScanlineRead = pReadAcc->GetScanline(nSrcY);
+ for( long nSrcX = aRectSrc.Left(), nDstX = aRectDst.Left(); nSrcX < nSrcEndX; nSrcX++, nDstX++ )
+ pWriteAcc->SetPixelOnData( pScanline, nDstX, BitmapColor( pMap[ pReadAcc->GetIndexFromData( pScanlineRead, nSrcX ) ] ));
+ }
+ }
+ else if( pReadAcc->HasPalette() )
+ {
+ for( long nSrcY = aRectSrc.Top(); nSrcY < nSrcEndY; nSrcY++, nDstY++ )
+ {
+ Scanline pScanline = pWriteAcc->GetScanline(nDstY);
+ Scanline pScanlineRead = pReadAcc->GetScanline(nSrcY);
+ for( long nSrcX = aRectSrc.Left(), nDstX = aRectDst.Left(); nSrcX < nSrcEndX; nSrcX++, nDstX++ )
+ pWriteAcc->SetPixelOnData( pScanline, nDstX, pReadAcc->GetPaletteColor( pReadAcc->GetIndexFromData( pScanlineRead, nSrcX ) ) );
+ }
+ }
+ else
+ for( long nSrcY = aRectSrc.Top(); nSrcY < nSrcEndY; nSrcY++, nDstY++ )
+ {
+ Scanline pScanline = pWriteAcc->GetScanline(nDstY);
+ Scanline pScanlineRead = pReadAcc->GetScanline(nSrcY);
+ for( long nSrcX = aRectSrc.Left(), nDstX = aRectDst.Left(); nSrcX < nSrcEndX; nSrcX++, nDstX++ )
+ pWriteAcc->SetPixelOnData( pScanline, nDstX, pReadAcc->GetPixelFromData( pScanlineRead, nSrcX ) );
+ }
+
+ pWriteAcc.reset();
+ bRet = ( nWidth > 0 ) && ( nHeight > 0 );
+ }
+
+ pReadAcc.reset();
+ }
+ }
+ }
+ else
+ {
+ tools::Rectangle aRectSrc( rRectSrc );
+
+ aRectSrc.Intersection( tools::Rectangle( Point(), aSizePix ) );
+
+ if( !aRectSrc.IsEmpty() && ( aRectSrc != aRectDst ) )
+ {
+ BitmapScopedWriteAccess pWriteAcc(*this);
+
+ if( pWriteAcc )
+ {
+ const long nWidth = std::min( aRectSrc.GetWidth(), aRectDst.GetWidth() );
+ const long nHeight = std::min( aRectSrc.GetHeight(), aRectDst.GetHeight() );
+ const long nSrcX = aRectSrc.Left();
+ const long nSrcY = aRectSrc.Top();
+ const long nSrcEndX1 = nSrcX + nWidth - 1;
+ const long nSrcEndY1 = nSrcY + nHeight - 1;
+ const long nDstX = aRectDst.Left();
+ const long nDstY = aRectDst.Top();
+ const long nDstEndX1 = nDstX + nWidth - 1;
+ const long nDstEndY1 = nDstY + nHeight - 1;
+
+ if( ( nDstX <= nSrcX ) && ( nDstY <= nSrcY ) )
+ {
+ for( long nY = nSrcY, nYN = nDstY; nY <= nSrcEndY1; nY++, nYN++ )
+ {
+ Scanline pScanline = pWriteAcc->GetScanline(nYN);
+ Scanline pScanlineSrc = pWriteAcc->GetScanline(nY);
+ for( long nX = nSrcX, nXN = nDstX; nX <= nSrcEndX1; nX++, nXN++ )
+ pWriteAcc->SetPixelOnData( pScanline, nXN, pWriteAcc->GetPixelFromData( pScanlineSrc, nX ) );
+ }
+ }
+ else if( ( nDstX <= nSrcX ) && ( nDstY >= nSrcY ) )
+ {
+ for( long nY = nSrcEndY1, nYN = nDstEndY1; nY >= nSrcY; nY--, nYN-- )
+ {
+ Scanline pScanline = pWriteAcc->GetScanline(nYN);
+ Scanline pScanlineSrc = pWriteAcc->GetScanline(nY);
+ for( long nX = nSrcX, nXN = nDstX; nX <= nSrcEndX1; nX++, nXN++ )
+ pWriteAcc->SetPixelOnData( pScanline, nXN, pWriteAcc->GetPixelFromData( pScanlineSrc, nX ) );
+ }
+ }
+ else if( ( nDstX >= nSrcX ) && ( nDstY <= nSrcY ) )
+ {
+ for( long nY = nSrcY, nYN = nDstY; nY <= nSrcEndY1; nY++, nYN++ )
+ {
+ Scanline pScanline = pWriteAcc->GetScanline(nYN);
+ Scanline pScanlineSrc = pWriteAcc->GetScanline(nY);
+ for( long nX = nSrcEndX1, nXN = nDstEndX1; nX >= nSrcX; nX--, nXN-- )
+ pWriteAcc->SetPixelOnData( pScanline, nXN, pWriteAcc->GetPixelFromData( pScanlineSrc, nX ) );
+ }
+ }
+ else
+ {
+ for( long nY = nSrcEndY1, nYN = nDstEndY1; nY >= nSrcY; nY--, nYN-- )
+ {
+ Scanline pScanline = pWriteAcc->GetScanline(nYN);
+ Scanline pScanlineSrc = pWriteAcc->GetScanline(nY);
+ for( long nX = nSrcEndX1, nXN = nDstEndX1; nX >= nSrcX; nX--, nXN-- )
+ pWriteAcc->SetPixelOnData( pScanline, nXN, pWriteAcc->GetPixelFromData( pScanlineSrc, nX ) );
+ }
+ }
+
+ pWriteAcc.reset();
+ bRet = true;
+ }
+ }
+ }
+ }
+
+ return bRet;
+}
+
+bool Bitmap::CopyPixel_AlphaOptimized( const tools::Rectangle& rRectDst, const tools::Rectangle& rRectSrc,
+ const Bitmap* pBmpSrc )
+{
+ // Note: this code is copied from Bitmap::CopyPixel but avoids any palette lookups
+ // This optimization is possible because the palettes of AlphaMasks are always identical (8bit GreyPalette, see ctor)
+ const Size aSizePix( GetSizePixel() );
+ tools::Rectangle aRectDst( rRectDst );
+ bool bRet = false;
+
+ aRectDst.Intersection( tools::Rectangle( Point(), aSizePix ) );
+
+ if( !aRectDst.IsEmpty() )
+ {
+ if( pBmpSrc && ( pBmpSrc->mxSalBmp != mxSalBmp ) )
+ {
+ Bitmap* pSrc = const_cast<Bitmap*>(pBmpSrc);
+ const Size aCopySizePix( pSrc->GetSizePixel() );
+ tools::Rectangle aRectSrc( rRectSrc );
+
+ aRectSrc.Intersection( tools::Rectangle( Point(), aCopySizePix ) );
+
+ if( !aRectSrc.IsEmpty() )
+ {
+ ScopedReadAccess pReadAcc(*pSrc);
+
+ if( pReadAcc )
+ {
+ BitmapScopedWriteAccess pWriteAcc(*this);
+
+ if( pWriteAcc )
+ {
+ const long nWidth = std::min( aRectSrc.GetWidth(), aRectDst.GetWidth() );
+ const long nHeight = std::min( aRectSrc.GetHeight(), aRectDst.GetHeight() );
+ const long nSrcEndX = aRectSrc.Left() + nWidth;
+ const long nSrcEndY = aRectSrc.Top() + nHeight;
+ long nDstY = aRectDst.Top();
+
+ for( long nSrcY = aRectSrc.Top(); nSrcY < nSrcEndY; nSrcY++, nDstY++)
+ {
+ Scanline pScanline = pWriteAcc->GetScanline(nDstY);
+ Scanline pScanlineRead = pReadAcc->GetScanline(nSrcY);
+ for( long nSrcX = aRectSrc.Left(), nDstX = aRectDst.Left(); nSrcX < nSrcEndX; nSrcX++, nDstX++ )
+ pWriteAcc->SetPixelOnData( pScanline, nDstX, pReadAcc->GetPixelFromData( pScanlineRead, nSrcX ) );
+ }
+
+ pWriteAcc.reset();
+ bRet = ( nWidth > 0 ) && ( nHeight > 0 );
+ }
+
+ pReadAcc.reset();
+ }
+ }
+ }
+ else
+ {
+ tools::Rectangle aRectSrc( rRectSrc );
+
+ aRectSrc.Intersection( tools::Rectangle( Point(), aSizePix ) );
+
+ if( !aRectSrc.IsEmpty() && ( aRectSrc != aRectDst ) )
+ {
+ BitmapScopedWriteAccess pWriteAcc(*this);
+
+ if( pWriteAcc )
+ {
+ const long nWidth = std::min( aRectSrc.GetWidth(), aRectDst.GetWidth() );
+ const long nHeight = std::min( aRectSrc.GetHeight(), aRectDst.GetHeight() );
+ const long nSrcX = aRectSrc.Left();
+ const long nSrcY = aRectSrc.Top();
+ const long nSrcEndX1 = nSrcX + nWidth - 1;
+ const long nSrcEndY1 = nSrcY + nHeight - 1;
+ const long nDstX = aRectDst.Left();
+ const long nDstY = aRectDst.Top();
+ const long nDstEndX1 = nDstX + nWidth - 1;
+ const long nDstEndY1 = nDstY + nHeight - 1;
+
+ if( ( nDstX <= nSrcX ) && ( nDstY <= nSrcY ) )
+ {
+ for( long nY = nSrcY, nYN = nDstY; nY <= nSrcEndY1; nY++, nYN++ )
+ {
+ Scanline pScanline = pWriteAcc->GetScanline(nYN);
+ Scanline pScanlineSrc = pWriteAcc->GetScanline(nY);
+ for( long nX = nSrcX, nXN = nDstX; nX <= nSrcEndX1; nX++, nXN++ )
+ pWriteAcc->SetPixelOnData( pScanline, nXN, pWriteAcc->GetPixelFromData( pScanlineSrc, nX ) );
+ }
+ }
+ else if( ( nDstX <= nSrcX ) && ( nDstY >= nSrcY ) )
+ {
+ for( long nY = nSrcEndY1, nYN = nDstEndY1; nY >= nSrcY; nY--, nYN-- )
+ {
+ Scanline pScanline = pWriteAcc->GetScanline(nYN);
+ Scanline pScanlineSrc = pWriteAcc->GetScanline(nY);
+ for( long nX = nSrcX, nXN = nDstX; nX <= nSrcEndX1; nX++, nXN++ )
+ pWriteAcc->SetPixelOnData( pScanline, nXN, pWriteAcc->GetPixelFromData( pScanlineSrc, nX ) );
+ }
+ }
+ else if( ( nDstX >= nSrcX ) && ( nDstY <= nSrcY ) )
+ {
+ for( long nY = nSrcY, nYN = nDstY; nY <= nSrcEndY1; nY++, nYN++ )
+ {
+ Scanline pScanline = pWriteAcc->GetScanline(nYN);
+ Scanline pScanlineSrc = pWriteAcc->GetScanline(nY);
+ for( long nX = nSrcEndX1, nXN = nDstEndX1; nX >= nSrcX; nX--, nXN-- )
+ pWriteAcc->SetPixelOnData( pScanline, nXN, pWriteAcc->GetPixelFromData( pScanlineSrc, nX ) );
+ }
+ }
+ else
+ {
+ for( long nY = nSrcEndY1, nYN = nDstEndY1; nY >= nSrcY; nY--, nYN-- )
+ {
+ Scanline pScanline = pWriteAcc->GetScanline(nYN);
+ Scanline pScanlineSrc = pWriteAcc->GetScanline(nY);
+ for( long nX = nSrcEndX1, nXN = nDstEndX1; nX >= nSrcX; nX--, nXN-- )
+ pWriteAcc->SetPixelOnData( pScanline, nXN, pWriteAcc->GetPixelFromData( pScanlineSrc, nX ) );
+ }
+ }
+
+ pWriteAcc.reset();
+ bRet = true;
+ }
+ }
+ }
+ }
+
+ return bRet;
+
+}
+
+bool Bitmap::Expand( sal_uLong nDX, sal_uLong nDY, const Color* pInitColor )
+{
+ bool bRet = false;
+
+ if( nDX || nDY )
+ {
+ const Size aSizePixel( GetSizePixel() );
+ const long nWidth = aSizePixel.Width();
+ const long nHeight = aSizePixel.Height();
+ const Size aNewSize( nWidth + nDX, nHeight + nDY );
+ ScopedReadAccess pReadAcc(*this);
+
+ if( pReadAcc )
+ {
+ BitmapPalette aBmpPal( pReadAcc->GetPalette() );
+ Bitmap aNewBmp( aNewSize, GetBitCount(), &aBmpPal );
+ BitmapScopedWriteAccess pWriteAcc(aNewBmp);
+
+ if( pWriteAcc )
+ {
+ BitmapColor aColor;
+ const long nNewX = nWidth;
+ const long nNewY = nHeight;
+ const long nNewWidth = pWriteAcc->Width();
+ const long nNewHeight = pWriteAcc->Height();
+ long nX;
+ long nY;
+
+ if( pInitColor )
+ aColor = pWriteAcc->GetBestMatchingColor( *pInitColor );
+
+ for( nY = 0; nY < nHeight; nY++ )
+ {
+ pWriteAcc->CopyScanline( nY, *pReadAcc );
+
+ if( pInitColor && nDX )
+ {
+ Scanline pScanline = pWriteAcc->GetScanline(nY);
+ for( nX = nNewX; nX < nNewWidth; nX++ )
+ pWriteAcc->SetPixelOnData( pScanline, nX, aColor );
+ }
+ }
+
+ if( pInitColor && nDY )
+ for( nY = nNewY; nY < nNewHeight; nY++ )
+ {
+ Scanline pScanline = pWriteAcc->GetScanline(nY);
+ for( nX = 0; nX < nNewWidth; nX++ )
+ pWriteAcc->SetPixelOnData( pScanline, nX, aColor );
+ }
+
+ pWriteAcc.reset();
+ bRet = true;
+ }
+
+ pReadAcc.reset();
+
+ if (bRet)
+ ReassignWithSize(aNewBmp);
+ }
+ }
+
+ return bRet;
+}
+
+Bitmap Bitmap::CreateDisplayBitmap( OutputDevice* pDisplay ) const
+{
+ Bitmap aDispBmp( *this );
+
+ SalGraphics* pDispGraphics = pDisplay->GetGraphics();
+
+ if( mxSalBmp && pDispGraphics )
+ {
+ std::shared_ptr<SalBitmap> xImpDispBmp(ImplGetSVData()->mpDefInst->CreateSalBitmap());
+ if (xImpDispBmp->Create(*mxSalBmp, pDispGraphics))
+ aDispBmp.ImplSetSalBitmap(xImpDispBmp);
+ }
+
+ return aDispBmp;
+}
+
+bool Bitmap::GetSystemData( BitmapSystemData& rData ) const
+{
+ return mxSalBmp && mxSalBmp->GetSystemData(rData);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/bitmap/bitmapfilter.cxx b/vcl/source/bitmap/bitmapfilter.cxx
new file mode 100644
index 000000000..58a01f1fb
--- /dev/null
+++ b/vcl/source/bitmap/bitmapfilter.cxx
@@ -0,0 +1,58 @@
+/* -*- 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/BitmapFilter.hxx>
+#include <vcl/animate/Animation.hxx>
+
+#include <sal/log.hxx>
+
+BitmapFilter::BitmapFilter() {}
+
+BitmapFilter::~BitmapFilter() {}
+
+bool BitmapFilter::Filter(BitmapEx& rBmpEx, BitmapFilter const & rFilter)
+{
+ BitmapEx aTmpBmpEx(rFilter.execute(rBmpEx));
+
+ if (aTmpBmpEx.IsEmpty())
+ {
+ SAL_WARN("vcl.gdi", "Bitmap filter failed");
+ return false;
+ }
+
+ rBmpEx = aTmpBmpEx;
+ return true;
+}
+
+bool BitmapFilter::Filter(Animation& rAnimation, BitmapFilter const & rFilter)
+{
+ SAL_WARN_IF(rAnimation.IsInAnimation(), "vcl", "Animation modified while it is animated");
+
+ bool bRet = false;
+
+ if (!rAnimation.IsInAnimation() && !rAnimation.Count())
+ {
+ bRet = true;
+
+ std::vector<std::unique_ptr<AnimationBitmap>>& aList = rAnimation.GetAnimationFrames();
+ for (size_t i = 0, n = aList.size(); (i < n) && bRet; ++i)
+ {
+ bRet = BitmapFilter::Filter(aList[i]->maBitmapEx, rFilter);
+ }
+
+ BitmapEx aBmpEx(rAnimation.GetBitmapEx());
+ BitmapFilter::Filter(aBmpEx, rFilter);
+ rAnimation.SetBitmapEx(aBmpEx);
+ }
+
+ return bRet;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/bitmap/bitmappaint.cxx b/vcl/source/bitmap/bitmappaint.cxx
new file mode 100644
index 000000000..5c14e1476
--- /dev/null
+++ b/vcl/source/bitmap/bitmappaint.cxx
@@ -0,0 +1,1146 @@
+/* -*- 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <tools/poly.hxx>
+#include <tools/helpers.hxx>
+#include <vcl/bitmap.hxx>
+#include <vcl/bitmapaccess.hxx>
+#include <vcl/alpha.hxx>
+
+#include <bitmapwriteaccess.hxx>
+#include <salbmp.hxx>
+#include <svdata.hxx>
+#include <salinst.hxx>
+
+#include <algorithm>
+#include <memory>
+
+bool Bitmap::Erase(const Color& rFillColor)
+{
+ if (IsEmpty())
+ return true;
+
+ BitmapScopedWriteAccess pWriteAcc(*this);
+ bool bRet = false;
+
+ if (pWriteAcc)
+ {
+ const ScanlineFormat nFormat = pWriteAcc->GetScanlineFormat();
+ sal_uInt8 cIndex = 0;
+ bool bFast = false;
+
+ switch (nFormat)
+ {
+ case ScanlineFormat::N1BitMsbPal:
+ case ScanlineFormat::N1BitLsbPal:
+ {
+ cIndex = static_cast<sal_uInt8>(pWriteAcc->GetBestPaletteIndex(rFillColor));
+ cIndex = (cIndex ? 255 : 0);
+ bFast = true;
+ }
+ break;
+
+ case ScanlineFormat::N4BitMsnPal:
+ case ScanlineFormat::N4BitLsnPal:
+ {
+ cIndex = static_cast<sal_uInt8>(pWriteAcc->GetBestPaletteIndex(rFillColor));
+ cIndex = cIndex | (cIndex << 4);
+ bFast = true;
+ }
+ break;
+
+ case ScanlineFormat::N8BitPal:
+ {
+ cIndex = static_cast<sal_uInt8>(pWriteAcc->GetBestPaletteIndex(rFillColor));
+ bFast = true;
+ }
+ break;
+
+ case ScanlineFormat::N24BitTcBgr:
+ case ScanlineFormat::N24BitTcRgb:
+ {
+ if (rFillColor.GetRed() == rFillColor.GetGreen()
+ && rFillColor.GetRed() == rFillColor.GetBlue())
+ {
+ cIndex = rFillColor.GetRed();
+ bFast = true;
+ }
+ else
+ {
+ bFast = false;
+ }
+ }
+ break;
+
+ default:
+ bFast = false;
+ break;
+ }
+
+ if (bFast)
+ {
+ const sal_uLong nBufSize = pWriteAcc->GetScanlineSize() * pWriteAcc->Height();
+ memset(pWriteAcc->GetBuffer(), cIndex, nBufSize);
+ }
+ else
+ {
+ const tools::Rectangle aRect(Point(), Size(pWriteAcc->Width(), pWriteAcc->Height()));
+ pWriteAcc->SetFillColor(rFillColor);
+ pWriteAcc->FillRect(aRect);
+ }
+
+ bRet = true;
+ }
+
+ return bRet;
+}
+
+bool Bitmap::Invert()
+{
+ BitmapScopedWriteAccess pAcc(*this);
+ bool bRet = false;
+
+ if (pAcc)
+ {
+ if (pAcc->HasPalette())
+ {
+ BitmapPalette aBmpPal(pAcc->GetPalette());
+ const sal_uInt16 nCount = aBmpPal.GetEntryCount();
+
+ for (sal_uInt16 i = 0; i < nCount; i++)
+ {
+ aBmpPal[i].Invert();
+ }
+
+ pAcc->SetPalette(aBmpPal);
+ }
+ else
+ {
+ const long nWidth = pAcc->Width();
+ const long nHeight = pAcc->Height();
+
+ for (long nY = 0; nY < nHeight; nY++)
+ {
+ Scanline pScanline = pAcc->GetScanline(nY);
+ for (long nX = 0; nX < nWidth; nX++)
+ {
+ BitmapColor aBmpColor = pAcc->GetPixelFromData(pScanline, nX);
+ aBmpColor.Invert();
+ pAcc->SetPixelOnData(pScanline, nX, aBmpColor);
+ }
+ }
+ }
+
+ mxSalBmp->InvalidateChecksum();
+ pAcc.reset();
+ bRet = true;
+ }
+
+ return bRet;
+}
+
+bool Bitmap::Mirror(BmpMirrorFlags nMirrorFlags)
+{
+ bool bHorz(nMirrorFlags & BmpMirrorFlags::Horizontal);
+ bool bVert(nMirrorFlags & BmpMirrorFlags::Vertical);
+ bool bRet = false;
+
+ if (bHorz && !bVert)
+ {
+ BitmapScopedWriteAccess pAcc(*this);
+
+ if (pAcc)
+ {
+ const long nWidth = pAcc->Width();
+ const long nHeight = pAcc->Height();
+ const long nWidth1 = nWidth - 1;
+ const long nWidth_2 = nWidth >> 1;
+
+ for (long nY = 0; nY < nHeight; nY++)
+ {
+ Scanline pScanline = pAcc->GetScanline(nY);
+ for (long nX = 0, nOther = nWidth1; nX < nWidth_2; nX++, nOther--)
+ {
+ const BitmapColor aTemp(pAcc->GetPixelFromData(pScanline, nX));
+
+ pAcc->SetPixelOnData(pScanline, nX, pAcc->GetPixelFromData(pScanline, nOther));
+ pAcc->SetPixelOnData(pScanline, nOther, aTemp);
+ }
+ }
+
+ pAcc.reset();
+ bRet = true;
+ }
+ }
+ else if (bVert && !bHorz)
+ {
+ BitmapScopedWriteAccess pAcc(*this);
+
+ if (pAcc)
+ {
+ const long nScanSize = pAcc->GetScanlineSize();
+ std::unique_ptr<sal_uInt8[]> pBuffer(new sal_uInt8[nScanSize]);
+ const long nHeight = pAcc->Height();
+ const long nHeight1 = nHeight - 1;
+ const long nHeight_2 = nHeight >> 1;
+
+ for (long nY = 0, nOther = nHeight1; nY < nHeight_2; nY++, nOther--)
+ {
+ memcpy(pBuffer.get(), pAcc->GetScanline(nY), nScanSize);
+ memcpy(pAcc->GetScanline(nY), pAcc->GetScanline(nOther), nScanSize);
+ memcpy(pAcc->GetScanline(nOther), pBuffer.get(), nScanSize);
+ }
+
+ pAcc.reset();
+ bRet = true;
+ }
+ }
+ else if (bHorz && bVert)
+ {
+ BitmapScopedWriteAccess pAcc(*this);
+
+ if (pAcc)
+ {
+ const long nWidth = pAcc->Width();
+ const long nWidth1 = nWidth - 1;
+ const long nHeight = pAcc->Height();
+ long nHeight_2 = nHeight >> 1;
+
+ for (long nY = 0, nOtherY = nHeight - 1; nY < nHeight_2; nY++, nOtherY--)
+ {
+ Scanline pScanline = pAcc->GetScanline(nY);
+ Scanline pScanlineOther = pAcc->GetScanline(nOtherY);
+ for (long nX = 0, nOtherX = nWidth1; nX < nWidth; nX++, nOtherX--)
+ {
+ const BitmapColor aTemp(pAcc->GetPixelFromData(pScanline, nX));
+
+ pAcc->SetPixelOnData(pScanline, nX,
+ pAcc->GetPixelFromData(pScanlineOther, nOtherX));
+ pAcc->SetPixelOnData(pScanlineOther, nOtherX, aTemp);
+ }
+ }
+
+ // if necessary, also mirror the middle line horizontally
+ if (nHeight & 1)
+ {
+ Scanline pScanline = pAcc->GetScanline(nHeight_2);
+ for (long nX = 0, nOtherX = nWidth1, nWidth_2 = nWidth >> 1; nX < nWidth_2;
+ nX++, nOtherX--)
+ {
+ const BitmapColor aTemp(pAcc->GetPixelFromData(pScanline, nX));
+ pAcc->SetPixelOnData(pScanline, nX, pAcc->GetPixelFromData(pScanline, nOtherX));
+ pAcc->SetPixelOnData(pScanline, nOtherX, aTemp);
+ }
+ }
+
+ pAcc.reset();
+ bRet = true;
+ }
+ }
+ else
+ bRet = true;
+
+ return bRet;
+}
+
+bool Bitmap::Rotate(long nAngle10, const Color& rFillColor)
+{
+ bool bRet = false;
+
+ nAngle10 %= 3600;
+ nAngle10 = (nAngle10 < 0) ? (3599L + nAngle10) : nAngle10;
+
+ if (!nAngle10)
+ bRet = true;
+ else if (nAngle10 == 1800)
+ bRet = Mirror(BmpMirrorFlags::Horizontal | BmpMirrorFlags::Vertical);
+ else
+ {
+ ScopedReadAccess pReadAcc(*this);
+ Bitmap aRotatedBmp;
+
+ if (pReadAcc)
+ {
+ const Size aSizePix(GetSizePixel());
+
+ if (nAngle10 == 900 || nAngle10 == 2700)
+ {
+ const Size aNewSizePix(aSizePix.Height(), aSizePix.Width());
+ Bitmap aNewBmp(aNewSizePix, GetBitCount(), &pReadAcc->GetPalette());
+ BitmapScopedWriteAccess pWriteAcc(aNewBmp);
+
+ if (pWriteAcc)
+ {
+ const long nWidth = aSizePix.Width();
+ const long nWidth1 = nWidth - 1;
+ const long nHeight = aSizePix.Height();
+ const long nHeight1 = nHeight - 1;
+ const long nNewWidth = aNewSizePix.Width();
+ const long nNewHeight = aNewSizePix.Height();
+
+ if (nAngle10 == 900)
+ {
+ for (long nY = 0, nOtherX = nWidth1; nY < nNewHeight; nY++, nOtherX--)
+ {
+ Scanline pScanline = pWriteAcc->GetScanline(nY);
+ for (long nX = 0, nOtherY = 0; nX < nNewWidth; nX++)
+ {
+ pWriteAcc->SetPixelOnData(pScanline, nX,
+ pReadAcc->GetPixel(nOtherY++, nOtherX));
+ }
+ }
+ }
+ else if (nAngle10 == 2700)
+ {
+ for (long nY = 0, nOtherX = 0; nY < nNewHeight; nY++, nOtherX++)
+ {
+ Scanline pScanline = pWriteAcc->GetScanline(nY);
+ for (long nX = 0, nOtherY = nHeight1; nX < nNewWidth; nX++)
+ {
+ pWriteAcc->SetPixelOnData(pScanline, nX,
+ pReadAcc->GetPixel(nOtherY--, nOtherX));
+ }
+ }
+ }
+
+ pWriteAcc.reset();
+ }
+
+ aRotatedBmp = aNewBmp;
+ }
+ else
+ {
+ Point aTmpPoint;
+ tools::Rectangle aTmpRectangle(aTmpPoint, aSizePix);
+ tools::Polygon aPoly(aTmpRectangle);
+ aPoly.Rotate(aTmpPoint, static_cast<sal_uInt16>(nAngle10));
+
+ tools::Rectangle aNewBound(aPoly.GetBoundRect());
+ const Size aNewSizePix(aNewBound.GetSize());
+ Bitmap aNewBmp(aNewSizePix, GetBitCount(), &pReadAcc->GetPalette());
+ BitmapScopedWriteAccess pWriteAcc(aNewBmp);
+
+ if (pWriteAcc)
+ {
+ const BitmapColor aFillColor(pWriteAcc->GetBestMatchingColor(rFillColor));
+ const double fCosAngle = cos(nAngle10 * F_PI1800);
+ const double fSinAngle = sin(nAngle10 * F_PI1800);
+ const double fXMin = aNewBound.Left();
+ const double fYMin = aNewBound.Top();
+ const long nWidth = aSizePix.Width();
+ const long nHeight = aSizePix.Height();
+ const long nNewWidth = aNewSizePix.Width();
+ const long nNewHeight = aNewSizePix.Height();
+ long nX;
+ long nY;
+ long nRotX;
+ long nRotY;
+ std::unique_ptr<long[]> pCosX(new long[nNewWidth]);
+ std::unique_ptr<long[]> pSinX(new long[nNewWidth]);
+ std::unique_ptr<long[]> pCosY(new long[nNewHeight]);
+ std::unique_ptr<long[]> pSinY(new long[nNewHeight]);
+
+ for (nX = 0; nX < nNewWidth; nX++)
+ {
+ const double fTmp = (fXMin + nX) * 64.;
+
+ pCosX[nX] = FRound(fCosAngle * fTmp);
+ pSinX[nX] = FRound(fSinAngle * fTmp);
+ }
+
+ for (nY = 0; nY < nNewHeight; nY++)
+ {
+ const double fTmp = (fYMin + nY) * 64.;
+
+ pCosY[nY] = FRound(fCosAngle * fTmp);
+ pSinY[nY] = FRound(fSinAngle * fTmp);
+ }
+
+ for (nY = 0; nY < nNewHeight; nY++)
+ {
+ long nSinY = pSinY[nY];
+ long nCosY = pCosY[nY];
+ Scanline pScanline = pWriteAcc->GetScanline(nY);
+
+ for (nX = 0; nX < nNewWidth; nX++)
+ {
+ nRotX = (pCosX[nX] - nSinY) >> 6;
+ nRotY = (pSinX[nX] + nCosY) >> 6;
+
+ if ((nRotX > -1) && (nRotX < nWidth) && (nRotY > -1)
+ && (nRotY < nHeight))
+ {
+ pWriteAcc->SetPixelOnData(pScanline, nX,
+ pReadAcc->GetPixel(nRotY, nRotX));
+ }
+ else
+ {
+ pWriteAcc->SetPixelOnData(pScanline, nX, aFillColor);
+ }
+ }
+ }
+
+ pWriteAcc.reset();
+ }
+
+ aRotatedBmp = aNewBmp;
+ }
+
+ pReadAcc.reset();
+ }
+
+ bRet = !!aRotatedBmp;
+ if (bRet)
+ ReassignWithSize(aRotatedBmp);
+ }
+
+ return bRet;
+};
+
+Bitmap Bitmap::CreateMask(const Color& rTransColor, sal_uInt8 nTol) const
+{
+ ScopedReadAccess pReadAcc(const_cast<Bitmap&>(*this));
+
+ if (!nTol && pReadAcc
+ && (pReadAcc->GetScanlineFormat() == ScanlineFormat::N1BitLsbPal
+ || pReadAcc->GetScanlineFormat() == ScanlineFormat::N1BitMsbPal)
+ && pReadAcc->GetBestMatchingColor(COL_WHITE) == pReadAcc->GetBestMatchingColor(rTransColor))
+ {
+ // if we're a 1 bit pixel already, and the transcolor matches the color that would replace it
+ // already, then just return a copy
+ return *this;
+ }
+
+ Bitmap aNewBmp(GetSizePixel(), 1);
+ BitmapScopedWriteAccess pWriteAcc(aNewBmp);
+ bool bRet = false;
+
+ if (pWriteAcc && pReadAcc)
+ {
+ const long nWidth = pReadAcc->Width();
+ const long nHeight = pReadAcc->Height();
+ const BitmapColor aBlack(pWriteAcc->GetBestMatchingColor(COL_BLACK));
+ const BitmapColor aWhite(pWriteAcc->GetBestMatchingColor(COL_WHITE));
+
+ if (!nTol)
+ {
+ const BitmapColor aTest(pReadAcc->GetBestMatchingColor(rTransColor));
+
+ if (pReadAcc->GetScanlineFormat() == ScanlineFormat::N4BitMsnPal
+ || pReadAcc->GetScanlineFormat() == ScanlineFormat::N4BitLsnPal)
+ {
+ // optimized for 4Bit-MSN/LSN source palette
+ const sal_uInt8 cTest = aTest.GetIndex();
+ const long nShiftInit
+ = ((pReadAcc->GetScanlineFormat() == ScanlineFormat::N4BitMsnPal) ? 4 : 0);
+
+ if (pWriteAcc->GetScanlineFormat() == ScanlineFormat::N1BitMsbPal
+ && aWhite.GetIndex() == 1)
+ {
+ // optimized for 1Bit-MSB destination palette
+ for (long nY = 0; nY < nHeight; ++nY)
+ {
+ Scanline pSrc = pReadAcc->GetScanline(nY);
+ Scanline pDst = pWriteAcc->GetScanline(nY);
+ for (long nX = 0, nShift = nShiftInit; nX < nWidth; nX++, nShift ^= 4)
+ {
+ if (cTest == ((pSrc[nX >> 1] >> nShift) & 0x0f))
+ pDst[nX >> 3] |= 1 << (7 - (nX & 7));
+ else
+ pDst[nX >> 3] &= ~(1 << (7 - (nX & 7)));
+ }
+ }
+ }
+ else
+ {
+ for (long nY = 0; nY < nHeight; ++nY)
+ {
+ Scanline pSrc = pReadAcc->GetScanline(nY);
+ Scanline pDst = pWriteAcc->GetScanline(nY);
+ for (long nX = 0, nShift = nShiftInit; nX < nWidth; nX++, nShift ^= 4)
+ {
+ if (cTest == ((pSrc[nX >> 1] >> nShift) & 0x0f))
+ pWriteAcc->SetPixelOnData(pDst, nX, aWhite);
+ else
+ pWriteAcc->SetPixelOnData(pDst, nX, aBlack);
+ }
+ }
+ }
+ }
+ else if (pReadAcc->GetScanlineFormat() == ScanlineFormat::N8BitPal)
+ {
+ // optimized for 8Bit source palette
+ const sal_uInt8 cTest = aTest.GetIndex();
+
+ if (pWriteAcc->GetScanlineFormat() == ScanlineFormat::N1BitMsbPal
+ && aWhite.GetIndex() == 1)
+ {
+ // optimized for 1Bit-MSB destination palette
+ for (long nY = 0; nY < nHeight; ++nY)
+ {
+ Scanline pSrc = pReadAcc->GetScanline(nY);
+ Scanline pDst = pWriteAcc->GetScanline(nY);
+ for (long nX = 0; nX < nWidth; ++nX)
+ {
+ if (cTest == pSrc[nX])
+ pDst[nX >> 3] |= 1 << (7 - (nX & 7));
+ else
+ pDst[nX >> 3] &= ~(1 << (7 - (nX & 7)));
+ }
+ }
+ }
+ else
+ {
+ for (long nY = 0; nY < nHeight; ++nY)
+ {
+ Scanline pSrc = pReadAcc->GetScanline(nY);
+ Scanline pDst = pWriteAcc->GetScanline(nY);
+ for (long nX = 0; nX < nWidth; ++nX)
+ {
+ if (cTest == pSrc[nX])
+ pWriteAcc->SetPixelOnData(pDst, nX, aWhite);
+ else
+ pWriteAcc->SetPixelOnData(pDst, nX, aBlack);
+ }
+ }
+ }
+ }
+ else if (pWriteAcc->GetScanlineFormat() == pReadAcc->GetScanlineFormat()
+ && aWhite.GetIndex() == 1
+ && (pReadAcc->GetScanlineFormat() == ScanlineFormat::N1BitLsbPal
+ || pReadAcc->GetScanlineFormat() == ScanlineFormat::N1BitMsbPal))
+ {
+ for (long nY = 0; nY < nHeight; ++nY)
+ {
+ Scanline pSrc = pReadAcc->GetScanline(nY);
+ Scanline pDst = pWriteAcc->GetScanline(nY);
+ assert(pWriteAcc->GetScanlineSize() == pReadAcc->GetScanlineSize());
+ const long nScanlineSize = pWriteAcc->GetScanlineSize();
+ for (long nX = 0; nX < nScanlineSize; ++nX)
+ pDst[nX] = ~pSrc[nX];
+ }
+ }
+ else
+ {
+ // not optimized
+ for (long nY = 0; nY < nHeight; ++nY)
+ {
+ Scanline pScanline = pWriteAcc->GetScanline(nY);
+ Scanline pScanlineRead = pReadAcc->GetScanline(nY);
+ for (long nX = 0; nX < nWidth; ++nX)
+ {
+ if (aTest == pReadAcc->GetPixelFromData(pScanlineRead, nX))
+ pWriteAcc->SetPixelOnData(pScanline, nX, aWhite);
+ else
+ pWriteAcc->SetPixelOnData(pScanline, nX, aBlack);
+ }
+ }
+ }
+ }
+ else
+ {
+ BitmapColor aCol;
+ long nR, nG, nB;
+ const long nMinR = MinMax<long>(rTransColor.GetRed() - nTol, 0, 255);
+ const long nMaxR = MinMax<long>(rTransColor.GetRed() + nTol, 0, 255);
+ const long nMinG = MinMax<long>(rTransColor.GetGreen() - nTol, 0, 255);
+ const long nMaxG = MinMax<long>(rTransColor.GetGreen() + nTol, 0, 255);
+ const long nMinB = MinMax<long>(rTransColor.GetBlue() - nTol, 0, 255);
+ const long nMaxB = MinMax<long>(rTransColor.GetBlue() + nTol, 0, 255);
+
+ if (pReadAcc->HasPalette())
+ {
+ for (long nY = 0; nY < nHeight; nY++)
+ {
+ Scanline pScanline = pWriteAcc->GetScanline(nY);
+ Scanline pScanlineRead = pReadAcc->GetScanline(nY);
+ for (long nX = 0; nX < nWidth; nX++)
+ {
+ aCol = pReadAcc->GetPaletteColor(
+ pReadAcc->GetIndexFromData(pScanlineRead, nX));
+ nR = aCol.GetRed();
+ nG = aCol.GetGreen();
+ nB = aCol.GetBlue();
+
+ if (nMinR <= nR && nMaxR >= nR && nMinG <= nG && nMaxG >= nG && nMinB <= nB
+ && nMaxB >= nB)
+ {
+ pWriteAcc->SetPixelOnData(pScanline, nX, aWhite);
+ }
+ else
+ {
+ pWriteAcc->SetPixelOnData(pScanline, nX, aBlack);
+ }
+ }
+ }
+ }
+ else
+ {
+ for (long nY = 0; nY < nHeight; nY++)
+ {
+ Scanline pScanline = pWriteAcc->GetScanline(nY);
+ Scanline pScanlineRead = pReadAcc->GetScanline(nY);
+ for (long nX = 0; nX < nWidth; nX++)
+ {
+ aCol = pReadAcc->GetPixelFromData(pScanlineRead, nX);
+ nR = aCol.GetRed();
+ nG = aCol.GetGreen();
+ nB = aCol.GetBlue();
+
+ if (nMinR <= nR && nMaxR >= nR && nMinG <= nG && nMaxG >= nG && nMinB <= nB
+ && nMaxB >= nB)
+ {
+ pWriteAcc->SetPixelOnData(pScanline, nX, aWhite);
+ }
+ else
+ {
+ pWriteAcc->SetPixelOnData(pScanline, nX, aBlack);
+ }
+ }
+ }
+ }
+ }
+
+ bRet = true;
+ }
+
+ pWriteAcc.reset();
+ pReadAcc.reset();
+
+ if (bRet)
+ {
+ aNewBmp.maPrefSize = maPrefSize;
+ aNewBmp.maPrefMapMode = maPrefMapMode;
+ }
+ else
+ aNewBmp = Bitmap();
+
+ return aNewBmp;
+}
+
+vcl::Region Bitmap::CreateRegion(const Color& rColor, const tools::Rectangle& rRect) const
+{
+ vcl::Region aRegion;
+ tools::Rectangle aRect(rRect);
+ ScopedReadAccess pReadAcc(const_cast<Bitmap&>(*this));
+
+ aRect.Intersection(tools::Rectangle(Point(), GetSizePixel()));
+ aRect.Justify();
+
+ if (pReadAcc)
+ {
+ const long nLeft = aRect.Left();
+ const long nTop = aRect.Top();
+ const long nRight = aRect.Right();
+ const long nBottom = aRect.Bottom();
+ const BitmapColor aMatch(pReadAcc->GetBestMatchingColor(rColor));
+
+ std::vector<long> aLine;
+ long nYStart(nTop);
+ long nY(nTop);
+
+ for (; nY <= nBottom; nY++)
+ {
+ std::vector<long> aNewLine;
+ long nX(nLeft);
+ Scanline pScanlineRead = pReadAcc->GetScanline(nY);
+
+ for (; nX <= nRight;)
+ {
+ while ((nX <= nRight) && (aMatch != pReadAcc->GetPixelFromData(pScanlineRead, nX)))
+ nX++;
+
+ if (nX <= nRight)
+ {
+ aNewLine.push_back(nX);
+
+ while ((nX <= nRight)
+ && (aMatch == pReadAcc->GetPixelFromData(pScanlineRead, nX)))
+ {
+ nX++;
+ }
+
+ aNewLine.push_back(nX - 1);
+ }
+ }
+
+ if (aNewLine != aLine)
+ {
+ // need to write aLine, it's different from the next line
+ if (!aLine.empty())
+ {
+ tools::Rectangle aSubRect;
+
+ // enter y values and proceed ystart
+ aSubRect.SetTop(nYStart);
+ aSubRect.SetBottom(nY ? nY - 1 : 0);
+
+ for (size_t a(0); a < aLine.size();)
+ {
+ aSubRect.SetLeft(aLine[a++]);
+ aSubRect.SetRight(aLine[a++]);
+ aRegion.Union(aSubRect);
+ }
+ }
+
+ // copy line as new line
+ aLine = aNewLine;
+ nYStart = nY;
+ }
+ }
+
+ // write last line if used
+ if (!aLine.empty())
+ {
+ tools::Rectangle aSubRect;
+
+ // enter y values
+ aSubRect.SetTop(nYStart);
+ aSubRect.SetBottom(nY ? nY - 1 : 0);
+
+ for (size_t a(0); a < aLine.size();)
+ {
+ aSubRect.SetLeft(aLine[a++]);
+ aSubRect.SetRight(aLine[a++]);
+ aRegion.Union(aSubRect);
+ }
+ }
+
+ pReadAcc.reset();
+ }
+ else
+ {
+ aRegion = aRect;
+ }
+
+ return aRegion;
+}
+
+bool Bitmap::Replace(const Bitmap& rMask, const Color& rReplaceColor)
+{
+ ScopedReadAccess pMaskAcc(const_cast<Bitmap&>(rMask));
+ BitmapScopedWriteAccess pAcc(*this);
+ bool bRet = false;
+
+ if (pMaskAcc && pAcc)
+ {
+ const long nWidth = std::min(pMaskAcc->Width(), pAcc->Width());
+ const long nHeight = std::min(pMaskAcc->Height(), pAcc->Height());
+ const BitmapColor aMaskWhite(pMaskAcc->GetBestMatchingColor(COL_WHITE));
+ BitmapColor aReplace;
+
+ if (pAcc->HasPalette())
+ {
+ const sal_uInt16 nActColors = pAcc->GetPaletteEntryCount();
+ const sal_uInt16 nMaxColors = 1 << pAcc->GetBitCount();
+
+ // default to the nearest color
+ aReplace = pAcc->GetBestMatchingColor(rReplaceColor);
+
+ // for paletted images without a matching palette entry
+ // look for an unused palette entry (NOTE: expensive!)
+ if (pAcc->GetPaletteColor(aReplace.GetIndex()) != BitmapColor(rReplaceColor))
+ {
+ // if the palette has empty entries use the last one
+ if (nActColors < nMaxColors)
+ {
+ pAcc->SetPaletteEntryCount(nActColors + 1);
+ pAcc->SetPaletteColor(nActColors, rReplaceColor);
+ aReplace = BitmapColor(static_cast<sal_uInt8>(nActColors));
+ }
+ else
+ {
+ std::unique_ptr<bool[]> pFlags(new bool[nMaxColors]);
+
+ // Set all entries to false
+ std::fill(pFlags.get(), pFlags.get() + nMaxColors, false);
+
+ for (long nY = 0; nY < nHeight; nY++)
+ {
+ Scanline pScanline = pAcc->GetScanline(nY);
+ for (long nX = 0; nX < nWidth; nX++)
+ pFlags[pAcc->GetIndexFromData(pScanline, nX)] = true;
+ }
+
+ for (sal_uInt16 i = 0; i < nMaxColors; i++)
+ {
+ // Hurray, we do have an unused entry
+ if (!pFlags[i])
+ {
+ pAcc->SetPaletteColor(i, rReplaceColor);
+ aReplace = BitmapColor(static_cast<sal_uInt8>(i));
+ }
+ }
+ }
+ }
+ }
+ else
+ aReplace = rReplaceColor;
+
+ for (long nY = 0; nY < nHeight; nY++)
+ {
+ Scanline pScanline = pAcc->GetScanline(nY);
+ Scanline pScanlineMask = pMaskAcc->GetScanline(nY);
+ for (long nX = 0; nX < nWidth; nX++)
+ {
+ if (pMaskAcc->GetPixelFromData(pScanlineMask, nX) == aMaskWhite)
+ pAcc->SetPixelOnData(pScanline, nX, aReplace);
+ }
+ }
+
+ bRet = true;
+ }
+
+ return bRet;
+}
+
+bool Bitmap::Replace(const AlphaMask& rAlpha, const Color& rMergeColor)
+{
+ Bitmap aNewBmp(GetSizePixel(), 24);
+ ScopedReadAccess pAcc(*this);
+ AlphaMask::ScopedReadAccess pAlphaAcc(const_cast<AlphaMask&>(rAlpha));
+ BitmapScopedWriteAccess pNewAcc(aNewBmp);
+ bool bRet = false;
+
+ if (pAcc && pAlphaAcc && pNewAcc)
+ {
+ BitmapColor aCol;
+ const long nWidth = std::min(pAlphaAcc->Width(), pAcc->Width());
+ const long nHeight = std::min(pAlphaAcc->Height(), pAcc->Height());
+
+ for (long nY = 0; nY < nHeight; nY++)
+ {
+ Scanline pScanline = pNewAcc->GetScanline(nY);
+ Scanline pScanlineAlpha = pAlphaAcc->GetScanline(nY);
+ for (long nX = 0; nX < nWidth; nX++)
+ {
+ aCol = pAcc->GetColor(nY, nX);
+ aCol.Merge(rMergeColor, 255 - pAlphaAcc->GetIndexFromData(pScanlineAlpha, nX));
+ pNewAcc->SetPixelOnData(pScanline, nX, aCol);
+ }
+ }
+
+ bRet = true;
+ }
+
+ pAcc.reset();
+ pAlphaAcc.reset();
+ pNewAcc.reset();
+
+ if (bRet)
+ {
+ const MapMode aMap(maPrefMapMode);
+ const Size aSize(maPrefSize);
+
+ *this = aNewBmp;
+
+ maPrefMapMode = aMap;
+ maPrefSize = aSize;
+ }
+
+ return bRet;
+}
+
+bool Bitmap::Replace(const Color& rSearchColor, const Color& rReplaceColor, sal_uInt8 nTol)
+{
+ if (mxSalBmp)
+ {
+ // implementation specific replace
+ std::shared_ptr<SalBitmap> xImpBmp(ImplGetSVData()->mpDefInst->CreateSalBitmap());
+ if (xImpBmp->Create(*mxSalBmp) && xImpBmp->Replace(rSearchColor, rReplaceColor, nTol))
+ {
+ ImplSetSalBitmap(xImpBmp);
+ maPrefMapMode = MapMode(MapUnit::MapPixel);
+ maPrefSize = xImpBmp->GetSize();
+ return true;
+ }
+ }
+
+ // Bitmaps with 1 bit color depth can cause problems if they have other entries than black/white
+ // in their palette
+ if (GetBitCount() == 1)
+ Convert(BmpConversion::N4BitColors);
+
+ BitmapScopedWriteAccess pAcc(*this);
+ bool bRet = false;
+
+ if (pAcc)
+ {
+ const long nMinR = MinMax<long>(rSearchColor.GetRed() - nTol, 0, 255);
+ const long nMaxR = MinMax<long>(rSearchColor.GetRed() + nTol, 0, 255);
+ const long nMinG = MinMax<long>(rSearchColor.GetGreen() - nTol, 0, 255);
+ const long nMaxG = MinMax<long>(rSearchColor.GetGreen() + nTol, 0, 255);
+ const long nMinB = MinMax<long>(rSearchColor.GetBlue() - nTol, 0, 255);
+ const long nMaxB = MinMax<long>(rSearchColor.GetBlue() + nTol, 0, 255);
+
+ if (pAcc->HasPalette())
+ {
+ for (sal_uInt16 i = 0, nPalCount = pAcc->GetPaletteEntryCount(); i < nPalCount; i++)
+ {
+ const BitmapColor& rCol = pAcc->GetPaletteColor(i);
+
+ if (nMinR <= rCol.GetRed() && nMaxR >= rCol.GetRed() && nMinG <= rCol.GetGreen()
+ && nMaxG >= rCol.GetGreen() && nMinB <= rCol.GetBlue()
+ && nMaxB >= rCol.GetBlue())
+ {
+ pAcc->SetPaletteColor(i, rReplaceColor);
+ }
+ }
+ }
+ else
+ {
+ BitmapColor aCol;
+ const BitmapColor aReplace(pAcc->GetBestMatchingColor(rReplaceColor));
+
+ for (long nY = 0, nHeight = pAcc->Height(); nY < nHeight; nY++)
+ {
+ Scanline pScanline = pAcc->GetScanline(nY);
+ for (long nX = 0, nWidth = pAcc->Width(); nX < nWidth; nX++)
+ {
+ aCol = pAcc->GetPixelFromData(pScanline, nX);
+
+ if (nMinR <= aCol.GetRed() && nMaxR >= aCol.GetRed() && nMinG <= aCol.GetGreen()
+ && nMaxG >= aCol.GetGreen() && nMinB <= aCol.GetBlue()
+ && nMaxB >= aCol.GetBlue())
+ {
+ pAcc->SetPixelOnData(pScanline, nX, aReplace);
+ }
+ }
+ }
+ }
+
+ pAcc.reset();
+ bRet = true;
+ }
+
+ return bRet;
+}
+
+bool Bitmap::Replace(const Color* pSearchColors, const Color* pReplaceColors, sal_uLong nColorCount,
+ sal_uInt8 const* pTols)
+{
+ // Bitmaps with 1 bit color depth can cause problems if they have other entries than black/white
+ // in their palette
+ if (GetBitCount() == 1)
+ Convert(BmpConversion::N4BitColors);
+
+ BitmapScopedWriteAccess pAcc(*this);
+ bool bRet = false;
+
+ if (pAcc)
+ {
+ std::unique_ptr<long[]> pMinR(new long[nColorCount]);
+ std::unique_ptr<long[]> pMaxR(new long[nColorCount]);
+ std::unique_ptr<long[]> pMinG(new long[nColorCount]);
+ std::unique_ptr<long[]> pMaxG(new long[nColorCount]);
+ std::unique_ptr<long[]> pMinB(new long[nColorCount]);
+ std::unique_ptr<long[]> pMaxB(new long[nColorCount]);
+
+ if (pTols)
+ {
+ for (sal_uLong i = 0; i < nColorCount; i++)
+ {
+ const Color& rCol = pSearchColors[i];
+ const sal_uInt8 nTol = pTols[i];
+
+ pMinR[i] = MinMax<long>(rCol.GetRed() - nTol, 0, 255);
+ pMaxR[i] = MinMax<long>(rCol.GetRed() + nTol, 0, 255);
+ pMinG[i] = MinMax<long>(rCol.GetGreen() - nTol, 0, 255);
+ pMaxG[i] = MinMax<long>(rCol.GetGreen() + nTol, 0, 255);
+ pMinB[i] = MinMax<long>(rCol.GetBlue() - nTol, 0, 255);
+ pMaxB[i] = MinMax<long>(rCol.GetBlue() + nTol, 0, 255);
+ }
+ }
+ else
+ {
+ for (sal_uLong i = 0; i < nColorCount; i++)
+ {
+ const Color& rCol = pSearchColors[i];
+
+ pMinR[i] = rCol.GetRed();
+ pMaxR[i] = rCol.GetRed();
+ pMinG[i] = rCol.GetGreen();
+ pMaxG[i] = rCol.GetGreen();
+ pMinB[i] = rCol.GetBlue();
+ pMaxB[i] = rCol.GetBlue();
+ }
+ }
+
+ if (pAcc->HasPalette())
+ {
+ for (sal_uInt16 nEntry = 0, nPalCount = pAcc->GetPaletteEntryCount();
+ nEntry < nPalCount; nEntry++)
+ {
+ const BitmapColor& rCol = pAcc->GetPaletteColor(nEntry);
+
+ for (sal_uLong i = 0; i < nColorCount; i++)
+ {
+ if (pMinR[i] <= rCol.GetRed() && pMaxR[i] >= rCol.GetRed()
+ && pMinG[i] <= rCol.GetGreen() && pMaxG[i] >= rCol.GetGreen()
+ && pMinB[i] <= rCol.GetBlue() && pMaxB[i] >= rCol.GetBlue())
+ {
+ pAcc->SetPaletteColor(nEntry, pReplaceColors[i]);
+ break;
+ }
+ }
+ }
+ }
+ else
+ {
+ BitmapColor aCol;
+ std::unique_ptr<BitmapColor[]> pReplaces(new BitmapColor[nColorCount]);
+
+ for (sal_uLong i = 0; i < nColorCount; i++)
+ pReplaces[i] = pAcc->GetBestMatchingColor(pReplaceColors[i]);
+
+ for (long nY = 0, nHeight = pAcc->Height(); nY < nHeight; nY++)
+ {
+ Scanline pScanline = pAcc->GetScanline(nY);
+ for (long nX = 0, nWidth = pAcc->Width(); nX < nWidth; nX++)
+ {
+ aCol = pAcc->GetPixelFromData(pScanline, nX);
+
+ for (sal_uLong i = 0; i < nColorCount; i++)
+ {
+ if (pMinR[i] <= aCol.GetRed() && pMaxR[i] >= aCol.GetRed()
+ && pMinG[i] <= aCol.GetGreen() && pMaxG[i] >= aCol.GetGreen()
+ && pMinB[i] <= aCol.GetBlue() && pMaxB[i] >= aCol.GetBlue())
+ {
+ pAcc->SetPixelOnData(pScanline, nX, pReplaces[i]);
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ pAcc.reset();
+ bRet = true;
+ }
+
+ return bRet;
+}
+
+bool Bitmap::CombineSimple(const Bitmap& rMask, BmpCombine eCombine)
+{
+ ScopedReadAccess pMaskAcc(const_cast<Bitmap&>(rMask));
+ BitmapScopedWriteAccess pAcc(*this);
+ bool bRet = false;
+
+ if (pMaskAcc && pAcc)
+ {
+ const long nWidth = std::min(pMaskAcc->Width(), pAcc->Width());
+ const long nHeight = std::min(pMaskAcc->Height(), pAcc->Height());
+ const Color aColBlack(COL_BLACK);
+ const BitmapColor aWhite(pAcc->GetBestMatchingColor(COL_WHITE));
+ const BitmapColor aBlack(pAcc->GetBestMatchingColor(aColBlack));
+ const BitmapColor aMaskBlack(pMaskAcc->GetBestMatchingColor(aColBlack));
+
+ switch (eCombine)
+ {
+ case BmpCombine::And:
+ {
+ for (long nY = 0; nY < nHeight; nY++)
+ {
+ Scanline pScanline = pAcc->GetScanline(nY);
+ Scanline pScanlineMask = pMaskAcc->GetScanline(nY);
+ for (long nX = 0; nX < nWidth; nX++)
+ {
+ if (pMaskAcc->GetPixelFromData(pScanlineMask, nX) != aMaskBlack
+ && pAcc->GetPixelFromData(pScanline, nX) != aBlack)
+ {
+ pAcc->SetPixelOnData(pScanline, nX, aWhite);
+ }
+ else
+ {
+ pAcc->SetPixelOnData(pScanline, nX, aBlack);
+ }
+ }
+ }
+ }
+ break;
+
+ case BmpCombine::Or:
+ {
+ for (long nY = 0; nY < nHeight; nY++)
+ {
+ Scanline pScanline = pAcc->GetScanline(nY);
+ Scanline pScanlineMask = pMaskAcc->GetScanline(nY);
+ for (long nX = 0; nX < nWidth; nX++)
+ {
+ if (pMaskAcc->GetPixelFromData(pScanlineMask, nX) != aMaskBlack
+ || pAcc->GetPixelFromData(pScanline, nX) != aBlack)
+ {
+ pAcc->SetPixelOnData(pScanline, nX, aWhite);
+ }
+ else
+ {
+ pAcc->SetPixelOnData(pScanline, nX, aBlack);
+ }
+ }
+ }
+ }
+ break;
+ }
+
+ bRet = true;
+ }
+
+ return bRet;
+}
+
+// TODO: Have a look at OutputDevice::ImplDrawAlpha() for some
+// optimizations. Might even consolidate the code here and there.
+bool Bitmap::Blend(const AlphaMask& rAlpha, const Color& rBackgroundColor)
+{
+ // Convert to a truecolor bitmap, if we're a paletted one. There's room for tradeoff decision here,
+ // maybe later for an overload (or a flag)
+ if (GetBitCount() <= 8)
+ Convert(BmpConversion::N24Bit);
+
+ AlphaMask::ScopedReadAccess pAlphaAcc(const_cast<AlphaMask&>(rAlpha));
+
+ BitmapScopedWriteAccess pAcc(*this);
+ bool bRet = false;
+
+ if (pAlphaAcc && pAcc)
+ {
+ const long nWidth = std::min(pAlphaAcc->Width(), pAcc->Width());
+ const long nHeight = std::min(pAlphaAcc->Height(), pAcc->Height());
+
+ for (long nY = 0; nY < nHeight; ++nY)
+ {
+ Scanline pScanline = pAcc->GetScanline(nY);
+ Scanline pScanlineAlpha = pAlphaAcc->GetScanline(nY);
+ for (long nX = 0; nX < nWidth; ++nX)
+ {
+ BitmapColor aBmpColor = pAcc->GetPixelFromData(pScanline, nX);
+ aBmpColor.Merge(rBackgroundColor,
+ 255 - pAlphaAcc->GetIndexFromData(pScanlineAlpha, nX));
+ pAcc->SetPixelOnData(pScanline, nX, aBmpColor);
+ }
+ }
+
+ bRet = true;
+ }
+
+ return bRet;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/bitmap/checksum.cxx b/vcl/source/bitmap/checksum.cxx
new file mode 100644
index 000000000..d33d88403
--- /dev/null
+++ b/vcl/source/bitmap/checksum.cxx
@@ -0,0 +1,154 @@
+/* -*- 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/types.h>
+#include <vcl/checksum.hxx>
+
+
+/*========================================================================
+ *
+ * vcl_crc64Table (CRC polynomial 0x95AC9329AC4BC9B5ULL).
+ *
+ *======================================================================*/
+static const sal_uInt64 vcl_crc64Table[256] = {
+ 0x0000000000000000ULL, 0x7ad870c830358979ULL, 0xf5b0e190606b12f2ULL,
+ 0x8f689158505e9b8bULL, 0xc038e5739841b68fULL, 0xbae095bba8743ff6ULL,
+ 0x358804e3f82aa47dULL, 0x4f50742bc81f2d04ULL, 0xab28ecb46814fe75ULL,
+ 0xd1f09c7c5821770cULL, 0x5e980d24087fec87ULL, 0x24407dec384a65feULL,
+ 0x6b1009c7f05548faULL, 0x11c8790fc060c183ULL, 0x9ea0e857903e5a08ULL,
+ 0xe478989fa00bd371ULL, 0x7d08ff3b88be6f81ULL, 0x07d08ff3b88be6f8ULL,
+ 0x88b81eabe8d57d73ULL, 0xf2606e63d8e0f40aULL, 0xbd301a4810ffd90eULL,
+ 0xc7e86a8020ca5077ULL, 0x4880fbd87094cbfcULL, 0x32588b1040a14285ULL,
+ 0xd620138fe0aa91f4ULL, 0xacf86347d09f188dULL, 0x2390f21f80c18306ULL,
+ 0x594882d7b0f40a7fULL, 0x1618f6fc78eb277bULL, 0x6cc0863448deae02ULL,
+ 0xe3a8176c18803589ULL, 0x997067a428b5bcf0ULL, 0xfa11fe77117cdf02ULL,
+ 0x80c98ebf2149567bULL, 0x0fa11fe77117cdf0ULL, 0x75796f2f41224489ULL,
+ 0x3a291b04893d698dULL, 0x40f16bccb908e0f4ULL, 0xcf99fa94e9567b7fULL,
+ 0xb5418a5cd963f206ULL, 0x513912c379682177ULL, 0x2be1620b495da80eULL,
+ 0xa489f35319033385ULL, 0xde51839b2936bafcULL, 0x9101f7b0e12997f8ULL,
+ 0xebd98778d11c1e81ULL, 0x64b116208142850aULL, 0x1e6966e8b1770c73ULL,
+ 0x8719014c99c2b083ULL, 0xfdc17184a9f739faULL, 0x72a9e0dcf9a9a271ULL,
+ 0x08719014c99c2b08ULL, 0x4721e43f0183060cULL, 0x3df994f731b68f75ULL,
+ 0xb29105af61e814feULL, 0xc849756751dd9d87ULL, 0x2c31edf8f1d64ef6ULL,
+ 0x56e99d30c1e3c78fULL, 0xd9810c6891bd5c04ULL, 0xa3597ca0a188d57dULL,
+ 0xec09088b6997f879ULL, 0x96d1784359a27100ULL, 0x19b9e91b09fcea8bULL,
+ 0x636199d339c963f2ULL, 0xdf7adabd7a6e2d6fULL, 0xa5a2aa754a5ba416ULL,
+ 0x2aca3b2d1a053f9dULL, 0x50124be52a30b6e4ULL, 0x1f423fcee22f9be0ULL,
+ 0x659a4f06d21a1299ULL, 0xeaf2de5e82448912ULL, 0x902aae96b271006bULL,
+ 0x74523609127ad31aULL, 0x0e8a46c1224f5a63ULL, 0x81e2d7997211c1e8ULL,
+ 0xfb3aa75142244891ULL, 0xb46ad37a8a3b6595ULL, 0xceb2a3b2ba0eececULL,
+ 0x41da32eaea507767ULL, 0x3b024222da65fe1eULL, 0xa2722586f2d042eeULL,
+ 0xd8aa554ec2e5cb97ULL, 0x57c2c41692bb501cULL, 0x2d1ab4dea28ed965ULL,
+ 0x624ac0f56a91f461ULL, 0x1892b03d5aa47d18ULL, 0x97fa21650afae693ULL,
+ 0xed2251ad3acf6feaULL, 0x095ac9329ac4bc9bULL, 0x7382b9faaaf135e2ULL,
+ 0xfcea28a2faafae69ULL, 0x8632586aca9a2710ULL, 0xc9622c4102850a14ULL,
+ 0xb3ba5c8932b0836dULL, 0x3cd2cdd162ee18e6ULL, 0x460abd1952db919fULL,
+ 0x256b24ca6b12f26dULL, 0x5fb354025b277b14ULL, 0xd0dbc55a0b79e09fULL,
+ 0xaa03b5923b4c69e6ULL, 0xe553c1b9f35344e2ULL, 0x9f8bb171c366cd9bULL,
+ 0x10e3202993385610ULL, 0x6a3b50e1a30ddf69ULL, 0x8e43c87e03060c18ULL,
+ 0xf49bb8b633338561ULL, 0x7bf329ee636d1eeaULL, 0x012b592653589793ULL,
+ 0x4e7b2d0d9b47ba97ULL, 0x34a35dc5ab7233eeULL, 0xbbcbcc9dfb2ca865ULL,
+ 0xc113bc55cb19211cULL, 0x5863dbf1e3ac9decULL, 0x22bbab39d3991495ULL,
+ 0xadd33a6183c78f1eULL, 0xd70b4aa9b3f20667ULL, 0x985b3e827bed2b63ULL,
+ 0xe2834e4a4bd8a21aULL, 0x6debdf121b863991ULL, 0x1733afda2bb3b0e8ULL,
+ 0xf34b37458bb86399ULL, 0x8993478dbb8deae0ULL, 0x06fbd6d5ebd3716bULL,
+ 0x7c23a61ddbe6f812ULL, 0x3373d23613f9d516ULL, 0x49aba2fe23cc5c6fULL,
+ 0xc6c333a67392c7e4ULL, 0xbc1b436e43a74e9dULL, 0x95ac9329ac4bc9b5ULL,
+ 0xef74e3e19c7e40ccULL, 0x601c72b9cc20db47ULL, 0x1ac40271fc15523eULL,
+ 0x5594765a340a7f3aULL, 0x2f4c0692043ff643ULL, 0xa02497ca54616dc8ULL,
+ 0xdafce7026454e4b1ULL, 0x3e847f9dc45f37c0ULL, 0x445c0f55f46abeb9ULL,
+ 0xcb349e0da4342532ULL, 0xb1eceec59401ac4bULL, 0xfebc9aee5c1e814fULL,
+ 0x8464ea266c2b0836ULL, 0x0b0c7b7e3c7593bdULL, 0x71d40bb60c401ac4ULL,
+ 0xe8a46c1224f5a634ULL, 0x927c1cda14c02f4dULL, 0x1d148d82449eb4c6ULL,
+ 0x67ccfd4a74ab3dbfULL, 0x289c8961bcb410bbULL, 0x5244f9a98c8199c2ULL,
+ 0xdd2c68f1dcdf0249ULL, 0xa7f41839ecea8b30ULL, 0x438c80a64ce15841ULL,
+ 0x3954f06e7cd4d138ULL, 0xb63c61362c8a4ab3ULL, 0xcce411fe1cbfc3caULL,
+ 0x83b465d5d4a0eeceULL, 0xf96c151de49567b7ULL, 0x76048445b4cbfc3cULL,
+ 0x0cdcf48d84fe7545ULL, 0x6fbd6d5ebd3716b7ULL, 0x15651d968d029fceULL,
+ 0x9a0d8ccedd5c0445ULL, 0xe0d5fc06ed698d3cULL, 0xaf85882d2576a038ULL,
+ 0xd55df8e515432941ULL, 0x5a3569bd451db2caULL, 0x20ed197575283bb3ULL,
+ 0xc49581ead523e8c2ULL, 0xbe4df122e51661bbULL, 0x3125607ab548fa30ULL,
+ 0x4bfd10b2857d7349ULL, 0x04ad64994d625e4dULL, 0x7e7514517d57d734ULL,
+ 0xf11d85092d094cbfULL, 0x8bc5f5c11d3cc5c6ULL, 0x12b5926535897936ULL,
+ 0x686de2ad05bcf04fULL, 0xe70573f555e26bc4ULL, 0x9ddd033d65d7e2bdULL,
+ 0xd28d7716adc8cfb9ULL, 0xa85507de9dfd46c0ULL, 0x273d9686cda3dd4bULL,
+ 0x5de5e64efd965432ULL, 0xb99d7ed15d9d8743ULL, 0xc3450e196da80e3aULL,
+ 0x4c2d9f413df695b1ULL, 0x36f5ef890dc31cc8ULL, 0x79a59ba2c5dc31ccULL,
+ 0x037deb6af5e9b8b5ULL, 0x8c157a32a5b7233eULL, 0xf6cd0afa9582aa47ULL,
+ 0x4ad64994d625e4daULL, 0x300e395ce6106da3ULL, 0xbf66a804b64ef628ULL,
+ 0xc5bed8cc867b7f51ULL, 0x8aeeace74e645255ULL, 0xf036dc2f7e51db2cULL,
+ 0x7f5e4d772e0f40a7ULL, 0x05863dbf1e3ac9deULL, 0xe1fea520be311aafULL,
+ 0x9b26d5e88e0493d6ULL, 0x144e44b0de5a085dULL, 0x6e963478ee6f8124ULL,
+ 0x21c640532670ac20ULL, 0x5b1e309b16452559ULL, 0xd476a1c3461bbed2ULL,
+ 0xaeaed10b762e37abULL, 0x37deb6af5e9b8b5bULL, 0x4d06c6676eae0222ULL,
+ 0xc26e573f3ef099a9ULL, 0xb8b627f70ec510d0ULL, 0xf7e653dcc6da3dd4ULL,
+ 0x8d3e2314f6efb4adULL, 0x0256b24ca6b12f26ULL, 0x788ec2849684a65fULL,
+ 0x9cf65a1b368f752eULL, 0xe62e2ad306bafc57ULL, 0x6946bb8b56e467dcULL,
+ 0x139ecb4366d1eea5ULL, 0x5ccebf68aecec3a1ULL, 0x2616cfa09efb4ad8ULL,
+ 0xa97e5ef8cea5d153ULL, 0xd3a62e30fe90582aULL, 0xb0c7b7e3c7593bd8ULL,
+ 0xca1fc72bf76cb2a1ULL, 0x45775673a732292aULL, 0x3faf26bb9707a053ULL,
+ 0x70ff52905f188d57ULL, 0x0a2722586f2d042eULL, 0x854fb3003f739fa5ULL,
+ 0xff97c3c80f4616dcULL, 0x1bef5b57af4dc5adULL, 0x61372b9f9f784cd4ULL,
+ 0xee5fbac7cf26d75fULL, 0x9487ca0fff135e26ULL, 0xdbd7be24370c7322ULL,
+ 0xa10fceec0739fa5bULL, 0x2e675fb4576761d0ULL, 0x54bf2f7c6752e8a9ULL,
+ 0xcdcf48d84fe75459ULL, 0xb71738107fd2dd20ULL, 0x387fa9482f8c46abULL,
+ 0x42a7d9801fb9cfd2ULL, 0x0df7adabd7a6e2d6ULL, 0x772fdd63e7936bafULL,
+ 0xf8474c3bb7cdf024ULL, 0x829f3cf387f8795dULL, 0x66e7a46c27f3aa2cULL,
+ 0x1c3fd4a417c62355ULL, 0x935745fc4798b8deULL, 0xe98f353477ad31a7ULL,
+ 0xa6df411fbfb21ca3ULL, 0xdc0731d78f8795daULL, 0x536fa08fdfd90e51ULL,
+ 0x29b7d047efec8728ULL
+};
+
+/*========================================================================
+ *
+ * vcl_crc64 implementation.
+ *
+ *======================================================================*/
+#define UPDCRC64(crc, octet) \
+ (vcl_crc64Table[((crc) ^ (octet)) & 0xff] ^ ((crc) >> 8))
+
+/*
+ * vcl_crc64.
+ */
+sal_uInt64 vcl_crc64 (
+ sal_uInt64 Crc,
+ const void *Data, sal_uInt32 DatLen) SAL_THROW_EXTERN_C()
+{
+ if (Data)
+ {
+ const sal_uInt8 *p = static_cast<const sal_uInt8 *>(Data);
+ const sal_uInt8 *q = p + DatLen;
+
+ Crc = ~Crc;
+ while (p < q)
+ Crc = UPDCRC64(Crc, *(p++));
+ Crc = ~Crc;
+ }
+ return Crc;
+}
+
+/*
+ * vcl_get_crc64_table.
+ */
+const sal_uInt64* vcl_get_crc64_table()
+{
+ return vcl_crc64Table;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/bitmap/salbmp.cxx b/vcl/source/bitmap/salbmp.cxx
new file mode 100644
index 000000000..f731a5690
--- /dev/null
+++ b/vcl/source/bitmap/salbmp.cxx
@@ -0,0 +1,225 @@
+/* -*- 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <salbmp.hxx>
+
+void SalBitmap::updateChecksum() const
+{
+ if (mbChecksumValid)
+ return;
+
+ BitmapChecksum nCrc = 0;
+ SalBitmap* pThis = const_cast<SalBitmap*>(this);
+ BitmapBuffer* pBuf = pThis->AcquireBuffer(BitmapAccessMode::Read);
+ if (pBuf)
+ {
+ nCrc = pBuf->maPalette.GetChecksum();
+ nCrc = vcl_get_checksum(nCrc, pBuf->mpBits, pBuf->mnScanlineSize * pBuf->mnHeight);
+ pThis->ReleaseBuffer(pBuf, BitmapAccessMode::Read);
+ pThis->mnChecksum = nCrc;
+ pThis->mbChecksumValid = true;
+ }
+ else
+ {
+ pThis->mbChecksumValid = false;
+ }
+}
+
+namespace
+{
+
+class ImplPixelFormat
+{
+protected:
+ const sal_uInt8* mpData;
+public:
+ static ImplPixelFormat* GetFormat( sal_uInt16 nBits, const BitmapPalette& rPalette );
+
+ virtual void StartLine( const sal_uInt8* pLine ) { mpData = pLine; }
+ virtual const BitmapColor& ReadPixel() = 0;
+ virtual ~ImplPixelFormat() { }
+};
+
+class ImplPixelFormat8 : public ImplPixelFormat
+{
+private:
+ const BitmapPalette& mrPalette;
+
+public:
+ explicit ImplPixelFormat8( const BitmapPalette& rPalette )
+ : mrPalette( rPalette )
+ {
+ }
+ virtual const BitmapColor& ReadPixel() override
+ {
+ assert( mrPalette.GetEntryCount() > *mpData );
+ return mrPalette[ *mpData++ ];
+ }
+};
+
+class ImplPixelFormat4 : public ImplPixelFormat
+{
+private:
+ const BitmapPalette& mrPalette;
+ sal_uInt32 mnX;
+ sal_uInt32 mnShift;
+
+public:
+ explicit ImplPixelFormat4( const BitmapPalette& rPalette )
+ : mrPalette( rPalette )
+ , mnX(0)
+ , mnShift(4)
+ {
+ }
+ virtual void StartLine( const sal_uInt8* pLine ) override
+ {
+ mpData = pLine;
+ mnX = 0;
+ mnShift = 4;
+ }
+ virtual const BitmapColor& ReadPixel() override
+ {
+ sal_uInt32 nIdx = ( mpData[mnX >> 1] >> mnShift) & 0x0f;
+ assert( mrPalette.GetEntryCount() > nIdx );
+ const BitmapColor& rColor = mrPalette[nIdx];
+ mnX++;
+ mnShift ^= 4;
+ return rColor;
+ }
+};
+
+class ImplPixelFormat1 : public ImplPixelFormat
+{
+private:
+ const BitmapPalette& mrPalette;
+ sal_uInt32 mnX;
+
+public:
+ explicit ImplPixelFormat1( const BitmapPalette& rPalette )
+ : mrPalette(rPalette)
+ , mnX(0)
+ {
+ }
+ virtual void StartLine( const sal_uInt8* pLine ) override
+ {
+ mpData = pLine;
+ mnX = 0;
+ }
+ virtual const BitmapColor& ReadPixel() override
+ {
+ const BitmapColor& rColor = mrPalette[ (mpData[mnX >> 3 ] >> ( 7 - ( mnX & 7 ) )) & 1];
+ mnX++;
+ return rColor;
+ }
+};
+
+ImplPixelFormat* ImplPixelFormat::GetFormat( sal_uInt16 nBits, const BitmapPalette& rPalette )
+{
+ switch( nBits )
+ {
+ case 1: return new ImplPixelFormat1( rPalette );
+ case 4: return new ImplPixelFormat4( rPalette );
+ case 8: return new ImplPixelFormat8( rPalette );
+ }
+
+ return nullptr;
+}
+
+} // namespace
+
+std::unique_ptr< sal_uInt8[] > SalBitmap::convertDataBitCount( const sal_uInt8* src,
+ int width, int height, int bitCount, int bytesPerRow, const BitmapPalette& palette, BitConvert type )
+{
+ assert( bitCount == 1 || bitCount == 4 || bitCount == 8 );
+ static const int bpp[] = { 1, 3, 3, 4, 4 };
+ std::unique_ptr< sal_uInt8[] > data( new sal_uInt8[width * height * bpp[ static_cast<int>(type) ]] );
+
+ if(type == BitConvert::A8 && bitCount == 8 && palette.IsGreyPalette8Bit())
+ { // no actual data conversion
+ for( int y = 0; y < height; ++y )
+ memcpy( data.get() + y * width, src + y * bytesPerRow, width );
+ return data;
+ }
+
+ std::unique_ptr<ImplPixelFormat> pSrcFormat(ImplPixelFormat::GetFormat(bitCount, palette));
+
+ const sal_uInt8* pSrcData = src;
+ sal_uInt8* pDstData = data.get();
+
+ sal_uInt32 nY = height;
+ while( nY-- )
+ {
+ pSrcFormat->StartLine( pSrcData );
+
+ sal_uInt32 nX = width;
+ switch( type )
+ {
+ case BitConvert::A8 :
+ while( nX-- )
+ {
+ const BitmapColor& c = pSrcFormat->ReadPixel();
+ *pDstData++ = c.GetBlue();
+ }
+ break;
+ case BitConvert::BGR :
+ while( nX-- )
+ {
+ const BitmapColor& c = pSrcFormat->ReadPixel();
+ *pDstData++ = c.GetBlue();
+ *pDstData++ = c.GetGreen();
+ *pDstData++ = c.GetRed();
+ }
+ break;
+ case BitConvert::RGB :
+ while( nX-- )
+ {
+ const BitmapColor& c = pSrcFormat->ReadPixel();
+ *pDstData++ = c.GetRed();
+ *pDstData++ = c.GetGreen();
+ *pDstData++ = c.GetBlue();
+ }
+ break;
+ case BitConvert::BGRA :
+ while( nX-- )
+ {
+ const BitmapColor& c = pSrcFormat->ReadPixel();
+ *pDstData++ = c.GetBlue();
+ *pDstData++ = c.GetGreen();
+ *pDstData++ = c.GetRed();
+ *pDstData++ = 0xff;
+ }
+ break;
+ case BitConvert::RGBA :
+ while( nX-- )
+ {
+ const BitmapColor& c = pSrcFormat->ReadPixel();
+ *pDstData++ = c.GetRed();
+ *pDstData++ = c.GetGreen();
+ *pDstData++ = c.GetBlue();
+ *pDstData++ = 0xff;
+ }
+ break;
+ }
+
+ pSrcData += bytesPerRow;
+ }
+ return data;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */