1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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>
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)
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
;
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
)
102 Bitmap::ScopedReadAccess
pReadAcc(rSource
);
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
);
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
);
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
]);
139 if(pReadAcc
->HasPalette())
141 aColor
= pReadAcc
->GetPaletteColor(pReadAcc
->GetIndexFromData(pScanlineRead
, aPixels
[aIndex
]));
145 aColor
= pReadAcc
->GetPixelFromData(pScanlineRead
, aPixels
[aIndex
]);
148 aValueRed
+= aWeight
* aColor
.GetRed();
149 aValueGreen
+= aWeight
* aColor
.GetGreen();
150 aValueBlue
+= aWeight
* aColor
.GetBlue();
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
);
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
)
192 Bitmap::ScopedReadAccess
pReadAcc(rSource
);
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
);
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
));
216 aScanline
[y
] = pReadAcc
->GetPixel(y
, x
);
217 for(long y(0); y
< nNewHeight
; y
++)
219 const long aBaseIndex(y
* aNumberOfContributions
);
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
]);
230 const BitmapColor
& aColor
= aScanline
[aPixels
[aIndex
]];
231 aValueRed
+= aWeight
* aColor
.GetRed();
232 aValueGreen
+= aWeight
* aColor
.GetGreen();
233 aValueBlue
+= aWeight
* aColor
.GetBlue();
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
)));
249 pWriteAcc
->SetPixel(y
, x
, aResultColor
);
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
)
288 BmpMirrorFlags
nMirrorFlags(BmpMirrorFlags::NONE
);
289 bool bMirrorAfter(false);
295 nMirrorFlags
|= BmpMirrorFlags::Horizontal
;
300 nMirrorFlags
|= BmpMirrorFlags::Vertical
;
303 const long nStartSize(nWidth
* nHeight
);
304 const long nEndSize(nNewWidth
* nNewHeight
);
306 bMirrorAfter
= nStartSize
> nEndSize
;
310 bResult
= rBitmap
.Mirror(nMirrorFlags
);
318 const long nInBetweenSizeHorFirst(nHeight
* nNewWidth
);
319 const long nInBetweenSizeVerFirst(nNewHeight
* nWidth
);
320 Bitmap
aSource(rBitmap
);
322 if(nInBetweenSizeHorFirst
< nInBetweenSizeVerFirst
)
326 bResult
= ImplScaleConvolutionHor(aSource
, aResult
, fScaleX
, aKernel
);
329 if(bResult
&& bScaleVer
)
333 // copy partial result, independent of color depth
337 bResult
= ImplScaleConvolutionVer(aSource
, aResult
, fScaleY
, aKernel
);
344 bResult
= ImplScaleConvolutionVer(aSource
, aResult
, fScaleY
, aKernel
);
347 if(bResult
&& bScaleHor
)
351 // copy partial result, independent of color depth
355 bResult
= ImplScaleConvolutionHor(aSource
, aResult
, fScaleX
, aKernel
);
360 if(bResult
&& bMirrorAfter
)
362 bResult
= aResult
.Mirror(nMirrorFlags
);
367 rBitmap
.AdaptBitCount(aResult
);
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
);
384 return BitmapEx(aBitmap
);
391 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */