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 <sal/config.h>
16 #include <vcl/BitmapTools.hxx>
18 #include <sal/log.hxx>
19 #include <comphelper/processfactory.hxx>
20 #include <comphelper/seqstream.hxx>
21 #include <vcl/canvastools.hxx>
22 #include <basegfx/matrix/b2dhommatrix.hxx>
24 #include <com/sun/star/graphic/SvgTools.hpp>
25 #include <com/sun/star/graphic/Primitive2DTools.hpp>
27 #include <drawinglayer/primitive2d/baseprimitive2d.hxx>
29 #include <com/sun/star/rendering/XIntegerReadOnlyBitmap.hpp>
31 #include <vcl/dibtools.hxx>
32 #include <vcl/settings.hxx>
33 #include <vcl/svapp.hxx>
34 #include <vcl/virdev.hxx>
35 #if ENABLE_CAIRO_CANVAS
38 #include <tools/diagnose_ex.h>
39 #include <tools/fract.hxx>
40 #include <tools/stream.hxx>
41 #include <bitmap/BitmapWriteAccess.hxx>
45 using drawinglayer::primitive2d::Primitive2DSequence
;
46 using drawinglayer::primitive2d::Primitive2DReference
;
51 BitmapEx
loadFromName(const OUString
& rFileName
, const ImageLoadFlags eFlags
)
58 aIconTheme
= Application::GetSettings().GetStyleSettings().DetermineIconTheme();
59 ImageTree::get().loadImage(rFileName
, aIconTheme
, aBitmapEx
, true, eFlags
);
66 SAL_WARN_IF(!bSuccess
, "vcl", "vcl::bitmap::loadFromName : could not load image " << rFileName
<< " via icon theme " << aIconTheme
);
71 void loadFromSvg(SvStream
& rStream
, const OUString
& sPath
, BitmapEx
& rBitmapEx
, double fScalingFactor
)
73 uno::Reference
<uno::XComponentContext
> xContext(comphelper::getProcessComponentContext());
74 const uno::Reference
<graphic::XSvgParser
> xSvgParser
= graphic::SvgTools::create(xContext
);
76 std::size_t nSize
= rStream
.remainingSize();
77 std::vector
<sal_Int8
> aBuffer(nSize
+ 1);
78 rStream
.ReadBytes(aBuffer
.data(), nSize
);
81 uno::Sequence
<sal_Int8
> aData(aBuffer
.data(), nSize
+ 1);
82 uno::Reference
<io::XInputStream
> aInputStream(new comphelper::SequenceInputStream(aData
));
84 const Primitive2DSequence aPrimitiveSequence
= xSvgParser
->getDecomposition(aInputStream
, sPath
);
86 if (!aPrimitiveSequence
.hasElements())
89 uno::Sequence
<beans::PropertyValue
> aViewParameters
;
91 geometry::RealRectangle2D aRealRect
;
92 basegfx::B2DRange aRange
;
93 for (Primitive2DReference
const & xReference
: aPrimitiveSequence
)
97 aRealRect
= xReference
->getRange(aViewParameters
);
98 aRange
.expand(basegfx::B2DRange(aRealRect
.X1
, aRealRect
.Y1
, aRealRect
.X2
, aRealRect
.Y2
));
102 aRealRect
.X1
= aRange
.getMinX();
103 aRealRect
.Y1
= aRange
.getMinY();
104 aRealRect
.X2
= aRange
.getMaxX();
105 aRealRect
.Y2
= aRange
.getMaxY();
107 double nDPI
= 96 * fScalingFactor
;
109 const css::uno::Reference
<css::graphic::XPrimitive2DRenderer
> xPrimitive2DRenderer
= css::graphic::Primitive2DTools::create(xContext
);
110 const css::uno::Reference
<css::rendering::XBitmap
> xBitmap(
111 xPrimitive2DRenderer
->rasterize(aPrimitiveSequence
, aViewParameters
, nDPI
, nDPI
, aRealRect
, 256*256));
115 const css::uno::Reference
<css::rendering::XIntegerReadOnlyBitmap
> xIntBmp(xBitmap
, uno::UNO_QUERY_THROW
);
116 rBitmapEx
= vcl::unotools::bitmapExFromXBitmap(xIntBmp
);
121 /** Copy block of image data into the bitmap.
122 Assumes that the Bitmap has been constructed with the desired size.
125 The block of data to copy
127 The number of bytes in a scanline, must >= (width * nBitCount / 8)
129 In case the endianness of pData is wrong, you could reverse colors
131 BitmapEx
CreateFromData( sal_uInt8
const *pData
, sal_Int32 nWidth
, sal_Int32 nHeight
, sal_Int32 nStride
, vcl::PixelFormat ePixelFormat
, bool bReversColors
)
133 auto nBitCount
= sal_uInt16(ePixelFormat
);
135 assert(nStride
>= (nWidth
* nBitCount
/ 8));
136 assert(nBitCount
== 1 || nBitCount
== 24 || nBitCount
== 32);
138 Bitmap
aBmp(Size(nWidth
, nHeight
), ePixelFormat
);
140 BitmapScopedWriteAccess
pWrite(aBmp
);
141 assert(pWrite
.get());
144 std::unique_ptr
<AlphaMask
> pAlphaMask
;
145 AlphaScopedWriteAccess xMaskAcc
;
148 pAlphaMask
.reset( new AlphaMask( Size(nWidth
, nHeight
) ) );
149 xMaskAcc
= AlphaScopedWriteAccess(*pAlphaMask
);
153 for( tools::Long y
= 0; y
< nHeight
; ++y
)
155 sal_uInt8
const *p
= pData
+ y
* nStride
/ 8;
156 Scanline pScanline
= pWrite
->GetScanline(y
);
157 for (tools::Long x
= 0; x
< nWidth
; ++x
)
159 int bitIndex
= (y
* nStride
+ x
) % 8;
161 pWrite
->SetPixelOnData(pScanline
, x
, BitmapColor((*p
>> bitIndex
) & 1));
167 for( tools::Long y
= 0; y
< nHeight
; ++y
)
169 sal_uInt8
const *p
= pData
+ (y
* nStride
);
170 Scanline pScanline
= pWrite
->GetScanline(y
);
171 for (tools::Long x
= 0; x
< nWidth
; ++x
)
175 col
= BitmapColor( p
[2], p
[1], p
[0] );
177 col
= BitmapColor( p
[0], p
[1], p
[2] );
178 pWrite
->SetPixelOnData(pScanline
, x
, col
);
183 p
= pData
+ (y
* nStride
) + 3;
184 Scanline pMaskScanLine
= xMaskAcc
->GetScanline(y
);
185 for (tools::Long x
= 0; x
< nWidth
; ++x
)
187 xMaskAcc
->SetPixelOnData(pMaskScanLine
, x
, BitmapColor(*p
));
194 return BitmapEx(aBmp
, *pAlphaMask
);
196 return BitmapEx(aBmp
);
199 /** Copy block of image data into the bitmap.
200 Assumes that the Bitmap has been constructed with the desired size.
202 BitmapEx
CreateFromData( RawBitmap
&& rawBitmap
)
204 auto nBitCount
= rawBitmap
.GetBitCount();
205 assert( nBitCount
== 24 || nBitCount
== 32);
207 auto ePixelFormat
= vcl::PixelFormat::INVALID
;
210 ePixelFormat
= vcl::PixelFormat::N24_BPP
;
211 else if (nBitCount
== 32)
212 ePixelFormat
= vcl::PixelFormat::N32_BPP
;
214 assert(ePixelFormat
!= vcl::PixelFormat::INVALID
);
216 Bitmap
aBmp(rawBitmap
.maSize
, ePixelFormat
);
218 BitmapScopedWriteAccess
pWrite(aBmp
);
219 assert(pWrite
.get());
222 std::unique_ptr
<AlphaMask
> pAlphaMask
;
223 AlphaScopedWriteAccess xMaskAcc
;
226 pAlphaMask
.reset( new AlphaMask( rawBitmap
.maSize
) );
227 xMaskAcc
= AlphaScopedWriteAccess(*pAlphaMask
);
230 auto nHeight
= rawBitmap
.maSize
.getHeight();
231 auto nWidth
= rawBitmap
.maSize
.getWidth();
232 auto nStride
= nWidth
* nBitCount
/ 8;
233 for( tools::Long y
= 0; y
< nHeight
; ++y
)
235 sal_uInt8
const *p
= rawBitmap
.mpData
.get() + (y
* nStride
);
236 Scanline pScanline
= pWrite
->GetScanline(y
);
237 for (tools::Long x
= 0; x
< nWidth
; ++x
)
239 BitmapColor
col(p
[0], p
[1], p
[2]);
240 pWrite
->SetPixelOnData(pScanline
, x
, col
);
245 p
= rawBitmap
.mpData
.get() + (y
* nStride
) + 3;
246 Scanline pMaskScanLine
= xMaskAcc
->GetScanline(y
);
247 for (tools::Long x
= 0; x
< nWidth
; ++x
)
249 xMaskAcc
->SetPixelOnData(pMaskScanLine
, x
, BitmapColor(255 - *p
));
255 return BitmapEx(aBmp
, *pAlphaMask
);
257 return BitmapEx(aBmp
);
260 #if ENABLE_CAIRO_CANVAS
261 BitmapEx
* CreateFromCairoSurface(Size aSize
, cairo_surface_t
* pSurface
)
263 // FIXME: if we could teach VCL/ about cairo handles, life could
264 // be significantly better here perhaps.
266 #if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 12, 0)
267 cairo_surface_t
*pPixels
= cairo_surface_create_similar_image(pSurface
,
269 cairo_surface_t
*pPixels
= cairo_image_surface_create(
271 CAIRO_FORMAT_ARGB32
, aSize
.Width(), aSize
.Height());
272 cairo_t
*pCairo
= cairo_create( pPixels
);
273 if( !pPixels
|| !pCairo
|| cairo_status(pCairo
) != CAIRO_STATUS_SUCCESS
)
276 // suck ourselves from the X server to this buffer so then we can fiddle with
277 // Alpha to turn it into the ultra-lame vcl required format and then push it
278 // all back again later at vast expense [ urgh ]
279 cairo_set_source_surface( pCairo
, pSurface
, 0, 0 );
280 cairo_set_operator( pCairo
, CAIRO_OPERATOR_SOURCE
);
281 cairo_paint( pCairo
);
283 Bitmap
aRGB(aSize
, vcl::PixelFormat::N24_BPP
);
284 ::AlphaMask
aMask( aSize
);
286 BitmapScopedWriteAccess
pRGBWrite(aRGB
);
291 AlphaScopedWriteAccess
pMaskWrite(aMask
);
296 cairo_surface_flush(pPixels
);
297 unsigned char *pSrc
= cairo_image_surface_get_data( pPixels
);
298 unsigned int nStride
= cairo_image_surface_get_stride( pPixels
);
299 vcl::bitmap::lookup_table
const & unpremultiply_table
= vcl::bitmap::get_unpremultiply_table();
300 for( tools::Long y
= 0; y
< aSize
.Height(); y
++ )
302 sal_uInt32
*pPix
= reinterpret_cast<sal_uInt32
*>(pSrc
+ nStride
* y
);
303 for( tools::Long x
= 0; x
< aSize
.Width(); x
++ )
305 #if defined OSL_BIGENDIAN
306 sal_uInt8 nB
= (*pPix
>> 24);
307 sal_uInt8 nG
= (*pPix
>> 16) & 0xff;
308 sal_uInt8 nR
= (*pPix
>> 8) & 0xff;
309 sal_uInt8 nAlpha
= *pPix
& 0xff;
311 sal_uInt8 nAlpha
= (*pPix
>> 24);
312 sal_uInt8 nR
= (*pPix
>> 16) & 0xff;
313 sal_uInt8 nG
= (*pPix
>> 8) & 0xff;
314 sal_uInt8 nB
= *pPix
& 0xff;
316 if( nAlpha
!= 0 && nAlpha
!= 255 )
318 // Cairo uses pre-multiplied alpha - we do not => re-multiply
319 nR
= unpremultiply_table
[nAlpha
][nR
];
320 nG
= unpremultiply_table
[nAlpha
][nG
];
321 nB
= unpremultiply_table
[nAlpha
][nB
];
323 pRGBWrite
->SetPixel( y
, x
, BitmapColor( nR
, nG
, nB
) );
324 pMaskWrite
->SetPixelIndex( y
, x
, 255 - nAlpha
);
329 // ignore potential errors above. will get caller a
330 // uniformly white bitmap, but not that there would
331 // be error handling in calling code ...
332 ::BitmapEx
*pBitmapEx
= new ::BitmapEx( aRGB
, aMask
);
334 cairo_destroy( pCairo
);
335 cairo_surface_destroy( pPixels
);
340 BitmapEx
CanvasTransformBitmap( const BitmapEx
& rBitmap
,
341 const ::basegfx::B2DHomMatrix
& rTransform
,
342 ::basegfx::B2DRectangle
const & rDestRect
,
343 ::basegfx::B2DHomMatrix
const & rLocalTransform
)
345 const Size
aBmpSize( rBitmap
.GetSizePixel() );
346 Bitmap
aSrcBitmap( rBitmap
.GetBitmap() );
349 // differentiate mask and alpha channel (on-off
350 // vs. multi-level transparency)
351 if( rBitmap
.IsAlpha() )
353 aSrcAlpha
= rBitmap
.GetAlpha().GetBitmap();
356 Bitmap::ScopedReadAccess
pReadAccess( aSrcBitmap
);
357 Bitmap::ScopedReadAccess
pAlphaReadAccess( rBitmap
.IsAlpha() ?
358 aSrcAlpha
.AcquireReadAccess() :
362 if( pReadAccess
.get() == nullptr ||
363 (pAlphaReadAccess
.get() == nullptr && rBitmap
.IsAlpha()) )
365 // TODO(E2): Error handling!
366 ENSURE_OR_THROW( false,
367 "transformBitmap(): could not access source bitmap" );
370 // mapping table, to translate pAlphaReadAccess' pixel
371 // values into destination alpha values (needed e.g. for
372 // paletted 1-bit masks).
373 sal_uInt8 aAlphaMap
[256];
375 if( rBitmap
.IsAlpha() )
377 // source already has alpha channel - 1:1 mapping,
378 // i.e. aAlphaMap[0]=0,...,aAlphaMap[255]=255.
380 sal_uInt8
* pCur
=aAlphaMap
;
381 sal_uInt8
* const pEnd
=&aAlphaMap
[256];
385 // else: mapping table is not used
387 const Size
aDestBmpSize( ::basegfx::fround( rDestRect
.getWidth() ),
388 ::basegfx::fround( rDestRect
.getHeight() ) );
390 if( aDestBmpSize
.IsEmpty() )
393 Bitmap
aDstBitmap(aDestBmpSize
, aSrcBitmap
.getPixelFormat(), &pReadAccess
->GetPalette());
394 Bitmap
aDstAlpha( AlphaMask( aDestBmpSize
).GetBitmap() );
397 // just to be on the safe side: let the
398 // ScopedAccessors get destructed before
399 // copy-constructing the resulting bitmap. This will
400 // rule out the possibility that cached accessor data
401 // is not yet written back.
402 BitmapScopedWriteAccess
pWriteAccess( aDstBitmap
);
403 BitmapScopedWriteAccess
pAlphaWriteAccess( aDstAlpha
);
406 if( pWriteAccess
.get() != nullptr &&
407 pAlphaWriteAccess
.get() != nullptr &&
408 rTransform
.isInvertible() )
410 // we're doing inverse mapping here, i.e. mapping
411 // points from the destination bitmap back to the
413 ::basegfx::B2DHomMatrix
aTransform( rLocalTransform
);
416 // for the time being, always read as ARGB
417 for( tools::Long y
=0; y
<aDestBmpSize
.Height(); ++y
)
419 // differentiate mask and alpha channel (on-off
420 // vs. multi-level transparency)
421 if( rBitmap
.IsAlpha() )
423 Scanline pScan
= pWriteAccess
->GetScanline( y
);
424 Scanline pScanAlpha
= pAlphaWriteAccess
->GetScanline( y
);
425 // Handling alpha and mask just the same...
426 for( tools::Long x
=0; x
<aDestBmpSize
.Width(); ++x
)
428 ::basegfx::B2DPoint
aPoint(x
,y
);
429 aPoint
*= aTransform
;
431 const int nSrcX( ::basegfx::fround( aPoint
.getX() ) );
432 const int nSrcY( ::basegfx::fround( aPoint
.getY() ) );
433 if( nSrcX
< 0 || nSrcX
>= aBmpSize
.Width() ||
434 nSrcY
< 0 || nSrcY
>= aBmpSize
.Height() )
436 pAlphaWriteAccess
->SetPixelOnData( pScanAlpha
, x
, BitmapColor(255) );
440 const sal_uInt8 cAlphaIdx
= pAlphaReadAccess
->GetPixelIndex( nSrcY
, nSrcX
);
441 pAlphaWriteAccess
->SetPixelOnData( pScanAlpha
, x
, BitmapColor(aAlphaMap
[ cAlphaIdx
]) );
442 pWriteAccess
->SetPixelOnData( pScan
, x
, pReadAccess
->GetPixel( nSrcY
, nSrcX
) );
448 Scanline pScan
= pWriteAccess
->GetScanline( y
);
449 Scanline pScanAlpha
= pAlphaWriteAccess
->GetScanline( y
);
450 for( tools::Long x
=0; x
<aDestBmpSize
.Width(); ++x
)
452 ::basegfx::B2DPoint
aPoint(x
,y
);
453 aPoint
*= aTransform
;
455 const int nSrcX( ::basegfx::fround( aPoint
.getX() ) );
456 const int nSrcY( ::basegfx::fround( aPoint
.getY() ) );
457 if( nSrcX
< 0 || nSrcX
>= aBmpSize
.Width() ||
458 nSrcY
< 0 || nSrcY
>= aBmpSize
.Height() )
460 pAlphaWriteAccess
->SetPixelOnData( pScanAlpha
, x
, BitmapColor(255) );
464 pAlphaWriteAccess
->SetPixelOnData( pScanAlpha
, x
, BitmapColor(0) );
465 pWriteAccess
->SetPixelOnData( pScan
, x
, pReadAccess
->GetPixel( nSrcY
,
474 // TODO(E2): Error handling!
475 ENSURE_OR_THROW( false,
476 "transformBitmap(): could not access bitmap" );
480 return BitmapEx(aDstBitmap
, AlphaMask(aDstAlpha
));
484 void DrawAlphaBitmapAndAlphaGradient(BitmapEx
& rBitmapEx
, bool bFixedTransparence
, float fTransparence
, AlphaMask
& rNewMask
)
486 // mix existing and new alpha mask
489 if(rBitmapEx
.IsAlpha())
491 aOldMask
= rBitmapEx
.GetAlpha();
495 AlphaScopedWriteAccess
pOld(aOldMask
);
497 assert(pOld
&& "Got no access to old alpha mask (!)");
499 const double fFactor(1.0 / 255.0);
501 if(bFixedTransparence
)
503 const double fOpNew(1.0 - fTransparence
);
505 for(tools::Long
y(0); y
< pOld
->Height(); y
++)
507 Scanline pScanline
= pOld
->GetScanline( y
);
508 for(tools::Long
x(0); x
< pOld
->Width(); x
++)
510 const double fOpOld(1.0 - (pOld
->GetIndexFromData(pScanline
, x
) * fFactor
));
511 const sal_uInt8
aCol(basegfx::fround((1.0 - (fOpOld
* fOpNew
)) * 255.0));
513 pOld
->SetPixelOnData(pScanline
, x
, BitmapColor(aCol
));
519 AlphaMask::ScopedReadAccess
pNew(rNewMask
);
521 assert(pNew
&& "Got no access to new alpha mask (!)");
523 assert(pOld
->Width() == pNew
->Width() && pOld
->Height() == pNew
->Height() &&
524 "Alpha masks have different sizes (!)");
526 for(tools::Long
y(0); y
< pOld
->Height(); y
++)
528 Scanline pScanline
= pOld
->GetScanline( y
);
529 for(tools::Long
x(0); x
< pOld
->Width(); x
++)
531 const double fOpOld(1.0 - (pOld
->GetIndexFromData(pScanline
, x
) * fFactor
));
532 const double fOpNew(1.0 - (pNew
->GetIndexFromData(pScanline
, x
) * fFactor
));
533 const sal_uInt8
aCol(basegfx::fround((1.0 - (fOpOld
* fOpNew
)) * 255.0));
535 pOld
->SetPixelOnData(pScanline
, x
, BitmapColor(aCol
));
542 // apply combined bitmap as mask
543 rBitmapEx
= BitmapEx(rBitmapEx
.GetBitmap(), aOldMask
);
547 void DrawAndClipBitmap(const Point
& rPos
, const Size
& rSize
, const BitmapEx
& rBitmap
, BitmapEx
& aBmpEx
, basegfx::B2DPolyPolygon
const & rClipPath
)
549 ScopedVclPtrInstance
< VirtualDevice
> pVDev
;
550 MapMode
aMapMode( MapUnit::Map100thMM
);
551 aMapMode
.SetOrigin( Point( -rPos
.X(), -rPos
.Y() ) );
552 const Size
aOutputSizePixel( pVDev
->LogicToPixel( rSize
, aMapMode
) );
553 const Size
aSizePixel( rBitmap
.GetSizePixel() );
554 if ( aOutputSizePixel
.Width() && aOutputSizePixel
.Height() )
556 aMapMode
.SetScaleX( Fraction( aSizePixel
.Width(), aOutputSizePixel
.Width() ) );
557 aMapMode
.SetScaleY( Fraction( aSizePixel
.Height(), aOutputSizePixel
.Height() ) );
559 pVDev
->SetMapMode( aMapMode
);
560 pVDev
->SetOutputSizePixel( aSizePixel
);
561 pVDev
->SetFillColor( COL_BLACK
);
562 const tools::PolyPolygon
aClip( rClipPath
);
563 pVDev
->DrawPolyPolygon( aClip
);
565 // #i50672# Extract whole VDev content (to match size of rBitmap)
566 pVDev
->EnableMapMode( false );
567 const Bitmap
aVDevMask(pVDev
->GetBitmap(Point(), aSizePixel
));
571 // bitmap already uses a Mask or Alpha, we need to blend that with
572 // the new masking in pVDev.
573 // need to blend in AlphaMask quality (8Bit)
574 AlphaMask
fromVDev(aVDevMask
);
575 AlphaMask
fromBmpEx(aBmpEx
.GetAlpha());
576 AlphaMask::ScopedReadAccess
pR(fromVDev
);
577 AlphaScopedWriteAccess
pW(fromBmpEx
);
581 const tools::Long
nWidth(std::min(pR
->Width(), pW
->Width()));
582 const tools::Long
nHeight(std::min(pR
->Height(), pW
->Height()));
584 for(tools::Long
nY(0); nY
< nHeight
; nY
++)
586 Scanline pScanlineR
= pR
->GetScanline( nY
);
587 Scanline pScanlineW
= pW
->GetScanline( nY
);
588 for(tools::Long
nX(0); nX
< nWidth
; nX
++)
590 const sal_uInt8
nIndR(pR
->GetIndexFromData(pScanlineR
, nX
));
591 const sal_uInt8
nIndW(pW
->GetIndexFromData(pScanlineW
, nX
));
593 // these values represent transparency (0 == no, 255 == fully transparent),
594 // so to blend these we have to multiply the inverse (opacity)
595 // and re-invert the result to transparence
596 const sal_uInt8
nCombined(0x00ff - (((0x00ff - nIndR
) * (0x00ff - nIndW
)) >> 8));
598 pW
->SetPixelOnData(pScanlineW
, nX
, BitmapColor(nCombined
));
605 aBmpEx
= BitmapEx(aBmpEx
.GetBitmap(), fromBmpEx
);
609 // no mask yet, create and add new mask. For better quality, use Alpha,
610 // this allows the drawn mask being processed with AntiAliasing (AAed)
611 aBmpEx
= BitmapEx(rBitmap
.GetBitmap(), aVDevMask
);
616 css::uno::Sequence
< sal_Int8
> GetMaskDIB(BitmapEx
const & aBmpEx
)
618 if ( aBmpEx
.IsAlpha() )
621 WriteDIB(aBmpEx
.GetAlpha().GetBitmap(), aMem
, false, true);
622 return css::uno::Sequence
< sal_Int8
>( static_cast<sal_Int8
const *>(aMem
.GetData()), aMem
.Tell() );
625 return css::uno::Sequence
< sal_Int8
>();
628 static bool readAlpha( BitmapReadAccess
const * pAlphaReadAcc
, tools::Long nY
, const tools::Long nWidth
, unsigned char* data
, tools::Long nOff
)
630 bool bIsAlpha
= false;
637 switch( pAlphaReadAcc
->GetScanlineFormat() )
639 case ScanlineFormat::N8BitPal
:
640 pReadScan
= pAlphaReadAcc
->GetScanline( nY
);
641 for( nX
= 0; nX
< nWidth
; nX
++ )
643 BitmapColor
const& rColor(
644 pAlphaReadAcc
->GetPaletteColor(*pReadScan
));
646 nAlpha
= data
[ nOff
] = 255 - rColor
.GetIndex();
653 SAL_INFO( "canvas.cairo", "fallback to GetColor for alpha - slow, format: " << static_cast<int>(pAlphaReadAcc
->GetScanlineFormat()) );
654 for( nX
= 0; nX
< nWidth
; nX
++ )
656 nAlpha
= data
[ nOff
] = 255 - pAlphaReadAcc
->GetColor( nY
, nX
).GetIndex();
669 * @param data will be filled with alpha data, if xBitmap is alpha/transparent image
670 * @param bHasAlpha will be set to true if resulting surface has alpha
672 void CanvasCairoExtractBitmapData( BitmapEx
const & aBmpEx
, Bitmap
& aBitmap
, unsigned char*& data
, bool& bHasAlpha
, tools::Long
& rnWidth
, tools::Long
& rnHeight
)
674 AlphaMask aAlpha
= aBmpEx
.GetAlpha();
676 ::BitmapReadAccess
* pBitmapReadAcc
= aBitmap
.AcquireReadAccess();
677 ::BitmapReadAccess
* pAlphaReadAcc
= nullptr;
678 const tools::Long nWidth
= rnWidth
= pBitmapReadAcc
->Width();
679 const tools::Long nHeight
= rnHeight
= pBitmapReadAcc
->Height();
681 bool bIsAlpha
= false;
683 if( aBmpEx
.IsAlpha() )
684 pAlphaReadAcc
= aAlpha
.AcquireReadAccess();
686 data
= static_cast<unsigned char*>(malloc( nWidth
*nHeight
*4 ));
688 tools::Long nOff
= 0;
690 unsigned int nAlpha
= 255;
692 vcl::bitmap::lookup_table
const & premultiply_table
= vcl::bitmap::get_premultiply_table();
693 for( nY
= 0; nY
< nHeight
; nY
++ )
695 ::Scanline pReadScan
;
697 switch( pBitmapReadAcc
->GetScanlineFormat() )
699 case ScanlineFormat::N8BitPal
:
700 pReadScan
= pBitmapReadAcc
->GetScanline( nY
);
702 if( readAlpha( pAlphaReadAcc
, nY
, nWidth
, data
, nOff
) )
705 for( nX
= 0; nX
< nWidth
; nX
++ )
709 nAlpha
= data
[ nOff
++ ];
711 nAlpha
= data
[ nOff
++ ] = 255;
714 nAlpha
= data
[ nOff
+ 3 ];
716 nAlpha
= data
[ nOff
+ 3 ] = 255;
718 aColor
= pBitmapReadAcc
->GetPaletteColor(*pReadScan
++);
721 data
[ nOff
++ ] = premultiply_table
[nAlpha
][aColor
.GetRed()];
722 data
[ nOff
++ ] = premultiply_table
[nAlpha
][aColor
.GetGreen()];
723 data
[ nOff
++ ] = premultiply_table
[nAlpha
][aColor
.GetBlue()];
725 data
[ nOff
++ ] = premultiply_table
[nAlpha
][aColor
.GetBlue()];
726 data
[ nOff
++ ] = premultiply_table
[nAlpha
][aColor
.GetGreen()];
727 data
[ nOff
++ ] = premultiply_table
[nAlpha
][aColor
.GetRed()];
732 case ScanlineFormat::N24BitTcBgr
:
733 pReadScan
= pBitmapReadAcc
->GetScanline( nY
);
735 if( readAlpha( pAlphaReadAcc
, nY
, nWidth
, data
, nOff
) )
738 for( nX
= 0; nX
< nWidth
; nX
++ )
742 nAlpha
= data
[ nOff
];
744 nAlpha
= data
[ nOff
] = 255;
745 data
[ nOff
+ 3 ] = premultiply_table
[nAlpha
][*pReadScan
++];
746 data
[ nOff
+ 2 ] = premultiply_table
[nAlpha
][*pReadScan
++];
747 data
[ nOff
+ 1 ] = premultiply_table
[nAlpha
][*pReadScan
++];
751 nAlpha
= data
[ nOff
+ 3 ];
753 nAlpha
= data
[ nOff
+ 3 ] = 255;
754 data
[ nOff
++ ] = premultiply_table
[nAlpha
][*pReadScan
++];
755 data
[ nOff
++ ] = premultiply_table
[nAlpha
][*pReadScan
++];
756 data
[ nOff
++ ] = premultiply_table
[nAlpha
][*pReadScan
++];
761 case ScanlineFormat::N24BitTcRgb
:
762 pReadScan
= pBitmapReadAcc
->GetScanline( nY
);
764 if( readAlpha( pAlphaReadAcc
, nY
, nWidth
, data
, nOff
) )
767 for( nX
= 0; nX
< nWidth
; nX
++ )
771 nAlpha
= data
[ nOff
++ ];
773 nAlpha
= data
[ nOff
++ ] = 255;
774 data
[ nOff
++ ] = premultiply_table
[nAlpha
][*pReadScan
++];
775 data
[ nOff
++ ] = premultiply_table
[nAlpha
][*pReadScan
++];
776 data
[ nOff
++ ] = premultiply_table
[nAlpha
][*pReadScan
++];
779 nAlpha
= data
[ nOff
+ 3 ];
781 nAlpha
= data
[ nOff
+ 3 ] = 255;
782 data
[ nOff
++ ] = premultiply_table
[nAlpha
][pReadScan
[ 2 ]];
783 data
[ nOff
++ ] = premultiply_table
[nAlpha
][pReadScan
[ 1 ]];
784 data
[ nOff
++ ] = premultiply_table
[nAlpha
][pReadScan
[ 0 ]];
790 case ScanlineFormat::N32BitTcBgra
:
791 pReadScan
= pBitmapReadAcc
->GetScanline( nY
);
793 if( readAlpha( pAlphaReadAcc
, nY
, nWidth
, data
, nOff
) )
796 for( nX
= 0; nX
< nWidth
; nX
++ )
800 nAlpha
= data
[ nOff
++ ];
802 nAlpha
= data
[ nOff
++ ] = 255;
803 data
[ nOff
++ ] = premultiply_table
[nAlpha
][pReadScan
[ 2 ]];
804 data
[ nOff
++ ] = premultiply_table
[nAlpha
][pReadScan
[ 1 ]];
805 data
[ nOff
++ ] = premultiply_table
[nAlpha
][pReadScan
[ 0 ]];
809 nAlpha
= data
[ nOff
+ 3 ];
811 nAlpha
= data
[ nOff
+ 3 ] = 255;
812 data
[ nOff
++ ] = premultiply_table
[nAlpha
][*pReadScan
++];
813 data
[ nOff
++ ] = premultiply_table
[nAlpha
][*pReadScan
++];
814 data
[ nOff
++ ] = premultiply_table
[nAlpha
][*pReadScan
++];
820 case ScanlineFormat::N32BitTcRgba
:
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
++];
834 data
[ nOff
++ ] = premultiply_table
[nAlpha
][*pReadScan
++];
835 data
[ nOff
++ ] = premultiply_table
[nAlpha
][*pReadScan
++];
839 nAlpha
= data
[ nOff
+ 3 ];
841 nAlpha
= data
[ nOff
+ 3 ] = 255;
842 data
[ nOff
++ ] = premultiply_table
[nAlpha
][pReadScan
[ 2 ]];
843 data
[ nOff
++ ] = premultiply_table
[nAlpha
][pReadScan
[ 1 ]];
844 data
[ nOff
++ ] = premultiply_table
[nAlpha
][pReadScan
[ 0 ]];
851 SAL_INFO( "canvas.cairo", "fallback to GetColor - slow, format: " << static_cast<int>(pBitmapReadAcc
->GetScanlineFormat()) );
854 if( readAlpha( pAlphaReadAcc
, nY
, nWidth
, data
, nOff
) )
857 for( nX
= 0; nX
< nWidth
; nX
++ )
859 aColor
= pBitmapReadAcc
->GetColor( nY
, nX
);
861 // cairo need premultiplied color values
862 // TODO(rodo) handle endianness
865 nAlpha
= data
[ nOff
++ ];
867 nAlpha
= data
[ nOff
++ ] = 255;
868 data
[ nOff
++ ] = premultiply_table
[nAlpha
][aColor
.GetRed()];
869 data
[ nOff
++ ] = premultiply_table
[nAlpha
][aColor
.GetGreen()];
870 data
[ nOff
++ ] = premultiply_table
[nAlpha
][aColor
.GetBlue()];
873 nAlpha
= data
[ nOff
+ 3 ];
875 nAlpha
= data
[ nOff
+ 3 ] = 255;
876 data
[ nOff
++ ] = premultiply_table
[nAlpha
][aColor
.GetBlue()];
877 data
[ nOff
++ ] = premultiply_table
[nAlpha
][aColor
.GetGreen()];
878 data
[ nOff
++ ] = premultiply_table
[nAlpha
][aColor
.GetRed()];
885 ::Bitmap::ReleaseAccess( pBitmapReadAcc
);
887 aAlpha
.ReleaseAccess( pAlphaReadAcc
);
889 bHasAlpha
= bIsAlpha
;
893 uno::Sequence
< sal_Int8
> CanvasExtractBitmapData(BitmapEx
const & rBitmapEx
, const geometry::IntegerRectangle2D
& rect
)
895 Bitmap
aBitmap( rBitmapEx
.GetBitmap() );
896 Bitmap
aAlpha( rBitmapEx
.GetAlpha().GetBitmap() );
898 Bitmap::ScopedReadAccess
pReadAccess( aBitmap
);
899 Bitmap::ScopedReadAccess
pAlphaReadAccess( aAlpha
.IsEmpty() ?
900 nullptr : aAlpha
.AcquireReadAccess(),
903 assert( pReadAccess
);
905 // TODO(F1): Support more formats.
906 const Size
aBmpSize( aBitmap
.GetSizePixel() );
908 // for the time being, always return as BGRA
909 uno::Sequence
< sal_Int8
> aRes( 4*aBmpSize
.Width()*aBmpSize
.Height() );
910 sal_Int8
* pRes
= aRes
.getArray();
913 for( tools::Long y
=rect
.Y1
;
914 y
<aBmpSize
.Height() && y
<rect
.Y2
;
917 if( pAlphaReadAccess
.get() != nullptr )
919 Scanline pScanlineReadAlpha
= pAlphaReadAccess
->GetScanline( y
);
920 for( tools::Long x
=rect
.X1
;
921 x
<aBmpSize
.Width() && x
<rect
.X2
;
924 pRes
[ nCurrPos
++ ] = pReadAccess
->GetColor( y
, x
).GetRed();
925 pRes
[ nCurrPos
++ ] = pReadAccess
->GetColor( y
, x
).GetGreen();
926 pRes
[ nCurrPos
++ ] = pReadAccess
->GetColor( y
, x
).GetBlue();
927 pRes
[ nCurrPos
++ ] = pAlphaReadAccess
->GetIndexFromData( pScanlineReadAlpha
, x
);
932 for( tools::Long x
=rect
.X1
;
933 x
<aBmpSize
.Width() && x
<rect
.X2
;
936 pRes
[ nCurrPos
++ ] = pReadAccess
->GetColor( y
, x
).GetRed();
937 pRes
[ nCurrPos
++ ] = pReadAccess
->GetColor( y
, x
).GetGreen();
938 pRes
[ nCurrPos
++ ] = pReadAccess
->GetColor( y
, x
).GetBlue();
939 pRes
[ nCurrPos
++ ] = sal_uInt8(255);
946 BitmapEx
createHistorical8x8FromArray(std::array
<sal_uInt8
,64> const & pArray
, Color aColorPix
, Color aColorBack
)
948 BitmapPalette
aPalette(2);
950 aPalette
[0] = BitmapColor(aColorBack
);
951 aPalette
[1] = BitmapColor(aColorPix
);
953 Bitmap
aBitmap(Size(8, 8), vcl::PixelFormat::N1_BPP
, &aPalette
);
954 BitmapScopedWriteAccess
pContent(aBitmap
);
956 for(sal_uInt16
a(0); a
< 8; a
++)
958 for(sal_uInt16
b(0); b
< 8; b
++)
960 if(pArray
[(a
* 8) + b
])
962 pContent
->SetPixelIndex(a
, b
, 1);
966 pContent
->SetPixelIndex(a
, b
, 0);
971 return BitmapEx(aBitmap
);
974 bool isHistorical8x8(const BitmapEx
& rBitmapEx
, Color
& o_rBack
, Color
& o_rFront
)
978 if(!rBitmapEx
.IsAlpha())
980 Bitmap
aBitmap(rBitmapEx
.GetBitmap());
982 if(8 == aBitmap
.GetSizePixel().Width() && 8 == aBitmap
.GetSizePixel().Height())
984 if (aBitmap
.getPixelFormat() == vcl::PixelFormat::N1_BPP
)
986 BitmapReadAccess
* pRead
= aBitmap
.AcquireReadAccess();
990 if(pRead
->HasPalette() && 2 == pRead
->GetPaletteEntryCount())
992 const BitmapPalette
& rPalette
= pRead
->GetPalette();
993 o_rFront
= rPalette
[1];
994 o_rBack
= rPalette
[0];
999 Bitmap::ReleaseAccess(pRead
);
1004 // Historical 1bpp images are getting really historical,
1005 // even to the point that e.g. the png loader actually loads
1006 // them as RGB. But the pattern code in svx relies on this
1007 // assumption that any 2-color 1bpp bitmap is a pattern, and so it would
1008 // get confused by RGB. Try to detect if this image is really
1009 // just two colors and say it's a pattern bitmap if so.
1010 Bitmap::ScopedReadAccess
access(aBitmap
);
1011 o_rBack
= access
->GetColor(0,0);
1012 bool foundSecondColor
= false;;
1013 for(tools::Long y
= 0; y
< access
->Height(); ++y
)
1014 for(tools::Long x
= 0; x
< access
->Width(); ++x
)
1016 if(!foundSecondColor
)
1018 if( access
->GetColor(y
,x
) != o_rBack
)
1020 o_rFront
= access
->GetColor(y
,x
);
1021 foundSecondColor
= true;
1022 // Hard to know which of the two colors is the background,
1023 // select the lighter one.
1024 if( o_rFront
.GetLuminance() > o_rBack
.GetLuminance())
1025 std::swap( o_rFront
, o_rBack
);
1030 if( access
->GetColor(y
,x
) != o_rBack
&& access
->GetColor(y
,x
) != o_rFront
)
1042 sal_uInt8
unpremultiply(sal_uInt8 c
, sal_uInt8 a
)
1044 return get_unpremultiply_table()[a
][c
];
1047 static constexpr sal_uInt8
unpremultiplyImpl(sal_uInt8 c
, sal_uInt8 a
)
1049 return (a
== 0) ? 0 : (c
* 255 + a
/ 2) / a
;
1052 sal_uInt8
premultiply(sal_uInt8 c
, sal_uInt8 a
)
1054 return get_premultiply_table()[a
][c
];
1057 static constexpr sal_uInt8
premultiplyImpl(sal_uInt8 c
, sal_uInt8 a
)
1059 return (c
* a
+ 127) / 255;
1062 template<int... Is
> static constexpr std::array
<sal_uInt8
, 256> make_unpremultiply_table_row_(
1063 int a
, std::integer_sequence
<int, Is
...>)
1065 return {unpremultiplyImpl(Is
, a
)...};
1068 template<int... Is
> static constexpr lookup_table
make_unpremultiply_table_(
1069 std::integer_sequence
<int, Is
...>)
1071 return {make_unpremultiply_table_row_(Is
, std::make_integer_sequence
<int, 256>{})...};
1074 lookup_table
const & get_unpremultiply_table()
1076 static constexpr auto unpremultiply_table
= make_unpremultiply_table_(
1077 std::make_integer_sequence
<int, 256>{});
1078 return unpremultiply_table
;
1081 template<int... Is
> static constexpr std::array
<sal_uInt8
, 256> make_premultiply_table_row_(
1082 int a
, std::integer_sequence
<int, Is
...>)
1084 return {premultiplyImpl(Is
, a
)...};
1087 template<int... Is
> static constexpr lookup_table
make_premultiply_table_(
1088 std::integer_sequence
<int, Is
...>)
1090 return {make_premultiply_table_row_(Is
, std::make_integer_sequence
<int, 256>{})...};
1093 lookup_table
const & get_premultiply_table()
1095 static constexpr auto premultiply_table
= make_premultiply_table_(
1096 std::make_integer_sequence
<int, 256>{});
1097 return premultiply_table
;
1100 bool convertBitmap32To24Plus8(BitmapEx
const & rInput
, BitmapEx
& rResult
)
1102 Bitmap
aBitmap(rInput
.GetBitmap());
1103 if (aBitmap
.getPixelFormat() != vcl::PixelFormat::N32_BPP
)
1106 Size aSize
= aBitmap
.GetSizePixel();
1107 Bitmap
aResultBitmap(aSize
, vcl::PixelFormat::N24_BPP
);
1108 AlphaMask
aResultAlpha(aSize
);
1110 BitmapScopedWriteAccess
pResultBitmapAccess(aResultBitmap
);
1111 AlphaScopedWriteAccess
pResultAlphaAccess(aResultAlpha
);
1113 Bitmap::ScopedReadAccess
pReadAccess(aBitmap
);
1115 for (tools::Long nY
= 0; nY
< aSize
.Height(); ++nY
)
1117 Scanline aResultScan
= pResultBitmapAccess
->GetScanline(nY
);
1118 Scanline aResultScanAlpha
= pResultAlphaAccess
->GetScanline(nY
);
1120 Scanline aReadScan
= pReadAccess
->GetScanline(nY
);
1122 for (tools::Long nX
= 0; nX
< aSize
.Width(); ++nX
)
1124 const BitmapColor aColor
= pReadAccess
->GetPixelFromData(aReadScan
, nX
);
1125 BitmapColor
aResultColor(aColor
.GetRed(), aColor
.GetGreen(), aColor
.GetBlue());
1126 BitmapColor
aResultColorAlpha(255 - aColor
.GetAlpha(), 255 - aColor
.GetAlpha(), 255 - aColor
.GetAlpha());
1128 pResultBitmapAccess
->SetPixelOnData(aResultScan
, nX
, aResultColor
);
1129 pResultAlphaAccess
->SetPixelOnData(aResultScanAlpha
, nX
, aResultColorAlpha
);
1133 if (rInput
.IsAlpha())
1134 rResult
= BitmapEx(aResultBitmap
, rInput
.GetAlpha());
1136 rResult
= BitmapEx(aResultBitmap
, aResultAlpha
);
1140 } // end vcl::bitmap
1142 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */