1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
11 #include <vcl/BitmapTools.hxx>
13 #include <sal/log.hxx>
14 #include <comphelper/processfactory.hxx>
15 #include <comphelper/seqstream.hxx>
16 #include <vcl/canvastools.hxx>
17 #include <basegfx/matrix/b2dhommatrix.hxx>
19 #include <com/sun/star/graphic/SvgTools.hpp>
20 #include <com/sun/star/graphic/Primitive2DTools.hpp>
22 #include <drawinglayer/primitive2d/baseprimitive2d.hxx>
24 #include <com/sun/star/rendering/XIntegerReadOnlyBitmap.hpp>
26 #include <vcl/dibtools.hxx>
27 #include <vcl/settings.hxx>
28 #include <vcl/svapp.hxx>
29 #include <vcl/bitmapaccess.hxx>
30 #include <vcl/virdev.hxx>
31 #if ENABLE_CAIRO_CANVAS
34 #include <tools/diagnose_ex.h>
35 #include <tools/fract.hxx>
36 #include <tools/stream.hxx>
37 #include <bitmapwriteaccess.hxx>
41 using drawinglayer::primitive2d::Primitive2DSequence
;
42 using drawinglayer::primitive2d::Primitive2DReference
;
50 BitmapEx
loadFromName(const OUString
& rFileName
, const ImageLoadFlags eFlags
)
54 OUString aIconTheme
= Application::GetSettings().GetStyleSettings().DetermineIconTheme();
56 ImageTree::get().loadImage(rFileName
, aIconTheme
, aBitmapEx
, true, eFlags
);
61 void loadFromSvg(SvStream
& rStream
, const OUString
& sPath
, BitmapEx
& rBitmapEx
, double fScalingFactor
)
63 uno::Reference
<uno::XComponentContext
> xContext(comphelper::getProcessComponentContext());
64 const uno::Reference
<graphic::XSvgParser
> xSvgParser
= graphic::SvgTools::create(xContext
);
66 std::size_t nSize
= rStream
.remainingSize();
67 std::vector
<sal_Int8
> aBuffer(nSize
+ 1);
68 rStream
.ReadBytes(aBuffer
.data(), nSize
);
71 uno::Sequence
<sal_Int8
> aData(aBuffer
.data(), nSize
+ 1);
72 uno::Reference
<io::XInputStream
> aInputStream(new comphelper::SequenceInputStream(aData
));
74 const Primitive2DSequence aPrimitiveSequence
= xSvgParser
->getDecomposition(aInputStream
, sPath
);
76 if (!aPrimitiveSequence
.hasElements())
79 uno::Sequence
<beans::PropertyValue
> aViewParameters
;
81 geometry::RealRectangle2D aRealRect
;
82 basegfx::B2DRange aRange
;
83 for (Primitive2DReference
const & xReference
: aPrimitiveSequence
)
87 aRealRect
= xReference
->getRange(aViewParameters
);
88 aRange
.expand(basegfx::B2DRange(aRealRect
.X1
, aRealRect
.Y1
, aRealRect
.X2
, aRealRect
.Y2
));
92 aRealRect
.X1
= aRange
.getMinX();
93 aRealRect
.Y1
= aRange
.getMinY();
94 aRealRect
.X2
= aRange
.getMaxX();
95 aRealRect
.Y2
= aRange
.getMaxY();
97 double nDPI
= 96 * fScalingFactor
;
99 const css::uno::Reference
<css::graphic::XPrimitive2DRenderer
> xPrimitive2DRenderer
= css::graphic::Primitive2DTools::create(xContext
);
100 const css::uno::Reference
<css::rendering::XBitmap
> xBitmap(
101 xPrimitive2DRenderer
->rasterize(aPrimitiveSequence
, aViewParameters
, nDPI
, nDPI
, aRealRect
, 256*256));
105 const css::uno::Reference
<css::rendering::XIntegerReadOnlyBitmap
> xIntBmp(xBitmap
, uno::UNO_QUERY_THROW
);
106 rBitmapEx
= vcl::unotools::bitmapExFromXBitmap(xIntBmp
);
111 /** Copy block of image data into the bitmap.
112 Assumes that the Bitmap has been constructed with the desired size.
115 The block of data to copy
117 The number of bytes in a scanline, must >= (width * nBitCount / 8)
119 BitmapEx
CreateFromData( sal_uInt8
const *pData
, sal_Int32 nWidth
, sal_Int32 nHeight
, sal_Int32 nStride
, sal_uInt16 nBitCount
)
121 assert(nStride
>= (nWidth
* nBitCount
/ 8));
122 assert( nBitCount
== 1 || nBitCount
== 24 || nBitCount
== 32);
123 Bitmap
aBmp( Size( nWidth
, nHeight
), nBitCount
);
125 BitmapScopedWriteAccess
pWrite(aBmp
);
126 assert(pWrite
.get());
129 std::unique_ptr
<AlphaMask
> pAlphaMask
;
130 AlphaScopedWriteAccess xMaskAcc
;
133 pAlphaMask
.reset( new AlphaMask( Size(nWidth
, nHeight
) ) );
134 xMaskAcc
= AlphaScopedWriteAccess(*pAlphaMask
);
138 for( long y
= 0; y
< nHeight
; ++y
)
140 Scanline pScanline
= pWrite
->GetScanline(y
);
141 for (long x
= 0; x
< nWidth
; ++x
)
143 sal_uInt8
const *p
= pData
+ y
* nStride
/ 8;
144 int bitIndex
= (y
* nStride
) % 8;
145 pWrite
->SetPixelOnData(pScanline
, x
, BitmapColor((*p
>> bitIndex
) & 1));
151 for( long y
= 0; y
< nHeight
; ++y
)
153 sal_uInt8
const *p
= pData
+ (y
* nStride
);
154 Scanline pScanline
= pWrite
->GetScanline(y
);
155 for (long x
= 0; x
< nWidth
; ++x
)
157 BitmapColor
col(p
[0], p
[1], p
[2]);
158 pWrite
->SetPixelOnData(pScanline
, x
, col
);
163 p
= pData
+ (y
* nStride
) + 3;
164 Scanline pMaskScanLine
= xMaskAcc
->GetScanline(y
);
165 for (long x
= 0; x
< nWidth
; ++x
)
167 xMaskAcc
->SetPixelOnData(pMaskScanLine
, x
, BitmapColor(*p
));
174 return BitmapEx(aBmp
, *pAlphaMask
);
176 return BitmapEx(aBmp
);
179 /** Copy block of image data into the bitmap.
180 Assumes that the Bitmap has been constructed with the desired size.
182 BitmapEx
CreateFromData( RawBitmap
&& rawBitmap
)
184 auto nBitCount
= rawBitmap
.GetBitCount();
185 assert( nBitCount
== 24 || nBitCount
== 32);
186 Bitmap
aBmp( rawBitmap
.maSize
, nBitCount
);
188 BitmapScopedWriteAccess
pWrite(aBmp
);
189 assert(pWrite
.get());
192 std::unique_ptr
<AlphaMask
> pAlphaMask
;
193 AlphaScopedWriteAccess xMaskAcc
;
196 pAlphaMask
.reset( new AlphaMask( rawBitmap
.maSize
) );
197 xMaskAcc
= AlphaScopedWriteAccess(*pAlphaMask
);
200 auto nHeight
= rawBitmap
.maSize
.getHeight();
201 auto nWidth
= rawBitmap
.maSize
.getWidth();
202 auto nStride
= nWidth
* nBitCount
/ 8;
203 for( long y
= 0; y
< nHeight
; ++y
)
205 sal_uInt8
const *p
= rawBitmap
.mpData
.get() + (y
* nStride
);
206 Scanline pScanline
= pWrite
->GetScanline(y
);
207 for (long x
= 0; x
< nWidth
; ++x
)
209 BitmapColor
col(p
[0], p
[1], p
[2]);
210 pWrite
->SetPixelOnData(pScanline
, x
, col
);
215 p
= rawBitmap
.mpData
.get() + (y
* nStride
) + 3;
216 Scanline pMaskScanLine
= xMaskAcc
->GetScanline(y
);
217 for (long x
= 0; x
< nWidth
; ++x
)
219 xMaskAcc
->SetPixelOnData(pMaskScanLine
, x
, BitmapColor(*p
));
225 return BitmapEx(aBmp
, *pAlphaMask
);
227 return BitmapEx(aBmp
);
230 #if ENABLE_CAIRO_CANVAS
231 BitmapEx
* CreateFromCairoSurface(Size aSize
, cairo_surface_t
* pSurface
)
233 // FIXME: if we could teach VCL/ about cairo handles, life could
234 // be significantly better here perhaps.
236 #if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 12, 0)
237 cairo_surface_t
*pPixels
= cairo_surface_create_similar_image(pSurface
,
239 cairo_surface_t
*pPixels
= cairo_image_surface_create(
241 CAIRO_FORMAT_ARGB32
, aSize
.Width(), aSize
.Height());
242 cairo_t
*pCairo
= cairo_create( pPixels
);
243 if( !pPixels
|| !pCairo
|| cairo_status(pCairo
) != CAIRO_STATUS_SUCCESS
)
246 // suck ourselves from the X server to this buffer so then we can fiddle with
247 // Alpha to turn it into the ultra-lame vcl required format and then push it
248 // all back again later at vast expense [ urgh ]
249 cairo_set_source_surface( pCairo
, pSurface
, 0, 0 );
250 cairo_set_operator( pCairo
, CAIRO_OPERATOR_SOURCE
);
251 cairo_paint( pCairo
);
253 ::Bitmap
aRGB( aSize
, 24 );
254 ::AlphaMask
aMask( aSize
);
256 BitmapScopedWriteAccess
pRGBWrite(aRGB
);
261 AlphaScopedWriteAccess
pMaskWrite(aMask
);
266 cairo_surface_flush(pPixels
);
267 unsigned char *pSrc
= cairo_image_surface_get_data( pPixels
);
268 unsigned int nStride
= cairo_image_surface_get_stride( pPixels
);
269 vcl::bitmap::lookup_table unpremultiply_table
= vcl::bitmap::get_unpremultiply_table();
270 for( unsigned long y
= 0; y
< static_cast<unsigned long>(aSize
.Height()); y
++ )
272 sal_uInt32
*pPix
= reinterpret_cast<sal_uInt32
*>(pSrc
+ nStride
* y
);
273 for( unsigned long x
= 0; x
< static_cast<unsigned long>(aSize
.Width()); x
++ )
275 #if defined OSL_BIGENDIAN
276 sal_uInt8 nB
= (*pPix
>> 24);
277 sal_uInt8 nG
= (*pPix
>> 16) & 0xff;
278 sal_uInt8 nR
= (*pPix
>> 8) & 0xff;
279 sal_uInt8 nAlpha
= *pPix
& 0xff;
281 sal_uInt8 nAlpha
= (*pPix
>> 24);
282 sal_uInt8 nR
= (*pPix
>> 16) & 0xff;
283 sal_uInt8 nG
= (*pPix
>> 8) & 0xff;
284 sal_uInt8 nB
= *pPix
& 0xff;
286 if( nAlpha
!= 0 && nAlpha
!= 255 )
288 // Cairo uses pre-multiplied alpha - we do not => re-multiply
289 nR
= unpremultiply_table
[nAlpha
][nR
];
290 nG
= unpremultiply_table
[nAlpha
][nG
];
291 nB
= unpremultiply_table
[nAlpha
][nB
];
293 pRGBWrite
->SetPixel( y
, x
, BitmapColor( nR
, nG
, nB
) );
294 pMaskWrite
->SetPixelIndex( y
, x
, 255 - nAlpha
);
299 // ignore potential errors above. will get caller a
300 // uniformly white bitmap, but not that there would
301 // be error handling in calling code ...
302 ::BitmapEx
*pBitmapEx
= new ::BitmapEx( aRGB
, aMask
);
304 cairo_destroy( pCairo
);
305 cairo_surface_destroy( pPixels
);
310 BitmapEx
CanvasTransformBitmap( const BitmapEx
& rBitmap
,
311 const ::basegfx::B2DHomMatrix
& rTransform
,
312 ::basegfx::B2DRectangle
const & rDestRect
,
313 ::basegfx::B2DHomMatrix
const & rLocalTransform
)
315 const Size
aBmpSize( rBitmap
.GetSizePixel() );
316 Bitmap
aSrcBitmap( rBitmap
.GetBitmap() );
319 // differentiate mask and alpha channel (on-off
320 // vs. multi-level transparency)
321 if( rBitmap
.IsTransparent() )
323 if( rBitmap
.IsAlpha() )
324 aSrcAlpha
= rBitmap
.GetAlpha().GetBitmap();
326 aSrcAlpha
= rBitmap
.GetMask();
329 Bitmap::ScopedReadAccess
pReadAccess( aSrcBitmap
);
330 Bitmap::ScopedReadAccess
pAlphaReadAccess( rBitmap
.IsTransparent() ?
331 aSrcAlpha
.AcquireReadAccess() :
335 if( pReadAccess
.get() == nullptr ||
336 (pAlphaReadAccess
.get() == nullptr && rBitmap
.IsTransparent()) )
338 // TODO(E2): Error handling!
339 ENSURE_OR_THROW( false,
340 "transformBitmap(): could not access source bitmap" );
343 // mapping table, to translate pAlphaReadAccess' pixel
344 // values into destination alpha values (needed e.g. for
345 // paletted 1-bit masks).
346 sal_uInt8 aAlphaMap
[256];
348 if( rBitmap
.IsTransparent() )
350 if( rBitmap
.IsAlpha() )
352 // source already has alpha channel - 1:1 mapping,
353 // i.e. aAlphaMap[0]=0,...,aAlphaMap[255]=255.
355 sal_uInt8
* pCur
=aAlphaMap
;
356 sal_uInt8
* const pEnd
=&aAlphaMap
[256];
362 // mask transparency - determine used palette colors
363 const BitmapColor
& rCol0( pAlphaReadAccess
->GetPaletteColor( 0 ) );
364 const BitmapColor
& rCol1( pAlphaReadAccess
->GetPaletteColor( 1 ) );
366 // shortcut for true luminance calculation
367 // (assumes that palette is grey-level)
368 aAlphaMap
[0] = rCol0
.GetRed();
369 aAlphaMap
[1] = rCol1
.GetRed();
372 // else: mapping table is not used
374 const Size
aDestBmpSize( ::basegfx::fround( rDestRect
.getWidth() ),
375 ::basegfx::fround( rDestRect
.getHeight() ) );
377 if( aDestBmpSize
.Width() == 0 || aDestBmpSize
.Height() == 0 )
380 Bitmap
aDstBitmap( aDestBmpSize
, aSrcBitmap
.GetBitCount(), &pReadAccess
->GetPalette() );
381 Bitmap
aDstAlpha( AlphaMask( aDestBmpSize
).GetBitmap() );
384 // just to be on the safe side: let the
385 // ScopedAccessors get destructed before
386 // copy-constructing the resulting bitmap. This will
387 // rule out the possibility that cached accessor data
388 // is not yet written back.
389 BitmapScopedWriteAccess
pWriteAccess( aDstBitmap
);
390 BitmapScopedWriteAccess
pAlphaWriteAccess( aDstAlpha
);
393 if( pWriteAccess
.get() != nullptr &&
394 pAlphaWriteAccess
.get() != nullptr &&
395 rTransform
.isInvertible() )
397 // we're doing inverse mapping here, i.e. mapping
398 // points from the destination bitmap back to the
400 ::basegfx::B2DHomMatrix
aTransform( rLocalTransform
);
403 // for the time being, always read as ARGB
404 for( long y
=0; y
<aDestBmpSize
.Height(); ++y
)
406 // differentiate mask and alpha channel (on-off
407 // vs. multi-level transparency)
408 if( rBitmap
.IsTransparent() )
410 Scanline pScan
= pWriteAccess
->GetScanline( y
);
411 Scanline pScanAlpha
= pAlphaWriteAccess
->GetScanline( y
);
412 // Handling alpha and mask just the same...
413 for( long x
=0; x
<aDestBmpSize
.Width(); ++x
)
415 ::basegfx::B2DPoint
aPoint(x
,y
);
416 aPoint
*= aTransform
;
418 const int nSrcX( ::basegfx::fround( aPoint
.getX() ) );
419 const int nSrcY( ::basegfx::fround( aPoint
.getY() ) );
420 if( nSrcX
< 0 || nSrcX
>= aBmpSize
.Width() ||
421 nSrcY
< 0 || nSrcY
>= aBmpSize
.Height() )
423 pAlphaWriteAccess
->SetPixelOnData( pScanAlpha
, x
, BitmapColor(255) );
427 const sal_uInt8 cAlphaIdx
= pAlphaReadAccess
->GetPixelIndex( nSrcY
, nSrcX
);
428 pAlphaWriteAccess
->SetPixelOnData( pScanAlpha
, x
, BitmapColor(aAlphaMap
[ cAlphaIdx
]) );
429 pWriteAccess
->SetPixelOnData( pScan
, x
, pReadAccess
->GetPixel( nSrcY
, nSrcX
) );
435 Scanline pScan
= pWriteAccess
->GetScanline( y
);
436 Scanline pScanAlpha
= pAlphaWriteAccess
->GetScanline( y
);
437 for( long x
=0; x
<aDestBmpSize
.Width(); ++x
)
439 ::basegfx::B2DPoint
aPoint(x
,y
);
440 aPoint
*= aTransform
;
442 const int nSrcX( ::basegfx::fround( aPoint
.getX() ) );
443 const int nSrcY( ::basegfx::fround( aPoint
.getY() ) );
444 if( nSrcX
< 0 || nSrcX
>= aBmpSize
.Width() ||
445 nSrcY
< 0 || nSrcY
>= aBmpSize
.Height() )
447 pAlphaWriteAccess
->SetPixelOnData( pScanAlpha
, x
, BitmapColor(255) );
451 pAlphaWriteAccess
->SetPixelOnData( pScanAlpha
, x
, BitmapColor(0) );
452 pWriteAccess
->SetPixelOnData( pScan
, x
, pReadAccess
->GetPixel( nSrcY
,
461 // TODO(E2): Error handling!
462 ENSURE_OR_THROW( false,
463 "transformBitmap(): could not access bitmap" );
467 return BitmapEx(aDstBitmap
, AlphaMask(aDstAlpha
));
471 void DrawAlphaBitmapAndAlphaGradient(BitmapEx
& rBitmapEx
, bool bFixedTransparence
, float fTransparence
, AlphaMask
& rNewMask
)
473 // mix existing and new alpha mask
476 if(rBitmapEx
.IsAlpha())
478 aOldMask
= rBitmapEx
.GetAlpha();
480 else if(TransparentType::Bitmap
== rBitmapEx
.GetTransparentType())
482 aOldMask
= rBitmapEx
.GetMask();
484 else if(TransparentType::Color
== rBitmapEx
.GetTransparentType())
486 aOldMask
= rBitmapEx
.GetBitmap().CreateMask(rBitmapEx
.GetTransparentColor());
490 AlphaScopedWriteAccess
pOld(aOldMask
);
492 assert(pOld
&& "Got no access to old alpha mask (!)");
494 const double fFactor(1.0 / 255.0);
496 if(bFixedTransparence
)
498 const double fOpNew(1.0 - fTransparence
);
500 for(long y(0); y
< pOld
->Height(); y
++)
502 Scanline pScanline
= pOld
->GetScanline( y
);
503 for(long x(0); x
< pOld
->Width(); x
++)
505 const double fOpOld(1.0 - (pOld
->GetIndexFromData(pScanline
, x
) * fFactor
));
506 const sal_uInt8
aCol(basegfx::fround((1.0 - (fOpOld
* fOpNew
)) * 255.0));
508 pOld
->SetPixelOnData(pScanline
, x
, BitmapColor(aCol
));
514 AlphaMask::ScopedReadAccess
pNew(rNewMask
);
516 assert(pNew
&& "Got no access to new alpha mask (!)");
518 assert(pOld
->Width() == pNew
->Width() && pOld
->Height() == pNew
->Height() &&
519 "Alpha masks have different sizes (!)");
521 for(long y(0); y
< pOld
->Height(); y
++)
523 Scanline pScanline
= pOld
->GetScanline( y
);
524 for(long x(0); x
< pOld
->Width(); x
++)
526 const double fOpOld(1.0 - (pOld
->GetIndexFromData(pScanline
, x
) * fFactor
));
527 const double fOpNew(1.0 - (pNew
->GetIndexFromData(pScanline
, x
) * fFactor
));
528 const sal_uInt8
aCol(basegfx::fround((1.0 - (fOpOld
* fOpNew
)) * 255.0));
530 pOld
->SetPixelOnData(pScanline
, x
, BitmapColor(aCol
));
537 // apply combined bitmap as mask
538 rBitmapEx
= BitmapEx(rBitmapEx
.GetBitmap(), aOldMask
);
542 void DrawAndClipBitmap(const Point
& rPos
, const Size
& rSize
, const BitmapEx
& rBitmap
, BitmapEx
& aBmpEx
, basegfx::B2DPolyPolygon
const & rClipPath
)
544 ScopedVclPtrInstance
< VirtualDevice
> pVDev
;
545 MapMode
aMapMode( MapUnit::Map100thMM
);
546 aMapMode
.SetOrigin( Point( -rPos
.X(), -rPos
.Y() ) );
547 const Size
aOutputSizePixel( pVDev
->LogicToPixel( rSize
, aMapMode
) );
548 const Size
aSizePixel( rBitmap
.GetSizePixel() );
549 if ( aOutputSizePixel
.Width() && aOutputSizePixel
.Height() )
551 aMapMode
.SetScaleX( Fraction( aSizePixel
.Width(), aOutputSizePixel
.Width() ) );
552 aMapMode
.SetScaleY( Fraction( aSizePixel
.Height(), aOutputSizePixel
.Height() ) );
554 pVDev
->SetMapMode( aMapMode
);
555 pVDev
->SetOutputSizePixel( aSizePixel
);
556 pVDev
->SetFillColor( COL_BLACK
);
557 const tools::PolyPolygon
aClip( rClipPath
);
558 pVDev
->DrawPolyPolygon( aClip
);
560 // #i50672# Extract whole VDev content (to match size of rBitmap)
561 pVDev
->EnableMapMode( false );
562 const Bitmap
aVDevMask(pVDev
->GetBitmap(Point(), aSizePixel
));
564 if(aBmpEx
.IsTransparent())
566 // bitmap already uses a Mask or Alpha, we need to blend that with
567 // the new masking in pVDev
570 // need to blend in AlphaMask quality (8Bit)
571 AlphaMask
fromVDev(aVDevMask
);
572 AlphaMask
fromBmpEx(aBmpEx
.GetAlpha());
573 AlphaMask::ScopedReadAccess
pR(fromVDev
);
574 AlphaScopedWriteAccess
pW(fromBmpEx
);
578 const long nWidth(std::min(pR
->Width(), pW
->Width()));
579 const long nHeight(std::min(pR
->Height(), pW
->Height()));
581 for(long nY(0); nY
< nHeight
; nY
++)
583 Scanline pScanlineR
= pR
->GetScanline( nY
);
584 Scanline pScanlineW
= pW
->GetScanline( nY
);
585 for(long nX(0); nX
< nWidth
; nX
++)
587 const sal_uInt8
nIndR(pR
->GetIndexFromData(pScanlineR
, nX
));
588 const sal_uInt8
nIndW(pW
->GetIndexFromData(pScanlineW
, nX
));
590 // these values represent transparency (0 == no, 255 == fully transparent),
591 // so to blend these we have to multiply the inverse (opacity)
592 // and re-invert the result to transparence
593 const sal_uInt8
nCombined(0x00ff - (((0x00ff - nIndR
) * (0x00ff - nIndW
)) >> 8));
595 pW
->SetPixelOnData(pScanlineW
, nX
, BitmapColor(nCombined
));
602 aBmpEx
= BitmapEx(aBmpEx
.GetBitmap(), fromBmpEx
);
606 // need to blend in Mask quality (1Bit)
607 Bitmap
aMask(aVDevMask
.CreateMask(COL_WHITE
));
609 if ( rBitmap
.GetTransparentColor() == COL_WHITE
)
611 aMask
.CombineSimple( rBitmap
.GetMask(), BmpCombine::Or
);
615 aMask
.CombineSimple( rBitmap
.GetMask(), BmpCombine::And
);
618 aBmpEx
= BitmapEx( rBitmap
.GetBitmap(), aMask
);
623 // no mask yet, create and add new mask. For better quality, use Alpha,
624 // this allows the drawn mask being processed with AntiAliasing (AAed)
625 aBmpEx
= BitmapEx(rBitmap
.GetBitmap(), aVDevMask
);
630 css::uno::Sequence
< sal_Int8
> GetMaskDIB(BitmapEx
const & aBmpEx
)
632 if ( aBmpEx
.IsAlpha() )
635 WriteDIB(aBmpEx
.GetAlpha().GetBitmap(), aMem
, false, true);
636 return css::uno::Sequence
< sal_Int8
>( static_cast<sal_Int8
const *>(aMem
.GetData()), aMem
.Tell() );
638 else if ( aBmpEx
.IsTransparent() )
641 WriteDIB(aBmpEx
.GetMask(), aMem
, false, true);
642 return css::uno::Sequence
< sal_Int8
>( static_cast<sal_Int8
const *>(aMem
.GetData()), aMem
.Tell() );
645 return css::uno::Sequence
< sal_Int8
>();
648 static bool readAlpha( BitmapReadAccess
const * pAlphaReadAcc
, long nY
, const long nWidth
, unsigned char* data
, long nOff
)
650 bool bIsAlpha
= false;
657 switch( pAlphaReadAcc
->GetScanlineFormat() )
659 case ScanlineFormat::N8BitTcMask
:
660 pReadScan
= pAlphaReadAcc
->GetScanline( nY
);
661 for( nX
= 0; nX
< nWidth
; nX
++ )
663 nAlpha
= data
[ nOff
] = 255 - ( *pReadScan
++ );
669 case ScanlineFormat::N8BitPal
:
670 pReadScan
= pAlphaReadAcc
->GetScanline( nY
);
671 for( nX
= 0; nX
< nWidth
; nX
++ )
673 BitmapColor
const& rColor(
674 pAlphaReadAcc
->GetPaletteColor(*pReadScan
));
676 nAlpha
= data
[ nOff
] = 255 - rColor
.GetIndex();
683 SAL_INFO( "canvas.cairo", "fallback to GetColor for alpha - slow, format: " << static_cast<int>(pAlphaReadAcc
->GetScanlineFormat()) );
684 for( nX
= 0; nX
< nWidth
; nX
++ )
686 nAlpha
= data
[ nOff
] = 255 - pAlphaReadAcc
->GetColor( nY
, nX
).GetIndex();
699 * @param data will be filled with alpha data, if xBitmap is alpha/transparent image
700 * @param bHasAlpha will be set to true if resulting surface has alpha
702 void CanvasCairoExtractBitmapData( BitmapEx
const & aBmpEx
, Bitmap
& aBitmap
, unsigned char*& data
, bool& bHasAlpha
, long& rnWidth
, long& rnHeight
)
704 AlphaMask aAlpha
= aBmpEx
.GetAlpha();
706 ::BitmapReadAccess
* pBitmapReadAcc
= aBitmap
.AcquireReadAccess();
707 ::BitmapReadAccess
* pAlphaReadAcc
= nullptr;
708 const long nWidth
= rnWidth
= pBitmapReadAcc
->Width();
709 const long nHeight
= rnHeight
= pBitmapReadAcc
->Height();
711 bool bIsAlpha
= false;
713 if( aBmpEx
.IsTransparent() || aBmpEx
.IsAlpha() )
714 pAlphaReadAcc
= aAlpha
.AcquireReadAccess();
716 data
= static_cast<unsigned char*>(malloc( nWidth
*nHeight
*4 ));
720 unsigned int nAlpha
= 255;
722 vcl::bitmap::lookup_table premultiply_table
= vcl::bitmap::get_premultiply_table();
723 for( nY
= 0; nY
< nHeight
; nY
++ )
725 ::Scanline pReadScan
;
727 switch( pBitmapReadAcc
->GetScanlineFormat() )
729 case ScanlineFormat::N8BitPal
:
730 pReadScan
= pBitmapReadAcc
->GetScanline( nY
);
732 if( readAlpha( pAlphaReadAcc
, nY
, nWidth
, data
, nOff
) )
735 for( nX
= 0; nX
< nWidth
; nX
++ )
739 nAlpha
= data
[ nOff
++ ];
741 nAlpha
= data
[ nOff
++ ] = 255;
744 nAlpha
= data
[ nOff
+ 3 ];
746 nAlpha
= data
[ nOff
+ 3 ] = 255;
748 aColor
= pBitmapReadAcc
->GetPaletteColor(*pReadScan
++);
751 data
[ nOff
++ ] = premultiply_table
[nAlpha
][aColor
.GetRed()];
752 data
[ nOff
++ ] = premultiply_table
[nAlpha
][aColor
.GetGreen()];
753 data
[ nOff
++ ] = premultiply_table
[nAlpha
][aColor
.GetBlue()];
755 data
[ nOff
++ ] = premultiply_table
[nAlpha
][aColor
.GetBlue()];
756 data
[ nOff
++ ] = premultiply_table
[nAlpha
][aColor
.GetGreen()];
757 data
[ nOff
++ ] = premultiply_table
[nAlpha
][aColor
.GetRed()];
762 case ScanlineFormat::N24BitTcBgr
:
763 pReadScan
= pBitmapReadAcc
->GetScanline( nY
);
765 if( readAlpha( pAlphaReadAcc
, nY
, nWidth
, data
, nOff
) )
768 for( nX
= 0; nX
< nWidth
; nX
++ )
772 nAlpha
= data
[ nOff
];
774 nAlpha
= data
[ nOff
] = 255;
775 data
[ nOff
+ 3 ] = premultiply_table
[nAlpha
][*pReadScan
++];
776 data
[ nOff
+ 2 ] = premultiply_table
[nAlpha
][*pReadScan
++];
777 data
[ nOff
+ 1 ] = premultiply_table
[nAlpha
][*pReadScan
++];
781 nAlpha
= data
[ nOff
+ 3 ];
783 nAlpha
= data
[ nOff
+ 3 ] = 255;
784 data
[ nOff
++ ] = premultiply_table
[nAlpha
][*pReadScan
++];
785 data
[ nOff
++ ] = premultiply_table
[nAlpha
][*pReadScan
++];
786 data
[ nOff
++ ] = premultiply_table
[nAlpha
][*pReadScan
++];
791 case ScanlineFormat::N24BitTcRgb
:
792 pReadScan
= pBitmapReadAcc
->GetScanline( nY
);
794 if( readAlpha( pAlphaReadAcc
, nY
, nWidth
, data
, nOff
) )
797 for( nX
= 0; nX
< nWidth
; nX
++ )
801 nAlpha
= data
[ nOff
++ ];
803 nAlpha
= data
[ nOff
++ ] = 255;
804 data
[ nOff
++ ] = premultiply_table
[nAlpha
][*pReadScan
++];
805 data
[ nOff
++ ] = premultiply_table
[nAlpha
][*pReadScan
++];
806 data
[ nOff
++ ] = premultiply_table
[nAlpha
][*pReadScan
++];
809 nAlpha
= data
[ nOff
+ 3 ];
811 nAlpha
= data
[ nOff
+ 3 ] = 255;
812 data
[ nOff
++ ] = premultiply_table
[nAlpha
][pReadScan
[ 2 ]];
813 data
[ nOff
++ ] = premultiply_table
[nAlpha
][pReadScan
[ 1 ]];
814 data
[ nOff
++ ] = premultiply_table
[nAlpha
][pReadScan
[ 0 ]];
820 case ScanlineFormat::N32BitTcBgra
:
821 pReadScan
= pBitmapReadAcc
->GetScanline( nY
);
823 if( readAlpha( pAlphaReadAcc
, nY
, nWidth
, data
, nOff
) )
826 for( nX
= 0; nX
< nWidth
; nX
++ )
830 nAlpha
= data
[ nOff
++ ];
832 nAlpha
= data
[ nOff
++ ] = 255;
833 data
[ nOff
++ ] = premultiply_table
[nAlpha
][pReadScan
[ 2 ]];
834 data
[ nOff
++ ] = premultiply_table
[nAlpha
][pReadScan
[ 1 ]];
835 data
[ nOff
++ ] = premultiply_table
[nAlpha
][pReadScan
[ 0 ]];
839 nAlpha
= data
[ nOff
+ 3 ];
841 nAlpha
= data
[ nOff
+ 3 ] = 255;
842 data
[ nOff
++ ] = premultiply_table
[nAlpha
][*pReadScan
++];
843 data
[ nOff
++ ] = premultiply_table
[nAlpha
][*pReadScan
++];
844 data
[ nOff
++ ] = premultiply_table
[nAlpha
][*pReadScan
++];
850 case ScanlineFormat::N32BitTcRgba
:
851 pReadScan
= pBitmapReadAcc
->GetScanline( nY
);
853 if( readAlpha( pAlphaReadAcc
, nY
, nWidth
, data
, nOff
) )
856 for( nX
= 0; nX
< nWidth
; nX
++ )
860 nAlpha
= data
[ nOff
++ ];
862 nAlpha
= data
[ nOff
++ ] = 255;
863 data
[ nOff
++ ] = premultiply_table
[nAlpha
][*pReadScan
++];
864 data
[ nOff
++ ] = premultiply_table
[nAlpha
][*pReadScan
++];
865 data
[ nOff
++ ] = premultiply_table
[nAlpha
][*pReadScan
++];
869 nAlpha
= data
[ nOff
+ 3 ];
871 nAlpha
= data
[ nOff
+ 3 ] = 255;
872 data
[ nOff
++ ] = premultiply_table
[nAlpha
][pReadScan
[ 2 ]];
873 data
[ nOff
++ ] = premultiply_table
[nAlpha
][pReadScan
[ 1 ]];
874 data
[ nOff
++ ] = premultiply_table
[nAlpha
][pReadScan
[ 0 ]];
881 SAL_INFO( "canvas.cairo", "fallback to GetColor - slow, format: " << static_cast<int>(pBitmapReadAcc
->GetScanlineFormat()) );
884 if( readAlpha( pAlphaReadAcc
, nY
, nWidth
, data
, nOff
) )
887 for( nX
= 0; nX
< nWidth
; nX
++ )
889 aColor
= pBitmapReadAcc
->GetColor( nY
, nX
);
891 // cairo need premultiplied color values
892 // TODO(rodo) handle endianness
895 nAlpha
= data
[ nOff
++ ];
897 nAlpha
= data
[ nOff
++ ] = 255;
898 data
[ nOff
++ ] = premultiply_table
[nAlpha
][aColor
.GetRed()];
899 data
[ nOff
++ ] = premultiply_table
[nAlpha
][aColor
.GetGreen()];
900 data
[ nOff
++ ] = premultiply_table
[nAlpha
][aColor
.GetBlue()];
903 nAlpha
= data
[ nOff
+ 3 ];
905 nAlpha
= data
[ nOff
+ 3 ] = 255;
906 data
[ nOff
++ ] = premultiply_table
[nAlpha
][aColor
.GetBlue()];
907 data
[ nOff
++ ] = premultiply_table
[nAlpha
][aColor
.GetGreen()];
908 data
[ nOff
++ ] = premultiply_table
[nAlpha
][aColor
.GetRed()];
915 ::Bitmap::ReleaseAccess( pBitmapReadAcc
);
917 aAlpha
.ReleaseAccess( pAlphaReadAcc
);
919 bHasAlpha
= bIsAlpha
;
923 uno::Sequence
< sal_Int8
> CanvasExtractBitmapData(BitmapEx
const & rBitmapEx
, const geometry::IntegerRectangle2D
& rect
)
925 Bitmap
aBitmap( rBitmapEx
.GetBitmap() );
926 Bitmap
aAlpha( rBitmapEx
.GetAlpha().GetBitmap() );
928 Bitmap::ScopedReadAccess
pReadAccess( aBitmap
);
929 Bitmap::ScopedReadAccess
pAlphaReadAccess( aAlpha
.IsEmpty() ?
930 nullptr : aAlpha
.AcquireReadAccess(),
933 assert( pReadAccess
);
935 // TODO(F1): Support more formats.
936 const Size
aBmpSize( aBitmap
.GetSizePixel() );
938 // for the time being, always return as BGRA
939 uno::Sequence
< sal_Int8
> aRes( 4*aBmpSize
.Width()*aBmpSize
.Height() );
940 sal_Int8
* pRes
= aRes
.getArray();
944 y
<aBmpSize
.Height() && y
<rect
.Y2
;
947 if( pAlphaReadAccess
.get() != nullptr )
949 Scanline pScanlineReadAlpha
= pAlphaReadAccess
->GetScanline( y
);
951 x
<aBmpSize
.Width() && x
<rect
.X2
;
954 pRes
[ nCurrPos
++ ] = pReadAccess
->GetColor( y
, x
).GetRed();
955 pRes
[ nCurrPos
++ ] = pReadAccess
->GetColor( y
, x
).GetGreen();
956 pRes
[ nCurrPos
++ ] = pReadAccess
->GetColor( y
, x
).GetBlue();
957 pRes
[ nCurrPos
++ ] = pAlphaReadAccess
->GetIndexFromData( pScanlineReadAlpha
, x
);
963 x
<aBmpSize
.Width() && x
<rect
.X2
;
966 pRes
[ nCurrPos
++ ] = pReadAccess
->GetColor( y
, x
).GetRed();
967 pRes
[ nCurrPos
++ ] = pReadAccess
->GetColor( y
, x
).GetGreen();
968 pRes
[ nCurrPos
++ ] = pReadAccess
->GetColor( y
, x
).GetBlue();
969 pRes
[ nCurrPos
++ ] = sal_uInt8(255);
976 BitmapEx
createHistorical8x8FromArray(std::array
<sal_uInt8
,64> const & pArray
, Color aColorPix
, Color aColorBack
)
978 BitmapPalette
aPalette(2);
980 aPalette
[0] = BitmapColor(aColorBack
);
981 aPalette
[1] = BitmapColor(aColorPix
);
983 Bitmap
aBitmap(Size(8, 8), 1, &aPalette
);
984 BitmapWriteAccess
* pContent(aBitmap
.AcquireWriteAccess());
986 for(sal_uInt16
a(0); a
< 8; a
++)
988 for(sal_uInt16
b(0); b
< 8; b
++)
990 if(pArray
[(a
* 8) + b
])
992 pContent
->SetPixelIndex(a
, b
, 1);
996 pContent
->SetPixelIndex(a
, b
, 0);
1001 return BitmapEx(aBitmap
);
1004 bool isHistorical8x8(const BitmapEx
& rBitmapEx
, Color
& o_rBack
, Color
& o_rFront
)
1008 if(!rBitmapEx
.IsTransparent())
1010 Bitmap
aBitmap(rBitmapEx
.GetBitmap());
1012 if(8 == aBitmap
.GetSizePixel().Width() && 8 == aBitmap
.GetSizePixel().Height())
1014 if(2 == aBitmap
.GetColorCount())
1016 BitmapReadAccess
* pRead
= aBitmap
.AcquireReadAccess();
1020 if(pRead
->HasPalette() && 2 == pRead
->GetPaletteEntryCount())
1022 const BitmapPalette
& rPalette
= pRead
->GetPalette();
1024 // #i123564# background and foreground were exchanged; of course
1025 // rPalette[0] is the background color
1026 o_rFront
= rPalette
[1];
1027 o_rBack
= rPalette
[0];
1032 Bitmap::ReleaseAccess(pRead
);
1041 sal_uInt8
unpremultiply(sal_uInt8 c
, sal_uInt8 a
)
1043 return (a
== 0) ? 0 : (c
* 255 + a
/ 2) / a
;
1046 sal_uInt8
premultiply(sal_uInt8 c
, sal_uInt8 a
)
1048 return (c
* a
+ 127) / 255;
1051 lookup_table
get_unpremultiply_table()
1054 static sal_uInt8 unpremultiply_table
[256][256];
1058 for (int a
= 0; a
< 256; ++a
)
1059 for (int c
= 0; c
< 256; ++c
)
1060 unpremultiply_table
[a
][c
] = unpremultiply(c
, a
);
1064 return unpremultiply_table
;
1067 lookup_table
get_premultiply_table()
1070 static sal_uInt8 premultiply_table
[256][256];
1074 for (int a
= 0; a
< 256; ++a
)
1075 for (int c
= 0; c
< 256; ++c
)
1076 premultiply_table
[a
][c
] = premultiply(c
, a
);
1080 return premultiply_table
;
1083 bool convertBitmap32To24Plus8(BitmapEx
const & rInput
, BitmapEx
& rResult
)
1085 Bitmap
aBitmap(rInput
.GetBitmap());
1086 if (aBitmap
.GetBitCount() != 32)
1089 Size aSize
= aBitmap
.GetSizePixel();
1090 Bitmap
aResultBitmap(aSize
, 24);
1091 AlphaMask
aResultAlpha(aSize
);
1093 BitmapScopedWriteAccess
pResultBitmapAccess(aResultBitmap
);
1094 AlphaScopedWriteAccess
pResultAlphaAccess(aResultAlpha
);
1096 Bitmap::ScopedReadAccess
pReadAccess(aBitmap
);
1098 for (long nY
= 0; nY
< aSize
.Height(); ++nY
)
1100 Scanline aResultScan
= pResultBitmapAccess
->GetScanline(nY
);
1101 Scanline aResultScanAlpha
= pResultAlphaAccess
->GetScanline(nY
);
1103 Scanline aReadScan
= pReadAccess
->GetScanline(nY
);
1105 for (long nX
= 0; nX
< aSize
.Width(); ++nX
)
1107 const BitmapColor aColor
= pReadAccess
->GetPixelFromData(aReadScan
, nX
);
1108 BitmapColor
aResultColor(aColor
.GetRed(), aColor
.GetGreen(), aColor
.GetBlue());
1109 BitmapColor
aResultColorAlpha(aColor
.GetAlpha(), aColor
.GetAlpha(), aColor
.GetAlpha());
1111 pResultBitmapAccess
->SetPixelOnData(aResultScan
, nX
, aResultColor
);
1112 pResultAlphaAccess
->SetPixelOnData(aResultScanAlpha
, nX
, aResultColorAlpha
);
1116 if (rInput
.IsTransparent())
1117 rResult
= BitmapEx(aResultBitmap
, rInput
.GetAlpha());
1119 rResult
= BitmapEx(aResultBitmap
, aResultAlpha
);
1123 }} // end vcl::bitmap
1125 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */