tdf#130857 qt weld: Implement QtInstanceWidget::get_text_height
[LibreOffice.git] / vcl / source / bitmap / BitmapScaleSuperFilter.cxx
bloba5f478948c047c5e33f40a209b0ef344afbe85cd
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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/profilezone.hxx>
21 #include <comphelper/threadpool.hxx>
22 #include <tools/helpers.hxx>
24 #include <vcl/BitmapWriteAccess.hxx>
25 #include <bitmap/BitmapScaleSuperFilter.hxx>
27 #include <algorithm>
28 #include <memory>
29 #include <svdata.hxx>
30 #include <sal/log.hxx>
33 A scaling algorithm that uses bilinear if not downscaling too much,
34 and averaging otherwise (bilinear would produce poor results for big downscaling).
36 By default the combination of two filters is used: bilinear and averaging algorithm.
37 Bilinear filtering is used for bitmap enlarging and shrinking till factor 0.6. Below
38 this bilinear gives bad results because of limited sampling. For such cases averaging
39 is used which is a simple algorithm for shrinking. In averaging the algorithm
40 calculates the average of samples which result is the new pixel.
43 namespace {
45 constexpr int MAP_PRECISION = 7;
47 typedef sal_Int32 BilinearWeightType;
49 constexpr BilinearWeightType lclMaxWeight()
51 return BilinearWeightType(1) << MAP_PRECISION;
54 constexpr sal_uInt8 MAP(sal_uInt8 cVal0, sal_uInt8 cVal1, BilinearWeightType nFrac)
56 return sal_uInt8(((BilinearWeightType(cVal0) << MAP_PRECISION) + nFrac * (BilinearWeightType(cVal1) - BilinearWeightType(cVal0))) >> MAP_PRECISION);
59 struct ScaleContext
61 BitmapReadAccess* mpSrc;
62 BitmapWriteAccess* mpDest;
63 sal_Int32 mnDestW;
64 bool mbHMirr;
65 bool mbVMirr;
66 std::vector<sal_Int32> maMapIX;
67 std::vector<sal_Int32> maMapIY;
68 std::vector<BilinearWeightType> maMapFX;
69 std::vector<BilinearWeightType> maMapFY;
71 ScaleContext( BitmapReadAccess *pSrc,
72 BitmapWriteAccess *pDest,
73 sal_Int32 nSrcW, sal_Int32 nDestW,
74 sal_Int32 nSrcH, sal_Int32 nDestH,
75 bool bHMirr, bool bVMirr)
76 : mpSrc(pSrc)
77 , mpDest(pDest)
78 , mnDestW(nDestW)
79 , mbHMirr(bHMirr)
80 , mbVMirr(bVMirr)
81 , maMapIX(nDestW)
82 , maMapIY(nDestH)
83 , maMapFX(nDestW)
84 , maMapFY(nDestH)
86 generateMap(nSrcW, nDestW, bHMirr, maMapIX, maMapFX);
87 generateMap(nSrcH, nDestH, bVMirr, maMapIY, maMapFY);
90 static void generateMap(sal_Int32 nSourceLength, sal_Int32 nDestinationLength, bool bMirrored,
91 std::vector<sal_Int32> & rMapIX, std::vector<BilinearWeightType> & rMapFX)
93 const double fRevScale = (nDestinationLength > 1) ? double(nSourceLength - 1) / (nDestinationLength - 1) : 0.0;
95 sal_Int32 nTemp = nSourceLength - 2;
96 sal_Int32 nTempX = nSourceLength - 1;
98 for (sal_Int32 i = 0; i < nDestinationLength; i++)
100 double fTemp = i * fRevScale;
101 if (bMirrored)
102 fTemp = nTempX - fTemp;
103 rMapIX[i] = std::clamp(sal_Int32(fTemp), sal_Int32(0), nTemp);
104 rMapFX[i] = BilinearWeightType((fTemp - rMapIX[i]) * (BilinearWeightType(1) << MAP_PRECISION));
109 constexpr sal_Int32 constScaleThreadStrip = 32;
111 typedef void (*ScaleRangeFn)(const ScaleContext & rContext, sal_Int32 nStartY, sal_Int32 nEndY);
113 template <size_t nSize> struct ScaleFunc
115 // for scale down
117 static inline void generateSumRows(Scanline& pTmpX, std::array<int, nSize>& sumRows)
119 for (int& n : sumRows)
120 n += (*pTmpX++) << MAP_PRECISION;
123 static inline void generateSumRows(BilinearWeightType const nWeightX, Scanline& pTmpX,
124 std::array<int, nSize>& sumRows)
126 for (int& n : sumRows)
127 n += (nWeightX * (*pTmpX++));
130 static inline void generateSumRows(BilinearWeightType const nTotalWeightX,
131 std::array<int, nSize>& sumRows)
133 for (int& n : sumRows)
134 n /= nTotalWeightX;
137 static inline void generateSumNumbers(BilinearWeightType const nWeightY,
138 std::array<int, nSize>& sumRows,
139 std::array<int, nSize>& sumNumbers)
141 std::transform(sumRows.begin(), sumRows.end(), sumNumbers.begin(), sumNumbers.begin(),
142 [nWeightY](int n1, int n2) { return nWeightY * n1 + n2; });
145 static inline void generateSumNumbers(BilinearWeightType const nTotalWeightY,
146 std::array<int, nSize>& sumNumbers)
148 for (int& n : sumNumbers)
149 n /= nTotalWeightY;
152 static inline void calculateDestination(Scanline& pScanDest, std::array<int, nSize>& sumNumbers)
154 pScanDest = std::copy(sumNumbers.begin(), sumNumbers.end(), pScanDest);
157 // for scale up
159 static inline void generateComponent(Scanline pColorPtr0, Scanline pColorPtr1,
160 BilinearWeightType const nTempFX,
161 std::array<sal_uInt8, nSize>& nComponents)
163 for (sal_uInt8& rComponent : nComponents)
164 rComponent = MAP(*pColorPtr0++, *pColorPtr1++, nTempFX);
167 static inline void calculateDestination(Scanline& pScanDest, BilinearWeightType const nTempFY,
168 const std::array<sal_uInt8, nSize>& nComponents1,
169 const std::array<sal_uInt8, nSize>& nComponents2)
171 pScanDest = std::transform(
172 nComponents1.begin(), nComponents1.end(), nComponents2.begin(), pScanDest,
173 [nTempFY](sal_uInt8 c1, sal_uInt8 c2) { return MAP(c1, c2, nTempFY); });
177 template <int nColorBits>
178 void scaleDown (const ScaleContext &rCtx, sal_Int32 nStartY, sal_Int32 nEndY)
180 comphelper::ProfileZone pz("BitmapScaleSuperFilter::scaleDown");
181 constexpr int nColorComponents = nColorBits / 8;
182 static_assert(nColorComponents * 8 == nColorBits, "nColorBits must be divisible by 8");
183 using ScaleFunction = ScaleFunc<nColorComponents>;
184 const sal_Int32 nStartX = 0;
185 const sal_Int32 nEndX = rCtx.mnDestW - 1;
187 for (sal_Int32 nY = nStartY; nY <= nEndY; nY++)
189 sal_Int32 nTop = rCtx.mbVMirr ? (nY + 1) : nY;
190 sal_Int32 nBottom = rCtx.mbVMirr ? nY : (nY + 1);
192 sal_Int32 nLineStart;
193 sal_Int32 nLineRange;
194 if (nY == nEndY)
196 nLineStart = rCtx.maMapIY[nY];
197 nLineRange = 0;
199 else
201 nLineStart = rCtx.maMapIY[nTop];
202 nLineRange = (rCtx.maMapIY[nBottom] == rCtx.maMapIY[nTop]) ?
203 1 : (rCtx.maMapIY[nBottom] - rCtx.maMapIY[nTop]);
206 Scanline pScanDest = rCtx.mpDest->GetScanline(nY);
207 for (sal_Int32 nX = nStartX; nX <= nEndX; nX++)
209 sal_Int32 nLeft = rCtx.mbHMirr ? (nX + 1) : nX;
210 sal_Int32 nRight = rCtx.mbHMirr ? nX : (nX + 1);
212 sal_Int32 nRowStart;
213 sal_Int32 nRowRange;
214 if (nX == nEndX)
216 nRowStart = rCtx.maMapIX[nX];
217 nRowRange = 0;
219 else
221 nRowStart = rCtx.maMapIX[nLeft];
222 nRowRange = (rCtx.maMapIX[nRight] == rCtx.maMapIX[nLeft]) ?
223 1 : (rCtx.maMapIX[nRight] - rCtx.maMapIX[nLeft]);
226 std::array<int, nColorComponents> sumNumbers{}; // zero-initialize
227 BilinearWeightType nTotalWeightY = 0;
229 for (sal_Int32 i = 0; i<= nLineRange; i++)
231 Scanline pTmpY = rCtx.mpSrc->GetScanline(nLineStart + i);
232 Scanline pTmpX = pTmpY + nColorComponents * nRowStart;
234 std::array<int, nColorComponents> sumRows{}; // zero-initialize
235 BilinearWeightType nTotalWeightX = 0;
237 for (sal_Int32 j = 0; j <= nRowRange; j++)
239 if (nX == nEndX)
241 ScaleFunction::generateSumRows(pTmpX, sumRows);
242 nTotalWeightX += lclMaxWeight();
244 else if(j == 0)
246 BilinearWeightType nWeightX = lclMaxWeight() - rCtx.maMapFX[nLeft];
247 ScaleFunction::generateSumRows(nWeightX, pTmpX, sumRows);
248 nTotalWeightX += nWeightX;
250 else if ( nRowRange == j )
252 BilinearWeightType nWeightX = rCtx.maMapFX[ nRight ] ;
253 ScaleFunction::generateSumRows(nWeightX, pTmpX, sumRows);
254 nTotalWeightX += nWeightX;
256 else
258 ScaleFunction::generateSumRows(pTmpX, sumRows);
259 nTotalWeightX += lclMaxWeight();
263 BilinearWeightType nWeightY = lclMaxWeight();
264 if (nY == nEndY)
265 nWeightY = lclMaxWeight();
266 else if (i == 0)
267 nWeightY = lclMaxWeight() - rCtx.maMapFY[nTop];
268 else if (nLineRange == 1)
269 nWeightY = rCtx.maMapFY[nTop];
270 else if (nLineRange == i)
271 nWeightY = rCtx.maMapFY[nBottom];
273 if (nTotalWeightX)
275 ScaleFunction::generateSumRows(nTotalWeightX, sumRows);
277 ScaleFunction::generateSumNumbers(nWeightY, sumRows, sumNumbers);
278 nTotalWeightY += nWeightY;
282 if (nTotalWeightY)
284 ScaleFunction::generateSumNumbers(nTotalWeightY, sumNumbers);
287 // Write the calculated color components to the destination
288 ScaleFunction::calculateDestination(pScanDest, sumNumbers);
293 template <int nColorBits>
294 void scaleUp(const ScaleContext &rCtx, sal_Int32 nStartY, sal_Int32 nEndY)
296 comphelper::ProfileZone pz("BitmapScaleSuperFilter::scaleUp");
297 constexpr int nColorComponents = nColorBits / 8;
298 static_assert(nColorComponents * 8 == nColorBits, "nColorBits must be divisible by 8");
299 using ScaleFunction = ScaleFunc<nColorComponents>;
300 const sal_Int32 nStartX = 0;
301 const sal_Int32 nEndX = rCtx.mnDestW - 1;
303 for (sal_Int32 nY = nStartY; nY <= nEndY; nY++)
305 sal_Int32 nTempY = rCtx.maMapIY[nY];
306 BilinearWeightType nTempFY = rCtx.maMapFY[nY];
308 Scanline pLine0 = rCtx.mpSrc->GetScanline(nTempY+0);
309 Scanline pLine1 = rCtx.mpSrc->GetScanline(nTempY+1);
310 Scanline pScanDest = rCtx.mpDest->GetScanline(nY);
312 std::array<sal_uInt8, nColorComponents> nComponents1; // no need to initialize since it's
313 std::array<sal_uInt8, nColorComponents> nComponents2; // initialized in generateComponent
315 Scanline pColorPtr0;
316 Scanline pColorPtr1;
318 for (sal_Int32 nX = nStartX; nX <= nEndX; nX++)
320 sal_Int32 nTempX = rCtx.maMapIX[nX];
321 BilinearWeightType nTempFX = rCtx.maMapFX[nX];
323 pColorPtr0 = pLine0 + nTempX * nColorComponents;
324 pColorPtr1 = pColorPtr0 + nColorComponents;
326 ScaleFunction::generateComponent(pColorPtr0, pColorPtr1, nTempFX, nComponents1);
328 pColorPtr0 = pLine1 + nTempX * nColorComponents;
329 pColorPtr1 = pColorPtr0 + nColorComponents;
331 ScaleFunction::generateComponent(pColorPtr0, pColorPtr1, nTempFX, nComponents2);
332 ScaleFunction::calculateDestination(pScanDest, nTempFY, nComponents1, nComponents2);
337 class ScaleTask : public comphelper::ThreadTask
339 ScaleRangeFn mpScaleRangeFunction;
340 const ScaleContext& mrContext;
341 sal_Int32 mnStartY;
342 sal_Int32 mnEndY;
344 public:
345 explicit ScaleTask(const std::shared_ptr<comphelper::ThreadTaskTag>& pTag,
346 ScaleRangeFn pScaleRangeFunction,
347 const ScaleContext& rContext,
348 sal_Int32 nStartY, sal_Int32 nEndY)
349 : comphelper::ThreadTask(pTag)
350 , mpScaleRangeFunction(pScaleRangeFunction)
351 , mrContext(rContext)
352 , mnStartY(nStartY)
353 , mnEndY(nEndY)
356 virtual void doWork() override
358 mpScaleRangeFunction(mrContext, mnStartY, mnEndY);
362 void scaleUpPalette8bit(const ScaleContext &rCtx, sal_Int32 nStartY, sal_Int32 nEndY)
364 const sal_Int32 nStartX = 0, nEndX = rCtx.mnDestW - 1;
366 for( sal_Int32 nY = nStartY; nY <= nEndY; nY++ )
368 sal_Int32 nTempY = rCtx.maMapIY[ nY ];
369 BilinearWeightType nTempFY = rCtx.maMapFY[ nY ];
370 Scanline pLine0 = rCtx.mpSrc->GetScanline( nTempY );
371 Scanline pLine1 = rCtx.mpSrc->GetScanline( ++nTempY );
372 Scanline pScanDest = rCtx.mpDest->GetScanline( nY );
374 for(sal_Int32 nX = nStartX, nXDst = 0; nX <= nEndX; nX++ )
376 sal_Int32 nTempX = rCtx.maMapIX[ nX ];
377 BilinearWeightType nTempFX = rCtx.maMapFX[ nX ];
379 const BitmapColor& rCol0 = rCtx.mpSrc->GetPaletteColor( pLine0[ nTempX ] );
380 const BitmapColor& rCol2 = rCtx.mpSrc->GetPaletteColor( pLine1[ nTempX ] );
381 const BitmapColor& rCol1 = rCtx.mpSrc->GetPaletteColor( pLine0[ ++nTempX ] );
382 const BitmapColor& rCol3 = rCtx.mpSrc->GetPaletteColor( pLine1[ nTempX ] );
384 sal_uInt8 cR0 = MAP( rCol0.GetRed(), rCol1.GetRed(), nTempFX );
385 sal_uInt8 cG0 = MAP( rCol0.GetGreen(), rCol1.GetGreen(), nTempFX );
386 sal_uInt8 cB0 = MAP( rCol0.GetBlue(), rCol1.GetBlue(), nTempFX );
388 sal_uInt8 cR1 = MAP( rCol2.GetRed(), rCol3.GetRed(), nTempFX );
389 sal_uInt8 cG1 = MAP( rCol2.GetGreen(), rCol3.GetGreen(), nTempFX );
390 sal_uInt8 cB1 = MAP( rCol2.GetBlue(), rCol3.GetBlue(), nTempFX );
392 BitmapColor aColRes( MAP( cR0, cR1, nTempFY ),
393 MAP( cG0, cG1, nTempFY ),
394 MAP( cB0, cB1, nTempFY ) );
395 rCtx.mpDest->SetPixelOnData( pScanDest, nXDst++, aColRes );
400 void scaleUpPaletteGeneral(const ScaleContext &rCtx, sal_Int32 nStartY, sal_Int32 nEndY)
402 const sal_Int32 nStartX = 0, nEndX = rCtx.mnDestW - 1;
404 for( sal_Int32 nY = nStartY; nY <= nEndY; nY++ )
406 sal_Int32 nTempY = rCtx.maMapIY[ nY ];
407 BilinearWeightType nTempFY = rCtx.maMapFY[ nY ];
408 Scanline pScanline = rCtx.mpDest->GetScanline( nY );
410 for( sal_Int32 nX = nStartX, nXDst = 0; nX <= nEndX; nX++ )
412 sal_Int32 nTempX = rCtx.maMapIX[ nX ];
413 BilinearWeightType nTempFX = rCtx.maMapFX[ nX ];
415 BitmapColor aCol0 = rCtx.mpSrc->GetPaletteColor( rCtx.mpSrc->GetPixelIndex( nTempY, nTempX ) );
416 BitmapColor aCol1 = rCtx.mpSrc->GetPaletteColor( rCtx.mpSrc->GetPixelIndex( nTempY, ++nTempX ) );
417 sal_uInt8 cR0 = MAP( aCol0.GetRed(), aCol1.GetRed(), nTempFX );
418 sal_uInt8 cG0 = MAP( aCol0.GetGreen(), aCol1.GetGreen(), nTempFX );
419 sal_uInt8 cB0 = MAP( aCol0.GetBlue(), aCol1.GetBlue(), nTempFX );
421 aCol1 = rCtx.mpSrc->GetPaletteColor( rCtx.mpSrc->GetPixelIndex( ++nTempY, nTempX ) );
422 aCol0 = rCtx.mpSrc->GetPaletteColor( rCtx.mpSrc->GetPixelIndex( nTempY--, --nTempX ) );
423 sal_uInt8 cR1 = MAP( aCol0.GetRed(), aCol1.GetRed(), nTempFX );
424 sal_uInt8 cG1 = MAP( aCol0.GetGreen(), aCol1.GetGreen(), nTempFX );
425 sal_uInt8 cB1 = MAP( aCol0.GetBlue(), aCol1.GetBlue(), nTempFX );
427 BitmapColor aColRes( MAP( cR0, cR1, nTempFY ),
428 MAP( cG0, cG1, nTempFY ),
429 MAP( cB0, cB1, nTempFY ) );
430 rCtx.mpDest->SetPixelOnData( pScanline, nXDst++, aColRes );
435 void scaleUpNonPaletteGeneral(const ScaleContext &rCtx, sal_Int32 nStartY, sal_Int32 nEndY)
437 const sal_Int32 nStartX = 0, nEndX = rCtx.mnDestW - 1;
439 for( sal_Int32 nY = nStartY; nY <= nEndY; nY++ )
441 sal_Int32 nTempY = rCtx.maMapIY[ nY ];
442 BilinearWeightType nTempFY = rCtx.maMapFY[ nY ];
443 Scanline pScanDest = rCtx.mpDest->GetScanline( nY );
445 for( sal_Int32 nX = nStartX, nXDst = 0; nX <= nEndX; nX++ )
447 sal_Int32 nTempX = rCtx.maMapIX[ nX ];
448 BilinearWeightType nTempFX = rCtx.maMapFX[ nX ];
450 BitmapColor aCol0 = rCtx.mpSrc->GetPixel( nTempY, nTempX );
451 BitmapColor aCol1 = rCtx.mpSrc->GetPixel( nTempY, ++nTempX );
452 sal_uInt8 cR0 = MAP( aCol0.GetRed(), aCol1.GetRed(), nTempFX );
453 sal_uInt8 cG0 = MAP( aCol0.GetGreen(), aCol1.GetGreen(), nTempFX );
454 sal_uInt8 cB0 = MAP( aCol0.GetBlue(), aCol1.GetBlue(), nTempFX );
456 aCol1 = rCtx.mpSrc->GetPixel( ++nTempY, nTempX );
457 aCol0 = rCtx.mpSrc->GetPixel( nTempY--, --nTempX );
458 sal_uInt8 cR1 = MAP( aCol0.GetRed(), aCol1.GetRed(), nTempFX );
459 sal_uInt8 cG1 = MAP( aCol0.GetGreen(), aCol1.GetGreen(), nTempFX );
460 sal_uInt8 cB1 = MAP( aCol0.GetBlue(), aCol1.GetBlue(), nTempFX );
462 BitmapColor aColRes( MAP( cR0, cR1, nTempFY ),
463 MAP( cG0, cG1, nTempFY ),
464 MAP( cB0, cB1, nTempFY ) );
465 rCtx.mpDest->SetPixelOnData( pScanDest, nXDst++, aColRes );
470 void scaleDownPalette8bit(const ScaleContext &rCtx, sal_Int32 nStartY, sal_Int32 nEndY)
472 const sal_Int32 nStartX = 0, nEndX = rCtx.mnDestW - 1;
474 for( sal_Int32 nY = nStartY; nY <= nEndY; nY++ )
476 sal_Int32 nTop = rCtx.mbVMirr ? ( nY + 1 ) : nY;
477 sal_Int32 nBottom = rCtx.mbVMirr ? nY : ( nY + 1 ) ;
479 sal_Int32 nLineStart, nLineRange;
480 if( nY == nEndY )
482 nLineStart = rCtx.maMapIY[ nY ];
483 nLineRange = 0;
485 else
487 nLineStart = rCtx.maMapIY[ nTop ] ;
488 nLineRange = ( rCtx.maMapIY[ nBottom ] == rCtx.maMapIY[ nTop ] ) ? 1 :( rCtx.maMapIY[ nBottom ] - rCtx.maMapIY[ nTop ] );
491 Scanline pScanDest = rCtx.mpDest->GetScanline( nY );
492 for( sal_Int32 nX = nStartX , nXDst = 0; nX <= nEndX; nX++ )
494 sal_Int32 nLeft = rCtx.mbHMirr ? ( nX + 1 ) : nX;
495 sal_Int32 nRight = rCtx.mbHMirr ? nX : ( nX + 1 ) ;
497 sal_Int32 nRowStart;
498 sal_Int32 nRowRange;
499 if( nX == nEndX )
501 nRowStart = rCtx.maMapIX[ nX ];
502 nRowRange = 0;
504 else
506 nRowStart = rCtx.maMapIX[ nLeft ];
507 nRowRange = ( rCtx.maMapIX[ nRight ] == rCtx.maMapIX[ nLeft ] )? 1 : ( rCtx.maMapIX[ nRight ] - rCtx.maMapIX[ nLeft ] );
510 int nSumR = 0;
511 int nSumG = 0;
512 int nSumB = 0;
513 BilinearWeightType nTotalWeightY = 0;
515 for(sal_Int32 i = 0; i<= nLineRange; i++)
517 Scanline pTmpY = rCtx.mpSrc->GetScanline( nLineStart + i );
518 int nSumRowR = 0;
519 int nSumRowG = 0;
520 int nSumRowB = 0;
521 BilinearWeightType nTotalWeightX = 0;
523 for(sal_Int32 j = 0; j <= nRowRange; j++)
525 const BitmapColor& rCol = rCtx.mpSrc->GetPaletteColor( pTmpY[ nRowStart + j ] );
527 if(nX == nEndX )
529 nSumRowB += rCol.GetBlue() << MAP_PRECISION;
530 nSumRowG += rCol.GetGreen() << MAP_PRECISION;
531 nSumRowR += rCol.GetRed() << MAP_PRECISION;
532 nTotalWeightX += lclMaxWeight();
534 else if( j == 0 )
536 BilinearWeightType nWeightX = lclMaxWeight() - rCtx.maMapFX[ nLeft ];
537 nSumRowB += ( nWeightX *rCol.GetBlue()) ;
538 nSumRowG += ( nWeightX *rCol.GetGreen()) ;
539 nSumRowR += ( nWeightX *rCol.GetRed()) ;
540 nTotalWeightX += nWeightX;
542 else if ( nRowRange == j )
544 BilinearWeightType nWeightX = rCtx.maMapFX[ nRight ] ;
545 nSumRowB += ( nWeightX *rCol.GetBlue() );
546 nSumRowG += ( nWeightX *rCol.GetGreen() );
547 nSumRowR += ( nWeightX *rCol.GetRed() );
548 nTotalWeightX += nWeightX;
550 else
552 nSumRowB += rCol.GetBlue() << MAP_PRECISION;
553 nSumRowG += rCol.GetGreen() << MAP_PRECISION;
554 nSumRowR += rCol.GetRed() << MAP_PRECISION;
555 nTotalWeightX += lclMaxWeight();
559 BilinearWeightType nWeightY = lclMaxWeight();
560 if( nY == nEndY )
561 nWeightY = lclMaxWeight();
562 else if( i == 0 )
563 nWeightY = lclMaxWeight() - rCtx.maMapFY[ nTop ];
564 else if( nLineRange == 1 )
565 nWeightY = rCtx.maMapFY[ nTop ];
566 else if ( nLineRange == i )
567 nWeightY = rCtx.maMapFY[ nBottom ];
569 if (nTotalWeightX)
571 nSumRowB /= nTotalWeightX;
572 nSumRowG /= nTotalWeightX;
573 nSumRowR /= nTotalWeightX;
576 nSumB += nWeightY * nSumRowB;
577 nSumG += nWeightY * nSumRowG;
578 nSumR += nWeightY * nSumRowR;
579 nTotalWeightY += nWeightY;
582 if (nTotalWeightY)
584 nSumR /= nTotalWeightY;
585 nSumG /= nTotalWeightY;
586 nSumB /= nTotalWeightY;
589 BitmapColor aColRes(static_cast<sal_uInt8>(nSumR), static_cast<sal_uInt8>(nSumG), static_cast<sal_uInt8>(nSumB));
590 rCtx.mpDest->SetPixelOnData( pScanDest, nXDst++, aColRes );
595 void scaleDownPaletteGeneral(const ScaleContext &rCtx, sal_Int32 nStartY, sal_Int32 nEndY)
597 const sal_Int32 nStartX = 0, nEndX = rCtx.mnDestW - 1;
599 for( sal_Int32 nY = nStartY; nY <= nEndY; nY++ )
601 sal_Int32 nTop = rCtx.mbVMirr ? ( nY + 1 ) : nY;
602 sal_Int32 nBottom = rCtx.mbVMirr ? nY : ( nY + 1 ) ;
604 sal_Int32 nLineStart, nLineRange;
605 if( nY ==nEndY )
607 nLineStart = rCtx.maMapIY[ nY ];
608 nLineRange = 0;
610 else
612 nLineStart = rCtx.maMapIY[ nTop ] ;
613 nLineRange = ( rCtx.maMapIY[ nBottom ] == rCtx.maMapIY[ nTop ] ) ? 1 :( rCtx.maMapIY[ nBottom ] - rCtx.maMapIY[ nTop ] );
616 Scanline pScanDest = rCtx.mpDest->GetScanline( nY );
617 for( sal_Int32 nX = nStartX , nXDst = 0; nX <= nEndX; nX++ )
619 sal_Int32 nLeft = rCtx.mbHMirr ? ( nX + 1 ) : nX;
620 sal_Int32 nRight = rCtx.mbHMirr ? nX : ( nX + 1 ) ;
622 sal_Int32 nRowStart, nRowRange;
623 if( nX == nEndX )
625 nRowStart = rCtx.maMapIX[ nX ];
626 nRowRange = 0;
628 else
630 nRowStart = rCtx.maMapIX[ nLeft ];
631 nRowRange = ( rCtx.maMapIX[ nRight ] == rCtx.maMapIX[ nLeft ] )? 1 : ( rCtx.maMapIX[ nRight ] - rCtx.maMapIX[ nLeft ] );
634 int nSumR = 0;
635 int nSumG = 0;
636 int nSumB = 0;
637 BilinearWeightType nTotalWeightY = 0;
639 for(sal_Int32 i = 0; i<= nLineRange; i++)
641 int nSumRowR = 0;
642 int nSumRowG = 0;
643 int nSumRowB = 0;
644 BilinearWeightType nTotalWeightX = 0;
646 Scanline pScanlineSrc = rCtx.mpSrc->GetScanline( nLineStart + i );
647 for(sal_Int32 j = 0; j <= nRowRange; j++)
649 BitmapColor aCol0 = rCtx.mpSrc->GetPaletteColor ( rCtx.mpSrc->GetIndexFromData( pScanlineSrc, nRowStart + j ) );
651 if(nX == nEndX )
654 nSumRowB += aCol0.GetBlue() << MAP_PRECISION;
655 nSumRowG += aCol0.GetGreen() << MAP_PRECISION;
656 nSumRowR += aCol0.GetRed() << MAP_PRECISION;
657 nTotalWeightX += lclMaxWeight();
659 else if( j == 0 )
662 BilinearWeightType nWeightX = lclMaxWeight() - rCtx.maMapFX[ nLeft ];
663 nSumRowB += ( nWeightX *aCol0.GetBlue()) ;
664 nSumRowG += ( nWeightX *aCol0.GetGreen()) ;
665 nSumRowR += ( nWeightX *aCol0.GetRed()) ;
666 nTotalWeightX += nWeightX;
668 else if ( nRowRange == j )
671 BilinearWeightType nWeightX = rCtx.maMapFX[ nRight ] ;
672 nSumRowB += ( nWeightX *aCol0.GetBlue() );
673 nSumRowG += ( nWeightX *aCol0.GetGreen() );
674 nSumRowR += ( nWeightX *aCol0.GetRed() );
675 nTotalWeightX += nWeightX;
677 else
680 nSumRowB += aCol0.GetBlue() << MAP_PRECISION;
681 nSumRowG += aCol0.GetGreen() << MAP_PRECISION;
682 nSumRowR += aCol0.GetRed() << MAP_PRECISION;
683 nTotalWeightX += lclMaxWeight();
687 sal_Int32 nWeightY = lclMaxWeight();
688 if( nY == nEndY )
689 nWeightY = lclMaxWeight();
690 else if( i == 0 )
691 nWeightY = lclMaxWeight() - rCtx.maMapFY[ nTop ];
692 else if( nLineRange == 1 )
693 nWeightY = rCtx.maMapFY[ nTop ];
694 else if ( nLineRange == i )
695 nWeightY = rCtx.maMapFY[ nBottom ];
697 if (nTotalWeightX)
699 nSumRowB /= nTotalWeightX;
700 nSumRowG /= nTotalWeightX;
701 nSumRowR /= nTotalWeightX;
704 nSumB += nWeightY * nSumRowB;
705 nSumG += nWeightY * nSumRowG;
706 nSumR += nWeightY * nSumRowR;
707 nTotalWeightY += nWeightY;
710 if (nTotalWeightY)
712 nSumR /= nTotalWeightY;
713 nSumG /= nTotalWeightY;
714 nSumB /= nTotalWeightY;
717 BitmapColor aColRes(static_cast<sal_uInt8>(nSumR), static_cast<sal_uInt8>(nSumG), static_cast<sal_uInt8>(nSumB));
718 rCtx.mpDest->SetPixelOnData( pScanDest, nXDst++, aColRes );
723 void scaleDownNonPaletteGeneral(const ScaleContext &rCtx, sal_Int32 nStartY, sal_Int32 nEndY)
725 const sal_Int32 nStartX = 0, nEndX = rCtx.mnDestW - 1;
727 for( sal_Int32 nY = nStartY; nY <= nEndY; nY++ )
729 sal_Int32 nTop = rCtx.mbVMirr ? ( nY + 1 ) : nY;
730 sal_Int32 nBottom = rCtx.mbVMirr ? nY : ( nY + 1 ) ;
732 sal_Int32 nLineStart, nLineRange;
733 if( nY ==nEndY )
735 nLineStart = rCtx.maMapIY[ nY ];
736 nLineRange = 0;
738 else
740 nLineStart = rCtx.maMapIY[ nTop ] ;
741 nLineRange = ( rCtx.maMapIY[ nBottom ] == rCtx.maMapIY[ nTop ] ) ? 1 :( rCtx.maMapIY[ nBottom ] - rCtx.maMapIY[ nTop ] );
744 Scanline pScanDest = rCtx.mpDest->GetScanline( nY );
745 for( sal_Int32 nX = nStartX , nXDst = 0; nX <= nEndX; nX++ )
747 sal_Int32 nLeft = rCtx.mbHMirr ? ( nX + 1 ) : nX;
748 sal_Int32 nRight = rCtx.mbHMirr ? nX : ( nX + 1 ) ;
750 sal_Int32 nRowStart, nRowRange;
751 if( nX == nEndX )
753 nRowStart = rCtx.maMapIX[ nX ];
754 nRowRange = 0;
756 else
758 nRowStart = rCtx.maMapIX[ nLeft ];
759 nRowRange = ( rCtx.maMapIX[ nRight ] == rCtx.maMapIX[ nLeft ] )? 1 : ( rCtx.maMapIX[ nRight ] - rCtx.maMapIX[ nLeft ] );
762 int nSumR = 0;
763 int nSumG = 0;
764 int nSumB = 0;
765 BilinearWeightType nTotalWeightY = 0;
767 for(sal_Int32 i = 0; i<= nLineRange; i++)
769 int nSumRowR = 0;
770 int nSumRowG = 0;
771 int nSumRowB = 0;
772 BilinearWeightType nTotalWeightX = 0;
774 Scanline pScanlineSrc = rCtx.mpSrc->GetScanline( nLineStart + i );
775 for(sal_Int32 j = 0; j <= nRowRange; j++)
777 BitmapColor aCol0 = rCtx.mpSrc->GetPixelFromData( pScanlineSrc, nRowStart + j );
779 if(nX == nEndX )
782 nSumRowB += aCol0.GetBlue() << MAP_PRECISION;
783 nSumRowG += aCol0.GetGreen() << MAP_PRECISION;
784 nSumRowR += aCol0.GetRed() << MAP_PRECISION;
785 nTotalWeightX += lclMaxWeight();
787 else if( j == 0 )
790 BilinearWeightType nWeightX = lclMaxWeight() - rCtx.maMapFX[ nLeft ];
791 nSumRowB += ( nWeightX *aCol0.GetBlue()) ;
792 nSumRowG += ( nWeightX *aCol0.GetGreen()) ;
793 nSumRowR += ( nWeightX *aCol0.GetRed()) ;
794 nTotalWeightX += nWeightX;
796 else if ( nRowRange == j )
799 BilinearWeightType nWeightX = rCtx.maMapFX[ nRight ] ;
800 nSumRowB += ( nWeightX *aCol0.GetBlue() );
801 nSumRowG += ( nWeightX *aCol0.GetGreen() );
802 nSumRowR += ( nWeightX *aCol0.GetRed() );
803 nTotalWeightX += nWeightX;
805 else
807 nSumRowB += aCol0.GetBlue() << MAP_PRECISION;
808 nSumRowG += aCol0.GetGreen() << MAP_PRECISION;
809 nSumRowR += aCol0.GetRed() << MAP_PRECISION;
810 nTotalWeightX += lclMaxWeight();
814 BilinearWeightType nWeightY = lclMaxWeight();
815 if( nY == nEndY )
816 nWeightY = lclMaxWeight();
817 else if( i == 0 )
818 nWeightY = lclMaxWeight() - rCtx.maMapFY[ nTop ];
819 else if( nLineRange == 1 )
820 nWeightY = rCtx.maMapFY[ nTop ];
821 else if ( nLineRange == i )
822 nWeightY = rCtx.maMapFY[ nBottom ];
824 if (nTotalWeightX)
826 nSumRowB /= nTotalWeightX;
827 nSumRowG /= nTotalWeightX;
828 nSumRowR /= nTotalWeightX;
831 nSumB += nWeightY * nSumRowB;
832 nSumG += nWeightY * nSumRowG;
833 nSumR += nWeightY * nSumRowR;
834 nTotalWeightY += nWeightY;
837 if (nTotalWeightY)
839 nSumR /= nTotalWeightY;
840 nSumG /= nTotalWeightY;
841 nSumB /= nTotalWeightY;
844 BitmapColor aColRes(static_cast<sal_uInt8>(nSumR), static_cast<sal_uInt8>(nSumG), static_cast<sal_uInt8>(nSumB));
845 rCtx.mpDest->SetPixelOnData( pScanDest, nXDst++, aColRes );
850 } // end anonymous namespace
852 BitmapScaleSuperFilter::BitmapScaleSuperFilter(const double& rScaleX, const double& rScaleY) :
853 mrScaleX(rScaleX),
854 mrScaleY(rScaleY)
857 BitmapScaleSuperFilter::~BitmapScaleSuperFilter()
860 BitmapEx BitmapScaleSuperFilter::execute(BitmapEx const& rBitmap) const
862 Bitmap aBitmap(rBitmap.GetBitmap());
863 bool bRet = false;
865 const Size aSizePix(rBitmap.GetSizePixel());
867 bool bHMirr = mrScaleX < 0;
868 bool bVMirr = mrScaleY < 0;
870 double fScaleX = std::fabs(mrScaleX);
871 double fScaleY = std::fabs(mrScaleY);
873 const sal_Int32 nDstW = basegfx::fround(aSizePix.Width() * fScaleX);
874 const sal_Int32 nDstH = basegfx::fround(aSizePix.Height() * fScaleY);
876 constexpr double fScaleThresh = 0.6;
878 if (nDstW <= 1 || nDstH <= 1)
879 return BitmapEx();
881 // check cache for a previously scaled version of this
882 ScaleCacheKey aKey(aBitmap.ImplGetSalBitmap().get(),
883 Size(nDstW, nDstH));
885 ImplSVData* pSVData = ImplGetSVData();
886 auto& rCache = pSVData->maGDIData.maScaleCache;
887 auto aFind = rCache.find(aKey);
888 if (aFind != rCache.end())
890 if (aFind->second.GetSizePixel().Width() == nDstW && aFind->second.GetSizePixel().Height() == nDstH)
891 return aFind->second;
892 else
893 SAL_WARN("vcl.gdi", "Error: size mismatch in scale cache");
897 BitmapScopedReadAccess pReadAccess(aBitmap);
899 // If source format is less than 24BPP, use 24BPP
900 auto eSourcePixelFormat = aBitmap.getPixelFormat();
901 auto ePixelFormat = eSourcePixelFormat;
902 if (sal_uInt16(eSourcePixelFormat) < 24)
903 ePixelFormat = vcl::PixelFormat::N24_BPP;
905 Bitmap aOutBmp(Size(nDstW, nDstH), ePixelFormat);
906 Size aOutSize = aOutBmp.GetSizePixel();
907 auto eTargetPixelFormat = aOutBmp.getPixelFormat();
909 if (!aOutSize.Width() || !aOutSize.Height())
911 SAL_WARN("vcl.gdi", "bmp creation failed");
912 return BitmapEx();
915 BitmapScopedWriteAccess pWriteAccess(aOutBmp);
917 const sal_Int32 nEndY = nDstH - 1;
919 if (pReadAccess && pWriteAccess)
921 ScaleRangeFn pScaleRangeFn;
922 const ScaleContext aContext( pReadAccess.get(),
923 pWriteAccess.get(),
924 pReadAccess->Width(),
925 pWriteAccess->Width(),
926 pReadAccess->Height(),
927 pWriteAccess->Height(),
928 bVMirr, bHMirr );
930 bool bScaleUp = fScaleX >= fScaleThresh && fScaleY >= fScaleThresh;
931 // If we have a source bitmap with a palette the scaling converts
932 // from up to 8 bit image -> 24 bit non-palette, which is then
933 // adapted back to the same type as original.
934 if (pReadAccess->HasPalette())
936 switch( pReadAccess->GetScanlineFormat() )
938 case ScanlineFormat::N8BitPal:
939 pScaleRangeFn = bScaleUp ? scaleUpPalette8bit
940 : scaleDownPalette8bit;
941 break;
942 default:
943 pScaleRangeFn = bScaleUp ? scaleUpPaletteGeneral
944 : scaleDownPaletteGeneral;
945 break;
948 // Here we know that we are dealing with a non-palette source bitmap.
949 // The target is either 24 or 32 bit, depending on the image and
950 // the capabilities of the backend. If for some reason the destination
951 // is not the same bit-depth as the source, then we can't use
952 // a fast path, so we always need to process with a general scaler.
953 else if (eSourcePixelFormat != eTargetPixelFormat)
955 pScaleRangeFn = bScaleUp ? scaleUpNonPaletteGeneral : scaleDownNonPaletteGeneral;
957 // If we get here then we can only use a fast path, but let's
958 // still keep the fallback to the general scaler alive.
959 else
961 switch( pReadAccess->GetScanlineFormat() )
963 case ScanlineFormat::N24BitTcBgr:
964 case ScanlineFormat::N24BitTcRgb:
965 pScaleRangeFn = bScaleUp ? scaleUp<24> : scaleDown<24>;
966 break;
967 case ScanlineFormat::N32BitTcRgba:
968 case ScanlineFormat::N32BitTcBgra:
969 case ScanlineFormat::N32BitTcArgb:
970 case ScanlineFormat::N32BitTcAbgr:
971 pScaleRangeFn = bScaleUp ? scaleUp<32> : scaleDown<32>;
972 break;
973 default:
974 pScaleRangeFn = bScaleUp ? scaleUpNonPaletteGeneral
975 : scaleDownNonPaletteGeneral;
976 break;
980 // We want to thread - only if there is a lot of work to do:
981 // We work hard when there is a large destination image, or
982 // A large source image.
983 bool bHorizontalWork = pReadAccess->Height() >= 512 && pReadAccess->Width() >= 512;
984 bool bUseThreads = true;
985 const sal_Int32 nStartY = 0;
987 static bool bDisableThreadedScaling = getenv ("VCL_NO_THREAD_SCALE");
988 if (bDisableThreadedScaling || !bHorizontalWork)
990 SAL_INFO("vcl.gdi", "Scale in main thread");
991 bUseThreads = false;
994 if (bUseThreads)
998 // partition and queue work
999 comphelper::ThreadPool &rShared = comphelper::ThreadPool::getSharedOptimalPool();
1000 std::shared_ptr<comphelper::ThreadTaskTag> pTag = comphelper::ThreadPool::createThreadTaskTag();
1002 vcl::bitmap::generateStripRanges<constScaleThreadStrip>(nStartY, nEndY,
1003 [&] (sal_Int32 const nStart, sal_Int32 const nEnd, bool const bLast)
1005 if (!bLast)
1007 auto pTask(std::make_unique<ScaleTask>(pTag, pScaleRangeFn, aContext, nStart, nEnd));
1008 rShared.pushTask(std::move(pTask));
1010 else
1011 pScaleRangeFn(aContext, nStart, nEnd);
1013 rShared.waitUntilDone(pTag);
1014 SAL_INFO("vcl.gdi", "All threaded scaling tasks complete");
1016 catch (...)
1018 SAL_WARN("vcl.gdi", "threaded bitmap scaling failed");
1019 bUseThreads = false;
1023 if (!bUseThreads)
1024 pScaleRangeFn( aContext, nStartY, nEndY );
1026 pWriteAccess.reset();
1027 bRet = true;
1028 aBitmap.AdaptBitCount(aOutBmp);
1029 aBitmap = std::move(aOutBmp);
1033 if (bRet)
1035 tools::Rectangle aRect(Point(0, 0), Point(nDstW, nDstH));
1036 aBitmap.Crop(aRect);
1037 BitmapEx aRet(aBitmap);
1038 rCache.insert(std::make_pair(aKey, aRet));
1039 return aRet;
1042 return BitmapEx();
1046 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */