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