Version 6.4.0.0.beta1, tag libreoffice-6.4.0.0.beta1
[LibreOffice.git] / vcl / source / bitmap / BitmapScaleConvolutionFilter.cxx
blobb679e172d5544591732ebf72768a7b42906928e6
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <osl/diagnose.h>
22 #include <tools/helpers.hxx>
23 #include <vcl/bitmapaccess.hxx>
25 #include <bitmapwriteaccess.hxx>
26 #include <BitmapScaleConvolutionFilter.hxx>
28 #include <algorithm>
29 #include <memory>
31 namespace vcl
34 namespace
37 void ImplCalculateContributions(
38 const long aSourceSize,
39 const long aDestinationSize,
40 long& aNumberOfContributions,
41 std::vector<sal_Int16>& rWeights,
42 std::vector<sal_Int32>& rPixels,
43 std::vector<sal_Int32>& rCounts,
44 const Kernel& aKernel)
46 const double fSamplingRadius(aKernel.GetWidth());
47 const double fScale(aDestinationSize / static_cast< double >(aSourceSize));
48 const double fScaledRadius((fScale < 1.0) ? fSamplingRadius / fScale : fSamplingRadius);
49 const double fFilterFactor(std::min(fScale, 1.0));
51 aNumberOfContributions = (long(fabs(ceil(fScaledRadius))) * 2) + 1;
52 const long nAllocSize(aDestinationSize * aNumberOfContributions);
53 rWeights.resize(nAllocSize);
54 rPixels.resize(nAllocSize);
55 rCounts.resize(aDestinationSize);
57 for(long i(0); i < aDestinationSize; i++)
59 const long aIndex(i * aNumberOfContributions);
60 const double aCenter(i / fScale);
61 const sal_Int32 aLeft(static_cast< sal_Int32 >(floor(aCenter - fScaledRadius)));
62 const sal_Int32 aRight(static_cast< sal_Int32 >(ceil(aCenter + fScaledRadius)));
63 long aCurrentCount(0);
65 for(sal_Int32 j(aLeft); j <= aRight; j++)
67 const double aWeight(aKernel.Calculate(fFilterFactor * (aCenter - static_cast< double>(j))));
69 // Reduce calculations with ignoring weights of 0.0
70 if(fabs(aWeight) < 0.0001)
72 continue;
75 // Handling on edges
76 const long aPixelIndex(MinMax(j, 0, aSourceSize - 1));
77 const long nIndex(aIndex + aCurrentCount);
79 // scale the weight by 255 since we're converting from float to int
80 rWeights[nIndex] = aWeight * 255;
81 rPixels[nIndex] = aPixelIndex;
83 aCurrentCount++;
86 rCounts[i] = aCurrentCount;
90 bool ImplScaleConvolutionHor(Bitmap& rSource, Bitmap& rTarget, const double& rScaleX, const Kernel& aKernel)
92 // Do horizontal filtering
93 OSL_ENSURE(rScaleX > 0.0, "Error in scaling: Mirror given in non-mirror-capable method (!)");
94 const long nWidth(rSource.GetSizePixel().Width());
95 const long nNewWidth(FRound(nWidth * rScaleX));
97 if(nWidth == nNewWidth)
99 return true;
102 Bitmap::ScopedReadAccess pReadAcc(rSource);
104 if(pReadAcc)
106 std::vector<sal_Int16> aWeights;
107 std::vector<sal_Int32> aPixels;
108 std::vector<sal_Int32> aCounts;
109 long aNumberOfContributions(0);
111 const long nHeight(rSource.GetSizePixel().Height());
112 ImplCalculateContributions(nWidth, nNewWidth, aNumberOfContributions, aWeights, aPixels, aCounts, aKernel);
113 rTarget = Bitmap(Size(nNewWidth, nHeight), 24);
114 BitmapScopedWriteAccess pWriteAcc(rTarget);
115 bool bResult(pWriteAcc);
117 if(bResult)
119 for(long y(0); y < nHeight; y++)
121 Scanline pScanline = pWriteAcc->GetScanline( y );
122 Scanline pScanlineRead = pReadAcc->GetScanline( y );
123 for(long x(0); x < nNewWidth; x++)
125 const long aBaseIndex(x * aNumberOfContributions);
126 sal_Int32 aSum(0);
127 sal_Int32 aValueRed(0);
128 sal_Int32 aValueGreen(0);
129 sal_Int32 aValueBlue(0);
131 for(long j(0); j < aCounts[x]; j++)
133 const long aIndex(aBaseIndex + j);
134 const sal_Int16 aWeight(aWeights[aIndex]);
135 BitmapColor aColor;
137 aSum += aWeight;
139 if(pReadAcc->HasPalette())
141 aColor = pReadAcc->GetPaletteColor(pReadAcc->GetIndexFromData(pScanlineRead, aPixels[aIndex]));
143 else
145 aColor = pReadAcc->GetPixelFromData(pScanlineRead, aPixels[aIndex]);
148 aValueRed += aWeight * aColor.GetRed();
149 aValueGreen += aWeight * aColor.GetGreen();
150 aValueBlue += aWeight * aColor.GetBlue();
153 assert(aSum != 0);
155 const BitmapColor aResultColor(
156 static_cast< sal_uInt8 >(MinMax(static_cast< sal_Int32 >(aValueRed / aSum), 0, 255)),
157 static_cast< sal_uInt8 >(MinMax(static_cast< sal_Int32 >(aValueGreen / aSum), 0, 255)),
158 static_cast< sal_uInt8 >(MinMax(static_cast< sal_Int32 >(aValueBlue / aSum), 0, 255)));
160 pWriteAcc->SetPixelOnData(pScanline, x, aResultColor);
164 pWriteAcc.reset();
167 aWeights.clear();
168 aCounts.clear();
169 aPixels.clear();
171 if(bResult)
173 return true;
177 return false;
180 bool ImplScaleConvolutionVer(Bitmap& rSource, Bitmap& rTarget, const double& rScaleY, const Kernel& aKernel)
182 // Do vertical filtering
183 OSL_ENSURE(rScaleY > 0.0, "Error in scaling: Mirror given in non-mirror-capable method (!)");
184 const long nHeight(rSource.GetSizePixel().Height());
185 const long nNewHeight(FRound(nHeight * rScaleY));
187 if(nHeight == nNewHeight)
189 return true;
192 Bitmap::ScopedReadAccess pReadAcc(rSource);
194 if(pReadAcc)
196 std::vector<sal_Int16> aWeights;
197 std::vector<sal_Int32> aPixels;
198 std::vector<sal_Int32> aCounts;
199 long aNumberOfContributions(0);
201 const long nWidth(rSource.GetSizePixel().Width());
202 ImplCalculateContributions(nHeight, nNewHeight, aNumberOfContributions, aWeights, aPixels, aCounts, aKernel);
203 rTarget = Bitmap(Size(nWidth, nNewHeight), 24);
204 BitmapScopedWriteAccess pWriteAcc(rTarget);
205 bool bResult(pWriteAcc);
207 if(pWriteAcc)
209 std::vector<BitmapColor> aScanline(nHeight);
210 for(long x(0); x < nWidth; x++)
212 for(long y(0); y < nHeight; y++)
213 if(pReadAcc->HasPalette())
214 aScanline[y] = pReadAcc->GetPaletteColor(pReadAcc->GetPixelIndex(y, x));
215 else
216 aScanline[y] = pReadAcc->GetPixel(y, x);
217 for(long y(0); y < nNewHeight; y++)
219 const long aBaseIndex(y * aNumberOfContributions);
220 sal_Int32 aSum(0);
221 sal_Int32 aValueRed(0);
222 sal_Int32 aValueGreen(0);
223 sal_Int32 aValueBlue(0);
225 for(long j(0); j < aCounts[y]; j++)
227 const long aIndex(aBaseIndex + j);
228 const sal_Int16 aWeight(aWeights[aIndex]);
229 aSum += aWeight;
230 const BitmapColor & aColor = aScanline[aPixels[aIndex]];
231 aValueRed += aWeight * aColor.GetRed();
232 aValueGreen += aWeight * aColor.GetGreen();
233 aValueBlue += aWeight * aColor.GetBlue();
236 assert(aSum != 0);
238 const BitmapColor aResultColor(
239 static_cast< sal_uInt8 >(MinMax(static_cast< sal_Int32 >(aValueRed / aSum), 0, 255)),
240 static_cast< sal_uInt8 >(MinMax(static_cast< sal_Int32 >(aValueGreen / aSum), 0, 255)),
241 static_cast< sal_uInt8 >(MinMax(static_cast< sal_Int32 >(aValueBlue / aSum), 0, 255)));
243 if(pWriteAcc->HasPalette())
245 pWriteAcc->SetPixelIndex(y, x, static_cast< sal_uInt8 >(pWriteAcc->GetBestPaletteIndex(aResultColor)));
247 else
249 pWriteAcc->SetPixel(y, x, aResultColor);
255 aWeights.clear();
256 aCounts.clear();
257 aPixels.clear();
259 if(bResult)
261 return true;
265 return false;
268 bool ImplScaleConvolution(Bitmap& rBitmap, const double& rScaleX, const double& rScaleY, const Kernel& aKernel)
270 const bool bMirrorHor(rScaleX < 0.0);
271 const bool bMirrorVer(rScaleY < 0.0);
272 const double fScaleX(bMirrorHor ? -rScaleX : rScaleX);
273 const double fScaleY(bMirrorVer ? -rScaleY : rScaleY);
274 const long nWidth(rBitmap.GetSizePixel().Width());
275 const long nHeight(rBitmap.GetSizePixel().Height());
276 const long nNewWidth(FRound(nWidth * fScaleX));
277 const long nNewHeight(FRound(nHeight * fScaleY));
278 const bool bScaleHor(nWidth != nNewWidth);
279 const bool bScaleVer(nHeight != nNewHeight);
280 const bool bMirror(bMirrorHor || bMirrorVer);
282 if (!bMirror && !bScaleHor && !bScaleVer)
284 return true;
287 bool bResult(true);
288 BmpMirrorFlags nMirrorFlags(BmpMirrorFlags::NONE);
289 bool bMirrorAfter(false);
291 if (bMirror)
293 if(bMirrorHor)
295 nMirrorFlags |= BmpMirrorFlags::Horizontal;
298 if(bMirrorVer)
300 nMirrorFlags |= BmpMirrorFlags::Vertical;
303 const long nStartSize(nWidth * nHeight);
304 const long nEndSize(nNewWidth * nNewHeight);
306 bMirrorAfter = nStartSize > nEndSize;
308 if(!bMirrorAfter)
310 bResult = rBitmap.Mirror(nMirrorFlags);
314 Bitmap aResult;
316 if (bResult)
318 const long nInBetweenSizeHorFirst(nHeight * nNewWidth);
319 const long nInBetweenSizeVerFirst(nNewHeight * nWidth);
320 Bitmap aSource(rBitmap);
322 if(nInBetweenSizeHorFirst < nInBetweenSizeVerFirst)
324 if(bScaleHor)
326 bResult = ImplScaleConvolutionHor(aSource, aResult, fScaleX, aKernel);
329 if(bResult && bScaleVer)
331 if(bScaleHor)
333 // copy partial result, independent of color depth
334 aSource = aResult;
337 bResult = ImplScaleConvolutionVer(aSource, aResult, fScaleY, aKernel);
340 else
342 if(bScaleVer)
344 bResult = ImplScaleConvolutionVer(aSource, aResult, fScaleY, aKernel);
347 if(bResult && bScaleHor)
349 if(bScaleVer)
351 // copy partial result, independent of color depth
352 aSource = aResult;
355 bResult = ImplScaleConvolutionHor(aSource, aResult, fScaleX, aKernel);
360 if(bResult && bMirrorAfter)
362 bResult = aResult.Mirror(nMirrorFlags);
365 if(bResult)
367 rBitmap.AdaptBitCount(aResult);
368 rBitmap = aResult;
371 return bResult;
374 } // end anonymous namespace
376 BitmapEx BitmapScaleConvolutionFilter::execute(BitmapEx const& rBitmapEx) const
378 bool bRetval = false;
379 Bitmap aBitmap(rBitmapEx.GetBitmap());
381 bRetval = ImplScaleConvolution(aBitmap, mrScaleX, mrScaleY, *mxKernel);
383 if (bRetval)
384 return BitmapEx(aBitmap);
386 return BitmapEx();
391 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */