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