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