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
29namespace vcl
30{
31
32namespace
33{
34
35void ImplCalculateContributions(
36 const sal_Int32 aSourceSize,
37 const sal_Int32 aDestinationSize,
38 sal_Int32& 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 = (sal_Int32(fabs(ceil(fScaledRadius))) * 2) + 1;
50 const sal_Int32 nAllocSize(aDestinationSize * aNumberOfContributions);
51 rWeights.resize(nAllocSize);
52 rPixels.resize(nAllocSize);
53 rCounts.resize(aDestinationSize);
54
55 for(sal_Int32 i(0); i < aDestinationSize; i++)
56 {
57 const sal_Int32 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 sal_Int32 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 sal_Int32 aPixelIndex(MinMax(j, 0, aSourceSize - 1));
75 const sal_Int32 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
88bool 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 sal_Int32 nWidth(rSource.GetSizePixel().Width());
93 const sal_Int32 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 sal_Int32 aNumberOfContributions(0);
108
109 const sal_Int32 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(sal_Int32 y(0); y < nHeight; y++)
118 {
119 Scanline pScanline = pWriteAcc->GetScanline( y );
120 Scanline pScanlineRead = pReadAcc->GetScanline( y );
121 for(sal_Int32 x(0); x < nNewWidth; x++)
122 {
123 const sal_Int32 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(sal_Int32 j(0); j < aCounts[x]; j++)
130 {
131 const sal_Int32 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
178bool 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 sal_Int32 nHeight(rSource.GetSizePixel().Height());
183 const sal_Int32 nNewHeight(FRound(nHeight * rScaleY));
184
185 if(nHeight == nNewHeight)
186 {
187 return true;
188 }
189
190 Bitmap::ScopedReadAccess pReadAcc(rSource);
191 if(!pReadAcc)
192 return false;
193
194 std::vector<sal_Int16> aWeights;
195 std::vector<sal_Int32> aPixels;
196 std::vector<sal_Int32> aCounts;
197 sal_Int32 aNumberOfContributions(0);
198
199 const sal_Int32 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 if(!pWriteAcc)
204 return false;
205
206 std::vector<BitmapColor> aScanline(nHeight);
207 for(sal_Int32 x(0); x < nWidth; x++)
208 {
209 for(sal_Int32 y(0); y < nHeight; y++)
210 if(pReadAcc->HasPalette())
211 aScanline[y] = pReadAcc->GetPaletteColor(pReadAcc->GetPixelIndex(y, x));
212 else
213 aScanline[y] = pReadAcc->GetPixel(y, x);
214 for(sal_Int32 y(0); y < nNewHeight; y++)
215 {
216 const sal_Int32 aBaseIndex(y * aNumberOfContributions);
217 sal_Int32 aSum(0);
218 sal_Int32 aValueRed(0);
219 sal_Int32 aValueGreen(0);
220 sal_Int32 aValueBlue(0);
221
222 for(sal_Int32 j(0); j < aCounts[y]; j++)
223 {
224 const sal_Int32 aIndex(aBaseIndex + j);
225 const sal_Int16 aWeight(aWeights[aIndex]);
226 aSum += aWeight;
227 const BitmapColor & aColor = aScanline[aPixels[aIndex]];
228 aValueRed += aWeight * aColor.GetRed();
229 aValueGreen += aWeight * aColor.GetGreen();
230 aValueBlue += aWeight * aColor.GetBlue();
231 }
232
233 assert(aSum != 0);
234
235 const BitmapColor aResultColor(
236 static_cast< sal_uInt8 >(MinMax(static_cast< sal_Int32 >(aValueRed / aSum), 0, 255)),
237 static_cast< sal_uInt8 >(MinMax(static_cast< sal_Int32 >(aValueGreen / aSum), 0, 255)),
238 static_cast< sal_uInt8 >(MinMax(static_cast< sal_Int32 >(aValueBlue / aSum), 0, 255)));
239
240 if(pWriteAcc->HasPalette())
241 {
242 pWriteAcc->SetPixelIndex(y, x, static_cast< sal_uInt8 >(pWriteAcc->GetBestPaletteIndex(aResultColor)));
243 }
244 else
245 {
246 pWriteAcc->SetPixel(y, x, aResultColor);
247 }
248 }
249 }
250
251 aWeights.clear();
252 aCounts.clear();
253 aPixels.clear();
254
255 return true;
256}
257
258bool ImplScaleConvolution(Bitmap& rBitmap, const double& rScaleX, const double& rScaleY, const Kernel& aKernel)
259{
260 const bool bMirrorHor(rScaleX < 0.0);
261 const bool bMirrorVer(rScaleY < 0.0);
262 const double fScaleX(bMirrorHor ? -rScaleX : rScaleX);
263 const double fScaleY(bMirrorVer ? -rScaleY : rScaleY);
264 const sal_Int32 nWidth(rBitmap.GetSizePixel().Width());
265 const sal_Int32 nHeight(rBitmap.GetSizePixel().Height());
266 const sal_Int32 nNewWidth(FRound(nWidth * fScaleX));
267 const sal_Int32 nNewHeight(FRound(nHeight * fScaleY));
268 const bool bScaleHor(nWidth != nNewWidth);
269 const bool bScaleVer(nHeight != nNewHeight);
270 const bool bMirror(bMirrorHor || bMirrorVer);
271
272 if (!bMirror && !bScaleHor && !bScaleVer)
273 {
274 return true;
275 }
276
277 bool bResult(true);
279 bool bMirrorAfter(false);
280
281 if (bMirror)
282 {
283 if(bMirrorHor)
284 {
285 nMirrorFlags |= BmpMirrorFlags::Horizontal;
286 }
287
288 if(bMirrorVer)
289 {
290 nMirrorFlags |= BmpMirrorFlags::Vertical;
291 }
292
293 const sal_Int32 nStartSize(nWidth * nHeight);
294 const sal_Int32 nEndSize(nNewWidth * nNewHeight);
295
296 bMirrorAfter = nStartSize > nEndSize;
297
298 if(!bMirrorAfter)
299 {
300 bResult = rBitmap.Mirror(nMirrorFlags);
301 }
302 }
303
304 Bitmap aResult;
305
306 if (bResult)
307 {
308 const sal_Int32 nInBetweenSizeHorFirst(nHeight * nNewWidth);
309 const sal_Int32 nInBetweenSizeVerFirst(nNewHeight * nWidth);
310 Bitmap aSource(rBitmap);
311
312 if(nInBetweenSizeHorFirst < nInBetweenSizeVerFirst)
313 {
314 if(bScaleHor)
315 {
316 bResult = ImplScaleConvolutionHor(aSource, aResult, fScaleX, aKernel);
317 }
318
319 if(bResult && bScaleVer)
320 {
321 if(bScaleHor)
322 {
323 // copy partial result, independent of color depth
324 aSource = aResult;
325 }
326
327 bResult = ImplScaleConvolutionVer(aSource, aResult, fScaleY, aKernel);
328 }
329 }
330 else
331 {
332 if(bScaleVer)
333 {
334 bResult = ImplScaleConvolutionVer(aSource, aResult, fScaleY, aKernel);
335 }
336
337 if(bResult && bScaleHor)
338 {
339 if(bScaleVer)
340 {
341 // copy partial result, independent of color depth
342 aSource = aResult;
343 }
344
345 bResult = ImplScaleConvolutionHor(aSource, aResult, fScaleX, aKernel);
346 }
347 }
348 }
349
350 if(bResult && bMirrorAfter)
351 {
352 bResult = aResult.Mirror(nMirrorFlags);
353 }
354
355 if(bResult)
356 {
357 rBitmap.AdaptBitCount(aResult);
358 rBitmap = aResult;
359 }
360
361 return bResult;
362}
363
364} // end anonymous namespace
365
367{
368 bool bRetval = false;
369 Bitmap aBitmap(rBitmapEx.GetBitmap());
370
371 bRetval = ImplScaleConvolution(aBitmap, mrScaleX, mrScaleY, *mxKernel);
372
373 if (bRetval)
374 return BitmapEx(aBitmap);
375
376 return BitmapEx();
377}
378
379}
380
381/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
sal_uInt8 * Scanline
Definition: Scanline.hxx:26
Bitmap GetBitmap(Color aTransparentReplaceColor) const
Definition: BitmapEx.cxx:217
Size GetSizePixel() const
void AdaptBitCount(Bitmap &rNew) const
bool Mirror(BmpMirrorFlags nMirrorFlags)
Mirror the bitmap.
sal_uInt8 GetBlue() const
sal_uInt8 GetRed() const
sal_uInt8 GetGreen() const
constexpr tools::Long Height() const
constexpr tools::Long Width() const
virtual BitmapEx execute(BitmapEx const &rBitmap) const override
float y
float x
std::deque< AttacherIndex_Impl > aIndex
FilterGroup & rTarget
tools::Long FRound(double fVal)
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)
BmpMirrorFlags
sal_Int32 nIndex
int i
unsigned char sal_uInt8