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 <comphelper/threadpool.hxx>
22 #include <tools/helpers.hxx>
23 #include <vcl/bitmapaccess.hxx>
25 #include <bitmapwriteaccess.hxx>
26 #include <BitmapScaleSuperFilter.hxx>
31 #include <sal/log.hxx>
35 #define MAP_PRECISION 7
37 typedef sal_Int32 BilinearWeightType
;
39 constexpr BilinearWeightType
lclMaxWeight()
41 return BilinearWeightType(1) << MAP_PRECISION
;
44 constexpr sal_uInt8
MAP(sal_uInt8 cVal0
, sal_uInt8 cVal1
, BilinearWeightType nFrac
)
46 return sal_uInt8(((BilinearWeightType(cVal0
) << MAP_PRECISION
) + nFrac
* (BilinearWeightType(cVal1
) - BilinearWeightType(cVal0
))) >> MAP_PRECISION
);
51 BitmapReadAccess
* const mpSrc
;
52 BitmapWriteAccess
* mpDest
;
56 std::vector
<long> maMapIX
;
57 std::vector
<long> maMapIY
;
58 std::vector
<BilinearWeightType
> maMapFX
;
59 std::vector
<BilinearWeightType
> maMapFY
;
61 ScaleContext( BitmapReadAccess
*pSrc
,
62 BitmapWriteAccess
*pDest
,
63 long nSrcW
, long nDestW
,
64 long nSrcH
, long nDestH
,
65 bool bHMirr
, bool bVMirr
)
76 generateMap(nSrcW
, nDestW
, bHMirr
, maMapIX
, maMapFX
);
77 generateMap(nSrcH
, nDestH
, bVMirr
, maMapIY
, maMapFY
);
80 static void generateMap(long nSourceLength
, long nDestinationLength
, bool bMirrored
,
81 std::vector
<long> & rMapIX
, std::vector
<BilinearWeightType
> & rMapFX
)
83 const double fRevScale
= (nDestinationLength
> 1) ? double(nSourceLength
- 1) / (nDestinationLength
- 1) : 0.0;
85 long nTemp
= nSourceLength
- 2;
86 long nTempX
= nSourceLength
- 1;
88 for (long i
= 0; i
< nDestinationLength
; i
++)
90 double fTemp
= i
* fRevScale
;
92 fTemp
= nTempX
- fTemp
;
93 rMapIX
[i
] = MinMax(long(fTemp
), 0, nTemp
);
94 rMapFX
[i
] = BilinearWeightType((fTemp
- rMapIX
[i
]) * (BilinearWeightType(1) << MAP_PRECISION
));
99 constexpr long constScaleThreadStrip
= 32;
101 typedef void (*ScaleRangeFn
)(ScaleContext
&rContext
, long nStartY
, long nEndY
);
103 class ScaleTask
: public comphelper::ThreadTask
105 ScaleRangeFn
const mpScaleRangeFunction
;
106 ScaleContext
& mrContext
;
111 explicit ScaleTask(const std::shared_ptr
<comphelper::ThreadTaskTag
>& pTag
,
112 ScaleRangeFn pScaleRangeFunction
,
113 ScaleContext
& rContext
,
114 long nStartY
, long nEndY
)
115 : comphelper::ThreadTask(pTag
)
116 , mpScaleRangeFunction(pScaleRangeFunction
)
117 , mrContext(rContext
)
122 virtual void doWork() override
124 mpScaleRangeFunction(mrContext
, mnStartY
, mnEndY
);
128 void scaleUp32bit(ScaleContext
&rCtx
, long nStartY
, long nEndY
)
130 const int nColorComponents
= 4;
132 const long nStartX
= 0;
133 const long nEndX
= rCtx
.mnDestW
- 1;
135 for (long nY
= nStartY
; nY
<= nEndY
; nY
++)
137 long nTempY
= rCtx
.maMapIY
[nY
];
138 BilinearWeightType nTempFY
= rCtx
.maMapFY
[nY
];
140 Scanline pLine0
= rCtx
.mpSrc
->GetScanline(nTempY
+0);
141 Scanline pLine1
= rCtx
.mpSrc
->GetScanline(nTempY
+1);
142 Scanline pScanDest
= rCtx
.mpDest
->GetScanline(nY
);
144 sal_uInt8 nComponent1
[nColorComponents
];
145 sal_uInt8 nComponent2
[nColorComponents
];
150 for (long nX
= nStartX
; nX
<= nEndX
; nX
++)
152 long nTempX
= rCtx
.maMapIX
[nX
];
153 BilinearWeightType nTempFX
= rCtx
.maMapFX
[nX
];
155 pColorPtr0
= pLine0
+ nTempX
* nColorComponents
;
156 pColorPtr1
= pColorPtr0
+ nColorComponents
;
158 nComponent1
[0] = MAP(*pColorPtr0
, *pColorPtr1
, nTempFX
);
159 pColorPtr0
++; pColorPtr1
++;
160 nComponent1
[1] = MAP(*pColorPtr0
, *pColorPtr1
, nTempFX
);
161 pColorPtr0
++; pColorPtr1
++;
162 nComponent1
[2] = MAP(*pColorPtr0
, *pColorPtr1
, nTempFX
);
163 pColorPtr0
++; pColorPtr1
++;
164 nComponent1
[3] = MAP(*pColorPtr0
, *pColorPtr1
, nTempFX
);
166 pColorPtr0
= pLine1
+ nTempX
* nColorComponents
;
167 pColorPtr1
= pColorPtr0
+ nColorComponents
;
169 nComponent2
[0] = MAP(*pColorPtr0
, *pColorPtr1
, nTempFX
);
170 pColorPtr0
++; pColorPtr1
++;
171 nComponent2
[1] = MAP(*pColorPtr0
, *pColorPtr1
, nTempFX
);
172 pColorPtr0
++; pColorPtr1
++;
173 nComponent2
[2] = MAP(*pColorPtr0
, *pColorPtr1
, nTempFX
);
174 pColorPtr0
++; pColorPtr1
++;
175 nComponent2
[3] = MAP(*pColorPtr0
, *pColorPtr1
, nTempFX
);
177 *pScanDest
= MAP(nComponent1
[0], nComponent2
[0], nTempFY
);
179 *pScanDest
= MAP(nComponent1
[1], nComponent2
[1], nTempFY
);
181 *pScanDest
= MAP(nComponent1
[2], nComponent2
[2], nTempFY
);
183 *pScanDest
= MAP(nComponent1
[3], nComponent2
[3], nTempFY
);
189 void scaleUpPalette8bit(ScaleContext
&rCtx
, long nStartY
, long nEndY
)
191 const long nStartX
= 0, nEndX
= rCtx
.mnDestW
- 1;
193 for( long nY
= nStartY
; nY
<= nEndY
; nY
++ )
195 long nTempY
= rCtx
.maMapIY
[ nY
];
196 BilinearWeightType nTempFY
= rCtx
.maMapFY
[ nY
];
197 Scanline pLine0
= rCtx
.mpSrc
->GetScanline( nTempY
);
198 Scanline pLine1
= rCtx
.mpSrc
->GetScanline( ++nTempY
);
199 Scanline pScanDest
= rCtx
.mpDest
->GetScanline( nY
);
201 for(long nX
= nStartX
, nXDst
= 0; nX
<= nEndX
; nX
++ )
203 long nTempX
= rCtx
.maMapIX
[ nX
];
204 BilinearWeightType nTempFX
= rCtx
.maMapFX
[ nX
];
206 const BitmapColor
& rCol0
= rCtx
.mpSrc
->GetPaletteColor( pLine0
[ nTempX
] );
207 const BitmapColor
& rCol2
= rCtx
.mpSrc
->GetPaletteColor( pLine1
[ nTempX
] );
208 const BitmapColor
& rCol1
= rCtx
.mpSrc
->GetPaletteColor( pLine0
[ ++nTempX
] );
209 const BitmapColor
& rCol3
= rCtx
.mpSrc
->GetPaletteColor( pLine1
[ nTempX
] );
211 sal_uInt8 cR0
= MAP( rCol0
.GetRed(), rCol1
.GetRed(), nTempFX
);
212 sal_uInt8 cG0
= MAP( rCol0
.GetGreen(), rCol1
.GetGreen(), nTempFX
);
213 sal_uInt8 cB0
= MAP( rCol0
.GetBlue(), rCol1
.GetBlue(), nTempFX
);
215 sal_uInt8 cR1
= MAP( rCol2
.GetRed(), rCol3
.GetRed(), nTempFX
);
216 sal_uInt8 cG1
= MAP( rCol2
.GetGreen(), rCol3
.GetGreen(), nTempFX
);
217 sal_uInt8 cB1
= MAP( rCol2
.GetBlue(), rCol3
.GetBlue(), nTempFX
);
219 BitmapColor
aColRes( MAP( cR0
, cR1
, nTempFY
),
220 MAP( cG0
, cG1
, nTempFY
),
221 MAP( cB0
, cB1
, nTempFY
) );
222 rCtx
.mpDest
->SetPixelOnData( pScanDest
, nXDst
++, aColRes
);
227 void scaleUpPaletteGeneral(ScaleContext
&rCtx
, long nStartY
, long nEndY
)
229 const long nStartX
= 0, nEndX
= rCtx
.mnDestW
- 1;
231 for( long nY
= nStartY
; nY
<= nEndY
; nY
++ )
233 long nTempY
= rCtx
.maMapIY
[ nY
];
234 BilinearWeightType nTempFY
= rCtx
.maMapFY
[ nY
];
235 Scanline pScanline
= rCtx
.mpDest
->GetScanline( nY
);
237 for( long nX
= nStartX
, nXDst
= 0; nX
<= nEndX
; nX
++ )
239 long nTempX
= rCtx
.maMapIX
[ nX
];
240 BilinearWeightType nTempFX
= rCtx
.maMapFX
[ nX
];
242 BitmapColor aCol0
= rCtx
.mpSrc
->GetPaletteColor( rCtx
.mpSrc
->GetPixelIndex( nTempY
, nTempX
) );
243 BitmapColor aCol1
= rCtx
.mpSrc
->GetPaletteColor( rCtx
.mpSrc
->GetPixelIndex( nTempY
, ++nTempX
) );
244 sal_uInt8 cR0
= MAP( aCol0
.GetRed(), aCol1
.GetRed(), nTempFX
);
245 sal_uInt8 cG0
= MAP( aCol0
.GetGreen(), aCol1
.GetGreen(), nTempFX
);
246 sal_uInt8 cB0
= MAP( aCol0
.GetBlue(), aCol1
.GetBlue(), nTempFX
);
248 aCol1
= rCtx
.mpSrc
->GetPaletteColor( rCtx
.mpSrc
->GetPixelIndex( ++nTempY
, nTempX
) );
249 aCol0
= rCtx
.mpSrc
->GetPaletteColor( rCtx
.mpSrc
->GetPixelIndex( nTempY
--, --nTempX
) );
250 sal_uInt8 cR1
= MAP( aCol0
.GetRed(), aCol1
.GetRed(), nTempFX
);
251 sal_uInt8 cG1
= MAP( aCol0
.GetGreen(), aCol1
.GetGreen(), nTempFX
);
252 sal_uInt8 cB1
= MAP( aCol0
.GetBlue(), aCol1
.GetBlue(), nTempFX
);
254 BitmapColor
aColRes( MAP( cR0
, cR1
, nTempFY
),
255 MAP( cG0
, cG1
, nTempFY
),
256 MAP( cB0
, cB1
, nTempFY
) );
257 rCtx
.mpDest
->SetPixelOnData( pScanline
, nXDst
++, aColRes
);
262 void scaleUp24bit(ScaleContext
&rCtx
, long nStartY
, long nEndY
)
264 const int nColorComponents
= 3;
266 const long nStartX
= 0;
267 const long nEndX
= rCtx
.mnDestW
- 1;
269 for (long nY
= nStartY
; nY
<= nEndY
; nY
++)
271 long nTempY
= rCtx
.maMapIY
[nY
];
272 BilinearWeightType nTempFY
= rCtx
.maMapFY
[nY
];
274 Scanline pLine0
= rCtx
.mpSrc
->GetScanline(nTempY
+0);
275 Scanline pLine1
= rCtx
.mpSrc
->GetScanline(nTempY
+1);
276 Scanline pScanDest
= rCtx
.mpDest
->GetScanline(nY
);
278 sal_uInt8 nComponent1
[nColorComponents
];
279 sal_uInt8 nComponent2
[nColorComponents
];
284 for (long nX
= nStartX
; nX
<= nEndX
; nX
++)
286 long nTempX
= rCtx
.maMapIX
[nX
];
287 BilinearWeightType nTempFX
= rCtx
.maMapFX
[nX
];
289 pColorPtr0
= pLine0
+ nTempX
* nColorComponents
;
290 pColorPtr1
= pColorPtr0
+ nColorComponents
;
292 nComponent1
[0] = MAP(*pColorPtr0
, *pColorPtr1
, nTempFX
);
293 pColorPtr0
++; pColorPtr1
++;
294 nComponent1
[1] = MAP(*pColorPtr0
, *pColorPtr1
, nTempFX
);
295 pColorPtr0
++; pColorPtr1
++;
296 nComponent1
[2] = MAP(*pColorPtr0
, *pColorPtr1
, nTempFX
);
298 pColorPtr0
= pLine1
+ nTempX
* nColorComponents
;
299 pColorPtr1
= pColorPtr0
+ nColorComponents
;
301 nComponent2
[0] = MAP(*pColorPtr0
, *pColorPtr1
, nTempFX
);
302 pColorPtr0
++; pColorPtr1
++;
303 nComponent2
[1] = MAP(*pColorPtr0
, *pColorPtr1
, nTempFX
);
304 pColorPtr0
++; pColorPtr1
++;
305 nComponent2
[2] = MAP(*pColorPtr0
, *pColorPtr1
, nTempFX
);
307 *pScanDest
= MAP(nComponent1
[0], nComponent2
[0], nTempFY
);
309 *pScanDest
= MAP(nComponent1
[1], nComponent2
[1], nTempFY
);
311 *pScanDest
= MAP(nComponent1
[2], nComponent2
[2], nTempFY
);
317 void scaleUpNonPaletteGeneral(ScaleContext
&rCtx
, long nStartY
, long nEndY
)
319 const long nStartX
= 0, nEndX
= rCtx
.mnDestW
- 1;
321 for( long nY
= nStartY
; nY
<= nEndY
; nY
++ )
323 long nTempY
= rCtx
.maMapIY
[ nY
];
324 BilinearWeightType nTempFY
= rCtx
.maMapFY
[ nY
];
325 Scanline pScanDest
= rCtx
.mpDest
->GetScanline( nY
);
327 for( long nX
= nStartX
, nXDst
= 0; nX
<= nEndX
; nX
++ )
329 long nTempX
= rCtx
.maMapIX
[ nX
];
330 BilinearWeightType nTempFX
= rCtx
.maMapFX
[ nX
];
332 BitmapColor aCol0
= rCtx
.mpSrc
->GetPixel( nTempY
, nTempX
);
333 BitmapColor aCol1
= rCtx
.mpSrc
->GetPixel( nTempY
, ++nTempX
);
334 sal_uInt8 cR0
= MAP( aCol0
.GetRed(), aCol1
.GetRed(), nTempFX
);
335 sal_uInt8 cG0
= MAP( aCol0
.GetGreen(), aCol1
.GetGreen(), nTempFX
);
336 sal_uInt8 cB0
= MAP( aCol0
.GetBlue(), aCol1
.GetBlue(), nTempFX
);
338 aCol1
= rCtx
.mpSrc
->GetPixel( ++nTempY
, nTempX
);
339 aCol0
= rCtx
.mpSrc
->GetPixel( nTempY
--, --nTempX
);
340 sal_uInt8 cR1
= MAP( aCol0
.GetRed(), aCol1
.GetRed(), nTempFX
);
341 sal_uInt8 cG1
= MAP( aCol0
.GetGreen(), aCol1
.GetGreen(), nTempFX
);
342 sal_uInt8 cB1
= MAP( aCol0
.GetBlue(), aCol1
.GetBlue(), nTempFX
);
344 BitmapColor
aColRes( MAP( cR0
, cR1
, nTempFY
),
345 MAP( cG0
, cG1
, nTempFY
),
346 MAP( cB0
, cB1
, nTempFY
) );
347 rCtx
.mpDest
->SetPixelOnData( pScanDest
, nXDst
++, aColRes
);
352 void scaleDown32bit(ScaleContext
&rCtx
, long nStartY
, long nEndY
)
354 const int constColorComponents
= 4;
356 const long nStartX
= 0;
357 const long nEndX
= rCtx
.mnDestW
- 1;
359 for (long nY
= nStartY
; nY
<= nEndY
; nY
++)
361 long nTop
= rCtx
.mbVMirr
? (nY
+ 1) : nY
;
362 long nBottom
= rCtx
.mbVMirr
? nY
: (nY
+ 1);
368 nLineStart
= rCtx
.maMapIY
[nY
];
373 nLineStart
= rCtx
.maMapIY
[nTop
];
374 nLineRange
= (rCtx
.maMapIY
[nBottom
] == rCtx
.maMapIY
[nTop
]) ?
375 1 : (rCtx
.maMapIY
[nBottom
] - rCtx
.maMapIY
[nTop
]);
378 Scanline pScanDest
= rCtx
.mpDest
->GetScanline(nY
);
379 for (long nX
= nStartX
; nX
<= nEndX
; nX
++)
381 long nLeft
= rCtx
.mbHMirr
? (nX
+ 1) : nX
;
382 long nRight
= rCtx
.mbHMirr
? nX
: (nX
+ 1);
388 nRowStart
= rCtx
.maMapIX
[nX
];
393 nRowStart
= rCtx
.maMapIX
[nLeft
];
394 nRowRange
= (rCtx
.maMapIX
[nRight
] == rCtx
.maMapIX
[nLeft
]) ?
395 1 : (rCtx
.maMapIX
[nRight
] - rCtx
.maMapIX
[nLeft
]);
402 BilinearWeightType nTotalWeightY
= 0;
404 for (long i
= 0; i
<= nLineRange
; i
++)
406 Scanline pTmpY
= rCtx
.mpSrc
->GetScanline(nLineStart
+ i
);
407 Scanline pTmpX
= pTmpY
+ constColorComponents
* nRowStart
;
413 BilinearWeightType nTotalWeightX
= 0;
415 for (long j
= 0; j
<= nRowRange
; j
++)
419 nSumRow1
+= (*pTmpX
) << MAP_PRECISION
; pTmpX
++;
420 nSumRow2
+= (*pTmpX
) << MAP_PRECISION
; pTmpX
++;
421 nSumRow3
+= (*pTmpX
) << MAP_PRECISION
; pTmpX
++;
422 nSumRow4
+= (*pTmpX
) << MAP_PRECISION
; pTmpX
++;
423 nTotalWeightX
+= lclMaxWeight();
427 BilinearWeightType nWeightX
= lclMaxWeight() - rCtx
.maMapFX
[nLeft
];
428 nSumRow1
+= (nWeightX
* (*pTmpX
)); pTmpX
++;
429 nSumRow2
+= (nWeightX
* (*pTmpX
)); pTmpX
++;
430 nSumRow3
+= (nWeightX
* (*pTmpX
)); pTmpX
++;
431 nSumRow4
+= (nWeightX
* (*pTmpX
)); pTmpX
++;
432 nTotalWeightX
+= nWeightX
;
434 else if ( nRowRange
== j
)
436 BilinearWeightType nWeightX
= rCtx
.maMapFX
[ nRight
] ;
437 nSumRow1
+= (nWeightX
* (*pTmpX
)); pTmpX
++;
438 nSumRow2
+= (nWeightX
* (*pTmpX
)); pTmpX
++;
439 nSumRow3
+= (nWeightX
* (*pTmpX
)); pTmpX
++;
440 nSumRow4
+= (nWeightX
* (*pTmpX
)); pTmpX
++;
441 nTotalWeightX
+= nWeightX
;
445 nSumRow1
+= (*pTmpX
) << MAP_PRECISION
; pTmpX
++;
446 nSumRow2
+= (*pTmpX
) << MAP_PRECISION
; pTmpX
++;
447 nSumRow3
+= (*pTmpX
) << MAP_PRECISION
; pTmpX
++;
448 nSumRow4
+= (*pTmpX
) << MAP_PRECISION
; pTmpX
++;
449 nTotalWeightX
+= lclMaxWeight();
453 BilinearWeightType nWeightY
= lclMaxWeight();
455 nWeightY
= lclMaxWeight();
457 nWeightY
= lclMaxWeight() - rCtx
.maMapFY
[nTop
];
458 else if (nLineRange
== 1)
459 nWeightY
= rCtx
.maMapFY
[nTop
];
460 else if (nLineRange
== i
)
461 nWeightY
= rCtx
.maMapFY
[nBottom
];
465 nSumRow1
/= nTotalWeightX
;
466 nSumRow2
/= nTotalWeightX
;
467 nSumRow3
/= nTotalWeightX
;
468 nSumRow4
/= nTotalWeightX
;
470 nSum1
+= nWeightY
* nSumRow1
;
471 nSum2
+= nWeightY
* nSumRow2
;
472 nSum3
+= nWeightY
* nSumRow3
;
473 nSum4
+= nWeightY
* nSumRow4
;
474 nTotalWeightY
+= nWeightY
;
479 nSum1
/= nTotalWeightY
;
480 nSum2
/= nTotalWeightY
;
481 nSum3
/= nTotalWeightY
;
482 nSum4
/= nTotalWeightY
;
485 // Write the calculated color components to the destination
486 *pScanDest
= nSum1
; pScanDest
++;
487 *pScanDest
= nSum2
; pScanDest
++;
488 *pScanDest
= nSum3
; pScanDest
++;
489 *pScanDest
= nSum4
; pScanDest
++;
494 void scaleDownPalette8bit(ScaleContext
&rCtx
, long nStartY
, long nEndY
)
496 const long nStartX
= 0, nEndX
= rCtx
.mnDestW
- 1;
498 for( long nY
= nStartY
; nY
<= nEndY
; nY
++ )
500 long nTop
= rCtx
.mbVMirr
? ( nY
+ 1 ) : nY
;
501 long nBottom
= rCtx
.mbVMirr
? nY
: ( nY
+ 1 ) ;
503 long nLineStart
, nLineRange
;
506 nLineStart
= rCtx
.maMapIY
[ nY
];
511 nLineStart
= rCtx
.maMapIY
[ nTop
] ;
512 nLineRange
= ( rCtx
.maMapIY
[ nBottom
] == rCtx
.maMapIY
[ nTop
] ) ? 1 :( rCtx
.maMapIY
[ nBottom
] - rCtx
.maMapIY
[ nTop
] );
515 Scanline pScanDest
= rCtx
.mpDest
->GetScanline( nY
);
516 for( long nX
= nStartX
, nXDst
= 0; nX
<= nEndX
; nX
++ )
518 long nLeft
= rCtx
.mbHMirr
? ( nX
+ 1 ) : nX
;
519 long nRight
= rCtx
.mbHMirr
? nX
: ( nX
+ 1 ) ;
525 nRowStart
= rCtx
.maMapIX
[ nX
];
530 nRowStart
= rCtx
.maMapIX
[ nLeft
];
531 nRowRange
= ( rCtx
.maMapIX
[ nRight
] == rCtx
.maMapIX
[ nLeft
] )? 1 : ( rCtx
.maMapIX
[ nRight
] - rCtx
.maMapIX
[ nLeft
] );
537 BilinearWeightType nTotalWeightY
= 0;
539 for(long i
= 0; i
<= nLineRange
; i
++)
541 Scanline pTmpY
= rCtx
.mpSrc
->GetScanline( nLineStart
+ i
);
545 BilinearWeightType nTotalWeightX
= 0;
547 for(long j
= 0; j
<= nRowRange
; j
++)
549 const BitmapColor
& rCol
= rCtx
.mpSrc
->GetPaletteColor( pTmpY
[ nRowStart
+ j
] );
553 nSumRowB
+= rCol
.GetBlue() << MAP_PRECISION
;
554 nSumRowG
+= rCol
.GetGreen() << MAP_PRECISION
;
555 nSumRowR
+= rCol
.GetRed() << MAP_PRECISION
;
556 nTotalWeightX
+= lclMaxWeight();
560 BilinearWeightType nWeightX
= lclMaxWeight() - rCtx
.maMapFX
[ nLeft
];
561 nSumRowB
+= ( nWeightX
*rCol
.GetBlue()) ;
562 nSumRowG
+= ( nWeightX
*rCol
.GetGreen()) ;
563 nSumRowR
+= ( nWeightX
*rCol
.GetRed()) ;
564 nTotalWeightX
+= nWeightX
;
566 else if ( nRowRange
== j
)
568 BilinearWeightType nWeightX
= rCtx
.maMapFX
[ nRight
] ;
569 nSumRowB
+= ( nWeightX
*rCol
.GetBlue() );
570 nSumRowG
+= ( nWeightX
*rCol
.GetGreen() );
571 nSumRowR
+= ( nWeightX
*rCol
.GetRed() );
572 nTotalWeightX
+= nWeightX
;
576 nSumRowB
+= rCol
.GetBlue() << MAP_PRECISION
;
577 nSumRowG
+= rCol
.GetGreen() << MAP_PRECISION
;
578 nSumRowR
+= rCol
.GetRed() << MAP_PRECISION
;
579 nTotalWeightX
+= lclMaxWeight();
583 BilinearWeightType nWeightY
= lclMaxWeight();
585 nWeightY
= lclMaxWeight();
587 nWeightY
= lclMaxWeight() - rCtx
.maMapFY
[ nTop
];
588 else if( nLineRange
== 1 )
589 nWeightY
= rCtx
.maMapFY
[ nTop
];
590 else if ( nLineRange
== i
)
591 nWeightY
= rCtx
.maMapFY
[ nBottom
];
595 nSumRowB
/= nTotalWeightX
;
596 nSumRowG
/= nTotalWeightX
;
597 nSumRowR
/= nTotalWeightX
;
600 nSumB
+= nWeightY
* nSumRowB
;
601 nSumG
+= nWeightY
* nSumRowG
;
602 nSumR
+= nWeightY
* nSumRowR
;
603 nTotalWeightY
+= nWeightY
;
608 nSumR
/= nTotalWeightY
;
609 nSumG
/= nTotalWeightY
;
610 nSumB
/= nTotalWeightY
;
613 BitmapColor
aColRes(static_cast<sal_uInt8
>(nSumR
), static_cast<sal_uInt8
>(nSumG
), static_cast<sal_uInt8
>(nSumB
));
614 rCtx
.mpDest
->SetPixelOnData( pScanDest
, nXDst
++, aColRes
);
619 void scaleDownPaletteGeneral(ScaleContext
&rCtx
, long nStartY
, long nEndY
)
621 const long nStartX
= 0, nEndX
= rCtx
.mnDestW
- 1;
623 for( long nY
= nStartY
; nY
<= nEndY
; nY
++ )
625 long nTop
= rCtx
.mbVMirr
? ( nY
+ 1 ) : nY
;
626 long nBottom
= rCtx
.mbVMirr
? nY
: ( nY
+ 1 ) ;
628 long nLineStart
, nLineRange
;
631 nLineStart
= rCtx
.maMapIY
[ nY
];
636 nLineStart
= rCtx
.maMapIY
[ nTop
] ;
637 nLineRange
= ( rCtx
.maMapIY
[ nBottom
] == rCtx
.maMapIY
[ nTop
] ) ? 1 :( rCtx
.maMapIY
[ nBottom
] - rCtx
.maMapIY
[ nTop
] );
640 Scanline pScanDest
= rCtx
.mpDest
->GetScanline( nY
);
641 for( long nX
= nStartX
, nXDst
= 0; nX
<= nEndX
; nX
++ )
643 long nLeft
= rCtx
.mbHMirr
? ( nX
+ 1 ) : nX
;
644 long nRight
= rCtx
.mbHMirr
? nX
: ( nX
+ 1 ) ;
646 long nRowStart
, nRowRange
;
649 nRowStart
= rCtx
.maMapIX
[ nX
];
654 nRowStart
= rCtx
.maMapIX
[ nLeft
];
655 nRowRange
= ( rCtx
.maMapIX
[ nRight
] == rCtx
.maMapIX
[ nLeft
] )? 1 : ( rCtx
.maMapIX
[ nRight
] - rCtx
.maMapIX
[ nLeft
] );
661 BilinearWeightType nTotalWeightY
= 0;
663 for(long i
= 0; i
<= nLineRange
; i
++)
668 BilinearWeightType nTotalWeightX
= 0;
670 Scanline pScanlineSrc
= rCtx
.mpSrc
->GetScanline( nLineStart
+ i
);
671 for(long j
= 0; j
<= nRowRange
; j
++)
673 BitmapColor aCol0
= rCtx
.mpSrc
->GetPaletteColor ( rCtx
.mpSrc
->GetIndexFromData( pScanlineSrc
, nRowStart
+ j
) );
678 nSumRowB
+= aCol0
.GetBlue() << MAP_PRECISION
;
679 nSumRowG
+= aCol0
.GetGreen() << MAP_PRECISION
;
680 nSumRowR
+= aCol0
.GetRed() << MAP_PRECISION
;
681 nTotalWeightX
+= lclMaxWeight();
686 BilinearWeightType nWeightX
= lclMaxWeight() - rCtx
.maMapFX
[ nLeft
];
687 nSumRowB
+= ( nWeightX
*aCol0
.GetBlue()) ;
688 nSumRowG
+= ( nWeightX
*aCol0
.GetGreen()) ;
689 nSumRowR
+= ( nWeightX
*aCol0
.GetRed()) ;
690 nTotalWeightX
+= nWeightX
;
692 else if ( nRowRange
== j
)
695 BilinearWeightType nWeightX
= rCtx
.maMapFX
[ nRight
] ;
696 nSumRowB
+= ( nWeightX
*aCol0
.GetBlue() );
697 nSumRowG
+= ( nWeightX
*aCol0
.GetGreen() );
698 nSumRowR
+= ( nWeightX
*aCol0
.GetRed() );
699 nTotalWeightX
+= nWeightX
;
704 nSumRowB
+= aCol0
.GetBlue() << MAP_PRECISION
;
705 nSumRowG
+= aCol0
.GetGreen() << MAP_PRECISION
;
706 nSumRowR
+= aCol0
.GetRed() << MAP_PRECISION
;
707 nTotalWeightX
+= lclMaxWeight();
711 long nWeightY
= lclMaxWeight();
713 nWeightY
= lclMaxWeight();
715 nWeightY
= lclMaxWeight() - rCtx
.maMapFY
[ nTop
];
716 else if( nLineRange
== 1 )
717 nWeightY
= rCtx
.maMapFY
[ nTop
];
718 else if ( nLineRange
== i
)
719 nWeightY
= rCtx
.maMapFY
[ nBottom
];
723 nSumRowB
/= nTotalWeightX
;
724 nSumRowG
/= nTotalWeightX
;
725 nSumRowR
/= nTotalWeightX
;
728 nSumB
+= nWeightY
* nSumRowB
;
729 nSumG
+= nWeightY
* nSumRowG
;
730 nSumR
+= nWeightY
* nSumRowR
;
731 nTotalWeightY
+= nWeightY
;
736 nSumR
/= nTotalWeightY
;
737 nSumG
/= nTotalWeightY
;
738 nSumB
/= nTotalWeightY
;
741 BitmapColor
aColRes(static_cast<sal_uInt8
>(nSumR
), static_cast<sal_uInt8
>(nSumG
), static_cast<sal_uInt8
>(nSumB
));
742 rCtx
.mpDest
->SetPixelOnData( pScanDest
, nXDst
++, aColRes
);
747 void scaleDown24bit(ScaleContext
&rCtx
, long nStartY
, long nEndY
)
749 const int constColorComponents
= 3;
751 const long nStartX
= 0;
752 const long nEndX
= rCtx
.mnDestW
- 1;
754 for (long nY
= nStartY
; nY
<= nEndY
; nY
++)
756 long nTop
= rCtx
.mbVMirr
? (nY
+ 1) : nY
;
757 long nBottom
= rCtx
.mbVMirr
? nY
: (nY
+ 1);
763 nLineStart
= rCtx
.maMapIY
[nY
];
768 nLineStart
= rCtx
.maMapIY
[nTop
];
769 nLineRange
= (rCtx
.maMapIY
[nBottom
] == rCtx
.maMapIY
[nTop
]) ?
770 1 : (rCtx
.maMapIY
[nBottom
] - rCtx
.maMapIY
[nTop
]);
773 Scanline pScanDest
= rCtx
.mpDest
->GetScanline(nY
);
774 for (long nX
= nStartX
; nX
<= nEndX
; nX
++)
776 long nLeft
= rCtx
.mbHMirr
? (nX
+ 1) : nX
;
777 long nRight
= rCtx
.mbHMirr
? nX
: (nX
+ 1);
783 nRowStart
= rCtx
.maMapIX
[nX
];
788 nRowStart
= rCtx
.maMapIX
[nLeft
];
789 nRowRange
= (rCtx
.maMapIX
[nRight
] == rCtx
.maMapIX
[nLeft
]) ?
790 1 : (rCtx
.maMapIX
[nRight
] - rCtx
.maMapIX
[nLeft
]);
796 BilinearWeightType nTotalWeightY
= 0;
798 for (long i
= 0; i
<= nLineRange
; i
++)
800 Scanline pTmpY
= rCtx
.mpSrc
->GetScanline(nLineStart
+ i
);
801 Scanline pTmpX
= pTmpY
+ constColorComponents
* nRowStart
;
806 BilinearWeightType nTotalWeightX
= 0;
808 for (long j
= 0; j
<= nRowRange
; j
++)
812 nSumRow1
+= (*pTmpX
) << MAP_PRECISION
; pTmpX
++;
813 nSumRow2
+= (*pTmpX
) << MAP_PRECISION
; pTmpX
++;
814 nSumRow3
+= (*pTmpX
) << MAP_PRECISION
; pTmpX
++;
815 nTotalWeightX
+= lclMaxWeight();
819 BilinearWeightType nWeightX
= lclMaxWeight() - rCtx
.maMapFX
[nLeft
];
820 nSumRow1
+= (nWeightX
* (*pTmpX
)); pTmpX
++;
821 nSumRow2
+= (nWeightX
* (*pTmpX
)); pTmpX
++;
822 nSumRow3
+= (nWeightX
* (*pTmpX
)); pTmpX
++;
823 nTotalWeightX
+= nWeightX
;
825 else if ( nRowRange
== j
)
827 BilinearWeightType nWeightX
= rCtx
.maMapFX
[ nRight
] ;
828 nSumRow1
+= (nWeightX
* (*pTmpX
)); pTmpX
++;
829 nSumRow2
+= (nWeightX
* (*pTmpX
)); pTmpX
++;
830 nSumRow3
+= (nWeightX
* (*pTmpX
)); pTmpX
++;
831 nTotalWeightX
+= nWeightX
;
835 nSumRow1
+= (*pTmpX
) << MAP_PRECISION
; pTmpX
++;
836 nSumRow2
+= (*pTmpX
) << MAP_PRECISION
; pTmpX
++;
837 nSumRow3
+= (*pTmpX
) << MAP_PRECISION
; pTmpX
++;
838 nTotalWeightX
+= lclMaxWeight();
842 BilinearWeightType nWeightY
= lclMaxWeight();
844 nWeightY
= lclMaxWeight();
846 nWeightY
= lclMaxWeight() - rCtx
.maMapFY
[nTop
];
847 else if (nLineRange
== 1)
848 nWeightY
= rCtx
.maMapFY
[nTop
];
849 else if (nLineRange
== i
)
850 nWeightY
= rCtx
.maMapFY
[nBottom
];
854 nSumRow1
/= nTotalWeightX
;
855 nSumRow2
/= nTotalWeightX
;
856 nSumRow3
/= nTotalWeightX
;
858 nSum1
+= nWeightY
* nSumRow1
;
859 nSum2
+= nWeightY
* nSumRow2
;
860 nSum3
+= nWeightY
* nSumRow3
;
861 nTotalWeightY
+= nWeightY
;
866 nSum1
/= nTotalWeightY
;
867 nSum2
/= nTotalWeightY
;
868 nSum3
/= nTotalWeightY
;
871 // Write the calculated color components to the destination
872 *pScanDest
= nSum1
; pScanDest
++;
873 *pScanDest
= nSum2
; pScanDest
++;
874 *pScanDest
= nSum3
; pScanDest
++;
879 void scaleDownNonPaletteGeneral(ScaleContext
&rCtx
, long nStartY
, long nEndY
)
881 const long nStartX
= 0, nEndX
= rCtx
.mnDestW
- 1;
883 for( long nY
= nStartY
; nY
<= nEndY
; nY
++ )
885 long nTop
= rCtx
.mbVMirr
? ( nY
+ 1 ) : nY
;
886 long nBottom
= rCtx
.mbVMirr
? nY
: ( nY
+ 1 ) ;
888 long nLineStart
, nLineRange
;
891 nLineStart
= rCtx
.maMapIY
[ nY
];
896 nLineStart
= rCtx
.maMapIY
[ nTop
] ;
897 nLineRange
= ( rCtx
.maMapIY
[ nBottom
] == rCtx
.maMapIY
[ nTop
] ) ? 1 :( rCtx
.maMapIY
[ nBottom
] - rCtx
.maMapIY
[ nTop
] );
900 Scanline pScanDest
= rCtx
.mpDest
->GetScanline( nY
);
901 for( long nX
= nStartX
, nXDst
= 0; nX
<= nEndX
; nX
++ )
903 long nLeft
= rCtx
.mbHMirr
? ( nX
+ 1 ) : nX
;
904 long nRight
= rCtx
.mbHMirr
? nX
: ( nX
+ 1 ) ;
906 long nRowStart
, nRowRange
;
909 nRowStart
= rCtx
.maMapIX
[ nX
];
914 nRowStart
= rCtx
.maMapIX
[ nLeft
];
915 nRowRange
= ( rCtx
.maMapIX
[ nRight
] == rCtx
.maMapIX
[ nLeft
] )? 1 : ( rCtx
.maMapIX
[ nRight
] - rCtx
.maMapIX
[ nLeft
] );
921 BilinearWeightType nTotalWeightY
= 0;
923 for(long i
= 0; i
<= nLineRange
; i
++)
928 BilinearWeightType nTotalWeightX
= 0;
930 Scanline pScanlineSrc
= rCtx
.mpSrc
->GetScanline( nLineStart
+ i
);
931 for(long j
= 0; j
<= nRowRange
; j
++)
933 BitmapColor aCol0
= rCtx
.mpSrc
->GetPixelFromData( pScanlineSrc
, nRowStart
+ j
);
938 nSumRowB
+= aCol0
.GetBlue() << MAP_PRECISION
;
939 nSumRowG
+= aCol0
.GetGreen() << MAP_PRECISION
;
940 nSumRowR
+= aCol0
.GetRed() << MAP_PRECISION
;
941 nTotalWeightX
+= lclMaxWeight();
946 BilinearWeightType nWeightX
= lclMaxWeight() - rCtx
.maMapFX
[ nLeft
];
947 nSumRowB
+= ( nWeightX
*aCol0
.GetBlue()) ;
948 nSumRowG
+= ( nWeightX
*aCol0
.GetGreen()) ;
949 nSumRowR
+= ( nWeightX
*aCol0
.GetRed()) ;
950 nTotalWeightX
+= nWeightX
;
952 else if ( nRowRange
== j
)
955 BilinearWeightType nWeightX
= rCtx
.maMapFX
[ nRight
] ;
956 nSumRowB
+= ( nWeightX
*aCol0
.GetBlue() );
957 nSumRowG
+= ( nWeightX
*aCol0
.GetGreen() );
958 nSumRowR
+= ( nWeightX
*aCol0
.GetRed() );
959 nTotalWeightX
+= nWeightX
;
963 nSumRowB
+= aCol0
.GetBlue() << MAP_PRECISION
;
964 nSumRowG
+= aCol0
.GetGreen() << MAP_PRECISION
;
965 nSumRowR
+= aCol0
.GetRed() << MAP_PRECISION
;
966 nTotalWeightX
+= lclMaxWeight();
970 BilinearWeightType nWeightY
= lclMaxWeight();
972 nWeightY
= lclMaxWeight();
974 nWeightY
= lclMaxWeight() - rCtx
.maMapFY
[ nTop
];
975 else if( nLineRange
== 1 )
976 nWeightY
= rCtx
.maMapFY
[ nTop
];
977 else if ( nLineRange
== i
)
978 nWeightY
= rCtx
.maMapFY
[ nBottom
];
982 nSumRowB
/= nTotalWeightX
;
983 nSumRowG
/= nTotalWeightX
;
984 nSumRowR
/= nTotalWeightX
;
987 nSumB
+= nWeightY
* nSumRowB
;
988 nSumG
+= nWeightY
* nSumRowG
;
989 nSumR
+= nWeightY
* nSumRowR
;
990 nTotalWeightY
+= nWeightY
;
995 nSumR
/= nTotalWeightY
;
996 nSumG
/= nTotalWeightY
;
997 nSumB
/= nTotalWeightY
;
1000 BitmapColor
aColRes(static_cast<sal_uInt8
>(nSumR
), static_cast<sal_uInt8
>(nSumG
), static_cast<sal_uInt8
>(nSumB
));
1001 rCtx
.mpDest
->SetPixelOnData( pScanDest
, nXDst
++, aColRes
);
1006 } // end anonymous namespace
1008 BitmapScaleSuperFilter::BitmapScaleSuperFilter(const double& rScaleX
, const double& rScaleY
) :
1013 BitmapScaleSuperFilter::~BitmapScaleSuperFilter()
1016 BitmapEx
BitmapScaleSuperFilter::execute(BitmapEx
const& rBitmap
) const
1018 Bitmap
aBitmap(rBitmap
.GetBitmap());
1019 SalBitmap
* pKey
= aBitmap
.ImplGetSalBitmap().get();
1023 const Size
aSizePix(rBitmap
.GetSizePixel());
1025 bool bHMirr
= mrScaleX
< 0;
1026 bool bVMirr
= mrScaleY
< 0;
1028 double fScaleX
= std::fabs(mrScaleX
);
1029 double fScaleY
= std::fabs(mrScaleY
);
1031 const long nDstW
= FRound(aSizePix
.Width() * fScaleX
);
1032 const long nDstH
= FRound(aSizePix
.Height() * fScaleY
);
1034 const double fScaleThresh
= 0.6;
1036 if (nDstW
<= 1 || nDstH
<= 1)
1039 // check cache for a previously scaled version of this
1040 ImplSVData
* pSVData
= ImplGetSVData();
1041 auto& rCache
= pSVData
->maGDIData
.maScaleCache
;
1042 auto aFind
= rCache
.find(pKey
);
1043 if (aFind
!= rCache
.end())
1045 if (aFind
->second
.GetSizePixel().Width() == nDstW
&& aFind
->second
.GetSizePixel().Height() == nDstH
)
1046 return aFind
->second
;
1050 Bitmap::ScopedReadAccess
pReadAccess(aBitmap
);
1052 sal_uInt16 nSourceBitcount
= aBitmap
.GetBitCount();
1054 Bitmap
aOutBmp(Size(nDstW
, nDstH
), std::max(nSourceBitcount
, sal_uInt16(24)));
1055 Size aOutSize
= aOutBmp
.GetSizePixel();
1056 sal_uInt16 nTargetBitcount
= aOutBmp
.GetBitCount();
1058 if (!aOutSize
.Width() || !aOutSize
.Height())
1060 SAL_WARN("vcl.gdi", "bmp creation failed");
1064 BitmapScopedWriteAccess
pWriteAccess(aOutBmp
);
1066 const long nStartY
= 0;
1067 const long nEndY
= nDstH
- 1;
1069 if (pReadAccess
&& pWriteAccess
)
1071 ScaleRangeFn pScaleRangeFn
;
1072 ScaleContext
aContext( pReadAccess
.get(),
1074 pReadAccess
->Width(),
1075 pWriteAccess
->Width(),
1076 pReadAccess
->Height(),
1077 pWriteAccess
->Height(),
1080 bool bScaleUp
= fScaleX
>= fScaleThresh
&& fScaleY
>= fScaleThresh
;
1081 // If we have a source bitmap with a palette the scaling converts
1082 // from up to 8 bit image -> 24 bit non-palette, which is then
1083 // adapted back to the same type as original.
1084 if (pReadAccess
->HasPalette())
1086 switch( pReadAccess
->GetScanlineFormat() )
1088 case ScanlineFormat::N8BitPal
:
1089 pScaleRangeFn
= bScaleUp
? scaleUpPalette8bit
1090 : scaleDownPalette8bit
;
1093 pScaleRangeFn
= bScaleUp
? scaleUpPaletteGeneral
1094 : scaleDownPaletteGeneral
;
1098 // Here we know that we are dealing with a non-palette source bitmap.
1099 // The target is either 24 or 32 bit, depending on the image and
1100 // the capabilities of the backend. If for some reason the destination
1101 // is not the same bit-depth as the source, then we can't use
1102 // a fast path, so we always need to process with a general scaler.
1103 else if (nSourceBitcount
!= nTargetBitcount
)
1105 pScaleRangeFn
= bScaleUp
? scaleUpNonPaletteGeneral
: scaleDownNonPaletteGeneral
;
1107 // If we get here then we can only use a fast path, but let's
1108 // still keep the fallback to the general scaler alive.
1111 switch( pReadAccess
->GetScanlineFormat() )
1113 case ScanlineFormat::N24BitTcBgr
:
1114 case ScanlineFormat::N24BitTcRgb
:
1115 pScaleRangeFn
= bScaleUp
? scaleUp24bit
: scaleDown24bit
;
1117 case ScanlineFormat::N32BitTcRgba
:
1118 case ScanlineFormat::N32BitTcBgra
:
1119 case ScanlineFormat::N32BitTcArgb
:
1120 case ScanlineFormat::N32BitTcAbgr
:
1121 pScaleRangeFn
= bScaleUp
? scaleUp32bit
: scaleDown32bit
;
1124 pScaleRangeFn
= bScaleUp
? scaleUpNonPaletteGeneral
1125 : scaleDownNonPaletteGeneral
;
1130 // We want to thread - only if there is a lot of work to do:
1131 // We work hard when there is a large destination image, or
1132 // A large source image.
1133 bool bHorizontalWork
= pReadAccess
->Height() >= 512 && pReadAccess
->Width() >= 512;
1134 bool bUseThreads
= true;
1136 static bool bDisableThreadedScaling
= getenv ("VCL_NO_THREAD_SCALE");
1137 if (bDisableThreadedScaling
|| !bHorizontalWork
)
1139 SAL_INFO("vcl.gdi", "Scale in main thread");
1140 bUseThreads
= false;
1147 // partition and queue work
1148 comphelper::ThreadPool
&rShared
= comphelper::ThreadPool::getSharedOptimalPool();
1149 std::shared_ptr
<comphelper::ThreadTaskTag
> pTag
= comphelper::ThreadPool::createThreadTaskTag();
1151 long nStripYStart
= nStartY
;
1152 long nStripYEnd
= nStripYStart
+ constScaleThreadStrip
- 1;
1154 while (nStripYEnd
< nEndY
)
1156 std::unique_ptr
<ScaleTask
> pTask(new ScaleTask(pTag
, pScaleRangeFn
, aContext
, nStripYStart
, nStripYEnd
));
1157 rShared
.pushTask(std::move(pTask
));
1158 nStripYStart
+= constScaleThreadStrip
;
1159 nStripYEnd
+= constScaleThreadStrip
;
1161 if (nStripYStart
<= nEndY
)
1163 std::unique_ptr
<ScaleTask
> pTask(new ScaleTask(pTag
, pScaleRangeFn
, aContext
, nStripYStart
, nEndY
));
1164 rShared
.pushTask(std::move(pTask
));
1166 rShared
.waitUntilDone(pTag
);
1167 SAL_INFO("vcl.gdi", "All threaded scaling tasks complete");
1171 SAL_WARN("vcl.gdi", "threaded bitmap scaling failed");
1172 bUseThreads
= false;
1177 pScaleRangeFn( aContext
, nStartY
, nEndY
);
1180 aBitmap
.AdaptBitCount(aOutBmp
);
1187 tools::Rectangle
aRect(Point(0, 0), Point(nDstW
, nDstH
));
1188 aBitmap
.Crop(aRect
);
1189 BitmapEx
aRet(aBitmap
);
1190 rCache
.insert(std::make_pair(pKey
, aRet
));
1198 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */