Version 5.4.3.2, tag libreoffice-5.4.3.2
[LibreOffice.git] / vcl / source / gdi / bitmap4.cxx
blob789ebc80296ebc1567516a47c4e3c369e4c41606
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 <memory>
21 #include <stdlib.h>
22 #include <osl/diagnose.h>
23 #include <vcl/bitmapaccess.hxx>
24 #include <vcl/bitmap.hxx>
26 #define S2(a,b) { long t; if( ( t = b - a ) < 0 ) { a += t; b -= t; } }
27 #define MN3(a,b,c) S2(a,b); S2(a,c);
28 #define MX3(a,b,c) S2(b,c); S2(a,c);
29 #define MNMX3(a,b,c) MX3(a,b,c); S2(a,b);
30 #define MNMX4(a,b,c,d) S2(a,b); S2(c,d); S2(a,c); S2(b,d);
31 #define MNMX5(a,b,c,d,e) S2(a,b); S2(c,d); MN3(a,c,e); MX3(b,d,e);
32 #define MNMX6(a,b,c,d,e,f) S2(a,d); S2(b,e); S2(c,f); MN3(a,b,c); MX3(d,e,f);
34 static inline sal_uInt8 lcl_getDuotoneColorComponent( sal_uInt8 base, sal_uInt16 color1, sal_uInt16 color2 )
36 color2 = color2*base/0xFF;
37 color1 = color1*(0xFF-base)/0xFF;
39 return (sal_uInt8) (color1+color2);
42 bool Bitmap::Filter( BmpFilter eFilter, const BmpFilterParam* pFilterParam )
44 bool bRet = false;
46 switch( eFilter )
48 case BmpFilter::Smooth:
50 // Blur for positive values of mnRadius
51 if (pFilterParam->mnRadius > 0.0)
53 bRet = ImplSeparableBlurFilter(pFilterParam->mnRadius);
55 // Unsharpen Mask for negative values of mnRadius
56 else if (pFilterParam->mnRadius < 0.0)
58 bRet = ImplSeparableUnsharpenFilter(pFilterParam->mnRadius);
60 else
62 bRet = false;
65 break;
67 case BmpFilter::Sharpen:
69 const long pSharpenMatrix[] = { -1, -1, -1, -1, 16, -1, -1, -1, -1 };
70 bRet = ImplConvolute3( &pSharpenMatrix[ 0 ] );
72 break;
74 case BmpFilter::RemoveNoise:
75 bRet = ImplMedianFilter();
76 break;
78 case BmpFilter::SobelGrey:
79 bRet = ImplSobelGrey();
80 break;
82 case BmpFilter::Solarize:
83 bRet = ImplSolarize( pFilterParam );
84 break;
86 case BmpFilter::Sepia:
87 bRet = ImplSepia( pFilterParam );
88 break;
90 case BmpFilter::Mosaic:
91 bRet = ImplMosaic( pFilterParam );
92 break;
94 case BmpFilter::EmbossGrey:
95 bRet = ImplEmbossGrey( pFilterParam );
96 break;
98 case BmpFilter::PopArt:
99 bRet = ImplPopArt();
100 break;
102 case BmpFilter::DuoTone:
103 bRet = ImplDuotoneFilter( pFilterParam->mnProgressStart, pFilterParam->mnProgressEnd );
104 break;
106 default:
107 OSL_FAIL( "Bitmap::Convert(): Unsupported filter" );
108 break;
111 return bRet;
114 bool Bitmap::ImplConvolute3( const long* pMatrix )
116 const long nDivisor = 8;
117 ScopedReadAccess pReadAcc(*this);
118 bool bRet = false;
120 if( pReadAcc )
122 Bitmap aNewBmp( GetSizePixel(), 24 );
123 ScopedWriteAccess pWriteAcc(aNewBmp);
125 if( pWriteAcc )
127 const long nWidth = pWriteAcc->Width(), nWidth2 = nWidth + 2;
128 const long nHeight = pWriteAcc->Height(), nHeight2 = nHeight + 2;
129 long* pColm = new long[ nWidth2 ];
130 long* pRows = new long[ nHeight2 ];
131 BitmapColor* pColRow1 = reinterpret_cast<BitmapColor*>(new sal_uInt8[ sizeof( BitmapColor ) * nWidth2 ]);
132 BitmapColor* pColRow2 = reinterpret_cast<BitmapColor*>(new sal_uInt8[ sizeof( BitmapColor ) * nWidth2 ]);
133 BitmapColor* pColRow3 = reinterpret_cast<BitmapColor*>(new sal_uInt8[ sizeof( BitmapColor ) * nWidth2 ]);
134 BitmapColor* pRowTmp1 = pColRow1;
135 BitmapColor* pRowTmp2 = pColRow2;
136 BitmapColor* pRowTmp3 = pColRow3;
137 BitmapColor* pColor;
138 long nY, nX, i, nSumR, nSumG, nSumB, nMatrixVal, nTmp;
139 long (*pKoeff)[ 256 ] = new long[ 9 ][ 256 ];
140 long* pTmp;
142 // create LUT of products of matrix value and possible color component values
143 for( nY = 0; nY < 9; nY++ )
144 for( nX = nTmp = 0, nMatrixVal = pMatrix[ nY ]; nX < 256; nX++, nTmp += nMatrixVal )
145 pKoeff[ nY ][ nX ] = nTmp;
147 // create column LUT
148 for( i = 0; i < nWidth2; i++ )
149 pColm[ i ] = ( i > 0 ) ? ( i - 1 ) : 0;
151 pColm[ nWidth + 1 ] = pColm[ nWidth ];
153 // create row LUT
154 for( i = 0; i < nHeight2; i++ )
155 pRows[ i ] = ( i > 0 ) ? ( i - 1 ) : 0;
157 pRows[ nHeight + 1 ] = pRows[ nHeight ];
159 // read first three rows of bitmap color
160 for( i = 0; i < nWidth2; i++ )
162 pColRow1[ i ] = pReadAcc->GetColor( pRows[ 0 ], pColm[ i ] );
163 pColRow2[ i ] = pReadAcc->GetColor( pRows[ 1 ], pColm[ i ] );
164 pColRow3[ i ] = pReadAcc->GetColor( pRows[ 2 ], pColm[ i ] );
167 // do convolution
168 for( nY = 0; nY < nHeight; )
170 for( nX = 0; nX < nWidth; nX++ )
172 // first row
173 nSumR = ( pTmp = pKoeff[ 0 ] )[ ( pColor = pRowTmp1 + nX )->GetRed() ];
174 nSumG = pTmp[ pColor->GetGreen() ];
175 nSumB = pTmp[ pColor->GetBlue() ];
177 nSumR += ( pTmp = pKoeff[ 1 ] )[ ( ++pColor )->GetRed() ];
178 nSumG += pTmp[ pColor->GetGreen() ];
179 nSumB += pTmp[ pColor->GetBlue() ];
181 nSumR += ( pTmp = pKoeff[ 2 ] )[ ( ++pColor )->GetRed() ];
182 nSumG += pTmp[ pColor->GetGreen() ];
183 nSumB += pTmp[ pColor->GetBlue() ];
185 // second row
186 nSumR += ( pTmp = pKoeff[ 3 ] )[ ( pColor = pRowTmp2 + nX )->GetRed() ];
187 nSumG += pTmp[ pColor->GetGreen() ];
188 nSumB += pTmp[ pColor->GetBlue() ];
190 nSumR += ( pTmp = pKoeff[ 4 ] )[ ( ++pColor )->GetRed() ];
191 nSumG += pTmp[ pColor->GetGreen() ];
192 nSumB += pTmp[ pColor->GetBlue() ];
194 nSumR += ( pTmp = pKoeff[ 5 ] )[ ( ++pColor )->GetRed() ];
195 nSumG += pTmp[ pColor->GetGreen() ];
196 nSumB += pTmp[ pColor->GetBlue() ];
198 // third row
199 nSumR += ( pTmp = pKoeff[ 6 ] )[ ( pColor = pRowTmp3 + nX )->GetRed() ];
200 nSumG += pTmp[ pColor->GetGreen() ];
201 nSumB += pTmp[ pColor->GetBlue() ];
203 nSumR += ( pTmp = pKoeff[ 7 ] )[ ( ++pColor )->GetRed() ];
204 nSumG += pTmp[ pColor->GetGreen() ];
205 nSumB += pTmp[ pColor->GetBlue() ];
207 nSumR += ( pTmp = pKoeff[ 8 ] )[ ( ++pColor )->GetRed() ];
208 nSumG += pTmp[ pColor->GetGreen() ];
209 nSumB += pTmp[ pColor->GetBlue() ];
211 // calculate destination color
212 pWriteAcc->SetPixel( nY, nX, BitmapColor( (sal_uInt8) MinMax( nSumR / nDivisor, 0, 255 ),
213 (sal_uInt8) MinMax( nSumG / nDivisor, 0, 255 ),
214 (sal_uInt8) MinMax( nSumB / nDivisor, 0, 255 ) ) );
217 if( ++nY < nHeight )
219 if( pRowTmp1 == pColRow1 )
221 pRowTmp1 = pColRow2;
222 pRowTmp2 = pColRow3;
223 pRowTmp3 = pColRow1;
225 else if( pRowTmp1 == pColRow2 )
227 pRowTmp1 = pColRow3;
228 pRowTmp2 = pColRow1;
229 pRowTmp3 = pColRow2;
231 else
233 pRowTmp1 = pColRow1;
234 pRowTmp2 = pColRow2;
235 pRowTmp3 = pColRow3;
238 for( i = 0; i < nWidth2; i++ )
239 pRowTmp3[ i ] = pReadAcc->GetColor( pRows[ nY + 2 ], pColm[ i ] );
243 delete[] pKoeff;
244 delete[] reinterpret_cast<sal_uInt8*>(pColRow1);
245 delete[] reinterpret_cast<sal_uInt8*>(pColRow2);
246 delete[] reinterpret_cast<sal_uInt8*>(pColRow3);
247 delete[] pColm;
248 delete[] pRows;
250 pWriteAcc.reset();
252 bRet = true;
255 pReadAcc.reset();
257 if( bRet )
259 const MapMode aMap( maPrefMapMode );
260 const Size aSize( maPrefSize );
262 *this = aNewBmp;
264 maPrefMapMode = aMap;
265 maPrefSize = aSize;
269 return bRet;
272 bool Bitmap::ImplMedianFilter()
274 ScopedReadAccess pReadAcc(*this);
275 bool bRet = false;
277 if( pReadAcc )
279 Bitmap aNewBmp( GetSizePixel(), 24 );
280 ScopedWriteAccess pWriteAcc(aNewBmp);
282 if( pWriteAcc )
284 const long nWidth = pWriteAcc->Width(), nWidth2 = nWidth + 2;
285 const long nHeight = pWriteAcc->Height(), nHeight2 = nHeight + 2;
286 long* pColm = new long[ nWidth2 ];
287 long* pRows = new long[ nHeight2 ];
288 BitmapColor* pColRow1 = reinterpret_cast<BitmapColor*>(new sal_uInt8[ sizeof( BitmapColor ) * nWidth2 ]);
289 BitmapColor* pColRow2 = reinterpret_cast<BitmapColor*>(new sal_uInt8[ sizeof( BitmapColor ) * nWidth2 ]);
290 BitmapColor* pColRow3 = reinterpret_cast<BitmapColor*>(new sal_uInt8[ sizeof( BitmapColor ) * nWidth2 ]);
291 BitmapColor* pRowTmp1 = pColRow1;
292 BitmapColor* pRowTmp2 = pColRow2;
293 BitmapColor* pRowTmp3 = pColRow3;
294 BitmapColor* pColor;
295 long nY, nX, i;
296 long nR1, nR2, nR3, nR4, nR5, nR6, nR7, nR8, nR9;
297 long nG1, nG2, nG3, nG4, nG5, nG6, nG7, nG8, nG9;
298 long nB1, nB2, nB3, nB4, nB5, nB6, nB7, nB8, nB9;
300 // create column LUT
301 for( i = 0; i < nWidth2; i++ )
302 pColm[ i ] = ( i > 0 ) ? ( i - 1 ) : 0;
304 pColm[ nWidth + 1 ] = pColm[ nWidth ];
306 // create row LUT
307 for( i = 0; i < nHeight2; i++ )
308 pRows[ i ] = ( i > 0 ) ? ( i - 1 ) : 0;
310 pRows[ nHeight + 1 ] = pRows[ nHeight ];
312 // read first three rows of bitmap color
313 if (nHeight2 > 2)
315 for( i = 0; i < nWidth2; i++ )
317 pColRow1[ i ] = pReadAcc->GetColor( pRows[ 0 ], pColm[ i ] );
318 pColRow2[ i ] = pReadAcc->GetColor( pRows[ 1 ], pColm[ i ] );
319 pColRow3[ i ] = pReadAcc->GetColor( pRows[ 2 ], pColm[ i ] );
323 // do median filtering
324 for( nY = 0; nY < nHeight; )
326 for( nX = 0; nX < nWidth; nX++ )
328 nR1 = ( pColor = pRowTmp1 + nX )->GetRed();
329 nG1 = pColor->GetGreen();
330 nB1 = pColor->GetBlue();
331 nR2 = ( ++pColor )->GetRed();
332 nG2 = pColor->GetGreen();
333 nB2 = pColor->GetBlue();
334 nR3 = ( ++pColor )->GetRed();
335 nG3 = pColor->GetGreen();
336 nB3 = pColor->GetBlue();
338 nR4 = ( pColor = pRowTmp2 + nX )->GetRed();
339 nG4 = pColor->GetGreen();
340 nB4 = pColor->GetBlue();
341 nR5 = ( ++pColor )->GetRed();
342 nG5 = pColor->GetGreen();
343 nB5 = pColor->GetBlue();
344 nR6 = ( ++pColor )->GetRed();
345 nG6 = pColor->GetGreen();
346 nB6 = pColor->GetBlue();
348 nR7 = ( pColor = pRowTmp3 + nX )->GetRed();
349 nG7 = pColor->GetGreen();
350 nB7 = pColor->GetBlue();
351 nR8 = ( ++pColor )->GetRed();
352 nG8 = pColor->GetGreen();
353 nB8 = pColor->GetBlue();
354 nR9 = ( ++pColor )->GetRed();
355 nG9 = pColor->GetGreen();
356 nB9 = pColor->GetBlue();
358 MNMX6( nR1, nR2, nR3, nR4, nR5, nR6 );
359 MNMX5( nR7, nR2, nR3, nR4, nR5 );
360 MNMX4( nR8, nR2, nR3, nR4 );
361 MNMX3( nR9, nR2, nR3 );
363 MNMX6( nG1, nG2, nG3, nG4, nG5, nG6 );
364 MNMX5( nG7, nG2, nG3, nG4, nG5 );
365 MNMX4( nG8, nG2, nG3, nG4 );
366 MNMX3( nG9, nG2, nG3 );
368 MNMX6( nB1, nB2, nB3, nB4, nB5, nB6 );
369 MNMX5( nB7, nB2, nB3, nB4, nB5 );
370 MNMX4( nB8, nB2, nB3, nB4 );
371 MNMX3( nB9, nB2, nB3 );
373 // set destination color
374 pWriteAcc->SetPixel( nY, nX, BitmapColor( (sal_uInt8) nR2, (sal_uInt8) nG2, (sal_uInt8) nB2 ) );
377 if( ++nY < nHeight )
379 if( pRowTmp1 == pColRow1 )
381 pRowTmp1 = pColRow2;
382 pRowTmp2 = pColRow3;
383 pRowTmp3 = pColRow1;
385 else if( pRowTmp1 == pColRow2 )
387 pRowTmp1 = pColRow3;
388 pRowTmp2 = pColRow1;
389 pRowTmp3 = pColRow2;
391 else
393 pRowTmp1 = pColRow1;
394 pRowTmp2 = pColRow2;
395 pRowTmp3 = pColRow3;
398 for( i = 0; i < nWidth2; i++ )
399 pRowTmp3[ i ] = pReadAcc->GetColor( pRows[ nY + 2 ], pColm[ i ] );
403 delete[] reinterpret_cast<sal_uInt8*>(pColRow1);
404 delete[] reinterpret_cast<sal_uInt8*>(pColRow2);
405 delete[] reinterpret_cast<sal_uInt8*>(pColRow3);
406 delete[] pColm;
407 delete[] pRows;
409 pWriteAcc.reset();
411 bRet = true;
414 pReadAcc.reset();
416 if( bRet )
418 const MapMode aMap( maPrefMapMode );
419 const Size aSize( maPrefSize );
421 *this = aNewBmp;
423 maPrefMapMode = aMap;
424 maPrefSize = aSize;
428 return bRet;
431 bool Bitmap::ImplSobelGrey()
433 bool bRet = ImplMakeGreyscales( 256 );
435 if( bRet )
437 bRet = false;
439 ScopedReadAccess pReadAcc(*this);
441 if( pReadAcc )
443 Bitmap aNewBmp( GetSizePixel(), 8, &pReadAcc->GetPalette() );
444 ScopedWriteAccess pWriteAcc(aNewBmp);
446 if( pWriteAcc )
448 BitmapColor aGrey( (sal_uInt8) 0 );
449 const long nWidth = pWriteAcc->Width();
450 const long nHeight = pWriteAcc->Height();
451 const long nMask111 = -1, nMask121 = 0, nMask131 = 1;
452 const long nMask211 = -2, nMask221 = 0, nMask231 = 2;
453 const long nMask311 = -1, nMask321 = 0, nMask331 = 1;
454 const long nMask112 = 1, nMask122 = 2, nMask132 = 1;
455 const long nMask212 = 0, nMask222 = 0, nMask232 = 0;
456 const long nMask312 = -1, nMask322 = -2, nMask332 = -1;
457 long nGrey11, nGrey12, nGrey13;
458 long nGrey21, nGrey22, nGrey23;
459 long nGrey31, nGrey32, nGrey33;
460 long* pHMap = new long[ nWidth + 2 ];
461 long* pVMap = new long[ nHeight + 2 ];
462 long nX, nY, nSum1, nSum2;
464 // fill mapping tables
465 pHMap[ 0 ] = 0;
466 for( nX = 1; nX <= nWidth; nX++ )
467 pHMap[ nX ] = nX - 1;
468 pHMap[ nWidth + 1 ] = nWidth - 1;
470 pVMap[ 0 ] = 0;
471 for( nY = 1; nY <= nHeight; nY++ )
472 pVMap[ nY ] = nY - 1;
473 pVMap[ nHeight + 1 ] = nHeight - 1;
475 for( nY = 0; nY < nHeight ; nY++ )
477 nGrey11 = pReadAcc->GetPixel( pVMap[ nY ], pHMap[ 0 ] ).GetIndex();
478 nGrey12 = pReadAcc->GetPixel( pVMap[ nY ], pHMap[ 1 ] ).GetIndex();
479 nGrey13 = pReadAcc->GetPixel( pVMap[ nY ], pHMap[ 2 ] ).GetIndex();
480 nGrey21 = pReadAcc->GetPixel( pVMap[ nY + 1 ], pHMap[ 0 ] ).GetIndex();
481 nGrey22 = pReadAcc->GetPixel( pVMap[ nY + 1 ], pHMap[ 1 ] ).GetIndex();
482 nGrey23 = pReadAcc->GetPixel( pVMap[ nY + 1 ], pHMap[ 2 ] ).GetIndex();
483 nGrey31 = pReadAcc->GetPixel( pVMap[ nY + 2 ], pHMap[ 0 ] ).GetIndex();
484 nGrey32 = pReadAcc->GetPixel( pVMap[ nY + 2 ], pHMap[ 1 ] ).GetIndex();
485 nGrey33 = pReadAcc->GetPixel( pVMap[ nY + 2 ], pHMap[ 2 ] ).GetIndex();
487 for( nX = 0; nX < nWidth; nX++ )
489 nSum1 = nSum2 = 0;
491 nSum1 += nMask111 * nGrey11;
492 nSum2 += nMask112 * nGrey11;
494 nSum1 += nMask121 * nGrey12;
495 nSum2 += nMask122 * nGrey12;
497 nSum1 += nMask131 * nGrey13;
498 nSum2 += nMask132 * nGrey13;
500 nSum1 += nMask211 * nGrey21;
501 nSum2 += nMask212 * nGrey21;
503 nSum1 += nMask221 * nGrey22;
504 nSum2 += nMask222 * nGrey22;
506 nSum1 += nMask231 * nGrey23;
507 nSum2 += nMask232 * nGrey23;
509 nSum1 += nMask311 * nGrey31;
510 nSum2 += nMask312 * nGrey31;
512 nSum1 += nMask321 * nGrey32;
513 nSum2 += nMask322 * nGrey32;
515 nSum1 += nMask331 * nGrey33;
516 nSum2 += nMask332 * nGrey33;
518 nSum1 = (long) sqrt( (double)( nSum1 * nSum1 + nSum2 * nSum2 ) );
519 aGrey.SetIndex( ~(sal_uInt8) SAL_BOUND( nSum1, 0, 255 ) );
520 pWriteAcc->SetPixel( nY, nX, aGrey );
522 if( nX < ( nWidth - 1 ) )
524 const long nNextX = pHMap[ nX + 3 ];
526 nGrey11 = nGrey12; nGrey12 = nGrey13; nGrey13 = pReadAcc->GetPixel( pVMap[ nY ], nNextX ).GetIndex();
527 nGrey21 = nGrey22; nGrey22 = nGrey23; nGrey23 = pReadAcc->GetPixel( pVMap[ nY + 1 ], nNextX ).GetIndex();
528 nGrey31 = nGrey32; nGrey32 = nGrey33; nGrey33 = pReadAcc->GetPixel( pVMap[ nY + 2 ], nNextX ).GetIndex();
533 delete[] pHMap;
534 delete[] pVMap;
535 pWriteAcc.reset();
536 bRet = true;
539 pReadAcc.reset();
541 if( bRet )
543 const MapMode aMap( maPrefMapMode );
544 const Size aSize( maPrefSize );
546 *this = aNewBmp;
548 maPrefMapMode = aMap;
549 maPrefSize = aSize;
554 return bRet;
557 bool Bitmap::ImplEmbossGrey( const BmpFilterParam* pFilterParam )
559 bool bRet = ImplMakeGreyscales( 256 );
561 if( bRet )
563 bRet = false;
565 ScopedReadAccess pReadAcc(*this);
567 if( pReadAcc )
569 Bitmap aNewBmp( GetSizePixel(), 8, &pReadAcc->GetPalette() );
570 ScopedWriteAccess pWriteAcc(aNewBmp);
572 if( pWriteAcc )
574 BitmapColor aGrey( (sal_uInt8) 0 );
575 const long nWidth = pWriteAcc->Width();
576 const long nHeight = pWriteAcc->Height();
577 long nGrey11, nGrey12, nGrey13;
578 long nGrey21, nGrey22, nGrey23;
579 long nGrey31, nGrey32, nGrey33;
580 double fAzim = ( ( pFilterParam && pFilterParam->meFilter == BmpFilter::EmbossGrey ) ?
581 ( pFilterParam->maEmbossAngles.mnAzimuthAngle100 * 0.01 ) : 0.0 ) * F_PI180;
582 double fElev = ( ( pFilterParam && pFilterParam->meFilter == BmpFilter::EmbossGrey ) ?
583 ( pFilterParam->maEmbossAngles.mnElevationAngle100 * 0.01 ) : 90.0 ) * F_PI180;
584 long* pHMap = new long[ nWidth + 2 ];
585 long* pVMap = new long[ nHeight + 2 ];
586 long nX, nY, nNx, nNy, nDotL;
587 const long nLx = FRound( cos( fAzim ) * cos( fElev ) * 255.0 );
588 const long nLy = FRound( sin( fAzim ) * cos( fElev ) * 255.0 );
589 const long nLz = FRound( sin( fElev ) * 255.0 );
590 const auto nZ2 = ( ( 6 * 255 ) / 4 ) * ( ( 6 * 255 ) / 4 );
591 const long nNzLz = ( ( 6 * 255 ) / 4 ) * nLz;
592 const sal_uInt8 cLz = (sal_uInt8) SAL_BOUND( nLz, 0, 255 );
594 // fill mapping tables
595 pHMap[ 0 ] = 0;
596 for( nX = 1; nX <= nWidth; nX++ )
597 pHMap[ nX ] = nX - 1;
598 pHMap[ nWidth + 1 ] = nWidth - 1;
600 pVMap[ 0 ] = 0;
601 for( nY = 1; nY <= nHeight; nY++ )
602 pVMap[ nY ] = nY - 1;
603 pVMap[ nHeight + 1 ] = nHeight - 1;
605 for( nY = 0; nY < nHeight ; nY++ )
607 nGrey11 = pReadAcc->GetPixel( pVMap[ nY ], pHMap[ 0 ] ).GetIndex();
608 nGrey12 = pReadAcc->GetPixel( pVMap[ nY ], pHMap[ 1 ] ).GetIndex();
609 nGrey13 = pReadAcc->GetPixel( pVMap[ nY ], pHMap[ 2 ] ).GetIndex();
610 nGrey21 = pReadAcc->GetPixel( pVMap[ nY + 1 ], pHMap[ 0 ] ).GetIndex();
611 nGrey22 = pReadAcc->GetPixel( pVMap[ nY + 1 ], pHMap[ 1 ] ).GetIndex();
612 nGrey23 = pReadAcc->GetPixel( pVMap[ nY + 1 ], pHMap[ 2 ] ).GetIndex();
613 nGrey31 = pReadAcc->GetPixel( pVMap[ nY + 2 ], pHMap[ 0 ] ).GetIndex();
614 nGrey32 = pReadAcc->GetPixel( pVMap[ nY + 2 ], pHMap[ 1 ] ).GetIndex();
615 nGrey33 = pReadAcc->GetPixel( pVMap[ nY + 2 ], pHMap[ 2 ] ).GetIndex();
617 for( nX = 0; nX < nWidth; nX++ )
619 nNx = nGrey11 + nGrey21 + nGrey31 - nGrey13 - nGrey23 - nGrey33;
620 nNy = nGrey31 + nGrey32 + nGrey33 - nGrey11 - nGrey12 - nGrey13;
622 if( !nNx && !nNy )
623 aGrey.SetIndex( cLz );
624 else if( ( nDotL = nNx * nLx + nNy * nLy +nNzLz ) < 0 )
625 aGrey.SetIndex( 0 );
626 else
628 const double fGrey = nDotL / sqrt( (double)(nNx * nNx + nNy * nNy + nZ2) );
629 aGrey.SetIndex( (sal_uInt8) SAL_BOUND( fGrey, 0, 255 ) );
632 pWriteAcc->SetPixel( nY, nX, aGrey );
634 if( nX < ( nWidth - 1 ) )
636 const long nNextX = pHMap[ nX + 3 ];
638 nGrey11 = nGrey12; nGrey12 = nGrey13; nGrey13 = pReadAcc->GetPixel( pVMap[ nY ], nNextX ).GetIndex();
639 nGrey21 = nGrey22; nGrey22 = nGrey23; nGrey23 = pReadAcc->GetPixel( pVMap[ nY + 1 ], nNextX ).GetIndex();
640 nGrey31 = nGrey32; nGrey32 = nGrey33; nGrey33 = pReadAcc->GetPixel( pVMap[ nY + 2 ], nNextX ).GetIndex();
645 delete[] pHMap;
646 delete[] pVMap;
647 pWriteAcc.reset();
648 bRet = true;
651 pReadAcc.reset();
653 if( bRet )
655 const MapMode aMap( maPrefMapMode );
656 const Size aSize( maPrefSize );
658 *this = aNewBmp;
660 maPrefMapMode = aMap;
661 maPrefSize = aSize;
666 return bRet;
669 bool Bitmap::ImplSolarize( const BmpFilterParam* pFilterParam )
671 bool bRet = false;
672 ScopedWriteAccess pWriteAcc(*this);
674 if( pWriteAcc )
676 const sal_uInt8 cThreshold = ( pFilterParam && pFilterParam->meFilter == BmpFilter::Solarize ) ?
677 pFilterParam->mcSolarGreyThreshold : 128;
679 if( pWriteAcc->HasPalette() )
681 const BitmapPalette& rPal = pWriteAcc->GetPalette();
683 for( sal_uInt16 i = 0, nCount = rPal.GetEntryCount(); i < nCount; i++ )
685 if( rPal[ i ].GetLuminance() >= cThreshold )
687 BitmapColor aCol( rPal[ i ] );
688 pWriteAcc->SetPaletteColor( i, aCol.Invert() );
692 else
694 BitmapColor aCol;
695 const long nWidth = pWriteAcc->Width();
696 const long nHeight = pWriteAcc->Height();
698 for( long nY = 0; nY < nHeight ; nY++ )
700 for( long nX = 0; nX < nWidth; nX++ )
702 aCol = pWriteAcc->GetPixel( nY, nX );
704 if( aCol.GetLuminance() >= cThreshold )
705 pWriteAcc->SetPixel( nY, nX, aCol.Invert() );
710 pWriteAcc.reset();
711 bRet = true;
714 return bRet;
717 bool Bitmap::ImplSepia( const BmpFilterParam* pFilterParam )
719 ScopedReadAccess pReadAcc(*this);
720 bool bRet = false;
722 if( pReadAcc )
724 long nSepiaPercent = ( pFilterParam && pFilterParam->meFilter == BmpFilter::Sepia ) ?
725 pFilterParam->mnSepiaPercent : 10;
726 const long nSepia = 10000 - 100 * SAL_BOUND( nSepiaPercent, 0, 100 );
727 BitmapPalette aSepiaPal( 256 );
729 for( sal_uInt16 i = 0; i < 256; i++ )
731 BitmapColor& rCol = aSepiaPal[ i ];
732 const sal_uInt8 cSepiaValue = (sal_uInt8) ( nSepia * i / 10000 );
734 rCol.SetRed( (sal_uInt8) i );
735 rCol.SetGreen( cSepiaValue );
736 rCol.SetBlue( cSepiaValue );
739 Bitmap aNewBmp( GetSizePixel(), 8, &aSepiaPal );
740 ScopedWriteAccess pWriteAcc(aNewBmp);
742 if( pWriteAcc )
744 BitmapColor aCol( (sal_uInt8) 0 );
745 const long nWidth = pWriteAcc->Width();
746 const long nHeight = pWriteAcc->Height();
748 if( pReadAcc->HasPalette() )
750 for( long nY = 0; nY < nHeight ; nY++ )
752 const sal_uInt16 nPalCount = pReadAcc->GetPaletteEntryCount();
753 std::unique_ptr<sal_uInt8[]> pIndexMap( new sal_uInt8[ nPalCount ] );
755 for( sal_uInt16 i = 0; i < nPalCount; i++ )
756 pIndexMap[ i ] = pReadAcc->GetPaletteColor( i ).GetLuminance();
758 for( long nX = 0; nX < nWidth; nX++ )
760 aCol.SetIndex( pIndexMap[ pReadAcc->GetPixel( nY, nX ).GetIndex() ] );
761 pWriteAcc->SetPixel( nY, nX, aCol );
765 else
767 for( long nY = 0; nY < nHeight ; nY++ )
769 for( long nX = 0; nX < nWidth; nX++ )
771 aCol.SetIndex( pReadAcc->GetPixel( nY, nX ).GetLuminance() );
772 pWriteAcc->SetPixel( nY, nX, aCol );
777 pWriteAcc.reset();
778 bRet = true;
781 pReadAcc.reset();
783 if( bRet )
785 const MapMode aMap( maPrefMapMode );
786 const Size aSize( maPrefSize );
788 *this = aNewBmp;
790 maPrefMapMode = aMap;
791 maPrefSize = aSize;
795 return bRet;
798 bool Bitmap::ImplMosaic( const BmpFilterParam* pFilterParam )
800 sal_uLong nTileWidth = ( pFilterParam && pFilterParam->meFilter == BmpFilter::Mosaic ) ?
801 pFilterParam->maMosaicTileSize.mnTileWidth : 4;
802 sal_uLong nTileHeight = ( pFilterParam && pFilterParam->meFilter == BmpFilter::Mosaic ) ?
803 pFilterParam->maMosaicTileSize.mnTileHeight : 4;
804 bool bRet = false;
806 if( !nTileWidth )
807 nTileWidth = 1;
809 if( !nTileHeight )
810 nTileHeight = 1;
812 if( nTileWidth > 1 || nTileHeight > 1 )
814 Bitmap* pNewBmp;
815 BitmapReadAccess* pReadAcc;
816 BitmapWriteAccess* pWriteAcc;
818 if( GetBitCount() > 8 )
820 pNewBmp = nullptr;
821 pReadAcc = pWriteAcc = AcquireWriteAccess();
823 else
825 pNewBmp = new Bitmap( GetSizePixel(), 24 );
826 pReadAcc = AcquireReadAccess();
827 pWriteAcc = pNewBmp->AcquireWriteAccess();
830 bool bConditionsMet = false;
831 long nWidth(0);
832 long nHeight(0);
833 if (pReadAcc && pWriteAcc)
835 nWidth = pReadAcc->Width();
836 nHeight = pReadAcc->Height();
837 bConditionsMet = (nWidth > 0 && nHeight > 0);
840 if (bConditionsMet)
842 BitmapColor aCol;
843 long nX, nY, nX1, nX2, nY1, nY2, nSumR, nSumG, nSumB;
844 double fArea_1;
846 nY1 = 0; nY2 = nTileHeight - 1;
848 if( nY2 >= nHeight )
849 nY2 = nHeight - 1;
853 nX1 = 0; nX2 = nTileWidth - 1;
855 if( nX2 >= nWidth )
856 nX2 = nWidth - 1;
858 fArea_1 = 1.0 / ( ( nX2 - nX1 + 1 ) * ( nY2 - nY1 + 1 ) );
860 if( !pNewBmp )
864 for( nY = nY1, nSumR = nSumG = nSumB = 0; nY <= nY2; nY++ )
866 for( nX = nX1; nX <= nX2; nX++ )
868 aCol = pReadAcc->GetPixel( nY, nX );
869 nSumR += aCol.GetRed();
870 nSumG += aCol.GetGreen();
871 nSumB += aCol.GetBlue();
875 aCol.SetRed( (sal_uInt8) ( nSumR * fArea_1 ) );
876 aCol.SetGreen( (sal_uInt8) ( nSumG * fArea_1 ) );
877 aCol.SetBlue( (sal_uInt8) ( nSumB * fArea_1 ) );
879 for( nY = nY1; nY <= nY2; nY++ )
880 for( nX = nX1; nX <= nX2; nX++ )
881 pWriteAcc->SetPixel( nY, nX, aCol );
883 nX1 += nTileWidth; nX2 += nTileWidth;
885 if( nX2 >= nWidth )
887 nX2 = nWidth - 1;
888 fArea_1 = 1.0 / ( ( nX2 - nX1 + 1 ) * ( nY2 - nY1 + 1 ) );
891 while( nX1 < nWidth );
893 else
897 for( nY = nY1, nSumR = nSumG = nSumB = 0; nY <= nY2; nY++ )
899 for( nX = nX1; nX <= nX2; nX++ )
901 const BitmapColor& rCol = pReadAcc->GetPaletteColor( pReadAcc->GetPixelIndex( nY, nX ) );
902 nSumR += rCol.GetRed();
903 nSumG += rCol.GetGreen();
904 nSumB += rCol.GetBlue();
908 aCol.SetRed( (sal_uInt8) ( nSumR * fArea_1 ) );
909 aCol.SetGreen( (sal_uInt8) ( nSumG * fArea_1 ) );
910 aCol.SetBlue( (sal_uInt8) ( nSumB * fArea_1 ) );
912 for( nY = nY1; nY <= nY2; nY++ )
913 for( nX = nX1; nX <= nX2; nX++ )
914 pWriteAcc->SetPixel( nY, nX, aCol );
916 nX1 += nTileWidth; nX2 += nTileWidth;
918 if( nX2 >= nWidth )
920 nX2 = nWidth - 1;
921 fArea_1 = 1.0 / ( ( nX2 - nX1 + 1 ) * ( nY2 - nY1 + 1 ) );
924 while( nX1 < nWidth );
927 nY1 += nTileHeight; nY2 += nTileHeight;
929 if( nY2 >= nHeight )
930 nY2 = nHeight - 1;
932 while( nY1 < nHeight );
934 bRet = true;
937 ReleaseAccess( pReadAcc );
939 if( pNewBmp )
941 Bitmap::ReleaseAccess( pWriteAcc );
943 if( bRet )
945 const MapMode aMap( maPrefMapMode );
946 const Size aSize( maPrefSize );
948 *this = *pNewBmp;
950 maPrefMapMode = aMap;
951 maPrefSize = aSize;
954 delete pNewBmp;
957 else
958 bRet = true;
960 return bRet;
964 struct PopArtEntry
966 sal_uInt32 mnIndex;
967 sal_uInt32 mnCount;
970 extern "C" int SAL_CALL ImplPopArtCmpFnc( const void* p1, const void* p2 )
972 int nRet;
974 if( static_cast<PopArtEntry const *>(p1)->mnCount < static_cast<PopArtEntry const *>(p2)->mnCount )
975 nRet = 1;
976 else if( static_cast<PopArtEntry const *>(p1)->mnCount == static_cast<PopArtEntry const *>(p2)->mnCount )
977 nRet = 0;
978 else
979 nRet = -1;
981 return nRet;
984 bool Bitmap::ImplPopArt()
986 /* note: GetBitCount() after that is no more than 8 */
987 bool bRet = ( GetBitCount() <= 8 ) || Convert( BmpConversion::N8BitColors );
989 if( bRet )
991 bRet = false;
993 ScopedWriteAccess pWriteAcc(*this);
995 if( pWriteAcc )
997 const long nWidth = pWriteAcc->Width();
998 const long nHeight = pWriteAcc->Height();
999 const int nEntryCount = 1 << pWriteAcc->GetBitCount();
1000 int n;
1001 PopArtEntry* pPopArtTable = new PopArtEntry[ nEntryCount ];
1003 for( n = 0; n < nEntryCount; n++ )
1005 PopArtEntry& rEntry = pPopArtTable[ n ];
1006 rEntry.mnIndex = (sal_uInt16) n;
1007 rEntry.mnCount = 0;
1010 // get pixel count for each palette entry
1011 for( long nY = 0; nY < nHeight ; nY++ )
1012 for( long nX = 0; nX < nWidth; nX++ )
1013 pPopArtTable[ pWriteAcc->GetPixel( nY, nX ).GetIndex() ].mnCount++;
1015 // sort table
1016 qsort( pPopArtTable, nEntryCount, sizeof( PopArtEntry ), ImplPopArtCmpFnc );
1018 // get last used entry
1019 sal_uLong nFirstEntry;
1020 sal_uLong nLastEntry = 0;
1022 for( n = 0; n < nEntryCount; n++ )
1023 if( pPopArtTable[ n ].mnCount )
1024 nLastEntry = n;
1026 // rotate palette (one entry)
1027 const BitmapColor aFirstCol( pWriteAcc->GetPaletteColor( sal::static_int_cast<sal_uInt16>(pPopArtTable[ 0 ].mnIndex) ) );
1028 for( nFirstEntry = 0; nFirstEntry < nLastEntry; nFirstEntry++ )
1030 pWriteAcc->SetPaletteColor( sal::static_int_cast<sal_uInt16>(pPopArtTable[ nFirstEntry ].mnIndex),
1031 pWriteAcc->GetPaletteColor( sal::static_int_cast<sal_uInt16>(pPopArtTable[ nFirstEntry + 1 ].mnIndex) ) );
1033 pWriteAcc->SetPaletteColor( sal::static_int_cast<sal_uInt16>(pPopArtTable[ nLastEntry ].mnIndex), aFirstCol );
1035 // cleanup
1036 delete[] pPopArtTable;
1037 pWriteAcc.reset();
1038 bRet = true;
1042 return bRet;
1045 double* MakeBlurKernel(const double radius, int& rows) {
1046 int intRadius = (int) radius + 1.0;
1047 rows = intRadius * 2 + 1;
1048 double* matrix = new double[rows];
1050 double sigma = radius / 3;
1051 double radius2 = radius * radius;
1052 int index = 0;
1053 for (int row = -intRadius; row <= intRadius; row++)
1055 double distance = row*row;
1056 if (distance > radius2) {
1057 matrix[index] = 0.0;
1058 }else {
1059 matrix[index] = exp( -distance / (2.0 * sigma * sigma) ) / sqrt( 2.0 * M_PI * sigma );
1061 index++;
1063 return matrix;
1066 void Bitmap::ImplBlurContributions( const int aSize, const int aNumberOfContributions,
1067 double* pBlurVector, double*& pWeights, int*& pPixels, int*& pCount )
1069 pWeights = new double[ aSize*aNumberOfContributions ];
1070 pPixels = new int[ aSize*aNumberOfContributions ];
1071 pCount = new int[ aSize ];
1073 int aLeft, aRight, aCurrentCount, aPixelIndex;
1074 double aWeight;
1076 for ( int i = 0; i < aSize; i++ )
1078 aLeft = i - aNumberOfContributions / 2;
1079 aRight = i + aNumberOfContributions / 2;
1080 aCurrentCount = 0;
1081 for ( int j = aLeft; j <= aRight; j++ )
1083 aWeight = pBlurVector[aCurrentCount];
1085 // Mirror edges
1086 if (j < 0)
1088 aPixelIndex = -j;
1090 else if ( j >= aSize )
1092 aPixelIndex = (aSize - j) + aSize - 1;
1094 else
1096 aPixelIndex = j;
1099 // Edge case for small bitmaps
1100 if ( aPixelIndex < 0 || aPixelIndex >= aSize )
1102 aWeight = 0.0;
1105 pWeights[ i*aNumberOfContributions + aCurrentCount ] = aWeight;
1106 pPixels[ i*aNumberOfContributions + aCurrentCount ] = aPixelIndex;
1108 aCurrentCount++;
1110 pCount[ i ] = aCurrentCount;
1114 // Separable Gaussian Blur
1116 // Separable Gaussian Blur filter and accepts a blur radius
1117 // as a parameter so the user can change the strength of the blur.
1118 // Radius of 1.0 is 3 * standard deviation of gauss function.
1120 // Separable Blur implementation uses 2x separable 1D convolution
1121 // to process the image.
1122 bool Bitmap::ImplSeparableBlurFilter(const double radius)
1124 const long nWidth = GetSizePixel().Width();
1125 const long nHeight = GetSizePixel().Height();
1127 // Prepare Blur Vector
1128 int aNumberOfContributions;
1129 double* pBlurVector = MakeBlurKernel(radius, aNumberOfContributions);
1131 double* pWeights;
1132 int* pPixels;
1133 int* pCount;
1135 // Do horizontal filtering
1136 ImplBlurContributions( nWidth, aNumberOfContributions, pBlurVector, pWeights, pPixels, pCount);
1138 ScopedReadAccess pReadAcc(*this);
1140 // switch coordinates as convolution pass transposes result
1141 Bitmap aNewBitmap( Size( nHeight, nWidth ), 24 );
1143 bool bResult = ImplConvolutionPass( aNewBitmap, pReadAcc.get(), aNumberOfContributions, pWeights, pPixels, pCount );
1145 // Cleanup
1146 pReadAcc.reset();
1147 delete[] pWeights;
1148 delete[] pPixels;
1149 delete[] pCount;
1151 if ( !bResult )
1153 delete[] pBlurVector;
1154 return bResult;
1157 // Swap current bitmap with new bitmap
1158 ImplAssignWithSize( aNewBitmap );
1160 // Do vertical filtering
1161 ImplBlurContributions(nHeight, aNumberOfContributions, pBlurVector, pWeights, pPixels, pCount );
1163 pReadAcc = ScopedReadAccess(*this);
1164 aNewBitmap = Bitmap( Size( nWidth, nHeight ), 24 );
1165 bResult = ImplConvolutionPass( aNewBitmap, pReadAcc.get(), aNumberOfContributions, pWeights, pPixels, pCount );
1167 // Cleanup
1168 pReadAcc.reset();
1169 delete[] pWeights;
1170 delete[] pCount;
1171 delete[] pPixels;
1172 delete[] pBlurVector;
1174 if ( !bResult )
1175 return bResult;
1177 // Swap current bitmap with new bitmap
1178 ImplAssignWithSize( aNewBitmap );
1180 return true;
1183 // Separable Unsharpen Mask filter is actually a subtracted blurred
1184 // image from the original image.
1185 bool Bitmap::ImplSeparableUnsharpenFilter(const double radius) {
1186 const long nWidth = GetSizePixel().Width();
1187 const long nHeight = GetSizePixel().Height();
1189 Bitmap aBlur( *this );
1190 aBlur.ImplSeparableBlurFilter(-radius);
1192 // Amount of unsharpening effect on image - currently set to a fixed value
1193 double aAmount = 2.0;
1195 Bitmap aResultBitmap( Size( nWidth, nHeight ), 24);
1197 ScopedReadAccess pReadAccBlur(aBlur);
1198 ScopedReadAccess pReadAcc(*this);
1199 ScopedWriteAccess pWriteAcc(aResultBitmap);
1201 BitmapColor aColor, aColorBlur;
1203 // For all pixels in original image subtract pixels values from blurred image
1204 for( long x = 0; x < nWidth; x++ )
1206 for( long y = 0; y < nHeight; y++ )
1208 aColorBlur = pReadAccBlur->GetColor( y , x );
1209 aColor = pReadAcc->GetColor( y , x );
1211 BitmapColor aResultColor(
1212 (sal_uInt8) MinMax( aColor.GetRed() + (aColor.GetRed() - aColorBlur.GetRed()) * aAmount, 0, 255 ),
1213 (sal_uInt8) MinMax( aColor.GetGreen() + (aColor.GetGreen() - aColorBlur.GetGreen()) * aAmount, 0, 255 ),
1214 (sal_uInt8) MinMax( aColor.GetBlue() + (aColor.GetBlue() - aColorBlur.GetBlue()) * aAmount, 0, 255 ) );
1216 pWriteAcc->SetPixel( y, x, aResultColor );
1220 pWriteAcc.reset();
1221 pReadAcc.reset();
1222 pReadAccBlur.reset();
1223 ImplAssignWithSize ( aResultBitmap );
1224 return true;
1227 bool Bitmap::ImplDuotoneFilter( const sal_uLong nColorOne, const sal_uLong nColorTwo )
1229 const long nWidth = GetSizePixel().Width();
1230 const long nHeight = GetSizePixel().Height();
1232 Bitmap aResultBitmap( GetSizePixel(), 24);
1233 ScopedReadAccess pReadAcc(*this);
1234 ScopedWriteAccess pWriteAcc(aResultBitmap);
1235 const BitmapColor aColorOne( static_cast< sal_uInt8 >( nColorOne >> 16 ), static_cast< sal_uInt8 >( nColorOne >> 8 ), static_cast< sal_uInt8 >( nColorOne ) );
1236 const BitmapColor aColorTwo( static_cast< sal_uInt8 >( nColorTwo >> 16 ), static_cast< sal_uInt8 >( nColorTwo >> 8 ), static_cast< sal_uInt8 >( nColorTwo ) );
1238 for( long x = 0; x < nWidth; x++ )
1240 for( long y = 0; y < nHeight; y++ )
1242 BitmapColor aColor = pReadAcc->GetColor( y, x );
1243 sal_uInt8 luminance = aColor.GetLuminance();
1244 BitmapColor aResultColor(
1245 lcl_getDuotoneColorComponent( luminance, aColorOne.GetRed(), aColorTwo.GetRed() ) ,
1246 lcl_getDuotoneColorComponent( luminance, aColorOne.GetGreen(), aColorTwo.GetGreen() ) ,
1247 lcl_getDuotoneColorComponent( luminance, aColorOne.GetBlue(), aColorTwo.GetBlue() ) );
1248 pWriteAcc->SetPixel( y, x, aResultColor );
1252 pWriteAcc.reset();
1253 pReadAcc.reset();
1254 ImplAssignWithSize ( aResultBitmap );
1255 return true;
1258 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */