1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
5  * This Source Code Form is subject to the terms of the Mozilla Public
6  * License, v. 2.0. If a copy of the MPL was not distributed with this
7  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
11  *   Licensed to the Apache Software Foundation (ASF) under one or more
12  *   contributor license agreements. See the NOTICE file distributed
13  *   with this work for additional information regarding copyright
14  *   ownership. The ASF licenses this file to you under the Apache
15  *   License, Version 2.0 (the "License"); you may not use this file
16  *   except in compliance with the License. You may obtain a copy of
17  *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
18  */
19 
20 #include <sal/log.hxx>
21 #include <o3tl/underlyingenumvalue.hxx>
22 #include <osl/diagnose.h>
23 #include <basegfx/matrix/b2dhommatrixtools.hxx>
24 #include <basegfx/color/bcolormodifier.hxx>
25 
26 #include <vcl/ImageTree.hxx>
27 #include <vcl/outdev.hxx>
28 #include <vcl/alpha.hxx>
29 #include <vcl/bitmapex.hxx>
30 #include <vcl/svapp.hxx>
31 #include <vcl/bitmapaccess.hxx>
32 #include <vcl/virdev.hxx>
33 #include <vcl/settings.hxx>
34 #include <vcl/BitmapMonochromeFilter.hxx>
35 
36 // BitmapEx::Create
37 #include <salbmp.hxx>
38 #include <salinst.hxx>
39 #include <svdata.hxx>
40 #include <bitmapwriteaccess.hxx>
41 
42 #include <o3tl/any.hxx>
43 
44 #include <com/sun/star/beans/XFastPropertySet.hpp>
45 
46 #include <memory>
47 
48 using namespace ::com::sun::star;
49 
BitmapEx()50 BitmapEx::BitmapEx()
51     : meTransparent(TransparentType::NONE)
52     , mbAlpha(false)
53 {
54 }
55 
56 BitmapEx::BitmapEx( const BitmapEx& ) = default;
57 
BitmapEx(const BitmapEx & rBitmapEx,Point aSrc,Size aSize)58 BitmapEx::BitmapEx( const BitmapEx& rBitmapEx, Point aSrc, Size aSize )
59     : meTransparent(TransparentType::NONE)
60     , mbAlpha(false)
61 {
62     if( rBitmapEx.IsEmpty() )
63         return;
64 
65     maBitmap = Bitmap( aSize, rBitmapEx.maBitmap.GetBitCount() );
66     SetSizePixel(aSize);
67     if( rBitmapEx.IsAlpha() )
68     {
69         mbAlpha = true;
70         maMask = AlphaMask( aSize ).ImplGetBitmap();
71     }
72     else if( rBitmapEx.IsTransparent() )
73         maMask = Bitmap( aSize, rBitmapEx.maMask.GetBitCount() );
74 
75     tools::Rectangle aDestRect( Point( 0, 0 ), aSize );
76     tools::Rectangle aSrcRect( aSrc, aSize );
77     CopyPixel( aDestRect, aSrcRect, &rBitmapEx );
78 }
79 
BitmapEx(Size aSize,sal_uInt16 nBitCount)80 BitmapEx::BitmapEx( Size aSize, sal_uInt16 nBitCount )
81     : meTransparent(TransparentType::NONE)
82     , mbAlpha(false)
83 {
84     maBitmap = Bitmap( aSize, nBitCount );
85     SetSizePixel(aSize);
86 }
87 
BitmapEx(const OUString & rIconName)88 BitmapEx::BitmapEx( const OUString& rIconName )
89     : meTransparent(TransparentType::NONE)
90     , mbAlpha(false)
91 {
92     loadFromIconTheme( rIconName );
93 }
94 
loadFromIconTheme(const OUString & rIconName)95 void BitmapEx::loadFromIconTheme( const OUString& rIconName )
96 {
97     bool bSuccess;
98     OUString aIconTheme;
99 
100     try
101     {
102         aIconTheme = Application::GetSettings().GetStyleSettings().DetermineIconTheme();
103         bSuccess = ImageTree::get().loadImage(rIconName, aIconTheme, *this, true);
104     }
105     catch (...)
106     {
107         bSuccess = false;
108     }
109 
110     SAL_WARN_IF( !bSuccess, "vcl", "BitmapEx::BitmapEx(): could not load image " << rIconName << " via icon theme " << aIconTheme);
111 }
112 
BitmapEx(const Bitmap & rBmp)113 BitmapEx::BitmapEx( const Bitmap& rBmp ) :
114         maBitmap     ( rBmp ),
115         maBitmapSize ( maBitmap.GetSizePixel() ),
116         meTransparent( TransparentType::NONE ),
117         mbAlpha      ( false )
118 {
119 }
120 
BitmapEx(const Bitmap & rBmp,const Bitmap & rMask)121 BitmapEx::BitmapEx( const Bitmap& rBmp, const Bitmap& rMask ) :
122         maBitmap         ( rBmp ),
123         maMask           ( rMask ),
124         maBitmapSize     ( maBitmap.GetSizePixel() ),
125         meTransparent    ( !rMask ? TransparentType::NONE : TransparentType::Bitmap ),
126         mbAlpha          ( false )
127 {
128     // Ensure a mask is exactly one bit deep
129     if( !!maMask && maMask.GetBitCount() != 1 )
130     {
131         SAL_WARN( "vcl", "BitmapEx: forced mask to monochrome");
132         BitmapEx aMaskEx(maMask);
133         BitmapFilter::Filter(aMaskEx, BitmapMonochromeFilter(255));
134         maMask = aMaskEx.GetBitmap();
135     }
136 
137     if (!!maBitmap && !!maMask && maBitmap.GetSizePixel() != maMask.GetSizePixel())
138     {
139         OSL_ENSURE(false, "Mask size differs from Bitmap size, corrected Mask (!)");
140         maMask.Scale(maBitmap.GetSizePixel());
141     }
142 }
143 
BitmapEx(const Bitmap & rBmp,const AlphaMask & rAlphaMask)144 BitmapEx::BitmapEx( const Bitmap& rBmp, const AlphaMask& rAlphaMask ) :
145         maBitmap         ( rBmp ),
146         maMask           ( rAlphaMask.ImplGetBitmap() ),
147         maBitmapSize     ( maBitmap.GetSizePixel() ),
148         meTransparent    ( !rAlphaMask ? TransparentType::NONE : TransparentType::Bitmap ),
149         mbAlpha          ( !rAlphaMask.IsEmpty() )
150 {
151     if (!!maBitmap && !!maMask && maBitmap.GetSizePixel() != maMask.GetSizePixel())
152     {
153         OSL_ENSURE(false, "Alpha size differs from Bitmap size, corrected Mask (!)");
154         maMask.Scale(rBmp.GetSizePixel());
155     }
156 
157     // #i75531# the workaround below can go when
158     // X11SalGraphics::drawAlphaBitmap()'s render acceleration
159     // can handle the bitmap depth mismatch directly
160     if( maBitmap.GetBitCount() < maMask.GetBitCount() )
161         maBitmap.Convert( BmpConversion::N24Bit );
162 }
163 
BitmapEx(const Bitmap & rBmp,const Color & rTransparentColor)164 BitmapEx::BitmapEx( const Bitmap& rBmp, const Color& rTransparentColor ) :
165         maBitmap             ( rBmp ),
166         maBitmapSize         ( maBitmap.GetSizePixel() ),
167         maTransparentColor   ( rTransparentColor ),
168         meTransparent        ( TransparentType::Bitmap ),
169         mbAlpha              ( false )
170 {
171     maMask = maBitmap.CreateMask( maTransparentColor );
172 
173     SAL_WARN_IF(rBmp.GetSizePixel() != maMask.GetSizePixel(), "vcl",
174                 "BitmapEx::BitmapEx(): size mismatch for bitmap and alpha mask.");
175 }
176 
177 BitmapEx& BitmapEx::operator=( const BitmapEx& ) = default;
178 
operator ==(const BitmapEx & rBitmapEx) const179 bool BitmapEx::operator==( const BitmapEx& rBitmapEx ) const
180 {
181     if (meTransparent != rBitmapEx.meTransparent)
182         return false;
183 
184     if (GetSizePixel() != rBitmapEx.GetSizePixel())
185         return false;
186 
187     if (meTransparent != rBitmapEx.meTransparent)
188         return false;
189 
190     if (meTransparent == TransparentType::Color
191         && maTransparentColor != rBitmapEx.maTransparentColor)
192         return false;
193 
194     if (mbAlpha != rBitmapEx.mbAlpha)
195         return false;
196 
197     if (maBitmap != rBitmapEx.maBitmap)
198         return false;
199 
200     return maMask == rBitmapEx.maMask;
201 }
202 
IsEmpty() const203 bool BitmapEx::IsEmpty() const
204 {
205     return( maBitmap.IsEmpty() && maMask.IsEmpty() );
206 }
207 
SetEmpty()208 void BitmapEx::SetEmpty()
209 {
210     maBitmap.SetEmpty();
211     maMask.SetEmpty();
212     meTransparent = TransparentType::NONE;
213     mbAlpha = false;
214 }
215 
Clear()216 void BitmapEx::Clear()
217 {
218     SetEmpty();
219 }
220 
IsTransparent() const221 bool BitmapEx::IsTransparent() const
222 {
223     return( meTransparent != TransparentType::NONE );
224 }
225 
IsAlpha() const226 bool BitmapEx::IsAlpha() const
227 {
228     return( IsTransparent() && mbAlpha );
229 }
230 
GetBitmapRef() const231 const Bitmap& BitmapEx::GetBitmapRef() const
232 {
233     return maBitmap;
234 }
235 
GetBitmap(const Color * pTransReplaceColor) const236 Bitmap BitmapEx::GetBitmap( const Color* pTransReplaceColor ) const
237 {
238     Bitmap aRetBmp( maBitmap );
239 
240     if( pTransReplaceColor && ( meTransparent != TransparentType::NONE ) )
241     {
242         Bitmap aTempMask;
243 
244         if( meTransparent == TransparentType::Color )
245             aTempMask = maBitmap.CreateMask( maTransparentColor );
246         else
247             aTempMask = maMask;
248 
249         if( !IsAlpha() )
250             aRetBmp.Replace( aTempMask, *pTransReplaceColor );
251         else
252             aRetBmp.Replace( GetAlpha(), *pTransReplaceColor );
253     }
254 
255     return aRetBmp;
256 }
257 
GetMask() const258 Bitmap BitmapEx::GetMask() const
259 {
260     if (!IsAlpha())
261         return maMask;
262 
263     BitmapEx aMaskEx(maMask);
264     BitmapFilter::Filter(aMaskEx, BitmapMonochromeFilter(255));
265     return aMaskEx.GetBitmap();
266 }
267 
GetAlpha() const268 AlphaMask BitmapEx::GetAlpha() const
269 {
270     if( IsAlpha() )
271     {
272         AlphaMask aAlpha;
273         aAlpha.ImplSetBitmap( maMask );
274         return aAlpha;
275     }
276     else
277     {
278         return AlphaMask(maMask);
279     }
280 }
281 
GetSizeBytes() const282 sal_uLong BitmapEx::GetSizeBytes() const
283 {
284     sal_uLong nSizeBytes = maBitmap.GetSizeBytes();
285 
286     if( meTransparent == TransparentType::Bitmap )
287         nSizeBytes += maMask.GetSizeBytes();
288 
289     return nSizeBytes;
290 }
291 
GetChecksum() const292 BitmapChecksum BitmapEx::GetChecksum() const
293 {
294     BitmapChecksum  nCrc = maBitmap.GetChecksum();
295     SVBT32      aBT32;
296     BitmapChecksumOctetArray aBCOA;
297 
298     UInt32ToSVBT32( o3tl::underlyingEnumValue(meTransparent), aBT32 );
299     nCrc = vcl_get_checksum( nCrc, aBT32, 4 );
300 
301     UInt32ToSVBT32( sal_uInt32(mbAlpha), aBT32 );
302     nCrc = vcl_get_checksum( nCrc, aBT32, 4 );
303 
304     if( ( TransparentType::Bitmap == meTransparent ) && !maMask.IsEmpty() )
305     {
306         BCToBCOA( maMask.GetChecksum(), aBCOA );
307         nCrc = vcl_get_checksum( nCrc, aBCOA, BITMAP_CHECKSUM_SIZE );
308     }
309 
310     return nCrc;
311 }
312 
SetSizePixel(const Size & rNewSize)313 void BitmapEx::SetSizePixel(const Size& rNewSize)
314 {
315     maBitmapSize = rNewSize;
316 }
317 
Invert()318 bool BitmapEx::Invert()
319 {
320     bool bRet = false;
321 
322     if (!!maBitmap)
323     {
324         bRet = maBitmap.Invert();
325 
326         if (bRet && (meTransparent == TransparentType::Color))
327             maTransparentColor.Invert();
328     }
329 
330     return bRet;
331 }
332 
Mirror(BmpMirrorFlags nMirrorFlags)333 bool BitmapEx::Mirror( BmpMirrorFlags nMirrorFlags )
334 {
335     bool bRet = false;
336 
337     if( !!maBitmap )
338     {
339         bRet = maBitmap.Mirror( nMirrorFlags );
340 
341         if( bRet && ( meTransparent == TransparentType::Bitmap ) && !!maMask )
342             maMask.Mirror( nMirrorFlags );
343     }
344 
345     return bRet;
346 }
347 
Scale(const double & rScaleX,const double & rScaleY,BmpScaleFlag nScaleFlag)348 bool BitmapEx::Scale( const double& rScaleX, const double& rScaleY, BmpScaleFlag nScaleFlag )
349 {
350     bool bRet = false;
351 
352     if( !!maBitmap )
353     {
354         bRet = maBitmap.Scale( rScaleX, rScaleY, nScaleFlag );
355 
356         if( bRet && ( meTransparent == TransparentType::Bitmap ) && !!maMask )
357         {
358             maMask.Scale( rScaleX, rScaleY, nScaleFlag );
359         }
360 
361         SetSizePixel(maBitmap.GetSizePixel());
362 
363         SAL_WARN_IF( !!maMask && maBitmap.GetSizePixel() != maMask.GetSizePixel(), "vcl",
364                     "BitmapEx::Scale(): size mismatch for bitmap and alpha mask." );
365     }
366 
367     return bRet;
368 }
369 
Scale(const Size & rNewSize,BmpScaleFlag nScaleFlag)370 bool BitmapEx::Scale( const Size& rNewSize, BmpScaleFlag nScaleFlag )
371 {
372     bool bRet;
373 
374     if (GetSizePixel().Width() && GetSizePixel().Height()
375             && (rNewSize.Width()  != GetSizePixel().Width()
376                     || rNewSize.Height() != GetSizePixel().Height() ) )
377     {
378         bRet = Scale( static_cast<double>(rNewSize.Width()) / GetSizePixel().Width(),
379                       static_cast<double>(rNewSize.Height()) / GetSizePixel().Height(),
380                       nScaleFlag );
381     }
382     else
383     {
384         bRet = true;
385     }
386 
387     return bRet;
388 }
389 
Rotate(long nAngle10,const Color & rFillColor)390 bool BitmapEx::Rotate( long nAngle10, const Color& rFillColor )
391 {
392     bool bRet = false;
393 
394     if( !!maBitmap )
395     {
396         const bool bTransRotate = ( COL_TRANSPARENT == rFillColor );
397 
398         if( bTransRotate )
399         {
400             if( meTransparent == TransparentType::Color )
401                 bRet = maBitmap.Rotate( nAngle10, maTransparentColor );
402             else
403             {
404                 bRet = maBitmap.Rotate( nAngle10, COL_BLACK );
405 
406                 if( meTransparent == TransparentType::NONE )
407                 {
408                     maMask = Bitmap(GetSizePixel(), 1);
409                     maMask.Erase( COL_BLACK );
410                     meTransparent = TransparentType::Bitmap;
411                 }
412 
413                 if( bRet && !!maMask )
414                     maMask.Rotate( nAngle10, COL_WHITE );
415             }
416         }
417         else
418         {
419             bRet = maBitmap.Rotate( nAngle10, rFillColor );
420 
421             if( bRet && ( meTransparent == TransparentType::Bitmap ) && !!maMask )
422                 maMask.Rotate( nAngle10, COL_WHITE );
423         }
424 
425         SetSizePixel(maBitmap.GetSizePixel());
426 
427         SAL_WARN_IF(!!maMask && maBitmap.GetSizePixel() != maMask.GetSizePixel(), "vcl",
428                     "BitmapEx::Rotate(): size mismatch for bitmap and alpha mask.");
429     }
430 
431     return bRet;
432 }
433 
Crop(const tools::Rectangle & rRectPixel)434 bool BitmapEx::Crop( const tools::Rectangle& rRectPixel )
435 {
436     bool bRet = false;
437 
438     if( !!maBitmap )
439     {
440         bRet = maBitmap.Crop( rRectPixel );
441 
442         if( bRet && ( meTransparent == TransparentType::Bitmap ) && !!maMask )
443             maMask.Crop( rRectPixel );
444 
445         SetSizePixel(maBitmap.GetSizePixel());
446 
447         SAL_WARN_IF(!!maMask && maBitmap.GetSizePixel() != maMask.GetSizePixel(), "vcl",
448                     "BitmapEx::Crop(): size mismatch for bitmap and alpha mask.");
449     }
450 
451     return bRet;
452 }
453 
Convert(BmpConversion eConversion)454 bool BitmapEx::Convert( BmpConversion eConversion )
455 {
456     return !!maBitmap && maBitmap.Convert( eConversion );
457 }
458 
Expand(sal_uLong nDX,sal_uLong nDY,bool bExpandTransparent)459 void BitmapEx::Expand( sal_uLong nDX, sal_uLong nDY, bool bExpandTransparent )
460 {
461     bool bRet = false;
462 
463     if( !!maBitmap )
464     {
465         bRet = maBitmap.Expand( nDX, nDY );
466 
467         if( bRet && ( meTransparent == TransparentType::Bitmap ) && !!maMask )
468         {
469             Color aColor( bExpandTransparent ? COL_WHITE : COL_BLACK );
470             maMask.Expand( nDX, nDY, &aColor );
471         }
472 
473         SetSizePixel(maBitmap.GetSizePixel());
474 
475         SAL_WARN_IF(!!maMask && maBitmap.GetSizePixel() != maMask.GetSizePixel(), "vcl",
476                     "BitmapEx::Expand(): size mismatch for bitmap and alpha mask.");
477     }
478 }
479 
CopyPixel(const tools::Rectangle & rRectDst,const tools::Rectangle & rRectSrc,const BitmapEx * pBmpExSrc)480 bool BitmapEx::CopyPixel( const tools::Rectangle& rRectDst, const tools::Rectangle& rRectSrc,
481                           const BitmapEx* pBmpExSrc )
482 {
483     bool bRet = false;
484 
485     if( !pBmpExSrc || pBmpExSrc->IsEmpty() )
486     {
487         if( !maBitmap.IsEmpty() )
488         {
489             bRet = maBitmap.CopyPixel( rRectDst, rRectSrc );
490 
491             if( bRet && ( meTransparent == TransparentType::Bitmap ) && !!maMask )
492                 maMask.CopyPixel( rRectDst, rRectSrc );
493         }
494     }
495     else
496     {
497         if( !maBitmap.IsEmpty() )
498         {
499             bRet = maBitmap.CopyPixel( rRectDst, rRectSrc, &pBmpExSrc->maBitmap );
500 
501             if( bRet )
502             {
503                 if( pBmpExSrc->IsAlpha() )
504                 {
505                     if( IsAlpha() )
506                         // cast to use the optimized AlphaMask::CopyPixel
507                         maMask.CopyPixel_AlphaOptimized( rRectDst, rRectSrc, &pBmpExSrc->maMask );
508                     else if( IsTransparent() )
509                     {
510                         std::unique_ptr<AlphaMask> pAlpha(new AlphaMask( maMask ));
511 
512                         maMask = pAlpha->ImplGetBitmap();
513                         pAlpha.reset();
514                         mbAlpha = true;
515                         maMask.CopyPixel( rRectDst, rRectSrc, &pBmpExSrc->maMask );
516                     }
517                     else
518                     {
519                         sal_uInt8 cBlack = 0;
520                         std::unique_ptr<AlphaMask> pAlpha(new AlphaMask(GetSizePixel(), &cBlack));
521 
522                         maMask = pAlpha->ImplGetBitmap();
523                         pAlpha.reset();
524                         meTransparent = TransparentType::Bitmap;
525                         mbAlpha = true;
526                         maMask.CopyPixel( rRectDst, rRectSrc, &pBmpExSrc->maMask );
527                     }
528                 }
529                 else if( pBmpExSrc->IsTransparent() )
530                 {
531                     if (IsAlpha())
532                     {
533                         AlphaMask aAlpha( pBmpExSrc->maMask );
534                         maMask.CopyPixel( rRectDst, rRectSrc, &aAlpha.ImplGetBitmap() );
535                     }
536                     else if (IsTransparent())
537                     {
538                         maMask.CopyPixel( rRectDst, rRectSrc, &pBmpExSrc->maMask );
539                     }
540                     else
541                     {
542                         maMask = Bitmap(GetSizePixel(), 1);
543                         maMask.Erase(COL_BLACK);
544                         meTransparent = TransparentType::Bitmap;
545                         maMask.CopyPixel( rRectDst, rRectSrc, &pBmpExSrc->maMask );
546                     }
547                 }
548                 else if (IsAlpha())
549                 {
550                     sal_uInt8 cBlack = 0;
551                     const AlphaMask aAlphaSrc(pBmpExSrc->GetSizePixel(), &cBlack);
552 
553                     maMask.CopyPixel( rRectDst, rRectSrc, &aAlphaSrc.ImplGetBitmap() );
554                 }
555                 else if (IsTransparent())
556                 {
557                     Bitmap aMaskSrc(pBmpExSrc->GetSizePixel(), 1);
558 
559                     aMaskSrc.Erase( COL_BLACK );
560                     maMask.CopyPixel( rRectDst, rRectSrc, &aMaskSrc );
561                 }
562             }
563         }
564     }
565 
566     return bRet;
567 }
568 
Erase(const Color & rFillColor)569 bool BitmapEx::Erase( const Color& rFillColor )
570 {
571     bool bRet = false;
572 
573     if( !!maBitmap )
574     {
575         bRet = maBitmap.Erase( rFillColor );
576 
577         if( bRet && ( meTransparent == TransparentType::Bitmap ) && !!maMask )
578         {
579             // Respect transparency on fill color
580             if( rFillColor.GetTransparency() )
581             {
582                 const Color aFill( rFillColor.GetTransparency(), rFillColor.GetTransparency(), rFillColor.GetTransparency() );
583                 maMask.Erase( aFill );
584             }
585             else
586             {
587                 const Color aBlack( COL_BLACK );
588                 maMask.Erase( aBlack );
589             }
590         }
591     }
592 
593     return bRet;
594 }
595 
Replace(const Color & rSearchColor,const Color & rReplaceColor)596 void BitmapEx::Replace( const Color& rSearchColor, const Color& rReplaceColor )
597 {
598     if (!!maBitmap)
599         maBitmap.Replace( rSearchColor, rReplaceColor );
600 }
601 
Replace(const Color * pSearchColors,const Color * pReplaceColors,sal_uLong nColorCount)602 void BitmapEx::Replace( const Color* pSearchColors, const Color* pReplaceColors, sal_uLong nColorCount )
603 {
604     if (!!maBitmap)
605         maBitmap.Replace( pSearchColors, pReplaceColors, nColorCount, /*pTols*/nullptr );
606 }
607 
Adjust(short nLuminancePercent,short nContrastPercent,short nChannelRPercent,short nChannelGPercent,short nChannelBPercent,double fGamma,bool bInvert,bool msoBrightness)608 bool BitmapEx::Adjust( short nLuminancePercent, short nContrastPercent,
609                        short nChannelRPercent, short nChannelGPercent, short nChannelBPercent,
610                        double fGamma, bool bInvert, bool msoBrightness )
611 {
612     return !!maBitmap && maBitmap.Adjust( nLuminancePercent, nContrastPercent,
613                                         nChannelRPercent, nChannelGPercent, nChannelBPercent,
614                                         fGamma, bInvert, msoBrightness );
615 }
616 
Draw(OutputDevice * pOutDev,const Point & rDestPt) const617 void BitmapEx::Draw( OutputDevice* pOutDev, const Point& rDestPt ) const
618 {
619     pOutDev->DrawBitmapEx( rDestPt, *this );
620 }
621 
Draw(OutputDevice * pOutDev,const Point & rDestPt,const Size & rDestSize) const622 void BitmapEx::Draw( OutputDevice* pOutDev,
623                      const Point& rDestPt, const Size& rDestSize ) const
624 {
625     pOutDev->DrawBitmapEx( rDestPt, rDestSize, *this );
626 }
627 
AutoScaleBitmap(BitmapEx const & aBitmap,const long aStandardSize)628 BitmapEx BitmapEx:: AutoScaleBitmap(BitmapEx const & aBitmap, const long aStandardSize)
629 {
630     Point aEmptyPoint(0,0);
631     double imgposX = 0;
632     double imgposY = 0;
633     BitmapEx  aRet = aBitmap;
634     double imgOldWidth = aRet.GetSizePixel().Width();
635     double imgOldHeight = aRet.GetSizePixel().Height();
636 
637     Size aScaledSize;
638     if (imgOldWidth >= aStandardSize || imgOldHeight >= aStandardSize)
639     {
640         sal_Int32 imgNewWidth = 0;
641         sal_Int32 imgNewHeight = 0;
642         if (imgOldWidth >= imgOldHeight)
643         {
644             imgNewWidth = aStandardSize;
645             imgNewHeight = sal_Int32(imgOldHeight / (imgOldWidth / aStandardSize) + 0.5);
646             imgposX = 0;
647             imgposY = (aStandardSize - (imgOldHeight / (imgOldWidth / aStandardSize) + 0.5)) / 2 + 0.5;
648         }
649         else
650         {
651             imgNewHeight = aStandardSize;
652             imgNewWidth = sal_Int32(imgOldWidth / (imgOldHeight / aStandardSize) + 0.5);
653             imgposY = 0;
654             imgposX = (aStandardSize - (imgOldWidth / (imgOldHeight / aStandardSize) + 0.5)) / 2 + 0.5;
655         }
656 
657         aScaledSize = Size( imgNewWidth, imgNewHeight );
658         aRet.Scale( aScaledSize, BmpScaleFlag::BestQuality );
659     }
660     else
661     {
662         imgposX = (aStandardSize - imgOldWidth) / 2 + 0.5;
663         imgposY = (aStandardSize - imgOldHeight) / 2 + 0.5;
664     }
665 
666     Size aStdSize( aStandardSize, aStandardSize );
667     tools::Rectangle aRect(aEmptyPoint, aStdSize );
668 
669     ScopedVclPtrInstance< VirtualDevice > aVirDevice(*Application::GetDefaultDevice(),
670                                                      DeviceFormat::DEFAULT, DeviceFormat::BITMASK);
671     aVirDevice->SetOutputSizePixel( aStdSize );
672     aVirDevice->SetFillColor( COL_TRANSPARENT );
673     aVirDevice->SetLineColor( COL_TRANSPARENT );
674 
675     // Draw a rect into virDevice
676     aVirDevice->DrawRect( aRect );
677     Point aPointPixel( static_cast<long>(imgposX), static_cast<long>(imgposY) );
678     aVirDevice->DrawBitmapEx( aPointPixel, aRet );
679     aRet = aVirDevice->GetBitmapEx( aEmptyPoint, aStdSize );
680 
681     return aRet;
682 }
683 
GetTransparency(sal_Int32 nX,sal_Int32 nY) const684 sal_uInt8 BitmapEx::GetTransparency(sal_Int32 nX, sal_Int32 nY) const
685 {
686     sal_uInt8 nTransparency(0xff);
687 
688     if(!maBitmap.IsEmpty())
689     {
690         if (nX >= 0 && nX < GetSizePixel().Width() && nY >= 0 && nY < GetSizePixel().Height())
691         {
692             switch(meTransparent)
693             {
694                 case TransparentType::NONE:
695                 {
696                     // Not transparent, ergo all covered
697                     nTransparency = 0x00;
698                     break;
699                 }
700                 case TransparentType::Color:
701                 {
702                     Bitmap aTestBitmap(maBitmap);
703                     Bitmap::ScopedReadAccess pRead(aTestBitmap);
704 
705                     if(pRead)
706                     {
707                         const BitmapColor aBmpColor = pRead->GetColor(nY, nX);
708 
709                         // If color is not equal to TransparentColor, we are not transparent
710                         if (aBmpColor != maTransparentColor)
711                             nTransparency = 0x00;
712 
713                     }
714                     break;
715                 }
716                 case TransparentType::Bitmap:
717                 {
718                     if(!maMask.IsEmpty())
719                     {
720                         Bitmap aTestBitmap(maMask);
721                         Bitmap::ScopedReadAccess pRead(aTestBitmap);
722 
723                         if(pRead)
724                         {
725                             const BitmapColor aBitmapColor(pRead->GetPixel(nY, nX));
726 
727                             if(mbAlpha)
728                             {
729                                 nTransparency = aBitmapColor.GetIndex();
730                             }
731                             else
732                             {
733                                 if(0x00 == aBitmapColor.GetIndex())
734                                 {
735                                     nTransparency = 0x00;
736                                 }
737                             }
738                         }
739                     }
740                     break;
741                 }
742             }
743         }
744     }
745 
746     return nTransparency;
747 }
748 
749 
GetPixelColor(sal_Int32 nX,sal_Int32 nY) const750 Color BitmapEx::GetPixelColor(sal_Int32 nX, sal_Int32 nY) const
751 {
752     Bitmap::ScopedReadAccess pReadAccess( const_cast<Bitmap&>(maBitmap) );
753     assert(pReadAccess);
754 
755     BitmapColor aColor = pReadAccess->GetColor(nY, nX);
756 
757     if (IsAlpha())
758     {
759         AlphaMask aAlpha = GetAlpha();
760         AlphaMask::ScopedReadAccess pAlphaReadAccess(aAlpha);
761         aColor.SetTransparency(pAlphaReadAccess->GetPixel(nY, nX).GetIndex());
762     }
763     else if (maBitmap.GetBitCount() != 32)
764     {
765         aColor.SetTransparency(0);
766     }
767     return aColor;
768 }
769 
770 // Shift alpha transparent pixels between cppcanvas/ implementations
771 // and vcl in a generally grotesque and under-performing fashion
Create(const css::uno::Reference<css::rendering::XBitmapCanvas> & xBitmapCanvas,const Size & rSize)772 bool BitmapEx::Create( const css::uno::Reference< css::rendering::XBitmapCanvas > &xBitmapCanvas,
773                        const Size &rSize )
774 {
775     uno::Reference< beans::XFastPropertySet > xFastPropertySet( xBitmapCanvas, uno::UNO_QUERY );
776     if( xFastPropertySet.get() )
777     {
778         // 0 means get BitmapEx
779         uno::Any aAny = xFastPropertySet->getFastPropertyValue( 0 );
780         std::unique_ptr<BitmapEx> xBitmapEx(reinterpret_cast<BitmapEx*>(*o3tl::doAccess<sal_Int64>(aAny)));
781         if( xBitmapEx )
782         {
783             *this = *xBitmapEx;
784             return true;
785         }
786     }
787 
788     std::shared_ptr<SalBitmap> pSalBmp;
789     std::shared_ptr<SalBitmap> pSalMask;
790 
791     pSalBmp = ImplGetSVData()->mpDefInst->CreateSalBitmap();
792 
793     Size aLocalSize(rSize);
794     if( pSalBmp->Create( xBitmapCanvas, aLocalSize ) )
795     {
796         pSalMask = ImplGetSVData()->mpDefInst->CreateSalBitmap();
797         if ( pSalMask->Create( xBitmapCanvas, aLocalSize, true ) )
798         {
799             *this = BitmapEx(Bitmap(pSalBmp), Bitmap(pSalMask) );
800             return true;
801         }
802         else
803         {
804             *this = BitmapEx(Bitmap(pSalBmp));
805             return true;
806         }
807     }
808 
809     return false;
810 }
811 
812 namespace
813 {
impTransformBitmap(const Bitmap & rSource,const Size & rDestinationSize,const basegfx::B2DHomMatrix & rTransform,bool bSmooth)814     Bitmap impTransformBitmap(
815         const Bitmap& rSource,
816         const Size& rDestinationSize,
817         const basegfx::B2DHomMatrix& rTransform,
818         bool bSmooth)
819     {
820         Bitmap aDestination(rDestinationSize, 24);
821         BitmapScopedWriteAccess xWrite(aDestination);
822 
823         if(xWrite)
824         {
825             Bitmap::ScopedReadAccess xRead(const_cast< Bitmap& >(rSource));
826 
827             if (xRead)
828             {
829                 const Size aDestinationSizePixel(aDestination.GetSizePixel());
830                 const BitmapColor aOutside(BitmapColor(0xff, 0xff, 0xff));
831 
832                 for(long y(0); y < aDestinationSizePixel.getHeight(); y++)
833                 {
834                     Scanline pScanline = xWrite->GetScanline( y );
835                     for(long x(0); x < aDestinationSizePixel.getWidth(); x++)
836                     {
837                         const basegfx::B2DPoint aSourceCoor(rTransform * basegfx::B2DPoint(x, y));
838 
839                         if(bSmooth)
840                         {
841                             xWrite->SetPixelOnData(
842                                 pScanline,
843                                 x,
844                                 xRead->GetInterpolatedColorWithFallback(
845                                     aSourceCoor.getY(),
846                                     aSourceCoor.getX(),
847                                     aOutside));
848                         }
849                         else
850                         {
851                             // this version does the correct <= 0.0 checks, so no need
852                             // to do the static_cast< sal_Int32 > self and make an error
853                             xWrite->SetPixelOnData(
854                                 pScanline,
855                                 x,
856                                 xRead->GetColorWithFallback(
857                                     aSourceCoor.getY(),
858                                     aSourceCoor.getX(),
859                                     aOutside));
860                         }
861                     }
862                 }
863             }
864         }
865 
866         rSource.AdaptBitCount(aDestination);
867 
868         return aDestination;
869     }
870 
871     /// Decides if rTransformation needs smoothing or not (e.g. 180 deg rotation doesn't need it).
implTransformNeedsSmooth(const basegfx::B2DHomMatrix & rTransformation)872     bool implTransformNeedsSmooth(const basegfx::B2DHomMatrix& rTransformation)
873     {
874         basegfx::B2DVector aScale, aTranslate;
875         double fRotate, fShearX;
876         rTransformation.decompose(aScale, aTranslate, fRotate, fShearX);
877         if (aScale != basegfx::B2DVector(1, 1))
878         {
879             return true;
880         }
881 
882         fRotate = fmod( fRotate, F_2PI );
883         if (fRotate < 0)
884         {
885             fRotate += F_2PI;
886         }
887         if (!rtl::math::approxEqual(fRotate, 0)
888             && !rtl::math::approxEqual(fRotate, F_PI2)
889             && !rtl::math::approxEqual(fRotate, F_PI)
890             && !rtl::math::approxEqual(fRotate, 3 * F_PI2))
891         {
892             return true;
893         }
894 
895         if (!rtl::math::approxEqual(fShearX, 0))
896         {
897             return true;
898         }
899 
900         return false;
901     }
902 } // end of anonymous namespace
903 
TransformBitmapEx(double fWidth,double fHeight,const basegfx::B2DHomMatrix & rTransformation) const904 BitmapEx BitmapEx::TransformBitmapEx(
905     double fWidth,
906     double fHeight,
907     const basegfx::B2DHomMatrix& rTransformation) const
908 {
909     if(fWidth <= 1 || fHeight <= 1)
910         return BitmapEx();
911 
912     // force destination to 24 bit, we want to smooth output
913     const Size aDestinationSize(basegfx::fround(fWidth), basegfx::fround(fHeight));
914     bool bSmooth = implTransformNeedsSmooth(rTransformation);
915     const Bitmap aDestination(impTransformBitmap(GetBitmapRef(), aDestinationSize, rTransformation, bSmooth));
916 
917     // create mask
918     if(IsTransparent())
919     {
920         if(IsAlpha())
921         {
922             const Bitmap aAlpha(impTransformBitmap(GetAlpha().GetBitmap(), aDestinationSize, rTransformation, bSmooth));
923             return BitmapEx(aDestination, AlphaMask(aAlpha));
924         }
925         else
926         {
927             const Bitmap aLclMask(impTransformBitmap(GetMask(), aDestinationSize, rTransformation, false));
928             return BitmapEx(aDestination, aLclMask);
929         }
930     }
931 
932     return BitmapEx(aDestination);
933 }
934 
getTransformed(const basegfx::B2DHomMatrix & rTransformation,const basegfx::B2DRange & rVisibleRange,double fMaximumArea) const935 BitmapEx BitmapEx::getTransformed(
936     const basegfx::B2DHomMatrix& rTransformation,
937     const basegfx::B2DRange& rVisibleRange,
938     double fMaximumArea) const
939 {
940     BitmapEx aRetval;
941 
942     if(IsEmpty())
943         return aRetval;
944 
945     const sal_uInt32 nSourceWidth(GetSizePixel().Width());
946     const sal_uInt32 nSourceHeight(GetSizePixel().Height());
947 
948     if(!nSourceWidth || !nSourceHeight)
949         return aRetval;
950 
951     // Get aOutlineRange
952     basegfx::B2DRange aOutlineRange(0.0, 0.0, 1.0, 1.0);
953 
954     aOutlineRange.transform(rTransformation);
955 
956     // create visible range from it by moving from relative to absolute
957     basegfx::B2DRange aVisibleRange(rVisibleRange);
958 
959     aVisibleRange.transform(
960         basegfx::utils::createScaleTranslateB2DHomMatrix(
961             aOutlineRange.getRange(),
962             aOutlineRange.getMinimum()));
963 
964     // get target size (which is visible range's size)
965     double fWidth(aVisibleRange.getWidth());
966     double fHeight(aVisibleRange.getHeight());
967 
968     if(fWidth < 1.0 || fHeight < 1.0)
969     {
970         return aRetval;
971     }
972 
973     // test if discrete size (pixel) maybe too big and limit it
974     const double fArea(fWidth * fHeight);
975     const bool bNeedToReduce(basegfx::fTools::more(fArea, fMaximumArea));
976     double fReduceFactor(1.0);
977 
978     if(bNeedToReduce)
979     {
980         fReduceFactor = sqrt(fMaximumArea / fArea);
981         fWidth *= fReduceFactor;
982         fHeight *= fReduceFactor;
983     }
984 
985     // Build complete transform from source pixels to target pixels.
986     // Start by scaling from source pixel size to unit coordinates
987     basegfx::B2DHomMatrix aTransform(
988         basegfx::utils::createScaleB2DHomMatrix(
989             1.0 / nSourceWidth,
990             1.0 / nSourceHeight));
991 
992     // multiply with given transform which leads from unit coordinates inside
993     // aOutlineRange
994     aTransform = rTransformation * aTransform;
995 
996     // subtract top-left of absolute VisibleRange
997     aTransform.translate(
998         -aVisibleRange.getMinX(),
999         -aVisibleRange.getMinY());
1000 
1001     // scale to target pixels (if needed)
1002     if(bNeedToReduce)
1003     {
1004         aTransform.scale(fReduceFactor, fReduceFactor);
1005     }
1006 
1007     // invert to get transformation from target pixel coordinates to source pixels
1008     aTransform.invert();
1009 
1010     // create bitmap using source, destination and linear back-transformation
1011     aRetval = TransformBitmapEx(fWidth, fHeight, aTransform);
1012 
1013     return aRetval;
1014 }
1015 
ModifyBitmapEx(const basegfx::BColorModifierStack & rBColorModifierStack) const1016 BitmapEx BitmapEx::ModifyBitmapEx(const basegfx::BColorModifierStack& rBColorModifierStack) const
1017 {
1018     Bitmap aChangedBitmap(GetBitmapRef());
1019     bool bDone(false);
1020 
1021     for(sal_uInt32 a(rBColorModifierStack.count()); a && !bDone; )
1022     {
1023         const basegfx::BColorModifierSharedPtr& rModifier = rBColorModifierStack.getBColorModifier(--a);
1024         const basegfx::BColorModifier_replace* pReplace = dynamic_cast< const basegfx::BColorModifier_replace* >(rModifier.get());
1025 
1026         if(pReplace)
1027         {
1028             // complete replace
1029             if(IsTransparent())
1030             {
1031                 // clear bitmap with dest color
1032                 if(aChangedBitmap.GetBitCount() <= 8)
1033                 {
1034                     // do NOT use erase; for e.g. 8bit Bitmaps, the nearest color to the given
1035                     // erase color is determined and used -> this may be different from what is
1036                     // wanted here. Better create a new bitmap with the needed color explicitly
1037                     Bitmap::ScopedReadAccess xReadAccess(aChangedBitmap);
1038                     OSL_ENSURE(xReadAccess, "Got no Bitmap ReadAccess ?!?");
1039 
1040                     if(xReadAccess)
1041                     {
1042                         BitmapPalette aNewPalette(xReadAccess->GetPalette());
1043                         aNewPalette[0] = BitmapColor(Color(pReplace->getBColor()));
1044                         aChangedBitmap = Bitmap(
1045                             aChangedBitmap.GetSizePixel(),
1046                             aChangedBitmap.GetBitCount(),
1047                             &aNewPalette);
1048                     }
1049                 }
1050                 else
1051                 {
1052                     aChangedBitmap.Erase(Color(pReplace->getBColor()));
1053                 }
1054             }
1055             else
1056             {
1057                 // erase bitmap, caller will know to paint direct
1058                 aChangedBitmap.SetEmpty();
1059             }
1060 
1061             bDone = true;
1062         }
1063         else
1064         {
1065             BitmapScopedWriteAccess xContent(aChangedBitmap);
1066 
1067             if(xContent)
1068             {
1069                 const double fConvertColor(1.0 / 255.0);
1070 
1071                 if(xContent->HasPalette())
1072                 {
1073                     const sal_uInt16 nCount(xContent->GetPaletteEntryCount());
1074 
1075                     for(sal_uInt16 b(0); b < nCount; b++)
1076                     {
1077                         const BitmapColor& rCol = xContent->GetPaletteColor(b);
1078                         const basegfx::BColor aBSource(
1079                             rCol.GetRed() * fConvertColor,
1080                             rCol.GetGreen() * fConvertColor,
1081                             rCol.GetBlue() * fConvertColor);
1082                         const basegfx::BColor aBDest(rModifier->getModifiedColor(aBSource));
1083                         xContent->SetPaletteColor(b, BitmapColor(Color(aBDest)));
1084                     }
1085                 }
1086                 else if(ScanlineFormat::N24BitTcBgr == xContent->GetScanlineFormat())
1087                 {
1088                     for(sal_uInt32 y(0); y < static_cast<sal_uInt32>(xContent->Height()); y++)
1089                     {
1090                         Scanline pScan = xContent->GetScanline(y);
1091 
1092                         for(sal_uInt32 x(0); x < static_cast<sal_uInt32>(xContent->Width()); x++)
1093                         {
1094                             const basegfx::BColor aBSource(
1095                                 *(pScan + 2)* fConvertColor,
1096                                 *(pScan + 1) * fConvertColor,
1097                                 *pScan * fConvertColor);
1098                             const basegfx::BColor aBDest(rModifier->getModifiedColor(aBSource));
1099                             *pScan++ = static_cast< sal_uInt8 >(aBDest.getBlue() * 255.0);
1100                             *pScan++ = static_cast< sal_uInt8 >(aBDest.getGreen() * 255.0);
1101                             *pScan++ = static_cast< sal_uInt8 >(aBDest.getRed() * 255.0);
1102                         }
1103                     }
1104                 }
1105                 else if(ScanlineFormat::N24BitTcRgb == xContent->GetScanlineFormat())
1106                 {
1107                     for(sal_uInt32 y(0); y < static_cast<sal_uInt32>(xContent->Height()); y++)
1108                     {
1109                         Scanline pScan = xContent->GetScanline(y);
1110 
1111                         for(sal_uInt32 x(0); x < static_cast<sal_uInt32>(xContent->Width()); x++)
1112                         {
1113                             const basegfx::BColor aBSource(
1114                                 *pScan * fConvertColor,
1115                                 *(pScan + 1) * fConvertColor,
1116                                 *(pScan + 2) * fConvertColor);
1117                             const basegfx::BColor aBDest(rModifier->getModifiedColor(aBSource));
1118                             *pScan++ = static_cast< sal_uInt8 >(aBDest.getRed() * 255.0);
1119                             *pScan++ = static_cast< sal_uInt8 >(aBDest.getGreen() * 255.0);
1120                             *pScan++ = static_cast< sal_uInt8 >(aBDest.getBlue() * 255.0);
1121                         }
1122                     }
1123                 }
1124                 else
1125                 {
1126                     for(sal_uInt32 y(0); y < static_cast<sal_uInt32>(xContent->Height()); y++)
1127                     {
1128                         Scanline pScanline = xContent->GetScanline( y );
1129                         for(sal_uInt32 x(0); x < static_cast<sal_uInt32>(xContent->Width()); x++)
1130                         {
1131                             const BitmapColor aBMCol(xContent->GetColor(y, x));
1132                             const basegfx::BColor aBSource(
1133                                 static_cast<double>(aBMCol.GetRed()) * fConvertColor,
1134                                 static_cast<double>(aBMCol.GetGreen()) * fConvertColor,
1135                                 static_cast<double>(aBMCol.GetBlue()) * fConvertColor);
1136                             const basegfx::BColor aBDest(rModifier->getModifiedColor(aBSource));
1137 
1138                             xContent->SetPixelOnData(pScanline, x, BitmapColor(Color(aBDest)));
1139                         }
1140                     }
1141                 }
1142             }
1143         }
1144     }
1145 
1146     if(aChangedBitmap.IsEmpty())
1147     {
1148         return BitmapEx();
1149     }
1150     else
1151     {
1152         if(IsTransparent())
1153         {
1154             if(IsAlpha())
1155             {
1156                 return BitmapEx(aChangedBitmap, GetAlpha());
1157             }
1158             else
1159             {
1160                 return BitmapEx(aChangedBitmap, GetMask());
1161             }
1162         }
1163         else
1164         {
1165             return BitmapEx(aChangedBitmap);
1166         }
1167     }
1168 }
1169 
createBlendFrame(const Size & rSize,sal_uInt8 nAlpha,Color aColorTopLeft,Color aColorBottomRight)1170 BitmapEx createBlendFrame(
1171     const Size& rSize,
1172     sal_uInt8 nAlpha,
1173     Color aColorTopLeft,
1174     Color aColorBottomRight)
1175 {
1176     const sal_uInt32 nW(rSize.Width());
1177     const sal_uInt32 nH(rSize.Height());
1178 
1179     if(nW || nH)
1180     {
1181         Color aColTopRight(aColorTopLeft);
1182         Color aColBottomLeft(aColorTopLeft);
1183         const sal_uInt32 nDE(nW + nH);
1184 
1185         aColTopRight.Merge(aColorBottomRight, 255 - sal_uInt8((nW * 255) / nDE));
1186         aColBottomLeft.Merge(aColorBottomRight, 255 - sal_uInt8((nH * 255) / nDE));
1187 
1188         return createBlendFrame(rSize, nAlpha, aColorTopLeft, aColTopRight, aColorBottomRight, aColBottomLeft);
1189     }
1190 
1191     return BitmapEx();
1192 }
1193 
createBlendFrame(const Size & rSize,sal_uInt8 nAlpha,Color aColorTopLeft,Color aColorTopRight,Color aColorBottomRight,Color aColorBottomLeft)1194 BitmapEx createBlendFrame(
1195     const Size& rSize,
1196     sal_uInt8 nAlpha,
1197     Color aColorTopLeft,
1198     Color aColorTopRight,
1199     Color aColorBottomRight,
1200     Color aColorBottomLeft)
1201 {
1202     BlendFrameCache* pBlendFrameCache = ImplGetBlendFrameCache();
1203 
1204     if(pBlendFrameCache->m_aLastSize == rSize
1205         && pBlendFrameCache->m_nLastAlpha == nAlpha
1206         && pBlendFrameCache->m_aLastColorTopLeft == aColorTopLeft
1207         && pBlendFrameCache->m_aLastColorTopRight == aColorTopRight
1208         && pBlendFrameCache->m_aLastColorBottomRight == aColorBottomRight
1209         && pBlendFrameCache->m_aLastColorBottomLeft == aColorBottomLeft)
1210     {
1211         return pBlendFrameCache->m_aLastResult;
1212     }
1213 
1214     pBlendFrameCache->m_aLastSize = rSize;
1215     pBlendFrameCache->m_nLastAlpha = nAlpha;
1216     pBlendFrameCache->m_aLastColorTopLeft = aColorTopLeft;
1217     pBlendFrameCache->m_aLastColorTopRight = aColorTopRight;
1218     pBlendFrameCache->m_aLastColorBottomRight = aColorBottomRight;
1219     pBlendFrameCache->m_aLastColorBottomLeft = aColorBottomLeft;
1220     pBlendFrameCache->m_aLastResult.Clear();
1221 
1222     const long nW(rSize.Width());
1223     const long nH(rSize.Height());
1224 
1225     if(nW > 1 && nH > 1)
1226     {
1227         sal_uInt8 aEraseTrans(0xff);
1228         Bitmap aContent(rSize, 24);
1229         AlphaMask aAlpha(rSize, &aEraseTrans);
1230 
1231         aContent.Erase(COL_BLACK);
1232 
1233         BitmapScopedWriteAccess pContent(aContent);
1234         AlphaScopedWriteAccess pAlpha(aAlpha);
1235 
1236         if(pContent && pAlpha)
1237         {
1238             long x(0);
1239             long y(0);
1240             Scanline pScanContent = pContent->GetScanline( 0 );
1241             Scanline pScanAlpha = pContent->GetScanline( 0 );
1242 
1243             // x == 0, y == 0, top-left corner
1244             pContent->SetPixelOnData(pScanContent, 0, aColorTopLeft);
1245             pAlpha->SetPixelOnData(pScanAlpha, 0, BitmapColor(nAlpha));
1246 
1247             // y == 0, top line left to right
1248             for(x = 1; x < nW - 1; x++)
1249             {
1250                 Color aMix(aColorTopLeft);
1251 
1252                 aMix.Merge(aColorTopRight, 255 - sal_uInt8((x * 255) / nW));
1253                 pContent->SetPixelOnData(pScanContent, x, aMix);
1254                 pAlpha->SetPixelOnData(pScanAlpha, x, BitmapColor(nAlpha));
1255             }
1256 
1257             // x == nW - 1, y == 0, top-right corner
1258             // #i123690# Caution! When nW is 1, x == nW is possible (!)
1259             if(x < nW)
1260             {
1261                 pContent->SetPixelOnData(pScanContent, x, aColorTopRight);
1262                 pAlpha->SetPixelOnData(pScanAlpha, x, BitmapColor(nAlpha));
1263             }
1264 
1265             // x == 0 and nW - 1, left and right line top-down
1266             for(y = 1; y < nH - 1; y++)
1267             {
1268                 pScanContent = pContent->GetScanline( y );
1269                 pScanAlpha = pContent->GetScanline( y );
1270                 Color aMixA(aColorTopLeft);
1271 
1272                 aMixA.Merge(aColorBottomLeft, 255 - sal_uInt8((y * 255) / nH));
1273                 pContent->SetPixelOnData(pScanContent, 0, aMixA);
1274                 pAlpha->SetPixelOnData(pScanAlpha, 0, BitmapColor(nAlpha));
1275 
1276                 // #i123690# Caution! When nW is 1, x == nW is possible (!)
1277                 if(x < nW)
1278                 {
1279                     Color aMixB(aColorTopRight);
1280 
1281                     aMixB.Merge(aColorBottomRight, 255 - sal_uInt8((y * 255) / nH));
1282                     pContent->SetPixelOnData(pScanContent, x, aMixB);
1283                     pAlpha->SetPixelOnData(pScanAlpha, x, BitmapColor(nAlpha));
1284                 }
1285             }
1286 
1287             // #i123690# Caution! When nH is 1, y == nH is possible (!)
1288             if(y < nH)
1289             {
1290                 // x == 0, y == nH - 1, bottom-left corner
1291                 pContent->SetPixelOnData(pScanContent, 0, aColorBottomLeft);
1292                 pAlpha->SetPixelOnData(pScanAlpha, 0, BitmapColor(nAlpha));
1293 
1294                 // y == nH - 1, bottom line left to right
1295                 for(x = 1; x < nW - 1; x++)
1296                 {
1297                     Color aMix(aColorBottomLeft);
1298 
1299                     aMix.Merge(aColorBottomRight, 255 - sal_uInt8(((x - 0)* 255) / nW));
1300                     pContent->SetPixelOnData(pScanContent, x, aMix);
1301                     pAlpha->SetPixelOnData(pScanAlpha, x, BitmapColor(nAlpha));
1302                 }
1303 
1304                 // x == nW - 1, y == nH - 1, bottom-right corner
1305                 // #i123690# Caution! When nW is 1, x == nW is possible (!)
1306                 if(x < nW)
1307                 {
1308                     pContent->SetPixelOnData(pScanContent, x, aColorBottomRight);
1309                     pAlpha->SetPixelOnData(pScanAlpha, x, BitmapColor(nAlpha));
1310                 }
1311             }
1312 
1313             pContent.reset();
1314             pAlpha.reset();
1315 
1316             pBlendFrameCache->m_aLastResult = BitmapEx(aContent, aAlpha);
1317         }
1318     }
1319 
1320     return pBlendFrameCache->m_aLastResult;
1321 }
1322 
Replace(const Color & rSearchColor,const Color & rReplaceColor,sal_uInt8 nTolerance)1323 void BitmapEx::Replace(const Color& rSearchColor,
1324                            const Color& rReplaceColor,
1325                            sal_uInt8 nTolerance)
1326 {
1327     maBitmap.Replace(rSearchColor, rReplaceColor, nTolerance);
1328 }
1329 
Replace(const Color * pSearchColors,const Color * pReplaceColors,sal_uLong nColorCount,sal_uInt8 const * pTols)1330 void BitmapEx::Replace( const Color* pSearchColors,
1331                         const Color* pReplaceColors,
1332                         sal_uLong nColorCount,
1333                         sal_uInt8 const * pTols )
1334 {
1335     maBitmap.Replace( pSearchColors, pReplaceColors, nColorCount, pTols );
1336 }
1337 
ReplaceTransparency(const Color & rColor)1338 void BitmapEx::ReplaceTransparency(const Color& rColor)
1339 {
1340     if( IsTransparent() )
1341     {
1342         maBitmap.Replace( GetMask(), rColor );
1343         maMask = Bitmap();
1344         maBitmapSize = maBitmap.GetSizePixel();
1345         maTransparentColor = Color();
1346         meTransparent = TransparentType::NONE;
1347         mbAlpha = false;
1348     }
1349 }
1350 
DetectEdges(const Bitmap & rBmp)1351 static Bitmap DetectEdges( const Bitmap& rBmp )
1352 {
1353     constexpr sal_uInt8 cEdgeDetectThreshold = 128;
1354     const Size  aSize( rBmp.GetSizePixel() );
1355     Bitmap      aRetBmp;
1356 
1357     if( ( aSize.Width() > 2 ) && ( aSize.Height() > 2 ) )
1358     {
1359         Bitmap aWorkBmp( rBmp );
1360 
1361         if( aWorkBmp.Convert( BmpConversion::N8BitGreys ) )
1362         {
1363             bool bRet = false;
1364 
1365             ScopedVclPtr<VirtualDevice> pVirDev(VclPtr<VirtualDevice>::Create());
1366             pVirDev->SetOutputSizePixel(aSize);
1367             Bitmap::ScopedReadAccess pReadAcc(aWorkBmp);
1368 
1369             if( pReadAcc )
1370             {
1371                 const long          nWidth = aSize.Width();
1372                 const long          nWidth2 = nWidth - 2;
1373                 const long          nHeight = aSize.Height();
1374                 const long          nHeight2 = nHeight - 2;
1375                 const long          lThres2 = static_cast<long>(cEdgeDetectThreshold) * cEdgeDetectThreshold;
1376                 long                nSum1;
1377                 long                nSum2;
1378                 long                lGray;
1379 
1380                 // initialize border with white pixels
1381                 pVirDev->SetLineColor( COL_WHITE );
1382                 pVirDev->DrawLine( Point(), Point( nWidth - 1, 0L ) );
1383                 pVirDev->DrawLine( Point( nWidth - 1, 0L ), Point( nWidth - 1, nHeight - 1 ) );
1384                 pVirDev->DrawLine( Point( nWidth - 1, nHeight - 1 ), Point( 0L, nHeight - 1 ) );
1385                 pVirDev->DrawLine( Point( 0, nHeight - 1 ), Point() );
1386 
1387                 for( long nY = 0, nY1 = 1, nY2 = 2; nY < nHeight2; nY++, nY1++, nY2++ )
1388                 {
1389                     Scanline pScanlineRead = pReadAcc->GetScanline( nY );
1390                     Scanline pScanlineRead1 = pReadAcc->GetScanline( nY1 );
1391                     Scanline pScanlineRead2 = pReadAcc->GetScanline( nY2 );
1392                     for( long nX = 0, nXDst = 1, nXTmp; nX < nWidth2; nX++, nXDst++ )
1393                     {
1394                         nXTmp = nX;
1395 
1396                         nSum2 = lGray = pReadAcc->GetIndexFromData( pScanlineRead, nXTmp++ );
1397                         nSum1 = -nSum2;
1398                         nSum2 += static_cast<long>(pReadAcc->GetIndexFromData( pScanlineRead, nXTmp++ )) << 1;
1399                         lGray = pReadAcc->GetIndexFromData( pScanlineRead, nXTmp );
1400                         nSum1 += lGray;
1401                         nSum2 += lGray;
1402 
1403                         nSum1 += static_cast<long>(pReadAcc->GetIndexFromData( pScanlineRead1, nXTmp )) << 1;
1404                         nXTmp -= 2;
1405                         nSum1 -= static_cast<long>(pReadAcc->GetIndexFromData( pScanlineRead1, nXTmp )) << 1;
1406 
1407                         lGray = -static_cast<long>(pReadAcc->GetIndexFromData( pScanlineRead2, nXTmp++ ));
1408                         nSum1 += lGray;
1409                         nSum2 += lGray;
1410                         nSum2 -= static_cast<long>(pReadAcc->GetIndexFromData( pScanlineRead2, nXTmp++ )) << 1;
1411                         lGray = static_cast<long>(pReadAcc->GetIndexFromData( pScanlineRead2, nXTmp ));
1412                         nSum1 += lGray;
1413                         nSum2 -= lGray;
1414 
1415                         if( ( nSum1 * nSum1 + nSum2 * nSum2 ) < lThres2 )
1416                             pVirDev->DrawPixel( Point(nXDst, nY), COL_WHITE );
1417                         else
1418                             pVirDev->DrawPixel( Point(nXDst, nY), COL_BLACK );
1419                     }
1420                 }
1421 
1422                 bRet = true;
1423             }
1424 
1425             pReadAcc.reset();
1426 
1427             if( bRet )
1428                 aRetBmp = pVirDev->GetBitmap(Point(0,0), aSize);
1429         }
1430     }
1431 
1432     if( !aRetBmp )
1433         aRetBmp = rBmp;
1434     else
1435     {
1436         aRetBmp.SetPrefMapMode( rBmp.GetPrefMapMode() );
1437         aRetBmp.SetPrefSize( rBmp.GetPrefSize() );
1438     }
1439 
1440     return aRetBmp;
1441 }
1442 
1443 /** Get contours in image */
GetContour(bool bContourEdgeDetect,const tools::Rectangle * pWorkRectPixel)1444 tools::Polygon  BitmapEx::GetContour( bool bContourEdgeDetect,
1445                                     const tools::Rectangle* pWorkRectPixel )
1446 {
1447     Bitmap aWorkBmp;
1448     tools::Polygon aRetPoly;
1449     tools::Rectangle   aWorkRect( Point(), maBitmap.GetSizePixel() );
1450 
1451     if( pWorkRectPixel )
1452         aWorkRect.Intersection( *pWorkRectPixel );
1453 
1454     aWorkRect.Justify();
1455 
1456     if( ( aWorkRect.GetWidth() > 4 ) && ( aWorkRect.GetHeight() > 4 ) )
1457     {
1458         // if the flag is set, we need to detect edges
1459         if( bContourEdgeDetect )
1460             aWorkBmp = DetectEdges( maBitmap );
1461         else
1462             aWorkBmp = maBitmap;
1463 
1464         BitmapReadAccess* pAcc = aWorkBmp.AcquireReadAccess();
1465 
1466         const long nWidth = pAcc ? pAcc->Width() : 0;
1467         const long nHeight = pAcc ? pAcc->Height() : 0;
1468 
1469         if (pAcc && nWidth && nHeight)
1470         {
1471             const Size&         rPrefSize = aWorkBmp.GetPrefSize();
1472             const double        fFactorX = static_cast<double>(rPrefSize.Width()) / nWidth;
1473             const double        fFactorY = static_cast<double>(rPrefSize.Height()) / nHeight;
1474             const long          nStartX1 = aWorkRect.Left() + 1;
1475             const long          nEndX1 = aWorkRect.Right();
1476             const long          nStartX2 = nEndX1 - 1;
1477             const long          nStartY1 = aWorkRect.Top() + 1;
1478             const long          nEndY1 = aWorkRect.Bottom();
1479             std::unique_ptr<Point[]> pPoints1;
1480             std::unique_ptr<Point[]> pPoints2;
1481             long                nX, nY;
1482             sal_uInt16              nPolyPos = 0;
1483             const BitmapColor   aBlack = pAcc->GetBestMatchingColor( COL_BLACK );
1484 
1485             pPoints1.reset(new Point[ nHeight ]);
1486             pPoints2.reset(new Point[ nHeight ]);
1487 
1488             for ( nY = nStartY1; nY < nEndY1; nY++ )
1489             {
1490                 nX = nStartX1;
1491                 Scanline pScanline = pAcc->GetScanline( nY );
1492 
1493                 // scan row from left to right
1494                 while( nX < nEndX1 )
1495                 {
1496                     if( aBlack == pAcc->GetPixelFromData( pScanline, nX ) )
1497                     {
1498                         pPoints1[ nPolyPos ] = Point( nX, nY );
1499                         nX = nStartX2;
1500 
1501                         // this loop always breaks eventually as there is at least one pixel
1502                         while( true )
1503                         {
1504                             if( aBlack == pAcc->GetPixelFromData( pScanline, nX ) )
1505                             {
1506                                 pPoints2[ nPolyPos ] = Point( nX, nY );
1507                                 break;
1508                             }
1509 
1510                             nX--;
1511                         }
1512 
1513                         nPolyPos++;
1514                         break;
1515                     }
1516 
1517                     nX++;
1518                 }
1519             }
1520 
1521             const sal_uInt16 nNewSize1 = nPolyPos << 1;
1522 
1523             aRetPoly = tools::Polygon( nPolyPos, pPoints1.get() );
1524             aRetPoly.SetSize( nNewSize1 + 1 );
1525             aRetPoly[ nNewSize1 ] = aRetPoly[ 0 ];
1526 
1527             for( sal_uInt16 j = nPolyPos; nPolyPos < nNewSize1; )
1528                 aRetPoly[ nPolyPos++ ] = pPoints2[ --j ];
1529 
1530             if( ( fFactorX != 0. ) && ( fFactorY != 0. ) )
1531                 aRetPoly.Scale( fFactorX, fFactorY );
1532         }
1533 
1534         Bitmap::ReleaseAccess(pAcc);
1535     }
1536 
1537     return aRetPoly;
1538 }
1539 
setAlphaFrom(sal_uInt8 cIndexFrom,sal_Int8 nAlphaTo)1540 void BitmapEx::setAlphaFrom( sal_uInt8 cIndexFrom, sal_Int8 nAlphaTo )
1541 {
1542     AlphaMask aAlphaMask(GetAlpha());
1543     BitmapScopedWriteAccess pWriteAccess(aAlphaMask);
1544     Bitmap::ScopedReadAccess pReadAccess(maBitmap);
1545     assert( pReadAccess.get() && pWriteAccess.get() );
1546     if ( pReadAccess.get() && pWriteAccess.get() )
1547     {
1548         for ( long nY = 0; nY < pReadAccess->Height(); nY++ )
1549         {
1550             Scanline pScanline = pWriteAccess->GetScanline( nY );
1551             Scanline pScanlineRead = pReadAccess->GetScanline( nY );
1552             for ( long nX = 0; nX < pReadAccess->Width(); nX++ )
1553             {
1554                 const sal_uInt8 cIndex = pReadAccess->GetPixelFromData( pScanlineRead, nX ).GetIndex();
1555                 if ( cIndex == cIndexFrom )
1556                     pWriteAccess->SetPixelOnData( pScanline, nX, BitmapColor(nAlphaTo) );
1557             }
1558         }
1559     }
1560 }
1561 
AdjustTransparency(sal_uInt8 cTrans)1562 void BitmapEx::AdjustTransparency(sal_uInt8 cTrans)
1563 {
1564     AlphaMask   aAlpha;
1565 
1566     if (!IsTransparent())
1567     {
1568         aAlpha = AlphaMask(GetSizePixel(), &cTrans);
1569     }
1570     else if( !IsAlpha() )
1571     {
1572         aAlpha = GetMask();
1573         aAlpha.Replace( 0, cTrans );
1574     }
1575     else
1576     {
1577         aAlpha = GetAlpha();
1578         BitmapScopedWriteAccess pA(aAlpha);
1579         assert(pA);
1580 
1581         if( !pA )
1582             return;
1583 
1584         sal_uLong       nTrans = cTrans, nNewTrans;
1585         const long  nWidth = pA->Width(), nHeight = pA->Height();
1586 
1587         if( pA->GetScanlineFormat() == ScanlineFormat::N8BitPal )
1588         {
1589             for( long nY = 0; nY < nHeight; nY++ )
1590             {
1591                 Scanline pAScan = pA->GetScanline( nY );
1592 
1593                 for( long nX = 0; nX < nWidth; nX++ )
1594                 {
1595                     nNewTrans = nTrans + *pAScan;
1596                     *pAScan++ = static_cast<sal_uInt8>( ( nNewTrans & 0xffffff00 ) ? 255 : nNewTrans );
1597                 }
1598             }
1599         }
1600         else
1601         {
1602             BitmapColor aAlphaValue( 0 );
1603 
1604             for( long nY = 0; nY < nHeight; nY++ )
1605             {
1606                 Scanline pScanline = pA->GetScanline( nY );
1607                 for( long nX = 0; nX < nWidth; nX++ )
1608                 {
1609                     nNewTrans = nTrans + pA->GetIndexFromData( pScanline, nX );
1610                     aAlphaValue.SetIndex( static_cast<sal_uInt8>( ( nNewTrans & 0xffffff00 ) ? 255 : nNewTrans ) );
1611                     pA->SetPixelOnData( pScanline, nX, aAlphaValue );
1612                 }
1613             }
1614         }
1615     }
1616     *this = BitmapEx( GetBitmapRef(), aAlpha );
1617 }
1618 
1619 // AS: Because JPEGs require the alpha channel provided separately (JPEG does not
1620 //  natively support alpha channel, but SWF lets you provide it separately), we
1621 //  extract the alpha channel into a separate array here.
GetSplitData(std::vector<sal_uInt8> & rvColorData,std::vector<sal_uInt8> & rvAlphaData) const1622 void BitmapEx::GetSplitData( std::vector<sal_uInt8>& rvColorData, std::vector<sal_uInt8>& rvAlphaData ) const
1623 {
1624     if( IsEmpty() )
1625         return;
1626 
1627     Bitmap::ScopedReadAccess pRAcc(const_cast<Bitmap&>(maBitmap));
1628 
1629     assert( pRAcc );
1630 
1631     AlphaMask   aAlpha;
1632     sal_uInt32 nWidth = pRAcc->Width();
1633     sal_uInt32 nHeight = pRAcc->Height();
1634     rvColorData.resize(nWidth*nHeight*4);
1635     rvAlphaData.resize(nWidth*nHeight);
1636     sal_uInt8* p = rvColorData.data(), *pAlpha = rvAlphaData.data();
1637 
1638 
1639     if (IsAlpha())
1640     {
1641         aAlpha = GetAlpha();
1642     }
1643     else if (IsTransparent())
1644     {
1645         aAlpha = GetMask();
1646     }
1647     else
1648     {
1649         sal_uInt8 cAlphaVal = 0;
1650         aAlpha = AlphaMask(maBitmap.GetSizePixel(), &cAlphaVal);
1651     }
1652 
1653     AlphaMask::ScopedReadAccess pAAcc(aAlpha);
1654 
1655     assert( pAAcc );
1656 
1657     for( sal_uInt32 nY = 0; nY < nHeight; nY++ )
1658     {
1659         Scanline pScanlineAA = pAAcc->GetScanline( nY );
1660         for( sal_uInt32 nX = 0; nX < nWidth; nX++ )
1661         {
1662             const sal_uInt8     nAlpha = pAAcc->GetIndexFromData( pScanlineAA, nX );
1663             const BitmapColor   aPixelColor( pRAcc->GetColor( nY, nX ) );
1664 
1665             if( nAlpha == 0xff )
1666             {
1667                 *p++ = 0;
1668                 *p++ = 0;
1669                 *p++ = 0;
1670                 *p++ = 0;
1671             }
1672             else
1673             {
1674                 *p++ = 0xff-nAlpha;
1675                 *p++ = aPixelColor.GetRed();
1676                 *p++ = aPixelColor.GetGreen();
1677                 *p++ = aPixelColor.GetBlue();
1678             }
1679             *pAlpha++ = 0xff - nAlpha;
1680         }
1681     }
1682 }
1683 
CombineMaskOr(Color maskColor,sal_uInt8 nTol)1684 void BitmapEx::CombineMaskOr(Color maskColor, sal_uInt8 nTol)
1685 {
1686     Bitmap aNewMask = maBitmap.CreateMask( maskColor, nTol );
1687     if ( IsTransparent() )
1688          aNewMask.CombineSimple( maMask, BmpCombine::Or );
1689     maMask = aNewMask;
1690     meTransparent = TransparentType::Bitmap;
1691 }
1692 
1693 /**
1694  * Retrieves the color model data we need for the XImageConsumer stuff.
1695  */
GetColorModel(css::uno::Sequence<sal_Int32> & rRGBPalette,sal_uInt32 & rnRedMask,sal_uInt32 & rnGreenMask,sal_uInt32 & rnBlueMask,sal_uInt32 & rnAlphaMask,sal_uInt32 & rnTransparencyIndex,sal_uInt32 & rnWidth,sal_uInt32 & rnHeight,sal_uInt8 & rnBitCount)1696 void  BitmapEx::GetColorModel(css::uno::Sequence< sal_Int32 >& rRGBPalette,
1697         sal_uInt32& rnRedMask, sal_uInt32& rnGreenMask, sal_uInt32& rnBlueMask, sal_uInt32& rnAlphaMask, sal_uInt32& rnTransparencyIndex,
1698         sal_uInt32& rnWidth, sal_uInt32& rnHeight, sal_uInt8& rnBitCount)
1699 {
1700     Bitmap::ScopedReadAccess pReadAccess( maBitmap );
1701     assert( pReadAccess );
1702 
1703     if( pReadAccess->HasPalette() )
1704     {
1705         sal_uInt16 nPalCount = pReadAccess->GetPaletteEntryCount();
1706 
1707         if( nPalCount )
1708         {
1709             rRGBPalette = css::uno::Sequence< sal_Int32 >( nPalCount + 1 );
1710 
1711             sal_Int32* pTmp = rRGBPalette.getArray();
1712 
1713             for( sal_uInt32 i = 0; i < nPalCount; i++, pTmp++ )
1714             {
1715                 const BitmapColor& rCol = pReadAccess->GetPaletteColor( static_cast<sal_uInt16>(i) );
1716 
1717                 *pTmp = static_cast<sal_Int32>(rCol.GetRed()) << sal_Int32(24);
1718                 *pTmp |= static_cast<sal_Int32>(rCol.GetGreen()) << sal_Int32(16);
1719                 *pTmp |= static_cast<sal_Int32>(rCol.GetBlue()) << sal_Int32(8);
1720                 *pTmp |= sal_Int32(0x000000ffL);
1721             }
1722 
1723             if( IsTransparent() )
1724             {
1725                 // append transparent entry
1726                 *pTmp = sal_Int32(0xffffff00L);
1727                 rnTransparencyIndex = nPalCount;
1728                 nPalCount++;
1729             }
1730             else
1731                 rnTransparencyIndex = 0;
1732         }
1733     }
1734     else
1735     {
1736         rnRedMask = 0xff000000UL;
1737         rnGreenMask = 0x00ff0000UL;
1738         rnBlueMask = 0x0000ff00UL;
1739         rnAlphaMask = 0x000000ffUL;
1740         rnTransparencyIndex = 0;
1741     }
1742 
1743     rnWidth = pReadAccess->Width();
1744     rnHeight = pReadAccess->Height();
1745     rnBitCount = pReadAccess->GetBitCount();
1746 }
1747 
1748 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
1749