bump product version to 6.4.0.3
[LibreOffice.git] / vcl / source / helper / canvastools.cxx
blobd9f6a9923a0c98edccde8149cd5ae20d28152dd0
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 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <com/sun/star/geometry/RealSize2D.hpp>
21 #include <com/sun/star/geometry/IntegerSize2D.hpp>
22 #include <com/sun/star/geometry/IntegerPoint2D.hpp>
23 #include <com/sun/star/geometry/IntegerRectangle2D.hpp>
25 #include <com/sun/star/rendering/ColorSpaceType.hpp>
26 #include <com/sun/star/rendering/RenderingIntent.hpp>
27 #include <com/sun/star/rendering/VolatileContentDestroyedException.hpp>
28 #include <com/sun/star/rendering/XBitmap.hpp>
29 #include <com/sun/star/rendering/IntegerBitmapLayout.hpp>
30 #include <com/sun/star/rendering/ColorComponentTag.hpp>
32 #include <basegfx/point/b2dpoint.hxx>
33 #include <basegfx/range/b2drectangle.hxx>
34 #include <basegfx/point/b2ipoint.hxx>
35 #include <basegfx/range/b2irectangle.hxx>
37 #include <sal/log.hxx>
38 #include <tools/helpers.hxx>
39 #include <tools/diagnose_ex.h>
41 #include <vcl/bitmapex.hxx>
43 #include <canvasbitmap.hxx>
44 #include <vcl/canvastools.hxx>
45 #include <bitmapwriteaccess.hxx>
47 using namespace ::com::sun::star;
49 namespace vcl
51 namespace unotools
53 uno::Reference< rendering::XBitmap > xBitmapFromBitmapEx(const ::BitmapEx& inputBitmap )
55 SAL_INFO( "vcl.helper", "vcl::unotools::xBitmapFromBitmapEx()" );
57 return new vcl::unotools::VclCanvasBitmap( inputBitmap );
60 namespace
62 bool equalsLayout( const rendering::IntegerBitmapLayout& rLHS,
63 const rendering::IntegerBitmapLayout& rRHS )
65 return
66 rLHS.ScanLineBytes == rRHS.ScanLineBytes &&
67 rLHS.ScanLineStride == rRHS.ScanLineStride &&
68 rLHS.PlaneStride == rRHS.PlaneStride &&
69 rLHS.ColorSpace == rRHS.ColorSpace &&
70 rLHS.Palette == rRHS.Palette &&
71 rLHS.IsMsbFirst == rRHS.IsMsbFirst;
73 bool readBmp( sal_Int32 nWidth,
74 sal_Int32 nHeight,
75 const rendering::IntegerBitmapLayout& rLayout,
76 const uno::Reference< rendering::XIntegerReadOnlyBitmap >& xInputBitmap,
77 BitmapScopedWriteAccess& rWriteAcc,
78 BitmapScopedWriteAccess& rAlphaAcc )
80 rendering::IntegerBitmapLayout aCurrLayout;
81 geometry::IntegerRectangle2D aRect;
82 uno::Sequence<sal_Int8> aPixelData;
83 uno::Sequence<rendering::RGBColor> aRGBColors;
84 uno::Sequence<rendering::ARGBColor> aARGBColors;
86 for( aRect.Y1=0; aRect.Y1<nHeight; ++aRect.Y1 )
88 aRect.X1 = 0; aRect.X2 = nWidth; aRect.Y2 = aRect.Y1+1;
89 try
91 aPixelData = xInputBitmap->getData(aCurrLayout,aRect);
93 catch( rendering::VolatileContentDestroyedException& )
95 // re-read bmp from the start
96 return false;
98 if( !equalsLayout(aCurrLayout, rLayout) )
99 return false; // re-read bmp from the start
101 Scanline pScanline = rWriteAcc->GetScanline( aRect.Y1 );
102 if( rAlphaAcc.get() )
104 Scanline pScanlineAlpha = rAlphaAcc->GetScanline( aRect.Y1 );
105 // read ARGB color
106 aARGBColors = rLayout.ColorSpace->convertIntegerToARGB(aPixelData);
108 if( rWriteAcc->HasPalette() )
110 for( sal_Int32 x=0; x<nWidth; ++x )
112 const rendering::ARGBColor& rColor=aARGBColors[x];
113 rWriteAcc->SetPixelOnData( pScanline, x,
114 BitmapColor(static_cast<sal_uInt8>(rWriteAcc->GetBestPaletteIndex(
115 BitmapColor( toByteColor(rColor.Red),
116 toByteColor(rColor.Green),
117 toByteColor(rColor.Blue))))) );
118 rAlphaAcc->SetPixelOnData( pScanlineAlpha, x,
119 BitmapColor( 255 - toByteColor(rColor.Alpha) ));
122 else
124 for( sal_Int32 x=0; x<nWidth; ++x )
126 const rendering::ARGBColor& rColor=aARGBColors[x];
127 rWriteAcc->SetPixelOnData( pScanline, x,
128 BitmapColor( toByteColor(rColor.Red),
129 toByteColor(rColor.Green),
130 toByteColor(rColor.Blue) ));
131 rAlphaAcc->SetPixelOnData( pScanlineAlpha, x,
132 BitmapColor( 255 - toByteColor(rColor.Alpha) ));
136 else
138 // read RGB color
139 aRGBColors = rLayout.ColorSpace->convertIntegerToRGB(aPixelData);
140 if( rWriteAcc->HasPalette() )
142 for( sal_Int32 x=0; x<nWidth; ++x )
144 const rendering::RGBColor& rColor=aRGBColors[x];
145 rWriteAcc->SetPixelOnData( pScanline, x,
146 BitmapColor(static_cast<sal_uInt8>(rWriteAcc->GetBestPaletteIndex(
147 BitmapColor( toByteColor(rColor.Red),
148 toByteColor(rColor.Green),
149 toByteColor(rColor.Blue))))) );
152 else
154 for( sal_Int32 x=0; x<nWidth; ++x )
156 const rendering::RGBColor& rColor=aRGBColors[x];
157 rWriteAcc->SetPixelOnData( pScanline, x,
158 BitmapColor( toByteColor(rColor.Red),
159 toByteColor(rColor.Green),
160 toByteColor(rColor.Blue) ));
166 return true;
170 ::BitmapEx bitmapExFromXBitmap( const uno::Reference< rendering::XIntegerReadOnlyBitmap >& xInputBitmap )
172 SAL_INFO( "vcl.helper", "vcl::unotools::bitmapExFromXBitmap()" );
174 if( !xInputBitmap.is() )
175 return ::BitmapEx();
177 // tunnel directly for known implementation
178 VclCanvasBitmap* pImplBitmap = dynamic_cast<VclCanvasBitmap*>(xInputBitmap.get());
179 if( pImplBitmap )
180 return pImplBitmap->getBitmapEx();
182 // retrieve data via UNO interface
184 // volatile bitmaps are a bit more complicated to read
185 // from...
187 // loop a few times, until successfully read (for XVolatileBitmap)
188 for( int i=0; i<10; ++i )
190 sal_Int32 nDepth=0;
191 sal_Int32 nAlphaDepth=0;
192 const rendering::IntegerBitmapLayout aLayout(
193 xInputBitmap->getMemoryLayout());
195 OSL_ENSURE(aLayout.ColorSpace.is(),
196 "Cannot convert image without color space!");
197 if( !aLayout.ColorSpace.is() )
198 return ::BitmapEx();
200 nDepth = aLayout.ColorSpace->getBitsPerPixel();
202 if( xInputBitmap->hasAlpha() )
204 // determine alpha channel depth
205 const uno::Sequence<sal_Int8> aTags(
206 aLayout.ColorSpace->getComponentTags() );
207 const sal_Int8* pStart(aTags.getConstArray());
208 const std::size_t nLen(aTags.getLength());
209 const sal_Int8* pEnd(pStart+nLen);
211 const std::ptrdiff_t nAlphaIndex =
212 std::find(pStart,pEnd,
213 rendering::ColorComponentTag::ALPHA) - pStart;
215 if( nAlphaIndex < sal::static_int_cast<std::ptrdiff_t>(nLen) )
217 nAlphaDepth = aLayout.ColorSpace->getComponentBitCounts()[nAlphaIndex] > 1 ? 8 : 1;
218 nDepth -= nAlphaDepth;
222 BitmapPalette aPalette;
223 if( aLayout.Palette.is() )
225 uno::Reference< rendering::XColorSpace > xPaletteColorSpace(
226 aLayout.Palette->getColorSpace());
227 ENSURE_OR_THROW(xPaletteColorSpace.is(),
228 "Palette without color space");
230 const sal_Int32 nEntryCount( aLayout.Palette->getNumberOfEntries() );
231 if( nEntryCount <= 256 )
233 if( nEntryCount <= 2 )
234 nDepth = 1;
235 else
236 nDepth = 8;
238 const sal_uInt16 nPaletteEntries(
239 sal::static_int_cast<sal_uInt16>(
240 std::min(sal_Int32(255), nEntryCount)));
242 // copy palette entries
243 aPalette.SetEntryCount(nPaletteEntries);
244 uno::Reference<rendering::XBitmapPalette> xPalette( aLayout.Palette );
245 uno::Reference<rendering::XColorSpace> xPalColorSpace( xPalette->getColorSpace() );
247 uno::Sequence<double> aPaletteEntry;
248 for( sal_uInt16 j=0; j<nPaletteEntries; ++j )
250 if( !xPalette->getIndex(aPaletteEntry,j) &&
251 nAlphaDepth == 0 )
253 nAlphaDepth = 1;
255 uno::Sequence<rendering::RGBColor> aColors=xPalColorSpace->convertToRGB(aPaletteEntry);
256 ENSURE_OR_THROW(aColors.getLength() == 1,
257 "Palette returned more or less than one entry");
258 const rendering::RGBColor& rColor=aColors[0];
259 aPalette[j] = BitmapColor(toByteColor(rColor.Red),
260 toByteColor(rColor.Green),
261 toByteColor(rColor.Blue));
266 const ::Size aPixelSize(
267 sizeFromIntegerSize2D(xInputBitmap->getSize()));
269 // normalize bitcount
270 nDepth =
271 ( nDepth <= 1 ) ? 1 :
272 ( nDepth <= 4 ) ? 4 :
273 ( nDepth <= 8 ) ? 8 : 24;
275 ::Bitmap aBitmap( aPixelSize,
276 sal::static_int_cast<sal_uInt16>(nDepth),
277 aLayout.Palette.is() ? &aPalette : nullptr );
278 ::Bitmap aAlpha;
279 if( nAlphaDepth )
280 aAlpha = ::Bitmap( aPixelSize,
281 sal::static_int_cast<sal_uInt16>(nAlphaDepth),
282 &::Bitmap::GetGreyPalette(
283 sal::static_int_cast<sal_uInt16>(1 << nAlphaDepth)) );
285 { // limit scoped access
286 BitmapScopedWriteAccess pWriteAccess( aBitmap );
287 BitmapScopedWriteAccess pAlphaWriteAccess( nAlphaDepth ? aAlpha.AcquireWriteAccess() : nullptr,
288 aAlpha );
290 ENSURE_OR_THROW(pWriteAccess.get() != nullptr,
291 "Cannot get write access to bitmap");
293 const sal_Int32 nWidth(aPixelSize.Width());
294 const sal_Int32 nHeight(aPixelSize.Height());
296 if( !readBmp(nWidth,nHeight,aLayout,xInputBitmap,
297 pWriteAccess,pAlphaWriteAccess) )
298 continue;
299 } // limit scoped access
301 if( nAlphaDepth )
302 return ::BitmapEx( aBitmap,
303 AlphaMask( aAlpha ) );
304 else
305 return ::BitmapEx( aBitmap );
308 // failed to read data 10 times - bail out
309 return ::BitmapEx();
312 geometry::RealSize2D size2DFromSize( const Size& rSize )
314 return geometry::RealSize2D( rSize.Width(),
315 rSize.Height() );
318 Size sizeFromRealSize2D( const geometry::RealSize2D& rSize )
320 return Size( static_cast<long>(rSize.Width + .5),
321 static_cast<long>(rSize.Height + .5) );
324 ::Size sizeFromB2DSize( const basegfx::B2DVector& rVec )
326 return ::Size( FRound( rVec.getX() ),
327 FRound( rVec.getY() ) );
330 ::Point pointFromB2DPoint( const basegfx::B2DPoint& rPoint )
332 return pointFromB2IPoint(basegfx::fround(rPoint));
335 ::tools::Rectangle rectangleFromB2DRectangle( const basegfx::B2DRange& rRect )
337 return rectangleFromB2IRectangle(basegfx::fround(rRect));
340 Point pointFromB2IPoint( const basegfx::B2IPoint& rPoint )
342 return ::Point( rPoint.getX(),
343 rPoint.getY() );
346 basegfx::B2IPoint b2IPointFromPoint(Point const& rPoint)
348 return basegfx::B2IPoint(rPoint.X(), rPoint.Y());
351 tools::Rectangle rectangleFromB2IRectangle( const basegfx::B2IRange& rRect )
353 return ::tools::Rectangle( rRect.getMinX(),
354 rRect.getMinY(),
355 rRect.getMaxX(),
356 rRect.getMaxY() );
359 basegfx::B2IRectangle b2IRectangleFromRectangle(tools::Rectangle const& rRect)
361 // although B2IRange internally has separate height/width emptiness, it doesn't
362 // expose any API to let us set them separately, so just do the best we can.
363 if (rRect.IsWidthEmpty() && rRect.IsHeightEmpty())
364 return basegfx::B2IRange( basegfx::B2ITuple( rRect.Left(), rRect.Top() ) );
365 return basegfx::B2IRange( rRect.Left(),
366 rRect.Top(),
367 rRect.IsWidthEmpty() ? rRect.Left() : rRect.Right(),
368 rRect.IsHeightEmpty() ? rRect.Top() : rRect.Bottom() );
371 basegfx::B2DVector b2DSizeFromSize( const ::Size& rSize )
373 return basegfx::B2DVector( rSize.Width(),
374 rSize.Height() );
377 basegfx::B2DPoint b2DPointFromPoint( const ::Point& rPoint )
379 return basegfx::B2DPoint( rPoint.X(),
380 rPoint.Y() );
383 basegfx::B2DRange b2DRectangleFromRectangle( const ::tools::Rectangle& rRect )
385 // although B2DRange internally has separate height/width emptiness, it doesn't
386 // expose any API to let us set them separately, so just do the best we can.
387 if (rRect.IsWidthEmpty() && rRect.IsHeightEmpty())
388 return basegfx::B2DRange( basegfx::B2DTuple( rRect.Left(), rRect.Top() ) );
389 return basegfx::B2DRectangle( rRect.Left(),
390 rRect.Top(),
391 rRect.IsWidthEmpty() ? rRect.Left() : rRect.Right(),
392 rRect.IsHeightEmpty() ? rRect.Top() : rRect.Bottom() );
395 geometry::IntegerSize2D integerSize2DFromSize( const Size& rSize )
397 return geometry::IntegerSize2D( rSize.Width(),
398 rSize.Height() );
401 Size sizeFromIntegerSize2D( const geometry::IntegerSize2D& rSize )
403 return Size( rSize.Width,
404 rSize.Height );
407 Point pointFromIntegerPoint2D( const geometry::IntegerPoint2D& rPoint )
409 return Point( rPoint.X,
410 rPoint.Y );
413 tools::Rectangle rectangleFromIntegerRectangle2D( const geometry::IntegerRectangle2D& rRectangle )
415 return tools::Rectangle( rRectangle.X1, rRectangle.Y1,
416 rRectangle.X2, rRectangle.Y2 );
419 namespace
421 class StandardColorSpace : public cppu::WeakImplHelper< css::rendering::XColorSpace >
423 private:
424 uno::Sequence< sal_Int8 > m_aComponentTags;
426 virtual ::sal_Int8 SAL_CALL getType( ) override
428 return rendering::ColorSpaceType::RGB;
430 virtual uno::Sequence< ::sal_Int8 > SAL_CALL getComponentTags( ) override
432 return m_aComponentTags;
434 virtual ::sal_Int8 SAL_CALL getRenderingIntent( ) override
436 return rendering::RenderingIntent::PERCEPTUAL;
438 virtual uno::Sequence< beans::PropertyValue > SAL_CALL getProperties( ) override
440 return uno::Sequence< beans::PropertyValue >();
442 virtual uno::Sequence< double > SAL_CALL convertColorSpace( const uno::Sequence< double >& deviceColor,
443 const uno::Reference< rendering::XColorSpace >& targetColorSpace ) override
445 // TODO(P3): if we know anything about target
446 // colorspace, this can be greatly sped up
447 uno::Sequence<rendering::ARGBColor> aIntermediate(
448 convertToARGB(deviceColor));
449 return targetColorSpace->convertFromARGB(aIntermediate);
451 virtual uno::Sequence< rendering::RGBColor > SAL_CALL convertToRGB( const uno::Sequence< double >& deviceColor ) override
453 const double* pIn( deviceColor.getConstArray() );
454 const std::size_t nLen( deviceColor.getLength() );
455 ENSURE_ARG_OR_THROW2(nLen%4==0,
456 "number of channels no multiple of 4",
457 static_cast<rendering::XColorSpace*>(this), 0);
459 uno::Sequence< rendering::RGBColor > aRes(nLen/4);
460 rendering::RGBColor* pOut( aRes.getArray() );
461 for( std::size_t i=0; i<nLen; i+=4 )
463 *pOut++ = rendering::RGBColor(pIn[0],pIn[1],pIn[2]);
464 pIn += 4;
466 return aRes;
468 virtual uno::Sequence< rendering::ARGBColor > SAL_CALL convertToARGB( const uno::Sequence< double >& deviceColor ) override
470 const double* pIn( deviceColor.getConstArray() );
471 const std::size_t nLen( deviceColor.getLength() );
472 ENSURE_ARG_OR_THROW2(nLen%4==0,
473 "number of channels no multiple of 4",
474 static_cast<rendering::XColorSpace*>(this), 0);
476 uno::Sequence< rendering::ARGBColor > aRes(nLen/4);
477 rendering::ARGBColor* pOut( aRes.getArray() );
478 for( std::size_t i=0; i<nLen; i+=4 )
480 *pOut++ = rendering::ARGBColor(pIn[3],pIn[0],pIn[1],pIn[2]);
481 pIn += 4;
483 return aRes;
485 virtual uno::Sequence< rendering::ARGBColor > SAL_CALL convertToPARGB( const uno::Sequence< double >& deviceColor ) override
487 const double* pIn( deviceColor.getConstArray() );
488 const std::size_t nLen( deviceColor.getLength() );
489 ENSURE_ARG_OR_THROW2(nLen%4==0,
490 "number of channels no multiple of 4",
491 static_cast<rendering::XColorSpace*>(this), 0);
493 uno::Sequence< rendering::ARGBColor > aRes(nLen/4);
494 rendering::ARGBColor* pOut( aRes.getArray() );
495 for( std::size_t i=0; i<nLen; i+=4 )
497 *pOut++ = rendering::ARGBColor(pIn[3],pIn[3]*pIn[0],pIn[3]*pIn[1],pIn[3]*pIn[2]);
498 pIn += 4;
500 return aRes;
502 virtual uno::Sequence< double > SAL_CALL convertFromRGB( const uno::Sequence< rendering::RGBColor >& rgbColor ) override
504 const std::size_t nLen( rgbColor.getLength() );
506 uno::Sequence< double > aRes(nLen*4);
507 double* pColors=aRes.getArray();
508 for( const auto& rIn : rgbColor )
510 *pColors++ = rIn.Red;
511 *pColors++ = rIn.Green;
512 *pColors++ = rIn.Blue;
513 *pColors++ = 1.0;
515 return aRes;
517 virtual uno::Sequence< double > SAL_CALL convertFromARGB( const uno::Sequence< rendering::ARGBColor >& rgbColor ) override
519 const std::size_t nLen( rgbColor.getLength() );
521 uno::Sequence< double > aRes(nLen*4);
522 double* pColors=aRes.getArray();
523 for( const auto& rIn : rgbColor )
525 *pColors++ = rIn.Red;
526 *pColors++ = rIn.Green;
527 *pColors++ = rIn.Blue;
528 *pColors++ = rIn.Alpha;
530 return aRes;
532 virtual uno::Sequence< double > SAL_CALL convertFromPARGB( const uno::Sequence< rendering::ARGBColor >& rgbColor ) override
534 const std::size_t nLen( rgbColor.getLength() );
536 uno::Sequence< double > aRes(nLen*4);
537 double* pColors=aRes.getArray();
538 for( const auto& rIn : rgbColor )
540 *pColors++ = rIn.Red/rIn.Alpha;
541 *pColors++ = rIn.Green/rIn.Alpha;
542 *pColors++ = rIn.Blue/rIn.Alpha;
543 *pColors++ = rIn.Alpha;
545 return aRes;
548 public:
549 StandardColorSpace() : m_aComponentTags(4)
551 sal_Int8* pTags = m_aComponentTags.getArray();
552 pTags[0] = rendering::ColorComponentTag::RGB_RED;
553 pTags[1] = rendering::ColorComponentTag::RGB_GREEN;
554 pTags[2] = rendering::ColorComponentTag::RGB_BLUE;
555 pTags[3] = rendering::ColorComponentTag::ALPHA;
560 uno::Reference<rendering::XColorSpace> createStandardColorSpace()
562 return new StandardColorSpace();
565 uno::Sequence< double > colorToStdColorSpaceSequence( const Color& rColor )
567 uno::Sequence< double > aRet(4);
568 double* pRet = aRet.getArray();
570 pRet[0] = toDoubleColor(rColor.GetRed());
571 pRet[1] = toDoubleColor(rColor.GetGreen());
572 pRet[2] = toDoubleColor(rColor.GetBlue());
574 // VCL's notion of alpha is different from the rest of the world's
575 pRet[3] = 1.0 - toDoubleColor(rColor.GetTransparency());
577 return aRet;
580 Color stdColorSpaceSequenceToColor( const uno::Sequence< double >& rColor )
582 ENSURE_ARG_OR_THROW( rColor.getLength() == 4,
583 "color must have 4 channels" );
585 Color aColor;
587 aColor.SetRed ( toByteColor(rColor[0]) );
588 aColor.SetGreen( toByteColor(rColor[1]) );
589 aColor.SetBlue ( toByteColor(rColor[2]) );
590 // VCL's notion of alpha is different from the rest of the world's
591 aColor.SetTransparency( 255 - toByteColor(rColor[3]) );
593 return aColor;
596 uno::Sequence< double > colorToDoubleSequence(
597 const Color& rColor,
598 const uno::Reference< rendering::XColorSpace >& xColorSpace )
600 uno::Sequence<rendering::ARGBColor> aSeq(1);
601 aSeq[0] = rendering::ARGBColor(
602 1.0-toDoubleColor(rColor.GetTransparency()),
603 toDoubleColor(rColor.GetRed()),
604 toDoubleColor(rColor.GetGreen()),
605 toDoubleColor(rColor.GetBlue()) );
607 return xColorSpace->convertFromARGB(aSeq);
610 Color doubleSequenceToColor(
611 const uno::Sequence< double >& rColor,
612 const uno::Reference< rendering::XColorSpace >& xColorSpace )
614 const rendering::ARGBColor aARGBColor(
615 xColorSpace->convertToARGB(rColor)[0]);
617 return Color( 255-toByteColor(aARGBColor.Alpha),
618 toByteColor(aARGBColor.Red),
619 toByteColor(aARGBColor.Green),
620 toByteColor(aARGBColor.Blue) );
623 } // namespace vcltools
625 } // namespace canvas
627 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */