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 <comphelper/threadpool.hxx>
21 
22 #include <tools/helpers.hxx>
23 #include <vcl/bitmapaccess.hxx>
24 
25 #include <bitmapwriteaccess.hxx>
26 #include <BitmapScaleSuperFilter.hxx>
27 
28 #include <algorithm>
29 #include <memory>
30 #include <svdata.hxx>
31 #include <sal/log.hxx>
32 
33 namespace {
34 
35 #define MAP_PRECISION 7
36 
37 typedef sal_Int32 BilinearWeightType;
38 
lclMaxWeight()39 constexpr BilinearWeightType lclMaxWeight()
40 {
41     return BilinearWeightType(1) << MAP_PRECISION;
42 }
43 
MAP(sal_uInt8 cVal0,sal_uInt8 cVal1,BilinearWeightType nFrac)44 constexpr sal_uInt8 MAP(sal_uInt8 cVal0, sal_uInt8 cVal1, BilinearWeightType nFrac)
45 {
46     return sal_uInt8(((BilinearWeightType(cVal0) << MAP_PRECISION) + nFrac * (BilinearWeightType(cVal1) - BilinearWeightType(cVal0))) >> MAP_PRECISION);
47 }
48 
49 struct ScaleContext
50 {
51     BitmapReadAccess* const mpSrc;
52     BitmapWriteAccess* mpDest;
53     long mnDestW;
54     bool mbHMirr;
55     bool mbVMirr;
56     std::vector<long> maMapIX;
57     std::vector<long> maMapIY;
58     std::vector<BilinearWeightType> maMapFX;
59     std::vector<BilinearWeightType> maMapFY;
60 
ScaleContext__anon747b35800111::ScaleContext61     ScaleContext( BitmapReadAccess *pSrc,
62                   BitmapWriteAccess *pDest,
63                   long nSrcW, long nDestW,
64                   long nSrcH, long nDestH,
65                   bool bHMirr, bool bVMirr)
66         : mpSrc(pSrc)
67         , mpDest(pDest)
68         , mnDestW(nDestW)
69         , mbHMirr(bHMirr)
70         , mbVMirr(bVMirr)
71         , maMapIX(nDestW)
72         , maMapIY(nDestH)
73         , maMapFX(nDestW)
74         , maMapFY(nDestH)
75     {
76         generateMap(nSrcW, nDestW, bHMirr, maMapIX, maMapFX);
77         generateMap(nSrcH, nDestH, bVMirr, maMapIY, maMapFY);
78     }
79 
generateMap__anon747b35800111::ScaleContext80     static void generateMap(long nSourceLength, long nDestinationLength, bool bMirrored,
81         std::vector<long> & rMapIX, std::vector<BilinearWeightType> & rMapFX)
82     {
83         const double fRevScale = (nDestinationLength > 1) ? double(nSourceLength - 1) / (nDestinationLength - 1) : 0.0;
84 
85         long nTemp = nSourceLength - 2;
86         long nTempX = nSourceLength - 1;
87 
88         for (long i = 0; i < nDestinationLength; i++)
89         {
90             double fTemp = i * fRevScale;
91             if (bMirrored)
92                 fTemp = nTempX - fTemp;
93             rMapIX[i] = MinMax(long(fTemp), 0, nTemp);
94             rMapFX[i] = BilinearWeightType((fTemp - rMapIX[i]) * (BilinearWeightType(1) << MAP_PRECISION));
95         }
96     }
97 };
98 
99 constexpr long constScaleThreadStrip = 32;
100 
101 typedef void (*ScaleRangeFn)(ScaleContext &rContext, long nStartY, long nEndY);
102 
103 class ScaleTask : public comphelper::ThreadTask
104 {
105     ScaleRangeFn const mpScaleRangeFunction;
106     ScaleContext& mrContext;
107     const long mnStartY;
108     const long mnEndY;
109 
110 public:
ScaleTask(const std::shared_ptr<comphelper::ThreadTaskTag> & pTag,ScaleRangeFn pScaleRangeFunction,ScaleContext & rContext,long nStartY,long nEndY)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)
118         , mnStartY(nStartY)
119         , mnEndY(nEndY)
120     {}
121 
doWork()122     virtual void doWork() override
123     {
124         mpScaleRangeFunction(mrContext, mnStartY, mnEndY);
125     }
126 };
127 
scaleUp32bit(ScaleContext & rCtx,long nStartY,long nEndY)128 void scaleUp32bit(ScaleContext &rCtx, long nStartY, long nEndY)
129 {
130     const int nColorComponents = 4;
131 
132     const long nStartX = 0;
133     const long nEndX = rCtx.mnDestW - 1;
134 
135     for (long nY = nStartY; nY <= nEndY; nY++)
136     {
137         long nTempY = rCtx.maMapIY[nY];
138         BilinearWeightType nTempFY = rCtx.maMapFY[nY];
139 
140         Scanline pLine0 = rCtx.mpSrc->GetScanline(nTempY+0);
141         Scanline pLine1 = rCtx.mpSrc->GetScanline(nTempY+1);
142         Scanline pScanDest = rCtx.mpDest->GetScanline(nY);
143 
144         sal_uInt8 nComponent1[nColorComponents];
145         sal_uInt8 nComponent2[nColorComponents];
146 
147         Scanline pColorPtr0;
148         Scanline pColorPtr1;
149 
150         for (long nX = nStartX; nX <= nEndX; nX++)
151         {
152             long nTempX = rCtx.maMapIX[nX];
153             BilinearWeightType nTempFX = rCtx.maMapFX[nX];
154 
155             pColorPtr0 = pLine0 + nTempX * nColorComponents;
156             pColorPtr1 = pColorPtr0 + nColorComponents;
157 
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);
165 
166             pColorPtr0 = pLine1 + nTempX * nColorComponents;
167             pColorPtr1 = pColorPtr0 + nColorComponents;
168 
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);
176 
177             *pScanDest = MAP(nComponent1[0], nComponent2[0], nTempFY);
178             pScanDest++;
179             *pScanDest = MAP(nComponent1[1], nComponent2[1], nTempFY);
180             pScanDest++;
181             *pScanDest = MAP(nComponent1[2], nComponent2[2], nTempFY);
182             pScanDest++;
183             *pScanDest = MAP(nComponent1[3], nComponent2[3], nTempFY);
184             pScanDest++;
185         }
186     }
187 }
188 
scaleUpPalette8bit(ScaleContext & rCtx,long nStartY,long nEndY)189 void scaleUpPalette8bit(ScaleContext &rCtx, long nStartY, long nEndY)
190 {
191     const long nStartX = 0, nEndX = rCtx.mnDestW - 1;
192 
193     for( long nY = nStartY; nY <= nEndY; nY++ )
194     {
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 );
200 
201         for(long nX = nStartX, nXDst = 0; nX <= nEndX; nX++ )
202         {
203             long nTempX = rCtx.maMapIX[ nX ];
204             BilinearWeightType nTempFX = rCtx.maMapFX[ nX ];
205 
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 ] );
210 
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 );
214 
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 );
218 
219             BitmapColor aColRes( MAP( cR0, cR1, nTempFY ),
220                     MAP( cG0, cG1, nTempFY ),
221                     MAP( cB0, cB1, nTempFY ) );
222             rCtx.mpDest->SetPixelOnData( pScanDest, nXDst++, aColRes );
223         }
224     }
225 }
226 
scaleUpPaletteGeneral(ScaleContext & rCtx,long nStartY,long nEndY)227 void scaleUpPaletteGeneral(ScaleContext &rCtx, long nStartY, long nEndY)
228 {
229     const long nStartX = 0, nEndX = rCtx.mnDestW - 1;
230 
231     for( long nY = nStartY; nY <= nEndY; nY++ )
232     {
233         long nTempY = rCtx.maMapIY[ nY ];
234         BilinearWeightType nTempFY = rCtx.maMapFY[ nY ];
235         Scanline pScanline = rCtx.mpDest->GetScanline( nY );
236 
237         for( long nX = nStartX, nXDst = 0; nX <= nEndX; nX++ )
238         {
239             long nTempX = rCtx.maMapIX[ nX ];
240             BilinearWeightType nTempFX = rCtx.maMapFX[ nX ];
241 
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 );
247 
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 );
253 
254             BitmapColor aColRes( MAP( cR0, cR1, nTempFY ),
255                     MAP( cG0, cG1, nTempFY ),
256                     MAP( cB0, cB1, nTempFY ) );
257             rCtx.mpDest->SetPixelOnData( pScanline, nXDst++, aColRes );
258         }
259     }
260 }
261 
scaleUp24bit(ScaleContext & rCtx,long nStartY,long nEndY)262 void scaleUp24bit(ScaleContext &rCtx, long nStartY, long nEndY)
263 {
264     const int nColorComponents = 3;
265 
266     const long nStartX = 0;
267     const long nEndX = rCtx.mnDestW - 1;
268 
269     for (long nY = nStartY; nY <= nEndY; nY++)
270     {
271         long nTempY = rCtx.maMapIY[nY];
272         BilinearWeightType nTempFY = rCtx.maMapFY[nY];
273 
274         Scanline pLine0 = rCtx.mpSrc->GetScanline(nTempY+0);
275         Scanline pLine1 = rCtx.mpSrc->GetScanline(nTempY+1);
276         Scanline pScanDest = rCtx.mpDest->GetScanline(nY);
277 
278         sal_uInt8 nComponent1[nColorComponents];
279         sal_uInt8 nComponent2[nColorComponents];
280 
281         Scanline pColorPtr0;
282         Scanline pColorPtr1;
283 
284         for (long nX = nStartX; nX <= nEndX; nX++)
285         {
286             long nTempX = rCtx.maMapIX[nX];
287             BilinearWeightType nTempFX = rCtx.maMapFX[nX];
288 
289             pColorPtr0 = pLine0 + nTempX * nColorComponents;
290             pColorPtr1 = pColorPtr0 + nColorComponents;
291 
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);
297 
298             pColorPtr0 = pLine1 + nTempX * nColorComponents;
299             pColorPtr1 = pColorPtr0 + nColorComponents;
300 
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);
306 
307             *pScanDest = MAP(nComponent1[0], nComponent2[0], nTempFY);
308             pScanDest++;
309             *pScanDest = MAP(nComponent1[1], nComponent2[1], nTempFY);
310             pScanDest++;
311             *pScanDest = MAP(nComponent1[2], nComponent2[2], nTempFY);
312             pScanDest++;
313         }
314     }
315 }
316 
scaleUpNonPaletteGeneral(ScaleContext & rCtx,long nStartY,long nEndY)317 void scaleUpNonPaletteGeneral(ScaleContext &rCtx, long nStartY, long nEndY)
318 {
319     const long nStartX = 0, nEndX = rCtx.mnDestW - 1;
320 
321     for( long nY = nStartY; nY <= nEndY; nY++ )
322     {
323         long nTempY = rCtx.maMapIY[ nY ];
324         BilinearWeightType nTempFY = rCtx.maMapFY[ nY ];
325         Scanline pScanDest = rCtx.mpDest->GetScanline( nY );
326 
327         for( long nX = nStartX, nXDst = 0; nX <= nEndX; nX++ )
328         {
329             long nTempX = rCtx.maMapIX[ nX ];
330             BilinearWeightType nTempFX = rCtx.maMapFX[ nX ];
331 
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 );
337 
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 );
343 
344             BitmapColor aColRes( MAP( cR0, cR1, nTempFY ),
345                     MAP( cG0, cG1, nTempFY ),
346                     MAP( cB0, cB1, nTempFY ) );
347             rCtx.mpDest->SetPixelOnData( pScanDest, nXDst++, aColRes );
348         }
349     }
350 }
351 
scaleDown32bit(ScaleContext & rCtx,long nStartY,long nEndY)352 void scaleDown32bit(ScaleContext &rCtx, long nStartY, long nEndY)
353 {
354     const int constColorComponents = 4;
355 
356     const long nStartX = 0;
357     const long nEndX = rCtx.mnDestW - 1;
358 
359     for (long nY = nStartY; nY <= nEndY; nY++)
360     {
361         long nTop = rCtx.mbVMirr ? (nY + 1) : nY;
362         long nBottom = rCtx.mbVMirr ? nY : (nY + 1);
363 
364         long nLineStart;
365         long nLineRange;
366         if (nY == nEndY)
367         {
368             nLineStart = rCtx.maMapIY[nY];
369             nLineRange = 0;
370         }
371         else
372         {
373             nLineStart = rCtx.maMapIY[nTop];
374             nLineRange = (rCtx.maMapIY[nBottom] == rCtx.maMapIY[nTop]) ?
375                             1 : (rCtx.maMapIY[nBottom] - rCtx.maMapIY[nTop]);
376         }
377 
378         Scanline pScanDest = rCtx.mpDest->GetScanline(nY);
379         for (long nX = nStartX; nX <= nEndX; nX++)
380         {
381             long nLeft = rCtx.mbHMirr ? (nX + 1) : nX;
382             long nRight = rCtx.mbHMirr ? nX : (nX + 1);
383 
384             long nRowStart;
385             long nRowRange;
386             if (nX == nEndX)
387             {
388                 nRowStart = rCtx.maMapIX[nX];
389                 nRowRange = 0;
390             }
391             else
392             {
393                 nRowStart = rCtx.maMapIX[nLeft];
394                 nRowRange = (rCtx.maMapIX[nRight] == rCtx.maMapIX[nLeft]) ?
395                                 1 : (rCtx.maMapIX[nRight] - rCtx.maMapIX[nLeft]);
396             }
397 
398             int nSum1 = 0;
399             int nSum2 = 0;
400             int nSum3 = 0;
401             int nSum4 = 0;
402             BilinearWeightType nTotalWeightY = 0;
403 
404             for (long i = 0; i<= nLineRange; i++)
405             {
406                 Scanline pTmpY = rCtx.mpSrc->GetScanline(nLineStart + i);
407                 Scanline pTmpX = pTmpY + constColorComponents * nRowStart;
408 
409                 int nSumRow1 = 0;
410                 int nSumRow2 = 0;
411                 int nSumRow3 = 0;
412                 int nSumRow4 = 0;
413                 BilinearWeightType nTotalWeightX = 0;
414 
415                 for (long j = 0; j <= nRowRange; j++)
416                 {
417                     if (nX == nEndX)
418                     {
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();
424                     }
425                     else if(j == 0)
426                     {
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;
433                     }
434                     else if ( nRowRange == j )
435                     {
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;
442                     }
443                     else
444                     {
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();
450                     }
451                 }
452 
453                 BilinearWeightType nWeightY = lclMaxWeight();
454                 if (nY == nEndY)
455                     nWeightY = lclMaxWeight();
456                 else if (i == 0)
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];
462 
463                 if (nTotalWeightX)
464                 {
465                     nSumRow1 /= nTotalWeightX;
466                     nSumRow2 /= nTotalWeightX;
467                     nSumRow3 /= nTotalWeightX;
468                     nSumRow4 /= nTotalWeightX;
469                 }
470                 nSum1 += nWeightY * nSumRow1;
471                 nSum2 += nWeightY * nSumRow2;
472                 nSum3 += nWeightY * nSumRow3;
473                 nSum4 += nWeightY * nSumRow4;
474                 nTotalWeightY += nWeightY;
475             }
476 
477             if (nTotalWeightY)
478             {
479                 nSum1 /= nTotalWeightY;
480                 nSum2 /= nTotalWeightY;
481                 nSum3 /= nTotalWeightY;
482                 nSum4 /= nTotalWeightY;
483             }
484 
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++;
490         }
491     }
492 }
493 
scaleDownPalette8bit(ScaleContext & rCtx,long nStartY,long nEndY)494 void scaleDownPalette8bit(ScaleContext &rCtx, long nStartY, long nEndY)
495 {
496     const long nStartX = 0, nEndX = rCtx.mnDestW - 1;
497 
498     for( long nY = nStartY; nY <= nEndY; nY++ )
499     {
500         long nTop = rCtx.mbVMirr ? ( nY + 1 ) : nY;
501         long nBottom = rCtx.mbVMirr ? nY : ( nY + 1 ) ;
502 
503         long nLineStart, nLineRange;
504         if( nY == nEndY )
505         {
506             nLineStart = rCtx.maMapIY[ nY ];
507             nLineRange = 0;
508         }
509         else
510         {
511             nLineStart = rCtx.maMapIY[ nTop ] ;
512             nLineRange = ( rCtx.maMapIY[ nBottom ] == rCtx.maMapIY[ nTop ] ) ? 1 :( rCtx.maMapIY[ nBottom ] - rCtx.maMapIY[ nTop ] );
513         }
514 
515         Scanline pScanDest = rCtx.mpDest->GetScanline( nY );
516         for( long nX = nStartX , nXDst = 0; nX <= nEndX; nX++ )
517         {
518             long nLeft = rCtx.mbHMirr ? ( nX + 1 ) : nX;
519             long nRight = rCtx.mbHMirr ? nX : ( nX + 1 ) ;
520 
521             long nRowStart;
522             long nRowRange;
523             if( nX == nEndX )
524             {
525                 nRowStart = rCtx.maMapIX[ nX ];
526                 nRowRange = 0;
527             }
528             else
529             {
530                 nRowStart = rCtx.maMapIX[ nLeft ];
531                 nRowRange = ( rCtx.maMapIX[ nRight ] == rCtx.maMapIX[ nLeft ] )? 1 : ( rCtx.maMapIX[ nRight ] - rCtx.maMapIX[ nLeft ] );
532             }
533 
534             int nSumR = 0;
535             int nSumG = 0;
536             int nSumB = 0;
537             BilinearWeightType nTotalWeightY = 0;
538 
539             for(long i = 0; i<= nLineRange; i++)
540             {
541                 Scanline pTmpY = rCtx.mpSrc->GetScanline( nLineStart + i );
542                 int nSumRowR = 0;
543                 int nSumRowG = 0;
544                 int nSumRowB = 0;
545                 BilinearWeightType nTotalWeightX = 0;
546 
547                 for(long j = 0; j <= nRowRange; j++)
548                 {
549                     const BitmapColor& rCol = rCtx.mpSrc->GetPaletteColor( pTmpY[ nRowStart + j ] );
550 
551                     if(nX == nEndX )
552                     {
553                         nSumRowB += rCol.GetBlue() << MAP_PRECISION;
554                         nSumRowG += rCol.GetGreen() << MAP_PRECISION;
555                         nSumRowR += rCol.GetRed() << MAP_PRECISION;
556                         nTotalWeightX += lclMaxWeight();
557                     }
558                     else if( j == 0 )
559                     {
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;
565                     }
566                     else if ( nRowRange == j )
567                     {
568                         BilinearWeightType nWeightX = rCtx.maMapFX[ nRight ] ;
569                         nSumRowB += ( nWeightX *rCol.GetBlue() );
570                         nSumRowG += ( nWeightX *rCol.GetGreen() );
571                         nSumRowR += ( nWeightX *rCol.GetRed() );
572                         nTotalWeightX += nWeightX;
573                     }
574                     else
575                     {
576                         nSumRowB += rCol.GetBlue() << MAP_PRECISION;
577                         nSumRowG += rCol.GetGreen() << MAP_PRECISION;
578                         nSumRowR += rCol.GetRed() << MAP_PRECISION;
579                         nTotalWeightX += lclMaxWeight();
580                     }
581                 }
582 
583                 BilinearWeightType nWeightY = lclMaxWeight();
584                 if( nY == nEndY )
585                     nWeightY = lclMaxWeight();
586                 else if( i == 0 )
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 ];
592 
593                 if (nTotalWeightX)
594                 {
595                     nSumRowB /= nTotalWeightX;
596                     nSumRowG /= nTotalWeightX;
597                     nSumRowR /= nTotalWeightX;
598                 }
599 
600                 nSumB += nWeightY * nSumRowB;
601                 nSumG += nWeightY * nSumRowG;
602                 nSumR += nWeightY * nSumRowR;
603                 nTotalWeightY += nWeightY;
604             }
605 
606             if (nTotalWeightY)
607             {
608                 nSumR /= nTotalWeightY;
609                 nSumG /= nTotalWeightY;
610                 nSumB /= nTotalWeightY;
611             }
612 
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 );
615         }
616     }
617 }
618 
scaleDownPaletteGeneral(ScaleContext & rCtx,long nStartY,long nEndY)619 void scaleDownPaletteGeneral(ScaleContext &rCtx, long nStartY, long nEndY)
620 {
621     const long nStartX = 0, nEndX = rCtx.mnDestW - 1;
622 
623     for( long nY = nStartY; nY <= nEndY; nY++ )
624     {
625         long nTop = rCtx.mbVMirr ? ( nY + 1 ) : nY;
626         long nBottom = rCtx.mbVMirr ? nY : ( nY + 1 ) ;
627 
628         long nLineStart, nLineRange;
629         if( nY ==nEndY )
630         {
631             nLineStart = rCtx.maMapIY[ nY ];
632             nLineRange = 0;
633         }
634         else
635         {
636             nLineStart = rCtx.maMapIY[ nTop ] ;
637             nLineRange = ( rCtx.maMapIY[ nBottom ] == rCtx.maMapIY[ nTop ] ) ? 1 :( rCtx.maMapIY[ nBottom ] - rCtx.maMapIY[ nTop ] );
638         }
639 
640         Scanline pScanDest = rCtx.mpDest->GetScanline( nY );
641         for( long nX = nStartX , nXDst = 0; nX <= nEndX; nX++ )
642         {
643             long nLeft = rCtx.mbHMirr ? ( nX + 1 ) : nX;
644             long nRight = rCtx.mbHMirr ? nX : ( nX + 1 ) ;
645 
646             long nRowStart, nRowRange;
647             if( nX == nEndX )
648             {
649                 nRowStart = rCtx.maMapIX[ nX ];
650                 nRowRange = 0;
651             }
652             else
653             {
654                 nRowStart = rCtx.maMapIX[ nLeft ];
655                 nRowRange = ( rCtx.maMapIX[ nRight ] == rCtx.maMapIX[ nLeft ] )? 1 : ( rCtx.maMapIX[ nRight ] - rCtx.maMapIX[ nLeft ] );
656             }
657 
658             int nSumR = 0;
659             int nSumG = 0;
660             int nSumB = 0;
661             BilinearWeightType nTotalWeightY = 0;
662 
663             for(long i = 0; i<= nLineRange; i++)
664             {
665                 int nSumRowR = 0;
666                 int nSumRowG = 0;
667                 int nSumRowB = 0;
668                 BilinearWeightType nTotalWeightX = 0;
669 
670                 Scanline pScanlineSrc = rCtx.mpSrc->GetScanline( nLineStart + i );
671                 for(long j = 0; j <= nRowRange; j++)
672                 {
673                     BitmapColor aCol0 = rCtx.mpSrc->GetPaletteColor ( rCtx.mpSrc->GetIndexFromData( pScanlineSrc, nRowStart + j ) );
674 
675                     if(nX == nEndX )
676                     {
677 
678                         nSumRowB += aCol0.GetBlue() << MAP_PRECISION;
679                         nSumRowG += aCol0.GetGreen() << MAP_PRECISION;
680                         nSumRowR += aCol0.GetRed() << MAP_PRECISION;
681                         nTotalWeightX += lclMaxWeight();
682                     }
683                     else if( j == 0 )
684                     {
685 
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;
691                     }
692                     else if ( nRowRange == j )
693                     {
694 
695                         BilinearWeightType nWeightX = rCtx.maMapFX[ nRight ] ;
696                         nSumRowB += ( nWeightX *aCol0.GetBlue() );
697                         nSumRowG += ( nWeightX *aCol0.GetGreen() );
698                         nSumRowR += ( nWeightX *aCol0.GetRed() );
699                         nTotalWeightX += nWeightX;
700                     }
701                     else
702                     {
703 
704                         nSumRowB += aCol0.GetBlue() << MAP_PRECISION;
705                         nSumRowG += aCol0.GetGreen() << MAP_PRECISION;
706                         nSumRowR += aCol0.GetRed() << MAP_PRECISION;
707                         nTotalWeightX += lclMaxWeight();
708                     }
709                 }
710 
711                 long nWeightY = lclMaxWeight();
712                 if( nY == nEndY )
713                     nWeightY = lclMaxWeight();
714                 else if( i == 0 )
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 ];
720 
721                 if (nTotalWeightX)
722                 {
723                     nSumRowB /= nTotalWeightX;
724                     nSumRowG /= nTotalWeightX;
725                     nSumRowR /= nTotalWeightX;
726                 }
727 
728                 nSumB += nWeightY * nSumRowB;
729                 nSumG += nWeightY * nSumRowG;
730                 nSumR += nWeightY * nSumRowR;
731                 nTotalWeightY += nWeightY;
732             }
733 
734             if (nTotalWeightY)
735             {
736                 nSumR /= nTotalWeightY;
737                 nSumG /= nTotalWeightY;
738                 nSumB /= nTotalWeightY;
739             }
740 
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 );
743         }
744     }
745 }
746 
scaleDown24bit(ScaleContext & rCtx,long nStartY,long nEndY)747 void scaleDown24bit(ScaleContext &rCtx, long nStartY, long nEndY)
748 {
749     const int constColorComponents = 3;
750 
751     const long nStartX = 0;
752     const long nEndX = rCtx.mnDestW - 1;
753 
754     for (long nY = nStartY; nY <= nEndY; nY++)
755     {
756         long nTop = rCtx.mbVMirr ? (nY + 1) : nY;
757         long nBottom = rCtx.mbVMirr ? nY : (nY + 1);
758 
759         long nLineStart;
760         long nLineRange;
761         if (nY == nEndY)
762         {
763             nLineStart = rCtx.maMapIY[nY];
764             nLineRange = 0;
765         }
766         else
767         {
768             nLineStart = rCtx.maMapIY[nTop];
769             nLineRange = (rCtx.maMapIY[nBottom] == rCtx.maMapIY[nTop]) ?
770                             1 : (rCtx.maMapIY[nBottom] - rCtx.maMapIY[nTop]);
771         }
772 
773         Scanline pScanDest = rCtx.mpDest->GetScanline(nY);
774         for (long nX = nStartX; nX <= nEndX; nX++)
775         {
776             long nLeft = rCtx.mbHMirr ? (nX + 1) : nX;
777             long nRight = rCtx.mbHMirr ? nX : (nX + 1);
778 
779             long nRowStart;
780             long nRowRange;
781             if (nX == nEndX)
782             {
783                 nRowStart = rCtx.maMapIX[nX];
784                 nRowRange = 0;
785             }
786             else
787             {
788                 nRowStart = rCtx.maMapIX[nLeft];
789                 nRowRange = (rCtx.maMapIX[nRight] == rCtx.maMapIX[nLeft]) ?
790                                 1 : (rCtx.maMapIX[nRight] - rCtx.maMapIX[nLeft]);
791             }
792 
793             int nSum1 = 0;
794             int nSum2 = 0;
795             int nSum3 = 0;
796             BilinearWeightType nTotalWeightY = 0;
797 
798             for (long i = 0; i<= nLineRange; i++)
799             {
800                 Scanline pTmpY = rCtx.mpSrc->GetScanline(nLineStart + i);
801                 Scanline pTmpX = pTmpY + constColorComponents * nRowStart;
802 
803                 int nSumRow1 = 0;
804                 int nSumRow2 = 0;
805                 int nSumRow3 = 0;
806                 BilinearWeightType nTotalWeightX = 0;
807 
808                 for (long j = 0; j <= nRowRange; j++)
809                 {
810                     if (nX == nEndX)
811                     {
812                         nSumRow1 += (*pTmpX) << MAP_PRECISION; pTmpX++;
813                         nSumRow2 += (*pTmpX) << MAP_PRECISION; pTmpX++;
814                         nSumRow3 += (*pTmpX) << MAP_PRECISION; pTmpX++;
815                         nTotalWeightX += lclMaxWeight();
816                     }
817                     else if(j == 0)
818                     {
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;
824                     }
825                     else if ( nRowRange == j )
826                     {
827                         BilinearWeightType nWeightX = rCtx.maMapFX[ nRight ] ;
828                         nSumRow1 += (nWeightX * (*pTmpX)); pTmpX++;
829                         nSumRow2 += (nWeightX * (*pTmpX)); pTmpX++;
830                         nSumRow3 += (nWeightX * (*pTmpX)); pTmpX++;
831                         nTotalWeightX += nWeightX;
832                     }
833                     else
834                     {
835                         nSumRow1 += (*pTmpX) << MAP_PRECISION; pTmpX++;
836                         nSumRow2 += (*pTmpX) << MAP_PRECISION; pTmpX++;
837                         nSumRow3 += (*pTmpX) << MAP_PRECISION; pTmpX++;
838                         nTotalWeightX += lclMaxWeight();
839                     }
840                 }
841 
842                 BilinearWeightType nWeightY = lclMaxWeight();
843                 if (nY == nEndY)
844                     nWeightY = lclMaxWeight();
845                 else if (i == 0)
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];
851 
852                 if (nTotalWeightX)
853                 {
854                     nSumRow1 /= nTotalWeightX;
855                     nSumRow2 /= nTotalWeightX;
856                     nSumRow3 /= nTotalWeightX;
857                 }
858                 nSum1 += nWeightY * nSumRow1;
859                 nSum2 += nWeightY * nSumRow2;
860                 nSum3 += nWeightY * nSumRow3;
861                 nTotalWeightY += nWeightY;
862             }
863 
864             if (nTotalWeightY)
865             {
866                 nSum1 /= nTotalWeightY;
867                 nSum2 /= nTotalWeightY;
868                 nSum3 /= nTotalWeightY;
869             }
870 
871             // Write the calculated color components to the destination
872             *pScanDest = nSum1; pScanDest++;
873             *pScanDest = nSum2; pScanDest++;
874             *pScanDest = nSum3; pScanDest++;
875         }
876     }
877 }
878 
scaleDownNonPaletteGeneral(ScaleContext & rCtx,long nStartY,long nEndY)879 void scaleDownNonPaletteGeneral(ScaleContext &rCtx, long nStartY, long nEndY)
880 {
881     const long nStartX = 0, nEndX = rCtx.mnDestW - 1;
882 
883     for( long nY = nStartY; nY <= nEndY; nY++ )
884     {
885         long nTop = rCtx.mbVMirr ? ( nY + 1 ) : nY;
886         long nBottom = rCtx.mbVMirr ? nY : ( nY + 1 ) ;
887 
888         long nLineStart, nLineRange;
889         if( nY ==nEndY )
890         {
891             nLineStart = rCtx.maMapIY[ nY ];
892             nLineRange = 0;
893         }
894         else
895         {
896             nLineStart = rCtx.maMapIY[ nTop ] ;
897             nLineRange = ( rCtx.maMapIY[ nBottom ] == rCtx.maMapIY[ nTop ] ) ? 1 :( rCtx.maMapIY[ nBottom ] - rCtx.maMapIY[ nTop ] );
898         }
899 
900         Scanline pScanDest = rCtx.mpDest->GetScanline( nY );
901         for( long nX = nStartX , nXDst = 0; nX <= nEndX; nX++ )
902         {
903             long nLeft = rCtx.mbHMirr ? ( nX + 1 ) : nX;
904             long nRight = rCtx.mbHMirr ? nX : ( nX + 1 ) ;
905 
906             long nRowStart, nRowRange;
907             if( nX == nEndX )
908             {
909                 nRowStart = rCtx.maMapIX[ nX ];
910                 nRowRange = 0;
911             }
912             else
913             {
914                 nRowStart = rCtx.maMapIX[ nLeft ];
915                 nRowRange = ( rCtx.maMapIX[ nRight ] == rCtx.maMapIX[ nLeft ] )? 1 : ( rCtx.maMapIX[ nRight ] - rCtx.maMapIX[ nLeft ] );
916             }
917 
918             int nSumR = 0;
919             int nSumG = 0;
920             int nSumB = 0;
921             BilinearWeightType nTotalWeightY = 0;
922 
923             for(long i = 0; i<= nLineRange; i++)
924             {
925                 int nSumRowR = 0;
926                 int nSumRowG = 0;
927                 int nSumRowB = 0;
928                 BilinearWeightType nTotalWeightX = 0;
929 
930                 Scanline pScanlineSrc = rCtx.mpSrc->GetScanline( nLineStart + i );
931                 for(long j = 0; j <= nRowRange; j++)
932                 {
933                     BitmapColor aCol0 = rCtx.mpSrc->GetPixelFromData( pScanlineSrc, nRowStart + j );
934 
935                     if(nX == nEndX )
936                     {
937 
938                         nSumRowB += aCol0.GetBlue() << MAP_PRECISION;
939                         nSumRowG += aCol0.GetGreen() << MAP_PRECISION;
940                         nSumRowR += aCol0.GetRed() << MAP_PRECISION;
941                         nTotalWeightX += lclMaxWeight();
942                     }
943                     else if( j == 0 )
944                     {
945 
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;
951                     }
952                     else if ( nRowRange == j )
953                     {
954 
955                         BilinearWeightType nWeightX = rCtx.maMapFX[ nRight ] ;
956                         nSumRowB += ( nWeightX *aCol0.GetBlue() );
957                         nSumRowG += ( nWeightX *aCol0.GetGreen() );
958                         nSumRowR += ( nWeightX *aCol0.GetRed() );
959                         nTotalWeightX += nWeightX;
960                     }
961                     else
962                     {
963                         nSumRowB += aCol0.GetBlue() << MAP_PRECISION;
964                         nSumRowG += aCol0.GetGreen() << MAP_PRECISION;
965                         nSumRowR += aCol0.GetRed() << MAP_PRECISION;
966                         nTotalWeightX += lclMaxWeight();
967                     }
968                 }
969 
970                 BilinearWeightType nWeightY = lclMaxWeight();
971                 if( nY == nEndY )
972                     nWeightY = lclMaxWeight();
973                 else if( i == 0 )
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 ];
979 
980                 if (nTotalWeightX)
981                 {
982                     nSumRowB /= nTotalWeightX;
983                     nSumRowG /= nTotalWeightX;
984                     nSumRowR /= nTotalWeightX;
985                 }
986 
987                 nSumB += nWeightY * nSumRowB;
988                 nSumG += nWeightY * nSumRowG;
989                 nSumR += nWeightY * nSumRowR;
990                 nTotalWeightY += nWeightY;
991             }
992 
993             if (nTotalWeightY)
994             {
995                 nSumR /= nTotalWeightY;
996                 nSumG /= nTotalWeightY;
997                 nSumB /= nTotalWeightY;
998             }
999 
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 );
1002         }
1003     }
1004 }
1005 
1006 } // end anonymous namespace
1007 
BitmapScaleSuperFilter(const double & rScaleX,const double & rScaleY)1008 BitmapScaleSuperFilter::BitmapScaleSuperFilter(const double& rScaleX, const double& rScaleY) :
1009     mrScaleX(rScaleX),
1010     mrScaleY(rScaleY)
1011 {}
1012 
~BitmapScaleSuperFilter()1013 BitmapScaleSuperFilter::~BitmapScaleSuperFilter()
1014 {}
1015 
execute(BitmapEx const & rBitmap) const1016 BitmapEx BitmapScaleSuperFilter::execute(BitmapEx const& rBitmap) const
1017 {
1018     Bitmap aBitmap(rBitmap.GetBitmap());
1019     SalBitmap* pKey = aBitmap.ImplGetSalBitmap().get();
1020 
1021     bool bRet = false;
1022 
1023     const Size aSizePix(rBitmap.GetSizePixel());
1024 
1025     bool bHMirr = mrScaleX < 0;
1026     bool bVMirr = mrScaleY < 0;
1027 
1028     double fScaleX = std::fabs(mrScaleX);
1029     double fScaleY = std::fabs(mrScaleY);
1030 
1031     const long nDstW = FRound(aSizePix.Width()  * fScaleX);
1032     const long nDstH = FRound(aSizePix.Height() * fScaleY);
1033 
1034     const double fScaleThresh = 0.6;
1035 
1036     if (nDstW <= 1 || nDstH <= 1)
1037         return BitmapEx();
1038 
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())
1044     {
1045         if (aFind->second.GetSizePixel().Width() == nDstW && aFind->second.GetSizePixel().Height() == nDstH)
1046             return aFind->second;
1047     }
1048 
1049     {
1050         Bitmap::ScopedReadAccess pReadAccess(aBitmap);
1051 
1052         sal_uInt16 nSourceBitcount = aBitmap.GetBitCount();
1053 
1054         Bitmap aOutBmp(Size(nDstW, nDstH), std::max(nSourceBitcount, sal_uInt16(24)));
1055         Size aOutSize = aOutBmp.GetSizePixel();
1056         sal_uInt16 nTargetBitcount = aOutBmp.GetBitCount();
1057 
1058         if (!aOutSize.Width() || !aOutSize.Height())
1059         {
1060             SAL_WARN("vcl.gdi", "bmp creation failed");
1061             return BitmapEx();
1062         }
1063 
1064         BitmapScopedWriteAccess pWriteAccess(aOutBmp);
1065 
1066         const long nStartY = 0;
1067         const long nEndY   = nDstH - 1;
1068 
1069         if (pReadAccess && pWriteAccess)
1070         {
1071             ScaleRangeFn pScaleRangeFn;
1072             ScaleContext aContext( pReadAccess.get(),
1073                                    pWriteAccess.get(),
1074                                    pReadAccess->Width(),
1075                                    pWriteAccess->Width(),
1076                                    pReadAccess->Height(),
1077                                    pWriteAccess->Height(),
1078                                    bVMirr, bHMirr );
1079 
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())
1085             {
1086                 switch( pReadAccess->GetScanlineFormat() )
1087                 {
1088                 case ScanlineFormat::N8BitPal:
1089                     pScaleRangeFn = bScaleUp ? scaleUpPalette8bit
1090                                              : scaleDownPalette8bit;
1091                     break;
1092                 default:
1093                     pScaleRangeFn = bScaleUp ? scaleUpPaletteGeneral
1094                                              : scaleDownPaletteGeneral;
1095                     break;
1096                 }
1097             }
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)
1104             {
1105                 pScaleRangeFn = bScaleUp ? scaleUpNonPaletteGeneral : scaleDownNonPaletteGeneral;
1106             }
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.
1109             else
1110             {
1111                 switch( pReadAccess->GetScanlineFormat() )
1112                 {
1113                 case ScanlineFormat::N24BitTcBgr:
1114                 case ScanlineFormat::N24BitTcRgb:
1115                     pScaleRangeFn = bScaleUp ? scaleUp24bit : scaleDown24bit;
1116                     break;
1117                 case ScanlineFormat::N32BitTcRgba:
1118                 case ScanlineFormat::N32BitTcBgra:
1119                 case ScanlineFormat::N32BitTcArgb:
1120                 case ScanlineFormat::N32BitTcAbgr:
1121                     pScaleRangeFn = bScaleUp ? scaleUp32bit : scaleDown32bit;
1122                     break;
1123                 default:
1124                     pScaleRangeFn = bScaleUp ? scaleUpNonPaletteGeneral
1125                                              : scaleDownNonPaletteGeneral;
1126                     break;
1127                 }
1128             }
1129 
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;
1135 
1136             static bool bDisableThreadedScaling = getenv ("VCL_NO_THREAD_SCALE");
1137             if (bDisableThreadedScaling || !bHorizontalWork)
1138             {
1139                 SAL_INFO("vcl.gdi", "Scale in main thread");
1140                 bUseThreads = false;
1141             }
1142 
1143             if (bUseThreads)
1144             {
1145                 try
1146                 {
1147                     // partition and queue work
1148                     comphelper::ThreadPool &rShared = comphelper::ThreadPool::getSharedOptimalPool();
1149                     std::shared_ptr<comphelper::ThreadTaskTag> pTag = comphelper::ThreadPool::createThreadTaskTag();
1150 
1151                     long nStripYStart = nStartY;
1152                     long nStripYEnd = nStripYStart + constScaleThreadStrip - 1;
1153 
1154                     while (nStripYEnd < nEndY)
1155                     {
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;
1160                     }
1161                     if (nStripYStart <= nEndY)
1162                     {
1163                         std::unique_ptr<ScaleTask> pTask(new ScaleTask(pTag, pScaleRangeFn, aContext, nStripYStart, nEndY));
1164                         rShared.pushTask(std::move(pTask));
1165                     }
1166                     rShared.waitUntilDone(pTag);
1167                     SAL_INFO("vcl.gdi", "All threaded scaling tasks complete");
1168                 }
1169                 catch (...)
1170                 {
1171                     SAL_WARN("vcl.gdi", "threaded bitmap scaling failed");
1172                     bUseThreads = false;
1173                 }
1174             }
1175 
1176             if (!bUseThreads)
1177                 pScaleRangeFn( aContext, nStartY, nEndY );
1178 
1179             bRet = true;
1180             aBitmap.AdaptBitCount(aOutBmp);
1181             aBitmap = aOutBmp;
1182         }
1183     }
1184 
1185     if (bRet)
1186     {
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));
1191         return aRet;
1192     }
1193 
1194     return BitmapEx();
1195 
1196 }
1197 
1198 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
1199