LibreOffice Module vcl (master)  1
BitmapScaleSuperFilter.cxx
Go to the documentation of this file.
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 
21 
22 #include <tools/helpers.hxx>
23 #include <vcl/bitmapaccess.hxx>
24 
25 #include <bitmapwriteaccess.hxx>
27 
28 #include <algorithm>
29 #include <memory>
30 #include <svdata.hxx>
31 #include <sal/log.hxx>
32 
33 namespace {
34 
35 constexpr int MAP_PRECISION = 7;
36 
37 typedef sal_Int32 BilinearWeightType;
38 
39 constexpr BilinearWeightType lclMaxWeight()
40 {
41  return BilinearWeightType(1) << MAP_PRECISION;
42 }
43 
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* 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 
61  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 
80  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)(const ScaleContext & rContext, long nStartY, long nEndY);
102 
103 template <size_t nSize> struct ScaleFunc
104 {
105  // for scale down
106 
107  static inline void generateSumRows(Scanline& pTmpX, std::array<int, nSize>& sumRows)
108  {
109  for (int& n : sumRows)
110  n += (*pTmpX++) << MAP_PRECISION;
111  }
112 
113  static inline void generateSumRows(BilinearWeightType const nWeightX, Scanline& pTmpX,
114  std::array<int, nSize>& sumRows)
115  {
116  for (int& n : sumRows)
117  n += (nWeightX * (*pTmpX++));
118  }
119 
120  static inline void generateSumRows(BilinearWeightType const nTotalWeightX,
121  std::array<int, nSize>& sumRows)
122  {
123  for (int& n : sumRows)
124  n /= nTotalWeightX;
125  }
126 
127  static inline void generateSumNumbers(BilinearWeightType const nWeightY,
128  std::array<int, nSize>& sumRows,
129  std::array<int, nSize>& sumNumbers)
130  {
131  std::transform(sumRows.begin(), sumRows.end(), sumNumbers.begin(), sumNumbers.begin(),
132  [nWeightY](int n1, int n2) { return nWeightY * n1 + n2; });
133  }
134 
135  static inline void generateSumNumbers(BilinearWeightType const nTotalWeightY,
136  std::array<int, nSize>& sumNumbers)
137  {
138  for (int& n : sumNumbers)
139  n /= nTotalWeightY;
140  }
141 
142  static inline void calculateDestination(Scanline& pScanDest, std::array<int, nSize>& sumNumbers)
143  {
144  pScanDest = std::copy(sumNumbers.begin(), sumNumbers.end(), pScanDest);
145  }
146 
147  // for scale up
148 
149  static inline void generateComponent(Scanline pColorPtr0, Scanline pColorPtr1,
150  BilinearWeightType const nTempFX,
151  std::array<sal_uInt8, nSize>& nComponents)
152  {
153  for (sal_uInt8& rComponent : nComponents)
154  rComponent = MAP(*pColorPtr0++, *pColorPtr1++, nTempFX);
155  }
156 
157  static inline void calculateDestination(Scanline& pScanDest, BilinearWeightType const nTempFY,
158  const std::array<sal_uInt8, nSize>& nComponents1,
159  const std::array<sal_uInt8, nSize>& nComponents2)
160  {
161  pScanDest = std::transform(
162  nComponents1.begin(), nComponents1.end(), nComponents2.begin(), pScanDest,
163  [nTempFY](sal_uInt8 c1, sal_uInt8 c2) { return MAP(c1, c2, nTempFY); });
164  }
165 };
166 
167 template <int nColorBits>
168 void scaleDown (const ScaleContext &rCtx, long nStartY, long nEndY)
169 {
170  constexpr int nColorComponents = nColorBits / 8;
171  static_assert(nColorComponents * 8 == nColorBits, "nColorBits must be divisible by 8");
172  using ScaleFunction = ScaleFunc<nColorComponents>;
173  const long nStartX = 0;
174  const long nEndX = rCtx.mnDestW - 1;
175 
176  for (long nY = nStartY; nY <= nEndY; nY++)
177  {
178  long nTop = rCtx.mbVMirr ? (nY + 1) : nY;
179  long nBottom = rCtx.mbVMirr ? nY : (nY + 1);
180 
181  long nLineStart;
182  long nLineRange;
183  if (nY == nEndY)
184  {
185  nLineStart = rCtx.maMapIY[nY];
186  nLineRange = 0;
187  }
188  else
189  {
190  nLineStart = rCtx.maMapIY[nTop];
191  nLineRange = (rCtx.maMapIY[nBottom] == rCtx.maMapIY[nTop]) ?
192  1 : (rCtx.maMapIY[nBottom] - rCtx.maMapIY[nTop]);
193  }
194 
195  Scanline pScanDest = rCtx.mpDest->GetScanline(nY);
196  for (long nX = nStartX; nX <= nEndX; nX++)
197  {
198  long nLeft = rCtx.mbHMirr ? (nX + 1) : nX;
199  long nRight = rCtx.mbHMirr ? nX : (nX + 1);
200 
201  long nRowStart;
202  long nRowRange;
203  if (nX == nEndX)
204  {
205  nRowStart = rCtx.maMapIX[nX];
206  nRowRange = 0;
207  }
208  else
209  {
210  nRowStart = rCtx.maMapIX[nLeft];
211  nRowRange = (rCtx.maMapIX[nRight] == rCtx.maMapIX[nLeft]) ?
212  1 : (rCtx.maMapIX[nRight] - rCtx.maMapIX[nLeft]);
213  }
214 
215  std::array<int, nColorComponents> sumNumbers{}; // zero-initialize
216  BilinearWeightType nTotalWeightY = 0;
217 
218  for (long i = 0; i<= nLineRange; i++)
219  {
220  Scanline pTmpY = rCtx.mpSrc->GetScanline(nLineStart + i);
221  Scanline pTmpX = pTmpY + nColorComponents * nRowStart;
222 
223  std::array<int, nColorComponents> sumRows{}; // zero-initialize
224  BilinearWeightType nTotalWeightX = 0;
225 
226  for (long j = 0; j <= nRowRange; j++)
227  {
228  if (nX == nEndX)
229  {
230  ScaleFunction::generateSumRows(pTmpX, sumRows);
231  nTotalWeightX += lclMaxWeight();
232  }
233  else if(j == 0)
234  {
235  BilinearWeightType nWeightX = lclMaxWeight() - rCtx.maMapFX[nLeft];
236  ScaleFunction::generateSumRows(nWeightX, pTmpX, sumRows);
237  nTotalWeightX += nWeightX;
238  }
239  else if ( nRowRange == j )
240  {
241  BilinearWeightType nWeightX = rCtx.maMapFX[ nRight ] ;
242  ScaleFunction::generateSumRows(nWeightX, pTmpX, sumRows);
243  nTotalWeightX += nWeightX;
244  }
245  else
246  {
247  ScaleFunction::generateSumRows(pTmpX, sumRows);
248  nTotalWeightX += lclMaxWeight();
249  }
250  }
251 
252  BilinearWeightType nWeightY = lclMaxWeight();
253  if (nY == nEndY)
254  nWeightY = lclMaxWeight();
255  else if (i == 0)
256  nWeightY = lclMaxWeight() - rCtx.maMapFY[nTop];
257  else if (nLineRange == 1)
258  nWeightY = rCtx.maMapFY[nTop];
259  else if (nLineRange == i)
260  nWeightY = rCtx.maMapFY[nBottom];
261 
262  if (nTotalWeightX)
263  {
264  ScaleFunction::generateSumRows(nTotalWeightX, sumRows);
265  }
266  ScaleFunction::generateSumNumbers(nWeightY, sumRows, sumNumbers);
267  nTotalWeightY += nWeightY;
268 
269  }
270 
271  if (nTotalWeightY)
272  {
273  ScaleFunction::generateSumNumbers(nTotalWeightY, sumNumbers);
274  }
275 
276  // Write the calculated color components to the destination
277  ScaleFunction::calculateDestination(pScanDest, sumNumbers);
278  }
279  }
280 }
281 
282 template <int nColorBits>
283 void scaleUp(const ScaleContext &rCtx, long nStartY, long nEndY)
284 {
285  constexpr int nColorComponents = nColorBits / 8;
286  static_assert(nColorComponents * 8 == nColorBits, "nColorBits must be divisible by 8");
287  using ScaleFunction = ScaleFunc<nColorComponents>;
288  const long nStartX = 0;
289  const long nEndX = rCtx.mnDestW - 1;
290 
291  for (long nY = nStartY; nY <= nEndY; nY++)
292  {
293  long nTempY = rCtx.maMapIY[nY];
294  BilinearWeightType nTempFY = rCtx.maMapFY[nY];
295 
296  Scanline pLine0 = rCtx.mpSrc->GetScanline(nTempY+0);
297  Scanline pLine1 = rCtx.mpSrc->GetScanline(nTempY+1);
298  Scanline pScanDest = rCtx.mpDest->GetScanline(nY);
299 
300  std::array<sal_uInt8, nColorComponents> nComponents1; // no need to initialize since it's
301  std::array<sal_uInt8, nColorComponents> nComponents2; // initialized in generateComponent
302 
303  Scanline pColorPtr0;
304  Scanline pColorPtr1;
305 
306  for (long nX = nStartX; nX <= nEndX; nX++)
307  {
308  long nTempX = rCtx.maMapIX[nX];
309  BilinearWeightType nTempFX = rCtx.maMapFX[nX];
310 
311  pColorPtr0 = pLine0 + nTempX * nColorComponents;
312  pColorPtr1 = pColorPtr0 + nColorComponents;
313 
314  ScaleFunction::generateComponent(pColorPtr0, pColorPtr1, nTempFX, nComponents1);
315 
316  pColorPtr0 = pLine1 + nTempX * nColorComponents;
317  pColorPtr1 = pColorPtr0 + nColorComponents;
318 
319  ScaleFunction::generateComponent(pColorPtr0, pColorPtr1, nTempFX, nComponents2);
320  ScaleFunction::calculateDestination(pScanDest, nTempFY, nComponents1, nComponents2);
321  }
322  }
323 }
324 
325 class ScaleTask : public comphelper::ThreadTask
326 {
327  ScaleRangeFn mpScaleRangeFunction;
328  const ScaleContext& mrContext;
329  long mnStartY;
330  long mnEndY;
331 
332 public:
333  explicit ScaleTask(const std::shared_ptr<comphelper::ThreadTaskTag>& pTag,
334  ScaleRangeFn pScaleRangeFunction,
335  const ScaleContext& rContext,
336  long nStartY, long nEndY)
337  : comphelper::ThreadTask(pTag)
338  , mpScaleRangeFunction(pScaleRangeFunction)
339  , mrContext(rContext)
340  , mnStartY(nStartY)
341  , mnEndY(nEndY)
342  {}
343 
344  virtual void doWork() override
345  {
346  mpScaleRangeFunction(mrContext, mnStartY, mnEndY);
347  }
348 };
349 
350 void scaleUpPalette8bit(const ScaleContext &rCtx, long nStartY, long nEndY)
351 {
352  const long nStartX = 0, nEndX = rCtx.mnDestW - 1;
353 
354  for( long nY = nStartY; nY <= nEndY; nY++ )
355  {
356  long nTempY = rCtx.maMapIY[ nY ];
357  BilinearWeightType nTempFY = rCtx.maMapFY[ nY ];
358  Scanline pLine0 = rCtx.mpSrc->GetScanline( nTempY );
359  Scanline pLine1 = rCtx.mpSrc->GetScanline( ++nTempY );
360  Scanline pScanDest = rCtx.mpDest->GetScanline( nY );
361 
362  for(long nX = nStartX, nXDst = 0; nX <= nEndX; nX++ )
363  {
364  long nTempX = rCtx.maMapIX[ nX ];
365  BilinearWeightType nTempFX = rCtx.maMapFX[ nX ];
366 
367  const BitmapColor& rCol0 = rCtx.mpSrc->GetPaletteColor( pLine0[ nTempX ] );
368  const BitmapColor& rCol2 = rCtx.mpSrc->GetPaletteColor( pLine1[ nTempX ] );
369  const BitmapColor& rCol1 = rCtx.mpSrc->GetPaletteColor( pLine0[ ++nTempX ] );
370  const BitmapColor& rCol3 = rCtx.mpSrc->GetPaletteColor( pLine1[ nTempX ] );
371 
372  sal_uInt8 cR0 = MAP( rCol0.GetRed(), rCol1.GetRed(), nTempFX );
373  sal_uInt8 cG0 = MAP( rCol0.GetGreen(), rCol1.GetGreen(), nTempFX );
374  sal_uInt8 cB0 = MAP( rCol0.GetBlue(), rCol1.GetBlue(), nTempFX );
375 
376  sal_uInt8 cR1 = MAP( rCol2.GetRed(), rCol3.GetRed(), nTempFX );
377  sal_uInt8 cG1 = MAP( rCol2.GetGreen(), rCol3.GetGreen(), nTempFX );
378  sal_uInt8 cB1 = MAP( rCol2.GetBlue(), rCol3.GetBlue(), nTempFX );
379 
380  BitmapColor aColRes( MAP( cR0, cR1, nTempFY ),
381  MAP( cG0, cG1, nTempFY ),
382  MAP( cB0, cB1, nTempFY ) );
383  rCtx.mpDest->SetPixelOnData( pScanDest, nXDst++, aColRes );
384  }
385  }
386 }
387 
388 void scaleUpPaletteGeneral(const ScaleContext &rCtx, long nStartY, long nEndY)
389 {
390  const long nStartX = 0, nEndX = rCtx.mnDestW - 1;
391 
392  for( long nY = nStartY; nY <= nEndY; nY++ )
393  {
394  long nTempY = rCtx.maMapIY[ nY ];
395  BilinearWeightType nTempFY = rCtx.maMapFY[ nY ];
396  Scanline pScanline = rCtx.mpDest->GetScanline( nY );
397 
398  for( long nX = nStartX, nXDst = 0; nX <= nEndX; nX++ )
399  {
400  long nTempX = rCtx.maMapIX[ nX ];
401  BilinearWeightType nTempFX = rCtx.maMapFX[ nX ];
402 
403  BitmapColor aCol0 = rCtx.mpSrc->GetPaletteColor( rCtx.mpSrc->GetPixelIndex( nTempY, nTempX ) );
404  BitmapColor aCol1 = rCtx.mpSrc->GetPaletteColor( rCtx.mpSrc->GetPixelIndex( nTempY, ++nTempX ) );
405  sal_uInt8 cR0 = MAP( aCol0.GetRed(), aCol1.GetRed(), nTempFX );
406  sal_uInt8 cG0 = MAP( aCol0.GetGreen(), aCol1.GetGreen(), nTempFX );
407  sal_uInt8 cB0 = MAP( aCol0.GetBlue(), aCol1.GetBlue(), nTempFX );
408 
409  aCol1 = rCtx.mpSrc->GetPaletteColor( rCtx.mpSrc->GetPixelIndex( ++nTempY, nTempX ) );
410  aCol0 = rCtx.mpSrc->GetPaletteColor( rCtx.mpSrc->GetPixelIndex( nTempY--, --nTempX ) );
411  sal_uInt8 cR1 = MAP( aCol0.GetRed(), aCol1.GetRed(), nTempFX );
412  sal_uInt8 cG1 = MAP( aCol0.GetGreen(), aCol1.GetGreen(), nTempFX );
413  sal_uInt8 cB1 = MAP( aCol0.GetBlue(), aCol1.GetBlue(), nTempFX );
414 
415  BitmapColor aColRes( MAP( cR0, cR1, nTempFY ),
416  MAP( cG0, cG1, nTempFY ),
417  MAP( cB0, cB1, nTempFY ) );
418  rCtx.mpDest->SetPixelOnData( pScanline, nXDst++, aColRes );
419  }
420  }
421 }
422 
423 void scaleUpNonPaletteGeneral(const ScaleContext &rCtx, long nStartY, long nEndY)
424 {
425  const long nStartX = 0, nEndX = rCtx.mnDestW - 1;
426 
427  for( long nY = nStartY; nY <= nEndY; nY++ )
428  {
429  long nTempY = rCtx.maMapIY[ nY ];
430  BilinearWeightType nTempFY = rCtx.maMapFY[ nY ];
431  Scanline pScanDest = rCtx.mpDest->GetScanline( nY );
432 
433  for( long nX = nStartX, nXDst = 0; nX <= nEndX; nX++ )
434  {
435  long nTempX = rCtx.maMapIX[ nX ];
436  BilinearWeightType nTempFX = rCtx.maMapFX[ nX ];
437 
438  BitmapColor aCol0 = rCtx.mpSrc->GetPixel( nTempY, nTempX );
439  BitmapColor aCol1 = rCtx.mpSrc->GetPixel( nTempY, ++nTempX );
440  sal_uInt8 cR0 = MAP( aCol0.GetRed(), aCol1.GetRed(), nTempFX );
441  sal_uInt8 cG0 = MAP( aCol0.GetGreen(), aCol1.GetGreen(), nTempFX );
442  sal_uInt8 cB0 = MAP( aCol0.GetBlue(), aCol1.GetBlue(), nTempFX );
443 
444  aCol1 = rCtx.mpSrc->GetPixel( ++nTempY, nTempX );
445  aCol0 = rCtx.mpSrc->GetPixel( nTempY--, --nTempX );
446  sal_uInt8 cR1 = MAP( aCol0.GetRed(), aCol1.GetRed(), nTempFX );
447  sal_uInt8 cG1 = MAP( aCol0.GetGreen(), aCol1.GetGreen(), nTempFX );
448  sal_uInt8 cB1 = MAP( aCol0.GetBlue(), aCol1.GetBlue(), nTempFX );
449 
450  BitmapColor aColRes( MAP( cR0, cR1, nTempFY ),
451  MAP( cG0, cG1, nTempFY ),
452  MAP( cB0, cB1, nTempFY ) );
453  rCtx.mpDest->SetPixelOnData( pScanDest, nXDst++, aColRes );
454  }
455  }
456 }
457 
458 void scaleDownPalette8bit(const ScaleContext &rCtx, long nStartY, long nEndY)
459 {
460  const long nStartX = 0, nEndX = rCtx.mnDestW - 1;
461 
462  for( long nY = nStartY; nY <= nEndY; nY++ )
463  {
464  long nTop = rCtx.mbVMirr ? ( nY + 1 ) : nY;
465  long nBottom = rCtx.mbVMirr ? nY : ( nY + 1 ) ;
466 
467  long nLineStart, nLineRange;
468  if( nY == nEndY )
469  {
470  nLineStart = rCtx.maMapIY[ nY ];
471  nLineRange = 0;
472  }
473  else
474  {
475  nLineStart = rCtx.maMapIY[ nTop ] ;
476  nLineRange = ( rCtx.maMapIY[ nBottom ] == rCtx.maMapIY[ nTop ] ) ? 1 :( rCtx.maMapIY[ nBottom ] - rCtx.maMapIY[ nTop ] );
477  }
478 
479  Scanline pScanDest = rCtx.mpDest->GetScanline( nY );
480  for( long nX = nStartX , nXDst = 0; nX <= nEndX; nX++ )
481  {
482  long nLeft = rCtx.mbHMirr ? ( nX + 1 ) : nX;
483  long nRight = rCtx.mbHMirr ? nX : ( nX + 1 ) ;
484 
485  long nRowStart;
486  long nRowRange;
487  if( nX == nEndX )
488  {
489  nRowStart = rCtx.maMapIX[ nX ];
490  nRowRange = 0;
491  }
492  else
493  {
494  nRowStart = rCtx.maMapIX[ nLeft ];
495  nRowRange = ( rCtx.maMapIX[ nRight ] == rCtx.maMapIX[ nLeft ] )? 1 : ( rCtx.maMapIX[ nRight ] - rCtx.maMapIX[ nLeft ] );
496  }
497 
498  int nSumR = 0;
499  int nSumG = 0;
500  int nSumB = 0;
501  BilinearWeightType nTotalWeightY = 0;
502 
503  for(long i = 0; i<= nLineRange; i++)
504  {
505  Scanline pTmpY = rCtx.mpSrc->GetScanline( nLineStart + i );
506  int nSumRowR = 0;
507  int nSumRowG = 0;
508  int nSumRowB = 0;
509  BilinearWeightType nTotalWeightX = 0;
510 
511  for(long j = 0; j <= nRowRange; j++)
512  {
513  const BitmapColor& rCol = rCtx.mpSrc->GetPaletteColor( pTmpY[ nRowStart + j ] );
514 
515  if(nX == nEndX )
516  {
517  nSumRowB += rCol.GetBlue() << MAP_PRECISION;
518  nSumRowG += rCol.GetGreen() << MAP_PRECISION;
519  nSumRowR += rCol.GetRed() << MAP_PRECISION;
520  nTotalWeightX += lclMaxWeight();
521  }
522  else if( j == 0 )
523  {
524  BilinearWeightType nWeightX = lclMaxWeight() - rCtx.maMapFX[ nLeft ];
525  nSumRowB += ( nWeightX *rCol.GetBlue()) ;
526  nSumRowG += ( nWeightX *rCol.GetGreen()) ;
527  nSumRowR += ( nWeightX *rCol.GetRed()) ;
528  nTotalWeightX += nWeightX;
529  }
530  else if ( nRowRange == j )
531  {
532  BilinearWeightType nWeightX = rCtx.maMapFX[ nRight ] ;
533  nSumRowB += ( nWeightX *rCol.GetBlue() );
534  nSumRowG += ( nWeightX *rCol.GetGreen() );
535  nSumRowR += ( nWeightX *rCol.GetRed() );
536  nTotalWeightX += nWeightX;
537  }
538  else
539  {
540  nSumRowB += rCol.GetBlue() << MAP_PRECISION;
541  nSumRowG += rCol.GetGreen() << MAP_PRECISION;
542  nSumRowR += rCol.GetRed() << MAP_PRECISION;
543  nTotalWeightX += lclMaxWeight();
544  }
545  }
546 
547  BilinearWeightType nWeightY = lclMaxWeight();
548  if( nY == nEndY )
549  nWeightY = lclMaxWeight();
550  else if( i == 0 )
551  nWeightY = lclMaxWeight() - rCtx.maMapFY[ nTop ];
552  else if( nLineRange == 1 )
553  nWeightY = rCtx.maMapFY[ nTop ];
554  else if ( nLineRange == i )
555  nWeightY = rCtx.maMapFY[ nBottom ];
556 
557  if (nTotalWeightX)
558  {
559  nSumRowB /= nTotalWeightX;
560  nSumRowG /= nTotalWeightX;
561  nSumRowR /= nTotalWeightX;
562  }
563 
564  nSumB += nWeightY * nSumRowB;
565  nSumG += nWeightY * nSumRowG;
566  nSumR += nWeightY * nSumRowR;
567  nTotalWeightY += nWeightY;
568  }
569 
570  if (nTotalWeightY)
571  {
572  nSumR /= nTotalWeightY;
573  nSumG /= nTotalWeightY;
574  nSumB /= nTotalWeightY;
575  }
576 
577  BitmapColor aColRes(static_cast<sal_uInt8>(nSumR), static_cast<sal_uInt8>(nSumG), static_cast<sal_uInt8>(nSumB));
578  rCtx.mpDest->SetPixelOnData( pScanDest, nXDst++, aColRes );
579  }
580  }
581 }
582 
583 void scaleDownPaletteGeneral(const ScaleContext &rCtx, long nStartY, long nEndY)
584 {
585  const long nStartX = 0, nEndX = rCtx.mnDestW - 1;
586 
587  for( long nY = nStartY; nY <= nEndY; nY++ )
588  {
589  long nTop = rCtx.mbVMirr ? ( nY + 1 ) : nY;
590  long nBottom = rCtx.mbVMirr ? nY : ( nY + 1 ) ;
591 
592  long nLineStart, nLineRange;
593  if( nY ==nEndY )
594  {
595  nLineStart = rCtx.maMapIY[ nY ];
596  nLineRange = 0;
597  }
598  else
599  {
600  nLineStart = rCtx.maMapIY[ nTop ] ;
601  nLineRange = ( rCtx.maMapIY[ nBottom ] == rCtx.maMapIY[ nTop ] ) ? 1 :( rCtx.maMapIY[ nBottom ] - rCtx.maMapIY[ nTop ] );
602  }
603 
604  Scanline pScanDest = rCtx.mpDest->GetScanline( nY );
605  for( long nX = nStartX , nXDst = 0; nX <= nEndX; nX++ )
606  {
607  long nLeft = rCtx.mbHMirr ? ( nX + 1 ) : nX;
608  long nRight = rCtx.mbHMirr ? nX : ( nX + 1 ) ;
609 
610  long nRowStart, nRowRange;
611  if( nX == nEndX )
612  {
613  nRowStart = rCtx.maMapIX[ nX ];
614  nRowRange = 0;
615  }
616  else
617  {
618  nRowStart = rCtx.maMapIX[ nLeft ];
619  nRowRange = ( rCtx.maMapIX[ nRight ] == rCtx.maMapIX[ nLeft ] )? 1 : ( rCtx.maMapIX[ nRight ] - rCtx.maMapIX[ nLeft ] );
620  }
621 
622  int nSumR = 0;
623  int nSumG = 0;
624  int nSumB = 0;
625  BilinearWeightType nTotalWeightY = 0;
626 
627  for(long i = 0; i<= nLineRange; i++)
628  {
629  int nSumRowR = 0;
630  int nSumRowG = 0;
631  int nSumRowB = 0;
632  BilinearWeightType nTotalWeightX = 0;
633 
634  Scanline pScanlineSrc = rCtx.mpSrc->GetScanline( nLineStart + i );
635  for(long j = 0; j <= nRowRange; j++)
636  {
637  BitmapColor aCol0 = rCtx.mpSrc->GetPaletteColor ( rCtx.mpSrc->GetIndexFromData( pScanlineSrc, nRowStart + j ) );
638 
639  if(nX == nEndX )
640  {
641 
642  nSumRowB += aCol0.GetBlue() << MAP_PRECISION;
643  nSumRowG += aCol0.GetGreen() << MAP_PRECISION;
644  nSumRowR += aCol0.GetRed() << MAP_PRECISION;
645  nTotalWeightX += lclMaxWeight();
646  }
647  else if( j == 0 )
648  {
649 
650  BilinearWeightType nWeightX = lclMaxWeight() - rCtx.maMapFX[ nLeft ];
651  nSumRowB += ( nWeightX *aCol0.GetBlue()) ;
652  nSumRowG += ( nWeightX *aCol0.GetGreen()) ;
653  nSumRowR += ( nWeightX *aCol0.GetRed()) ;
654  nTotalWeightX += nWeightX;
655  }
656  else if ( nRowRange == j )
657  {
658 
659  BilinearWeightType nWeightX = rCtx.maMapFX[ nRight ] ;
660  nSumRowB += ( nWeightX *aCol0.GetBlue() );
661  nSumRowG += ( nWeightX *aCol0.GetGreen() );
662  nSumRowR += ( nWeightX *aCol0.GetRed() );
663  nTotalWeightX += nWeightX;
664  }
665  else
666  {
667 
668  nSumRowB += aCol0.GetBlue() << MAP_PRECISION;
669  nSumRowG += aCol0.GetGreen() << MAP_PRECISION;
670  nSumRowR += aCol0.GetRed() << MAP_PRECISION;
671  nTotalWeightX += lclMaxWeight();
672  }
673  }
674 
675  long nWeightY = lclMaxWeight();
676  if( nY == nEndY )
677  nWeightY = lclMaxWeight();
678  else if( i == 0 )
679  nWeightY = lclMaxWeight() - rCtx.maMapFY[ nTop ];
680  else if( nLineRange == 1 )
681  nWeightY = rCtx.maMapFY[ nTop ];
682  else if ( nLineRange == i )
683  nWeightY = rCtx.maMapFY[ nBottom ];
684 
685  if (nTotalWeightX)
686  {
687  nSumRowB /= nTotalWeightX;
688  nSumRowG /= nTotalWeightX;
689  nSumRowR /= nTotalWeightX;
690  }
691 
692  nSumB += nWeightY * nSumRowB;
693  nSumG += nWeightY * nSumRowG;
694  nSumR += nWeightY * nSumRowR;
695  nTotalWeightY += nWeightY;
696  }
697 
698  if (nTotalWeightY)
699  {
700  nSumR /= nTotalWeightY;
701  nSumG /= nTotalWeightY;
702  nSumB /= nTotalWeightY;
703  }
704 
705  BitmapColor aColRes(static_cast<sal_uInt8>(nSumR), static_cast<sal_uInt8>(nSumG), static_cast<sal_uInt8>(nSumB));
706  rCtx.mpDest->SetPixelOnData( pScanDest, nXDst++, aColRes );
707  }
708  }
709 }
710 
711 void scaleDownNonPaletteGeneral(const ScaleContext &rCtx, long nStartY, long nEndY)
712 {
713  const long nStartX = 0, nEndX = rCtx.mnDestW - 1;
714 
715  for( long nY = nStartY; nY <= nEndY; nY++ )
716  {
717  long nTop = rCtx.mbVMirr ? ( nY + 1 ) : nY;
718  long nBottom = rCtx.mbVMirr ? nY : ( nY + 1 ) ;
719 
720  long nLineStart, nLineRange;
721  if( nY ==nEndY )
722  {
723  nLineStart = rCtx.maMapIY[ nY ];
724  nLineRange = 0;
725  }
726  else
727  {
728  nLineStart = rCtx.maMapIY[ nTop ] ;
729  nLineRange = ( rCtx.maMapIY[ nBottom ] == rCtx.maMapIY[ nTop ] ) ? 1 :( rCtx.maMapIY[ nBottom ] - rCtx.maMapIY[ nTop ] );
730  }
731 
732  Scanline pScanDest = rCtx.mpDest->GetScanline( nY );
733  for( long nX = nStartX , nXDst = 0; nX <= nEndX; nX++ )
734  {
735  long nLeft = rCtx.mbHMirr ? ( nX + 1 ) : nX;
736  long nRight = rCtx.mbHMirr ? nX : ( nX + 1 ) ;
737 
738  long nRowStart, nRowRange;
739  if( nX == nEndX )
740  {
741  nRowStart = rCtx.maMapIX[ nX ];
742  nRowRange = 0;
743  }
744  else
745  {
746  nRowStart = rCtx.maMapIX[ nLeft ];
747  nRowRange = ( rCtx.maMapIX[ nRight ] == rCtx.maMapIX[ nLeft ] )? 1 : ( rCtx.maMapIX[ nRight ] - rCtx.maMapIX[ nLeft ] );
748  }
749 
750  int nSumR = 0;
751  int nSumG = 0;
752  int nSumB = 0;
753  BilinearWeightType nTotalWeightY = 0;
754 
755  for(long i = 0; i<= nLineRange; i++)
756  {
757  int nSumRowR = 0;
758  int nSumRowG = 0;
759  int nSumRowB = 0;
760  BilinearWeightType nTotalWeightX = 0;
761 
762  Scanline pScanlineSrc = rCtx.mpSrc->GetScanline( nLineStart + i );
763  for(long j = 0; j <= nRowRange; j++)
764  {
765  BitmapColor aCol0 = rCtx.mpSrc->GetPixelFromData( pScanlineSrc, nRowStart + j );
766 
767  if(nX == nEndX )
768  {
769 
770  nSumRowB += aCol0.GetBlue() << MAP_PRECISION;
771  nSumRowG += aCol0.GetGreen() << MAP_PRECISION;
772  nSumRowR += aCol0.GetRed() << MAP_PRECISION;
773  nTotalWeightX += lclMaxWeight();
774  }
775  else if( j == 0 )
776  {
777 
778  BilinearWeightType nWeightX = lclMaxWeight() - rCtx.maMapFX[ nLeft ];
779  nSumRowB += ( nWeightX *aCol0.GetBlue()) ;
780  nSumRowG += ( nWeightX *aCol0.GetGreen()) ;
781  nSumRowR += ( nWeightX *aCol0.GetRed()) ;
782  nTotalWeightX += nWeightX;
783  }
784  else if ( nRowRange == j )
785  {
786 
787  BilinearWeightType nWeightX = rCtx.maMapFX[ nRight ] ;
788  nSumRowB += ( nWeightX *aCol0.GetBlue() );
789  nSumRowG += ( nWeightX *aCol0.GetGreen() );
790  nSumRowR += ( nWeightX *aCol0.GetRed() );
791  nTotalWeightX += nWeightX;
792  }
793  else
794  {
795  nSumRowB += aCol0.GetBlue() << MAP_PRECISION;
796  nSumRowG += aCol0.GetGreen() << MAP_PRECISION;
797  nSumRowR += aCol0.GetRed() << MAP_PRECISION;
798  nTotalWeightX += lclMaxWeight();
799  }
800  }
801 
802  BilinearWeightType nWeightY = lclMaxWeight();
803  if( nY == nEndY )
804  nWeightY = lclMaxWeight();
805  else if( i == 0 )
806  nWeightY = lclMaxWeight() - rCtx.maMapFY[ nTop ];
807  else if( nLineRange == 1 )
808  nWeightY = rCtx.maMapFY[ nTop ];
809  else if ( nLineRange == i )
810  nWeightY = rCtx.maMapFY[ nBottom ];
811 
812  if (nTotalWeightX)
813  {
814  nSumRowB /= nTotalWeightX;
815  nSumRowG /= nTotalWeightX;
816  nSumRowR /= nTotalWeightX;
817  }
818 
819  nSumB += nWeightY * nSumRowB;
820  nSumG += nWeightY * nSumRowG;
821  nSumR += nWeightY * nSumRowR;
822  nTotalWeightY += nWeightY;
823  }
824 
825  if (nTotalWeightY)
826  {
827  nSumR /= nTotalWeightY;
828  nSumG /= nTotalWeightY;
829  nSumB /= nTotalWeightY;
830  }
831 
832  BitmapColor aColRes(static_cast<sal_uInt8>(nSumR), static_cast<sal_uInt8>(nSumG), static_cast<sal_uInt8>(nSumB));
833  rCtx.mpDest->SetPixelOnData( pScanDest, nXDst++, aColRes );
834  }
835  }
836 }
837 
838 } // end anonymous namespace
839 
840 BitmapScaleSuperFilter::BitmapScaleSuperFilter(const double& rScaleX, const double& rScaleY) :
841  mrScaleX(rScaleX),
842  mrScaleY(rScaleY)
843 {}
844 
846 {}
847 
849 {
850  Bitmap aBitmap(rBitmap.GetBitmap());
851  bool bRet = false;
852 
853  const Size aSizePix(rBitmap.GetSizePixel());
854 
855  bool bHMirr = mrScaleX < 0;
856  bool bVMirr = mrScaleY < 0;
857 
858  double fScaleX = std::fabs(mrScaleX);
859  double fScaleY = std::fabs(mrScaleY);
860 
861  const long nDstW = FRound(aSizePix.Width() * fScaleX);
862  const long nDstH = FRound(aSizePix.Height() * fScaleY);
863 
864  const double fScaleThresh = 0.6;
865 
866  if (nDstW <= 1 || nDstH <= 1)
867  return BitmapEx();
868 
869  // check cache for a previously scaled version of this
870  ScaleCacheKey aKey(aBitmap.ImplGetSalBitmap().get(),
871  Size(nDstW, nDstH));
872 
873  ImplSVData* pSVData = ImplGetSVData();
874  auto& rCache = pSVData->maGDIData.maScaleCache;
875  auto aFind = rCache.find(aKey);
876  if (aFind != rCache.end())
877  {
878  if (aFind->second.GetSizePixel().Width() == nDstW && aFind->second.GetSizePixel().Height() == nDstH)
879  return aFind->second;
880  else
881  SAL_WARN("vcl.gdi", "Error: size mismatch in scale cache");
882  }
883 
884  {
885  Bitmap::ScopedReadAccess pReadAccess(aBitmap);
886 
887  sal_uInt16 nSourceBitcount = aBitmap.GetBitCount();
888 
889  Bitmap aOutBmp(Size(nDstW, nDstH), std::max(nSourceBitcount, sal_uInt16(24)));
890  Size aOutSize = aOutBmp.GetSizePixel();
891  sal_uInt16 nTargetBitcount = aOutBmp.GetBitCount();
892 
893  if (!aOutSize.Width() || !aOutSize.Height())
894  {
895  SAL_WARN("vcl.gdi", "bmp creation failed");
896  return BitmapEx();
897  }
898 
899  BitmapScopedWriteAccess pWriteAccess(aOutBmp);
900 
901  const long nStartY = 0;
902  const long nEndY = nDstH - 1;
903 
904  if (pReadAccess && pWriteAccess)
905  {
906  ScaleRangeFn pScaleRangeFn;
907  const ScaleContext aContext( pReadAccess.get(),
908  pWriteAccess.get(),
909  pReadAccess->Width(),
910  pWriteAccess->Width(),
911  pReadAccess->Height(),
912  pWriteAccess->Height(),
913  bVMirr, bHMirr );
914 
915  bool bScaleUp = fScaleX >= fScaleThresh && fScaleY >= fScaleThresh;
916  // If we have a source bitmap with a palette the scaling converts
917  // from up to 8 bit image -> 24 bit non-palette, which is then
918  // adapted back to the same type as original.
919  if (pReadAccess->HasPalette())
920  {
921  switch( pReadAccess->GetScanlineFormat() )
922  {
924  pScaleRangeFn = bScaleUp ? scaleUpPalette8bit
925  : scaleDownPalette8bit;
926  break;
927  default:
928  pScaleRangeFn = bScaleUp ? scaleUpPaletteGeneral
929  : scaleDownPaletteGeneral;
930  break;
931  }
932  }
933  // Here we know that we are dealing with a non-palette source bitmap.
934  // The target is either 24 or 32 bit, depending on the image and
935  // the capabilities of the backend. If for some reason the destination
936  // is not the same bit-depth as the source, then we can't use
937  // a fast path, so we always need to process with a general scaler.
938  else if (nSourceBitcount != nTargetBitcount)
939  {
940  pScaleRangeFn = bScaleUp ? scaleUpNonPaletteGeneral : scaleDownNonPaletteGeneral;
941  }
942  // If we get here then we can only use a fast path, but let's
943  // still keep the fallback to the general scaler alive.
944  else
945  {
946  switch( pReadAccess->GetScanlineFormat() )
947  {
950  pScaleRangeFn = bScaleUp ? scaleUp<24> : scaleDown<24>;
951  break;
956  pScaleRangeFn = bScaleUp ? scaleUp<32> : scaleDown<32>;
957  break;
958  default:
959  pScaleRangeFn = bScaleUp ? scaleUpNonPaletteGeneral
960  : scaleDownNonPaletteGeneral;
961  break;
962  }
963  }
964 
965  // We want to thread - only if there is a lot of work to do:
966  // We work hard when there is a large destination image, or
967  // A large source image.
968  bool bHorizontalWork = pReadAccess->Height() >= 512 && pReadAccess->Width() >= 512;
969  bool bUseThreads = true;
970 
971  static bool bDisableThreadedScaling = getenv ("VCL_NO_THREAD_SCALE");
972  if (bDisableThreadedScaling || !bHorizontalWork)
973  {
974  SAL_INFO("vcl.gdi", "Scale in main thread");
975  bUseThreads = false;
976  }
977 
978  if (bUseThreads)
979  {
980  try
981  {
982  // partition and queue work
984  std::shared_ptr<comphelper::ThreadTaskTag> pTag = comphelper::ThreadPool::createThreadTaskTag();
985 
986  long nStripYStart = nStartY;
987  long nStripYEnd = nStripYStart + constScaleThreadStrip - 1;
988 
989  while (nStripYEnd < nEndY)
990  {
991  std::unique_ptr<ScaleTask> pTask(new ScaleTask(pTag, pScaleRangeFn, aContext, nStripYStart, nStripYEnd));
992  rShared.pushTask(std::move(pTask));
993  nStripYStart += constScaleThreadStrip;
994  nStripYEnd += constScaleThreadStrip;
995  }
996  if (nStripYStart <= nEndY)
997  {
998  std::unique_ptr<ScaleTask> pTask(new ScaleTask(pTag, pScaleRangeFn, aContext, nStripYStart, nEndY));
999  rShared.pushTask(std::move(pTask));
1000  }
1001  rShared.waitUntilDone(pTag);
1002  SAL_INFO("vcl.gdi", "All threaded scaling tasks complete");
1003  }
1004  catch (...)
1005  {
1006  SAL_WARN("vcl.gdi", "threaded bitmap scaling failed");
1007  bUseThreads = false;
1008  }
1009  }
1010 
1011  if (!bUseThreads)
1012  pScaleRangeFn( aContext, nStartY, nEndY );
1013 
1014  pWriteAccess.reset();
1015  bRet = true;
1016  aBitmap.AdaptBitCount(aOutBmp);
1017  aBitmap = aOutBmp;
1018  }
1019  }
1020 
1021  if (bRet)
1022  {
1023  tools::Rectangle aRect(Point(0, 0), Point(nDstW, nDstH));
1024  aBitmap.Crop(aRect);
1025  BitmapEx aRet(aBitmap);
1026  rCache.insert(std::make_pair(aKey, aRet));
1027  return aRet;
1028  }
1029 
1030  return BitmapEx();
1031 
1032 }
1033 
1034 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
long Width() const
sal_uInt8 GetRed() const
virtual ~BitmapScaleSuperFilter() override
long FRound(double fVal)
void waitUntilDone(const std::shared_ptr< ThreadTaskTag > &, bool bJoinAll=true)
long Height() const
int n1
virtual void doWork()=0
sal_Int64 n
virtual BitmapEx execute(BitmapEx const &rBitmap) const override
Size GetSizePixel() const
long Width() const
#define MAP(name, prefix, token, type, context)
int n2
Cache multiple scalings for the same bitmap.
Definition: svdata.hxx:170
sal_uInt8 GetBlue() const
static ThreadPool & getSharedOptimalPool()
ImplSVData * ImplGetSVData()
Definition: svdata.cxx:76
sal_uInt8 * Scanline
Definition: Scanline.hxx:25
int i
bool HasPalette() const
void pushTask(std::unique_ptr< ThreadTask > pTask)
ScanlineFormat GetScanlineFormat() const
const NodeContext & mrContext
Bitmap GetBitmap(Color aTransparentReplaceColor) const
Definition: bitmapex.cxx:237
sal_uInt8 GetGreen() const
long Height() const
unsigned char sal_uInt8
#define SAL_INFO(area, stream)
BitmapScaleSuperFilter(const double &rScaleX, const double &rScaleY)
static std::shared_ptr< ThreadTaskTag > createThreadTaskTag()
#define SAL_WARN(area, stream)
sal_uInt16 GetBitCount() const
std::enable_if< std::is_signed< T >::value||std::is_floating_point< T >::value, long >::type MinMax(T nVal, long nMin, long nMax)
const Size & GetSizePixel() const
Definition: bitmapex.hxx:83
typedef void(CALLTYPE *GetFuncDataPtr)(sal_uInt16 &nNo