1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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 .
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
)
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
);
67 case BmpFilter::Sharpen
:
69 const long pSharpenMatrix
[] = { -1, -1, -1, -1, 16, -1, -1, -1, -1 };
70 bRet
= ImplConvolute3( &pSharpenMatrix
[ 0 ] );
74 case BmpFilter::RemoveNoise
:
75 bRet
= ImplMedianFilter();
78 case BmpFilter::SobelGrey
:
79 bRet
= ImplSobelGrey();
82 case BmpFilter::Solarize
:
83 bRet
= ImplSolarize( pFilterParam
);
86 case BmpFilter::Sepia
:
87 bRet
= ImplSepia( pFilterParam
);
90 case BmpFilter::Mosaic
:
91 bRet
= ImplMosaic( pFilterParam
);
94 case BmpFilter::EmbossGrey
:
95 bRet
= ImplEmbossGrey( pFilterParam
);
98 case BmpFilter::PopArt
:
102 case BmpFilter::DuoTone
:
103 bRet
= ImplDuotoneFilter( pFilterParam
->mnProgressStart
, pFilterParam
->mnProgressEnd
);
107 OSL_FAIL( "Bitmap::Convert(): Unsupported filter" );
114 bool Bitmap::ImplConvolute3( const long* pMatrix
)
116 const long nDivisor
= 8;
117 ScopedReadAccess
pReadAcc(*this);
122 Bitmap
aNewBmp( GetSizePixel(), 24 );
123 ScopedWriteAccess
pWriteAcc(aNewBmp
);
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
;
138 long nY
, nX
, i
, nSumR
, nSumG
, nSumB
, nMatrixVal
, nTmp
;
139 long (*pKoeff
)[ 256 ] = new long[ 9 ][ 256 ];
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
;
148 for( i
= 0; i
< nWidth2
; i
++ )
149 pColm
[ i
] = ( i
> 0 ) ? ( i
- 1 ) : 0;
151 pColm
[ nWidth
+ 1 ] = pColm
[ nWidth
];
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
] );
168 for( nY
= 0; nY
< nHeight
; )
170 for( nX
= 0; nX
< nWidth
; nX
++ )
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() ];
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() ];
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 ) ) );
219 if( pRowTmp1
== pColRow1
)
225 else if( pRowTmp1
== pColRow2
)
238 for( i
= 0; i
< nWidth2
; i
++ )
239 pRowTmp3
[ i
] = pReadAcc
->GetColor( pRows
[ nY
+ 2 ], pColm
[ i
] );
244 delete[] reinterpret_cast<sal_uInt8
*>(pColRow1
);
245 delete[] reinterpret_cast<sal_uInt8
*>(pColRow2
);
246 delete[] reinterpret_cast<sal_uInt8
*>(pColRow3
);
259 const MapMode
aMap( maPrefMapMode
);
260 const Size
aSize( maPrefSize
);
264 maPrefMapMode
= aMap
;
272 bool Bitmap::ImplMedianFilter()
274 ScopedReadAccess
pReadAcc(*this);
279 Bitmap
aNewBmp( GetSizePixel(), 24 );
280 ScopedWriteAccess
pWriteAcc(aNewBmp
);
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
;
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
;
301 for( i
= 0; i
< nWidth2
; i
++ )
302 pColm
[ i
] = ( i
> 0 ) ? ( i
- 1 ) : 0;
304 pColm
[ nWidth
+ 1 ] = pColm
[ nWidth
];
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
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
) );
379 if( pRowTmp1
== pColRow1
)
385 else if( pRowTmp1
== pColRow2
)
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
);
418 const MapMode
aMap( maPrefMapMode
);
419 const Size
aSize( maPrefSize
);
423 maPrefMapMode
= aMap
;
431 bool Bitmap::ImplSobelGrey()
433 bool bRet
= ImplMakeGreyscales( 256 );
439 ScopedReadAccess
pReadAcc(*this);
443 Bitmap
aNewBmp( GetSizePixel(), 8, &pReadAcc
->GetPalette() );
444 ScopedWriteAccess
pWriteAcc(aNewBmp
);
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
466 for( nX
= 1; nX
<= nWidth
; nX
++ )
467 pHMap
[ nX
] = nX
- 1;
468 pHMap
[ nWidth
+ 1 ] = nWidth
- 1;
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
++ )
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();
543 const MapMode
aMap( maPrefMapMode
);
544 const Size
aSize( maPrefSize
);
548 maPrefMapMode
= aMap
;
557 bool Bitmap::ImplEmbossGrey( const BmpFilterParam
* pFilterParam
)
559 bool bRet
= ImplMakeGreyscales( 256 );
565 ScopedReadAccess
pReadAcc(*this);
569 Bitmap
aNewBmp( GetSizePixel(), 8, &pReadAcc
->GetPalette() );
570 ScopedWriteAccess
pWriteAcc(aNewBmp
);
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
596 for( nX
= 1; nX
<= nWidth
; nX
++ )
597 pHMap
[ nX
] = nX
- 1;
598 pHMap
[ nWidth
+ 1 ] = nWidth
- 1;
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
;
623 aGrey
.SetIndex( cLz
);
624 else if( ( nDotL
= nNx
* nLx
+ nNy
* nLy
+nNzLz
) < 0 )
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();
655 const MapMode
aMap( maPrefMapMode
);
656 const Size
aSize( maPrefSize
);
660 maPrefMapMode
= aMap
;
669 bool Bitmap::ImplSolarize( const BmpFilterParam
* pFilterParam
)
672 ScopedWriteAccess
pWriteAcc(*this);
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() );
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() );
717 bool Bitmap::ImplSepia( const BmpFilterParam
* pFilterParam
)
719 ScopedReadAccess
pReadAcc(*this);
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
);
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
);
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
);
785 const MapMode
aMap( maPrefMapMode
);
786 const Size
aSize( maPrefSize
);
790 maPrefMapMode
= aMap
;
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;
812 if( nTileWidth
> 1 || nTileHeight
> 1 )
815 BitmapReadAccess
* pReadAcc
;
816 BitmapWriteAccess
* pWriteAcc
;
818 if( GetBitCount() > 8 )
821 pReadAcc
= pWriteAcc
= AcquireWriteAccess();
825 pNewBmp
= new Bitmap( GetSizePixel(), 24 );
826 pReadAcc
= AcquireReadAccess();
827 pWriteAcc
= pNewBmp
->AcquireWriteAccess();
830 bool bConditionsMet
= false;
833 if (pReadAcc
&& pWriteAcc
)
835 nWidth
= pReadAcc
->Width();
836 nHeight
= pReadAcc
->Height();
837 bConditionsMet
= (nWidth
> 0 && nHeight
> 0);
843 long nX
, nY
, nX1
, nX2
, nY1
, nY2
, nSumR
, nSumG
, nSumB
;
846 nY1
= 0; nY2
= nTileHeight
- 1;
853 nX1
= 0; nX2
= nTileWidth
- 1;
858 fArea_1
= 1.0 / ( ( nX2
- nX1
+ 1 ) * ( nY2
- nY1
+ 1 ) );
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
;
888 fArea_1
= 1.0 / ( ( nX2
- nX1
+ 1 ) * ( nY2
- nY1
+ 1 ) );
891 while( nX1
< nWidth
);
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
;
921 fArea_1
= 1.0 / ( ( nX2
- nX1
+ 1 ) * ( nY2
- nY1
+ 1 ) );
924 while( nX1
< nWidth
);
927 nY1
+= nTileHeight
; nY2
+= nTileHeight
;
932 while( nY1
< nHeight
);
937 ReleaseAccess( pReadAcc
);
941 Bitmap::ReleaseAccess( pWriteAcc
);
945 const MapMode
aMap( maPrefMapMode
);
946 const Size
aSize( maPrefSize
);
950 maPrefMapMode
= aMap
;
970 extern "C" int SAL_CALL
ImplPopArtCmpFnc( const void* p1
, const void* p2
)
974 if( static_cast<PopArtEntry
const *>(p1
)->mnCount
< static_cast<PopArtEntry
const *>(p2
)->mnCount
)
976 else if( static_cast<PopArtEntry
const *>(p1
)->mnCount
== static_cast<PopArtEntry
const *>(p2
)->mnCount
)
984 bool Bitmap::ImplPopArt()
986 /* note: GetBitCount() after that is no more than 8 */
987 bool bRet
= ( GetBitCount() <= 8 ) || Convert( BmpConversion::N8BitColors
);
993 ScopedWriteAccess
pWriteAcc(*this);
997 const long nWidth
= pWriteAcc
->Width();
998 const long nHeight
= pWriteAcc
->Height();
999 const int nEntryCount
= 1 << pWriteAcc
->GetBitCount();
1001 PopArtEntry
* pPopArtTable
= new PopArtEntry
[ nEntryCount
];
1003 for( n
= 0; n
< nEntryCount
; n
++ )
1005 PopArtEntry
& rEntry
= pPopArtTable
[ n
];
1006 rEntry
.mnIndex
= (sal_uInt16
) n
;
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
++;
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
)
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
);
1036 delete[] pPopArtTable
;
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
;
1053 for (int row
= -intRadius
; row
<= intRadius
; row
++)
1055 double distance
= row
*row
;
1056 if (distance
> radius2
) {
1057 matrix
[index
] = 0.0;
1059 matrix
[index
] = exp( -distance
/ (2.0 * sigma
* sigma
) ) / sqrt( 2.0 * M_PI
* sigma
);
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
;
1076 for ( int i
= 0; i
< aSize
; i
++ )
1078 aLeft
= i
- aNumberOfContributions
/ 2;
1079 aRight
= i
+ aNumberOfContributions
/ 2;
1081 for ( int j
= aLeft
; j
<= aRight
; j
++ )
1083 aWeight
= pBlurVector
[aCurrentCount
];
1090 else if ( j
>= aSize
)
1092 aPixelIndex
= (aSize
- j
) + aSize
- 1;
1099 // Edge case for small bitmaps
1100 if ( aPixelIndex
< 0 || aPixelIndex
>= aSize
)
1105 pWeights
[ i
*aNumberOfContributions
+ aCurrentCount
] = aWeight
;
1106 pPixels
[ i
*aNumberOfContributions
+ aCurrentCount
] = aPixelIndex
;
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
);
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
);
1153 delete[] pBlurVector
;
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
);
1172 delete[] pBlurVector
;
1177 // Swap current bitmap with new bitmap
1178 ImplAssignWithSize( aNewBitmap
);
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
);
1222 pReadAccBlur
.reset();
1223 ImplAssignWithSize ( aResultBitmap
);
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
);
1254 ImplAssignWithSize ( aResultBitmap
);
1258 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */