bump product version to 6.4.0.3
[LibreOffice.git] / vcl / source / gdi / bitmap3.cxx
blobe35636044f3f62b4ce270c85fb0e6055e02241ac
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include <math.h>
22 #include <vcl/bitmapaccess.hxx>
23 #include <vcl/bitmapex.hxx>
24 #include <vcl/bitmap.hxx>
25 #include <config_features.h>
26 #include <sal/log.hxx>
27 #include <osl/diagnose.h>
28 #include <tools/helpers.hxx>
29 #if HAVE_FEATURE_OPENGL
30 #include <vcl/opengl/OpenGLHelper.hxx>
31 #endif
32 #include <vcl/BitmapMonochromeFilter.hxx>
34 #include <BitmapScaleSuperFilter.hxx>
35 #include <BitmapScaleConvolutionFilter.hxx>
36 #include <BitmapFastScaleFilter.hxx>
37 #include <BitmapInterpolateScaleFilter.hxx>
38 #include <bitmapwriteaccess.hxx>
39 #include <bitmap/impoctree.hxx>
40 #include <bitmap/Octree.hxx>
41 #include <svdata.hxx>
42 #include <salinst.hxx>
43 #include <salbmp.hxx>
45 #include "impvect.hxx"
47 #include <memory>
49 #define GAMMA( _def_cVal, _def_InvGamma ) (static_cast<sal_uInt8>(MinMax(FRound(pow( _def_cVal/255.0,_def_InvGamma)*255.0),0,255)))
51 #define CALC_ERRORS \
52 nTemp = p1T[nX++] >> 12; \
53 nBErr = MinMax( nTemp, 0, 255 ); \
54 nBErr = nBErr - FloydIndexMap[ nBC = FloydMap[nBErr] ]; \
55 nTemp = p1T[nX++] >> 12; \
56 nGErr = MinMax( nTemp, 0, 255 ); \
57 nGErr = nGErr - FloydIndexMap[ nGC = FloydMap[nGErr] ]; \
58 nTemp = p1T[nX] >> 12; \
59 nRErr = MinMax( nTemp, 0, 255 ); \
60 nRErr = nRErr - FloydIndexMap[ nRC = FloydMap[nRErr] ];
62 #define CALC_TABLES3 \
63 p2T[nX++] += FloydError3[nBErr]; \
64 p2T[nX++] += FloydError3[nGErr]; \
65 p2T[nX++] += FloydError3[nRErr];
67 #define CALC_TABLES5 \
68 p2T[nX++] += FloydError5[nBErr]; \
69 p2T[nX++] += FloydError5[nGErr]; \
70 p2T[nX++] += FloydError5[nRErr];
72 #define CALC_TABLES7 \
73 p1T[++nX] += FloydError7[nBErr]; \
74 p2T[nX++] += FloydError1[nBErr]; \
75 p1T[nX] += FloydError7[nGErr]; \
76 p2T[nX++] += FloydError1[nGErr]; \
77 p1T[nX] += FloydError7[nRErr]; \
78 p2T[nX] += FloydError1[nRErr];
80 const extern sal_uLong nVCLRLut[ 6 ] = { 16, 17, 18, 19, 20, 21 };
81 const extern sal_uLong nVCLGLut[ 6 ] = { 0, 6, 12, 18, 24, 30 };
82 const extern sal_uLong nVCLBLut[ 6 ] = { 0, 36, 72, 108, 144, 180 };
84 const extern sal_uLong nVCLDitherLut[ 256 ] =
86 0, 49152, 12288, 61440, 3072, 52224, 15360, 64512, 768, 49920, 13056,
87 62208, 3840, 52992, 16128, 65280, 32768, 16384, 45056, 28672, 35840, 19456,
88 48128, 31744, 33536, 17152, 45824, 29440, 36608, 20224, 48896, 32512, 8192,
89 57344, 4096, 53248, 11264, 60416, 7168, 56320, 8960, 58112, 4864, 54016,
90 12032, 61184, 7936, 57088, 40960, 24576, 36864, 20480, 44032, 27648, 39936,
91 23552, 41728, 25344, 37632, 21248, 44800, 28416, 40704, 24320, 2048, 51200,
92 14336, 63488, 1024, 50176, 13312, 62464, 2816, 51968, 15104, 64256, 1792,
93 50944, 14080, 63232, 34816, 18432, 47104, 30720, 33792, 17408, 46080, 29696,
94 35584, 19200, 47872, 31488, 34560, 18176, 46848, 30464, 10240, 59392, 6144,
95 55296, 9216, 58368, 5120, 54272, 11008, 60160, 6912, 56064, 9984, 59136,
96 5888, 55040, 43008, 26624, 38912, 22528, 41984, 25600, 37888, 21504, 43776,
97 27392, 39680, 23296, 42752, 26368, 38656, 22272, 512, 49664, 12800, 61952,
98 3584, 52736, 15872, 65024, 256, 49408, 12544, 61696, 3328, 52480, 15616,
99 64768, 33280, 16896, 45568, 29184, 36352, 19968, 48640, 32256, 33024, 16640,
100 45312, 28928, 36096, 19712, 48384, 32000, 8704, 57856, 4608, 53760, 11776,
101 60928, 7680, 56832, 8448, 57600, 4352, 53504, 11520, 60672, 7424, 56576,
102 41472, 25088, 37376, 20992, 44544, 28160, 40448, 24064, 41216, 24832, 37120,
103 20736, 44288, 27904, 40192, 23808, 2560, 51712, 14848, 64000, 1536, 50688,
104 13824, 62976, 2304, 51456, 14592, 63744, 1280, 50432, 13568, 62720, 35328,
105 18944, 47616, 31232, 34304, 17920, 46592, 30208, 35072, 18688, 47360, 30976,
106 34048, 17664, 46336, 29952, 10752, 59904, 6656, 55808, 9728, 58880, 5632,
107 54784, 10496, 59648, 6400, 55552, 9472, 58624, 5376, 54528, 43520, 27136,
108 39424, 23040, 42496, 26112, 38400, 22016, 43264, 26880, 39168, 22784, 42240,
109 25856, 38144, 21760
112 const extern sal_uLong nVCLLut[ 256 ] =
114 0, 1286, 2572, 3858, 5144, 6430, 7716, 9002,
115 10288, 11574, 12860, 14146, 15432, 16718, 18004, 19290,
116 20576, 21862, 23148, 24434, 25720, 27006, 28292, 29578,
117 30864, 32150, 33436, 34722, 36008, 37294, 38580, 39866,
118 41152, 42438, 43724, 45010, 46296, 47582, 48868, 50154,
119 51440, 52726, 54012, 55298, 56584, 57870, 59156, 60442,
120 61728, 63014, 64300, 65586, 66872, 68158, 69444, 70730,
121 72016, 73302, 74588, 75874, 77160, 78446, 79732, 81018,
122 82304, 83590, 84876, 86162, 87448, 88734, 90020, 91306,
123 92592, 93878, 95164, 96450, 97736, 99022,100308,101594,
124 102880,104166,105452,106738,108024,109310,110596,111882,
125 113168,114454,115740,117026,118312,119598,120884,122170,
126 123456,124742,126028,127314,128600,129886,131172,132458,
127 133744,135030,136316,137602,138888,140174,141460,142746,
128 144032,145318,146604,147890,149176,150462,151748,153034,
129 154320,155606,156892,158178,159464,160750,162036,163322,
130 164608,165894,167180,168466,169752,171038,172324,173610,
131 174896,176182,177468,178754,180040,181326,182612,183898,
132 185184,186470,187756,189042,190328,191614,192900,194186,
133 195472,196758,198044,199330,200616,201902,203188,204474,
134 205760,207046,208332,209618,210904,212190,213476,214762,
135 216048,217334,218620,219906,221192,222478,223764,225050,
136 226336,227622,228908,230194,231480,232766,234052,235338,
137 236624,237910,239196,240482,241768,243054,244340,245626,
138 246912,248198,249484,250770,252056,253342,254628,255914,
139 257200,258486,259772,261058,262344,263630,264916,266202,
140 267488,268774,270060,271346,272632,273918,275204,276490,
141 277776,279062,280348,281634,282920,284206,285492,286778,
142 288064,289350,290636,291922,293208,294494,295780,297066,
143 298352,299638,300924,302210,303496,304782,306068,307354,
144 308640,309926,311212,312498,313784,315070,316356,317642,
145 318928,320214,321500,322786,324072,325358,326644,327930
148 const long FloydMap[256] =
150 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
151 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1,
152 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
153 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
154 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2,
155 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
156 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
157 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
158 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
159 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
160 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
161 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
162 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
163 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
164 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
165 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5
168 const long FloydError1[61] =
170 -7680, -7424, -7168, -6912, -6656, -6400, -6144,
171 -5888, -5632, -5376, -5120, -4864, -4608, -4352,
172 -4096, -3840, -3584, -3328, -3072, -2816, -2560,
173 -2304, -2048, -1792, -1536, -1280, -1024, -768,
174 -512, -256, 0, 256, 512, 768, 1024, 1280, 1536,
175 1792, 2048, 2304, 2560, 2816, 3072, 3328, 3584,
176 3840, 4096, 4352, 4608, 4864, 5120, 5376, 5632,
177 5888, 6144, 6400, 6656, 6912, 7168, 7424, 7680
180 const long FloydError3[61] =
182 -23040, -22272, -21504, -20736, -19968, -19200,
183 -18432, -17664, -16896, -16128, -15360, -14592,
184 -13824, -13056, -12288, -11520, -10752, -9984,
185 -9216, -8448, -7680, -6912, -6144, -5376, -4608,
186 -3840, -3072, -2304, -1536, -768, 0, 768, 1536,
187 2304, 3072, 3840, 4608, 5376, 6144, 6912, 7680,
188 8448, 9216, 9984, 10752, 11520, 12288, 13056,
189 13824, 14592, 15360, 16128, 16896, 17664, 18432,
190 19200, 19968, 20736, 21504, 22272, 23040
193 const long FloydError5[61] =
195 -38400, -37120, -35840, -34560, -33280, -32000,
196 -30720, -29440, -28160, -26880, -25600, -24320,
197 -23040, -21760, -20480, -19200, -17920, -16640,
198 -15360, -14080, -12800, -11520, -10240, -8960,
199 -7680, -6400, -5120, -3840, -2560, -1280, 0,
200 1280, 2560, 3840, 5120, 6400, 7680, 8960, 10240,
201 11520, 12800, 14080, 15360, 16640, 17920, 19200,
202 20480, 21760, 23040, 24320, 25600, 26880, 28160,
203 29440, 30720, 32000, 33280, 34560, 35840, 37120,
204 38400
207 const long FloydError7[61] =
209 -53760, -51968, -50176, -48384, -46592, -44800,
210 -43008, -41216, -39424, -37632, -35840, -34048,
211 -32256, -30464, -28672, -26880, -25088, -23296,
212 -21504, -19712, -17920, -16128, -14336, -12544,
213 -10752, -8960, -7168, -5376, -3584, -1792, 0,
214 1792, 3584, 5376, 7168, 8960, 10752, 12544, 14336,
215 16128, 17920, 19712, 21504, 23296, 25088, 26880,
216 28672, 30464, 32256, 34048, 35840, 37632, 39424,
217 41216, 43008, 44800, 46592, 48384, 50176, 51968,
218 53760
221 const long FloydIndexMap[6] =
223 -30, 21, 72, 123, 174, 225
226 bool Bitmap::Convert( BmpConversion eConversion )
228 // try to convert in backend
229 if (mxSalBmp)
231 // avoid large chunk of obsolete and hopefully rarely used conversions.
232 if (eConversion == BmpConversion::N8BitGreys)
234 std::shared_ptr<SalBitmap> xImpBmp(ImplGetSVData()->mpDefInst->CreateSalBitmap());
235 // frequently used conversion for creating alpha masks
236 if (xImpBmp->Create(*mxSalBmp) && xImpBmp->ConvertToGreyscale())
238 ImplSetSalBitmap(xImpBmp);
239 SAL_INFO( "vcl.opengl", "Ref count: " << mxSalBmp.use_count() );
240 return true;
245 const sal_uInt16 nBitCount = GetBitCount ();
246 bool bRet = false;
248 switch( eConversion )
250 case BmpConversion::N1BitThreshold:
252 BitmapEx aBmpEx(*this);
253 bRet = BitmapFilter::Filter(aBmpEx, BitmapMonochromeFilter(128));
254 *this = aBmpEx.GetBitmap();
256 break;
258 case BmpConversion::N4BitGreys:
259 bRet = ImplMakeGreyscales( 16 );
260 break;
262 case BmpConversion::N4BitColors:
264 if( nBitCount < 4 )
265 bRet = ImplConvertUp( 4 );
266 else if( nBitCount > 4 )
267 bRet = ImplConvertDown( 4 );
268 else
269 bRet = true;
271 break;
273 case BmpConversion::N8BitGreys:
274 bRet = ImplMakeGreyscales( 256 );
275 break;
277 case BmpConversion::N8BitColors:
279 if( nBitCount < 8 )
280 bRet = ImplConvertUp( 8 );
281 else if( nBitCount > 8 )
282 bRet = ImplConvertDown( 8 );
283 else
284 bRet = true;
286 break;
288 case BmpConversion::N8BitTrans:
290 Color aTrans( BMP_COL_TRANS );
292 if( nBitCount < 8 )
293 bRet = ImplConvertUp( 8, &aTrans );
294 else
295 bRet = ImplConvertDown( 8, &aTrans );
297 break;
299 case BmpConversion::N24Bit:
301 if( nBitCount < 24 )
302 bRet = ImplConvertUp( 24 );
303 else
304 bRet = true;
306 break;
308 case BmpConversion::Ghosted:
309 bRet = ImplConvertGhosted();
310 break;
312 default:
313 OSL_FAIL( "Bitmap::Convert(): Unsupported conversion" );
314 break;
317 return bRet;
320 bool Bitmap::ImplMakeGreyscales( sal_uInt16 nGreys )
322 SAL_WARN_IF( nGreys != 16 && nGreys != 256, "vcl", "Only 16 or 256 greyscales are supported!" );
324 ScopedReadAccess pReadAcc(*this);
325 bool bRet = false;
327 if( pReadAcc )
329 const BitmapPalette& rPal = GetGreyPalette( nGreys );
330 sal_uLong nShift = ( ( nGreys == 16 ) ? 4UL : 0UL );
331 bool bPalDiffers = !pReadAcc->HasPalette() || ( rPal.GetEntryCount() != pReadAcc->GetPaletteEntryCount() );
333 if( !bPalDiffers )
334 bPalDiffers = ( rPal != pReadAcc->GetPalette() );
336 if( bPalDiffers )
338 Bitmap aNewBmp( GetSizePixel(), ( nGreys == 16 ) ? 4 : 8, &rPal );
339 BitmapScopedWriteAccess pWriteAcc(aNewBmp);
341 if( pWriteAcc )
343 const long nWidth = pWriteAcc->Width();
344 const long nHeight = pWriteAcc->Height();
346 if( pReadAcc->HasPalette() )
348 for( long nY = 0; nY < nHeight; nY++ )
350 Scanline pScanline = pWriteAcc->GetScanline(nY);
351 Scanline pScanlineRead = pReadAcc->GetScanline(nY);
352 for( long nX = 0; nX < nWidth; nX++ )
354 const sal_uInt8 cIndex = pReadAcc->GetIndexFromData( pScanlineRead, nX );
355 pWriteAcc->SetPixelOnData( pScanline, nX,
356 BitmapColor(pReadAcc->GetPaletteColor( cIndex ).GetLuminance() >> nShift) );
360 else if( pReadAcc->GetScanlineFormat() == ScanlineFormat::N24BitTcBgr &&
361 pWriteAcc->GetScanlineFormat() == ScanlineFormat::N8BitPal )
363 nShift += 8;
365 for( long nY = 0; nY < nHeight; nY++ )
367 Scanline pReadScan = pReadAcc->GetScanline( nY );
368 Scanline pWriteScan = pWriteAcc->GetScanline( nY );
370 for( long nX = 0; nX < nWidth; nX++ )
372 const sal_uLong nB = *pReadScan++;
373 const sal_uLong nG = *pReadScan++;
374 const sal_uLong nR = *pReadScan++;
376 *pWriteScan++ = static_cast<sal_uInt8>( ( nB * 28UL + nG * 151UL + nR * 77UL ) >> nShift );
380 else if( pReadAcc->GetScanlineFormat() == ScanlineFormat::N24BitTcRgb &&
381 pWriteAcc->GetScanlineFormat() == ScanlineFormat::N8BitPal )
383 nShift += 8;
385 for( long nY = 0; nY < nHeight; nY++ )
387 Scanline pReadScan = pReadAcc->GetScanline( nY );
388 Scanline pWriteScan = pWriteAcc->GetScanline( nY );
390 for( long nX = 0; nX < nWidth; nX++ )
392 const sal_uLong nR = *pReadScan++;
393 const sal_uLong nG = *pReadScan++;
394 const sal_uLong nB = *pReadScan++;
396 *pWriteScan++ = static_cast<sal_uInt8>( ( nB * 28UL + nG * 151UL + nR * 77UL ) >> nShift );
400 else
402 for( long nY = 0; nY < nHeight; nY++ )
404 Scanline pScanline = pWriteAcc->GetScanline(nY);
405 Scanline pScanlineRead = pReadAcc->GetScanline(nY);
406 for( long nX = 0; nX < nWidth; nX++ )
407 pWriteAcc->SetPixelOnData( pScanline, nX, BitmapColor(pReadAcc->GetPixelFromData( pScanlineRead, nX ).GetLuminance() >> nShift) );
411 pWriteAcc.reset();
412 bRet = true;
415 pReadAcc.reset();
417 if( bRet )
419 const MapMode aMap( maPrefMapMode );
420 const Size aSize( maPrefSize );
422 *this = aNewBmp;
424 maPrefMapMode = aMap;
425 maPrefSize = aSize;
428 else
430 pReadAcc.reset();
431 bRet = true;
435 return bRet;
438 bool Bitmap::ImplConvertUp(sal_uInt16 nBitCount, Color const * pExtColor)
440 SAL_WARN_IF( nBitCount <= GetBitCount(), "vcl", "New BitCount must be greater!" );
442 Bitmap::ScopedReadAccess pReadAcc(*this);
443 bool bRet = false;
445 if (pReadAcc)
447 BitmapPalette aPalette;
448 Bitmap aNewBmp(GetSizePixel(), nBitCount, pReadAcc->HasPalette() ? &pReadAcc->GetPalette() : &aPalette);
449 BitmapScopedWriteAccess pWriteAcc(aNewBmp);
451 if (pWriteAcc)
453 const long nWidth = pWriteAcc->Width();
454 const long nHeight = pWriteAcc->Height();
456 if (pWriteAcc->HasPalette())
458 const BitmapPalette& rOldPalette = pReadAcc->GetPalette();
459 const sal_uInt16 nOldCount = rOldPalette.GetEntryCount();
460 assert(nOldCount <= (1 << GetBitCount()));
462 aPalette.SetEntryCount(1 << nBitCount);
464 for (sal_uInt16 i = 0; i < nOldCount; i++)
465 aPalette[i] = rOldPalette[i];
467 if (pExtColor)
468 aPalette[aPalette.GetEntryCount() - 1] = *pExtColor;
470 pWriteAcc->SetPalette(aPalette);
472 for (long nY = 0; nY < nHeight; nY++)
474 Scanline pScanline = pWriteAcc->GetScanline(nY);
475 Scanline pScanlineRead = pReadAcc->GetScanline(nY);
476 for (long nX = 0; nX < nWidth; nX++)
478 pWriteAcc->SetPixelOnData(pScanline, nX, pReadAcc->GetPixelFromData(pScanlineRead, nX));
482 else
484 if (pReadAcc->HasPalette())
486 for (long nY = 0; nY < nHeight; nY++)
488 Scanline pScanline = pWriteAcc->GetScanline(nY);
489 Scanline pScanlineRead = pReadAcc->GetScanline(nY);
490 for (long nX = 0; nX < nWidth; nX++)
492 pWriteAcc->SetPixelOnData(pScanline, nX, pReadAcc->GetPaletteColor(pReadAcc->GetIndexFromData(pScanlineRead, nX)));
496 else
498 for (long nY = 0; nY < nHeight; nY++)
500 Scanline pScanline = pWriteAcc->GetScanline(nY);
501 Scanline pScanlineRead = pReadAcc->GetScanline(nY);
502 for (long nX = 0; nX < nWidth; nX++)
504 pWriteAcc->SetPixelOnData(pScanline, nX, pReadAcc->GetPixelFromData(pScanlineRead, nX));
509 bRet = true;
512 if (bRet)
514 const MapMode aMap(maPrefMapMode);
515 const Size aSize(maPrefSize);
517 *this = aNewBmp;
519 maPrefMapMode = aMap;
520 maPrefSize = aSize;
524 return bRet;
527 bool Bitmap::ImplConvertDown(sal_uInt16 nBitCount, Color const * pExtColor)
529 SAL_WARN_IF(nBitCount > GetBitCount(), "vcl", "New BitCount must be lower ( or equal when pExtColor is set )!");
531 Bitmap::ScopedReadAccess pReadAcc(*this);
532 bool bRet = false;
534 if (pReadAcc)
536 BitmapPalette aPalette;
537 Bitmap aNewBmp(GetSizePixel(), nBitCount, &aPalette);
538 BitmapScopedWriteAccess pWriteAcc(aNewBmp);
540 if (pWriteAcc)
542 const sal_uInt16 nCount = 1 << nBitCount;
543 const long nWidth = pWriteAcc->Width();
544 const long nWidth1 = nWidth - 1;
545 const long nHeight = pWriteAcc->Height();
546 Octree aOctree(*pReadAcc, pExtColor ? (nCount - 1) : nCount);
547 aPalette = aOctree.GetPalette();
548 InverseColorMap aColorMap(aPalette);
549 BitmapColor aColor;
550 ImpErrorQuad aErrQuad;
551 std::vector<ImpErrorQuad> aErrQuad1(nWidth);
552 std::vector<ImpErrorQuad> aErrQuad2(nWidth);
553 ImpErrorQuad* pQLine1 = aErrQuad1.data();
554 ImpErrorQuad* pQLine2 = nullptr;
555 long nYTmp = 0;
556 sal_uInt8 cIndex;
557 bool bQ1 = true;
559 if (pExtColor)
561 aPalette.SetEntryCount(aPalette.GetEntryCount() + 1);
562 aPalette[aPalette.GetEntryCount() - 1] = *pExtColor;
565 // set Black/White always, if we have enough space
566 if (aPalette.GetEntryCount() < (nCount - 1))
568 aPalette.SetEntryCount(aPalette.GetEntryCount() + 2);
569 aPalette[aPalette.GetEntryCount() - 2] = COL_BLACK;
570 aPalette[aPalette.GetEntryCount() - 1] = COL_WHITE;
573 pWriteAcc->SetPalette(aPalette);
575 for (long nY = 0; nY < std::min(nHeight, 2L); nY++, nYTmp++)
577 pQLine2 = !nY ? aErrQuad1.data() : aErrQuad2.data();
578 Scanline pScanlineRead = pReadAcc->GetScanline(nYTmp);
579 for (long nX = 0; nX < nWidth; nX++)
581 if (pReadAcc->HasPalette())
582 pQLine2[nX] = pReadAcc->GetPaletteColor(pReadAcc->GetIndexFromData(pScanlineRead, nX));
583 else
584 pQLine2[nX] = pReadAcc->GetPixelFromData(pScanlineRead, nX);
588 assert(pQLine2 || nHeight == 0);
590 for (long nY = 0; nY < nHeight; nY++, nYTmp++)
592 // first pixel in the line
593 cIndex = static_cast<sal_uInt8>(aColorMap.GetBestPaletteIndex(pQLine1[0].ImplGetColor()));
594 Scanline pScanline = pWriteAcc->GetScanline(nY);
595 pWriteAcc->SetPixelOnData(pScanline, 0, BitmapColor(cIndex));
597 long nX;
598 for (nX = 1; nX < nWidth1; nX++)
600 aColor = pQLine1[nX].ImplGetColor();
601 cIndex = static_cast<sal_uInt8>(aColorMap.GetBestPaletteIndex(aColor));
602 aErrQuad = (ImpErrorQuad(aColor) -= pWriteAcc->GetPaletteColor(cIndex));
603 pQLine1[++nX].ImplAddColorError7(aErrQuad);
604 pQLine2[nX--].ImplAddColorError1(aErrQuad);
605 pQLine2[nX--].ImplAddColorError5(aErrQuad);
606 pQLine2[nX++].ImplAddColorError3(aErrQuad);
607 pWriteAcc->SetPixelOnData(pScanline, nX, BitmapColor(cIndex));
610 // Last RowPixel
611 if (nX < nWidth)
613 cIndex = static_cast<sal_uInt8>(aColorMap.GetBestPaletteIndex(pQLine1[nWidth1].ImplGetColor()));
614 pWriteAcc->SetPixelOnData(pScanline, nX, BitmapColor(cIndex));
617 // Refill/copy row buffer
618 pQLine1 = pQLine2;
619 bQ1 = !bQ1;
620 pQLine2 = bQ1 ? aErrQuad2.data() : aErrQuad1.data();
622 if (nYTmp < nHeight)
624 Scanline pScanlineRead = pReadAcc->GetScanline(nYTmp);
625 for (nX = 0; nX < nWidth; nX++)
627 if (pReadAcc->HasPalette())
628 pQLine2[nX] = pReadAcc->GetPaletteColor(pReadAcc->GetIndexFromData(pScanlineRead, nX));
629 else
630 pQLine2[nX] = pReadAcc->GetPixelFromData(pScanlineRead, nX);
635 bRet = true;
638 if(bRet)
640 const MapMode aMap(maPrefMapMode);
641 const Size aSize(maPrefSize);
643 *this = aNewBmp;
645 maPrefMapMode = aMap;
646 maPrefSize = aSize;
650 return bRet;
653 bool Bitmap::ImplConvertGhosted()
655 Bitmap aNewBmp;
656 ScopedReadAccess pR(*this);
657 bool bRet = false;
659 if( pR )
661 if( pR->HasPalette() )
663 BitmapPalette aNewPal( pR->GetPaletteEntryCount() );
665 for( long i = 0, nCount = aNewPal.GetEntryCount(); i < nCount; i++ )
667 const BitmapColor& rOld = pR->GetPaletteColor( static_cast<sal_uInt16>(i) );
668 aNewPal[ static_cast<sal_uInt16>(i) ] = BitmapColor( ( rOld.GetRed() >> 1 ) | 0x80,
669 ( rOld.GetGreen() >> 1 ) | 0x80,
670 ( rOld.GetBlue() >> 1 ) | 0x80 );
673 aNewBmp = Bitmap( GetSizePixel(), GetBitCount(), &aNewPal );
674 BitmapScopedWriteAccess pW(aNewBmp);
676 if( pW )
678 pW->CopyBuffer( *pR );
679 bRet = true;
682 else
684 aNewBmp = Bitmap( GetSizePixel(), 24 );
686 BitmapScopedWriteAccess pW(aNewBmp);
688 if( pW )
690 const long nWidth = pR->Width(), nHeight = pR->Height();
692 for( long nY = 0; nY < nHeight; nY++ )
694 Scanline pScanline = pW->GetScanline(nY);
695 Scanline pScanlineRead = pR->GetScanline(nY);
696 for( long nX = 0; nX < nWidth; nX++ )
698 const BitmapColor aOld( pR->GetPixelFromData( pScanlineRead, nX ) );
699 pW->SetPixelOnData( pScanline, nX, BitmapColor( ( aOld.GetRed() >> 1 ) | 0x80,
700 ( aOld.GetGreen() >> 1 ) | 0x80,
701 ( aOld.GetBlue() >> 1 ) | 0x80 ) );
706 bRet = true;
710 pR.reset();
713 if( bRet )
715 const MapMode aMap( maPrefMapMode );
716 const Size aSize( maPrefSize );
718 *this = aNewBmp;
720 maPrefMapMode = aMap;
721 maPrefSize = aSize;
724 return bRet;
727 bool Bitmap::Scale( const double& rScaleX, const double& rScaleY, BmpScaleFlag nScaleFlag )
729 if(basegfx::fTools::equalZero(rScaleX) || basegfx::fTools::equalZero(rScaleY))
731 // no scale
732 return true;
735 if(basegfx::fTools::equal(rScaleX, 1.0) && basegfx::fTools::equal(rScaleY, 1.0))
737 // no scale
738 return true;
741 const sal_uInt16 nStartCount(GetBitCount());
743 if (mxSalBmp && mxSalBmp->ScalingSupported())
745 // implementation specific scaling
746 std::shared_ptr<SalBitmap> xImpBmp(ImplGetSVData()->mpDefInst->CreateSalBitmap());
747 if (xImpBmp->Create(*mxSalBmp) && xImpBmp->Scale(rScaleX, rScaleY, nScaleFlag))
749 ImplSetSalBitmap(xImpBmp);
750 SAL_INFO( "vcl.opengl", "Ref count: " << mxSalBmp.use_count() );
751 maPrefMapMode = MapMode( MapUnit::MapPixel );
752 maPrefSize = xImpBmp->GetSize();
753 return true;
757 // fdo#33455
759 // If we start with a 1 bit image, then after scaling it in any mode except
760 // BmpScaleFlag::Fast we have a 24bit image which is perfectly correct, but we
761 // are going to down-shift it to mono again and Bitmap::MakeMonochrome just
762 // has "Bitmap aNewBmp( GetSizePixel(), 1 );" to create a 1 bit bitmap which
763 // will default to black/white and the colors mapped to which ever is closer
764 // to black/white
766 // So the easiest thing to do to retain the colors of 1 bit bitmaps is to
767 // just use the fast scale rather than attempting to count unique colors in
768 // the other converters and pass all the info down through
769 // Bitmap::MakeMonochrome
770 if (nStartCount == 1)
771 nScaleFlag = BmpScaleFlag::Fast;
773 BitmapEx aBmpEx(*this);
774 bool bRetval(false);
776 switch(nScaleFlag)
778 case BmpScaleFlag::Default:
779 if (GetSizePixel().Width() < 2 || GetSizePixel().Height() < 2)
780 bRetval = BitmapFilter::Filter(aBmpEx, BitmapFastScaleFilter(rScaleX, rScaleY));
781 else
782 bRetval = BitmapFilter::Filter(aBmpEx, BitmapScaleSuperFilter(rScaleX, rScaleY));
783 break;
785 case BmpScaleFlag::Fast:
786 case BmpScaleFlag::NearestNeighbor:
787 bRetval = BitmapFilter::Filter(aBmpEx, BitmapFastScaleFilter(rScaleX, rScaleY));
788 break;
790 case BmpScaleFlag::Interpolate:
791 bRetval = BitmapFilter::Filter(aBmpEx, BitmapInterpolateScaleFilter(rScaleX, rScaleY));
792 break;
794 case BmpScaleFlag::Super:
795 bRetval = BitmapFilter::Filter(aBmpEx, BitmapScaleSuperFilter(rScaleX, rScaleY));
796 break;
797 case BmpScaleFlag::BestQuality:
798 case BmpScaleFlag::Lanczos:
799 bRetval = BitmapFilter::Filter(aBmpEx, vcl::BitmapScaleLanczos3Filter(rScaleX, rScaleY));
800 break;
802 case BmpScaleFlag::BiCubic:
803 bRetval = BitmapFilter::Filter(aBmpEx, vcl::BitmapScaleBicubicFilter(rScaleX, rScaleY));
804 break;
806 case BmpScaleFlag::BiLinear:
807 bRetval = BitmapFilter::Filter(aBmpEx, vcl::BitmapScaleBilinearFilter(rScaleX, rScaleY));
808 break;
811 if (bRetval)
812 *this = aBmpEx.GetBitmapRef();
814 OSL_ENSURE(!bRetval || nStartCount == GetBitCount(), "Bitmap::Scale has changed the ColorDepth, this should *not* happen (!)");
815 return bRetval;
818 bool Bitmap::Scale( const Size& rNewSize, BmpScaleFlag nScaleFlag )
820 const Size aSize( GetSizePixel() );
821 bool bRet;
823 if( aSize.Width() && aSize.Height() )
825 bRet = Scale( static_cast<double>(rNewSize.Width()) / aSize.Width(),
826 static_cast<double>(rNewSize.Height()) / aSize.Height(),
827 nScaleFlag );
829 else
830 bRet = true;
832 return bRet;
835 bool Bitmap::HasFastScale()
837 #if HAVE_FEATURE_OPENGL
838 return OpenGLHelper::isVCLOpenGLEnabled();
839 #else
840 return false;
841 #endif
844 void Bitmap::AdaptBitCount(Bitmap& rNew) const
846 // aNew is the result of some operation; adapt it's BitCount to the original (this)
847 if(GetBitCount() != rNew.GetBitCount())
849 switch(GetBitCount())
851 case 1:
853 rNew.Convert(BmpConversion::N1BitThreshold);
854 break;
856 case 4:
858 if(HasGreyPalette())
860 rNew.Convert(BmpConversion::N4BitGreys);
862 else
864 rNew.Convert(BmpConversion::N4BitColors);
866 break;
868 case 8:
870 if(HasGreyPalette())
872 rNew.Convert(BmpConversion::N8BitGreys);
874 else
876 rNew.Convert(BmpConversion::N8BitColors);
878 break;
880 case 24:
882 rNew.Convert(BmpConversion::N24Bit);
883 break;
885 default:
887 OSL_ENSURE(false, "BitDepth adaptation failed (!)");
888 break;
894 bool Bitmap::Dither()
896 const Size aSize( GetSizePixel() );
898 if( aSize.Width() == 1 || aSize.Height() == 1 )
899 return true;
901 bool bRet = false;
903 if( ( aSize.Width() > 3 ) && ( aSize.Height() > 2 ) )
905 ScopedReadAccess pReadAcc(*this);
906 Bitmap aNewBmp( GetSizePixel(), 8 );
907 BitmapScopedWriteAccess pWriteAcc(aNewBmp);
909 if( pReadAcc && pWriteAcc )
911 BitmapColor aColor;
912 long nWidth = pReadAcc->Width();
913 long nWidth1 = nWidth - 1;
914 long nHeight = pReadAcc->Height();
915 long nX;
916 long nW = nWidth * 3;
917 long nW2 = nW - 3;
918 long nRErr, nGErr, nBErr;
919 long nRC, nGC, nBC;
920 std::unique_ptr<long[]> p1(new long[ nW ]);
921 std::unique_ptr<long[]> p2(new long[ nW ]);
922 long* p1T = p1.get();
923 long* p2T = p2.get();
924 long* pTmp;
925 bool bPal = pReadAcc->HasPalette();
927 pTmp = p2T;
929 if( bPal )
931 Scanline pScanlineRead = pReadAcc->GetScanline(0);
932 for( long nZ = 0; nZ < nWidth; nZ++ )
934 aColor = pReadAcc->GetPaletteColor( pReadAcc->GetIndexFromData( pScanlineRead, nZ ) );
936 *pTmp++ = static_cast<long>(aColor.GetBlue()) << 12;
937 *pTmp++ = static_cast<long>(aColor.GetGreen()) << 12;
938 *pTmp++ = static_cast<long>(aColor.GetRed()) << 12;
941 else
943 Scanline pScanlineRead = pReadAcc->GetScanline(0);
944 for( long nZ = 0; nZ < nWidth; nZ++ )
946 aColor = pReadAcc->GetPixelFromData( pScanlineRead, nZ );
948 *pTmp++ = static_cast<long>(aColor.GetBlue()) << 12;
949 *pTmp++ = static_cast<long>(aColor.GetGreen()) << 12;
950 *pTmp++ = static_cast<long>(aColor.GetRed()) << 12;
954 for( long nY = 1, nYAcc = 0; nY <= nHeight; nY++, nYAcc++ )
956 pTmp = p1T;
957 p1T = p2T;
958 p2T = pTmp;
960 if( nY < nHeight )
962 if( bPal )
964 Scanline pScanlineRead = pReadAcc->GetScanline(nY);
965 for( long nZ = 0; nZ < nWidth; nZ++ )
967 aColor = pReadAcc->GetPaletteColor( pReadAcc->GetIndexFromData( pScanlineRead, nZ ) );
969 *pTmp++ = static_cast<long>(aColor.GetBlue()) << 12;
970 *pTmp++ = static_cast<long>(aColor.GetGreen()) << 12;
971 *pTmp++ = static_cast<long>(aColor.GetRed()) << 12;
974 else
976 Scanline pScanlineRead = pReadAcc->GetScanline(nY);
977 for( long nZ = 0; nZ < nWidth; nZ++ )
979 aColor = pReadAcc->GetPixelFromData( pScanlineRead, nZ );
981 *pTmp++ = static_cast<long>(aColor.GetBlue()) << 12;
982 *pTmp++ = static_cast<long>(aColor.GetGreen()) << 12;
983 *pTmp++ = static_cast<long>(aColor.GetRed()) << 12;
988 // Examine first Pixel separately
989 nX = 0;
990 long nTemp;
991 CALC_ERRORS;
992 CALC_TABLES7;
993 nX -= 5;
994 CALC_TABLES5;
995 Scanline pScanline = pWriteAcc->GetScanline(nYAcc);
996 pWriteAcc->SetPixelOnData( pScanline, 0, BitmapColor(static_cast<sal_uInt8>(nVCLBLut[ nBC ] + nVCLGLut[nGC ] + nVCLRLut[nRC ])) );
998 // Get middle Pixels using a loop
999 long nXAcc;
1000 for ( nX = 3, nXAcc = 1; nX < nW2; nXAcc++ )
1002 CALC_ERRORS;
1003 CALC_TABLES7;
1004 nX -= 8;
1005 CALC_TABLES3;
1006 CALC_TABLES5;
1007 pWriteAcc->SetPixelOnData( pScanline, nXAcc, BitmapColor(static_cast<sal_uInt8>(nVCLBLut[ nBC ] + nVCLGLut[nGC ] + nVCLRLut[nRC ])) );
1010 // Treat last Pixel separately
1011 CALC_ERRORS;
1012 nX -= 5;
1013 CALC_TABLES3;
1014 CALC_TABLES5;
1015 pWriteAcc->SetPixelOnData( pScanline, nWidth1, BitmapColor(static_cast<sal_uInt8>(nVCLBLut[ nBC ] + nVCLGLut[nGC ] + nVCLRLut[nRC ])) );
1018 bRet = true;
1021 pReadAcc.reset();
1022 pWriteAcc.reset();
1024 if( bRet )
1026 const MapMode aMap( maPrefMapMode );
1027 const Size aPrefSize( maPrefSize );
1029 *this = aNewBmp;
1031 maPrefMapMode = aMap;
1032 maPrefSize = aPrefSize;
1036 return bRet;
1039 void Bitmap::Vectorize( GDIMetaFile& rMtf, sal_uInt8 cReduce, const Link<long,void>* pProgress )
1041 ImplVectorizer::ImplVectorize( *this, rMtf, cReduce, pProgress );
1044 bool Bitmap::Adjust( short nLuminancePercent, short nContrastPercent,
1045 short nChannelRPercent, short nChannelGPercent, short nChannelBPercent,
1046 double fGamma, bool bInvert, bool msoBrightness )
1048 bool bRet = false;
1050 // nothing to do => return quickly
1051 if( !nLuminancePercent && !nContrastPercent &&
1052 !nChannelRPercent && !nChannelGPercent && !nChannelBPercent &&
1053 ( fGamma == 1.0 ) && !bInvert )
1055 bRet = true;
1057 else
1059 BitmapScopedWriteAccess pAcc(*this);
1061 if( pAcc )
1063 BitmapColor aCol;
1064 const long nW = pAcc->Width();
1065 const long nH = pAcc->Height();
1066 std::unique_ptr<sal_uInt8[]> cMapR(new sal_uInt8[ 256 ]);
1067 std::unique_ptr<sal_uInt8[]> cMapG(new sal_uInt8[ 256 ]);
1068 std::unique_ptr<sal_uInt8[]> cMapB(new sal_uInt8[ 256 ]);
1069 double fM, fROff, fGOff, fBOff, fOff;
1071 // calculate slope
1072 if( nContrastPercent >= 0 )
1073 fM = 128.0 / ( 128.0 - 1.27 * MinMax( nContrastPercent, 0, 100 ) );
1074 else
1075 fM = ( 128.0 + 1.27 * MinMax( nContrastPercent, -100, 0 ) ) / 128.0;
1077 if(!msoBrightness)
1078 // total offset = luminance offset + contrast offset
1079 fOff = MinMax( nLuminancePercent, -100, 100 ) * 2.55 + 128.0 - fM * 128.0;
1080 else
1081 fOff = MinMax( nLuminancePercent, -100, 100 ) * 2.55;
1083 // channel offset = channel offset + total offset
1084 fROff = nChannelRPercent * 2.55 + fOff;
1085 fGOff = nChannelGPercent * 2.55 + fOff;
1086 fBOff = nChannelBPercent * 2.55 + fOff;
1088 // calculate gamma value
1089 fGamma = ( fGamma <= 0.0 || fGamma > 10.0 ) ? 1.0 : ( 1.0 / fGamma );
1090 const bool bGamma = ( fGamma != 1.0 );
1092 // create mapping table
1093 for( long nX = 0; nX < 256; nX++ )
1095 if(!msoBrightness)
1097 cMapR[ nX ] = static_cast<sal_uInt8>(MinMax( FRound( nX * fM + fROff ), 0, 255 ));
1098 cMapG[ nX ] = static_cast<sal_uInt8>(MinMax( FRound( nX * fM + fGOff ), 0, 255 ));
1099 cMapB[ nX ] = static_cast<sal_uInt8>(MinMax( FRound( nX * fM + fBOff ), 0, 255 ));
1101 else
1103 // LO simply uses (in a somewhat optimized form) "newcolor = (oldcolor-128)*contrast+brightness+128"
1104 // as the formula, i.e. contrast first, brightness afterwards. MSOffice, for whatever weird reason,
1105 // use neither first, but apparently it applies half of brightness before contrast and half afterwards.
1106 cMapR[ nX ] = static_cast<sal_uInt8>(MinMax( FRound( (nX+fROff/2-128) * fM + 128 + fROff/2 ), 0, 255 ));
1107 cMapG[ nX ] = static_cast<sal_uInt8>(MinMax( FRound( (nX+fGOff/2-128) * fM + 128 + fGOff/2 ), 0, 255 ));
1108 cMapB[ nX ] = static_cast<sal_uInt8>(MinMax( FRound( (nX+fBOff/2-128) * fM + 128 + fBOff/2 ), 0, 255 ));
1110 if( bGamma )
1112 cMapR[ nX ] = GAMMA( cMapR[ nX ], fGamma );
1113 cMapG[ nX ] = GAMMA( cMapG[ nX ], fGamma );
1114 cMapB[ nX ] = GAMMA( cMapB[ nX ], fGamma );
1117 if( bInvert )
1119 cMapR[ nX ] = ~cMapR[ nX ];
1120 cMapG[ nX ] = ~cMapG[ nX ];
1121 cMapB[ nX ] = ~cMapB[ nX ];
1125 // do modifying
1126 if( pAcc->HasPalette() )
1128 BitmapColor aNewCol;
1130 for( sal_uInt16 i = 0, nCount = pAcc->GetPaletteEntryCount(); i < nCount; i++ )
1132 const BitmapColor& rCol = pAcc->GetPaletteColor( i );
1133 aNewCol.SetRed( cMapR[ rCol.GetRed() ] );
1134 aNewCol.SetGreen( cMapG[ rCol.GetGreen() ] );
1135 aNewCol.SetBlue( cMapB[ rCol.GetBlue() ] );
1136 pAcc->SetPaletteColor( i, aNewCol );
1139 else if( pAcc->GetScanlineFormat() == ScanlineFormat::N24BitTcBgr )
1141 for( long nY = 0; nY < nH; nY++ )
1143 Scanline pScan = pAcc->GetScanline( nY );
1145 for( long nX = 0; nX < nW; nX++ )
1147 *pScan = cMapB[ *pScan ]; pScan++;
1148 *pScan = cMapG[ *pScan ]; pScan++;
1149 *pScan = cMapR[ *pScan ]; pScan++;
1153 else if( pAcc->GetScanlineFormat() == ScanlineFormat::N24BitTcRgb )
1155 for( long nY = 0; nY < nH; nY++ )
1157 Scanline pScan = pAcc->GetScanline( nY );
1159 for( long nX = 0; nX < nW; nX++ )
1161 *pScan = cMapR[ *pScan ]; pScan++;
1162 *pScan = cMapG[ *pScan ]; pScan++;
1163 *pScan = cMapB[ *pScan ]; pScan++;
1167 else
1169 for( long nY = 0; nY < nH; nY++ )
1171 Scanline pScanline = pAcc->GetScanline(nY);
1172 for( long nX = 0; nX < nW; nX++ )
1174 aCol = pAcc->GetPixelFromData( pScanline, nX );
1175 aCol.SetRed( cMapR[ aCol.GetRed() ] );
1176 aCol.SetGreen( cMapG[ aCol.GetGreen() ] );
1177 aCol.SetBlue( cMapB[ aCol.GetBlue() ] );
1178 pAcc->SetPixelOnData( pScanline, nX, aCol );
1183 pAcc.reset();
1184 bRet = true;
1188 return bRet;
1191 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */