Version 5.4.3.2, tag libreoffice-5.4.3.2
[LibreOffice.git] / vcl / source / gdi / bitmap3.cxx
blobce0f22210a32e7e4c7acf092215c7ddcd1cc0d1e
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>
21 #include <stdlib.h>
23 #include <vcl/bitmapaccess.hxx>
24 #include <vcl/bitmapex.hxx>
25 #include <vcl/bitmap.hxx>
26 #include <config_features.h>
27 #if HAVE_FEATURE_OPENGL
28 #include <vcl/opengl/OpenGLHelper.hxx>
29 #endif
30 #include <memory>
32 #include "impbmp.hxx"
33 #include "impoctree.hxx"
34 #include "impvect.hxx"
36 #include <bitmapscalesuper.hxx>
37 #include "octree.hxx"
38 #include "BitmapScaleConvolution.hxx"
40 #define RGB15( _def_cR, _def_cG, _def_cB ) (((sal_uLong)(_def_cR)<<10UL)|((sal_uLong)(_def_cG)<<5UL)|(sal_uLong)(_def_cB))
41 #define GAMMA( _def_cVal, _def_InvGamma ) ((sal_uInt8)MinMax(FRound(pow( _def_cVal/255.0,_def_InvGamma)*255.0),0,255))
43 #define CALC_ERRORS \
44 nTemp = p1T[nX++] >> 12; \
45 nBErr = MinMax( nTemp, 0, 255 ); \
46 nBErr = nBErr - FloydIndexMap[ nBC = FloydMap[nBErr] ]; \
47 nTemp = p1T[nX++] >> 12; \
48 nGErr = MinMax( nTemp, 0, 255 ); \
49 nGErr = nGErr - FloydIndexMap[ nGC = FloydMap[nGErr] ]; \
50 nTemp = p1T[nX] >> 12; \
51 nRErr = MinMax( nTemp, 0, 255 ); \
52 nRErr = nRErr - FloydIndexMap[ nRC = FloydMap[nRErr] ];
54 #define CALC_TABLES3 \
55 p2T[nX++] += FloydError3[nBErr]; \
56 p2T[nX++] += FloydError3[nGErr]; \
57 p2T[nX++] += FloydError3[nRErr];
59 #define CALC_TABLES5 \
60 p2T[nX++] += FloydError5[nBErr]; \
61 p2T[nX++] += FloydError5[nGErr]; \
62 p2T[nX++] += FloydError5[nRErr];
64 #define CALC_TABLES7 \
65 p1T[++nX] += FloydError7[nBErr]; \
66 p2T[nX++] += FloydError1[nBErr]; \
67 p1T[nX] += FloydError7[nGErr]; \
68 p2T[nX++] += FloydError1[nGErr]; \
69 p1T[nX] += FloydError7[nRErr]; \
70 p2T[nX] += FloydError1[nRErr];
72 const extern sal_uLong nVCLRLut[ 6 ] = { 16, 17, 18, 19, 20, 21 };
73 const extern sal_uLong nVCLGLut[ 6 ] = { 0, 6, 12, 18, 24, 30 };
74 const extern sal_uLong nVCLBLut[ 6 ] = { 0, 36, 72, 108, 144, 180 };
76 const extern sal_uLong nVCLDitherLut[ 256 ] =
78 0, 49152, 12288, 61440, 3072, 52224, 15360, 64512, 768, 49920, 13056,
79 62208, 3840, 52992, 16128, 65280, 32768, 16384, 45056, 28672, 35840, 19456,
80 48128, 31744, 33536, 17152, 45824, 29440, 36608, 20224, 48896, 32512, 8192,
81 57344, 4096, 53248, 11264, 60416, 7168, 56320, 8960, 58112, 4864, 54016,
82 12032, 61184, 7936, 57088, 40960, 24576, 36864, 20480, 44032, 27648, 39936,
83 23552, 41728, 25344, 37632, 21248, 44800, 28416, 40704, 24320, 2048, 51200,
84 14336, 63488, 1024, 50176, 13312, 62464, 2816, 51968, 15104, 64256, 1792,
85 50944, 14080, 63232, 34816, 18432, 47104, 30720, 33792, 17408, 46080, 29696,
86 35584, 19200, 47872, 31488, 34560, 18176, 46848, 30464, 10240, 59392, 6144,
87 55296, 9216, 58368, 5120, 54272, 11008, 60160, 6912, 56064, 9984, 59136,
88 5888, 55040, 43008, 26624, 38912, 22528, 41984, 25600, 37888, 21504, 43776,
89 27392, 39680, 23296, 42752, 26368, 38656, 22272, 512, 49664, 12800, 61952,
90 3584, 52736, 15872, 65024, 256, 49408, 12544, 61696, 3328, 52480, 15616,
91 64768, 33280, 16896, 45568, 29184, 36352, 19968, 48640, 32256, 33024, 16640,
92 45312, 28928, 36096, 19712, 48384, 32000, 8704, 57856, 4608, 53760, 11776,
93 60928, 7680, 56832, 8448, 57600, 4352, 53504, 11520, 60672, 7424, 56576,
94 41472, 25088, 37376, 20992, 44544, 28160, 40448, 24064, 41216, 24832, 37120,
95 20736, 44288, 27904, 40192, 23808, 2560, 51712, 14848, 64000, 1536, 50688,
96 13824, 62976, 2304, 51456, 14592, 63744, 1280, 50432, 13568, 62720, 35328,
97 18944, 47616, 31232, 34304, 17920, 46592, 30208, 35072, 18688, 47360, 30976,
98 34048, 17664, 46336, 29952, 10752, 59904, 6656, 55808, 9728, 58880, 5632,
99 54784, 10496, 59648, 6400, 55552, 9472, 58624, 5376, 54528, 43520, 27136,
100 39424, 23040, 42496, 26112, 38400, 22016, 43264, 26880, 39168, 22784, 42240,
101 25856, 38144, 21760
104 const extern sal_uLong nVCLLut[ 256 ] =
106 0, 1286, 2572, 3858, 5144, 6430, 7716, 9002,
107 10288, 11574, 12860, 14146, 15432, 16718, 18004, 19290,
108 20576, 21862, 23148, 24434, 25720, 27006, 28292, 29578,
109 30864, 32150, 33436, 34722, 36008, 37294, 38580, 39866,
110 41152, 42438, 43724, 45010, 46296, 47582, 48868, 50154,
111 51440, 52726, 54012, 55298, 56584, 57870, 59156, 60442,
112 61728, 63014, 64300, 65586, 66872, 68158, 69444, 70730,
113 72016, 73302, 74588, 75874, 77160, 78446, 79732, 81018,
114 82304, 83590, 84876, 86162, 87448, 88734, 90020, 91306,
115 92592, 93878, 95164, 96450, 97736, 99022,100308,101594,
116 102880,104166,105452,106738,108024,109310,110596,111882,
117 113168,114454,115740,117026,118312,119598,120884,122170,
118 123456,124742,126028,127314,128600,129886,131172,132458,
119 133744,135030,136316,137602,138888,140174,141460,142746,
120 144032,145318,146604,147890,149176,150462,151748,153034,
121 154320,155606,156892,158178,159464,160750,162036,163322,
122 164608,165894,167180,168466,169752,171038,172324,173610,
123 174896,176182,177468,178754,180040,181326,182612,183898,
124 185184,186470,187756,189042,190328,191614,192900,194186,
125 195472,196758,198044,199330,200616,201902,203188,204474,
126 205760,207046,208332,209618,210904,212190,213476,214762,
127 216048,217334,218620,219906,221192,222478,223764,225050,
128 226336,227622,228908,230194,231480,232766,234052,235338,
129 236624,237910,239196,240482,241768,243054,244340,245626,
130 246912,248198,249484,250770,252056,253342,254628,255914,
131 257200,258486,259772,261058,262344,263630,264916,266202,
132 267488,268774,270060,271346,272632,273918,275204,276490,
133 277776,279062,280348,281634,282920,284206,285492,286778,
134 288064,289350,290636,291922,293208,294494,295780,297066,
135 298352,299638,300924,302210,303496,304782,306068,307354,
136 308640,309926,311212,312498,313784,315070,316356,317642,
137 318928,320214,321500,322786,324072,325358,326644,327930
140 const long FloydMap[256] =
142 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
143 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1,
144 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
145 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
146 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2,
147 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
148 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
149 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
150 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
151 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
152 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
153 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
154 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
155 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
156 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
157 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5
160 const long FloydError1[61] =
162 -7680, -7424, -7168, -6912, -6656, -6400, -6144,
163 -5888, -5632, -5376, -5120, -4864, -4608, -4352,
164 -4096, -3840, -3584, -3328, -3072, -2816, -2560,
165 -2304, -2048, -1792, -1536, -1280, -1024, -768,
166 -512, -256, 0, 256, 512, 768, 1024, 1280, 1536,
167 1792, 2048, 2304, 2560, 2816, 3072, 3328, 3584,
168 3840, 4096, 4352, 4608, 4864, 5120, 5376, 5632,
169 5888, 6144, 6400, 6656, 6912, 7168, 7424, 7680
172 const long FloydError3[61] =
174 -23040, -22272, -21504, -20736, -19968, -19200,
175 -18432, -17664, -16896, -16128, -15360, -14592,
176 -13824, -13056, -12288, -11520, -10752, -9984,
177 -9216, -8448, -7680, -6912, -6144, -5376, -4608,
178 -3840, -3072, -2304, -1536, -768, 0, 768, 1536,
179 2304, 3072, 3840, 4608, 5376, 6144, 6912, 7680,
180 8448, 9216, 9984, 10752, 11520, 12288, 13056,
181 13824, 14592, 15360, 16128, 16896, 17664, 18432,
182 19200, 19968, 20736, 21504, 22272, 23040
185 const long FloydError5[61] =
187 -38400, -37120, -35840, -34560, -33280, -32000,
188 -30720, -29440, -28160, -26880, -25600, -24320,
189 -23040, -21760, -20480, -19200, -17920, -16640,
190 -15360, -14080, -12800, -11520, -10240, -8960,
191 -7680, -6400, -5120, -3840, -2560, -1280, 0,
192 1280, 2560, 3840, 5120, 6400, 7680, 8960, 10240,
193 11520, 12800, 14080, 15360, 16640, 17920, 19200,
194 20480, 21760, 23040, 24320, 25600, 26880, 28160,
195 29440, 30720, 32000, 33280, 34560, 35840, 37120,
196 38400
199 const long FloydError7[61] =
201 -53760, -51968, -50176, -48384, -46592, -44800,
202 -43008, -41216, -39424, -37632, -35840, -34048,
203 -32256, -30464, -28672, -26880, -25088, -23296,
204 -21504, -19712, -17920, -16128, -14336, -12544,
205 -10752, -8960, -7168, -5376, -3584, -1792, 0,
206 1792, 3584, 5376, 7168, 8960, 10752, 12544, 14336,
207 16128, 17920, 19712, 21504, 23296, 25088, 26880,
208 28672, 30464, 32256, 34048, 35840, 37632, 39424,
209 41216, 43008, 44800, 46592, 48384, 50176, 51968,
210 53760
213 const long FloydIndexMap[6] =
215 -30, 21, 72, 123, 174, 225
218 bool Bitmap::Convert( BmpConversion eConversion )
220 // try to convert in backend
221 if (mxImpBmp)
223 std::shared_ptr<ImpBitmap> xImpBmp(new ImpBitmap);
224 if (xImpBmp->ImplCreate(*mxImpBmp) && xImpBmp->ImplConvert(eConversion))
226 ImplSetImpBitmap(xImpBmp);
227 SAL_INFO( "vcl.opengl", "Ref count: " << mxImpBmp.use_count() );
228 return true;
232 const sal_uInt16 nBitCount = GetBitCount ();
233 bool bRet = false;
235 switch( eConversion )
237 case BmpConversion::N1BitThreshold:
238 bRet = ImplMakeMono( 128 );
239 break;
241 case BmpConversion::N4BitGreys:
242 bRet = ImplMakeGreyscales( 16 );
243 break;
245 case BmpConversion::N4BitColors:
247 if( nBitCount < 4 )
248 bRet = ImplConvertUp( 4 );
249 else if( nBitCount > 4 )
250 bRet = ImplConvertDown( 4 );
251 else
252 bRet = true;
254 break;
256 case BmpConversion::N8BitGreys:
257 bRet = ImplMakeGreyscales( 256 );
258 break;
260 case BmpConversion::N8BitColors:
262 if( nBitCount < 8 )
263 bRet = ImplConvertUp( 8 );
264 else if( nBitCount > 8 )
265 bRet = ImplConvertDown( 8 );
266 else
267 bRet = true;
269 break;
271 case BmpConversion::N8BitTrans:
273 Color aTrans( BMP_COL_TRANS );
275 if( nBitCount < 8 )
276 bRet = ImplConvertUp( 8, &aTrans );
277 else
278 bRet = ImplConvertDown( 8, &aTrans );
280 break;
282 case BmpConversion::N24Bit:
284 if( nBitCount < 24 )
285 bRet = ImplConvertUp( 24 );
286 else
287 bRet = true;
289 break;
291 case BmpConversion::Ghosted:
292 bRet = ImplConvertGhosted();
293 break;
295 default:
296 OSL_FAIL( "Bitmap::Convert(): Unsupported conversion" );
297 break;
300 return bRet;
303 bool Bitmap::ImplMakeMono( sal_uInt8 cThreshold )
305 ScopedReadAccess pReadAcc(*this);
306 bool bRet = false;
308 if( pReadAcc )
310 Bitmap aNewBmp( GetSizePixel(), 1 );
311 ScopedWriteAccess pWriteAcc(aNewBmp);
313 if( pWriteAcc )
315 const BitmapColor aBlack( pWriteAcc->GetBestMatchingColor( Color( COL_BLACK ) ) );
316 const BitmapColor aWhite( pWriteAcc->GetBestMatchingColor( Color( COL_WHITE ) ) );
317 const long nWidth = pWriteAcc->Width();
318 const long nHeight = pWriteAcc->Height();
320 if( pReadAcc->HasPalette() )
322 for( long nY = 0; nY < nHeight; nY++ )
324 for( long nX = 0; nX < nWidth; nX++ )
326 const sal_uInt8 cIndex = pReadAcc->GetPixelIndex( nY, nX );
327 if( pReadAcc->GetPaletteColor( cIndex ).GetLuminance() >=
328 cThreshold )
330 pWriteAcc->SetPixel( nY, nX, aWhite );
332 else
333 pWriteAcc->SetPixel( nY, nX, aBlack );
337 else
339 for( long nY = 0; nY < nHeight; nY++ )
341 for( long nX = 0; nX < nWidth; nX++ )
343 if( pReadAcc->GetPixel( nY, nX ).GetLuminance() >=
344 cThreshold )
346 pWriteAcc->SetPixel( nY, nX, aWhite );
348 else
349 pWriteAcc->SetPixel( nY, nX, aBlack );
354 pWriteAcc.reset();
355 bRet = true;
358 pReadAcc.reset();
360 if( bRet )
362 const MapMode aMap( maPrefMapMode );
363 const Size aSize( maPrefSize );
365 *this = aNewBmp;
367 maPrefMapMode = aMap;
368 maPrefSize = aSize;
372 return bRet;
375 bool Bitmap::ImplMakeGreyscales( sal_uInt16 nGreys )
377 SAL_WARN_IF( nGreys != 16 && nGreys != 256, "vcl", "Only 16 or 256 greyscales are supported!" );
379 ScopedReadAccess pReadAcc(*this);
380 bool bRet = false;
382 if( pReadAcc )
384 const BitmapPalette& rPal = GetGreyPalette( nGreys );
385 sal_uLong nShift = ( ( nGreys == 16 ) ? 4UL : 0UL );
386 bool bPalDiffers = !pReadAcc->HasPalette() || ( rPal.GetEntryCount() != pReadAcc->GetPaletteEntryCount() );
388 if( !bPalDiffers )
389 bPalDiffers = ( (BitmapPalette&) rPal != pReadAcc->GetPalette() );
391 if( bPalDiffers )
393 Bitmap aNewBmp( GetSizePixel(), ( nGreys == 16 ) ? 4 : 8, &rPal );
394 ScopedWriteAccess pWriteAcc(aNewBmp);
396 if( pWriteAcc )
398 const long nWidth = pWriteAcc->Width();
399 const long nHeight = pWriteAcc->Height();
401 if( pReadAcc->HasPalette() )
403 for( long nY = 0; nY < nHeight; nY++ )
405 for( long nX = 0; nX < nWidth; nX++ )
407 const sal_uInt8 cIndex = pReadAcc->GetPixelIndex( nY, nX );
408 pWriteAcc->SetPixelIndex( nY, nX,
409 (pReadAcc->GetPaletteColor( cIndex ).GetLuminance() >> nShift) );
413 else if( pReadAcc->GetScanlineFormat() == ScanlineFormat::N24BitTcBgr &&
414 pWriteAcc->GetScanlineFormat() == ScanlineFormat::N8BitPal )
416 nShift += 8;
418 for( long nY = 0; nY < nHeight; nY++ )
420 Scanline pReadScan = pReadAcc->GetScanline( nY );
421 Scanline pWriteScan = pWriteAcc->GetScanline( nY );
423 for( long nX = 0; nX < nWidth; nX++ )
425 const sal_uLong nB = *pReadScan++;
426 const sal_uLong nG = *pReadScan++;
427 const sal_uLong nR = *pReadScan++;
429 *pWriteScan++ = (sal_uInt8) ( ( nB * 28UL + nG * 151UL + nR * 77UL ) >> nShift );
433 else if( pReadAcc->GetScanlineFormat() == ScanlineFormat::N24BitTcRgb &&
434 pWriteAcc->GetScanlineFormat() == ScanlineFormat::N8BitPal )
436 nShift += 8;
438 for( long nY = 0; nY < nHeight; nY++ )
440 Scanline pReadScan = pReadAcc->GetScanline( nY );
441 Scanline pWriteScan = pWriteAcc->GetScanline( nY );
443 for( long nX = 0; nX < nWidth; nX++ )
445 const sal_uLong nR = *pReadScan++;
446 const sal_uLong nG = *pReadScan++;
447 const sal_uLong nB = *pReadScan++;
449 *pWriteScan++ = (sal_uInt8) ( ( nB * 28UL + nG * 151UL + nR * 77UL ) >> nShift );
453 else
455 for( long nY = 0; nY < nHeight; nY++ )
456 for( long nX = 0; nX < nWidth; nX++ )
457 pWriteAcc->SetPixelIndex( nY, nX, (pReadAcc->GetPixel( nY, nX ) ).GetLuminance() >> nShift );
460 pWriteAcc.reset();
461 bRet = true;
464 pReadAcc.reset();
466 if( bRet )
468 const MapMode aMap( maPrefMapMode );
469 const Size aSize( maPrefSize );
471 *this = aNewBmp;
473 maPrefMapMode = aMap;
474 maPrefSize = aSize;
477 else
479 pReadAcc.reset();
480 bRet = true;
484 return bRet;
487 bool Bitmap::ImplConvertUp(sal_uInt16 nBitCount, Color* pExtColor)
489 SAL_WARN_IF( nBitCount <= GetBitCount(), "vcl", "New BitCount must be greater!" );
491 Bitmap::ScopedReadAccess pReadAcc(*this);
492 bool bRet = false;
494 if (pReadAcc)
496 BitmapPalette aPalette;
497 Bitmap aNewBmp(GetSizePixel(), nBitCount, pReadAcc->HasPalette() ? &pReadAcc->GetPalette() : &aPalette);
498 Bitmap::ScopedWriteAccess pWriteAcc(aNewBmp);
500 if (pWriteAcc)
502 const long nWidth = pWriteAcc->Width();
503 const long nHeight = pWriteAcc->Height();
505 if (pWriteAcc->HasPalette())
507 const BitmapPalette& rOldPalette = pReadAcc->GetPalette();
508 const sal_uInt16 nOldCount = rOldPalette.GetEntryCount();
509 assert(nOldCount <= (1 << GetBitCount()));
511 aPalette.SetEntryCount(1 << nBitCount);
513 for (sal_uInt16 i = 0; i < nOldCount; i++)
514 aPalette[i] = rOldPalette[i];
516 if (pExtColor)
517 aPalette[aPalette.GetEntryCount() - 1] = *pExtColor;
519 pWriteAcc->SetPalette(aPalette);
521 for (long nY = 0; nY < nHeight; nY++)
523 for (long nX = 0; nX < nWidth; nX++)
525 pWriteAcc->SetPixel(nY, nX, pReadAcc->GetPixel(nY, nX));
529 else
531 if (pReadAcc->HasPalette())
533 for (long nY = 0; nY < nHeight; nY++)
535 for (long nX = 0; nX < nWidth; nX++)
537 pWriteAcc->SetPixel(nY, nX, pReadAcc->GetPaletteColor(pReadAcc->GetPixelIndex(nY, nX)));
541 else
543 for (long nY = 0; nY < nHeight; nY++)
545 for (long nX = 0; nX < nWidth; nX++)
547 pWriteAcc->SetPixel(nY, nX, pReadAcc->GetPixel(nY, nX));
552 bRet = true;
555 if (bRet)
557 const MapMode aMap(maPrefMapMode);
558 const Size aSize(maPrefSize);
560 *this = aNewBmp;
562 maPrefMapMode = aMap;
563 maPrefSize = aSize;
567 return bRet;
570 bool Bitmap::ImplConvertDown(sal_uInt16 nBitCount, Color* pExtColor)
572 SAL_WARN_IF(nBitCount > GetBitCount(), "vcl", "New BitCount must be lower ( or equal when pExtColor is set )!");
574 Bitmap::ScopedReadAccess pReadAcc(*this);
575 bool bRet = false;
577 if (pReadAcc)
579 BitmapPalette aPalette;
580 Bitmap aNewBmp(GetSizePixel(), nBitCount, &aPalette);
581 Bitmap::ScopedWriteAccess pWriteAcc(aNewBmp);
583 if (pWriteAcc)
585 const sal_uInt16 nCount = 1 << nBitCount;
586 const long nWidth = pWriteAcc->Width();
587 const long nWidth1 = nWidth - 1;
588 const long nHeight = pWriteAcc->Height();
589 Octree aOctree(*pReadAcc, pExtColor ? (nCount - 1) : nCount);
590 aPalette = aOctree.GetPalette();
591 InverseColorMap aColorMap(aPalette);
592 BitmapColor aColor;
593 ImpErrorQuad aErrQuad;
594 std::vector<ImpErrorQuad> aErrQuad1(nWidth);
595 std::vector<ImpErrorQuad> aErrQuad2(nWidth);
596 ImpErrorQuad* pQLine1 = aErrQuad1.data();
597 ImpErrorQuad* pQLine2 = nullptr;
598 long nYTmp = 0;
599 sal_uInt8 cIndex;
600 bool bQ1 = true;
602 if (pExtColor)
604 aPalette.SetEntryCount(aPalette.GetEntryCount() + 1);
605 aPalette[aPalette.GetEntryCount() - 1] = *pExtColor;
608 // set Black/White always, if we have enough space
609 if (aPalette.GetEntryCount() < (nCount - 1))
611 aPalette.SetEntryCount(aPalette.GetEntryCount() + 2);
612 aPalette[aPalette.GetEntryCount() - 2] = Color(COL_BLACK);
613 aPalette[aPalette.GetEntryCount() - 1] = Color(COL_WHITE);
616 pWriteAcc->SetPalette(aPalette);
618 for (long nY = 0; nY < std::min(nHeight, 2L); nY++, nYTmp++)
620 pQLine2 = !nY ? aErrQuad1.data() : aErrQuad2.data();
621 for (long nX = 0; nX < nWidth; nX++)
623 if (pReadAcc->HasPalette())
624 pQLine2[nX] = pReadAcc->GetPaletteColor(pReadAcc->GetPixelIndex(nYTmp, nX));
625 else
626 pQLine2[nX] = pReadAcc->GetPixel(nYTmp, nX);
630 for (long nY = 0; nY < nHeight; nY++, nYTmp++)
632 // first pixel in the line
633 cIndex = (sal_uInt8) aColorMap.GetBestPaletteIndex(pQLine1[0].ImplGetColor());
634 pWriteAcc->SetPixelIndex(nY, 0, cIndex);
636 long nX;
637 for (nX = 1; nX < nWidth1; nX++)
639 aColor = pQLine1[nX].ImplGetColor();
640 cIndex = static_cast<sal_uInt8>(aColorMap.GetBestPaletteIndex(aColor));
641 aErrQuad = (ImpErrorQuad(aColor) -= pWriteAcc->GetPaletteColor(cIndex));
642 pQLine1[++nX].ImplAddColorError7(aErrQuad);
643 pQLine2[nX--].ImplAddColorError1(aErrQuad);
644 pQLine2[nX--].ImplAddColorError5(aErrQuad);
645 pQLine2[nX++].ImplAddColorError3(aErrQuad);
646 pWriteAcc->SetPixelIndex(nY, nX, cIndex);
649 // Last RowPixel
650 if (nX < nWidth)
652 cIndex = static_cast<sal_uInt8>(aColorMap.GetBestPaletteIndex(pQLine1[nWidth1].ImplGetColor()));
653 pWriteAcc->SetPixelIndex(nY, nX, cIndex);
656 // Refill/copy row buffer
657 pQLine1 = pQLine2;
658 bQ1 = !bQ1;
659 pQLine2 = bQ1 ? aErrQuad2.data() : aErrQuad1.data();
661 if (nYTmp < nHeight)
663 for (nX = 0; nX < nWidth; nX++)
665 if (pReadAcc->HasPalette())
666 pQLine2[nX] = pReadAcc->GetPaletteColor(pReadAcc->GetPixelIndex(nYTmp, nX));
667 else
668 pQLine2[nX] = pReadAcc->GetPixel(nYTmp, nX);
673 bRet = true;
676 if(bRet)
678 const MapMode aMap(maPrefMapMode);
679 const Size aSize(maPrefSize);
681 *this = aNewBmp;
683 maPrefMapMode = aMap;
684 maPrefSize = aSize;
688 return bRet;
691 bool Bitmap::ImplConvertGhosted()
693 Bitmap aNewBmp;
694 ScopedReadAccess pR(*this);
695 bool bRet = false;
697 if( pR )
699 if( pR->HasPalette() )
701 BitmapPalette aNewPal( pR->GetPaletteEntryCount() );
703 for( long i = 0, nCount = aNewPal.GetEntryCount(); i < nCount; i++ )
705 const BitmapColor& rOld = pR->GetPaletteColor( (sal_uInt16) i );
706 aNewPal[ (sal_uInt16) i ] = BitmapColor( ( rOld.GetRed() >> 1 ) | 0x80,
707 ( rOld.GetGreen() >> 1 ) | 0x80,
708 ( rOld.GetBlue() >> 1 ) | 0x80 );
711 aNewBmp = Bitmap( GetSizePixel(), GetBitCount(), &aNewPal );
712 ScopedWriteAccess pW(aNewBmp);
714 if( pW )
716 pW->CopyBuffer( *pR );
717 bRet = true;
720 else
722 aNewBmp = Bitmap( GetSizePixel(), 24 );
724 ScopedWriteAccess pW(aNewBmp);
726 if( pW )
728 const long nWidth = pR->Width(), nHeight = pR->Height();
730 for( long nY = 0; nY < nHeight; nY++ )
732 for( long nX = 0; nX < nWidth; nX++ )
734 const BitmapColor aOld( pR->GetPixel( nY, nX ) );
735 pW->SetPixel( nY, nX, BitmapColor( ( aOld.GetRed() >> 1 ) | 0x80,
736 ( aOld.GetGreen() >> 1 ) | 0x80,
737 ( aOld.GetBlue() >> 1 ) | 0x80 ) );
742 bRet = true;
746 pR.reset();
749 if( bRet )
751 const MapMode aMap( maPrefMapMode );
752 const Size aSize( maPrefSize );
754 *this = aNewBmp;
756 maPrefMapMode = aMap;
757 maPrefSize = aSize;
760 return bRet;
763 bool Bitmap::Scale( const double& rScaleX, const double& rScaleY, BmpScaleFlag nScaleFlag )
765 if(basegfx::fTools::equalZero(rScaleX) || basegfx::fTools::equalZero(rScaleY))
767 // no scale
768 return true;
771 if(basegfx::fTools::equal(rScaleX, 1.0) && basegfx::fTools::equal(rScaleY, 1.0))
773 // no scale
774 return true;
777 const sal_uInt16 nStartCount(GetBitCount());
779 if (mxImpBmp && mxImpBmp->ImplScalingSupported())
781 // implementation specific scaling
782 std::shared_ptr<ImpBitmap> xImpBmp(new ImpBitmap);
783 if (xImpBmp->ImplCreate(*mxImpBmp) && xImpBmp->ImplScale(rScaleX, rScaleY, nScaleFlag))
785 ImplSetImpBitmap(xImpBmp);
786 SAL_INFO( "vcl.opengl", "Ref count: " << mxImpBmp.use_count() );
787 maPrefMapMode = MapMode( MapUnit::MapPixel );
788 maPrefSize = xImpBmp->ImplGetSize();
789 return true;
793 //fdo#33455
795 //If we start with a 1 bit image, then after scaling it in any mode except
796 //BmpScaleFlag::Fast we have a 24bit image which is perfectly correct, but we
797 //are going to down-shift it to mono again and Bitmap::ImplMakeMono just
798 //has "Bitmap aNewBmp( GetSizePixel(), 1 );" to create a 1 bit bitmap which
799 //will default to black/white and the colors mapped to which ever is closer
800 //to black/white
802 //So the easiest thing to do to retain the colors of 1 bit bitmaps is to
803 //just use the fast scale rather than attempting to count unique colors in
804 //the other converters and pass all the info down through
805 //Bitmap::ImplMakeMono
806 if (nStartCount == 1)
807 nScaleFlag = BmpScaleFlag::Fast;
809 bool bRetval(false);
811 switch(nScaleFlag)
813 case BmpScaleFlag::Fast :
815 bRetval = ImplScaleFast( rScaleX, rScaleY );
816 break;
818 case BmpScaleFlag::Interpolate :
820 bRetval = ImplScaleInterpolate( rScaleX, rScaleY );
821 break;
823 case BmpScaleFlag::Default:
825 if (GetSizePixel().Width() < 2 || GetSizePixel().Height() < 2)
827 // fallback to ImplScaleFast
828 bRetval = ImplScaleFast( rScaleX, rScaleY );
830 else
832 BitmapScaleSuper aScaleSuper(rScaleX, rScaleY);
833 bRetval = aScaleSuper.filter(*this);
835 break;
837 case BmpScaleFlag::Lanczos :
838 case BmpScaleFlag::BestQuality:
840 vcl::BitmapScaleConvolution aScaleConvolution(rScaleX, rScaleY, vcl::ConvolutionKernelType::Lanczos3);
841 bRetval = aScaleConvolution.filter(*this);
842 break;
844 case BmpScaleFlag::BiCubic :
846 vcl::BitmapScaleConvolution aScaleConvolution(rScaleX, rScaleY, vcl::ConvolutionKernelType::BiCubic);
847 bRetval = aScaleConvolution.filter(*this);
848 break;
850 case BmpScaleFlag::BiLinear :
852 vcl::BitmapScaleConvolution aScaleConvolution(rScaleX, rScaleY, vcl::ConvolutionKernelType::BiLinear);
853 bRetval = aScaleConvolution.filter(*this);
854 break;
858 OSL_ENSURE(!bRetval || nStartCount == GetBitCount(), "Bitmap::Scale has changed the ColorDepth, this should *not* happen (!)");
859 return bRetval;
862 bool Bitmap::Scale( const Size& rNewSize, BmpScaleFlag nScaleFlag )
864 const Size aSize( GetSizePixel() );
865 bool bRet;
867 if( aSize.Width() && aSize.Height() )
869 bRet = Scale( (double) rNewSize.Width() / aSize.Width(),
870 (double) rNewSize.Height() / aSize.Height(),
871 nScaleFlag );
873 else
874 bRet = true;
876 return bRet;
879 bool Bitmap::HasFastScale()
881 #if HAVE_FEATURE_OPENGL
882 return OpenGLHelper::isVCLOpenGLEnabled();
883 #else
884 return false;
885 #endif
888 void Bitmap::AdaptBitCount(Bitmap& rNew) const
890 ImplAdaptBitCount(rNew);
893 void Bitmap::ImplAdaptBitCount(Bitmap& rNew) const
895 // aNew is the result of some operation; adapt it's BitCount to the original (this)
896 if(GetBitCount() != rNew.GetBitCount())
898 switch(GetBitCount())
900 case 1:
902 rNew.Convert(BmpConversion::N1BitThreshold);
903 break;
905 case 4:
907 if(HasGreyPalette())
909 rNew.Convert(BmpConversion::N4BitGreys);
911 else
913 rNew.Convert(BmpConversion::N4BitColors);
915 break;
917 case 8:
919 if(HasGreyPalette())
921 rNew.Convert(BmpConversion::N8BitGreys);
923 else
925 rNew.Convert(BmpConversion::N8BitColors);
927 break;
929 case 24:
931 rNew.Convert(BmpConversion::N24Bit);
932 break;
934 default:
936 OSL_ENSURE(false, "BitDepth adaption failed (!)");
937 break;
943 bool Bitmap::ImplScaleFast( const double& rScaleX, const double& rScaleY )
945 const Size aSizePix( GetSizePixel() );
946 const long nNewWidth = FRound( aSizePix.Width() * rScaleX );
947 const long nNewHeight = FRound( aSizePix.Height() * rScaleY );
948 bool bRet = false;
950 if( nNewWidth && nNewHeight )
952 ScopedReadAccess pReadAcc(*this);
954 if(pReadAcc)
956 Bitmap aNewBmp( Size( nNewWidth, nNewHeight ), GetBitCount(), &pReadAcc->GetPalette() );
957 ScopedWriteAccess pWriteAcc(aNewBmp);
959 if( pWriteAcc )
961 const long nScanlineSize = pWriteAcc->GetScanlineSize();
962 const long nNewWidth1 = nNewWidth - 1;
963 const long nNewHeight1 = nNewHeight - 1;
965 if( nNewWidth1 && nNewHeight1 )
967 const double nWidth = pReadAcc->Width();
968 const double nHeight = pReadAcc->Height();
969 std::unique_ptr<long[]> pLutX(new long[ nNewWidth ]);
970 std::unique_ptr<long[]> pLutY(new long[ nNewHeight ]);
972 for( long nX = 0; nX < nNewWidth; nX++ )
973 pLutX[ nX ] = long(nX * nWidth / nNewWidth);
975 for( long nY = 0; nY < nNewHeight; nY++ )
976 pLutY[ nY ] = long(nY * nHeight / nNewHeight);
978 long nActY = 0;
979 while( nActY < nNewHeight )
981 long nMapY = pLutY[ nActY ];
983 for( long nX = 0; nX < nNewWidth; nX++ )
984 pWriteAcc->SetPixel( nActY, nX, pReadAcc->GetPixel( nMapY , pLutX[ nX ] ) );
986 while( ( nActY < nNewHeight1 ) && ( pLutY[ nActY + 1 ] == nMapY ) )
988 memcpy( pWriteAcc->GetScanline( nActY + 1 ),
989 pWriteAcc->GetScanline( nActY ), nScanlineSize );
990 nActY++;
992 nActY++;
995 bRet = true;
998 pWriteAcc.reset();
1000 pReadAcc.reset();
1002 if( bRet )
1003 ImplAssignWithSize( aNewBmp );
1007 return bRet;
1010 bool Bitmap::ImplScaleInterpolate( const double& rScaleX, const double& rScaleY )
1012 const Size aSizePix( GetSizePixel() );
1013 const long nNewWidth = FRound( aSizePix.Width() * rScaleX );
1014 const long nNewHeight = FRound( aSizePix.Height() * rScaleY );
1015 bool bRet = false;
1017 if( ( nNewWidth > 1 ) && ( nNewHeight > 1 ) )
1019 ScopedReadAccess pReadAcc(*this);
1020 if( pReadAcc )
1022 long nWidth = pReadAcc->Width();
1023 long nHeight = pReadAcc->Height();
1024 Bitmap aNewBmp( Size( nNewWidth, nHeight ), 24 );
1025 ScopedWriteAccess pWriteAcc(aNewBmp);
1027 if( pWriteAcc )
1029 const long nNewWidth1 = nNewWidth - 1;
1030 const long nWidth1 = pReadAcc->Width() - 1;
1031 const double fRevScaleX = (double) nWidth1 / nNewWidth1;
1033 std::unique_ptr<long[]> pLutInt(new long[ nNewWidth ]);
1034 std::unique_ptr<long[]> pLutFrac(new long[ nNewWidth ]);
1036 for( long nX = 0, nTemp = nWidth - 2; nX < nNewWidth; nX++ )
1038 double fTemp = nX * fRevScaleX;
1039 pLutInt[ nX ] = MinMax( (long) fTemp, 0, nTemp );
1040 fTemp -= pLutInt[ nX ];
1041 pLutFrac[ nX ] = (long) ( fTemp * 1024. );
1044 for( long nY = 0; nY < nHeight; nY++ )
1046 if( 1 == nWidth )
1048 BitmapColor aCol0;
1049 if( pReadAcc->HasPalette() )
1051 aCol0 = pReadAcc->GetPaletteColor( pReadAcc->GetPixelIndex( nY, 0 ) );
1053 else
1055 aCol0 = pReadAcc->GetPixel( nY, 0 );
1058 for( long nX = 0; nX < nNewWidth; nX++ )
1060 pWriteAcc->SetPixel( nY, nX, aCol0 );
1063 else
1065 for( long nX = 0; nX < nNewWidth; nX++ )
1067 long nTemp = pLutInt[ nX ];
1069 BitmapColor aCol0, aCol1;
1070 if( pReadAcc->HasPalette() )
1072 aCol0 = pReadAcc->GetPaletteColor( pReadAcc->GetPixelIndex( nY, nTemp++ ) );
1073 aCol1 = pReadAcc->GetPaletteColor( pReadAcc->GetPixelIndex( nY, nTemp ) );
1075 else
1077 aCol0 = pReadAcc->GetPixel( nY, nTemp++ );
1078 aCol1 = pReadAcc->GetPixel( nY, nTemp );
1081 nTemp = pLutFrac[ nX ];
1083 long lXR0 = aCol0.GetRed();
1084 long lXG0 = aCol0.GetGreen();
1085 long lXB0 = aCol0.GetBlue();
1086 long lXR1 = aCol1.GetRed() - lXR0;
1087 long lXG1 = aCol1.GetGreen() - lXG0;
1088 long lXB1 = aCol1.GetBlue() - lXB0;
1090 aCol0.SetRed( (sal_uInt8) ( ( lXR1 * nTemp + ( lXR0 << 10 ) ) >> 10 ) );
1091 aCol0.SetGreen( (sal_uInt8) ( ( lXG1 * nTemp + ( lXG0 << 10 ) ) >> 10 ) );
1092 aCol0.SetBlue( (sal_uInt8) ( ( lXB1 * nTemp + ( lXB0 << 10 ) ) >> 10 ) );
1094 pWriteAcc->SetPixel( nY, nX, aCol0 );
1099 bRet = true;
1102 pReadAcc.reset();
1103 pWriteAcc.reset();
1105 if( bRet )
1107 bRet = false;
1108 const Bitmap aOriginal(*this);
1109 *this = aNewBmp;
1110 aNewBmp = Bitmap( Size( nNewWidth, nNewHeight ), 24 );
1111 pReadAcc = ScopedReadAccess(*this);
1112 pWriteAcc = ScopedWriteAccess(aNewBmp);
1114 if( pReadAcc && pWriteAcc )
1116 const long nNewHeight1 = nNewHeight - 1;
1117 const long nHeight1 = pReadAcc->Height() - 1;
1118 const double fRevScaleY = (double) nHeight1 / nNewHeight1;
1120 std::unique_ptr<long[]> pLutInt(new long[ nNewHeight ]);
1121 std::unique_ptr<long[]> pLutFrac(new long[ nNewHeight ]);
1123 for( long nY = 0, nTemp = nHeight - 2; nY < nNewHeight; nY++ )
1125 double fTemp = nY * fRevScaleY;
1126 pLutInt[ nY ] = MinMax( (long) fTemp, 0, nTemp );
1127 fTemp -= pLutInt[ nY ];
1128 pLutFrac[ nY ] = (long) ( fTemp * 1024. );
1131 // after 1st step, bitmap *is* 24bit format (see above)
1132 OSL_ENSURE(!pReadAcc->HasPalette(), "OOps, somehow ImplScaleInterpolate in-between format has palette, should not happen (!)");
1134 for( long nX = 0; nX < nNewWidth; nX++ )
1136 if( 1 == nHeight )
1138 BitmapColor aCol0 = pReadAcc->GetPixel( 0, nX );
1140 for( long nY = 0; nY < nNewHeight; nY++ )
1142 pWriteAcc->SetPixel( nY, nX, aCol0 );
1145 else
1147 for( long nY = 0; nY < nNewHeight; nY++ )
1149 long nTemp = pLutInt[ nY ];
1151 BitmapColor aCol0 = pReadAcc->GetPixel( nTemp++, nX );
1152 BitmapColor aCol1 = pReadAcc->GetPixel( nTemp, nX );
1154 nTemp = pLutFrac[ nY ];
1156 long lXR0 = aCol0.GetRed();
1157 long lXG0 = aCol0.GetGreen();
1158 long lXB0 = aCol0.GetBlue();
1159 long lXR1 = aCol1.GetRed() - lXR0;
1160 long lXG1 = aCol1.GetGreen() - lXG0;
1161 long lXB1 = aCol1.GetBlue() - lXB0;
1163 aCol0.SetRed( (sal_uInt8) ( ( lXR1 * nTemp + ( lXR0 << 10 ) ) >> 10 ) );
1164 aCol0.SetGreen( (sal_uInt8) ( ( lXG1 * nTemp + ( lXG0 << 10 ) ) >> 10 ) );
1165 aCol0.SetBlue( (sal_uInt8) ( ( lXB1 * nTemp + ( lXB0 << 10 ) ) >> 10 ) );
1167 pWriteAcc->SetPixel( nY, nX, aCol0 );
1172 bRet = true;
1175 pReadAcc.reset();
1176 pWriteAcc.reset();
1178 if( bRet )
1180 aOriginal.ImplAdaptBitCount(aNewBmp);
1181 *this = aNewBmp;
1187 if( !bRet )
1189 bRet = ImplScaleFast( rScaleX, rScaleY );
1192 return bRet;
1195 bool Bitmap::Dither( BmpDitherFlags nDitherFlags )
1197 bool bRet = false;
1199 const Size aSizePix( GetSizePixel() );
1201 if( aSizePix.Width() == 1 || aSizePix.Height() == 1 )
1202 bRet = true;
1203 else if( nDitherFlags & BmpDitherFlags::Matrix )
1204 bRet = ImplDitherMatrix();
1205 else if( nDitherFlags & BmpDitherFlags::Floyd )
1206 bRet = ImplDitherFloyd();
1207 else if( ( nDitherFlags & BmpDitherFlags::Floyd16 ) && ( GetBitCount() == 24 ) )
1208 bRet = ImplDitherFloyd16();
1210 return bRet;
1213 bool Bitmap::ImplDitherMatrix()
1215 ScopedReadAccess pReadAcc(*this);
1216 Bitmap aNewBmp( GetSizePixel(), 8 );
1217 ScopedWriteAccess pWriteAcc(aNewBmp);
1218 bool bRet = false;
1220 if( pReadAcc && pWriteAcc )
1222 const sal_uLong nWidth = pReadAcc->Width();
1223 const sal_uLong nHeight = pReadAcc->Height();
1224 BitmapColor aIndex( (sal_uInt8) 0 );
1226 if( pReadAcc->HasPalette() )
1228 for( sal_uLong nY = 0UL; nY < nHeight; nY++ )
1230 for( sal_uLong nX = 0UL, nModY = ( nY & 0x0FUL ) << 4UL; nX < nWidth; nX++ )
1232 const BitmapColor aCol( pReadAcc->GetPaletteColor( pReadAcc->GetPixelIndex( nY, nX ) ) );
1233 const sal_uLong nD = nVCLDitherLut[ nModY + ( nX & 0x0FUL ) ];
1234 const sal_uLong nR = ( nVCLLut[ aCol.GetRed() ] + nD ) >> 16UL;
1235 const sal_uLong nG = ( nVCLLut[ aCol.GetGreen() ] + nD ) >> 16UL;
1236 const sal_uLong nB = ( nVCLLut[ aCol.GetBlue() ] + nD ) >> 16UL;
1238 aIndex.SetIndex( (sal_uInt8) ( nVCLRLut[ nR ] + nVCLGLut[ nG ] + nVCLBLut[ nB ] ) );
1239 pWriteAcc->SetPixel( nY, nX, aIndex );
1243 else
1245 for( sal_uLong nY = 0UL; nY < nHeight; nY++ )
1247 for( sal_uLong nX = 0UL, nModY = ( nY & 0x0FUL ) << 4UL; nX < nWidth; nX++ )
1249 const BitmapColor aCol( pReadAcc->GetPixel( nY, nX ) );
1250 const sal_uLong nD = nVCLDitherLut[ nModY + ( nX & 0x0FUL ) ];
1251 const sal_uLong nR = ( nVCLLut[ aCol.GetRed() ] + nD ) >> 16UL;
1252 const sal_uLong nG = ( nVCLLut[ aCol.GetGreen() ] + nD ) >> 16UL;
1253 const sal_uLong nB = ( nVCLLut[ aCol.GetBlue() ] + nD ) >> 16UL;
1255 aIndex.SetIndex( (sal_uInt8) ( nVCLRLut[ nR ] + nVCLGLut[ nG ] + nVCLBLut[ nB ] ) );
1256 pWriteAcc->SetPixel( nY, nX, aIndex );
1261 bRet = true;
1264 pReadAcc.reset();
1265 pWriteAcc.reset();
1267 if( bRet )
1269 const MapMode aMap( maPrefMapMode );
1270 const Size aSize( maPrefSize );
1272 *this = aNewBmp;
1274 maPrefMapMode = aMap;
1275 maPrefSize = aSize;
1278 return bRet;
1281 bool Bitmap::ImplDitherFloyd()
1283 const Size aSize( GetSizePixel() );
1284 bool bRet = false;
1286 if( ( aSize.Width() > 3 ) && ( aSize.Height() > 2 ) )
1288 ScopedReadAccess pReadAcc(*this);
1289 Bitmap aNewBmp( GetSizePixel(), 8 );
1290 ScopedWriteAccess pWriteAcc(aNewBmp);
1292 if( pReadAcc && pWriteAcc )
1294 BitmapColor aColor;
1295 long nWidth = pReadAcc->Width();
1296 long nWidth1 = nWidth - 1;
1297 long nHeight = pReadAcc->Height();
1298 long nX;
1299 long nW = nWidth * 3;
1300 long nW2 = nW - 3;
1301 long nRErr, nGErr, nBErr;
1302 long nRC, nGC, nBC;
1303 std::unique_ptr<long[]> p1(new long[ nW ]);
1304 std::unique_ptr<long[]> p2(new long[ nW ]);
1305 long* p1T = p1.get();
1306 long* p2T = p2.get();
1307 long* pTmp;
1308 bool bPal = pReadAcc->HasPalette();
1310 pTmp = p2T;
1312 if( bPal )
1314 for( long nZ = 0; nZ < nWidth; nZ++ )
1316 aColor = pReadAcc->GetPaletteColor( pReadAcc->GetPixelIndex( 0, nZ ) );
1318 *pTmp++ = (long) aColor.GetBlue() << 12;
1319 *pTmp++ = (long) aColor.GetGreen() << 12;
1320 *pTmp++ = (long) aColor.GetRed() << 12;
1323 else
1325 for( long nZ = 0; nZ < nWidth; nZ++ )
1327 aColor = pReadAcc->GetPixel( 0, nZ );
1329 *pTmp++ = (long) aColor.GetBlue() << 12;
1330 *pTmp++ = (long) aColor.GetGreen() << 12;
1331 *pTmp++ = (long) aColor.GetRed() << 12;
1335 for( long nY = 1, nYAcc = 0; nY <= nHeight; nY++, nYAcc++ )
1337 pTmp = p1T;
1338 p1T = p2T;
1339 p2T = pTmp;
1341 if( nY < nHeight )
1343 if( bPal )
1345 for( long nZ = 0; nZ < nWidth; nZ++ )
1347 aColor = pReadAcc->GetPaletteColor( pReadAcc->GetPixelIndex( nY, nZ ) );
1349 *pTmp++ = (long) aColor.GetBlue() << 12;
1350 *pTmp++ = (long) aColor.GetGreen() << 12;
1351 *pTmp++ = (long) aColor.GetRed() << 12;
1354 else
1356 for( long nZ = 0; nZ < nWidth; nZ++ )
1358 aColor = pReadAcc->GetPixel( nY, nZ );
1360 *pTmp++ = (long) aColor.GetBlue() << 12;
1361 *pTmp++ = (long) aColor.GetGreen() << 12;
1362 *pTmp++ = (long) aColor.GetRed() << 12;
1367 // Examine first Pixel separately
1368 nX = 0;
1369 long nTemp;
1370 CALC_ERRORS;
1371 CALC_TABLES7;
1372 nX -= 5;
1373 CALC_TABLES5;
1374 pWriteAcc->SetPixelIndex( nYAcc, 0, static_cast<sal_uInt8>(nVCLBLut[ nBC ] + nVCLGLut[nGC ] + nVCLRLut[nRC ]) );
1376 // Get middle Pixels using a loop
1377 long nXAcc;
1378 for ( nX = 3, nXAcc = 1; nX < nW2; nXAcc++ )
1380 CALC_ERRORS;
1381 CALC_TABLES7;
1382 nX -= 8;
1383 CALC_TABLES3;
1384 CALC_TABLES5;
1385 pWriteAcc->SetPixelIndex( nYAcc, nXAcc, static_cast<sal_uInt8>(nVCLBLut[ nBC ] + nVCLGLut[nGC ] + nVCLRLut[nRC ]) );
1388 // Treat last Pixel separately
1389 CALC_ERRORS;
1390 nX -= 5;
1391 CALC_TABLES3;
1392 CALC_TABLES5;
1393 pWriteAcc->SetPixelIndex( nYAcc, nWidth1, static_cast<sal_uInt8>(nVCLBLut[ nBC ] + nVCLGLut[nGC ] + nVCLRLut[nRC ]) );
1396 bRet = true;
1399 pReadAcc.reset();
1400 pWriteAcc.reset();
1402 if( bRet )
1404 const MapMode aMap( maPrefMapMode );
1405 const Size aPrefSize( maPrefSize );
1407 *this = aNewBmp;
1409 maPrefMapMode = aMap;
1410 maPrefSize = aPrefSize;
1414 return bRet;
1417 bool Bitmap::ImplDitherFloyd16()
1419 ScopedReadAccess pReadAcc(*this);
1420 Bitmap aNewBmp( GetSizePixel(), 24 );
1421 ScopedWriteAccess pWriteAcc(aNewBmp);
1422 bool bRet = false;
1424 if( pReadAcc && pWriteAcc )
1426 const long nWidth = pWriteAcc->Width();
1427 const long nWidth1 = nWidth - 1;
1428 const long nHeight = pWriteAcc->Height();
1429 BitmapColor aColor;
1430 BitmapColor aBestCol;
1431 ImpErrorQuad aErrQuad;
1432 std::unique_ptr<ImpErrorQuad[]> pErrQuad1(new ImpErrorQuad[ nWidth ]);
1433 std::unique_ptr<ImpErrorQuad[]> pErrQuad2(new ImpErrorQuad[ nWidth ]);
1434 ImpErrorQuad* pQLine1 = pErrQuad1.get();
1435 ImpErrorQuad* pQLine2 = nullptr;
1436 long nYTmp = 0;
1437 bool bQ1 = true;
1439 for( long nY = 0; nY < std::min( nHeight, 2L ); nY++, nYTmp++ )
1441 pQLine2 = !nY ? pErrQuad1.get() : pErrQuad2.get();
1442 for( long nX = 0; nX < nWidth; nX++ )
1443 pQLine2[ nX ] = pReadAcc->GetPixel( nYTmp, nX );
1446 for( long nY = 0; nY < nHeight; nY++, nYTmp++ )
1448 // First RowPixel
1449 aBestCol = pQLine1[ 0 ].ImplGetColor();
1450 aBestCol.SetRed( ( aBestCol.GetRed() & 248 ) | 7 );
1451 aBestCol.SetGreen( ( aBestCol.GetGreen() & 248 ) | 7 );
1452 aBestCol.SetBlue( ( aBestCol.GetBlue() & 248 ) | 7 );
1453 pWriteAcc->SetPixel( nY, 0, aBestCol );
1455 long nX;
1456 for( nX = 1; nX < nWidth1; nX++ )
1458 aColor = pQLine1[ nX ].ImplGetColor();
1459 aBestCol.SetRed( ( aColor.GetRed() & 248 ) | 7 );
1460 aBestCol.SetGreen( ( aColor.GetGreen() & 248 ) | 7 );
1461 aBestCol.SetBlue( ( aColor.GetBlue() & 248 ) | 7 );
1462 aErrQuad = ( ImpErrorQuad( aColor ) -= aBestCol );
1463 pQLine1[ ++nX ].ImplAddColorError7( aErrQuad );
1464 pQLine2[ nX-- ].ImplAddColorError1( aErrQuad );
1465 pQLine2[ nX-- ].ImplAddColorError5( aErrQuad );
1466 pQLine2[ nX++ ].ImplAddColorError3( aErrQuad );
1467 pWriteAcc->SetPixel( nY, nX, aBestCol );
1470 // Last RowPixel
1471 aBestCol = pQLine1[ nWidth1 ].ImplGetColor();
1472 aBestCol.SetRed( ( aBestCol.GetRed() & 248 ) | 7 );
1473 aBestCol.SetGreen( ( aBestCol.GetGreen() & 248 ) | 7 );
1474 aBestCol.SetBlue( ( aBestCol.GetBlue() & 248 ) | 7 );
1475 pWriteAcc->SetPixel( nY, nX, aBestCol );
1477 // Refill/copy row buffer
1478 pQLine1 = pQLine2;
1479 bQ1 = !bQ1;
1480 pQLine2 = bQ1 ? pErrQuad2.get() : pErrQuad1.get();
1482 if( nYTmp < nHeight )
1483 for( nX = 0; nX < nWidth; nX++ )
1484 pQLine2[ nX ] = pReadAcc->GetPixel( nYTmp, nX );
1487 bRet = true;
1490 pReadAcc.reset();
1491 pWriteAcc.reset();
1493 if( bRet )
1495 const MapMode aMap( maPrefMapMode );
1496 const Size aSize( maPrefSize );
1498 *this = aNewBmp;
1500 maPrefMapMode = aMap;
1501 maPrefSize = aSize;
1504 return bRet;
1507 bool Bitmap::ReduceColors( sal_uInt16 nColorCount, BmpReduce eReduce )
1509 bool bRet;
1511 if( GetColorCount() <= (sal_uLong) nColorCount )
1512 bRet = true;
1513 else if( nColorCount )
1515 if( BMP_REDUCE_SIMPLE == eReduce )
1516 bRet = ImplReduceSimple( nColorCount );
1517 else if( BMP_REDUCE_POPULAR == eReduce )
1518 bRet = ImplReducePopular( nColorCount );
1519 else
1520 bRet = ImplReduceMedian( nColorCount );
1522 else
1523 bRet = false;
1525 return bRet;
1528 bool Bitmap::ImplReduceSimple( sal_uInt16 nColorCount )
1530 Bitmap aNewBmp;
1531 ScopedReadAccess pRAcc(*this);
1532 const sal_uInt16 nColCount = std::min( nColorCount, (sal_uInt16) 256 );
1533 sal_uInt16 nBitCount;
1534 bool bRet = false;
1536 if( nColCount <= 2 )
1537 nBitCount = 1;
1538 else if( nColCount <= 16 )
1539 nBitCount = 4;
1540 else
1541 nBitCount = 8;
1543 if( pRAcc )
1545 Octree aOct( *pRAcc, nColCount );
1546 const BitmapPalette& rPal = aOct.GetPalette();
1548 aNewBmp = Bitmap( GetSizePixel(), nBitCount, &rPal );
1549 ScopedWriteAccess pWAcc(aNewBmp);
1551 if( pWAcc )
1553 const long nWidth = pRAcc->Width();
1554 const long nHeight = pRAcc->Height();
1556 if( pRAcc->HasPalette() )
1558 for( long nY = 0; nY < nHeight; nY++ )
1559 for( long nX =0; nX < nWidth; nX++ )
1560 pWAcc->SetPixelIndex( nY, nX, static_cast<sal_uInt8>(aOct.GetBestPaletteIndex( pRAcc->GetPaletteColor( pRAcc->GetPixelIndex( nY, nX ) ))) );
1562 else
1564 for( long nY = 0; nY < nHeight; nY++ )
1565 for( long nX =0; nX < nWidth; nX++ )
1566 pWAcc->SetPixelIndex( nY, nX, static_cast<sal_uInt8>(aOct.GetBestPaletteIndex( pRAcc->GetPixel( nY, nX ) )) );
1569 pWAcc.reset();
1570 bRet = true;
1573 pRAcc.reset();
1576 if( bRet )
1578 const MapMode aMap( maPrefMapMode );
1579 const Size aSize( maPrefSize );
1581 *this = aNewBmp;
1582 maPrefMapMode = aMap;
1583 maPrefSize = aSize;
1586 return bRet;
1589 struct PopularColorCount
1591 sal_uInt32 mnIndex;
1592 sal_uInt32 mnCount;
1595 extern "C" int SAL_CALL ImplPopularCmpFnc( const void* p1, const void* p2 )
1597 int nRet;
1599 if( static_cast<PopularColorCount const *>(p1)->mnCount < static_cast<PopularColorCount const *>(p2)->mnCount )
1600 nRet = 1;
1601 else if( static_cast<PopularColorCount const *>(p1)->mnCount == static_cast<PopularColorCount const *>(p2)->mnCount )
1602 nRet = 0;
1603 else
1604 nRet = -1;
1606 return nRet;
1609 bool Bitmap::ImplReducePopular( sal_uInt16 nColCount )
1611 ScopedReadAccess pRAcc(*this);
1612 sal_uInt16 nBitCount;
1613 bool bRet = false;
1615 if( nColCount > 256 )
1616 nColCount = 256;
1618 if( nColCount < 17 )
1619 nBitCount = 4;
1620 else
1621 nBitCount = 8;
1623 if( pRAcc )
1625 const sal_uInt32 nValidBits = 4;
1626 const sal_uInt32 nRightShiftBits = 8 - nValidBits;
1627 const sal_uInt32 nLeftShiftBits1 = nValidBits;
1628 const sal_uInt32 nLeftShiftBits2 = nValidBits << 1;
1629 const sal_uInt32 nColorsPerComponent = 1 << nValidBits;
1630 const sal_uInt32 nColorOffset = 256 / nColorsPerComponent;
1631 const sal_uInt32 nTotalColors = nColorsPerComponent * nColorsPerComponent * nColorsPerComponent;
1632 const long nWidth = pRAcc->Width();
1633 const long nHeight = pRAcc->Height();
1634 std::unique_ptr<PopularColorCount[]> pCountTable(new PopularColorCount[ nTotalColors ]);
1636 memset( pCountTable.get(), 0, nTotalColors * sizeof( PopularColorCount ) );
1638 for( long nR = 0, nIndex = 0; nR < 256; nR += nColorOffset )
1640 for( long nG = 0; nG < 256; nG += nColorOffset )
1642 for( long nB = 0; nB < 256; nB += nColorOffset )
1644 pCountTable[ nIndex ].mnIndex = nIndex;
1645 nIndex++;
1650 if( pRAcc->HasPalette() )
1652 for( long nY = 0; nY < nHeight; nY++ )
1654 for( long nX = 0; nX < nWidth; nX++ )
1656 const BitmapColor& rCol = pRAcc->GetPaletteColor( pRAcc->GetPixelIndex( nY, nX ) );
1657 pCountTable[ ( ( ( (sal_uInt32) rCol.GetRed() ) >> nRightShiftBits ) << nLeftShiftBits2 ) |
1658 ( ( ( (sal_uInt32) rCol.GetGreen() ) >> nRightShiftBits ) << nLeftShiftBits1 ) |
1659 ( ( (sal_uInt32) rCol.GetBlue() ) >> nRightShiftBits ) ].mnCount++;
1663 else
1665 for( long nY = 0; nY < nHeight; nY++ )
1667 for( long nX = 0; nX < nWidth; nX++ )
1669 const BitmapColor aCol( pRAcc->GetPixel( nY, nX ) );
1670 pCountTable[ ( ( ( (sal_uInt32) aCol.GetRed() ) >> nRightShiftBits ) << nLeftShiftBits2 ) |
1671 ( ( ( (sal_uInt32) aCol.GetGreen() ) >> nRightShiftBits ) << nLeftShiftBits1 ) |
1672 ( ( (sal_uInt32) aCol.GetBlue() ) >> nRightShiftBits ) ].mnCount++;
1677 BitmapPalette aNewPal( nColCount );
1679 qsort( pCountTable.get(), nTotalColors, sizeof( PopularColorCount ), ImplPopularCmpFnc );
1681 for( sal_uInt16 n = 0; n < nColCount; n++ )
1683 const PopularColorCount& rPop = pCountTable[ n ];
1684 aNewPal[ n ] = BitmapColor( (sal_uInt8) ( ( rPop.mnIndex >> nLeftShiftBits2 ) << nRightShiftBits ),
1685 (sal_uInt8) ( ( ( rPop.mnIndex >> nLeftShiftBits1 ) & ( nColorsPerComponent - 1 ) ) << nRightShiftBits ),
1686 (sal_uInt8) ( ( rPop.mnIndex & ( nColorsPerComponent - 1 ) ) << nRightShiftBits ) );
1689 Bitmap aNewBmp( GetSizePixel(), nBitCount, &aNewPal );
1690 ScopedWriteAccess pWAcc(aNewBmp);
1692 if( pWAcc )
1694 BitmapColor aDstCol( (sal_uInt8) 0 );
1695 std::unique_ptr<sal_uInt8[]> pIndexMap(new sal_uInt8[ nTotalColors ]);
1697 for( long nR = 0, nIndex = 0; nR < 256; nR += nColorOffset )
1698 for( long nG = 0; nG < 256; nG += nColorOffset )
1699 for( long nB = 0; nB < 256; nB += nColorOffset )
1700 pIndexMap[ nIndex++ ] = (sal_uInt8) aNewPal.GetBestIndex( BitmapColor( (sal_uInt8) nR, (sal_uInt8) nG, (sal_uInt8) nB ) );
1702 if( pRAcc->HasPalette() )
1704 for( long nY = 0; nY < nHeight; nY++ )
1706 for( long nX = 0; nX < nWidth; nX++ )
1708 const BitmapColor& rCol = pRAcc->GetPaletteColor( pRAcc->GetPixelIndex( nY, nX ) );
1709 aDstCol.SetIndex( pIndexMap[ ( ( ( (sal_uInt32) rCol.GetRed() ) >> nRightShiftBits ) << nLeftShiftBits2 ) |
1710 ( ( ( (sal_uInt32) rCol.GetGreen() ) >> nRightShiftBits ) << nLeftShiftBits1 ) |
1711 ( ( (sal_uInt32) rCol.GetBlue() ) >> nRightShiftBits ) ] );
1712 pWAcc->SetPixel( nY, nX, aDstCol );
1716 else
1718 for( long nY = 0; nY < nHeight; nY++ )
1720 for( long nX = 0; nX < nWidth; nX++ )
1722 const BitmapColor aCol( pRAcc->GetPixel( nY, nX ) );
1723 aDstCol.SetIndex( pIndexMap[ ( ( ( (sal_uInt32) aCol.GetRed() ) >> nRightShiftBits ) << nLeftShiftBits2 ) |
1724 ( ( ( (sal_uInt32) aCol.GetGreen() ) >> nRightShiftBits ) << nLeftShiftBits1 ) |
1725 ( ( (sal_uInt32) aCol.GetBlue() ) >> nRightShiftBits ) ] );
1726 pWAcc->SetPixel( nY, nX, aDstCol );
1731 pWAcc.reset();
1732 bRet = true;
1735 pCountTable.reset();
1736 pRAcc.reset();
1738 if( bRet )
1740 const MapMode aMap( maPrefMapMode );
1741 const Size aSize( maPrefSize );
1743 *this = aNewBmp;
1744 maPrefMapMode = aMap;
1745 maPrefSize = aSize;
1749 return bRet;
1752 bool Bitmap::ImplReduceMedian( sal_uInt16 nColCount )
1754 ScopedReadAccess pRAcc(*this);
1755 sal_uInt16 nBitCount;
1756 bool bRet = false;
1758 if( nColCount < 17 )
1759 nBitCount = 4;
1760 else if( nColCount < 257 )
1761 nBitCount = 8;
1762 else
1764 OSL_FAIL( "Bitmap::ImplReduceMedian(): invalid color count!" );
1765 nBitCount = 8;
1766 nColCount = 256;
1769 if( pRAcc )
1771 Bitmap aNewBmp( GetSizePixel(), nBitCount );
1772 ScopedWriteAccess pWAcc(aNewBmp);
1774 if( pWAcc )
1776 const sal_uLong nSize = 32768UL * sizeof( sal_uLong );
1777 sal_uLong* pColBuf = static_cast<sal_uLong*>(rtl_allocateMemory( nSize ));
1778 const long nWidth = pWAcc->Width();
1779 const long nHeight = pWAcc->Height();
1780 long nIndex = 0;
1782 memset( pColBuf, 0, nSize );
1784 // create Buffer
1785 if( pRAcc->HasPalette() )
1787 for( long nY = 0; nY < nHeight; nY++ )
1789 for( long nX = 0; nX < nWidth; nX++ )
1791 const BitmapColor& rCol = pRAcc->GetPaletteColor( pRAcc->GetPixelIndex( nY, nX ) );
1792 pColBuf[ RGB15( rCol.GetRed() >> 3, rCol.GetGreen() >> 3, rCol.GetBlue() >> 3 ) ]++;
1796 else
1798 for( long nY = 0; nY < nHeight; nY++ )
1800 for( long nX = 0; nX < nWidth; nX++ )
1802 const BitmapColor aCol( pRAcc->GetPixel( nY, nX ) );
1803 pColBuf[ RGB15( aCol.GetRed() >> 3, aCol.GetGreen() >> 3, aCol.GetBlue() >> 3 ) ]++;
1808 // create palette via median cut
1809 BitmapPalette aPal( pWAcc->GetPaletteEntryCount() );
1810 ImplMedianCut( pColBuf, aPal, 0, 31, 0, 31, 0, 31,
1811 nColCount, nWidth * nHeight, nIndex );
1813 // do mapping of colors to palette
1814 InverseColorMap aMap( aPal );
1815 pWAcc->SetPalette( aPal );
1816 for( long nY = 0; nY < nHeight; nY++ )
1817 for( long nX = 0; nX < nWidth; nX++ )
1818 pWAcc->SetPixelIndex( nY, nX, static_cast<sal_uInt8>( aMap.GetBestPaletteIndex( pRAcc->GetColor( nY, nX ) )) );
1820 rtl_freeMemory( pColBuf );
1821 pWAcc.reset();
1822 bRet = true;
1825 pRAcc.reset();
1827 if( bRet )
1829 const MapMode aMap( maPrefMapMode );
1830 const Size aSize( maPrefSize );
1832 *this = aNewBmp;
1833 maPrefMapMode = aMap;
1834 maPrefSize = aSize;
1838 return bRet;
1841 void Bitmap::ImplMedianCut( sal_uLong* pColBuf, BitmapPalette& rPal,
1842 long nR1, long nR2, long nG1, long nG2, long nB1, long nB2,
1843 long nColors, long nPixels, long& rIndex )
1845 if( !nPixels )
1846 return;
1848 BitmapColor aCol;
1849 const long nRLen = nR2 - nR1;
1850 const long nGLen = nG2 - nG1;
1851 const long nBLen = nB2 - nB1;
1852 sal_uLong* pBuf = pColBuf;
1854 if( !nRLen && !nGLen && !nBLen )
1856 if( pBuf[ RGB15( nR1, nG1, nB1 ) ] )
1858 aCol.SetRed( (sal_uInt8) ( nR1 << 3 ) );
1859 aCol.SetGreen( (sal_uInt8) ( nG1 << 3 ) );
1860 aCol.SetBlue( (sal_uInt8) ( nB1 << 3 ) );
1861 rPal[ (sal_uInt16) rIndex++ ] = aCol;
1864 else
1866 if( 1 == nColors || 1 == nPixels )
1868 long nPixSum = 0, nRSum = 0, nGSum = 0, nBSum = 0;
1870 for( long nR = nR1; nR <= nR2; nR++ )
1872 for( long nG = nG1; nG <= nG2; nG++ )
1874 for( long nB = nB1; nB <= nB2; nB++ )
1876 nPixSum = pBuf[ RGB15( nR, nG, nB ) ];
1878 if( nPixSum )
1880 nRSum += nR * nPixSum;
1881 nGSum += nG * nPixSum;
1882 nBSum += nB * nPixSum;
1888 aCol.SetRed( (sal_uInt8) ( ( nRSum / nPixels ) << 3 ) );
1889 aCol.SetGreen( (sal_uInt8) ( ( nGSum / nPixels ) << 3 ) );
1890 aCol.SetBlue( (sal_uInt8) ( ( nBSum / nPixels ) << 3 ) );
1891 rPal[ (sal_uInt16) rIndex++ ] = aCol;
1893 else
1895 const long nTest = ( nPixels >> 1 );
1896 long nPixOld = 0;
1897 long nPixNew = 0;
1899 if( nBLen > nGLen && nBLen > nRLen )
1901 long nB = nB1 - 1;
1903 while( nPixNew < nTest )
1905 nB++;
1906 nPixOld = nPixNew;
1907 for( long nR = nR1; nR <= nR2; nR++ )
1908 for( long nG = nG1; nG <= nG2; nG++ )
1909 nPixNew += pBuf[ RGB15( nR, nG, nB ) ];
1912 if( nB < nB2 )
1914 ImplMedianCut( pBuf, rPal, nR1, nR2, nG1, nG2, nB1, nB, nColors >> 1, nPixNew, rIndex );
1915 ImplMedianCut( pBuf, rPal, nR1, nR2, nG1, nG2, nB + 1, nB2, nColors >> 1, nPixels - nPixNew, rIndex );
1917 else
1919 ImplMedianCut( pBuf, rPal, nR1, nR2, nG1, nG2, nB1, nB - 1, nColors >> 1, nPixOld, rIndex );
1920 ImplMedianCut( pBuf, rPal, nR1, nR2, nG1, nG2, nB, nB2, nColors >> 1, nPixels - nPixOld, rIndex );
1923 else if( nGLen > nRLen )
1925 long nG = nG1 - 1;
1927 while( nPixNew < nTest )
1929 nG++;
1930 nPixOld = nPixNew;
1931 for( long nR = nR1; nR <= nR2; nR++ )
1932 for( long nB = nB1; nB <= nB2; nB++ )
1933 nPixNew += pBuf[ RGB15( nR, nG, nB ) ];
1936 if( nG < nG2 )
1938 ImplMedianCut( pBuf, rPal, nR1, nR2, nG1, nG, nB1, nB2, nColors >> 1, nPixNew, rIndex );
1939 ImplMedianCut( pBuf, rPal, nR1, nR2, nG + 1, nG2, nB1, nB2, nColors >> 1, nPixels - nPixNew, rIndex );
1941 else
1943 ImplMedianCut( pBuf, rPal, nR1, nR2, nG1, nG - 1, nB1, nB2, nColors >> 1, nPixOld, rIndex );
1944 ImplMedianCut( pBuf, rPal, nR1, nR2, nG, nG2, nB1, nB2, nColors >> 1, nPixels - nPixOld, rIndex );
1947 else
1949 long nR = nR1 - 1;
1951 while( nPixNew < nTest )
1953 nR++;
1954 nPixOld = nPixNew;
1955 for( long nG = nG1; nG <= nG2; nG++ )
1956 for( long nB = nB1; nB <= nB2; nB++ )
1957 nPixNew += pBuf[ RGB15( nR, nG, nB ) ];
1960 if( nR < nR2 )
1962 ImplMedianCut( pBuf, rPal, nR1, nR, nG1, nG2, nB1, nB2, nColors >> 1, nPixNew, rIndex );
1963 ImplMedianCut( pBuf, rPal, nR1 + 1, nR2, nG1, nG2, nB1, nB2, nColors >> 1, nPixels - nPixNew, rIndex );
1965 else
1967 ImplMedianCut( pBuf, rPal, nR1, nR - 1, nG1, nG2, nB1, nB2, nColors >> 1, nPixOld, rIndex );
1968 ImplMedianCut( pBuf, rPal, nR, nR2, nG1, nG2, nB1, nB2, nColors >> 1, nPixels - nPixOld, rIndex );
1975 bool Bitmap::Vectorize( GDIMetaFile& rMtf, sal_uInt8 cReduce, const Link<long,void>* pProgress )
1977 return ImplVectorizer::ImplVectorize( *this, rMtf, cReduce, pProgress );
1980 bool Bitmap::Adjust( short nLuminancePercent, short nContrastPercent,
1981 short nChannelRPercent, short nChannelGPercent, short nChannelBPercent,
1982 double fGamma, bool bInvert, bool msoBrightness )
1984 bool bRet = false;
1986 // nothing to do => return quickly
1987 if( !nLuminancePercent && !nContrastPercent &&
1988 !nChannelRPercent && !nChannelGPercent && !nChannelBPercent &&
1989 ( fGamma == 1.0 ) && !bInvert )
1991 bRet = true;
1993 else
1995 ScopedWriteAccess pAcc(*this);
1997 if( pAcc )
1999 BitmapColor aCol;
2000 const long nW = pAcc->Width();
2001 const long nH = pAcc->Height();
2002 std::unique_ptr<sal_uInt8[]> cMapR(new sal_uInt8[ 256 ]);
2003 std::unique_ptr<sal_uInt8[]> cMapG(new sal_uInt8[ 256 ]);
2004 std::unique_ptr<sal_uInt8[]> cMapB(new sal_uInt8[ 256 ]);
2005 double fM, fROff, fGOff, fBOff, fOff;
2007 // calculate slope
2008 if( nContrastPercent >= 0 )
2009 fM = 128.0 / ( 128.0 - 1.27 * MinMax( nContrastPercent, 0, 100 ) );
2010 else
2011 fM = ( 128.0 + 1.27 * MinMax( nContrastPercent, -100, 0 ) ) / 128.0;
2013 if(!msoBrightness)
2014 // total offset = luminance offset + contrast offset
2015 fOff = MinMax( nLuminancePercent, -100, 100 ) * 2.55 + 128.0 - fM * 128.0;
2016 else
2017 fOff = MinMax( nLuminancePercent, -100, 100 ) * 2.55;
2019 // channel offset = channel offset + total offset
2020 fROff = nChannelRPercent * 2.55 + fOff;
2021 fGOff = nChannelGPercent * 2.55 + fOff;
2022 fBOff = nChannelBPercent * 2.55 + fOff;
2024 // calculate gamma value
2025 fGamma = ( fGamma <= 0.0 || fGamma > 10.0 ) ? 1.0 : ( 1.0 / fGamma );
2026 const bool bGamma = ( fGamma != 1.0 );
2028 // create mapping table
2029 for( long nX = 0; nX < 256; nX++ )
2031 if(!msoBrightness)
2033 cMapR[ nX ] = (sal_uInt8) MinMax( FRound( nX * fM + fROff ), 0, 255 );
2034 cMapG[ nX ] = (sal_uInt8) MinMax( FRound( nX * fM + fGOff ), 0, 255 );
2035 cMapB[ nX ] = (sal_uInt8) MinMax( FRound( nX * fM + fBOff ), 0, 255 );
2037 else
2039 // LO simply uses (in a somewhat optimized form) "newcolor = (oldcolor-128)*contrast+brightness+128"
2040 // as the formula, i.e. contrast first, brightness afterwards. MSOffice, for whatever weird reason,
2041 // use neither first, but apparently it applies half of brightness before contrast and half afterwards.
2042 cMapR[ nX ] = (sal_uInt8) MinMax( FRound( (nX+fROff/2-128) * fM + 128 + fROff/2 ), 0, 255 );
2043 cMapG[ nX ] = (sal_uInt8) MinMax( FRound( (nX+fGOff/2-128) * fM + 128 + fGOff/2 ), 0, 255 );
2044 cMapB[ nX ] = (sal_uInt8) MinMax( FRound( (nX+fBOff/2-128) * fM + 128 + fBOff/2 ), 0, 255 );
2046 if( bGamma )
2048 cMapR[ nX ] = GAMMA( cMapR[ nX ], fGamma );
2049 cMapG[ nX ] = GAMMA( cMapG[ nX ], fGamma );
2050 cMapB[ nX ] = GAMMA( cMapB[ nX ], fGamma );
2053 if( bInvert )
2055 cMapR[ nX ] = ~cMapR[ nX ];
2056 cMapG[ nX ] = ~cMapG[ nX ];
2057 cMapB[ nX ] = ~cMapB[ nX ];
2061 // do modifying
2062 if( pAcc->HasPalette() )
2064 BitmapColor aNewCol;
2066 for( sal_uInt16 i = 0, nCount = pAcc->GetPaletteEntryCount(); i < nCount; i++ )
2068 const BitmapColor& rCol = pAcc->GetPaletteColor( i );
2069 aNewCol.SetRed( cMapR[ rCol.GetRed() ] );
2070 aNewCol.SetGreen( cMapG[ rCol.GetGreen() ] );
2071 aNewCol.SetBlue( cMapB[ rCol.GetBlue() ] );
2072 pAcc->SetPaletteColor( i, aNewCol );
2075 else if( pAcc->GetScanlineFormat() == ScanlineFormat::N24BitTcBgr )
2077 for( long nY = 0; nY < nH; nY++ )
2079 Scanline pScan = pAcc->GetScanline( nY );
2081 for( long nX = 0; nX < nW; nX++ )
2083 *pScan = cMapB[ *pScan ]; pScan++;
2084 *pScan = cMapG[ *pScan ]; pScan++;
2085 *pScan = cMapR[ *pScan ]; pScan++;
2089 else if( pAcc->GetScanlineFormat() == ScanlineFormat::N24BitTcRgb )
2091 for( long nY = 0; nY < nH; nY++ )
2093 Scanline pScan = pAcc->GetScanline( nY );
2095 for( long nX = 0; nX < nW; nX++ )
2097 *pScan = cMapR[ *pScan ]; pScan++;
2098 *pScan = cMapG[ *pScan ]; pScan++;
2099 *pScan = cMapB[ *pScan ]; pScan++;
2103 else
2105 for( long nY = 0; nY < nH; nY++ )
2107 for( long nX = 0; nX < nW; nX++ )
2109 aCol = pAcc->GetPixel( nY, nX );
2110 aCol.SetRed( cMapR[ aCol.GetRed() ] );
2111 aCol.SetGreen( cMapG[ aCol.GetGreen() ] );
2112 aCol.SetBlue( cMapB[ aCol.GetBlue() ] );
2113 pAcc->SetPixel( nY, nX, aCol );
2118 pAcc.reset();
2119 bRet = true;
2123 return bRet;
2126 bool Bitmap::ImplConvolutionPass(Bitmap& aNewBitmap, BitmapReadAccess* pReadAcc, int aNumberOfContributions, double* pWeights, int* pPixels, int* pCount)
2128 if (!pReadAcc)
2129 return false;
2131 ScopedWriteAccess pWriteAcc(aNewBitmap);
2132 if (!pWriteAcc)
2133 return false;
2135 const int nHeight = GetSizePixel().Height();
2136 assert(GetSizePixel().Height() == aNewBitmap.GetSizePixel().Width());
2137 const int nWidth = GetSizePixel().Width();
2138 assert(GetSizePixel().Width() == aNewBitmap.GetSizePixel().Height());
2140 BitmapColor aColor;
2141 double aValueRed, aValueGreen, aValueBlue;
2142 double aSum, aWeight;
2143 int aBaseIndex, aIndex;
2145 for (int nSourceY = 0; nSourceY < nHeight; ++nSourceY)
2147 for (int nSourceX = 0; nSourceX < nWidth; ++nSourceX)
2149 aBaseIndex = nSourceX * aNumberOfContributions;
2150 aSum = aValueRed = aValueGreen = aValueBlue = 0.0;
2152 for (int j = 0; j < pCount[nSourceX]; ++j)
2154 aIndex = aBaseIndex + j;
2155 aSum += aWeight = pWeights[ aIndex ];
2157 aColor = pReadAcc->GetColor(nSourceY, pPixels[aIndex]);
2159 aValueRed += aWeight * aColor.GetRed();
2160 aValueGreen += aWeight * aColor.GetGreen();
2161 aValueBlue += aWeight * aColor.GetBlue();
2164 BitmapColor aResultColor(
2165 (sal_uInt8) MinMax( aValueRed / aSum, 0, 255 ),
2166 (sal_uInt8) MinMax( aValueGreen / aSum, 0, 255 ),
2167 (sal_uInt8) MinMax( aValueBlue / aSum, 0, 255 ) );
2169 int nDestX = nSourceY;
2170 int nDestY = nSourceX;
2172 pWriteAcc->SetPixel(nDestY, nDestX, aResultColor);
2175 return true;
2178 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */