bump product version to 7.6.3.2-android
[LibreOffice.git] / vcl / source / helper / canvastools.cxx
blobe41417b8bdfea1ee831fddc1dd16d258f935add5
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/vector/b2dsize.hxx>
34 #include <basegfx/range/b2drectangle.hxx>
35 #include <basegfx/point/b2ipoint.hxx>
36 #include <basegfx/range/b2irectangle.hxx>
38 #include <sal/log.hxx>
39 #include <tools/helpers.hxx>
40 #include <comphelper/diagnose_ex.hxx>
42 #include <vcl/bitmapex.hxx>
44 #include <canvasbitmap.hxx>
45 #include <vcl/canvastools.hxx>
46 #include <bitmap/BitmapWriteAccess.hxx>
48 using namespace ::com::sun::star;
50 namespace vcl::unotools
52 uno::Reference< rendering::XBitmap > xBitmapFromBitmapEx(const ::BitmapEx& inputBitmap )
54 SAL_INFO( "vcl.helper", "vcl::unotools::xBitmapFromBitmapEx()" );
56 return new vcl::unotools::VclCanvasBitmap( inputBitmap );
59 namespace
61 bool equalsLayout( const rendering::IntegerBitmapLayout& rLHS,
62 const rendering::IntegerBitmapLayout& rRHS )
64 return
65 rLHS.ScanLineBytes == rRHS.ScanLineBytes &&
66 rLHS.ScanLineStride == rRHS.ScanLineStride &&
67 rLHS.PlaneStride == rRHS.PlaneStride &&
68 rLHS.ColorSpace == rRHS.ColorSpace &&
69 rLHS.Palette == rRHS.Palette &&
70 rLHS.IsMsbFirst == rRHS.IsMsbFirst;
72 bool readBmp( sal_Int32 nWidth,
73 sal_Int32 nHeight,
74 const rendering::IntegerBitmapLayout& rLayout,
75 const uno::Reference< rendering::XIntegerReadOnlyBitmap >& xInputBitmap,
76 BitmapScopedWriteAccess& rWriteAcc,
77 BitmapScopedWriteAccess& rAlphaAcc )
79 rendering::IntegerBitmapLayout aCurrLayout;
80 geometry::IntegerRectangle2D aRect;
81 uno::Sequence<sal_Int8> aPixelData;
82 uno::Sequence<rendering::RGBColor> aRGBColors;
83 uno::Sequence<rendering::ARGBColor> aARGBColors;
85 for( aRect.Y1=0; aRect.Y1<nHeight; ++aRect.Y1 )
87 aRect.X1 = 0; aRect.X2 = nWidth; aRect.Y2 = aRect.Y1+1;
88 try
90 aPixelData = xInputBitmap->getData(aCurrLayout,aRect);
92 catch( rendering::VolatileContentDestroyedException& )
94 // re-read bmp from the start
95 return false;
97 if( !equalsLayout(aCurrLayout, rLayout) )
98 return false; // re-read bmp from the start
100 Scanline pScanline = rWriteAcc->GetScanline( aRect.Y1 );
101 if( rAlphaAcc.get() )
103 Scanline pScanlineAlpha = rAlphaAcc->GetScanline( aRect.Y1 );
104 // read ARGB color
105 aARGBColors = rLayout.ColorSpace->convertIntegerToARGB(aPixelData);
107 if( rWriteAcc->HasPalette() )
109 for( sal_Int32 x=0; x<nWidth; ++x )
111 const rendering::ARGBColor& rColor=aARGBColors[x];
112 rWriteAcc->SetPixelOnData( pScanline, x,
113 BitmapColor(static_cast<sal_uInt8>(rWriteAcc->GetBestPaletteIndex(
114 BitmapColor( toByteColor(rColor.Red),
115 toByteColor(rColor.Green),
116 toByteColor(rColor.Blue))))) );
117 rAlphaAcc->SetPixelOnData( pScanlineAlpha, x,
118 BitmapColor( 255 - toByteColor(rColor.Alpha) ));
121 else
123 for( sal_Int32 x=0; x<nWidth; ++x )
125 const rendering::ARGBColor& rColor=aARGBColors[x];
126 rWriteAcc->SetPixelOnData( pScanline, x,
127 BitmapColor( toByteColor(rColor.Red),
128 toByteColor(rColor.Green),
129 toByteColor(rColor.Blue) ));
130 rAlphaAcc->SetPixelOnData( pScanlineAlpha, x,
131 BitmapColor( 255 - toByteColor(rColor.Alpha) ));
135 else
137 // read RGB color
138 aRGBColors = rLayout.ColorSpace->convertIntegerToRGB(aPixelData);
139 if( rWriteAcc->HasPalette() )
141 for( sal_Int32 x=0; x<nWidth; ++x )
143 const rendering::RGBColor& rColor=aRGBColors[x];
144 rWriteAcc->SetPixelOnData( pScanline, x,
145 BitmapColor(static_cast<sal_uInt8>(rWriteAcc->GetBestPaletteIndex(
146 BitmapColor( toByteColor(rColor.Red),
147 toByteColor(rColor.Green),
148 toByteColor(rColor.Blue))))) );
151 else
153 for( sal_Int32 x=0; x<nWidth; ++x )
155 const rendering::RGBColor& rColor=aRGBColors[x];
156 rWriteAcc->SetPixelOnData( pScanline, x,
157 BitmapColor( toByteColor(rColor.Red),
158 toByteColor(rColor.Green),
159 toByteColor(rColor.Blue) ));
165 return true;
169 ::BitmapEx bitmapExFromXBitmap( const uno::Reference< rendering::XIntegerReadOnlyBitmap >& xInputBitmap )
171 SAL_INFO( "vcl.helper", "vcl::unotools::bitmapExFromXBitmap()" );
173 if( !xInputBitmap.is() )
174 return ::BitmapEx();
176 // tunnel directly for known implementation
177 VclCanvasBitmap* pImplBitmap = dynamic_cast<VclCanvasBitmap*>(xInputBitmap.get());
178 if( pImplBitmap )
179 return pImplBitmap->getBitmapEx();
181 // retrieve data via UNO interface
183 // volatile bitmaps are a bit more complicated to read
184 // from...
186 // loop a few times, until successfully read (for XVolatileBitmap)
187 for( int i=0; i<10; ++i )
189 sal_Int32 nDepth=0;
190 sal_Int32 nAlphaDepth=0;
191 const rendering::IntegerBitmapLayout aLayout(
192 xInputBitmap->getMemoryLayout());
194 OSL_ENSURE(aLayout.ColorSpace.is(),
195 "Cannot convert image without color space!");
196 if( !aLayout.ColorSpace.is() )
197 return ::BitmapEx();
199 nDepth = aLayout.ColorSpace->getBitsPerPixel();
201 if( xInputBitmap->hasAlpha() )
203 // determine alpha channel depth
204 const uno::Sequence<sal_Int8> aTags(
205 aLayout.ColorSpace->getComponentTags() );
206 const sal_Int8* pStart(aTags.getConstArray());
207 const std::size_t nLen(aTags.getLength());
208 const sal_Int8* pEnd(pStart+nLen);
210 const std::ptrdiff_t nAlphaIndex =
211 std::find(pStart,pEnd,
212 rendering::ColorComponentTag::ALPHA) - pStart;
214 if( nAlphaIndex < sal::static_int_cast<std::ptrdiff_t>(nLen) )
216 nAlphaDepth = aLayout.ColorSpace->getComponentBitCounts()[nAlphaIndex] > 1 ? 8 : 1;
217 nDepth -= nAlphaDepth;
221 BitmapPalette aPalette;
222 if( aLayout.Palette.is() )
224 uno::Reference< rendering::XColorSpace > xPaletteColorSpace(
225 aLayout.Palette->getColorSpace());
226 ENSURE_OR_THROW(xPaletteColorSpace.is(),
227 "Palette without color space");
229 const sal_Int32 nEntryCount( aLayout.Palette->getNumberOfEntries() );
230 if( nEntryCount <= 256 )
232 if( nEntryCount <= 2 )
233 nDepth = 1;
234 else
235 nDepth = 8;
237 const sal_uInt16 nPaletteEntries(
238 sal::static_int_cast<sal_uInt16>(
239 std::min(sal_Int32(255), nEntryCount)));
241 // copy palette entries
242 aPalette.SetEntryCount(nPaletteEntries);
243 uno::Reference<rendering::XBitmapPalette> xPalette( aLayout.Palette );
244 uno::Reference<rendering::XColorSpace> xPalColorSpace( xPalette->getColorSpace() );
246 uno::Sequence<double> aPaletteEntry;
247 for( sal_uInt16 j=0; j<nPaletteEntries; ++j )
249 if( !xPalette->getIndex(aPaletteEntry,j) &&
250 nAlphaDepth == 0 )
252 nAlphaDepth = 1;
254 uno::Sequence<rendering::RGBColor> aColors=xPalColorSpace->convertToRGB(aPaletteEntry);
255 ENSURE_OR_THROW(aColors.getLength() == 1,
256 "Palette returned more or less than one entry");
257 const rendering::RGBColor& rColor=aColors[0];
258 aPalette[j] = BitmapColor(toByteColor(rColor.Red),
259 toByteColor(rColor.Green),
260 toByteColor(rColor.Blue));
265 const ::Size aPixelSize(
266 sizeFromIntegerSize2D(xInputBitmap->getSize()));
268 // normalize bitcount
269 auto ePixelFormat =
270 ( nDepth <= 8 ) ? vcl::PixelFormat::N8_BPP :
271 vcl::PixelFormat::N24_BPP;
272 auto eAlphaPixelFormat = vcl::PixelFormat::N8_BPP;
274 ::Bitmap aBitmap( aPixelSize,
275 ePixelFormat,
276 aLayout.Palette.is() ? &aPalette : nullptr );
277 ::Bitmap aAlpha;
278 if( nAlphaDepth )
279 aAlpha = Bitmap(aPixelSize,
280 eAlphaPixelFormat,
281 &Bitmap::GetGreyPalette(
282 sal::static_int_cast<sal_uInt16>(1 << nAlphaDepth)) );
284 { // limit scoped access
285 BitmapScopedWriteAccess pWriteAccess( aBitmap );
286 BitmapScopedWriteAccess pAlphaWriteAccess( nAlphaDepth ? aAlpha.AcquireWriteAccess() : nullptr,
287 aAlpha );
289 ENSURE_OR_THROW(pWriteAccess.get() != nullptr,
290 "Cannot get write access to bitmap");
292 const sal_Int32 nWidth(aPixelSize.Width());
293 const sal_Int32 nHeight(aPixelSize.Height());
295 if( !readBmp(nWidth,nHeight,aLayout,xInputBitmap,
296 pWriteAccess,pAlphaWriteAccess) )
297 continue;
298 } // limit scoped access
300 if( nAlphaDepth )
301 return ::BitmapEx( aBitmap,
302 AlphaMask( aAlpha ) );
303 else
304 return ::BitmapEx( aBitmap );
307 // failed to read data 10 times - bail out
308 return ::BitmapEx();
311 geometry::RealSize2D size2DFromSize( const Size& rSize )
313 return geometry::RealSize2D( rSize.Width(),
314 rSize.Height() );
317 Size sizeFromRealSize2D( const geometry::RealSize2D& rSize )
319 return Size( static_cast<tools::Long>(rSize.Width + .5),
320 static_cast<tools::Long>(rSize.Height + .5) );
323 ::Size sizeFromB2DSize( const basegfx::B2DVector& rVec )
325 return ::Size( FRound( rVec.getX() ),
326 FRound( rVec.getY() ) );
329 ::Point pointFromB2DPoint( const basegfx::B2DPoint& rPoint )
331 return pointFromB2IPoint(basegfx::fround(rPoint));
334 ::tools::Rectangle rectangleFromB2DRectangle( const basegfx::B2DRange& rRect )
336 return rectangleFromB2IRectangle(basegfx::fround(rRect));
339 Point pointFromB2IPoint( const basegfx::B2IPoint& rPoint )
341 return ::Point( rPoint.getX(),
342 rPoint.getY() );
345 basegfx::B2IPoint b2IPointFromPoint(Point const& rPoint)
347 return basegfx::B2IPoint(rPoint.X(), rPoint.Y());
350 tools::Rectangle rectangleFromB2IRectangle( const basegfx::B2IRange& rRect )
352 return ::tools::Rectangle( rRect.getMinX(),
353 rRect.getMinY(),
354 rRect.getMaxX(),
355 rRect.getMaxY() );
358 basegfx::B2IRectangle b2IRectangleFromRectangle(tools::Rectangle const& rRect)
360 // although B2IRange internally has separate height/width emptiness, it doesn't
361 // expose any API to let us set them separately, so just do the best we can.
362 if (rRect.IsWidthEmpty() && rRect.IsHeightEmpty())
363 return basegfx::B2IRange( basegfx::B2ITuple( rRect.Left(), rRect.Top() ) );
364 return basegfx::B2IRange( rRect.Left(),
365 rRect.Top(),
366 rRect.IsWidthEmpty() ? rRect.Left() : rRect.Right(),
367 rRect.IsHeightEmpty() ? rRect.Top() : rRect.Bottom() );
370 basegfx::B2DSize b2DSizeFromSize(const Size& rSize)
372 return basegfx::B2DSize(rSize.Width(), rSize.Height());
375 basegfx::B2DVector b2DVectorFromSize(const Size& rSize)
377 return basegfx::B2DVector(rSize.Width(), rSize.Height());
380 basegfx::B2DPoint b2DPointFromPoint( const ::Point& rPoint )
382 return basegfx::B2DPoint( rPoint.X(),
383 rPoint.Y() );
386 basegfx::B2DRange b2DRectangleFromRectangle( const ::tools::Rectangle& rRect )
388 // although B2DRange internally has separate height/width emptiness, it doesn't
389 // expose any API to let us set them separately, so just do the best we can.
390 if (rRect.IsWidthEmpty() && rRect.IsHeightEmpty())
391 return basegfx::B2DRange( basegfx::B2DTuple( rRect.Left(), rRect.Top() ) );
392 return basegfx::B2DRectangle( rRect.Left(),
393 rRect.Top(),
394 rRect.IsWidthEmpty() ? rRect.Left() : rRect.Right(),
395 rRect.IsHeightEmpty() ? rRect.Top() : rRect.Bottom() );
398 geometry::IntegerSize2D integerSize2DFromSize( const Size& rSize )
400 return geometry::IntegerSize2D( rSize.Width(),
401 rSize.Height() );
404 Size sizeFromIntegerSize2D( const geometry::IntegerSize2D& rSize )
406 return Size( rSize.Width,
407 rSize.Height );
410 Point pointFromIntegerPoint2D( const geometry::IntegerPoint2D& rPoint )
412 return Point( rPoint.X,
413 rPoint.Y );
416 tools::Rectangle rectangleFromIntegerRectangle2D( const geometry::IntegerRectangle2D& rRectangle )
418 return tools::Rectangle( rRectangle.X1, rRectangle.Y1,
419 rRectangle.X2, rRectangle.Y2 );
422 namespace
424 class StandardColorSpace : public cppu::WeakImplHelper< css::rendering::XColorSpace >
426 private:
427 uno::Sequence< sal_Int8 > m_aComponentTags;
429 virtual ::sal_Int8 SAL_CALL getType( ) override
431 return rendering::ColorSpaceType::RGB;
433 virtual uno::Sequence< ::sal_Int8 > SAL_CALL getComponentTags( ) override
435 return m_aComponentTags;
437 virtual ::sal_Int8 SAL_CALL getRenderingIntent( ) override
439 return rendering::RenderingIntent::PERCEPTUAL;
441 virtual uno::Sequence< beans::PropertyValue > SAL_CALL getProperties( ) override
443 return uno::Sequence< beans::PropertyValue >();
445 virtual uno::Sequence< double > SAL_CALL convertColorSpace( const uno::Sequence< double >& deviceColor,
446 const uno::Reference< rendering::XColorSpace >& targetColorSpace ) override
448 // TODO(P3): if we know anything about target
449 // colorspace, this can be greatly sped up
450 uno::Sequence<rendering::ARGBColor> aIntermediate(
451 convertToARGB(deviceColor));
452 return targetColorSpace->convertFromARGB(aIntermediate);
454 virtual uno::Sequence< rendering::RGBColor > SAL_CALL convertToRGB( const uno::Sequence< double >& deviceColor ) override
456 const double* pIn( deviceColor.getConstArray() );
457 const std::size_t nLen( deviceColor.getLength() );
458 ENSURE_ARG_OR_THROW2(nLen%4==0,
459 "number of channels no multiple of 4",
460 static_cast<rendering::XColorSpace*>(this), 0);
462 uno::Sequence< rendering::RGBColor > aRes(nLen/4);
463 rendering::RGBColor* pOut( aRes.getArray() );
464 for( std::size_t i=0; i<nLen; i+=4 )
466 *pOut++ = rendering::RGBColor(pIn[0],pIn[1],pIn[2]);
467 pIn += 4;
469 return aRes;
471 virtual uno::Sequence< rendering::ARGBColor > SAL_CALL convertToARGB( const uno::Sequence< double >& deviceColor ) override
473 const double* pIn( deviceColor.getConstArray() );
474 const std::size_t nLen( deviceColor.getLength() );
475 ENSURE_ARG_OR_THROW2(nLen%4==0,
476 "number of channels no multiple of 4",
477 static_cast<rendering::XColorSpace*>(this), 0);
479 uno::Sequence< rendering::ARGBColor > aRes(nLen/4);
480 rendering::ARGBColor* pOut( aRes.getArray() );
481 for( std::size_t i=0; i<nLen; i+=4 )
483 *pOut++ = rendering::ARGBColor(pIn[3],pIn[0],pIn[1],pIn[2]);
484 pIn += 4;
486 return aRes;
488 virtual uno::Sequence< rendering::ARGBColor > SAL_CALL convertToPARGB( const uno::Sequence< double >& deviceColor ) override
490 const double* pIn( deviceColor.getConstArray() );
491 const std::size_t nLen( deviceColor.getLength() );
492 ENSURE_ARG_OR_THROW2(nLen%4==0,
493 "number of channels no multiple of 4",
494 static_cast<rendering::XColorSpace*>(this), 0);
496 uno::Sequence< rendering::ARGBColor > aRes(nLen/4);
497 rendering::ARGBColor* pOut( aRes.getArray() );
498 for( std::size_t i=0; i<nLen; i+=4 )
500 *pOut++ = rendering::ARGBColor(pIn[3],pIn[3]*pIn[0],pIn[3]*pIn[1],pIn[3]*pIn[2]);
501 pIn += 4;
503 return aRes;
505 virtual uno::Sequence< double > SAL_CALL convertFromRGB( const uno::Sequence< rendering::RGBColor >& rgbColor ) override
507 const std::size_t nLen( rgbColor.getLength() );
509 uno::Sequence< double > aRes(nLen*4);
510 double* pColors=aRes.getArray();
511 for( const auto& rIn : rgbColor )
513 *pColors++ = rIn.Red;
514 *pColors++ = rIn.Green;
515 *pColors++ = rIn.Blue;
516 *pColors++ = 1.0;
518 return aRes;
520 virtual uno::Sequence< double > SAL_CALL convertFromARGB( const uno::Sequence< rendering::ARGBColor >& rgbColor ) override
522 const std::size_t nLen( rgbColor.getLength() );
524 uno::Sequence< double > aRes(nLen*4);
525 double* pColors=aRes.getArray();
526 for( const auto& rIn : rgbColor )
528 *pColors++ = rIn.Red;
529 *pColors++ = rIn.Green;
530 *pColors++ = rIn.Blue;
531 *pColors++ = rIn.Alpha;
533 return aRes;
535 virtual uno::Sequence< double > SAL_CALL convertFromPARGB( const uno::Sequence< rendering::ARGBColor >& rgbColor ) override
537 const std::size_t nLen( rgbColor.getLength() );
539 uno::Sequence< double > aRes(nLen*4);
540 double* pColors=aRes.getArray();
541 for( const auto& rIn : rgbColor )
543 *pColors++ = rIn.Red/rIn.Alpha;
544 *pColors++ = rIn.Green/rIn.Alpha;
545 *pColors++ = rIn.Blue/rIn.Alpha;
546 *pColors++ = rIn.Alpha;
548 return aRes;
551 public:
552 StandardColorSpace() : m_aComponentTags(4)
554 sal_Int8* pTags = m_aComponentTags.getArray();
555 pTags[0] = rendering::ColorComponentTag::RGB_RED;
556 pTags[1] = rendering::ColorComponentTag::RGB_GREEN;
557 pTags[2] = rendering::ColorComponentTag::RGB_BLUE;
558 pTags[3] = rendering::ColorComponentTag::ALPHA;
563 uno::Reference<rendering::XColorSpace> createStandardColorSpace()
565 return new StandardColorSpace();
568 uno::Sequence< double > colorToStdColorSpaceSequence( const Color& rColor )
570 return
572 toDoubleColor(rColor.GetRed()),
573 toDoubleColor(rColor.GetGreen()),
574 toDoubleColor(rColor.GetBlue()),
575 toDoubleColor(rColor.GetAlpha())
579 Color stdColorSpaceSequenceToColor( const uno::Sequence< double >& rColor )
581 ENSURE_ARG_OR_THROW( rColor.getLength() == 4,
582 "color must have 4 channels" );
584 Color aColor;
586 aColor.SetRed ( toByteColor(rColor[0]) );
587 aColor.SetGreen( toByteColor(rColor[1]) );
588 aColor.SetBlue ( toByteColor(rColor[2]) );
589 aColor.SetAlpha( toByteColor(rColor[3]) );
591 return aColor;
594 uno::Sequence< double > colorToDoubleSequence(
595 const Color& rColor,
596 const uno::Reference< rendering::XColorSpace >& xColorSpace )
598 uno::Sequence<rendering::ARGBColor> aSeq
601 toDoubleColor(rColor.GetAlpha()),
602 toDoubleColor(rColor.GetRed()),
603 toDoubleColor(rColor.GetGreen()),
604 toDoubleColor(rColor.GetBlue())
608 return xColorSpace->convertFromARGB(aSeq);
611 Color doubleSequenceToColor(
612 const uno::Sequence< double >& rColor,
613 const uno::Reference< rendering::XColorSpace >& xColorSpace )
615 const rendering::ARGBColor aARGBColor(
616 xColorSpace->convertToARGB(rColor)[0]);
618 return Color( ColorAlpha, toByteColor(aARGBColor.Alpha),
619 toByteColor(aARGBColor.Red),
620 toByteColor(aARGBColor.Green),
621 toByteColor(aARGBColor.Blue) );
624 } // namespace canvas
626 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */