diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:06:44 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:06:44 +0000 |
commit | ed5640d8b587fbcfed7dd7967f3de04b37a76f26 (patch) | |
tree | 7a5f7c6c9d02226d7471cb3cc8fbbf631b415303 /vcl/source/bitmap/BitmapColorQuantizationFilter.cxx | |
parent | Initial commit. (diff) | |
download | libreoffice-upstream.tar.xz libreoffice-upstream.zip |
Adding upstream version 4:7.4.7.upstream/4%7.4.7upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'vcl/source/bitmap/BitmapColorQuantizationFilter.cxx')
-rw-r--r-- | vcl/source/bitmap/BitmapColorQuantizationFilter.cxx | 224 |
1 files changed, 224 insertions, 0 deletions
diff --git a/vcl/source/bitmap/BitmapColorQuantizationFilter.cxx b/vcl/source/bitmap/BitmapColorQuantizationFilter.cxx new file mode 100644 index 000000000..45d39add3 --- /dev/null +++ b/vcl/source/bitmap/BitmapColorQuantizationFilter.cxx @@ -0,0 +1,224 @@ +/* -*- 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 <vcl/bitmap.hxx> +#include <vcl/bitmapex.hxx> +#include <vcl/BitmapColorQuantizationFilter.hxx> + +#include <bitmap/BitmapWriteAccess.hxx> + +#include <algorithm> +#include <cstdlib> + +BitmapEx BitmapColorQuantizationFilter::execute(BitmapEx const& aBitmapEx) const +{ + Bitmap aBitmap = aBitmapEx.GetBitmap(); + + bool bRet = false; + + if (vcl::numberOfColors(aBitmap.getPixelFormat()) <= sal_Int64(mnNewColorCount)) + { + bRet = true; + } + else + { + Bitmap::ScopedReadAccess pRAcc(aBitmap); + + auto const cappedNewColorCount = std::min(mnNewColorCount, sal_uInt16(256)); + + 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 sal_Int32 nWidth = pRAcc->Width(); + const sal_Int32 nHeight = pRAcc->Height(); + std::unique_ptr<PopularColorCount[]> pCountTable(new PopularColorCount[nTotalColors]); + + memset(pCountTable.get(), 0, nTotalColors * sizeof(PopularColorCount)); + + for (sal_Int32 nR = 0, nIndex = 0; nR < 256; nR += nColorOffset) + { + for (sal_Int32 nG = 0; nG < 256; nG += nColorOffset) + { + for (sal_Int32 nB = 0; nB < 256; nB += nColorOffset) + { + pCountTable[nIndex].mnIndex = nIndex; + nIndex++; + } + } + } + + if (pRAcc->HasPalette()) + { + for (sal_Int32 nY = 0; nY < nHeight; nY++) + { + Scanline pScanlineRead = pRAcc->GetScanline(nY); + for (sal_Int32 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 (sal_Int32 nY = 0; nY < nHeight; nY++) + { + Scanline pScanlineRead = pRAcc->GetScanline(nY); + for (sal_Int32 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(), vcl::PixelFormat::N8_BPP, &aNewPal); + BitmapScopedWriteAccess pWAcc(aNewBmp); + + if (pWAcc) + { + BitmapColor aDstCol(sal_uInt8(0)); + std::unique_ptr<sal_uInt8[]> pIndexMap(new sal_uInt8[nTotalColors]); + + for (sal_Int32 nR = 0, nIndex = 0; nR < 256; nR += nColorOffset) + { + for (sal_Int32 nG = 0; nG < 256; nG += nColorOffset) + { + for (sal_Int32 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 (sal_Int32 nY = 0; nY < nHeight; nY++) + { + Scanline pScanline = pWAcc->GetScanline(nY); + Scanline pScanlineRead = pRAcc->GetScanline(nY); + for (sal_Int32 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 (sal_Int32 nY = 0; nY < nHeight; nY++) + { + Scanline pScanline = pWAcc->GetScanline(nY); + Scanline pScanlineRead = pRAcc->GetScanline(nY); + + for (sal_Int32 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: */ |