build fix: no comphelper/profilezone.hxx in this branch
[LibreOffice.git] / vcl / source / bitmap / BitmapScaleConvolution.cxx
blob6149573953f496576dd686c521e4cfdd88bfe16e
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 "BitmapScaleConvolution.hxx"
21 #include "ResampleKernel.hxx"
23 #include <vcl/bitmapaccess.hxx>
24 #include <osl/diagnose.h>
26 #include <algorithm>
27 #include <memory>
29 namespace vcl
32 namespace
35 void ImplCalculateContributions(
36 const long aSourceSize,
37 const long aDestinationSize,
38 long& aNumberOfContributions,
39 double*& pWeights,
40 long*& pPixels,
41 long*& pCount,
42 const Kernel& aKernel)
44 const double fSamplingRadius(aKernel.GetWidth());
45 const double fScale(aDestinationSize / static_cast< double >(aSourceSize));
46 const double fScaledRadius((fScale < 1.0) ? fSamplingRadius / fScale : fSamplingRadius);
47 const double fFilterFactor((fScale < 1.0) ? fScale : 1.0);
49 aNumberOfContributions = (long(fabs(ceil(fScaledRadius))) * 2) + 1;
50 const long nAllocSize(aDestinationSize * aNumberOfContributions);
51 pWeights = new double[nAllocSize];
52 pPixels = new long[nAllocSize];
53 pCount = new long[aDestinationSize];
55 for(long i(0); i < aDestinationSize; i++)
57 const long aIndex(i * aNumberOfContributions);
58 const double aCenter(i / fScale);
59 const sal_Int32 aLeft(static_cast< sal_Int32 >(floor(aCenter - fScaledRadius)));
60 const sal_Int32 aRight(static_cast< sal_Int32 >(ceil(aCenter + fScaledRadius)));
61 long aCurrentCount(0);
63 for(sal_Int32 j(aLeft); j <= aRight; j++)
65 const double aWeight(aKernel.Calculate(fFilterFactor * (aCenter - static_cast< double>(j))));
67 // Reduce calculations with ignoring weights of 0.0
68 if(fabs(aWeight) < 0.0001)
70 continue;
73 // Handling on edges
74 const long aPixelIndex(MinMax(j, 0, aSourceSize - 1));
75 const long nIndex(aIndex + aCurrentCount);
77 pWeights[nIndex] = aWeight;
78 pPixels[nIndex] = aPixelIndex;
80 aCurrentCount++;
83 pCount[i] = aCurrentCount;
87 bool ImplScaleConvolutionHor(Bitmap& rSource, Bitmap& rTarget, const double& rScaleX, const Kernel& aKernel)
89 // Do horizontal filtering
90 OSL_ENSURE(rScaleX > 0.0, "Error in scaling: Mirror given in non-mirror-capable method (!)");
91 const long nWidth(rSource.GetSizePixel().Width());
92 const long nNewWidth(FRound(nWidth * rScaleX));
94 if(nWidth == nNewWidth)
96 return true;
99 Bitmap::ScopedReadAccess pReadAcc(rSource);
101 if(pReadAcc)
103 double* pWeights = nullptr;
104 long* pPixels = nullptr;
105 long* pCount = nullptr;
106 long aNumberOfContributions(0);
108 const long nHeight(rSource.GetSizePixel().Height());
109 ImplCalculateContributions(nWidth, nNewWidth, aNumberOfContributions, pWeights, pPixels, pCount, aKernel);
110 rTarget = Bitmap(Size(nNewWidth, nHeight), 24);
111 Bitmap::ScopedWriteAccess pWriteAcc(rTarget);
112 bool bResult(nullptr != pWriteAcc);
114 if(bResult)
116 for(long y(0); y < nHeight; y++)
118 for(long x(0); x < nNewWidth; x++)
120 const long aBaseIndex(x * aNumberOfContributions);
121 double aSum(0.0);
122 double aValueRed(0.0);
123 double aValueGreen(0.0);
124 double aValueBlue(0.0);
126 for(long j(0); j < pCount[x]; j++)
128 const long aIndex(aBaseIndex + j);
129 const double aWeight(pWeights[aIndex]);
130 BitmapColor aColor;
132 aSum += aWeight;
134 if(pReadAcc->HasPalette())
136 aColor = pReadAcc->GetPaletteColor(pReadAcc->GetPixelIndex(y, pPixels[aIndex]));
138 else
140 aColor = pReadAcc->GetPixel(y, pPixels[aIndex]);
143 aValueRed += aWeight * aColor.GetRed();
144 aValueGreen += aWeight * aColor.GetGreen();
145 aValueBlue += aWeight * aColor.GetBlue();
148 const BitmapColor aResultColor(
149 static_cast< sal_uInt8 >(MinMax(static_cast< sal_Int32 >(aValueRed / aSum), 0, 255)),
150 static_cast< sal_uInt8 >(MinMax(static_cast< sal_Int32 >(aValueGreen / aSum), 0, 255)),
151 static_cast< sal_uInt8 >(MinMax(static_cast< sal_Int32 >(aValueBlue / aSum), 0, 255)));
153 pWriteAcc->SetPixel(y, x, aResultColor);
157 pWriteAcc.reset();
160 delete[] pWeights;
161 delete[] pCount;
162 delete[] pPixels;
164 if(bResult)
166 return true;
170 return false;
173 bool ImplScaleConvolutionVer(Bitmap& rSource, Bitmap& rTarget, const double& rScaleY, const Kernel& aKernel)
175 // Do vertical filtering
176 OSL_ENSURE(rScaleY > 0.0, "Error in scaling: Mirror given in non-mirror-capable method (!)");
177 const long nHeight(rSource.GetSizePixel().Height());
178 const long nNewHeight(FRound(nHeight * rScaleY));
180 if(nHeight == nNewHeight)
182 return true;
185 Bitmap::ScopedReadAccess pReadAcc(rSource);
187 if(pReadAcc)
189 double* pWeights = nullptr;
190 long* pPixels = nullptr;
191 long* pCount = nullptr;
192 long aNumberOfContributions(0);
194 const long nWidth(rSource.GetSizePixel().Width());
195 ImplCalculateContributions(nHeight, nNewHeight, aNumberOfContributions, pWeights, pPixels, pCount, aKernel);
196 rTarget = Bitmap(Size(nWidth, nNewHeight), 24);
197 Bitmap::ScopedWriteAccess pWriteAcc(rTarget);
198 bool bResult(nullptr != pWriteAcc);
200 if(pWriteAcc)
202 for(long x(0); x < nWidth; x++)
204 for(long y(0); y < nNewHeight; y++)
206 const long aBaseIndex(y * aNumberOfContributions);
207 double aSum(0.0);
208 double aValueRed(0.0);
209 double aValueGreen(0.0);
210 double aValueBlue(0.0);
212 for(long j(0); j < pCount[y]; j++)
214 const long aIndex(aBaseIndex + j);
215 const double aWeight(pWeights[aIndex]);
216 BitmapColor aColor;
218 aSum += aWeight;
220 if(pReadAcc->HasPalette())
222 aColor = pReadAcc->GetPaletteColor(pReadAcc->GetPixelIndex(pPixels[aIndex], x));
224 else
226 aColor = pReadAcc->GetPixel(pPixels[aIndex], x);
229 aValueRed += aWeight * aColor.GetRed();
230 aValueGreen += aWeight * aColor.GetGreen();
231 aValueBlue += aWeight * aColor.GetBlue();
234 const BitmapColor aResultColor(
235 static_cast< sal_uInt8 >(MinMax(static_cast< sal_Int32 >(aValueRed / aSum), 0, 255)),
236 static_cast< sal_uInt8 >(MinMax(static_cast< sal_Int32 >(aValueGreen / aSum), 0, 255)),
237 static_cast< sal_uInt8 >(MinMax(static_cast< sal_Int32 >(aValueBlue / aSum), 0, 255)));
239 if(pWriteAcc->HasPalette())
241 pWriteAcc->SetPixelIndex(y, x, static_cast< sal_uInt8 >(pWriteAcc->GetBestPaletteIndex(aResultColor)));
243 else
245 pWriteAcc->SetPixel(y, x, aResultColor);
251 delete[] pWeights;
252 delete[] pCount;
253 delete[] pPixels;
255 if(bResult)
257 return true;
261 return false;
264 bool ImplScaleConvolution(Bitmap& rBitmap, const double& rScaleX, const double& rScaleY, const Kernel& aKernel)
266 const bool bMirrorHor(rScaleX < 0.0);
267 const bool bMirrorVer(rScaleY < 0.0);
268 const double fScaleX(bMirrorHor ? -rScaleX : rScaleX);
269 const double fScaleY(bMirrorVer ? -rScaleY : rScaleY);
270 const long nWidth(rBitmap.GetSizePixel().Width());
271 const long nHeight(rBitmap.GetSizePixel().Height());
272 const long nNewWidth(FRound(nWidth * fScaleX));
273 const long nNewHeight(FRound(nHeight * fScaleY));
274 const bool bScaleHor(nWidth != nNewWidth);
275 const bool bScaleVer(nHeight != nNewHeight);
276 const bool bMirror(bMirrorHor || bMirrorVer);
278 if (!bMirror && !bScaleHor && !bScaleVer)
280 return true;
283 bool bResult(true);
284 BmpMirrorFlags nMirrorFlags(BmpMirrorFlags::NONE);
285 bool bMirrorAfter(false);
287 if (bMirror)
289 if(bMirrorHor)
291 nMirrorFlags |= BmpMirrorFlags::Horizontal;
294 if(bMirrorVer)
296 nMirrorFlags |= BmpMirrorFlags::Vertical;
299 const long nStartSize(nWidth * nHeight);
300 const long nEndSize(nNewWidth * nNewHeight);
302 bMirrorAfter = nStartSize > nEndSize;
304 if(!bMirrorAfter)
306 bResult = rBitmap.Mirror(nMirrorFlags);
310 Bitmap aResult;
312 if (bResult)
314 const long nInBetweenSizeHorFirst(nHeight * nNewWidth);
315 const long nInBetweenSizeVerFirst(nNewHeight * nWidth);
316 Bitmap aSource(rBitmap);
318 if(nInBetweenSizeHorFirst < nInBetweenSizeVerFirst)
320 if(bScaleHor)
322 bResult = ImplScaleConvolutionHor(aSource, aResult, fScaleX, aKernel);
325 if(bResult && bScaleVer)
327 if(bScaleHor)
329 // copy partial result, independent of color depth
330 aSource = aResult;
333 bResult = ImplScaleConvolutionVer(aSource, aResult, fScaleY, aKernel);
336 else
338 if(bScaleVer)
340 bResult = ImplScaleConvolutionVer(aSource, aResult, fScaleY, aKernel);
343 if(bResult && bScaleHor)
345 if(bScaleVer)
347 // copy partial result, independent of color depth
348 aSource = aResult;
351 bResult = ImplScaleConvolutionHor(aSource, aResult, fScaleX, aKernel);
356 if(bResult && bMirrorAfter)
358 bResult = aResult.Mirror(nMirrorFlags);
361 if(bResult)
363 rBitmap.ImplAdaptBitCount(aResult);
364 rBitmap = aResult;
367 return bResult;
370 } // end anonymous namespace
372 bool BitmapScaleConvolution::filter(Bitmap& rBitmap)
375 switch(meKernelType)
377 case ConvolutionKernelType::Box:
378 return ImplScaleConvolution(rBitmap, mrScaleX, mrScaleY, BoxKernel());
379 case ConvolutionKernelType::BiLinear:
380 return ImplScaleConvolution(rBitmap, mrScaleX, mrScaleY, BilinearKernel());
381 case ConvolutionKernelType::BiCubic:
382 return ImplScaleConvolution(rBitmap, mrScaleX, mrScaleY, BicubicKernel());
383 case ConvolutionKernelType::Lanczos3:
384 return ImplScaleConvolution(rBitmap, mrScaleX, mrScaleY, Lanczos3Kernel());
385 default:
386 break;
388 return false;
393 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */