/* -*- 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 #include #include #include #include #include #include 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 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(rCol.GetRed()) >> nRightShiftBits) << nLeftShiftBits2) | ((static_cast(rCol.GetGreen()) >> nRightShiftBits) << nLeftShiftBits1) | (static_cast(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(aCol.GetRed()) >> nRightShiftBits) << nLeftShiftBits2) | ((static_cast(aCol.GetGreen()) >> nRightShiftBits) << nLeftShiftBits1) | (static_cast(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(p1)->mnCount < static_cast(p2)->mnCount) nRet = 1; else if (static_cast(p1)->mnCount == static_cast(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((rPop.mnIndex >> nLeftShiftBits2) << nRightShiftBits), static_cast( ((rPop.mnIndex >> nLeftShiftBits1) & (nColorsPerComponent - 1)) << nRightShiftBits), static_cast((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 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(aNewPal.GetBestIndex( BitmapColor(static_cast(nR), static_cast(nG), static_cast(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(rCol.GetRed()) >> nRightShiftBits) << nLeftShiftBits2) | ((static_cast(rCol.GetGreen()) >> nRightShiftBits) << nLeftShiftBits1) | (static_cast(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(aCol.GetRed()) >> nRightShiftBits) << nLeftShiftBits2) | ((static_cast(aCol.GetGreen()) >> nRightShiftBits) << nLeftShiftBits1) | (static_cast(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: */