LibreOffice Module vcl (master)  1
BitmapScaleConvolutionFilter.cxx
Go to the documentation of this file.
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
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/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
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 .
18  */
19 
20 #include <osl/diagnose.h>
21 #include <tools/helpers.hxx>
22 
25 
26 #include <algorithm>
27 #include <memory>
28 
29 namespace vcl
30 {
31 
32 namespace
33 {
34 
35 void ImplCalculateContributions(
36  const tools::Long aSourceSize,
37  const tools::Long aDestinationSize,
38  tools::Long& aNumberOfContributions,
39  std::vector<sal_Int16>& rWeights,
40  std::vector<sal_Int32>& rPixels,
41  std::vector<sal_Int32>& rCounts,
42  const Kernel& aKernel)
43 {
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(std::min(fScale, 1.0));
48 
49  aNumberOfContributions = (tools::Long(fabs(ceil(fScaledRadius))) * 2) + 1;
50  const tools::Long nAllocSize(aDestinationSize * aNumberOfContributions);
51  rWeights.resize(nAllocSize);
52  rPixels.resize(nAllocSize);
53  rCounts.resize(aDestinationSize);
54 
55  for(tools::Long i(0); i < aDestinationSize; i++)
56  {
57  const tools::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  tools::Long aCurrentCount(0);
62 
63  for(sal_Int32 j(aLeft); j <= aRight; j++)
64  {
65  const double aWeight(aKernel.Calculate(fFilterFactor * (aCenter - static_cast< double>(j))));
66 
67  // Reduce calculations with ignoring weights of 0.0
68  if(fabs(aWeight) < 0.0001)
69  {
70  continue;
71  }
72 
73  // Handling on edges
74  const tools::Long aPixelIndex(MinMax(j, 0, aSourceSize - 1));
75  const tools::Long nIndex(aIndex + aCurrentCount);
76 
77  // scale the weight by 255 since we're converting from float to int
78  rWeights[nIndex] = aWeight * 255;
79  rPixels[nIndex] = aPixelIndex;
80 
81  aCurrentCount++;
82  }
83 
84  rCounts[i] = aCurrentCount;
85  }
86 }
87 
88 bool ImplScaleConvolutionHor(Bitmap& rSource, Bitmap& rTarget, const double& rScaleX, const Kernel& aKernel)
89 {
90  // Do horizontal filtering
91  OSL_ENSURE(rScaleX > 0.0, "Error in scaling: Mirror given in non-mirror-capable method (!)");
92  const tools::Long nWidth(rSource.GetSizePixel().Width());
93  const tools::Long nNewWidth(FRound(nWidth * rScaleX));
94 
95  if(nWidth == nNewWidth)
96  {
97  return true;
98  }
99 
100  Bitmap::ScopedReadAccess pReadAcc(rSource);
101 
102  if(pReadAcc)
103  {
104  std::vector<sal_Int16> aWeights;
105  std::vector<sal_Int32> aPixels;
106  std::vector<sal_Int32> aCounts;
107  tools::Long aNumberOfContributions(0);
108 
109  const tools::Long nHeight(rSource.GetSizePixel().Height());
110  ImplCalculateContributions(nWidth, nNewWidth, aNumberOfContributions, aWeights, aPixels, aCounts, aKernel);
111  rTarget = Bitmap(Size(nNewWidth, nHeight), vcl::PixelFormat::N24_BPP);
112  BitmapScopedWriteAccess pWriteAcc(rTarget);
113  bool bResult(pWriteAcc);
114 
115  if(bResult)
116  {
117  for(tools::Long y(0); y < nHeight; y++)
118  {
119  Scanline pScanline = pWriteAcc->GetScanline( y );
120  Scanline pScanlineRead = pReadAcc->GetScanline( y );
121  for(tools::Long x(0); x < nNewWidth; x++)
122  {
123  const tools::Long aBaseIndex(x * aNumberOfContributions);
124  sal_Int32 aSum(0);
125  sal_Int32 aValueRed(0);
126  sal_Int32 aValueGreen(0);
127  sal_Int32 aValueBlue(0);
128 
129  for(tools::Long j(0); j < aCounts[x]; j++)
130  {
131  const tools::Long aIndex(aBaseIndex + j);
132  const sal_Int16 aWeight(aWeights[aIndex]);
133  BitmapColor aColor;
134 
135  aSum += aWeight;
136 
137  if(pReadAcc->HasPalette())
138  {
139  aColor = pReadAcc->GetPaletteColor(pReadAcc->GetIndexFromData(pScanlineRead, aPixels[aIndex]));
140  }
141  else
142  {
143  aColor = pReadAcc->GetPixelFromData(pScanlineRead, aPixels[aIndex]);
144  }
145 
146  aValueRed += aWeight * aColor.GetRed();
147  aValueGreen += aWeight * aColor.GetGreen();
148  aValueBlue += aWeight * aColor.GetBlue();
149  }
150 
151  assert(aSum != 0);
152 
153  const BitmapColor aResultColor(
154  static_cast< sal_uInt8 >(MinMax(static_cast< sal_Int32 >(aValueRed / aSum), 0, 255)),
155  static_cast< sal_uInt8 >(MinMax(static_cast< sal_Int32 >(aValueGreen / aSum), 0, 255)),
156  static_cast< sal_uInt8 >(MinMax(static_cast< sal_Int32 >(aValueBlue / aSum), 0, 255)));
157 
158  pWriteAcc->SetPixelOnData(pScanline, x, aResultColor);
159  }
160  }
161 
162  pWriteAcc.reset();
163  }
164 
165  aWeights.clear();
166  aCounts.clear();
167  aPixels.clear();
168 
169  if(bResult)
170  {
171  return true;
172  }
173  }
174 
175  return false;
176 }
177 
178 bool ImplScaleConvolutionVer(Bitmap& rSource, Bitmap& rTarget, const double& rScaleY, const Kernel& aKernel)
179 {
180  // Do vertical filtering
181  OSL_ENSURE(rScaleY > 0.0, "Error in scaling: Mirror given in non-mirror-capable method (!)");
182  const tools::Long nHeight(rSource.GetSizePixel().Height());
183  const tools::Long nNewHeight(FRound(nHeight * rScaleY));
184 
185  if(nHeight == nNewHeight)
186  {
187  return true;
188  }
189 
190  Bitmap::ScopedReadAccess pReadAcc(rSource);
191 
192  if(pReadAcc)
193  {
194  std::vector<sal_Int16> aWeights;
195  std::vector<sal_Int32> aPixels;
196  std::vector<sal_Int32> aCounts;
197  tools::Long aNumberOfContributions(0);
198 
199  const tools::Long nWidth(rSource.GetSizePixel().Width());
200  ImplCalculateContributions(nHeight, nNewHeight, aNumberOfContributions, aWeights, aPixels, aCounts, aKernel);
201  rTarget = Bitmap(Size(nWidth, nNewHeight), vcl::PixelFormat::N24_BPP);
202  BitmapScopedWriteAccess pWriteAcc(rTarget);
203  bool bResult(pWriteAcc);
204 
205  if(pWriteAcc)
206  {
207  std::vector<BitmapColor> aScanline(nHeight);
208  for(tools::Long x(0); x < nWidth; x++)
209  {
210  for(tools::Long y(0); y < nHeight; y++)
211  if(pReadAcc->HasPalette())
212  aScanline[y] = pReadAcc->GetPaletteColor(pReadAcc->GetPixelIndex(y, x));
213  else
214  aScanline[y] = pReadAcc->GetPixel(y, x);
215  for(tools::Long y(0); y < nNewHeight; y++)
216  {
217  const tools::Long aBaseIndex(y * aNumberOfContributions);
218  sal_Int32 aSum(0);
219  sal_Int32 aValueRed(0);
220  sal_Int32 aValueGreen(0);
221  sal_Int32 aValueBlue(0);
222 
223  for(tools::Long j(0); j < aCounts[y]; j++)
224  {
225  const tools::Long aIndex(aBaseIndex + j);
226  const sal_Int16 aWeight(aWeights[aIndex]);
227  aSum += aWeight;
228  const BitmapColor & aColor = aScanline[aPixels[aIndex]];
229  aValueRed += aWeight * aColor.GetRed();
230  aValueGreen += aWeight * aColor.GetGreen();
231  aValueBlue += aWeight * aColor.GetBlue();
232  }
233 
234  assert(aSum != 0);
235 
236  const BitmapColor aResultColor(
237  static_cast< sal_uInt8 >(MinMax(static_cast< sal_Int32 >(aValueRed / aSum), 0, 255)),
238  static_cast< sal_uInt8 >(MinMax(static_cast< sal_Int32 >(aValueGreen / aSum), 0, 255)),
239  static_cast< sal_uInt8 >(MinMax(static_cast< sal_Int32 >(aValueBlue / aSum), 0, 255)));
240 
241  if(pWriteAcc->HasPalette())
242  {
243  pWriteAcc->SetPixelIndex(y, x, static_cast< sal_uInt8 >(pWriteAcc->GetBestPaletteIndex(aResultColor)));
244  }
245  else
246  {
247  pWriteAcc->SetPixel(y, x, aResultColor);
248  }
249  }
250  }
251  }
252 
253  aWeights.clear();
254  aCounts.clear();
255  aPixels.clear();
256 
257  if(bResult)
258  {
259  return true;
260  }
261  }
262 
263  return false;
264 }
265 
266 bool ImplScaleConvolution(Bitmap& rBitmap, const double& rScaleX, const double& rScaleY, const Kernel& aKernel)
267 {
268  const bool bMirrorHor(rScaleX < 0.0);
269  const bool bMirrorVer(rScaleY < 0.0);
270  const double fScaleX(bMirrorHor ? -rScaleX : rScaleX);
271  const double fScaleY(bMirrorVer ? -rScaleY : rScaleY);
272  const tools::Long nWidth(rBitmap.GetSizePixel().Width());
273  const tools::Long nHeight(rBitmap.GetSizePixel().Height());
274  const tools::Long nNewWidth(FRound(nWidth * fScaleX));
275  const tools::Long nNewHeight(FRound(nHeight * fScaleY));
276  const bool bScaleHor(nWidth != nNewWidth);
277  const bool bScaleVer(nHeight != nNewHeight);
278  const bool bMirror(bMirrorHor || bMirrorVer);
279 
280  if (!bMirror && !bScaleHor && !bScaleVer)
281  {
282  return true;
283  }
284 
285  bool bResult(true);
286  BmpMirrorFlags nMirrorFlags(BmpMirrorFlags::NONE);
287  bool bMirrorAfter(false);
288 
289  if (bMirror)
290  {
291  if(bMirrorHor)
292  {
293  nMirrorFlags |= BmpMirrorFlags::Horizontal;
294  }
295 
296  if(bMirrorVer)
297  {
298  nMirrorFlags |= BmpMirrorFlags::Vertical;
299  }
300 
301  const tools::Long nStartSize(nWidth * nHeight);
302  const tools::Long nEndSize(nNewWidth * nNewHeight);
303 
304  bMirrorAfter = nStartSize > nEndSize;
305 
306  if(!bMirrorAfter)
307  {
308  bResult = rBitmap.Mirror(nMirrorFlags);
309  }
310  }
311 
312  Bitmap aResult;
313 
314  if (bResult)
315  {
316  const tools::Long nInBetweenSizeHorFirst(nHeight * nNewWidth);
317  const tools::Long nInBetweenSizeVerFirst(nNewHeight * nWidth);
318  Bitmap aSource(rBitmap);
319 
320  if(nInBetweenSizeHorFirst < nInBetweenSizeVerFirst)
321  {
322  if(bScaleHor)
323  {
324  bResult = ImplScaleConvolutionHor(aSource, aResult, fScaleX, aKernel);
325  }
326 
327  if(bResult && bScaleVer)
328  {
329  if(bScaleHor)
330  {
331  // copy partial result, independent of color depth
332  aSource = aResult;
333  }
334 
335  bResult = ImplScaleConvolutionVer(aSource, aResult, fScaleY, aKernel);
336  }
337  }
338  else
339  {
340  if(bScaleVer)
341  {
342  bResult = ImplScaleConvolutionVer(aSource, aResult, fScaleY, aKernel);
343  }
344 
345  if(bResult && bScaleHor)
346  {
347  if(bScaleVer)
348  {
349  // copy partial result, independent of color depth
350  aSource = aResult;
351  }
352 
353  bResult = ImplScaleConvolutionHor(aSource, aResult, fScaleX, aKernel);
354  }
355  }
356  }
357 
358  if(bResult && bMirrorAfter)
359  {
360  bResult = aResult.Mirror(nMirrorFlags);
361  }
362 
363  if(bResult)
364  {
365  rBitmap.AdaptBitCount(aResult);
366  rBitmap = aResult;
367  }
368 
369  return bResult;
370 }
371 
372 } // end anonymous namespace
373 
375 {
376  bool bRetval = false;
377  Bitmap aBitmap(rBitmapEx.GetBitmap());
378 
379  bRetval = ImplScaleConvolution(aBitmap, mrScaleX, mrScaleY, *mxKernel);
380 
381  if (bRetval)
382  return BitmapEx(aBitmap);
383 
384  return BitmapEx();
385 }
386 
387 }
388 
389 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
sal_Int32 nIndex
sal_uInt8 GetRed() const
long Long
std::enable_if< std::is_signed< T >::value||std::is_floating_point< T >::value, long >::type MinMax(T nVal, tools::Long nMin, tools::Long nMax)
Size GetSizePixel() const
float x
bool Mirror(BmpMirrorFlags nMirrorFlags)
Mirror the bitmap.
const BorderLinePrimitive2D *pCandidateB assert(pCandidateA)
sal_uInt8 GetBlue() const
virtual BitmapEx execute(BitmapEx const &rBitmap) const override
float y
sal_uInt8 * Scanline
Definition: Scanline.hxx:25
int i
tools::Long FRound(double fVal)
tools::Long Width() const
BmpMirrorFlags
Definition: bitmap.hxx:42
Bitmap GetBitmap(Color aTransparentReplaceColor) const
Definition: BitmapEx.cxx:229
sal_uInt8 GetGreen() const
std::deque< AttacherIndex_Impl > aIndex
tools::Long Height() const
void AdaptBitCount(Bitmap &rNew) const