1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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 .
21 #include <rtl/logfile.hxx>
22 #include <cppuhelper/compbase1.hxx>
24 #include <com/sun/star/geometry/RealSize2D.hpp>
25 #include <com/sun/star/geometry/RealPoint2D.hpp>
26 #include <com/sun/star/geometry/RealRectangle2D.hpp>
27 #include <com/sun/star/geometry/IntegerSize2D.hpp>
28 #include <com/sun/star/geometry/IntegerPoint2D.hpp>
29 #include <com/sun/star/geometry/IntegerRectangle2D.hpp>
30 #include <com/sun/star/geometry/RealBezierSegment2D.hpp>
32 #include <com/sun/star/rendering/ColorSpaceType.hpp>
33 #include <com/sun/star/rendering/RenderingIntent.hpp>
34 #include <com/sun/star/rendering/XGraphicDevice.hpp>
35 #include <com/sun/star/rendering/XBitmap.hpp>
36 #include <com/sun/star/rendering/XPolyPolygon2D.hpp>
37 #include <com/sun/star/rendering/IntegerBitmapLayout.hpp>
38 #include <com/sun/star/rendering/XIntegerBitmap.hpp>
39 #include <com/sun/star/rendering/ColorComponentTag.hpp>
41 #include <basegfx/matrix/b2dhommatrix.hxx>
42 #include <basegfx/vector/b2dsize.hxx>
43 #include <basegfx/point/b2dpoint.hxx>
44 #include <basegfx/range/b2drectangle.hxx>
45 #include <basegfx/vector/b2isize.hxx>
46 #include <basegfx/point/b2ipoint.hxx>
47 #include <basegfx/range/b2irectangle.hxx>
50 #include <basegfx/polygon/b2dpolygon.hxx>
51 #include <basegfx/tools/canvastools.hxx>
52 #include <basegfx/polygon/b2dpolypolygon.hxx>
54 #include <tools/poly.hxx>
55 #include <tools/diagnose_ex.h>
58 #include <vcl/salbtype.hxx>
59 #include <vcl/bmpacc.hxx>
60 #include <vcl/bitmapex.hxx>
62 #include <canvasbitmap.hxx>
63 #include <vcl/canvastools.hxx>
64 #include <boost/unordered_map.hpp>
67 using namespace ::com::sun::star
;
73 uno::Reference
< rendering::XBitmap
> xBitmapFromBitmapEx( const uno::Reference
< rendering::XGraphicDevice
>& /*xGraphicDevice*/,
74 const ::BitmapEx
& inputBitmap
)
76 RTL_LOGFILE_CONTEXT( aLog
, "::vcl::unotools::xBitmapFromBitmapEx()" );
78 return new vcl::unotools::VclCanvasBitmap( inputBitmap
);
83 inline bool operator==( const rendering::IntegerBitmapLayout
& rLHS
,
84 const rendering::IntegerBitmapLayout
& rRHS
)
87 rLHS
.ScanLineBytes
== rRHS
.ScanLineBytes
&&
88 rLHS
.ScanLineStride
== rRHS
.ScanLineStride
&&
89 rLHS
.PlaneStride
== rRHS
.PlaneStride
&&
90 rLHS
.ColorSpace
== rRHS
.ColorSpace
&&
91 rLHS
.Palette
== rRHS
.Palette
&&
92 rLHS
.IsMsbFirst
== rRHS
.IsMsbFirst
;
95 bool readBmp( sal_Int32 nWidth
,
97 const rendering::IntegerBitmapLayout
& rLayout
,
98 const uno::Reference
< rendering::XIntegerReadOnlyBitmap
>& xInputBitmap
,
99 Bitmap::ScopedWriteAccess
& rWriteAcc
,
100 Bitmap::ScopedWriteAccess
& rAlphaAcc
)
102 rendering::IntegerBitmapLayout aCurrLayout
;
103 geometry::IntegerRectangle2D aRect
;
104 uno::Sequence
<sal_Int8
> aPixelData
;
105 uno::Sequence
<rendering::RGBColor
> aRGBColors
;
106 uno::Sequence
<rendering::ARGBColor
> aARGBColors
;
108 for( aRect
.Y1
=0; aRect
.Y1
<nHeight
; ++aRect
.Y1
)
110 aRect
.X1
= 0; aRect
.X2
= nWidth
; aRect
.Y2
= aRect
.Y1
+1;
113 aPixelData
= xInputBitmap
->getData(aCurrLayout
,aRect
);
115 catch( rendering::VolatileContentDestroyedException
& )
117 // re-read bmp from the start
120 if( !(aCurrLayout
== rLayout
) )
121 return false; // re-read bmp from the start
123 if( rAlphaAcc
.get() )
126 aARGBColors
= rLayout
.ColorSpace
->convertIntegerToARGB(aPixelData
);
128 if( rWriteAcc
->HasPalette() )
130 for( sal_Int32 x
=0; x
<nWidth
; ++x
)
132 const rendering::ARGBColor
& rColor
=aARGBColors
[x
];
133 rWriteAcc
->SetPixelIndex( aRect
.Y1
, x
,
134 (sal_uInt8
) rWriteAcc
->GetBestPaletteIndex(
135 BitmapColor( toByteColor(rColor
.Red
),
136 toByteColor(rColor
.Green
),
137 toByteColor(rColor
.Blue
))) );
138 rAlphaAcc
->SetPixel( aRect
.Y1
, x
,
139 BitmapColor( 255 - toByteColor(rColor
.Alpha
) ));
144 for( sal_Int32 x
=0; x
<nWidth
; ++x
)
146 const rendering::ARGBColor
& rColor
=aARGBColors
[x
];
147 rWriteAcc
->SetPixel( aRect
.Y1
, x
,
148 BitmapColor( toByteColor(rColor
.Red
),
149 toByteColor(rColor
.Green
),
150 toByteColor(rColor
.Blue
) ));
151 rAlphaAcc
->SetPixel( aRect
.Y1
, x
,
152 BitmapColor( 255 - toByteColor(rColor
.Alpha
) ));
159 aRGBColors
= rLayout
.ColorSpace
->convertIntegerToRGB(aPixelData
);
160 if( rWriteAcc
->HasPalette() )
162 for( sal_Int32 x
=0; x
<nWidth
; ++x
)
164 const rendering::RGBColor
& rColor
=aRGBColors
[x
];
165 rWriteAcc
->SetPixelIndex( aRect
.Y1
, x
,
166 (sal_uInt8
) rWriteAcc
->GetBestPaletteIndex(
167 BitmapColor( toByteColor(rColor
.Red
),
168 toByteColor(rColor
.Green
),
169 toByteColor(rColor
.Blue
))) );
174 for( sal_Int32 x
=0; x
<nWidth
; ++x
)
176 const rendering::RGBColor
& rColor
=aRGBColors
[x
];
177 rWriteAcc
->SetPixel( aRect
.Y1
, x
,
178 BitmapColor( toByteColor(rColor
.Red
),
179 toByteColor(rColor
.Green
),
180 toByteColor(rColor
.Blue
) ));
190 ::BitmapEx VCL_DLLPUBLIC
bitmapExFromXBitmap( const uno::Reference
< rendering::XIntegerReadOnlyBitmap
>& xInputBitmap
)
192 RTL_LOGFILE_CONTEXT( aLog
, "::vcl::unotools::bitmapExFromXBitmap()" );
194 if( !xInputBitmap
.is() )
197 // tunnel directly for known implementation
198 VclCanvasBitmap
* pImplBitmap
= dynamic_cast<VclCanvasBitmap
*>(xInputBitmap
.get());
200 return pImplBitmap
->getBitmapEx();
202 // retrieve data via UNO interface
204 // volatile bitmaps are a bit more complicated to read
206 uno::Reference
<rendering::XVolatileBitmap
> xVolatileBitmap(
207 xInputBitmap
, uno::UNO_QUERY
);
209 // loop a few times, until successfully read (for XVolatileBitmap)
210 for( int i
=0; i
<10; ++i
)
213 sal_Int32 nAlphaDepth
=0;
214 const rendering::IntegerBitmapLayout
aLayout(
215 xInputBitmap
->getMemoryLayout());
217 OSL_ENSURE(aLayout
.ColorSpace
.is(),
218 "Cannot convert image without color space!");
219 if( !aLayout
.ColorSpace
.is() )
222 nDepth
= aLayout
.ColorSpace
->getBitsPerPixel();
224 if( xInputBitmap
->hasAlpha() )
226 // determine alpha channel depth
227 const uno::Sequence
<sal_Int8
> aTags(
228 aLayout
.ColorSpace
->getComponentTags() );
229 const uno::Sequence
<sal_Int32
> aDepths(
230 aLayout
.ColorSpace
->getComponentBitCounts() );
231 const sal_Int8
* pStart(aTags
.getConstArray());
232 const sal_Size
nLen(aTags
.getLength());
233 const sal_Int8
* pEnd(pStart
+nLen
);
235 const std::ptrdiff_t nAlphaIndex
=
236 std::find(pStart
,pEnd
,
237 rendering::ColorComponentTag::ALPHA
) - pStart
;
239 if( nAlphaIndex
< sal::static_int_cast
<std::ptrdiff_t>(nLen
) )
241 nAlphaDepth
= aLayout
.ColorSpace
->getComponentBitCounts()[nAlphaIndex
] > 1 ? 8 : 1;
242 nDepth
-= nAlphaDepth
;
246 BitmapPalette aPalette
;
247 if( aLayout
.Palette
.is() )
249 uno::Reference
< rendering::XColorSpace
> xPaletteColorSpace(
250 aLayout
.Palette
->getColorSpace());
251 ENSURE_OR_THROW(xPaletteColorSpace
.is(),
252 "Palette without color space");
254 const sal_Int32
nEntryCount( aLayout
.Palette
->getNumberOfEntries() );
255 if( nEntryCount
<= 256 )
257 if( nEntryCount
<= 2 )
262 const sal_uInt16
nPaletteEntries(
263 sal::static_int_cast
<sal_uInt16
>(
264 std::min(sal_Int32(255), nEntryCount
)));
266 // copy palette entries
267 aPalette
.SetEntryCount(nPaletteEntries
);
268 uno::Reference
<rendering::XBitmapPalette
> xPalette( aLayout
.Palette
);
269 uno::Reference
<rendering::XColorSpace
> xPalColorSpace( xPalette
->getColorSpace() );
271 uno::Sequence
<double> aPaletteEntry
;
272 for( sal_uInt16 j
=0; j
<nPaletteEntries
; ++j
)
274 if( !xPalette
->getIndex(aPaletteEntry
,j
) &&
279 uno::Sequence
<rendering::RGBColor
> aColors
=xPalColorSpace
->convertToRGB(aPaletteEntry
);
280 ENSURE_OR_THROW(aColors
.getLength() == 1,
281 "Palette returned more or less than one entry");
282 const rendering::RGBColor
& rColor
=aColors
[0];
283 aPalette
[j
] = BitmapColor(toByteColor(rColor
.Red
),
284 toByteColor(rColor
.Green
),
285 toByteColor(rColor
.Blue
));
290 const ::Size
aPixelSize(
291 sizeFromIntegerSize2D(xInputBitmap
->getSize()));
293 // normalize bitcount
295 ( nDepth
<= 1 ) ? 1 :
296 ( nDepth
<= 4 ) ? 4 :
297 ( nDepth
<= 8 ) ? 8 : 24;
299 ::Bitmap
aBitmap( aPixelSize
,
300 sal::static_int_cast
<sal_uInt16
>(nDepth
),
301 aLayout
.Palette
.is() ? &aPalette
: NULL
);
304 aAlpha
= ::Bitmap( aPixelSize
,
305 sal::static_int_cast
<sal_uInt16
>(nAlphaDepth
),
306 &::Bitmap::GetGreyPalette(
307 sal::static_int_cast
<sal_uInt16
>(1L << nAlphaDepth
)) );
309 { // limit scoped access
310 Bitmap::ScopedWriteAccess
pWriteAccess( aBitmap
);
311 Bitmap::ScopedWriteAccess
pAlphaWriteAccess( nAlphaDepth
? aAlpha
.AcquireWriteAccess() : NULL
,
314 ENSURE_OR_THROW(pWriteAccess
.get() != NULL
,
315 "Cannot get write access to bitmap");
317 const sal_Int32
nWidth(aPixelSize
.Width());
318 const sal_Int32
nHeight(aPixelSize
.Height());
320 if( !readBmp(nWidth
,nHeight
,aLayout
,xInputBitmap
,
321 pWriteAccess
,pAlphaWriteAccess
) )
323 } // limit scoped access
326 return ::BitmapEx( aBitmap
,
327 AlphaMask( aAlpha
) );
329 return ::BitmapEx( aBitmap
);
332 // failed to read data 10 times - bail out
337 geometry::RealSize2D
size2DFromSize( const Size
& rSize
)
339 return geometry::RealSize2D( rSize
.Width(),
343 Size
sizeFromRealSize2D( const geometry::RealSize2D
& rSize
)
345 return Size( static_cast<long>(rSize
.Width
+ .5),
346 static_cast<long>(rSize
.Height
+ .5) );
349 ::Size
sizeFromB2DSize( const ::basegfx::B2DVector
& rVec
)
351 return ::Size( FRound( rVec
.getX() ),
352 FRound( rVec
.getY() ) );
355 ::Point
pointFromB2DPoint( const ::basegfx::B2DPoint
& rPoint
)
357 return ::Point( FRound( rPoint
.getX() ),
358 FRound( rPoint
.getY() ) );
361 ::Rectangle
rectangleFromB2DRectangle( const ::basegfx::B2DRange
& rRect
)
363 return ::Rectangle( FRound( rRect
.getMinX() ),
364 FRound( rRect
.getMinY() ),
365 FRound( rRect
.getMaxX() ),
366 FRound( rRect
.getMaxY() ) );
369 Point
pointFromB2IPoint( const ::basegfx::B2IPoint
& rPoint
)
371 return ::Point( rPoint
.getX(),
375 Rectangle
rectangleFromB2IRectangle( const ::basegfx::B2IRange
& rRect
)
377 return ::Rectangle( rRect
.getMinX(),
383 ::basegfx::B2DVector
b2DSizeFromSize( const ::Size
& rSize
)
385 return ::basegfx::B2DVector( rSize
.Width(),
389 ::basegfx::B2DPoint
b2DPointFromPoint( const ::Point
& rPoint
)
391 return ::basegfx::B2DPoint( rPoint
.X(),
395 ::basegfx::B2DRange
b2DRectangleFromRectangle( const ::Rectangle
& rRect
)
397 return ::basegfx::B2DRange( rRect
.Left(),
403 geometry::IntegerSize2D
integerSize2DFromSize( const Size
& rSize
)
405 return geometry::IntegerSize2D( rSize
.Width(),
409 Size
sizeFromIntegerSize2D( const geometry::IntegerSize2D
& rSize
)
411 return Size( rSize
.Width
,
415 Point
pointFromIntegerPoint2D( const geometry::IntegerPoint2D
& rPoint
)
417 return Point( rPoint
.X
,
421 Rectangle
rectangleFromIntegerRectangle2D( const geometry::IntegerRectangle2D
& rRectangle
)
423 return Rectangle( rRectangle
.X1
, rRectangle
.Y1
,
424 rRectangle
.X2
, rRectangle
.Y2
);
429 class StandardColorSpace
: public cppu::WeakImplHelper1
< com::sun::star::rendering::XColorSpace
>
432 uno::Sequence
< sal_Int8
> m_aComponentTags
;
434 virtual ::sal_Int8 SAL_CALL
getType( ) throw (uno::RuntimeException
)
436 return rendering::ColorSpaceType::RGB
;
438 virtual uno::Sequence
< ::sal_Int8
> SAL_CALL
getComponentTags( ) throw (uno::RuntimeException
)
440 return m_aComponentTags
;
442 virtual ::sal_Int8 SAL_CALL
getRenderingIntent( ) throw (uno::RuntimeException
)
444 return rendering::RenderingIntent::PERCEPTUAL
;
446 virtual uno::Sequence
< beans::PropertyValue
> SAL_CALL
getProperties( ) throw (uno::RuntimeException
)
448 return uno::Sequence
< beans::PropertyValue
>();
450 virtual uno::Sequence
< double > SAL_CALL
convertColorSpace( const uno::Sequence
< double >& deviceColor
,
451 const uno::Reference
< rendering::XColorSpace
>& targetColorSpace
) throw (lang::IllegalArgumentException
,
452 uno::RuntimeException
)
454 // TODO(P3): if we know anything about target
455 // colorspace, this can be greatly sped up
456 uno::Sequence
<rendering::ARGBColor
> aIntermediate(
457 convertToARGB(deviceColor
));
458 return targetColorSpace
->convertFromARGB(aIntermediate
);
460 virtual uno::Sequence
< rendering::RGBColor
> SAL_CALL
convertToRGB( const uno::Sequence
< double >& deviceColor
) throw (lang::IllegalArgumentException
, uno::RuntimeException
)
462 const double* pIn( deviceColor
.getConstArray() );
463 const sal_Size
nLen( deviceColor
.getLength() );
464 ENSURE_ARG_OR_THROW2(nLen
%4==0,
465 "number of channels no multiple of 4",
466 static_cast<rendering::XColorSpace
*>(this), 0);
468 uno::Sequence
< rendering::RGBColor
> aRes(nLen
/4);
469 rendering::RGBColor
* pOut( aRes
.getArray() );
470 for( sal_Size i
=0; i
<nLen
; i
+=4 )
472 *pOut
++ = rendering::RGBColor(pIn
[0],pIn
[1],pIn
[2]);
477 virtual uno::Sequence
< rendering::ARGBColor
> SAL_CALL
convertToARGB( const uno::Sequence
< double >& deviceColor
) throw (lang::IllegalArgumentException
, uno::RuntimeException
)
479 const double* pIn( deviceColor
.getConstArray() );
480 const sal_Size
nLen( deviceColor
.getLength() );
481 ENSURE_ARG_OR_THROW2(nLen
%4==0,
482 "number of channels no multiple of 4",
483 static_cast<rendering::XColorSpace
*>(this), 0);
485 uno::Sequence
< rendering::ARGBColor
> aRes(nLen
/4);
486 rendering::ARGBColor
* pOut( aRes
.getArray() );
487 for( sal_Size i
=0; i
<nLen
; i
+=4 )
489 *pOut
++ = rendering::ARGBColor(pIn
[3],pIn
[0],pIn
[1],pIn
[2]);
494 virtual uno::Sequence
< rendering::ARGBColor
> SAL_CALL
convertToPARGB( const uno::Sequence
< double >& deviceColor
) throw (lang::IllegalArgumentException
, uno::RuntimeException
)
496 const double* pIn( deviceColor
.getConstArray() );
497 const sal_Size
nLen( deviceColor
.getLength() );
498 ENSURE_ARG_OR_THROW2(nLen
%4==0,
499 "number of channels no multiple of 4",
500 static_cast<rendering::XColorSpace
*>(this), 0);
502 uno::Sequence
< rendering::ARGBColor
> aRes(nLen
/4);
503 rendering::ARGBColor
* pOut( aRes
.getArray() );
504 for( sal_Size i
=0; i
<nLen
; i
+=4 )
506 *pOut
++ = rendering::ARGBColor(pIn
[3],pIn
[3]*pIn
[0],pIn
[3]*pIn
[1],pIn
[3]*pIn
[2]);
511 virtual uno::Sequence
< double > SAL_CALL
convertFromRGB( const uno::Sequence
< rendering::RGBColor
>& rgbColor
) throw (lang::IllegalArgumentException
, uno::RuntimeException
)
513 const rendering::RGBColor
* pIn( rgbColor
.getConstArray() );
514 const sal_Size
nLen( rgbColor
.getLength() );
516 uno::Sequence
< double > aRes(nLen
*4);
517 double* pColors
=aRes
.getArray();
518 for( sal_Size i
=0; i
<nLen
; ++i
)
520 *pColors
++ = pIn
->Red
;
521 *pColors
++ = pIn
->Green
;
522 *pColors
++ = pIn
->Blue
;
528 virtual uno::Sequence
< double > SAL_CALL
convertFromARGB( const uno::Sequence
< rendering::ARGBColor
>& rgbColor
) throw (lang::IllegalArgumentException
, uno::RuntimeException
)
530 const rendering::ARGBColor
* pIn( rgbColor
.getConstArray() );
531 const sal_Size
nLen( rgbColor
.getLength() );
533 uno::Sequence
< double > aRes(nLen
*4);
534 double* pColors
=aRes
.getArray();
535 for( sal_Size i
=0; i
<nLen
; ++i
)
537 *pColors
++ = pIn
->Red
;
538 *pColors
++ = pIn
->Green
;
539 *pColors
++ = pIn
->Blue
;
540 *pColors
++ = pIn
->Alpha
;
545 virtual uno::Sequence
< double > SAL_CALL
convertFromPARGB( const uno::Sequence
< rendering::ARGBColor
>& rgbColor
) throw (lang::IllegalArgumentException
, uno::RuntimeException
)
547 const rendering::ARGBColor
* pIn( rgbColor
.getConstArray() );
548 const sal_Size
nLen( rgbColor
.getLength() );
550 uno::Sequence
< double > aRes(nLen
*4);
551 double* pColors
=aRes
.getArray();
552 for( sal_Size i
=0; i
<nLen
; ++i
)
554 *pColors
++ = pIn
->Red
/pIn
->Alpha
;
555 *pColors
++ = pIn
->Green
/pIn
->Alpha
;
556 *pColors
++ = pIn
->Blue
/pIn
->Alpha
;
557 *pColors
++ = pIn
->Alpha
;
564 StandardColorSpace() : m_aComponentTags(4)
566 sal_Int8
* pTags
= m_aComponentTags
.getArray();
567 pTags
[0] = rendering::ColorComponentTag::RGB_RED
;
568 pTags
[1] = rendering::ColorComponentTag::RGB_GREEN
;
569 pTags
[2] = rendering::ColorComponentTag::RGB_BLUE
;
570 pTags
[3] = rendering::ColorComponentTag::ALPHA
;
575 uno::Reference
<rendering::XColorSpace
> VCL_DLLPUBLIC
createStandardColorSpace()
577 return new StandardColorSpace();
581 Color
stdColorSpaceSequenceToColor( const uno::Sequence
< double >& rColor
)
583 ENSURE_ARG_OR_THROW( rColor
.getLength() == 4,
584 "color must have 4 channels" );
588 aColor
.SetRed ( toByteColor(rColor
[0]) );
589 aColor
.SetGreen( toByteColor(rColor
[1]) );
590 aColor
.SetBlue ( toByteColor(rColor
[2]) );
591 // VCL's notion of alpha is different from the rest of the world's
592 aColor
.SetTransparency( 255 - toByteColor(rColor
[3]) );
597 uno::Sequence
< double > VCL_DLLPUBLIC
colorToDoubleSequence(
599 const uno::Reference
< rendering::XColorSpace
>& xColorSpace
)
601 uno::Sequence
<rendering::ARGBColor
> aSeq(1);
602 aSeq
[0] = rendering::ARGBColor(
603 1.0-toDoubleColor(rColor
.GetTransparency()),
604 toDoubleColor(rColor
.GetRed()),
605 toDoubleColor(rColor
.GetGreen()),
606 toDoubleColor(rColor
.GetBlue()) );
608 return xColorSpace
->convertFromARGB(aSeq
);
611 Color VCL_DLLPUBLIC
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( 255-toByteColor(aARGBColor
.Alpha
),
619 toByteColor(aARGBColor
.Red
),
620 toByteColor(aARGBColor
.Green
),
621 toByteColor(aARGBColor
.Blue
) );
625 } // namespace vcltools
627 } // namespace canvas
629 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */