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 
22 #include <tools/helpers.hxx>
23 #include <vcl/bitmapaccess.hxx>
24 
25 #include <bitmapwriteaccess.hxx>
27 
28 #include <algorithm>
29 #include <memory>
30 
31 namespace vcl
32 {
33 
34 namespace
35 {
36 
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)
45 {
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));
50 
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);
56 
57  for(long i(0); i < aDestinationSize; i++)
58  {
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);
64 
65  for(sal_Int32 j(aLeft); j <= aRight; j++)
66  {
67  const double aWeight(aKernel.Calculate(fFilterFactor * (aCenter - static_cast< double>(j))));
68 
69  // Reduce calculations with ignoring weights of 0.0
70  if(fabs(aWeight) < 0.0001)
71  {
72  continue;
73  }
74 
75  // Handling on edges
76  const long aPixelIndex(MinMax(j, 0, aSourceSize - 1));
77  const long nIndex(aIndex + aCurrentCount);
78 
79  // scale the weight by 255 since we're converting from float to int
80  rWeights[nIndex] = aWeight * 255;
81  rPixels[nIndex] = aPixelIndex;
82 
83  aCurrentCount++;
84  }
85 
86  rCounts[i] = aCurrentCount;
87  }
88 }
89 
90 bool ImplScaleConvolutionHor(Bitmap& rSource, Bitmap& rTarget, const double& rScaleX, const Kernel& aKernel)
91 {
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));
96 
97  if(nWidth == nNewWidth)
98  {
99  return true;
100  }
101 
102  Bitmap::ScopedReadAccess pReadAcc(rSource);
103 
104  if(pReadAcc)
105  {
106  std::vector<sal_Int16> aWeights;
107  std::vector<sal_Int32> aPixels;
108  std::vector<sal_Int32> aCounts;
109  long aNumberOfContributions(0);
110 
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);
116 
117  if(bResult)
118  {
119  for(long y(0); y < nHeight; y++)
120  {
121  Scanline pScanline = pWriteAcc->GetScanline( y );
122  Scanline pScanlineRead = pReadAcc->GetScanline( y );
123  for(long x(0); x < nNewWidth; x++)
124  {
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);
130 
131  for(long j(0); j < aCounts[x]; j++)
132  {
133  const long aIndex(aBaseIndex + j);
134  const sal_Int16 aWeight(aWeights[aIndex]);
135  BitmapColor aColor;
136 
137  aSum += aWeight;
138 
139  if(pReadAcc->HasPalette())
140  {
141  aColor = pReadAcc->GetPaletteColor(pReadAcc->GetIndexFromData(pScanlineRead, aPixels[aIndex]));
142  }
143  else
144  {
145  aColor = pReadAcc->GetPixelFromData(pScanlineRead, aPixels[aIndex]);
146  }
147 
148  aValueRed += aWeight * aColor.GetRed();
149  aValueGreen += aWeight * aColor.GetGreen();
150  aValueBlue += aWeight * aColor.GetBlue();
151  }
152 
153  assert(aSum != 0);
154 
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)));
159 
160  pWriteAcc->SetPixelOnData(pScanline, x, aResultColor);
161  }
162  }
163 
164  pWriteAcc.reset();
165  }
166 
167  aWeights.clear();
168  aCounts.clear();
169  aPixels.clear();
170 
171  if(bResult)
172  {
173  return true;
174  }
175  }
176 
177  return false;
178 }
179 
180 bool ImplScaleConvolutionVer(Bitmap& rSource, Bitmap& rTarget, const double& rScaleY, const Kernel& aKernel)
181 {
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));
186 
187  if(nHeight == nNewHeight)
188  {
189  return true;
190  }
191 
192  Bitmap::ScopedReadAccess pReadAcc(rSource);
193 
194  if(pReadAcc)
195  {
196  std::vector<sal_Int16> aWeights;
197  std::vector<sal_Int32> aPixels;
198  std::vector<sal_Int32> aCounts;
199  long aNumberOfContributions(0);
200 
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);
206 
207  if(pWriteAcc)
208  {
209  std::vector<BitmapColor> aScanline(nHeight);
210  for(long x(0); x < nWidth; x++)
211  {
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++)
218  {
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);
224 
225  for(long j(0); j < aCounts[y]; j++)
226  {
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();
234  }
235 
236  assert(aSum != 0);
237 
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)));
242 
243  if(pWriteAcc->HasPalette())
244  {
245  pWriteAcc->SetPixelIndex(y, x, static_cast< sal_uInt8 >(pWriteAcc->GetBestPaletteIndex(aResultColor)));
246  }
247  else
248  {
249  pWriteAcc->SetPixel(y, x, aResultColor);
250  }
251  }
252  }
253  }
254 
255  aWeights.clear();
256  aCounts.clear();
257  aPixels.clear();
258 
259  if(bResult)
260  {
261  return true;
262  }
263  }
264 
265  return false;
266 }
267 
268 bool ImplScaleConvolution(Bitmap& rBitmap, const double& rScaleX, const double& rScaleY, const Kernel& aKernel)
269 {
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);
281 
282  if (!bMirror && !bScaleHor && !bScaleVer)
283  {
284  return true;
285  }
286 
287  bool bResult(true);
288  BmpMirrorFlags nMirrorFlags(BmpMirrorFlags::NONE);
289  bool bMirrorAfter(false);
290 
291  if (bMirror)
292  {
293  if(bMirrorHor)
294  {
295  nMirrorFlags |= BmpMirrorFlags::Horizontal;
296  }
297 
298  if(bMirrorVer)
299  {
300  nMirrorFlags |= BmpMirrorFlags::Vertical;
301  }
302 
303  const long nStartSize(nWidth * nHeight);
304  const long nEndSize(nNewWidth * nNewHeight);
305 
306  bMirrorAfter = nStartSize > nEndSize;
307 
308  if(!bMirrorAfter)
309  {
310  bResult = rBitmap.Mirror(nMirrorFlags);
311  }
312  }
313 
314  Bitmap aResult;
315 
316  if (bResult)
317  {
318  const long nInBetweenSizeHorFirst(nHeight * nNewWidth);
319  const long nInBetweenSizeVerFirst(nNewHeight * nWidth);
320  Bitmap aSource(rBitmap);
321 
322  if(nInBetweenSizeHorFirst < nInBetweenSizeVerFirst)
323  {
324  if(bScaleHor)
325  {
326  bResult = ImplScaleConvolutionHor(aSource, aResult, fScaleX, aKernel);
327  }
328 
329  if(bResult && bScaleVer)
330  {
331  if(bScaleHor)
332  {
333  // copy partial result, independent of color depth
334  aSource = aResult;
335  }
336 
337  bResult = ImplScaleConvolutionVer(aSource, aResult, fScaleY, aKernel);
338  }
339  }
340  else
341  {
342  if(bScaleVer)
343  {
344  bResult = ImplScaleConvolutionVer(aSource, aResult, fScaleY, aKernel);
345  }
346 
347  if(bResult && bScaleHor)
348  {
349  if(bScaleVer)
350  {
351  // copy partial result, independent of color depth
352  aSource = aResult;
353  }
354 
355  bResult = ImplScaleConvolutionHor(aSource, aResult, fScaleX, aKernel);
356  }
357  }
358  }
359 
360  if(bResult && bMirrorAfter)
361  {
362  bResult = aResult.Mirror(nMirrorFlags);
363  }
364 
365  if(bResult)
366  {
367  rBitmap.AdaptBitCount(aResult);
368  rBitmap = aResult;
369  }
370 
371  return bResult;
372 }
373 
374 } // end anonymous namespace
375 
377 {
378  bool bRetval = false;
379  Bitmap aBitmap(rBitmapEx.GetBitmap());
380 
381  bRetval = ImplScaleConvolution(aBitmap, mrScaleX, mrScaleY, *mxKernel);
382 
383  if (bRetval)
384  return BitmapEx(aBitmap);
385 
386  return BitmapEx();
387 }
388 
389 }
390 
391 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
long Width() const
sal_uInt8 GetRed() const
long FRound(double fVal)
long Height() const
Size GetSizePixel() const
float x
bool Mirror(BmpMirrorFlags nMirrorFlags)
Mirror the bitmap.
sal_uInt8 GetBlue() const
virtual BitmapEx execute(BitmapEx const &rBitmap) const override
float y
Bitmap GetBitmap(const Color *pTransReplaceColor=nullptr) const
Definition: bitmapex.cxx:236
sal_uInt8 * Scanline
Definition: Scanline.hxx:25
int i
BmpMirrorFlags
Definition: bitmap.hxx:36
sal_uInt8 GetGreen() const
void AdaptBitCount(Bitmap &rNew) const
Definition: bitmap3.cxx:844
std::enable_if< std::is_signed< T >::value||std::is_floating_point< T >::value, long >::type MinMax(T nVal, long nMin, long nMax)