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  tools::Long mnDestW;
54  bool mbHMirr;
55  bool mbVMirr;
56  std::vector<tools::Long> maMapIX;
57  std::vector<tools::Long> maMapIY;
58  std::vector<BilinearWeightType> maMapFX;
59  std::vector<BilinearWeightType> maMapFY;
60 
61  ScaleContext( BitmapReadAccess *pSrc,
62  BitmapWriteAccess *pDest,
63  tools::Long nSrcW, tools::Long nDestW,
64  tools::Long nSrcH, tools::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(tools::Long nSourceLength, tools::Long nDestinationLength, bool bMirrored,
81  std::vector<tools::Long> & rMapIX, std::vector<BilinearWeightType> & rMapFX)
82  {
83  const double fRevScale = (nDestinationLength > 1) ? double(nSourceLength - 1) / (nDestinationLength - 1) : 0.0;
84 
85  tools::Long nTemp = nSourceLength - 2;
86  tools::Long nTempX = nSourceLength - 1;
87 
88  for (tools::Long i = 0; i < nDestinationLength; i++)
89  {
90  double fTemp = i * fRevScale;
91  if (bMirrored)
92  fTemp = nTempX - fTemp;
93  rMapIX[i] = MinMax(tools::Long(fTemp), 0, nTemp);
94  rMapFX[i] = BilinearWeightType((fTemp - rMapIX[i]) * (BilinearWeightType(1) << MAP_PRECISION));
95  }
96  }
97 };
98 
99 constexpr tools::Long constScaleThreadStrip = 32;
100 
101 typedef void (*ScaleRangeFn)(const ScaleContext & rContext, tools::Long nStartY, tools::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, tools::Long nStartY, tools::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 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  constexpr int nColorComponents = nColorBits / 8;
286  static_assert(nColorComponents * 8 == nColorBits, "nColorBits must be divisible by 8");
287  using ScaleFunction = ScaleFunc<nColorComponents>;
288  const tools::Long nStartX = 0;
289  const tools::Long nEndX = rCtx.mnDestW - 1;
290 
291  for (tools::Long nY = nStartY; nY <= nEndY; nY++)
292  {
293  tools::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 (tools::Long nX = nStartX; nX <= nEndX; nX++)
307  {
308  tools::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  tools::Long mnStartY;
330  tools::Long mnEndY;
331 
332 public:
333  explicit ScaleTask(const std::shared_ptr<comphelper::ThreadTaskTag>& pTag,
334  ScaleRangeFn pScaleRangeFunction,
335  const ScaleContext& rContext,
336  tools::Long nStartY, tools::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, tools::Long nStartY, tools::Long nEndY)
351 {
352  const tools::Long nStartX = 0, nEndX = rCtx.mnDestW - 1;
353 
354  for( tools::Long nY = nStartY; nY <= nEndY; nY++ )
355  {
356  tools::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(tools::Long nX = nStartX, nXDst = 0; nX <= nEndX; nX++ )
363  {
364  tools::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, tools::Long nStartY, tools::Long nEndY)
389 {
390  const tools::Long nStartX = 0, nEndX = rCtx.mnDestW - 1;
391 
392  for( tools::Long nY = nStartY; nY <= nEndY; nY++ )
393  {
394  tools::Long nTempY = rCtx.maMapIY[ nY ];
395  BilinearWeightType nTempFY = rCtx.maMapFY[ nY ];
396  Scanline pScanline = rCtx.mpDest->GetScanline( nY );
397 
398  for( tools::Long nX = nStartX, nXDst = 0; nX <= nEndX; nX++ )
399  {
400  tools::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, tools::Long nStartY, tools::Long nEndY)
424 {
425  const tools::Long nStartX = 0, nEndX = rCtx.mnDestW - 1;
426 
427  for( tools::Long nY = nStartY; nY <= nEndY; nY++ )
428  {
429  tools::Long nTempY = rCtx.maMapIY[ nY ];
430  BilinearWeightType nTempFY = rCtx.maMapFY[ nY ];
431  Scanline pScanDest = rCtx.mpDest->GetScanline( nY );
432 
433  for( tools::Long nX = nStartX, nXDst = 0; nX <= nEndX; nX++ )
434  {
435  tools::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, tools::Long nStartY, tools::Long nEndY)
459 {
460  const tools::Long nStartX = 0, nEndX = rCtx.mnDestW - 1;
461 
462  for( tools::Long nY = nStartY; nY <= nEndY; nY++ )
463  {
464  tools::Long nTop = rCtx.mbVMirr ? ( nY + 1 ) : nY;
465  tools::Long nBottom = rCtx.mbVMirr ? nY : ( nY + 1 ) ;
466 
467  tools::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( tools::Long nX = nStartX , nXDst = 0; nX <= nEndX; nX++ )
481  {
482  tools::Long nLeft = rCtx.mbHMirr ? ( nX + 1 ) : nX;
483  tools::Long nRight = rCtx.mbHMirr ? nX : ( nX + 1 ) ;
484 
485  tools::Long nRowStart;
486  tools::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(tools::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(tools::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, tools::Long nStartY, tools::Long nEndY)
584 {
585  const tools::Long nStartX = 0, nEndX = rCtx.mnDestW - 1;
586 
587  for( tools::Long nY = nStartY; nY <= nEndY; nY++ )
588  {
589  tools::Long nTop = rCtx.mbVMirr ? ( nY + 1 ) : nY;
590  tools::Long nBottom = rCtx.mbVMirr ? nY : ( nY + 1 ) ;
591 
592  tools::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( tools::Long nX = nStartX , nXDst = 0; nX <= nEndX; nX++ )
606  {
607  tools::Long nLeft = rCtx.mbHMirr ? ( nX + 1 ) : nX;
608  tools::Long nRight = rCtx.mbHMirr ? nX : ( nX + 1 ) ;
609 
610  tools::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(tools::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(tools::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  tools::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, tools::Long nStartY, tools::Long nEndY)
712 {
713  const tools::Long nStartX = 0, nEndX = rCtx.mnDestW - 1;
714 
715  for( tools::Long nY = nStartY; nY <= nEndY; nY++ )
716  {
717  tools::Long nTop = rCtx.mbVMirr ? ( nY + 1 ) : nY;
718  tools::Long nBottom = rCtx.mbVMirr ? nY : ( nY + 1 ) ;
719 
720  tools::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( tools::Long nX = nStartX , nXDst = 0; nX <= nEndX; nX++ )
734  {
735  tools::Long nLeft = rCtx.mbHMirr ? ( nX + 1 ) : nX;
736  tools::Long nRight = rCtx.mbHMirr ? nX : ( nX + 1 ) ;
737 
738  tools::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(tools::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(tools::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 tools::Long nDstW = FRound(aSizePix.Width() * fScaleX);
862  const tools::Long nDstH = FRound(aSizePix.Height() * fScaleY);
863 
864  constexpr 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 tools::Long nEndY = nDstH - 1;
902 
903  if (pReadAccess && pWriteAccess)
904  {
905  ScaleRangeFn pScaleRangeFn;
906  const ScaleContext aContext( pReadAccess.get(),
907  pWriteAccess.get(),
908  pReadAccess->Width(),
909  pWriteAccess->Width(),
910  pReadAccess->Height(),
911  pWriteAccess->Height(),
912  bVMirr, bHMirr );
913 
914  bool bScaleUp = fScaleX >= fScaleThresh && fScaleY >= fScaleThresh;
915  // If we have a source bitmap with a palette the scaling converts
916  // from up to 8 bit image -> 24 bit non-palette, which is then
917  // adapted back to the same type as original.
918  if (pReadAccess->HasPalette())
919  {
920  switch( pReadAccess->GetScanlineFormat() )
921  {
923  pScaleRangeFn = bScaleUp ? scaleUpPalette8bit
924  : scaleDownPalette8bit;
925  break;
926  default:
927  pScaleRangeFn = bScaleUp ? scaleUpPaletteGeneral
928  : scaleDownPaletteGeneral;
929  break;
930  }
931  }
932  // Here we know that we are dealing with a non-palette source bitmap.
933  // The target is either 24 or 32 bit, depending on the image and
934  // the capabilities of the backend. If for some reason the destination
935  // is not the same bit-depth as the source, then we can't use
936  // a fast path, so we always need to process with a general scaler.
937  else if (nSourceBitcount != nTargetBitcount)
938  {
939  pScaleRangeFn = bScaleUp ? scaleUpNonPaletteGeneral : scaleDownNonPaletteGeneral;
940  }
941  // If we get here then we can only use a fast path, but let's
942  // still keep the fallback to the general scaler alive.
943  else
944  {
945  switch( pReadAccess->GetScanlineFormat() )
946  {
949  pScaleRangeFn = bScaleUp ? scaleUp<24> : scaleDown<24>;
950  break;
955  pScaleRangeFn = bScaleUp ? scaleUp<32> : scaleDown<32>;
956  break;
957  default:
958  pScaleRangeFn = bScaleUp ? scaleUpNonPaletteGeneral
959  : scaleDownNonPaletteGeneral;
960  break;
961  }
962  }
963 
964  // We want to thread - only if there is a lot of work to do:
965  // We work hard when there is a large destination image, or
966  // A large source image.
967  bool bHorizontalWork = pReadAccess->Height() >= 512 && pReadAccess->Width() >= 512;
968  bool bUseThreads = true;
969  const tools::Long nStartY = 0;
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  vcl::bitmap::generateStripRanges<constScaleThreadStrip>(nStartY, nEndY,
987  [&] (tools::Long const nStart, tools::Long const nEnd, bool const bLast)
988  {
989  if (!bLast)
990  {
991  auto pTask(std::make_unique<ScaleTask>(pTag, pScaleRangeFn, aContext, nStart, nEnd));
992  rShared.pushTask(std::move(pTask));
993  }
994  else
995  pScaleRangeFn(aContext, nStart, nEnd);
996  });
997  rShared.waitUntilDone(pTag);
998  SAL_INFO("vcl.gdi", "All threaded scaling tasks complete");
999  }
1000  catch (...)
1001  {
1002  SAL_WARN("vcl.gdi", "threaded bitmap scaling failed");
1003  bUseThreads = false;
1004  }
1005  }
1006 
1007  if (!bUseThreads)
1008  pScaleRangeFn( aContext, nStartY, nEndY );
1009 
1010  pWriteAccess.reset();
1011  bRet = true;
1012  aBitmap.AdaptBitCount(aOutBmp);
1013  aBitmap = aOutBmp;
1014  }
1015  }
1016 
1017  if (bRet)
1018  {
1019  tools::Rectangle aRect(Point(0, 0), Point(nDstW, nDstH));
1020  aBitmap.Crop(aRect);
1021  BitmapEx aRet(aBitmap);
1022  rCache.insert(std::make_pair(aKey, aRet));
1023  return aRet;
1024  }
1025 
1026  return BitmapEx();
1027 
1028 }
1029 
1030 /* 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)
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
tools::Long FRound(double fVal)
tools::Long Width() const
tools::Long Width() const
const NodeContext & mrContext
Bitmap GetBitmap(Color aTransparentReplaceColor) const
Definition: bitmapex.cxx:232
sal_uInt8 GetGreen() const
unsigned char sal_uInt8
#define SAL_INFO(area, stream)
BitmapScaleSuperFilter(const double &rScaleX, const double &rScaleY)
tools::Long Height() const
static std::shared_ptr< ThreadTaskTag > createThreadTaskTag()
#define SAL_WARN(area, stream)
sal_uInt16 GetBitCount() const
const Size & GetSizePixel() const
Definition: bitmapex.hxx:84
typedef void(CALLTYPE *GetFuncDataPtr)(sal_uInt16 &nNo