Line data Source code
1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /*
3 : * This file is part of the LibreOffice project.
4 : *
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/.
8 : *
9 : * This file incorporates work covered by the following license notice:
10 : *
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 .
18 : */
19 :
20 :
21 : #include <canvas/debug.hxx>
22 : #include <tools/diagnose_ex.h>
23 :
24 : #include <rtl/math.hxx>
25 : #include <rtl/logfile.hxx>
26 :
27 : #include <com/sun/star/geometry/RealSize2D.hpp>
28 : #include <com/sun/star/geometry/RealPoint2D.hpp>
29 : #include <com/sun/star/geometry/RealRectangle2D.hpp>
30 : #include <com/sun/star/rendering/RenderState.hpp>
31 : #include <com/sun/star/rendering/XCanvas.hpp>
32 : #include <com/sun/star/rendering/XBitmap.hpp>
33 : #include <com/sun/star/rendering/XPolyPolygon2D.hpp>
34 : #include <com/sun/star/geometry/RealBezierSegment2D.hpp>
35 : #include <com/sun/star/rendering/XIntegerBitmap.hpp>
36 :
37 : #include <vcl/salbtype.hxx>
38 : #include <vcl/bmpacc.hxx>
39 : #include <vcl/bitmapex.hxx>
40 : #include <vcl/metric.hxx>
41 : #include <vcl/canvastools.hxx>
42 :
43 : #include <basegfx/point/b2dpoint.hxx>
44 : #include <basegfx/tuple/b2dtuple.hxx>
45 : #include <basegfx/polygon/b2dpolygontools.hxx>
46 : #include <basegfx/range/b2drectangle.hxx>
47 : #include <basegfx/matrix/b2dhommatrix.hxx>
48 : #include <basegfx/tools/canvastools.hxx>
49 : #include <basegfx/numeric/ftools.hxx>
50 :
51 : #include <canvas/canvastools.hxx>
52 : #include <o3tl/compat_functional.hxx>
53 :
54 : #include "impltools.hxx"
55 : #include "canvasbitmap.hxx"
56 :
57 : #include <numeric>
58 :
59 :
60 : using namespace ::com::sun::star;
61 :
62 : namespace vclcanvas
63 : {
64 : namespace tools
65 : {
66 0 : ::BitmapEx bitmapExFromXBitmap( const uno::Reference< rendering::XBitmap >& xBitmap )
67 : {
68 : // TODO(F3): CanvasCustomSprite should also be tunnelled
69 : // through (also implements XIntegerBitmap interface)
70 0 : CanvasBitmap* pBitmapImpl = dynamic_cast< CanvasBitmap* >( xBitmap.get() );
71 :
72 0 : if( pBitmapImpl )
73 : {
74 0 : return pBitmapImpl->getBitmap();
75 : }
76 : else
77 : {
78 0 : SpriteCanvas* pCanvasImpl = dynamic_cast< SpriteCanvas* >( xBitmap.get() );
79 0 : if( pCanvasImpl && pCanvasImpl->getBackBuffer() )
80 : {
81 : // TODO(F3): mind the plain Canvas impl. Consolidate with CWS canvas05
82 0 : const ::OutputDevice& rDev( pCanvasImpl->getBackBuffer()->getOutDev() );
83 0 : const ::Point aEmptyPoint;
84 : return rDev.GetBitmapEx( aEmptyPoint,
85 0 : rDev.GetOutputSizePixel() );
86 : }
87 :
88 : // TODO(F2): add support for floating point bitmap formats
89 : uno::Reference< rendering::XIntegerReadOnlyBitmap > xIntBmp(
90 0 : xBitmap, uno::UNO_QUERY_THROW );
91 :
92 0 : ::BitmapEx aBmpEx = ::vcl::unotools::bitmapExFromXBitmap( xIntBmp );
93 0 : if( !!aBmpEx )
94 0 : return aBmpEx;
95 :
96 : // TODO(F1): extract pixel from XBitmap interface
97 0 : ENSURE_OR_THROW( false,
98 0 : "bitmapExFromXBitmap(): could not extract bitmap" );
99 : }
100 :
101 : return ::BitmapEx();
102 : }
103 :
104 0 : bool setupFontTransform( ::Point& o_rPoint,
105 : ::Font& io_rVCLFont,
106 : const rendering::ViewState& rViewState,
107 : const rendering::RenderState& rRenderState,
108 : ::OutputDevice& rOutDev )
109 : {
110 0 : ::basegfx::B2DHomMatrix aMatrix;
111 :
112 : ::canvas::tools::mergeViewAndRenderTransform(aMatrix,
113 : rViewState,
114 0 : rRenderState);
115 :
116 0 : ::basegfx::B2DTuple aScale;
117 0 : ::basegfx::B2DTuple aTranslate;
118 : double nRotate, nShearX;
119 :
120 0 : aMatrix.decompose( aScale, aTranslate, nRotate, nShearX );
121 :
122 : // query font metric _before_ tampering with width and height
123 0 : if( !::rtl::math::approxEqual(aScale.getX(), aScale.getY()) )
124 : {
125 : // retrieve true font width
126 0 : const sal_Int32 nFontWidth( rOutDev.GetFontMetric( io_rVCLFont ).GetWidth() );
127 :
128 0 : const sal_Int32 nScaledFontWidth( ::basegfx::fround(nFontWidth * aScale.getX()) );
129 :
130 0 : if( !nScaledFontWidth )
131 : {
132 : // scale is smaller than one pixel - disable text
133 : // output altogether
134 0 : return false;
135 : }
136 :
137 0 : io_rVCLFont.SetWidth( nScaledFontWidth );
138 : }
139 :
140 0 : if( !::rtl::math::approxEqual(aScale.getY(), 1.0) )
141 : {
142 0 : const sal_Int32 nFontHeight( io_rVCLFont.GetHeight() );
143 0 : io_rVCLFont.SetHeight( ::basegfx::fround(nFontHeight * aScale.getY()) );
144 : }
145 :
146 0 : io_rVCLFont.SetOrientation( static_cast< short >( ::basegfx::fround(-fmod(nRotate, 2*M_PI)*(1800.0/M_PI)) ) );
147 :
148 : // TODO(F2): Missing functionality in VCL: shearing
149 0 : o_rPoint.X() = ::basegfx::fround(aTranslate.getX());
150 0 : o_rPoint.Y() = ::basegfx::fround(aTranslate.getY());
151 :
152 0 : return true;
153 : }
154 :
155 0 : bool isRectangle( const PolyPolygon& rPolyPoly )
156 : {
157 : // exclude some cheap cases first
158 0 : if( rPolyPoly.Count() != 1 )
159 0 : return false;
160 :
161 0 : const ::Polygon& rPoly( rPolyPoly[0] );
162 :
163 0 : sal_uInt16 nCount( rPoly.GetSize() );
164 0 : if( nCount < 4 )
165 0 : return false;
166 :
167 : // delegate to basegfx
168 0 : return ::basegfx::tools::isRectangle( rPoly.getB2DPolygon() );
169 : }
170 :
171 :
172 : // VCL-Canvas related
173 : //---------------------------------------------------------------------
174 :
175 0 : ::Point mapRealPoint2D( const geometry::RealPoint2D& rPoint,
176 : const rendering::ViewState& rViewState,
177 : const rendering::RenderState& rRenderState )
178 : {
179 0 : ::basegfx::B2DPoint aPoint( ::basegfx::unotools::b2DPointFromRealPoint2D(rPoint) );
180 :
181 0 : ::basegfx::B2DHomMatrix aMatrix;
182 : aPoint *= ::canvas::tools::mergeViewAndRenderTransform(aMatrix,
183 : rViewState,
184 0 : rRenderState);
185 :
186 0 : return ::vcl::unotools::pointFromB2DPoint( aPoint );
187 : }
188 :
189 0 : ::PolyPolygon mapPolyPolygon( const ::basegfx::B2DPolyPolygon& rPoly,
190 : const rendering::ViewState& rViewState,
191 : const rendering::RenderState& rRenderState )
192 : {
193 0 : ::basegfx::B2DHomMatrix aMatrix;
194 : ::canvas::tools::mergeViewAndRenderTransform(aMatrix,
195 : rViewState,
196 0 : rRenderState);
197 :
198 0 : ::basegfx::B2DPolyPolygon aTemp( rPoly );
199 :
200 0 : aTemp.transform( aMatrix );
201 :
202 0 : return ::PolyPolygon( aTemp );
203 : }
204 :
205 0 : ::BitmapEx transformBitmap( const BitmapEx& rBitmap,
206 : const ::basegfx::B2DHomMatrix& rTransform,
207 : const uno::Sequence< double >& rDeviceColor,
208 : ModulationMode eModulationMode )
209 : {
210 : RTL_LOGFILE_CONTEXT( aLog, "::vclcanvas::tools::transformBitmap()" );
211 : RTL_LOGFILE_CONTEXT_TRACE1( aLog, "::vclcanvas::tools::transformBitmap: 0x%X", &rBitmap );
212 :
213 : // calc transformation and size of bitmap to be
214 : // generated. Note, that the translational components are
215 : // deleted from the transformation; this can be handled by
216 : // an offset when painting the bitmap
217 0 : const Size aBmpSize( rBitmap.GetSizePixel() );
218 0 : ::basegfx::B2DRectangle aDestRect;
219 :
220 0 : bool bCopyBack( false );
221 :
222 : // calc effective transformation for bitmap
223 : const ::basegfx::B2DRectangle aSrcRect( 0, 0,
224 0 : aBmpSize.Width(),
225 0 : aBmpSize.Height() );
226 : ::canvas::tools::calcTransformedRectBounds( aDestRect,
227 : aSrcRect,
228 0 : rTransform );
229 :
230 : // re-center bitmap, such that it's left, top border is
231 : // aligned with (0,0). The method takes the given
232 : // rectangle, and calculates a transformation that maps
233 : // this rectangle unscaled to the origin.
234 0 : ::basegfx::B2DHomMatrix aLocalTransform;
235 : ::canvas::tools::calcRectToOriginTransform( aLocalTransform,
236 : aSrcRect,
237 0 : rTransform );
238 :
239 : const bool bModulateColors( eModulationMode == MODULATE_WITH_DEVICECOLOR &&
240 0 : rDeviceColor.getLength() > 2 );
241 0 : const double nRedModulation( bModulateColors ? rDeviceColor[0] : 1.0 );
242 0 : const double nGreenModulation( bModulateColors ? rDeviceColor[1] : 1.0 );
243 0 : const double nBlueModulation( bModulateColors ? rDeviceColor[2] : 1.0 );
244 0 : const double nAlphaModulation( bModulateColors && rDeviceColor.getLength() > 3 ?
245 0 : rDeviceColor[3] : 1.0 );
246 :
247 0 : Bitmap aSrcBitmap( rBitmap.GetBitmap() );
248 0 : Bitmap aSrcAlpha;
249 :
250 : // differentiate mask and alpha channel (on-off
251 : // vs. multi-level transparency)
252 0 : if( rBitmap.IsTransparent() )
253 : {
254 0 : if( rBitmap.IsAlpha() )
255 0 : aSrcAlpha = rBitmap.GetAlpha().GetBitmap();
256 : else
257 0 : aSrcAlpha = rBitmap.GetMask();
258 : }
259 :
260 0 : Bitmap::ScopedReadAccess pReadAccess( aSrcBitmap );
261 0 : Bitmap::ScopedReadAccess pAlphaReadAccess( rBitmap.IsTransparent() ?
262 : aSrcAlpha.AcquireReadAccess() :
263 : (BitmapReadAccess*)NULL,
264 0 : aSrcAlpha );
265 :
266 0 : if( pReadAccess.get() == NULL ||
267 0 : (pAlphaReadAccess.get() == NULL && rBitmap.IsTransparent()) )
268 : {
269 : // TODO(E2): Error handling!
270 0 : ENSURE_OR_THROW( false,
271 : "transformBitmap(): could not access source bitmap" );
272 : }
273 :
274 : // mapping table, to translate pAlphaReadAccess' pixel
275 : // values into destination alpha values (needed e.g. for
276 : // paletted 1-bit masks).
277 : sal_uInt8 aAlphaMap[256];
278 :
279 0 : if( rBitmap.IsTransparent() )
280 : {
281 0 : if( rBitmap.IsAlpha() )
282 : {
283 : // source already has alpha channel - 1:1 mapping,
284 : // i.e. aAlphaMap[0]=0,...,aAlphaMap[255]=255.
285 0 : ::o3tl::iota( aAlphaMap, &aAlphaMap[256], 0 );
286 : }
287 : else
288 : {
289 : // mask transparency - determine used palette colors
290 0 : const BitmapColor& rCol0( pAlphaReadAccess->GetPaletteColor( 0 ) );
291 0 : const BitmapColor& rCol1( pAlphaReadAccess->GetPaletteColor( 1 ) );
292 :
293 : // shortcut for true luminance calculation
294 : // (assumes that palette is grey-level)
295 0 : aAlphaMap[0] = rCol0.GetRed();
296 0 : aAlphaMap[1] = rCol1.GetRed();
297 : }
298 : }
299 : // else: mapping table is not used
300 :
301 : const Size aDestBmpSize( ::basegfx::fround( aDestRect.getWidth() ),
302 0 : ::basegfx::fround( aDestRect.getHeight() ) );
303 :
304 0 : if( aDestBmpSize.Width() == 0 || aDestBmpSize.Height() == 0 )
305 0 : return BitmapEx();
306 :
307 0 : Bitmap aDstBitmap( aDestBmpSize, aSrcBitmap.GetBitCount(), &pReadAccess->GetPalette() );
308 0 : Bitmap aDstAlpha( AlphaMask( aDestBmpSize ).GetBitmap() );
309 :
310 : {
311 : // just to be on the safe side: let the
312 : // ScopedAccessors get destructed before
313 : // copy-constructing the resulting bitmap. This will
314 : // rule out the possibility that cached accessor data
315 : // is not yet written back.
316 0 : Bitmap::ScopedWriteAccess pWriteAccess( aDstBitmap );
317 0 : Bitmap::ScopedWriteAccess pAlphaWriteAccess( aDstAlpha );
318 :
319 :
320 0 : if( pWriteAccess.get() != NULL &&
321 0 : pAlphaWriteAccess.get() != NULL &&
322 0 : rTransform.isInvertible() )
323 : {
324 : // we're doing inverse mapping here, i.e. mapping
325 : // points from the destination bitmap back to the
326 : // source
327 0 : ::basegfx::B2DHomMatrix aTransform( aLocalTransform );
328 0 : aTransform.invert();
329 :
330 : // for the time being, always read as ARGB
331 0 : for( int y=0; y<aDestBmpSize.Height(); ++y )
332 : {
333 0 : if( bModulateColors )
334 : {
335 : // TODO(P2): Have different branches for
336 : // alpha-only modulation (color
337 : // modulations eq. 1.0)
338 :
339 : // modulate all color channels with given
340 : // values
341 :
342 : // differentiate mask and alpha channel (on-off
343 : // vs. multi-level transparency)
344 0 : if( rBitmap.IsTransparent() )
345 : {
346 : // Handling alpha and mask just the same...
347 0 : for( int x=0; x<aDestBmpSize.Width(); ++x )
348 : {
349 0 : ::basegfx::B2DPoint aPoint(x,y);
350 0 : aPoint *= aTransform;
351 :
352 0 : const int nSrcX( ::basegfx::fround( aPoint.getX() ) );
353 0 : const int nSrcY( ::basegfx::fround( aPoint.getY() ) );
354 0 : if( nSrcX < 0 || nSrcX >= aBmpSize.Width() ||
355 0 : nSrcY < 0 || nSrcY >= aBmpSize.Height() )
356 : {
357 0 : pAlphaWriteAccess->SetPixel( y, x, BitmapColor(255) );
358 : }
359 : else
360 : {
361 : // modulate alpha with
362 : // nAlphaModulation. This is a
363 : // little bit verbose, formula
364 : // is 255 - (255-pixAlpha)*nAlphaModulation
365 : // (invert 'alpha' pixel value,
366 : // to get the standard alpha
367 : // channel behaviour)
368 : pAlphaWriteAccess->SetPixel( y, x,
369 : BitmapColor(
370 : 255U -
371 : static_cast<sal_uInt8>(
372 : nAlphaModulation*
373 : (255U
374 : - aAlphaMap[ pAlphaReadAccess->GetPixel(
375 : nSrcY,
376 0 : nSrcX ).GetIndex() ] ) + .5 ) ) );
377 :
378 : BitmapColor aColor( pReadAccess->GetPixel( nSrcY,
379 0 : nSrcX ) );
380 :
381 : aColor.SetRed(
382 : static_cast<sal_uInt8>(
383 : nRedModulation *
384 0 : aColor.GetRed() + .5 ));
385 : aColor.SetGreen(
386 : static_cast<sal_uInt8>(
387 : nGreenModulation *
388 0 : aColor.GetGreen() + .5 ));
389 : aColor.SetBlue(
390 : static_cast<sal_uInt8>(
391 : nBlueModulation *
392 0 : aColor.GetBlue() + .5 ));
393 :
394 : pWriteAccess->SetPixel( y, x,
395 0 : aColor );
396 : }
397 0 : }
398 : }
399 : else
400 : {
401 0 : for( int x=0; x<aDestBmpSize.Width(); ++x )
402 : {
403 0 : ::basegfx::B2DPoint aPoint(x,y);
404 0 : aPoint *= aTransform;
405 :
406 0 : const int nSrcX( ::basegfx::fround( aPoint.getX() ) );
407 0 : const int nSrcY( ::basegfx::fround( aPoint.getY() ) );
408 0 : if( nSrcX < 0 || nSrcX >= aBmpSize.Width() ||
409 0 : nSrcY < 0 || nSrcY >= aBmpSize.Height() )
410 : {
411 0 : pAlphaWriteAccess->SetPixel( y, x, BitmapColor(255) );
412 : }
413 : else
414 : {
415 : // modulate alpha with
416 : // nAlphaModulation. This is a
417 : // little bit verbose, formula
418 : // is 255 - 255*nAlphaModulation
419 : // (invert 'alpha' pixel value,
420 : // to get the standard alpha
421 : // channel behaviour)
422 : pAlphaWriteAccess->SetPixel( y, x,
423 : BitmapColor(
424 : 255U -
425 : static_cast<sal_uInt8>(
426 : nAlphaModulation*255.0
427 0 : + .5 ) ) );
428 :
429 : BitmapColor aColor( pReadAccess->GetPixel( nSrcY,
430 0 : nSrcX ) );
431 :
432 : aColor.SetRed(
433 : static_cast<sal_uInt8>(
434 : nRedModulation *
435 0 : aColor.GetRed() + .5 ));
436 : aColor.SetGreen(
437 : static_cast<sal_uInt8>(
438 : nGreenModulation *
439 0 : aColor.GetGreen() + .5 ));
440 : aColor.SetBlue(
441 : static_cast<sal_uInt8>(
442 : nBlueModulation *
443 0 : aColor.GetBlue() + .5 ));
444 :
445 : pWriteAccess->SetPixel( y, x,
446 0 : aColor );
447 : }
448 0 : }
449 : }
450 : }
451 : else
452 : {
453 : // differentiate mask and alpha channel (on-off
454 : // vs. multi-level transparency)
455 0 : if( rBitmap.IsTransparent() )
456 : {
457 : // Handling alpha and mask just the same...
458 0 : for( int x=0; x<aDestBmpSize.Width(); ++x )
459 : {
460 0 : ::basegfx::B2DPoint aPoint(x,y);
461 0 : aPoint *= aTransform;
462 :
463 0 : const int nSrcX( ::basegfx::fround( aPoint.getX() ) );
464 0 : const int nSrcY( ::basegfx::fround( aPoint.getY() ) );
465 0 : if( nSrcX < 0 || nSrcX >= aBmpSize.Width() ||
466 0 : nSrcY < 0 || nSrcY >= aBmpSize.Height() )
467 : {
468 0 : pAlphaWriteAccess->SetPixel( y, x, BitmapColor(255) );
469 : }
470 : else
471 : {
472 : pAlphaWriteAccess->SetPixel( y, x,
473 : aAlphaMap[
474 : pAlphaReadAccess->GetPixel( nSrcY,
475 0 : nSrcX ) ] );
476 :
477 : pWriteAccess->SetPixel( y, x, pReadAccess->GetPixel( nSrcY,
478 0 : nSrcX ) );
479 : }
480 0 : }
481 : }
482 : else
483 : {
484 0 : for( int x=0; x<aDestBmpSize.Width(); ++x )
485 : {
486 0 : ::basegfx::B2DPoint aPoint(x,y);
487 0 : aPoint *= aTransform;
488 :
489 0 : const int nSrcX( ::basegfx::fround( aPoint.getX() ) );
490 0 : const int nSrcY( ::basegfx::fround( aPoint.getY() ) );
491 0 : if( nSrcX < 0 || nSrcX >= aBmpSize.Width() ||
492 0 : nSrcY < 0 || nSrcY >= aBmpSize.Height() )
493 : {
494 0 : pAlphaWriteAccess->SetPixel( y, x, BitmapColor(255) );
495 : }
496 : else
497 : {
498 0 : pAlphaWriteAccess->SetPixel( y, x, BitmapColor(0) );
499 : pWriteAccess->SetPixel( y, x, pReadAccess->GetPixel( nSrcY,
500 0 : nSrcX ) );
501 : }
502 0 : }
503 : }
504 : }
505 : }
506 :
507 0 : bCopyBack = true;
508 : }
509 : else
510 : {
511 : // TODO(E2): Error handling!
512 0 : ENSURE_OR_THROW( false,
513 : "transformBitmap(): could not access bitmap" );
514 0 : }
515 : }
516 :
517 0 : if( bCopyBack )
518 0 : return BitmapEx( aDstBitmap, AlphaMask( aDstAlpha ) );
519 : else
520 0 : return BitmapEx();
521 : }
522 : }
523 0 : }
524 :
525 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|