use insert function instead of for loop
[LibreOffice.git] / vcl / source / bitmap / BitmapTools.cxx
blobe55575e8b93443976c35205d270a82bf87f90209
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 <tools/helpers.hxx>
17 #include <vcl/BitmapTools.hxx>
19 #include <sal/log.hxx>
20 #include <comphelper/processfactory.hxx>
21 #include <comphelper/seqstream.hxx>
22 #include <vcl/canvastools.hxx>
23 #include <basegfx/matrix/b2dhommatrix.hxx>
25 #include <com/sun/star/graphic/SvgTools.hpp>
26 #include <com/sun/star/graphic/Primitive2DTools.hpp>
28 #include <drawinglayer/primitive2d/baseprimitive2d.hxx>
30 #include <com/sun/star/rendering/XIntegerReadOnlyBitmap.hpp>
32 #include <vcl/dibtools.hxx>
33 #include <vcl/settings.hxx>
34 #include <vcl/svapp.hxx>
35 #include <vcl/virdev.hxx>
36 #if ENABLE_CAIRO_CANVAS
37 #include <cairo.h>
38 #endif
39 #include <comphelper/diagnose_ex.hxx>
40 #include <tools/fract.hxx>
41 #include <tools/stream.hxx>
42 #include <vcl/BitmapWriteAccess.hxx>
44 using namespace css;
46 using drawinglayer::primitive2d::Primitive2DSequence;
47 using drawinglayer::primitive2d::Primitive2DReference;
49 namespace vcl::bitmap
52 BitmapEx loadFromName(const OUString& rFileName, const ImageLoadFlags eFlags)
54 bool bSuccess = true;
55 OUString aIconTheme;
56 BitmapEx aBitmapEx;
57 try
59 aIconTheme = Application::GetSettings().GetStyleSettings().DetermineIconTheme();
60 ImageTree::get().loadImage(rFileName, aIconTheme, aBitmapEx, true, eFlags);
62 catch (...)
64 bSuccess = false;
67 SAL_WARN_IF(!bSuccess, "vcl", "vcl::bitmap::loadFromName : could not load image " << rFileName << " via icon theme " << aIconTheme);
69 return aBitmapEx;
72 void loadFromSvg(SvStream& rStream, const OUString& sPath, BitmapEx& rBitmapEx, double fScalingFactor)
74 const uno::Reference<uno::XComponentContext>& xContext(comphelper::getProcessComponentContext());
75 const uno::Reference<graphic::XSvgParser> xSvgParser = graphic::SvgTools::create(xContext);
77 std::size_t nSize = rStream.remainingSize();
78 std::vector<sal_Int8> aBuffer(nSize + 1);
79 rStream.ReadBytes(aBuffer.data(), nSize);
80 aBuffer[nSize] = 0;
82 uno::Sequence<sal_Int8> aData(aBuffer.data(), nSize + 1);
83 uno::Reference<io::XInputStream> aInputStream(new comphelper::SequenceInputStream(aData));
85 const Primitive2DSequence aPrimitiveSequence = xSvgParser->getDecomposition(aInputStream, sPath);
87 if (!aPrimitiveSequence.hasElements())
88 return;
90 uno::Sequence<beans::PropertyValue> aViewParameters;
92 geometry::RealRectangle2D aRealRect;
93 basegfx::B2DRange aRange;
94 for (css::uno::Reference<css::graphic::XPrimitive2D> const & xReference : aPrimitiveSequence)
96 if (xReference.is())
98 aRealRect = xReference->getRange(aViewParameters);
99 aRange.expand(basegfx::B2DRange(aRealRect.X1, aRealRect.Y1, aRealRect.X2, aRealRect.Y2));
103 aRealRect.X1 = aRange.getMinX();
104 aRealRect.Y1 = aRange.getMinY();
105 aRealRect.X2 = aRange.getMaxX();
106 aRealRect.Y2 = aRange.getMaxY();
108 double nDPI = 96 * fScalingFactor;
110 const css::uno::Reference<css::graphic::XPrimitive2DRenderer> xPrimitive2DRenderer = css::graphic::Primitive2DTools::create(xContext);
111 const css::uno::Reference<css::rendering::XBitmap> xBitmap(
112 xPrimitive2DRenderer->rasterize(aPrimitiveSequence, aViewParameters, nDPI, nDPI, aRealRect, 256*256));
114 if (xBitmap.is())
116 const css::uno::Reference<css::rendering::XIntegerReadOnlyBitmap> xIntBmp(xBitmap, uno::UNO_QUERY_THROW);
117 rBitmapEx = vcl::unotools::bitmapExFromXBitmap(xIntBmp);
122 /** Copy block of image data into the bitmap.
123 Assumes that the Bitmap has been constructed with the desired size.
125 @param pData
126 The block of data to copy
127 @param nStride
128 The number of bytes in a scanline, must >= (width * nBitCount / 8)
129 @param bReversColors
130 In case the endianness of pData is wrong, you could reverse colors
132 BitmapEx CreateFromData(sal_uInt8 const *pData, sal_Int32 nWidth, sal_Int32 nHeight,
133 sal_Int32 nStride, sal_Int8 nBitCount,
134 bool bReversColors, bool bReverseAlpha)
136 assert(nStride >= (nWidth * nBitCount / 8));
137 assert(nBitCount == 1 || nBitCount == 8 || nBitCount == 24 || nBitCount == 32);
139 PixelFormat ePixelFormat;
140 if (nBitCount == 1)
141 ePixelFormat = PixelFormat::N8_BPP; // we convert 1-bit input data to 8-bit format
142 else if (nBitCount == 8)
143 ePixelFormat = PixelFormat::N8_BPP;
144 else if (nBitCount == 24)
145 ePixelFormat = PixelFormat::N24_BPP;
146 else if (nBitCount == 32)
147 ePixelFormat = PixelFormat::N32_BPP;
148 else
149 std::abort();
150 Bitmap aBmp;
151 if (nBitCount == 1)
153 BitmapPalette aBiLevelPalette { COL_BLACK, COL_WHITE };
154 aBmp = Bitmap(Size(nWidth, nHeight), PixelFormat::N8_BPP, &aBiLevelPalette);
156 else
157 aBmp = Bitmap(Size(nWidth, nHeight), ePixelFormat);
159 BitmapScopedWriteAccess pWrite(aBmp);
160 assert(pWrite.get());
161 if( !pWrite )
162 return BitmapEx();
163 std::optional<AlphaMask> pAlphaMask;
164 BitmapScopedWriteAccess xMaskAcc;
165 if (nBitCount == 32)
167 pAlphaMask.emplace( Size(nWidth, nHeight) );
168 xMaskAcc = *pAlphaMask;
170 if (nBitCount == 1)
172 for( tools::Long y = 0; y < nHeight; ++y )
174 sal_uInt8 const *p = pData + y * nStride / 8;
175 Scanline pScanline = pWrite->GetScanline(y);
176 for (tools::Long x = 0; x < nWidth; ++x)
178 int bitIndex = (y * nStride + x) % 8;
180 pWrite->SetPixelOnData(pScanline, x, BitmapColor((*p >> bitIndex) & 1));
184 else
186 for( tools::Long y = 0; y < nHeight; ++y )
188 sal_uInt8 const *p = pData + (y * nStride);
189 Scanline pScanline = pWrite->GetScanline(y);
190 for (tools::Long x = 0; x < nWidth; ++x)
192 BitmapColor col;
193 if (nBitCount == 8)
194 col = BitmapColor( *p );
195 else if ( bReversColors )
196 col = BitmapColor( p[2], p[1], p[0] );
197 else
198 col = BitmapColor( p[0], p[1], p[2] );
199 pWrite->SetPixelOnData(pScanline, x, col);
200 p += nBitCount/8;
202 if (nBitCount == 32)
204 p = pData + (y * nStride) + 3;
205 Scanline pMaskScanLine = xMaskAcc->GetScanline(y);
206 for (tools::Long x = 0; x < nWidth; ++x)
208 // FIXME this parameter is badly named
209 const sal_uInt8 nValue = bReverseAlpha ? *p : 0xff - *p;
210 xMaskAcc->SetPixelOnData(pMaskScanLine, x, BitmapColor(nValue));
211 p += 4;
216 // Avoid further bitmap use with unfinished write access
217 pWrite.reset();
218 xMaskAcc.reset();
219 if (nBitCount == 32)
220 return BitmapEx(aBmp, *pAlphaMask);
221 else
222 return BitmapEx(aBmp);
225 /** Copy block of image data into the bitmap.
226 Assumes that the Bitmap has been constructed with the desired size.
228 BitmapEx CreateFromData( RawBitmap&& rawBitmap )
230 auto nBitCount = rawBitmap.GetBitCount();
231 assert( nBitCount == 24 || nBitCount == 32);
233 auto ePixelFormat = vcl::PixelFormat::INVALID;
235 if (nBitCount == 24)
236 ePixelFormat = vcl::PixelFormat::N24_BPP;
237 else if (nBitCount == 32)
238 ePixelFormat = vcl::PixelFormat::N32_BPP;
240 assert(ePixelFormat != vcl::PixelFormat::INVALID);
242 Bitmap aBmp(rawBitmap.maSize, ePixelFormat);
244 BitmapScopedWriteAccess pWrite(aBmp);
245 assert(pWrite.get());
246 if( !pWrite )
247 return BitmapEx();
248 std::optional<AlphaMask> pAlphaMask;
249 BitmapScopedWriteAccess xMaskAcc;
250 if (nBitCount == 32)
252 pAlphaMask.emplace( rawBitmap.maSize );
253 xMaskAcc = *pAlphaMask;
256 auto nHeight = rawBitmap.maSize.getHeight();
257 auto nWidth = rawBitmap.maSize.getWidth();
258 auto nStride = nWidth * nBitCount / 8;
259 for( tools::Long y = 0; y < nHeight; ++y )
261 sal_uInt8 const *p = rawBitmap.mpData.get() + (y * nStride);
262 Scanline pScanline = pWrite->GetScanline(y);
263 for (tools::Long x = 0; x < nWidth; ++x)
265 BitmapColor col(p[0], p[1], p[2]);
266 pWrite->SetPixelOnData(pScanline, x, col);
267 p += nBitCount/8;
269 if (nBitCount == 32)
271 p = rawBitmap.mpData.get() + (y * nStride) + 3;
272 Scanline pMaskScanLine = xMaskAcc->GetScanline(y);
273 for (tools::Long x = 0; x < nWidth; ++x)
275 xMaskAcc->SetPixelOnData(pMaskScanLine, x, BitmapColor(*p));
276 p += 4;
281 xMaskAcc.reset();
282 pWrite.reset();
284 if (nBitCount == 32)
285 return BitmapEx(aBmp, *pAlphaMask);
286 else
287 return BitmapEx(aBmp);
290 void fillWithData(sal_uInt8* pData, BitmapEx const& rBitmapEx)
292 const Bitmap& aBitmap = rBitmapEx.GetBitmap();
293 const AlphaMask& aAlphaMask = rBitmapEx.GetAlphaMask();
294 BitmapScopedReadAccess aReadAccessBitmap(aBitmap);
295 BitmapScopedReadAccess aReadAccessAlpha(aAlphaMask);
297 assert(!aReadAccessAlpha || aReadAccessBitmap->Height() == aReadAccessAlpha->Height());
298 assert(!aReadAccessAlpha || aReadAccessBitmap->Width() == aReadAccessAlpha->Width());
300 sal_uInt8* p = pData;
302 for (tools::Long y = 0; y < aReadAccessBitmap->Height(); ++y)
304 Scanline dataBitmap = aReadAccessBitmap->GetScanline(y);
305 Scanline dataAlpha = aReadAccessAlpha ? aReadAccessAlpha->GetScanline(y) : nullptr;
307 for (tools::Long x = 0; x < aReadAccessBitmap->Width(); ++x)
309 BitmapColor aColor = aReadAccessBitmap->GetPixelFromData(dataBitmap, x);
310 sal_uInt8 aAlpha = dataAlpha ? aReadAccessAlpha->GetPixelFromData(dataAlpha, x).GetBlue() : 255;
311 *p++ = aColor.GetBlue();
312 *p++ = aColor.GetGreen();
313 *p++ = aColor.GetRed();
314 *p++ = aAlpha;
320 #if ENABLE_CAIRO_CANVAS
321 BitmapEx* CreateFromCairoSurface(Size aSize, cairo_surface_t * pSurface)
323 // FIXME: if we could teach VCL/ about cairo handles, life could
324 // be significantly better here perhaps.
326 cairo_surface_t *pPixels = cairo_surface_create_similar_image(pSurface,
327 CAIRO_FORMAT_ARGB32, aSize.Width(), aSize.Height());
328 cairo_t *pCairo = cairo_create( pPixels );
329 if( !pPixels || !pCairo || cairo_status(pCairo) != CAIRO_STATUS_SUCCESS )
330 return nullptr;
332 // suck ourselves from the X server to this buffer so then we can fiddle with
333 // Alpha to turn it into the ultra-lame vcl required format and then push it
334 // all back again later at vast expense [ urgh ]
335 cairo_set_source_surface( pCairo, pSurface, 0, 0 );
336 cairo_set_operator( pCairo, CAIRO_OPERATOR_SOURCE );
337 cairo_paint( pCairo );
339 Bitmap aRGB(aSize, vcl::PixelFormat::N24_BPP);
340 ::AlphaMask aMask( aSize );
342 BitmapScopedWriteAccess pRGBWrite(aRGB);
343 assert(pRGBWrite);
344 if (!pRGBWrite)
345 return nullptr;
347 BitmapScopedWriteAccess pMaskWrite(aMask);
348 assert(pMaskWrite);
349 if (!pMaskWrite)
350 return nullptr;
352 cairo_surface_flush(pPixels);
353 unsigned char *pSrc = cairo_image_surface_get_data( pPixels );
354 unsigned int nStride = cairo_image_surface_get_stride( pPixels );
355 #if !ENABLE_WASM_STRIP_PREMULTIPLY
356 vcl::bitmap::lookup_table const & unpremultiply_table = vcl::bitmap::get_unpremultiply_table();
357 #endif
358 for( tools::Long y = 0; y < aSize.Height(); y++ )
360 sal_uInt32 *pPix = reinterpret_cast<sal_uInt32 *>(pSrc + nStride * y);
361 for( tools::Long x = 0; x < aSize.Width(); x++ )
363 #if defined OSL_BIGENDIAN
364 sal_uInt8 nB = (*pPix >> 24);
365 sal_uInt8 nG = (*pPix >> 16) & 0xff;
366 sal_uInt8 nR = (*pPix >> 8) & 0xff;
367 sal_uInt8 nAlpha = *pPix & 0xff;
368 #else
369 sal_uInt8 nAlpha = (*pPix >> 24);
370 sal_uInt8 nR = (*pPix >> 16) & 0xff;
371 sal_uInt8 nG = (*pPix >> 8) & 0xff;
372 sal_uInt8 nB = *pPix & 0xff;
373 #endif
374 if( nAlpha != 0 && nAlpha != 255 )
376 // Cairo uses pre-multiplied alpha - we do not => re-multiply
377 #if ENABLE_WASM_STRIP_PREMULTIPLY
378 nR = vcl::bitmap::unpremultiply(nR, nAlpha);
379 nG = vcl::bitmap::unpremultiply(nG, nAlpha);
380 nB = vcl::bitmap::unpremultiply(nB, nAlpha);
381 #else
382 nR = unpremultiply_table[nAlpha][nR];
383 nG = unpremultiply_table[nAlpha][nG];
384 nB = unpremultiply_table[nAlpha][nB];
385 #endif
387 pRGBWrite->SetPixel( y, x, BitmapColor( nR, nG, nB ) );
388 pMaskWrite->SetPixelIndex( y, x, nAlpha );
389 pPix++;
393 // ignore potential errors above. will get caller a
394 // uniformly white bitmap, but not that there would
395 // be error handling in calling code ...
396 ::BitmapEx *pBitmapEx = new ::BitmapEx( aRGB, aMask );
398 cairo_destroy( pCairo );
399 cairo_surface_destroy( pPixels );
400 return pBitmapEx;
402 #endif
404 BitmapEx CanvasTransformBitmap( const BitmapEx& rBitmap,
405 const ::basegfx::B2DHomMatrix& rTransform,
406 ::basegfx::B2DRectangle const & rDestRect,
407 ::basegfx::B2DHomMatrix const & rLocalTransform )
409 const Size aDestBmpSize( ::basegfx::fround<tools::Long>( rDestRect.getWidth() ),
410 ::basegfx::fround<tools::Long>( rDestRect.getHeight() ) );
412 if( aDestBmpSize.IsEmpty() )
413 return BitmapEx();
415 const Size aBmpSize( rBitmap.GetSizePixel() );
416 const Bitmap& aSrcBitmap( rBitmap.GetBitmap() );
417 Bitmap aSrcAlpha;
419 // differentiate mask and alpha channel (on-off
420 // vs. multi-level transparency)
421 if( rBitmap.IsAlpha() )
423 aSrcAlpha = rBitmap.GetAlphaMask().GetBitmap();
426 BitmapScopedReadAccess pReadAccess( aSrcBitmap );
427 BitmapScopedReadAccess pAlphaReadAccess;
428 if (rBitmap.IsAlpha())
429 pAlphaReadAccess = aSrcAlpha;
431 if( !pReadAccess || (!pAlphaReadAccess && rBitmap.IsAlpha()) )
433 // TODO(E2): Error handling!
434 ENSURE_OR_THROW( false,
435 "transformBitmap(): could not access source bitmap" );
438 // mapping table, to translate pAlphaReadAccess' pixel
439 // values into destination alpha values (needed e.g. for
440 // paletted 1-bit masks).
441 sal_uInt8 aAlphaMap[256];
443 if( rBitmap.IsAlpha() )
445 // source already has alpha channel - 1:1 mapping,
446 // i.e. aAlphaMap[0]=0,...,aAlphaMap[255]=255.
447 sal_uInt8 val=0;
448 sal_uInt8* pCur=aAlphaMap;
449 sal_uInt8* const pEnd=&aAlphaMap[256];
450 while(pCur != pEnd)
451 *pCur++ = val++;
453 // else: mapping table is not used
455 Bitmap aDstBitmap(aDestBmpSize, aSrcBitmap.getPixelFormat(), &pReadAccess->GetPalette());
456 Bitmap aDstAlpha( AlphaMask( aDestBmpSize ).GetBitmap() );
459 // just to be on the safe side: let the
460 // ScopedAccessors get destructed before
461 // copy-constructing the resulting bitmap. This will
462 // rule out the possibility that cached accessor data
463 // is not yet written back.
464 BitmapScopedWriteAccess pWriteAccess( aDstBitmap );
465 BitmapScopedWriteAccess pAlphaWriteAccess( aDstAlpha );
468 if( pWriteAccess.get() != nullptr &&
469 pAlphaWriteAccess.get() != nullptr &&
470 rTransform.isInvertible() )
472 // we're doing inverse mapping here, i.e. mapping
473 // points from the destination bitmap back to the
474 // source
475 ::basegfx::B2DHomMatrix aTransform( rLocalTransform );
476 aTransform.invert();
478 // for the time being, always read as ARGB
479 for( tools::Long y=0; y<aDestBmpSize.Height(); ++y )
481 // differentiate mask and alpha channel (on-off
482 // vs. multi-level transparency)
483 if( rBitmap.IsAlpha() )
485 Scanline pScan = pWriteAccess->GetScanline( y );
486 Scanline pScanAlpha = pAlphaWriteAccess->GetScanline( y );
487 // Handling alpha and mask just the same...
488 for( tools::Long x=0; x<aDestBmpSize.Width(); ++x )
490 ::basegfx::B2DPoint aPoint(x,y);
491 aPoint *= aTransform;
493 const tools::Long nSrcX( ::basegfx::fround<tools::Long>( aPoint.getX() ) );
494 const tools::Long nSrcY( ::basegfx::fround<tools::Long>( aPoint.getY() ) );
495 if( nSrcX < 0 || nSrcX >= aBmpSize.Width() ||
496 nSrcY < 0 || nSrcY >= aBmpSize.Height() )
498 pAlphaWriteAccess->SetPixelOnData( pScanAlpha, x, BitmapColor(0) );
500 else
502 const sal_uInt8 cAlphaIdx = pAlphaReadAccess->GetPixelIndex( nSrcY, nSrcX );
503 pAlphaWriteAccess->SetPixelOnData( pScanAlpha, x, BitmapColor(aAlphaMap[ cAlphaIdx ]) );
504 pWriteAccess->SetPixelOnData( pScan, x, pReadAccess->GetPixel( nSrcY, nSrcX ) );
508 else
510 Scanline pScan = pWriteAccess->GetScanline( y );
511 Scanline pScanAlpha = pAlphaWriteAccess->GetScanline( y );
512 for( tools::Long x=0; x<aDestBmpSize.Width(); ++x )
514 ::basegfx::B2DPoint aPoint(x,y);
515 aPoint *= aTransform;
517 const tools::Long nSrcX( ::basegfx::fround<tools::Long>( aPoint.getX() ) );
518 const tools::Long nSrcY( ::basegfx::fround<tools::Long>( aPoint.getY() ) );
519 if( nSrcX < 0 || nSrcX >= aBmpSize.Width() ||
520 nSrcY < 0 || nSrcY >= aBmpSize.Height() )
522 pAlphaWriteAccess->SetPixelOnData( pScanAlpha, x, BitmapColor(0) );
524 else
526 pAlphaWriteAccess->SetPixelOnData( pScanAlpha, x, BitmapColor(255) );
527 pWriteAccess->SetPixelOnData( pScan, x, pReadAccess->GetPixel( nSrcY,
528 nSrcX ) );
534 else
536 // TODO(E2): Error handling!
537 ENSURE_OR_THROW( false,
538 "transformBitmap(): could not access bitmap" );
542 return BitmapEx(aDstBitmap, AlphaMask(aDstAlpha));
545 void DrawAlphaBitmapAndAlphaGradient(BitmapEx & rBitmapEx, bool bFixedTransparence, float fTransparence, AlphaMask & rNewMask)
547 // mix existing and new alpha mask
548 AlphaMask aOldMask;
550 if(rBitmapEx.IsAlpha())
552 aOldMask = rBitmapEx.GetAlphaMask();
557 BitmapScopedWriteAccess pOld(aOldMask);
559 assert(pOld && "Got no access to old alpha mask (!)");
561 const double fFactor(1.0 / 255.0);
563 if(bFixedTransparence)
565 const double fOpNew(1.0 - fTransparence);
567 for(tools::Long y(0); y < pOld->Height(); y++)
569 Scanline pScanline = pOld->GetScanline( y );
570 for(tools::Long x(0); x < pOld->Width(); x++)
572 const double fOpOld(pOld->GetIndexFromData(pScanline, x) * fFactor);
573 const sal_uInt8 aCol(basegfx::fround((fOpOld * fOpNew) * 255.0));
575 pOld->SetPixelOnData(pScanline, x, BitmapColor(aCol));
579 else
581 BitmapScopedReadAccess pNew(rNewMask);
583 assert(pNew && "Got no access to new alpha mask (!)");
585 assert(pOld->Width() == pNew->Width() && pOld->Height() == pNew->Height() &&
586 "Alpha masks have different sizes (!)");
588 for(tools::Long y(0); y < pOld->Height(); y++)
590 Scanline pScanline = pOld->GetScanline( y );
591 for(tools::Long x(0); x < pOld->Width(); x++)
593 const double fOpOld(pOld->GetIndexFromData(pScanline, x) * fFactor);
594 const double fOpNew(pNew->GetIndexFromData(pScanline, x) * fFactor);
595 const sal_uInt8 aCol(basegfx::fround((fOpOld * fOpNew) * 255.0));
597 pOld->SetPixelOnData(pScanline, x, BitmapColor(aCol));
604 // apply combined bitmap as mask
605 rBitmapEx = BitmapEx(rBitmapEx.GetBitmap(), aOldMask);
609 void DrawAndClipBitmap(const Point& rPos, const Size& rSize, const BitmapEx& rBitmap, BitmapEx & aBmpEx, basegfx::B2DPolyPolygon const & rClipPath)
611 ScopedVclPtrInstance< VirtualDevice > pVDev;
612 MapMode aMapMode( MapUnit::Map100thMM );
613 aMapMode.SetOrigin( Point( -rPos.X(), -rPos.Y() ) );
614 const Size aOutputSizePixel( pVDev->LogicToPixel( rSize, aMapMode ) );
615 const Size aSizePixel( rBitmap.GetSizePixel() );
616 if ( aOutputSizePixel.Width() && aOutputSizePixel.Height() )
618 aMapMode.SetScaleX( Fraction( aSizePixel.Width(), aOutputSizePixel.Width() ) );
619 aMapMode.SetScaleY( Fraction( aSizePixel.Height(), aOutputSizePixel.Height() ) );
621 pVDev->SetMapMode( aMapMode );
622 pVDev->SetOutputSizePixel( aSizePixel );
623 pVDev->SetFillColor( COL_BLACK );
624 const tools::PolyPolygon aClip( rClipPath );
625 pVDev->DrawPolyPolygon( aClip );
627 // #i50672# Extract whole VDev content (to match size of rBitmap)
628 pVDev->EnableMapMode( false );
629 const Bitmap aVDevMask(pVDev->GetBitmap(Point(), aSizePixel));
631 if(aBmpEx.IsAlpha())
633 // bitmap already uses a Mask or Alpha, we need to blend that with
634 // the new masking in pVDev.
635 // need to blend in AlphaMask quality (8Bit)
636 AlphaMask fromVDev(aVDevMask);
637 AlphaMask fromBmpEx(aBmpEx.GetAlphaMask());
638 BitmapScopedReadAccess pR(fromVDev);
639 BitmapScopedWriteAccess pW(fromBmpEx);
641 if(pR && pW)
643 const tools::Long nWidth(std::min(pR->Width(), pW->Width()));
644 const tools::Long nHeight(std::min(pR->Height(), pW->Height()));
646 for(tools::Long nY(0); nY < nHeight; nY++)
648 Scanline pScanlineR = pR->GetScanline( nY );
649 Scanline pScanlineW = pW->GetScanline( nY );
650 for(tools::Long nX(0); nX < nWidth; nX++)
652 const sal_uInt8 nIndR(pR->GetIndexFromData(pScanlineR, nX));
653 const sal_uInt8 nIndW(pW->GetIndexFromData(pScanlineW, nX));
655 // these values represent alpha (255 == no, 0 == fully transparent),
656 // so to blend these we have to multiply
657 const sal_uInt8 nCombined((nIndR * nIndW) >> 8);
659 pW->SetPixelOnData(pScanlineW, nX, BitmapColor(nCombined));
664 pR.reset();
665 pW.reset();
666 aBmpEx = BitmapEx(aBmpEx.GetBitmap(), fromBmpEx);
668 else
670 // no mask yet, create and add new mask. For better quality, use Alpha,
671 // this allows the drawn mask being processed with AntiAliasing (AAed)
672 aBmpEx = BitmapEx(rBitmap.GetBitmap(), aVDevMask);
676 css::uno::Sequence< sal_Int8 > GetMaskDIB(BitmapEx const & aBmpEx)
678 if ( aBmpEx.IsAlpha() )
680 SvMemoryStream aMem;
681 WriteDIB(aBmpEx.GetAlphaMask().GetBitmap(), aMem, false, true);
682 return css::uno::Sequence< sal_Int8 >( static_cast<sal_Int8 const *>(aMem.GetData()), aMem.Tell() );
685 return css::uno::Sequence< sal_Int8 >();
688 static bool readAlpha( BitmapReadAccess const * pAlphaReadAcc, tools::Long nY, const tools::Long nWidth, unsigned char* data, tools::Long nOff )
690 bool bIsAlpha = false;
691 tools::Long nX;
692 int nAlpha;
693 Scanline pReadScan;
695 nOff += 3;
697 switch( pAlphaReadAcc->GetScanlineFormat() )
699 case ScanlineFormat::N8BitPal:
700 pReadScan = pAlphaReadAcc->GetScanline( nY );
701 for( nX = 0; nX < nWidth; nX++ )
703 BitmapColor const& rColor(
704 pAlphaReadAcc->GetPaletteColor(*pReadScan));
705 pReadScan++;
706 nAlpha = data[ nOff ] = rColor.GetIndex();
707 if( nAlpha != 255 )
708 bIsAlpha = true;
709 nOff += 4;
711 break;
712 default:
713 SAL_INFO( "canvas.cairo", "fallback to GetColor for alpha - slow, format: " << static_cast<int>(pAlphaReadAcc->GetScanlineFormat()) );
714 for( nX = 0; nX < nWidth; nX++ )
716 nAlpha = data[ nOff ] = pAlphaReadAcc->GetColor( nY, nX ).GetIndex();
717 if( nAlpha != 255 )
718 bIsAlpha = true;
719 nOff += 4;
723 return bIsAlpha;
729 * @param data will be filled with alpha data, if xBitmap is alpha/transparent image
730 * @param bHasAlpha will be set to true if resulting surface has alpha
732 void CanvasCairoExtractBitmapData( BitmapEx const & aBmpEx, Bitmap & aBitmap, unsigned char*& data, bool& bHasAlpha, tools::Long& rnWidth, tools::Long& rnHeight )
734 const AlphaMask& aAlpha = aBmpEx.GetAlphaMask();
736 BitmapScopedReadAccess pBitmapReadAcc( aBitmap );
737 BitmapScopedReadAccess pAlphaReadAcc;
738 const tools::Long nWidth = rnWidth = pBitmapReadAcc->Width();
739 const tools::Long nHeight = rnHeight = pBitmapReadAcc->Height();
740 tools::Long nX, nY;
741 bool bIsAlpha = false;
743 if( aBmpEx.IsAlpha() )
744 pAlphaReadAcc = aAlpha;
746 data = static_cast<unsigned char*>(malloc( nWidth*nHeight*4 ));
748 tools::Long nOff = 0;
749 ::Color aColor;
750 unsigned int nAlpha = 255;
752 #if !ENABLE_WASM_STRIP_PREMULTIPLY
753 vcl::bitmap::lookup_table const & premultiply_table = vcl::bitmap::get_premultiply_table();
754 #endif
755 for( nY = 0; nY < nHeight; nY++ )
757 ::Scanline pReadScan;
759 switch( pBitmapReadAcc->GetScanlineFormat() )
761 case ScanlineFormat::N8BitPal:
762 pReadScan = pBitmapReadAcc->GetScanline( nY );
763 if( pAlphaReadAcc )
764 if( readAlpha( pAlphaReadAcc.get(), 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 #else
775 if( pAlphaReadAcc )
776 nAlpha = data[ nOff + 3 ];
777 else
778 nAlpha = data[ nOff + 3 ] = 255;
779 #endif
780 aColor = pBitmapReadAcc->GetPaletteColor(*pReadScan++);
782 #ifdef OSL_BIGENDIAN
783 #if ENABLE_WASM_STRIP_PREMULTIPLY
784 data[ nOff++ ] = vcl::bitmap::premultiply(aColor.GetRed(), nAlpha);
785 data[ nOff++ ] = vcl::bitmap::premultiply(aColor.GetGreen(), nAlpha);
786 data[ nOff++ ] = vcl::bitmap::premultiply(aColor.GetBlue(), nAlpha);
787 #else
788 data[ nOff++ ] = premultiply_table[nAlpha][aColor.GetRed()];
789 data[ nOff++ ] = premultiply_table[nAlpha][aColor.GetGreen()];
790 data[ nOff++ ] = premultiply_table[nAlpha][aColor.GetBlue()];
791 #endif
792 #else
793 #if ENABLE_WASM_STRIP_PREMULTIPLY
794 data[ nOff++ ] = vcl::bitmap::premultiply(aColor.GetBlue(), nAlpha);
795 data[ nOff++ ] = vcl::bitmap::premultiply(aColor.GetGreen(), nAlpha);
796 data[ nOff++ ] = vcl::bitmap::premultiply(aColor.GetRed(), nAlpha);
797 #else
798 data[ nOff++ ] = premultiply_table[nAlpha][aColor.GetBlue()];
799 data[ nOff++ ] = premultiply_table[nAlpha][aColor.GetGreen()];
800 data[ nOff++ ] = premultiply_table[nAlpha][aColor.GetRed()];
801 #endif
802 nOff++;
803 #endif
805 break;
806 case ScanlineFormat::N24BitTcBgr:
807 pReadScan = pBitmapReadAcc->GetScanline( nY );
808 if( pAlphaReadAcc )
809 if( readAlpha( pAlphaReadAcc.get(), nY, nWidth, data, nOff ) )
810 bIsAlpha = true;
812 for( nX = 0; nX < nWidth; nX++ )
814 #ifdef OSL_BIGENDIAN
815 if( pAlphaReadAcc )
816 nAlpha = data[ nOff ];
817 else
818 nAlpha = data[ nOff ] = 255;
819 #if ENABLE_WASM_STRIP_PREMULTIPLY
820 data[ nOff + 3 ] = vcl::bitmap::premultiply(*pReadScan++, nAlpha);
821 data[ nOff + 2 ] = vcl::bitmap::premultiply(*pReadScan++, nAlpha);
822 data[ nOff + 1 ] = vcl::bitmap::premultiply(*pReadScan++, nAlpha);
823 #else
824 data[ nOff + 3 ] = premultiply_table[nAlpha][*pReadScan++];
825 data[ nOff + 2 ] = premultiply_table[nAlpha][*pReadScan++];
826 data[ nOff + 1 ] = premultiply_table[nAlpha][*pReadScan++];
827 #endif
828 nOff += 4;
829 #else
830 if( pAlphaReadAcc )
831 nAlpha = data[ nOff + 3 ];
832 else
833 nAlpha = data[ nOff + 3 ] = 255;
834 #if ENABLE_WASM_STRIP_PREMULTIPLY
835 data[ nOff++ ] = vcl::bitmap::premultiply(*pReadScan++, nAlpha);
836 data[ nOff++ ] = vcl::bitmap::premultiply(*pReadScan++, nAlpha);
837 data[ nOff++ ] = vcl::bitmap::premultiply(*pReadScan++, nAlpha);
838 #else
839 data[ nOff++ ] = premultiply_table[nAlpha][*pReadScan++];
840 data[ nOff++ ] = premultiply_table[nAlpha][*pReadScan++];
841 data[ nOff++ ] = premultiply_table[nAlpha][*pReadScan++];
842 #endif
843 nOff++;
844 #endif
846 break;
847 case ScanlineFormat::N24BitTcRgb:
848 pReadScan = pBitmapReadAcc->GetScanline( nY );
849 if( pAlphaReadAcc )
850 if( readAlpha( pAlphaReadAcc.get(), nY, nWidth, data, nOff ) )
851 bIsAlpha = true;
853 for( nX = 0; nX < nWidth; nX++ )
855 #ifdef OSL_BIGENDIAN
856 if( pAlphaReadAcc )
857 nAlpha = data[ nOff++ ];
858 else
859 nAlpha = data[ nOff++ ] = 255;
860 #if ENABLE_WASM_STRIP_PREMULTIPLY
861 data[ nOff++ ] = vcl::bitmap::premultiply(*pReadScan++, nAlpha);
862 data[ nOff++ ] = vcl::bitmap::premultiply(*pReadScan++, nAlpha);
863 data[ nOff++ ] = vcl::bitmap::premultiply(*pReadScan++, nAlpha);
864 #else
865 data[ nOff++ ] = premultiply_table[nAlpha][*pReadScan++];
866 data[ nOff++ ] = premultiply_table[nAlpha][*pReadScan++];
867 data[ nOff++ ] = premultiply_table[nAlpha][*pReadScan++];
868 #endif
869 #else
870 if( pAlphaReadAcc )
871 nAlpha = data[ nOff + 3 ];
872 else
873 nAlpha = data[ nOff + 3 ] = 255;
874 #if ENABLE_WASM_STRIP_PREMULTIPLY
875 data[ nOff++ ] = vcl::bitmap::premultiply(pReadScan[ 2 ], nAlpha);
876 data[ nOff++ ] = vcl::bitmap::premultiply(pReadScan[ 1 ], nAlpha);
877 data[ nOff++ ] = vcl::bitmap::premultiply(pReadScan[ 0 ], nAlpha);
878 #else
879 data[ nOff++ ] = premultiply_table[nAlpha][pReadScan[ 2 ]];
880 data[ nOff++ ] = premultiply_table[nAlpha][pReadScan[ 1 ]];
881 data[ nOff++ ] = premultiply_table[nAlpha][pReadScan[ 0 ]];
882 #endif
883 pReadScan += 3;
884 nOff++;
885 #endif
887 break;
888 case ScanlineFormat::N32BitTcBgra:
889 pReadScan = pBitmapReadAcc->GetScanline( nY );
890 if( pAlphaReadAcc )
891 if( readAlpha( pAlphaReadAcc.get(), nY, nWidth, data, nOff ) )
892 bIsAlpha = true;
894 for( nX = 0; nX < nWidth; nX++ )
896 #ifdef OSL_BIGENDIAN
897 if( pAlphaReadAcc )
898 nAlpha = data[ nOff++ ];
899 else
900 nAlpha = data[ nOff++ ] = 255;
901 #if ENABLE_WASM_STRIP_PREMULTIPLY
902 data[ nOff++ ] = vcl::bitmap::premultiply(pReadScan[ 2 ], nAlpha);
903 data[ nOff++ ] = vcl::bitmap::premultiply(pReadScan[ 1 ], nAlpha);
904 data[ nOff++ ] = vcl::bitmap::premultiply(pReadScan[ 0 ], nAlpha);
905 #else
906 data[ nOff++ ] = premultiply_table[nAlpha][pReadScan[ 2 ]];
907 data[ nOff++ ] = premultiply_table[nAlpha][pReadScan[ 1 ]];
908 data[ nOff++ ] = premultiply_table[nAlpha][pReadScan[ 0 ]];
909 #endif
910 pReadScan += 4;
911 #else
912 if( pAlphaReadAcc )
913 nAlpha = data[ nOff + 3 ];
914 else
915 nAlpha = data[ nOff + 3 ] = 255;
916 #if ENABLE_WASM_STRIP_PREMULTIPLY
917 data[ nOff++ ] = vcl::bitmap::premultiply(*pReadScan++, nAlpha);
918 data[ nOff++ ] = vcl::bitmap::premultiply(*pReadScan++, nAlpha);
919 data[ nOff++ ] = vcl::bitmap::premultiply(*pReadScan++, nAlpha);
920 #else
921 data[ nOff++ ] = premultiply_table[nAlpha][*pReadScan++];
922 data[ nOff++ ] = premultiply_table[nAlpha][*pReadScan++];
923 data[ nOff++ ] = premultiply_table[nAlpha][*pReadScan++];
924 #endif
925 pReadScan++;
926 nOff++;
927 #endif
929 break;
930 case ScanlineFormat::N32BitTcRgba:
931 pReadScan = pBitmapReadAcc->GetScanline( nY );
932 if( pAlphaReadAcc )
933 if( readAlpha( pAlphaReadAcc.get(), nY, nWidth, data, nOff ) )
934 bIsAlpha = true;
936 for( nX = 0; nX < nWidth; nX++ )
938 #ifdef OSL_BIGENDIAN
939 if( pAlphaReadAcc )
940 nAlpha = data[ nOff ++ ];
941 else
942 nAlpha = data[ nOff ++ ] = 255;
943 #if ENABLE_WASM_STRIP_PREMULTIPLY
944 data[ nOff++ ] = vcl::bitmap::premultiply(*pReadScan++, nAlpha);
945 data[ nOff++ ] = vcl::bitmap::premultiply(*pReadScan++, nAlpha);
946 data[ nOff++ ] = vcl::bitmap::premultiply(*pReadScan++, nAlpha);
947 #else
948 data[ nOff++ ] = premultiply_table[nAlpha][*pReadScan++];
949 data[ nOff++ ] = premultiply_table[nAlpha][*pReadScan++];
950 data[ nOff++ ] = premultiply_table[nAlpha][*pReadScan++];
951 #endif
952 pReadScan++;
953 #else
954 if( pAlphaReadAcc )
955 nAlpha = data[ nOff + 3 ];
956 else
957 nAlpha = data[ nOff + 3 ] = 255;
958 #if ENABLE_WASM_STRIP_PREMULTIPLY
959 data[ nOff++ ] = vcl::bitmap::premultiply(pReadScan[ 2 ], nAlpha);
960 data[ nOff++ ] = vcl::bitmap::premultiply(pReadScan[ 1 ], nAlpha);
961 data[ nOff++ ] = vcl::bitmap::premultiply(pReadScan[ 0 ], nAlpha);
962 #else
963 data[ nOff++ ] = premultiply_table[nAlpha][pReadScan[ 2 ]];
964 data[ nOff++ ] = premultiply_table[nAlpha][pReadScan[ 1 ]];
965 data[ nOff++ ] = premultiply_table[nAlpha][pReadScan[ 0 ]];
966 #endif
967 pReadScan += 4;
968 nOff++;
969 #endif
971 break;
972 default:
973 SAL_INFO( "canvas.cairo", "fallback to GetColor - slow, format: " << static_cast<int>(pBitmapReadAcc->GetScanlineFormat()) );
975 if( pAlphaReadAcc )
976 if( readAlpha( pAlphaReadAcc.get(), nY, nWidth, data, nOff ) )
977 bIsAlpha = true;
979 for( nX = 0; nX < nWidth; nX++ )
981 aColor = pBitmapReadAcc->GetColor( nY, nX );
983 // cairo need premultiplied color values
984 // TODO(rodo) handle endianness
985 #ifdef OSL_BIGENDIAN
986 if( pAlphaReadAcc )
987 nAlpha = data[ nOff++ ];
988 else
989 nAlpha = data[ nOff++ ] = 255;
990 #if ENABLE_WASM_STRIP_PREMULTIPLY
991 data[ nOff++ ] = vcl::bitmap::premultiply(aColor.GetRed(), nAlpha);
992 data[ nOff++ ] = vcl::bitmap::premultiply(aColor.GetGreen(), nAlpha);
993 data[ nOff++ ] = vcl::bitmap::premultiply(aColor.GetBlue(), nAlpha);
994 #else
995 data[ nOff++ ] = premultiply_table[nAlpha][aColor.GetRed()];
996 data[ nOff++ ] = premultiply_table[nAlpha][aColor.GetGreen()];
997 data[ nOff++ ] = premultiply_table[nAlpha][aColor.GetBlue()];
998 #endif
999 #else
1000 if( pAlphaReadAcc )
1001 nAlpha = data[ nOff + 3 ];
1002 else
1003 nAlpha = data[ nOff + 3 ] = 255;
1004 #if ENABLE_WASM_STRIP_PREMULTIPLY
1005 data[ nOff++ ] = vcl::bitmap::premultiply(aColor.GetBlue(), nAlpha);
1006 data[ nOff++ ] = vcl::bitmap::premultiply(aColor.GetGreen(), nAlpha);
1007 data[ nOff++ ] = vcl::bitmap::premultiply(aColor.GetRed(), nAlpha);
1008 #else
1009 data[ nOff++ ] = premultiply_table[nAlpha][aColor.GetBlue()];
1010 data[ nOff++ ] = premultiply_table[nAlpha][aColor.GetGreen()];
1011 data[ nOff++ ] = premultiply_table[nAlpha][aColor.GetRed()];
1012 #endif
1013 nOff ++;
1014 #endif
1019 bHasAlpha = bIsAlpha;
1023 uno::Sequence< sal_Int8 > CanvasExtractBitmapData(BitmapEx const & rBitmapEx, const geometry::IntegerRectangle2D& rect)
1025 const Bitmap& aBitmap( rBitmapEx.GetBitmap() );
1026 Bitmap aAlpha( rBitmapEx.GetAlphaMask().GetBitmap() );
1028 BitmapScopedReadAccess pReadAccess( aBitmap );
1029 BitmapScopedReadAccess pAlphaReadAccess;
1030 if (!aAlpha.IsEmpty())
1031 pAlphaReadAccess = aAlpha;
1033 assert( pReadAccess );
1035 // TODO(F1): Support more formats.
1036 const Size aBmpSize( aBitmap.GetSizePixel() );
1038 // for the time being, always return as BGRA
1039 uno::Sequence< sal_Int8 > aRes( 4*aBmpSize.Width()*aBmpSize.Height() );
1040 sal_Int8* pRes = aRes.getArray();
1042 int nCurrPos(0);
1043 for( tools::Long y=rect.Y1;
1044 y<aBmpSize.Height() && y<rect.Y2;
1045 ++y )
1047 if( pAlphaReadAccess.get() != nullptr )
1049 Scanline pScanlineReadAlpha = pAlphaReadAccess->GetScanline( y );
1050 for( tools::Long x=rect.X1;
1051 x<aBmpSize.Width() && x<rect.X2;
1052 ++x )
1054 pRes[ nCurrPos++ ] = pReadAccess->GetColor( y, x ).GetRed();
1055 pRes[ nCurrPos++ ] = pReadAccess->GetColor( y, x ).GetGreen();
1056 pRes[ nCurrPos++ ] = pReadAccess->GetColor( y, x ).GetBlue();
1057 pRes[ nCurrPos++ ] = 255 - pAlphaReadAccess->GetIndexFromData( pScanlineReadAlpha, x );
1060 else
1062 for( tools::Long x=rect.X1;
1063 x<aBmpSize.Width() && x<rect.X2;
1064 ++x )
1066 pRes[ nCurrPos++ ] = pReadAccess->GetColor( y, x ).GetRed();
1067 pRes[ nCurrPos++ ] = pReadAccess->GetColor( y, x ).GetGreen();
1068 pRes[ nCurrPos++ ] = pReadAccess->GetColor( y, x ).GetBlue();
1069 pRes[ nCurrPos++ ] = sal_uInt8(255);
1073 return aRes;
1076 BitmapEx createHistorical8x8FromArray(std::array<sal_uInt8,64> const & pArray, Color aColorPix, Color aColorBack)
1078 BitmapPalette aPalette(2);
1080 aPalette[0] = BitmapColor(aColorBack);
1081 aPalette[1] = BitmapColor(aColorPix);
1083 Bitmap aBitmap(Size(8, 8), vcl::PixelFormat::N8_BPP, &aPalette);
1084 BitmapScopedWriteAccess pContent(aBitmap);
1086 for(sal_uInt16 a(0); a < 8; a++)
1088 for(sal_uInt16 b(0); b < 8; b++)
1090 if(pArray[(a * 8) + b])
1092 pContent->SetPixelIndex(a, b, 1);
1094 else
1096 pContent->SetPixelIndex(a, b, 0);
1101 return BitmapEx(aBitmap);
1104 bool isHistorical8x8(const BitmapEx& rBitmapEx, Color& o_rBack, Color& o_rFront)
1106 bool bRet(false);
1108 if(!rBitmapEx.IsAlpha())
1110 const Bitmap& aBitmap(rBitmapEx.GetBitmap());
1112 if(8 == aBitmap.GetSizePixel().Width() && 8 == aBitmap.GetSizePixel().Height())
1114 // Historical 1bpp images are getting really historical,
1115 // even to the point that e.g. the png loader actually loads
1116 // them as RGB. But the pattern code in svx relies on this
1117 // assumption that any 2-color 1bpp bitmap is a pattern, and so it would
1118 // get confused by RGB. Try to detect if this image is really
1119 // just two colors and say it's a pattern bitmap if so.
1120 BitmapScopedReadAccess access(aBitmap);
1121 o_rBack = access->GetColor(0,0);
1122 bool foundSecondColor = false;;
1123 for(tools::Long y = 0; y < access->Height(); ++y)
1124 for(tools::Long x = 0; x < access->Width(); ++x)
1126 if(!foundSecondColor)
1128 if( access->GetColor(y,x) != o_rBack )
1130 o_rFront = access->GetColor(y,x);
1131 foundSecondColor = true;
1132 // Hard to know which of the two colors is the background,
1133 // select the lighter one.
1134 if( o_rFront.GetLuminance() > o_rBack.GetLuminance())
1135 std::swap( o_rFront, o_rBack );
1138 else
1140 if( access->GetColor(y,x) != o_rBack && access->GetColor(y,x) != o_rFront)
1141 return false;
1144 return true;
1148 return bRet;
1151 #if ENABLE_WASM_STRIP_PREMULTIPLY
1152 sal_uInt8 unpremultiply(sal_uInt8 c, sal_uInt8 a)
1154 return (a == 0) ? 0 : (c * 255 + a / 2) / a;
1157 sal_uInt8 premultiply(sal_uInt8 c, sal_uInt8 a)
1159 return (c * a + 127) / 255;
1161 #else
1162 sal_uInt8 unpremultiply(sal_uInt8 c, sal_uInt8 a)
1164 return get_unpremultiply_table()[a][c];
1167 static constexpr sal_uInt8 unpremultiplyImpl(sal_uInt8 c, sal_uInt8 a)
1169 return (a == 0) ? 0 : (c * 255 + a / 2) / a;
1172 sal_uInt8 premultiply(sal_uInt8 c, sal_uInt8 a)
1174 return get_premultiply_table()[a][c];
1177 static constexpr sal_uInt8 premultiplyImpl(sal_uInt8 c, sal_uInt8 a)
1179 return (c * a + 127) / 255;
1182 template<int... Is> static constexpr std::array<sal_uInt8, 256> make_unpremultiply_table_row_(
1183 int a, std::integer_sequence<int, Is...>)
1185 return {unpremultiplyImpl(Is, a)...};
1188 template<int... Is> static constexpr lookup_table make_unpremultiply_table_(
1189 std::integer_sequence<int, Is...>)
1191 return {make_unpremultiply_table_row_(Is, std::make_integer_sequence<int, 256>{})...};
1194 lookup_table const & get_unpremultiply_table()
1196 static constexpr auto unpremultiply_table = make_unpremultiply_table_(
1197 std::make_integer_sequence<int, 256>{});
1198 return unpremultiply_table;
1201 template<int... Is> static constexpr std::array<sal_uInt8, 256> make_premultiply_table_row_(
1202 int a, std::integer_sequence<int, Is...>)
1204 return {premultiplyImpl(Is, a)...};
1207 template<int... Is> static constexpr lookup_table make_premultiply_table_(
1208 std::integer_sequence<int, Is...>)
1210 return {make_premultiply_table_row_(Is, std::make_integer_sequence<int, 256>{})...};
1213 lookup_table const & get_premultiply_table()
1215 static constexpr auto premultiply_table = make_premultiply_table_(
1216 std::make_integer_sequence<int, 256>{});
1217 return premultiply_table;
1219 #endif
1221 bool convertBitmap32To24Plus8(BitmapEx const & rInput, BitmapEx & rResult)
1223 const Bitmap& aBitmap(rInput.GetBitmap());
1224 if (aBitmap.getPixelFormat() != vcl::PixelFormat::N32_BPP)
1225 return false;
1227 Size aSize = aBitmap.GetSizePixel();
1228 Bitmap aResultBitmap(aSize, vcl::PixelFormat::N24_BPP);
1229 AlphaMask aResultAlpha(aSize);
1231 BitmapScopedWriteAccess pResultBitmapAccess(aResultBitmap);
1232 BitmapScopedWriteAccess pResultAlphaAccess(aResultAlpha);
1234 BitmapScopedReadAccess pReadAccess(aBitmap);
1236 for (tools::Long nY = 0; nY < aSize.Height(); ++nY)
1238 Scanline aResultScan = pResultBitmapAccess->GetScanline(nY);
1239 Scanline aResultScanAlpha = pResultAlphaAccess->GetScanline(nY);
1241 Scanline aReadScan = pReadAccess->GetScanline(nY);
1243 for (tools::Long nX = 0; nX < aSize.Width(); ++nX)
1245 const BitmapColor aColor = pReadAccess->GetPixelFromData(aReadScan, nX);
1246 BitmapColor aResultColor(aColor.GetRed(), aColor.GetGreen(), aColor.GetBlue());
1247 BitmapColor aResultColorAlpha(aColor.GetAlpha(), aColor.GetAlpha(), aColor.GetAlpha());
1249 pResultBitmapAccess->SetPixelOnData(aResultScan, nX, aResultColor);
1250 pResultAlphaAccess->SetPixelOnData(aResultScanAlpha, nX, aResultColorAlpha);
1254 if (rInput.IsAlpha())
1255 rResult = BitmapEx(aResultBitmap, rInput.GetAlphaMask());
1256 else
1257 rResult = BitmapEx(aResultBitmap, aResultAlpha);
1258 return true;
1261 Bitmap GetDownsampledBitmap(Size const& rDstSizeTwip, Point const& rSrcPt, Size const& rSrcSz,
1262 Bitmap const& rBmp, tools::Long nMaxBmpDPIX, tools::Long nMaxBmpDPIY)
1264 Bitmap aBmp(rBmp);
1266 if (!aBmp.IsEmpty())
1268 const tools::Rectangle aBmpRect( Point(), aBmp.GetSizePixel() );
1269 tools::Rectangle aSrcRect( rSrcPt, rSrcSz );
1271 // do cropping if necessary
1272 if( aSrcRect.Intersection( aBmpRect ) != aBmpRect )
1274 if( !aSrcRect.IsEmpty() )
1275 aBmp.Crop( aSrcRect );
1276 else
1277 aBmp.SetEmpty();
1280 if( !aBmp.IsEmpty() )
1282 // do downsampling if necessary
1283 // #103209# Normalize size (mirroring has to happen outside of this method)
1284 Size aDstSizeTwip(std::abs(rDstSizeTwip.Width()), std::abs(rDstSizeTwip.Height()));
1286 const Size aBmpSize( aBmp.GetSizePixel() );
1287 const double fBmpPixelX = aBmpSize.Width();
1288 const double fBmpPixelY = aBmpSize.Height();
1289 const double fMaxPixelX
1290 = o3tl::convert<double>(aDstSizeTwip.Width(), o3tl::Length::twip, o3tl::Length::in)
1291 * nMaxBmpDPIX;
1292 const double fMaxPixelY
1293 = o3tl::convert<double>(aDstSizeTwip.Height(), o3tl::Length::twip, o3tl::Length::in)
1294 * nMaxBmpDPIY;
1296 // check, if the bitmap DPI exceeds the maximum DPI (allow 4 pixel rounding tolerance)
1297 if (((fBmpPixelX > (fMaxPixelX + 4)) ||
1298 (fBmpPixelY > (fMaxPixelY + 4))) &&
1299 (fBmpPixelY > 0.0) && (fMaxPixelY > 0.0))
1301 // do scaling
1302 Size aNewBmpSize;
1303 const double fBmpWH = fBmpPixelX / fBmpPixelY;
1304 const double fMaxWH = fMaxPixelX / fMaxPixelY;
1306 if (fBmpWH < fMaxWH)
1308 aNewBmpSize.setWidth(basegfx::fround<tools::Long>(fMaxPixelY * fBmpWH));
1309 aNewBmpSize.setHeight(basegfx::fround<tools::Long>(fMaxPixelY));
1311 else if (fBmpWH > 0.0)
1313 aNewBmpSize.setWidth(basegfx::fround<tools::Long>(fMaxPixelX));
1314 aNewBmpSize.setHeight(basegfx::fround<tools::Long>(fMaxPixelX / fBmpWH));
1317 if( aNewBmpSize.Width() && aNewBmpSize.Height() )
1318 aBmp.Scale(aNewBmpSize);
1319 else
1320 aBmp.SetEmpty();
1325 return aBmp;
1328 BitmapColor premultiply(const BitmapColor c)
1330 return BitmapColor(ColorAlpha, premultiply(c.GetRed(), c.GetAlpha()),
1331 premultiply(c.GetGreen(), c.GetAlpha()),
1332 premultiply(c.GetBlue(), c.GetAlpha()), c.GetAlpha());
1335 BitmapColor unpremultiply(const BitmapColor c)
1337 return BitmapColor(ColorAlpha, unpremultiply(c.GetRed(), c.GetAlpha()),
1338 unpremultiply(c.GetGreen(), c.GetAlpha()),
1339 unpremultiply(c.GetBlue(), c.GetAlpha()), c.GetAlpha());
1342 } // end vcl::bitmap
1344 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */