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