bump product version to 7.2.5.1
[LibreOffice.git] / vcl / source / bitmap / BitmapTools.cxx
blob014e62aab8f30e0b5629f46ad93db920adadc7cf
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 */
11 #include <sal/config.h>
13 #include <array>
14 #include <utility>
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
36 #include <cairo.h>
37 #endif
38 #include <tools/diagnose_ex.h>
39 #include <tools/fract.hxx>
40 #include <tools/stream.hxx>
41 #include <bitmap/BitmapWriteAccess.hxx>
43 using namespace css;
45 using drawinglayer::primitive2d::Primitive2DSequence;
46 using drawinglayer::primitive2d::Primitive2DReference;
48 namespace vcl::bitmap
51 BitmapEx loadFromName(const OUString& rFileName, const ImageLoadFlags eFlags)
53 bool bSuccess = true;
54 OUString aIconTheme;
55 BitmapEx aBitmapEx;
56 try
58 aIconTheme = Application::GetSettings().GetStyleSettings().DetermineIconTheme();
59 ImageTree::get().loadImage(rFileName, aIconTheme, aBitmapEx, true, eFlags);
61 catch (...)
63 bSuccess = false;
66 SAL_WARN_IF(!bSuccess, "vcl", "vcl::bitmap::loadFromName : could not load image " << rFileName << " via icon theme " << aIconTheme);
68 return aBitmapEx;
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);
79 aBuffer[nSize] = 0;
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())
87 return;
89 uno::Sequence<beans::PropertyValue> aViewParameters;
91 geometry::RealRectangle2D aRealRect;
92 basegfx::B2DRange aRange;
93 for (Primitive2DReference const & xReference : aPrimitiveSequence)
95 if (xReference.is())
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));
113 if (xBitmap.is())
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.
124 @param pData
125 The block of data to copy
126 @param nStride
127 The number of bytes in a scanline, must >= (width * nBitCount / 8)
128 @param bReversColors
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());
142 if( !pWrite )
143 return BitmapEx();
144 std::unique_ptr<AlphaMask> pAlphaMask;
145 AlphaScopedWriteAccess xMaskAcc;
146 if (nBitCount == 32)
148 pAlphaMask.reset( new AlphaMask( Size(nWidth, nHeight) ) );
149 xMaskAcc = AlphaScopedWriteAccess(*pAlphaMask);
151 if (nBitCount == 1)
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));
165 else
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)
173 BitmapColor col;
174 if ( bReversColors )
175 col = BitmapColor( p[2], p[1], p[0] );
176 else
177 col = BitmapColor( p[0], p[1], p[2] );
178 pWrite->SetPixelOnData(pScanline, x, col);
179 p += nBitCount/8;
181 if (nBitCount == 32)
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));
188 p += 4;
193 if (nBitCount == 32)
194 return BitmapEx(aBmp, *pAlphaMask);
195 else
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;
209 if (nBitCount == 24)
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());
220 if( !pWrite )
221 return BitmapEx();
222 std::unique_ptr<AlphaMask> pAlphaMask;
223 AlphaScopedWriteAccess xMaskAcc;
224 if (nBitCount == 32)
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);
241 p += nBitCount/8;
243 if (nBitCount == 32)
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));
250 p += 4;
254 if (nBitCount == 32)
255 return BitmapEx(aBmp, *pAlphaMask);
256 else
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,
268 #else
269 cairo_surface_t *pPixels = cairo_image_surface_create(
270 #endif
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 )
274 return nullptr;
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);
287 assert(pRGBWrite);
288 if (!pRGBWrite)
289 return nullptr;
291 AlphaScopedWriteAccess pMaskWrite(aMask);
292 assert(pMaskWrite);
293 if (!pMaskWrite)
294 return nullptr;
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;
310 #else
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;
315 #endif
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 );
325 pPix++;
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 );
336 return pBitmapEx;
338 #endif
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() );
347 Bitmap aSrcAlpha;
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() :
359 nullptr,
360 aSrcAlpha );
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.
379 sal_uInt8 val=0;
380 sal_uInt8* pCur=aAlphaMap;
381 sal_uInt8* const pEnd=&aAlphaMap[256];
382 while(pCur != pEnd)
383 *pCur++ = val++;
385 // else: mapping table is not used
387 const Size aDestBmpSize( ::basegfx::fround( rDestRect.getWidth() ),
388 ::basegfx::fround( rDestRect.getHeight() ) );
390 if( aDestBmpSize.IsEmpty() )
391 return BitmapEx();
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
412 // source
413 ::basegfx::B2DHomMatrix aTransform( rLocalTransform );
414 aTransform.invert();
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) );
438 else
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 ) );
446 else
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) );
462 else
464 pAlphaWriteAccess->SetPixelOnData( pScanAlpha, x, BitmapColor(0) );
465 pWriteAccess->SetPixelOnData( pScan, x, pReadAccess->GetPixel( nSrcY,
466 nSrcX ) );
472 else
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
487 AlphaMask aOldMask;
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));
517 else
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));
569 if(aBmpEx.IsAlpha())
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);
579 if(pR && pW)
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));
603 pR.reset();
604 pW.reset();
605 aBmpEx = BitmapEx(aBmpEx.GetBitmap(), fromBmpEx);
607 else
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() )
620 SvMemoryStream aMem;
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;
631 tools::Long nX;
632 int nAlpha;
633 Scanline pReadScan;
635 nOff += 3;
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));
645 pReadScan++;
646 nAlpha = data[ nOff ] = 255 - rColor.GetIndex();
647 if( nAlpha != 255 )
648 bIsAlpha = true;
649 nOff += 4;
651 break;
652 default:
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();
657 if( nAlpha != 255 )
658 bIsAlpha = true;
659 nOff += 4;
663 return bIsAlpha;
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();
680 tools::Long nX, nY;
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;
689 ::Color aColor;
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 );
701 if( pAlphaReadAcc )
702 if( readAlpha( pAlphaReadAcc, nY, nWidth, data, nOff ) )
703 bIsAlpha = true;
705 for( nX = 0; nX < nWidth; nX++ )
707 #ifdef OSL_BIGENDIAN
708 if( pAlphaReadAcc )
709 nAlpha = data[ nOff++ ];
710 else
711 nAlpha = data[ nOff++ ] = 255;
712 #else
713 if( pAlphaReadAcc )
714 nAlpha = data[ nOff + 3 ];
715 else
716 nAlpha = data[ nOff + 3 ] = 255;
717 #endif
718 aColor = pBitmapReadAcc->GetPaletteColor(*pReadScan++);
720 #ifdef OSL_BIGENDIAN
721 data[ nOff++ ] = premultiply_table[nAlpha][aColor.GetRed()];
722 data[ nOff++ ] = premultiply_table[nAlpha][aColor.GetGreen()];
723 data[ nOff++ ] = premultiply_table[nAlpha][aColor.GetBlue()];
724 #else
725 data[ nOff++ ] = premultiply_table[nAlpha][aColor.GetBlue()];
726 data[ nOff++ ] = premultiply_table[nAlpha][aColor.GetGreen()];
727 data[ nOff++ ] = premultiply_table[nAlpha][aColor.GetRed()];
728 nOff++;
729 #endif
731 break;
732 case ScanlineFormat::N24BitTcBgr:
733 pReadScan = pBitmapReadAcc->GetScanline( nY );
734 if( pAlphaReadAcc )
735 if( readAlpha( pAlphaReadAcc, nY, nWidth, data, nOff ) )
736 bIsAlpha = true;
738 for( nX = 0; nX < nWidth; nX++ )
740 #ifdef OSL_BIGENDIAN
741 if( pAlphaReadAcc )
742 nAlpha = data[ nOff ];
743 else
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++];
748 nOff += 4;
749 #else
750 if( pAlphaReadAcc )
751 nAlpha = data[ nOff + 3 ];
752 else
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++];
757 nOff++;
758 #endif
760 break;
761 case ScanlineFormat::N24BitTcRgb:
762 pReadScan = pBitmapReadAcc->GetScanline( nY );
763 if( pAlphaReadAcc )
764 if( readAlpha( pAlphaReadAcc, nY, nWidth, data, nOff ) )
765 bIsAlpha = true;
767 for( nX = 0; nX < nWidth; nX++ )
769 #ifdef OSL_BIGENDIAN
770 if( pAlphaReadAcc )
771 nAlpha = data[ nOff++ ];
772 else
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++];
777 #else
778 if( pAlphaReadAcc )
779 nAlpha = data[ nOff + 3 ];
780 else
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 ]];
785 pReadScan += 3;
786 nOff++;
787 #endif
789 break;
790 case ScanlineFormat::N32BitTcBgra:
791 pReadScan = pBitmapReadAcc->GetScanline( nY );
792 if( pAlphaReadAcc )
793 if( readAlpha( pAlphaReadAcc, nY, nWidth, data, nOff ) )
794 bIsAlpha = true;
796 for( nX = 0; nX < nWidth; nX++ )
798 #ifdef OSL_BIGENDIAN
799 if( pAlphaReadAcc )
800 nAlpha = data[ nOff++ ];
801 else
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 ]];
806 pReadScan += 4;
807 #else
808 if( pAlphaReadAcc )
809 nAlpha = data[ nOff + 3 ];
810 else
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++];
815 pReadScan++;
816 nOff++;
817 #endif
819 break;
820 case ScanlineFormat::N32BitTcRgba:
821 pReadScan = pBitmapReadAcc->GetScanline( nY );
822 if( pAlphaReadAcc )
823 if( readAlpha( pAlphaReadAcc, nY, nWidth, data, nOff ) )
824 bIsAlpha = true;
826 for( nX = 0; nX < nWidth; nX++ )
828 #ifdef OSL_BIGENDIAN
829 if( pAlphaReadAcc )
830 nAlpha = data[ nOff ++ ];
831 else
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++];
836 pReadScan++;
837 #else
838 if( pAlphaReadAcc )
839 nAlpha = data[ nOff + 3 ];
840 else
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 ]];
845 pReadScan += 4;
846 nOff++;
847 #endif
849 break;
850 default:
851 SAL_INFO( "canvas.cairo", "fallback to GetColor - slow, format: " << static_cast<int>(pBitmapReadAcc->GetScanlineFormat()) );
853 if( pAlphaReadAcc )
854 if( readAlpha( pAlphaReadAcc, nY, nWidth, data, nOff ) )
855 bIsAlpha = true;
857 for( nX = 0; nX < nWidth; nX++ )
859 aColor = pBitmapReadAcc->GetColor( nY, nX );
861 // cairo need premultiplied color values
862 // TODO(rodo) handle endianness
863 #ifdef OSL_BIGENDIAN
864 if( pAlphaReadAcc )
865 nAlpha = data[ nOff++ ];
866 else
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()];
871 #else
872 if( pAlphaReadAcc )
873 nAlpha = data[ nOff + 3 ];
874 else
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()];
879 nOff ++;
880 #endif
885 ::Bitmap::ReleaseAccess( pBitmapReadAcc );
886 if( pAlphaReadAcc )
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(),
901 aAlpha );
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();
912 int nCurrPos(0);
913 for( tools::Long y=rect.Y1;
914 y<aBmpSize.Height() && y<rect.Y2;
915 ++y )
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;
922 ++x )
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 );
930 else
932 for( tools::Long x=rect.X1;
933 x<aBmpSize.Width() && x<rect.X2;
934 ++x )
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);
943 return aRes;
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);
964 else
966 pContent->SetPixelIndex(a, b, 0);
971 return BitmapEx(aBitmap);
974 bool isHistorical8x8(const BitmapEx& rBitmapEx, Color& o_rBack, Color& o_rFront)
976 bool bRet(false);
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();
988 if(pRead)
990 if(pRead->HasPalette() && 2 == pRead->GetPaletteEntryCount())
992 const BitmapPalette& rPalette = pRead->GetPalette();
993 o_rFront = rPalette[1];
994 o_rBack = rPalette[0];
996 bRet = true;
999 Bitmap::ReleaseAccess(pRead);
1002 else
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 );
1028 else
1030 if( access->GetColor(y,x) != o_rBack && access->GetColor(y,x) != o_rFront)
1031 return false;
1034 return true;
1039 return bRet;
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)
1104 return false;
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());
1135 else
1136 rResult = BitmapEx(aResultBitmap, aResultAlpha);
1137 return true;
1140 } // end vcl::bitmap
1142 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */