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>
21 #include <tools/helpers.hxx>
23 #include <vcl/BitmapWriteAccess.hxx>
24 #include <bitmap/BitmapScaleConvolutionFilter.hxx>
35 void ImplCalculateContributions(const sal_Int32 nSoureSize
, const sal_Int32 nDestinationSize
,
36 sal_Int32
& rNumberOfContributions
, std::vector
<sal_Int16
>& rWeights
,
37 std::vector
<sal_Int32
>& rPixels
, std::vector
<sal_Int32
>& rCounts
,
38 const Kernel
& rKernel
)
40 const double fSamplingRadius(rKernel
.GetWidth());
41 const double fScale(nDestinationSize
/ static_cast< double >(nSoureSize
));
42 const double fScaledRadius((fScale
< 1.0) ? fSamplingRadius
/ fScale
: fSamplingRadius
);
43 const double fFilterFactor(std::min(fScale
, 1.0));
45 rNumberOfContributions
= (sal_Int32(fabs(ceil(fScaledRadius
))) * 2) + 1;
46 const sal_Int32
nAllocSize(nDestinationSize
* rNumberOfContributions
);
47 rWeights
.resize(nAllocSize
);
48 rPixels
.resize(nAllocSize
);
49 rCounts
.resize(nDestinationSize
);
51 for(sal_Int32
i(0); i
< nDestinationSize
; i
++)
53 const sal_Int32
nIndexes(i
* rNumberOfContributions
);
54 const double aCenter(i
/ fScale
);
55 const sal_Int32
nLeft(static_cast< sal_Int32
>(floor(aCenter
- fScaledRadius
)));
56 const sal_Int32
nRight(static_cast< sal_Int32
>(ceil(aCenter
+ fScaledRadius
)));
57 sal_Int32
nCurrentCount(0);
59 for(sal_Int32
j(nLeft
); j
<= nRight
; j
++)
61 const double fWeight(rKernel
.Calculate(fFilterFactor
* (aCenter
- static_cast< double>(j
))));
63 // Reduce calculations with ignoring weights of 0.0
64 if(fabs(fWeight
) < 0.0001)
68 const sal_Int32
nPixelIndex(std::clamp(j
, sal_Int32(0), nSoureSize
- 1));
69 const sal_Int32
nIndex(nIndexes
+ nCurrentCount
);
71 // scale the weight by 255 since we're converting from float to int
72 rWeights
[nIndex
] = fWeight
* 255;
73 rPixels
[nIndex
] = nPixelIndex
;
78 rCounts
[i
] = nCurrentCount
;
82 bool ImplScaleConvolutionHor(Bitmap
& rSource
, Bitmap
& rTarget
, const double& rScaleX
, const Kernel
& rKernel
)
84 // Do horizontal filtering
85 OSL_ENSURE(rScaleX
> 0.0, "Error in scaling: Mirror given in non-mirror-capable method (!)");
86 const sal_Int32
nWidth(rSource
.GetSizePixel().Width());
87 const sal_Int32
nNewWidth(basegfx::fround(nWidth
* rScaleX
));
89 if (nWidth
== nNewWidth
)
92 BitmapScopedReadAccess
pReadAcc(rSource
);
97 std::vector
<sal_Int16
> aWeights
;
98 std::vector
<sal_Int32
> aPixels
;
99 std::vector
<sal_Int32
> aCounts
;
100 sal_Int32
nNumberOfContributions(0);
102 const sal_Int32
nHeight(rSource
.GetSizePixel().Height());
103 ImplCalculateContributions(nWidth
, nNewWidth
, nNumberOfContributions
, aWeights
, aPixels
, aCounts
, rKernel
);
104 rTarget
= Bitmap(Size(nNewWidth
, nHeight
), vcl::PixelFormat::N24_BPP
);
105 BitmapScopedWriteAccess
pWriteAcc(rTarget
);
110 for (sal_Int32
y(0); y
< nHeight
; y
++)
112 Scanline pScanline
= pWriteAcc
->GetScanline( y
);
113 Scanline pScanlineRead
= pReadAcc
->GetScanline( y
);
115 for (sal_Int32
x(0); x
< nNewWidth
; x
++)
117 const sal_Int32
nBaseIndex(x
* nNumberOfContributions
);
119 sal_Int32
nValueRed(0);
120 sal_Int32
nValueGreen(0);
121 sal_Int32
nValueBlue(0);
123 for (sal_Int32
j(0); j
< aCounts
[x
]; j
++)
125 const sal_Int32
nIndex(nBaseIndex
+ j
);
126 const sal_Int16
nWeight(aWeights
[nIndex
]);
131 if (pReadAcc
->HasPalette())
132 aColor
= pReadAcc
->GetPaletteColor(pReadAcc
->GetIndexFromData(pScanlineRead
, aPixels
[nIndex
]));
134 aColor
= pReadAcc
->GetPixelFromData(pScanlineRead
, aPixels
[nIndex
]);
136 nValueRed
+= nWeight
* aColor
.GetRed();
137 nValueGreen
+= nWeight
* aColor
.GetGreen();
138 nValueBlue
+= nWeight
* aColor
.GetBlue();
143 const BitmapColor
aResultColor(
144 static_cast< sal_uInt8
>(std::clamp
< sal_Int32
>(nValueRed
/ nSum
, 0, 255)),
145 static_cast< sal_uInt8
>(std::clamp
< sal_Int32
>(nValueGreen
/ nSum
, 0, 255)),
146 static_cast< sal_uInt8
>(std::clamp
< sal_Int32
>(nValueBlue
/ nSum
, 0, 255)));
148 pWriteAcc
->SetPixelOnData(pScanline
, x
, aResultColor
);
157 bool ImplScaleConvolutionVer(Bitmap
& rSource
, Bitmap
& rTarget
, const double& rScaleY
, const Kernel
& rKernel
)
159 // Do vertical filtering
160 OSL_ENSURE(rScaleY
> 0.0, "Error in scaling: Mirror given in non-mirror-capable method (!)");
161 const sal_Int32
nHeight(rSource
.GetSizePixel().Height());
162 const sal_Int32
nNewHeight(basegfx::fround(nHeight
* rScaleY
));
164 if(nHeight
== nNewHeight
)
169 BitmapScopedReadAccess
pReadAcc(rSource
);
173 std::vector
<sal_Int16
> aWeights
;
174 std::vector
<sal_Int32
> aPixels
;
175 std::vector
<sal_Int32
> aCounts
;
176 sal_Int32
nNumberOfContributions(0);
178 const sal_Int32
nWidth(rSource
.GetSizePixel().Width());
179 ImplCalculateContributions(nHeight
, nNewHeight
, nNumberOfContributions
, aWeights
, aPixels
, aCounts
, rKernel
);
180 rTarget
= Bitmap(Size(nWidth
, nNewHeight
), vcl::PixelFormat::N24_BPP
);
181 BitmapScopedWriteAccess
pWriteAcc(rTarget
);
185 std::vector
<BitmapColor
> aScanline(nHeight
);
186 for(sal_Int32
x(0); x
< nWidth
; x
++)
188 for(sal_Int32
y(0); y
< nHeight
; y
++)
189 if(pReadAcc
->HasPalette())
190 aScanline
[y
] = pReadAcc
->GetPaletteColor(pReadAcc
->GetPixelIndex(y
, x
));
192 aScanline
[y
] = pReadAcc
->GetPixel(y
, x
);
193 for(sal_Int32
y(0); y
< nNewHeight
; y
++)
195 const sal_Int32 nBaseIndex
= y
* nNumberOfContributions
;
197 sal_Int32
nValueRed(0);
198 sal_Int32
nValueGreen(0);
199 sal_Int32
nValueBlue(0);
201 for(sal_Int32
j(0); j
< aCounts
[y
]; j
++)
203 const sal_Int32
nIndex(nBaseIndex
+ j
);
204 const sal_Int16
nWeight(aWeights
[nIndex
]);
206 const BitmapColor
& aColor
= aScanline
[aPixels
[nIndex
]];
207 nValueRed
+= nWeight
* aColor
.GetRed();
208 nValueGreen
+= nWeight
* aColor
.GetGreen();
209 nValueBlue
+= nWeight
* aColor
.GetBlue();
214 const BitmapColor
aResultColor(
215 static_cast< sal_uInt8
>(std::clamp
< sal_Int32
>(nValueRed
/ nSum
, 0, 255)),
216 static_cast< sal_uInt8
>(std::clamp
< sal_Int32
>(nValueGreen
/ nSum
, 0, 255)),
217 static_cast< sal_uInt8
>(std::clamp
< sal_Int32
>(nValueBlue
/ nSum
, 0, 255)));
219 if(pWriteAcc
->HasPalette())
221 pWriteAcc
->SetPixelIndex(y
, x
, static_cast< sal_uInt8
>(pWriteAcc
->GetBestPaletteIndex(aResultColor
)));
225 pWriteAcc
->SetPixel(y
, x
, aResultColor
);
233 bool ImplScaleConvolution(Bitmap
& rBitmap
, const double& rScaleX
, const double& rScaleY
, const Kernel
& rKernel
)
235 const bool bMirrorHor(rScaleX
< 0.0);
236 const bool bMirrorVer(rScaleY
< 0.0);
237 const double fScaleX(bMirrorHor
? -rScaleX
: rScaleX
);
238 const double fScaleY(bMirrorVer
? -rScaleY
: rScaleY
);
239 const sal_Int32
nWidth(rBitmap
.GetSizePixel().Width());
240 const sal_Int32
nHeight(rBitmap
.GetSizePixel().Height());
241 const sal_Int32
nNewWidth(basegfx::fround(nWidth
* fScaleX
));
242 const sal_Int32
nNewHeight(basegfx::fround(nHeight
* fScaleY
));
243 const bool bScaleHor(nWidth
!= nNewWidth
);
244 const bool bScaleVer(nHeight
!= nNewHeight
);
245 const bool bMirror(bMirrorHor
|| bMirrorVer
);
247 if (!bMirror
&& !bScaleHor
&& !bScaleVer
)
253 BmpMirrorFlags
nMirrorFlags(BmpMirrorFlags::NONE
);
254 bool bMirrorAfter(false);
260 nMirrorFlags
|= BmpMirrorFlags::Horizontal
;
265 nMirrorFlags
|= BmpMirrorFlags::Vertical
;
268 const sal_Int32
nStartSize(nWidth
* nHeight
);
269 const sal_Int32
nEndSize(nNewWidth
* nNewHeight
);
271 bMirrorAfter
= nStartSize
> nEndSize
;
275 bResult
= rBitmap
.Mirror(nMirrorFlags
);
283 const sal_Int32
nInBetweenSizeHorFirst(nHeight
* nNewWidth
);
284 const sal_Int32
nInBetweenSizeVerFirst(nNewHeight
* nWidth
);
285 Bitmap
aSource(rBitmap
);
287 if(nInBetweenSizeHorFirst
< nInBetweenSizeVerFirst
)
291 bResult
= ImplScaleConvolutionHor(aSource
, aResult
, fScaleX
, rKernel
);
294 if(bResult
&& bScaleVer
)
298 // copy partial result, independent of color depth
302 bResult
= ImplScaleConvolutionVer(aSource
, aResult
, fScaleY
, rKernel
);
309 bResult
= ImplScaleConvolutionVer(aSource
, aResult
, fScaleY
, rKernel
);
312 if(bResult
&& bScaleHor
)
316 // copy partial result, independent of color depth
320 bResult
= ImplScaleConvolutionHor(aSource
, aResult
, fScaleX
, rKernel
);
325 if(bResult
&& bMirrorAfter
)
327 bResult
= aResult
.Mirror(nMirrorFlags
);
332 rBitmap
.AdaptBitCount(aResult
);
333 rBitmap
= std::move(aResult
);
339 } // end anonymous namespace
341 BitmapEx
BitmapScaleConvolutionFilter::execute(BitmapEx
const& rBitmapEx
) const
343 bool bRetval
= false;
344 Bitmap
aBitmap(rBitmapEx
.GetBitmap());
346 bRetval
= ImplScaleConvolution(aBitmap
, mrScaleX
, mrScaleY
, *mxKernel
);
349 return BitmapEx(aBitmap
);
356 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */