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/.
10 #include "ogl_canvashelper.hxx"
13 #include <canvas/debug.hxx>
14 #include <tools/diagnose_ex.h>
15 #include <basegfx/tools/canvastools.hxx>
16 #include <basegfx/polygon/b2dpolypolygon.hxx>
17 #include <basegfx/polygon/b2dpolygontriangulator.hxx>
18 #include <basegfx/polygon/b2dpolypolygontools.hxx>
20 #include <com/sun/star/rendering/TexturingMode.hpp>
21 #include <com/sun/star/rendering/CompositeOperation.hpp>
22 #include <com/sun/star/rendering/RepaintResult.hpp>
23 #include <com/sun/star/rendering/PathCapType.hpp>
24 #include <com/sun/star/rendering/PathJoinType.hpp>
26 #include <vcl/virdev.hxx>
27 #include <vcl/metric.hxx>
28 #include <vcl/font.hxx>
30 #include "ogl_canvasfont.hxx"
31 #include "ogl_canvastools.hxx"
32 #include "ogl_canvasbitmap.hxx"
33 #include "ogl_spritecanvas.hxx"
34 #include "ogl_texturecache.hxx"
35 #include "ogl_tools.hxx"
39 #include <boost/scoped_array.hpp>
42 using namespace ::com::sun::star
;
49 This OpenGL canvas implementation tries to keep all render
50 output as high-level as possible, i.e. geometry data and
51 externally-provided bitmaps. Therefore, calls at the
52 XCanvas-interfaces are not immediately transformed into colored
53 pixel inside some GL buffer, but are retained simply with their
54 call parameters. Only after XSpriteCanvas::updateScreen() has
55 been called, this all gets transferred to the OpenGL subsystem
56 and converted to a visible scene. The big advantage is, this
57 makes sprite modifications practically zero-overhead, and saves
58 a lot on texture memory (compared to the directx canvas, which
59 immediately dumps every render call into a texture).
61 The drawback, of course, is that complex images churn a lot of
62 GPU cycles on every re-rendering.
64 For the while, I'll be using immediate mode, i.e. transfer data
65 over and over again to the OpenGL subsystem. Alternatively,
66 there are display lists, which at least keep the data on the
67 server, or even better, vertex buffers, which copy geometry
70 Next todo: put polygon geometry into vertex buffer (LRU cache
71 necessary?) - or, rather, buffer objects! prune entries older
72 than one updateScreen() call)
74 Text: http://www.opengl.org/resources/features/fontsurvey/
77 struct CanvasHelper::Action
79 ::basegfx::B2DHomMatrix maTransform
;
80 GLenum meSrcBlendMode
;
81 GLenum meDstBlendMode
;
82 rendering::ARGBColor maARGBColor
;
83 ::basegfx::B2DPolyPolygonVector maPolyPolys
;
85 ::boost::function6
< bool,
87 const ::basegfx::B2DHomMatrix
&,
90 const rendering::ARGBColor
&,
91 const ::basegfx::B2DPolyPolygonVector
& > maFunction
;
96 bool lcl_drawPoint( const CanvasHelper
& /*rHelper*/,
97 const ::basegfx::B2DHomMatrix
& rTransform
,
100 const rendering::ARGBColor
& rColor
,
101 const geometry::RealPoint2D
& rPoint
)
103 TransformationPreserver aPreserver
;
104 setupState(rTransform
, eSrcBlend
, eDstBlend
, rColor
);
107 glVertex2d(rPoint
.X
, rPoint
.Y
);
113 bool lcl_drawLine( const CanvasHelper
& /*rHelper*/,
114 const ::basegfx::B2DHomMatrix
& rTransform
,
117 const rendering::ARGBColor
& rColor
,
118 const geometry::RealPoint2D
& rStartPoint
,
119 const geometry::RealPoint2D
& rEndPoint
)
121 TransformationPreserver aPreserver
;
122 setupState(rTransform
, eSrcBlend
, eDstBlend
, rColor
);
125 glVertex2d(rStartPoint
.X
, rStartPoint
.Y
);
126 glVertex2d(rEndPoint
.X
, rEndPoint
.Y
);
132 bool lcl_drawPolyPolygon( const CanvasHelper
& /*rHelper*/,
133 const ::basegfx::B2DHomMatrix
& rTransform
,
136 const rendering::ARGBColor
& rColor
,
137 const ::basegfx::B2DPolyPolygonVector
& rPolyPolygons
)
139 TransformationPreserver aPreserver
;
140 setupState(rTransform
, eSrcBlend
, eDstBlend
, rColor
);
142 ::basegfx::B2DPolyPolygonVector::const_iterator aCurr
=rPolyPolygons
.begin();
143 const ::basegfx::B2DPolyPolygonVector::const_iterator aEnd
=rPolyPolygons
.end();
144 while( aCurr
!= aEnd
)
145 renderPolyPolygon(*aCurr
++);
150 bool lcl_fillPolyPolygon( const CanvasHelper
& /*rHelper*/,
151 const ::basegfx::B2DHomMatrix
& rTransform
,
154 const rendering::ARGBColor
& rColor
,
155 const ::basegfx::B2DPolyPolygonVector
& rPolyPolygons
)
157 TransformationPreserver aPreserver
;
158 setupState(rTransform
, eSrcBlend
, eDstBlend
, rColor
);
160 ::basegfx::B2DPolyPolygonVector::const_iterator aCurr
=rPolyPolygons
.begin();
161 const ::basegfx::B2DPolyPolygonVector::const_iterator aEnd
=rPolyPolygons
.end();
162 while( aCurr
!= aEnd
)
164 glBegin(GL_TRIANGLES
);
165 renderComplexPolyPolygon(*aCurr
++);
172 bool lcl_fillGradientPolyPolygon( const CanvasHelper
& rHelper
,
173 const ::basegfx::B2DHomMatrix
& rTransform
,
176 const ::canvas::ParametricPolyPolygon::Values
& rValues
,
177 const rendering::Texture
& rTexture
,
178 const ::basegfx::B2DPolyPolygonVector
& rPolyPolygons
)
180 TransformationPreserver aPreserver
;
181 setupState(rTransform
, eSrcBlend
, eDstBlend
, rendering::ARGBColor());
183 // convert to weird canvas textur coordinate system (not
184 // [0,1]^2, but path coordinate system)
185 ::basegfx::B2DHomMatrix aTextureTransform
;
186 ::basegfx::unotools::homMatrixFromAffineMatrix( aTextureTransform
,
187 rTexture
.AffineTransform
);
188 ::basegfx::B2DRange aBounds
;
189 ::basegfx::B2DPolyPolygonVector::const_iterator aCurr
=rPolyPolygons
.begin();
190 const ::basegfx::B2DPolyPolygonVector::const_iterator aEnd
=rPolyPolygons
.end();
191 while( aCurr
!= aEnd
)
192 aBounds
.expand(::basegfx::tools::getRange(*aCurr
++));
193 aTextureTransform
.translate(-aBounds
.getMinX(), -aBounds
.getMinY());
194 aTextureTransform
.scale(1/aBounds
.getWidth(), 1/aBounds
.getHeight());
196 const sal_Int32 nNumCols
=rValues
.maColors
.getLength();
197 uno::Sequence
< rendering::ARGBColor
> aColors(nNumCols
);
198 rendering::ARGBColor
* const pColors
=aColors
.getArray();
199 rendering::ARGBColor
* pCurrCol
=pColors
;
200 for( sal_Int32 i
=0; i
<nNumCols
; ++i
)
201 *pCurrCol
++ = rHelper
.getDevice()->getDeviceColorSpace()->convertToARGB(rValues
.maColors
[i
])[0];
203 OSL_ASSERT(nNumCols
== rValues
.maStops
.getLength());
205 switch( rValues
.meType
)
207 case ::canvas::ParametricPolyPolygon::GRADIENT_LINEAR
:
208 rHelper
.getDeviceHelper()->useLinearGradientShader(pColors
,
213 case ::canvas::ParametricPolyPolygon::GRADIENT_ELLIPTICAL
:
214 rHelper
.getDeviceHelper()->useRadialGradientShader(pColors
,
219 case ::canvas::ParametricPolyPolygon::GRADIENT_RECTANGULAR
:
220 rHelper
.getDeviceHelper()->useRectangularGradientShader(pColors
,
226 ENSURE_OR_THROW( false,
227 "CanvasHelper lcl_fillGradientPolyPolygon(): Unexpected case" );
231 aCurr
=rPolyPolygons
.begin();
232 while( aCurr
!= aEnd
)
234 glBegin(GL_TRIANGLES
);
235 renderComplexPolyPolygon(*aCurr
++);
241 glMatrixMode(GL_MODELVIEW
);
246 bool lcl_drawOwnBitmap( const CanvasHelper
& /*rHelper*/,
247 const ::basegfx::B2DHomMatrix
& rTransform
,
250 const rendering::ARGBColor
& rColor
,
251 const CanvasBitmap
& rBitmap
)
253 TransformationPreserver aPreserver
;
254 setupState(rTransform
, eSrcBlend
, eDstBlend
, rColor
);
256 return rBitmap
.renderRecordedActions();
259 bool lcl_drawGenericBitmap( const CanvasHelper
& rHelper
,
260 const ::basegfx::B2DHomMatrix
& rTransform
,
263 const rendering::ARGBColor
& rColor
,
264 const geometry::IntegerSize2D
& rPixelSize
,
265 const uno::Sequence
<sal_Int8
>& rPixelData
,
266 sal_uInt32 nPixelCrc32
)
268 TransformationPreserver aPreserver
;
269 setupState(rTransform
, eSrcBlend
, eDstBlend
, rColor
);
271 const unsigned int nTexId
=rHelper
.getDeviceHelper()->getTextureCache().getTexture(
272 rPixelSize
, rPixelData
.getConstArray(), nPixelCrc32
);
274 glBindTexture(GL_TEXTURE_2D
, nTexId
);
275 glEnable(GL_TEXTURE_2D
);
276 glTexParameteri(GL_TEXTURE_2D
,
277 GL_TEXTURE_MIN_FILTER
,
279 glTexParameteri(GL_TEXTURE_2D
,
280 GL_TEXTURE_MAG_FILTER
,
283 glBlendFunc(GL_SRC_ALPHA
,
284 GL_ONE_MINUS_SRC_ALPHA
);
286 // blend against fixed vertex color; texture alpha is multiplied in
289 glBegin(GL_TRIANGLE_STRIP
);
290 glTexCoord2f(0,0); glVertex2d(0,0);
291 glTexCoord2f(0,1); glVertex2d(0, rPixelSize
.Height
);
292 glTexCoord2f(1,0); glVertex2d(rPixelSize
.Width
,0);
293 glTexCoord2f(1,1); glVertex2d(rPixelSize
.Width
,rPixelSize
.Height
);
296 glBindTexture(GL_TEXTURE_2D
, 0);
297 glDisable(GL_TEXTURE_2D
);
302 bool lcl_fillTexturedPolyPolygon( const CanvasHelper
& rHelper
,
303 const ::basegfx::B2DHomMatrix
& rTransform
,
306 const rendering::Texture
& rTexture
,
307 const geometry::IntegerSize2D
& rPixelSize
,
308 const uno::Sequence
<sal_Int8
>& rPixelData
,
309 sal_uInt32 nPixelCrc32
,
310 const ::basegfx::B2DPolyPolygonVector
& rPolyPolygons
)
312 TransformationPreserver aPreserver
;
313 setupState(rTransform
, eSrcBlend
, eDstBlend
, rendering::ARGBColor());
315 const unsigned int nTexId
=rHelper
.getDeviceHelper()->getTextureCache().getTexture(
316 rPixelSize
, rPixelData
.getConstArray(), nPixelCrc32
);
318 glBindTexture(GL_TEXTURE_2D
, nTexId
);
319 glEnable(GL_TEXTURE_2D
);
320 glTexParameteri(GL_TEXTURE_2D
,
321 GL_TEXTURE_MIN_FILTER
,
323 glTexParameteri(GL_TEXTURE_2D
,
324 GL_TEXTURE_MAG_FILTER
,
327 glBlendFunc(GL_SRC_ALPHA
,
328 GL_ONE_MINUS_SRC_ALPHA
);
330 // convert to weird canvas textur coordinate system (not
331 // [0,1]^2, but path coordinate system)
332 ::basegfx::B2DHomMatrix aTextureTransform
;
333 ::basegfx::unotools::homMatrixFromAffineMatrix( aTextureTransform
,
334 rTexture
.AffineTransform
);
335 ::basegfx::B2DRange aBounds
;
336 ::basegfx::B2DPolyPolygonVector::const_iterator aCurr
=rPolyPolygons
.begin();
337 const ::basegfx::B2DPolyPolygonVector::const_iterator aEnd
=rPolyPolygons
.end();
338 while( aCurr
!= aEnd
)
339 aBounds
.expand(::basegfx::tools::getRange(*aCurr
++));
340 aTextureTransform
.translate(-aBounds
.getMinX(), -aBounds
.getMinY());
341 aTextureTransform
.scale(1/aBounds
.getWidth(), 1/aBounds
.getHeight());
342 aTextureTransform
.invert();
344 glMatrixMode(GL_TEXTURE
);
345 double aTexTransform
[] =
347 aTextureTransform
.get(0,0), aTextureTransform
.get(1,0), 0, 0,
348 aTextureTransform
.get(0,1), aTextureTransform
.get(1,1), 0, 0,
350 aTextureTransform
.get(0,2), aTextureTransform
.get(1,2), 0, 1
352 glLoadMatrixd(aTexTransform
);
354 // blend against fixed vertex color; texture alpha is multiplied in
355 glColor4f(1,1,1,rTexture
.Alpha
);
357 aCurr
=rPolyPolygons
.begin();
358 while( aCurr
!= aEnd
)
360 glBegin(GL_TRIANGLES
);
361 renderComplexPolyPolygon(*aCurr
++);
366 glMatrixMode(GL_MODELVIEW
);
368 glBindTexture(GL_TEXTURE_2D
, 0);
369 glDisable(GL_TEXTURE_2D
);
375 CanvasHelper::CanvasHelper() :
377 mpDeviceHelper( NULL
),
381 CanvasHelper::~CanvasHelper()
384 CanvasHelper
& CanvasHelper::operator=( const CanvasHelper
& rSrc
)
386 mpDevice
= rSrc
.mpDevice
;
387 mpDeviceHelper
= rSrc
.mpDeviceHelper
;
388 mpRecordedActions
= rSrc
.mpRecordedActions
;
392 void CanvasHelper::disposing()
394 RecordVectorT aThrowaway
;
395 mpRecordedActions
.swap( aThrowaway
);
397 mpDeviceHelper
= NULL
;
400 void CanvasHelper::init( rendering::XGraphicDevice
& rDevice
,
401 SpriteDeviceHelper
& rDeviceHelper
)
404 mpDeviceHelper
= &rDeviceHelper
;
407 void CanvasHelper::clear()
409 mpRecordedActions
->clear();
412 void CanvasHelper::drawPoint( const rendering::XCanvas
* /*pCanvas*/,
413 const geometry::RealPoint2D
& aPoint
,
414 const rendering::ViewState
& viewState
,
415 const rendering::RenderState
& renderState
)
419 mpRecordedActions
->push_back( Action() );
420 Action
& rAct
=mpRecordedActions
->back();
422 setupGraphicsState( rAct
, viewState
, renderState
);
423 rAct
.maFunction
= ::boost::bind(&lcl_drawPoint
,
429 void CanvasHelper::drawLine( const rendering::XCanvas
* /*pCanvas*/,
430 const geometry::RealPoint2D
& aStartPoint
,
431 const geometry::RealPoint2D
& aEndPoint
,
432 const rendering::ViewState
& viewState
,
433 const rendering::RenderState
& renderState
)
437 mpRecordedActions
->push_back( Action() );
438 Action
& rAct
=mpRecordedActions
->back();
440 setupGraphicsState( rAct
, viewState
, renderState
);
441 rAct
.maFunction
= ::boost::bind(&lcl_drawLine
,
443 aStartPoint
,aEndPoint
);
447 void CanvasHelper::drawBezier( const rendering::XCanvas
* /*pCanvas*/,
448 const geometry::RealBezierSegment2D
& aBezierSegment
,
449 const geometry::RealPoint2D
& aEndPoint
,
450 const rendering::ViewState
& viewState
,
451 const rendering::RenderState
& renderState
)
455 mpRecordedActions
->push_back( Action() );
456 Action
& rAct
=mpRecordedActions
->back();
458 setupGraphicsState( rAct
, viewState
, renderState
);
460 // TODO(F2): subdivide&render whole curve
461 rAct
.maFunction
= ::boost::bind(&lcl_drawLine
,
463 geometry::RealPoint2D(
470 uno::Reference
< rendering::XCachedPrimitive
> CanvasHelper::drawPolyPolygon( const rendering::XCanvas
* /*pCanvas*/,
471 const uno::Reference
< rendering::XPolyPolygon2D
>& xPolyPolygon
,
472 const rendering::ViewState
& viewState
,
473 const rendering::RenderState
& renderState
)
475 ENSURE_OR_THROW( xPolyPolygon
.is(),
476 "CanvasHelper::drawPolyPolygon: polygon is NULL");
480 mpRecordedActions
->push_back( Action() );
481 Action
& rAct
=mpRecordedActions
->back();
483 setupGraphicsState( rAct
, viewState
, renderState
);
484 rAct
.maPolyPolys
.push_back(
485 ::basegfx::unotools::b2DPolyPolygonFromXPolyPolygon2D(xPolyPolygon
));
486 rAct
.maPolyPolys
.back().makeUnique(); // own copy, for thread safety
488 rAct
.maFunction
= &lcl_drawPolyPolygon
;
491 // TODO(P1): Provide caching here.
492 return uno::Reference
< rendering::XCachedPrimitive
>(NULL
);
495 uno::Reference
< rendering::XCachedPrimitive
> CanvasHelper::strokePolyPolygon( const rendering::XCanvas
* /*pCanvas*/,
496 const uno::Reference
< rendering::XPolyPolygon2D
>& xPolyPolygon
,
497 const rendering::ViewState
& viewState
,
498 const rendering::RenderState
& renderState
,
499 const rendering::StrokeAttributes
& /*strokeAttributes*/ )
501 ENSURE_OR_THROW( xPolyPolygon
.is(),
502 "CanvasHelper::strokePolyPolygon: polygon is NULL");
506 mpRecordedActions
->push_back( Action() );
507 Action
& rAct
=mpRecordedActions
->back();
509 setupGraphicsState( rAct
, viewState
, renderState
);
510 rAct
.maPolyPolys
.push_back(
511 ::basegfx::unotools::b2DPolyPolygonFromXPolyPolygon2D(xPolyPolygon
));
512 rAct
.maPolyPolys
.back().makeUnique(); // own copy, for thread safety
514 // TODO(F3): fallback to drawPolyPolygon currently
515 rAct
.maFunction
= &lcl_drawPolyPolygon
;
518 // TODO(P1): Provide caching here.
519 return uno::Reference
< rendering::XCachedPrimitive
>(NULL
);
522 uno::Reference
< rendering::XCachedPrimitive
> CanvasHelper::strokeTexturedPolyPolygon( const rendering::XCanvas
* /*pCanvas*/,
523 const uno::Reference
< rendering::XPolyPolygon2D
>& /*xPolyPolygon*/,
524 const rendering::ViewState
& /*viewState*/,
525 const rendering::RenderState
& /*renderState*/,
526 const uno::Sequence
< rendering::Texture
>& /*textures*/,
527 const rendering::StrokeAttributes
& /*strokeAttributes*/ )
530 return uno::Reference
< rendering::XCachedPrimitive
>(NULL
);
533 uno::Reference
< rendering::XCachedPrimitive
> CanvasHelper::strokeTextureMappedPolyPolygon( const rendering::XCanvas
* /*pCanvas*/,
534 const uno::Reference
< rendering::XPolyPolygon2D
>& /*xPolyPolygon*/,
535 const rendering::ViewState
& /*viewState*/,
536 const rendering::RenderState
& /*renderState*/,
537 const uno::Sequence
< rendering::Texture
>& /*textures*/,
538 const uno::Reference
< geometry::XMapping2D
>& /*xMapping*/,
539 const rendering::StrokeAttributes
& /*strokeAttributes*/ )
542 return uno::Reference
< rendering::XCachedPrimitive
>(NULL
);
545 uno::Reference
< rendering::XPolyPolygon2D
> CanvasHelper::queryStrokeShapes( const rendering::XCanvas
* /*pCanvas*/,
546 const uno::Reference
< rendering::XPolyPolygon2D
>& /*xPolyPolygon*/,
547 const rendering::ViewState
& /*viewState*/,
548 const rendering::RenderState
& /*renderState*/,
549 const rendering::StrokeAttributes
& /*strokeAttributes*/ )
552 return uno::Reference
< rendering::XPolyPolygon2D
>(NULL
);
555 uno::Reference
< rendering::XCachedPrimitive
> CanvasHelper::fillPolyPolygon( const rendering::XCanvas
* /*pCanvas*/,
556 const uno::Reference
< rendering::XPolyPolygon2D
>& xPolyPolygon
,
557 const rendering::ViewState
& viewState
,
558 const rendering::RenderState
& renderState
)
560 ENSURE_OR_THROW( xPolyPolygon
.is(),
561 "CanvasHelper::fillPolyPolygon: polygon is NULL");
565 mpRecordedActions
->push_back( Action() );
566 Action
& rAct
=mpRecordedActions
->back();
568 setupGraphicsState( rAct
, viewState
, renderState
);
569 rAct
.maPolyPolys
.push_back(
570 ::basegfx::unotools::b2DPolyPolygonFromXPolyPolygon2D(xPolyPolygon
));
571 rAct
.maPolyPolys
.back().makeUnique(); // own copy, for thread safety
573 rAct
.maFunction
= &lcl_fillPolyPolygon
;
576 // TODO(P1): Provide caching here.
577 return uno::Reference
< rendering::XCachedPrimitive
>(NULL
);
580 uno::Reference
< rendering::XCachedPrimitive
> CanvasHelper::fillTexturedPolyPolygon( const rendering::XCanvas
* /*pCanvas*/,
581 const uno::Reference
< rendering::XPolyPolygon2D
>& xPolyPolygon
,
582 const rendering::ViewState
& viewState
,
583 const rendering::RenderState
& renderState
,
584 const uno::Sequence
< rendering::Texture
>& textures
)
586 ENSURE_OR_THROW( xPolyPolygon
.is(),
587 "CanvasHelper::fillPolyPolygon: polygon is NULL");
591 mpRecordedActions
->push_back( Action() );
592 Action
& rAct
=mpRecordedActions
->back();
594 setupGraphicsState( rAct
, viewState
, renderState
);
595 rAct
.maPolyPolys
.push_back(
596 ::basegfx::unotools::b2DPolyPolygonFromXPolyPolygon2D(xPolyPolygon
));
597 rAct
.maPolyPolys
.back().makeUnique(); // own copy, for thread safety
599 // TODO(F1): Multi-texturing
600 if( textures
[0].Gradient
.is() )
602 // try to cast XParametricPolyPolygon2D reference to
603 // our implementation class.
604 ::canvas::ParametricPolyPolygon
* pGradient
=
605 dynamic_cast< ::canvas::ParametricPolyPolygon
* >( textures
[0].Gradient
.get() );
609 // copy state from Gradient polypoly locally
610 // (given object might change!)
611 const ::canvas::ParametricPolyPolygon::Values
& rValues(
612 pGradient
->getValues() );
614 rAct
.maFunction
= ::boost::bind(&lcl_fillGradientPolyPolygon
,
622 // TODO(F1): The generic case is missing here
623 ENSURE_OR_THROW( false,
624 "CanvasHelper::fillTexturedPolyPolygon(): unknown parametric polygon encountered" );
627 else if( textures
[0].Bitmap
.is() )
630 CanvasBitmap
* pOwnBitmap
=dynamic_cast<CanvasBitmap
*>(textures
[0].Bitmap
.get());
633 // TODO(F2): own texture bitmap
637 // TODO(P3): Highly inefficient - simply copies pixel data
639 uno::Reference
< rendering::XIntegerReadOnlyBitmap
> xIntegerBitmap(
642 if( xIntegerBitmap
.is() )
644 const geometry::IntegerSize2D aSize
=xIntegerBitmap
->getSize();
645 rendering::IntegerBitmapLayout aLayout
;
646 uno::Sequence
<sal_Int8
> aPixelData
=
647 xIntegerBitmap
->getData(
649 geometry::IntegerRectangle2D(0,0,aSize
.Width
,aSize
.Height
));
651 // force-convert color to ARGB8888 int color space
652 uno::Sequence
<sal_Int8
> aARGBBytes(
653 aLayout
.ColorSpace
->convertToIntegerColorSpace(
655 canvas::tools::getStdColorSpace()));
657 rAct
.maFunction
= ::boost::bind(&lcl_fillTexturedPolyPolygon
,
663 aARGBBytes
.getConstArray(),
664 aARGBBytes
.getLength()),
667 // TODO(F1): handle non-integer case
672 // TODO(P1): Provide caching here.
673 return uno::Reference
< rendering::XCachedPrimitive
>(NULL
);
676 uno::Reference
< rendering::XCachedPrimitive
> CanvasHelper::fillTextureMappedPolyPolygon( const rendering::XCanvas
* /*pCanvas*/,
677 const uno::Reference
< rendering::XPolyPolygon2D
>& /*xPolyPolygon*/,
678 const rendering::ViewState
& /*viewState*/,
679 const rendering::RenderState
& /*renderState*/,
680 const uno::Sequence
< rendering::Texture
>& /*textures*/,
681 const uno::Reference
< geometry::XMapping2D
>& /*xMapping*/ )
684 return uno::Reference
< rendering::XCachedPrimitive
>(NULL
);
687 uno::Reference
< rendering::XCanvasFont
> CanvasHelper::createFont( const rendering::XCanvas
* /*pCanvas*/,
688 const rendering::FontRequest
& fontRequest
,
689 const uno::Sequence
< beans::PropertyValue
>& extraFontProperties
,
690 const geometry::Matrix2D
& fontMatrix
)
693 return uno::Reference
< rendering::XCanvasFont
>(
694 new CanvasFont(fontRequest
, extraFontProperties
, fontMatrix
) );
696 return uno::Reference
< rendering::XCanvasFont
>();
699 uno::Sequence
< rendering::FontInfo
> CanvasHelper::queryAvailableFonts( const rendering::XCanvas
* /*pCanvas*/,
700 const rendering::FontInfo
& /*aFilter*/,
701 const uno::Sequence
< beans::PropertyValue
>& /*aFontProperties*/ )
704 return uno::Sequence
< rendering::FontInfo
>();
707 uno::Reference
< rendering::XCachedPrimitive
> CanvasHelper::drawText( const rendering::XCanvas
* /*pCanvas*/,
708 const rendering::StringContext
& /*text*/,
709 const uno::Reference
< rendering::XCanvasFont
>& /*xFont*/,
710 const rendering::ViewState
& /*viewState*/,
711 const rendering::RenderState
& /*renderState*/,
712 sal_Int8
/*textDirection*/ )
714 // TODO - but not used from slideshow
715 return uno::Reference
< rendering::XCachedPrimitive
>(NULL
);
718 uno::Reference
< rendering::XCachedPrimitive
> CanvasHelper::drawTextLayout( const rendering::XCanvas
* /*pCanvas*/,
719 const uno::Reference
< rendering::XTextLayout
>& xLayoutetText
,
720 const rendering::ViewState
& viewState
,
721 const rendering::RenderState
& renderState
)
723 ENSURE_OR_THROW( xLayoutetText
.is(),
724 "CanvasHelper::drawTextLayout: text is NULL");
728 ScopedVclPtrInstance
< VirtualDevice
> pVDev
;
729 pVDev
->EnableOutput(false);
731 CanvasFont
* pFont
=dynamic_cast<CanvasFont
*>(xLayoutetText
->getFont().get());
732 const rendering::StringContext
& rTxt
=xLayoutetText
->getText();
733 if( pFont
&& rTxt
.Length
)
736 const rendering::FontRequest
& rFontRequest
= pFont
->getFontRequest();
737 const geometry::Matrix2D
& rFontMatrix
= pFont
->getFontMatrix();
739 rFontRequest
.FontDescription
.FamilyName
,
740 rFontRequest
.FontDescription
.StyleName
,
741 Size( 0, ::basegfx::fround(rFontRequest
.CellSize
)));
743 aFont
.SetAlign( ALIGN_BASELINE
);
744 aFont
.SetCharSet( (rFontRequest
.FontDescription
.IsSymbolFont
==util::TriState_YES
) ? RTL_TEXTENCODING_SYMBOL
: RTL_TEXTENCODING_UNICODE
);
745 aFont
.SetVertical( rFontRequest
.FontDescription
.IsVertical
==util::TriState_YES
);
746 aFont
.SetWeight( static_cast<FontWeight
>(rFontRequest
.FontDescription
.FontDescription
.Weight
) );
747 aFont
.SetItalic( (rFontRequest
.FontDescription
.FontDescription
.Letterform
<=8) ? ITALIC_NONE
: ITALIC_NORMAL
);
749 // adjust to stretched font
750 if(!::rtl::math::approxEqual(rFontMatrix
.m00
, rFontMatrix
.m11
))
752 const Size aSize
= pVDev
->GetFontMetric( aFont
).GetSize();
753 const double fDividend( rFontMatrix
.m10
+ rFontMatrix
.m11
);
754 double fStretch
= (rFontMatrix
.m00
+ rFontMatrix
.m01
);
756 if( !::basegfx::fTools::equalZero( fDividend
) )
757 fStretch
/= fDividend
;
759 const sal_Int32 nNewWidth
= ::basegfx::fround( aSize
.Width() * fStretch
);
761 aFont
.SetWidth( nNewWidth
);
765 pVDev
->SetFont(aFont
);
767 mpRecordedActions
->push_back( Action() );
768 Action
& rAct
=mpRecordedActions
->back();
770 setupGraphicsState( rAct
, viewState
, renderState
);
772 // handle custom spacing, if there
773 uno::Sequence
<double> aLogicalAdvancements
=xLayoutetText
->queryLogicalAdvancements();
774 if( aLogicalAdvancements
.getLength() )
776 // create the DXArray
777 const sal_Int32
nLen( aLogicalAdvancements
.getLength() );
778 ::boost::scoped_array
<long> pDXArray( new long[nLen
] );
779 for( sal_Int32 i
=0; i
<nLen
; ++i
)
780 pDXArray
[i
] = basegfx::fround( aLogicalAdvancements
[i
] );
783 pVDev
->GetTextOutlines(rAct
.maPolyPolys
,
795 pVDev
->GetTextOutlines(rAct
.maPolyPolys
,
802 // own copy, for thread safety
803 std::for_each(rAct
.maPolyPolys
.begin(),
804 rAct
.maPolyPolys
.end(),
805 ::boost::mem_fn(&::basegfx::B2DPolyPolygon::makeUnique
));
807 rAct
.maFunction
= &lcl_fillPolyPolygon
;
812 return uno::Reference
< rendering::XCachedPrimitive
>(NULL
);
815 uno::Reference
< rendering::XCachedPrimitive
> CanvasHelper::drawBitmap( const rendering::XCanvas
* /*pCanvas*/,
816 const uno::Reference
< rendering::XBitmap
>& xBitmap
,
817 const rendering::ViewState
& viewState
,
818 const rendering::RenderState
& renderState
)
820 ENSURE_OR_THROW( xBitmap
.is(),
821 "CanvasHelper::drawBitmap: bitmap is NULL");
826 CanvasBitmap
* pOwnBitmap
=dynamic_cast<CanvasBitmap
*>(xBitmap
.get());
829 // insert as transformed copy of bitmap action vector -
830 // during rendering, this gets rendered into a temporary
831 // buffer, and then composited to the front
832 mpRecordedActions
->push_back( Action() );
833 Action
& rAct
=mpRecordedActions
->back();
835 setupGraphicsState( rAct
, viewState
, renderState
);
836 rAct
.maFunction
= ::boost::bind(&lcl_drawOwnBitmap
,
842 // TODO(P3): Highly inefficient - simply copies pixel data
844 uno::Reference
< rendering::XIntegerReadOnlyBitmap
> xIntegerBitmap(
845 xBitmap
, uno::UNO_QUERY
);
846 if( xIntegerBitmap
.is() )
848 const geometry::IntegerSize2D aSize
=xBitmap
->getSize();
849 rendering::IntegerBitmapLayout aLayout
;
850 uno::Sequence
<sal_Int8
> aPixelData
=
851 xIntegerBitmap
->getData(
853 geometry::IntegerRectangle2D(0,0,aSize
.Width
,aSize
.Height
));
855 // force-convert color to ARGB8888 int color space
856 uno::Sequence
<sal_Int8
> aARGBBytes(
857 aLayout
.ColorSpace
->convertToIntegerColorSpace(
859 canvas::tools::getStdColorSpace()));
861 mpRecordedActions
->push_back( Action() );
862 Action
& rAct
=mpRecordedActions
->back();
864 setupGraphicsState( rAct
, viewState
, renderState
);
865 rAct
.maFunction
= ::boost::bind(&lcl_drawGenericBitmap
,
869 aARGBBytes
.getConstArray(),
870 aARGBBytes
.getLength()));
872 // TODO(F1): handle non-integer case
876 // TODO(P1): Provide caching here.
877 return uno::Reference
< rendering::XCachedPrimitive
>(NULL
);
880 uno::Reference
< rendering::XCachedPrimitive
> CanvasHelper::drawBitmapModulated( const rendering::XCanvas
* pCanvas
,
881 const uno::Reference
< rendering::XBitmap
>& xBitmap
,
882 const rendering::ViewState
& viewState
,
883 const rendering::RenderState
& renderState
)
885 // TODO(F3): remove this wart altogether
886 return drawBitmap(pCanvas
, xBitmap
, viewState
, renderState
);
890 void CanvasHelper::setupGraphicsState( Action
& o_action
,
891 const rendering::ViewState
& viewState
,
892 const rendering::RenderState
& renderState
)
894 ENSURE_OR_THROW( mpDevice
,
895 "CanvasHelper::setupGraphicsState: reference device invalid" );
897 // TODO(F3): clipping
898 // TODO(P2): think about caching transformations between canvas calls
900 // setup overall transform only now. View clip above was
901 // relative to view transform
902 ::basegfx::B2DHomMatrix aTransform
;
903 ::canvas::tools::mergeViewAndRenderTransform(o_action
.maTransform
,
906 // setup compositing - mapping courtesy David Reveman
907 // (glitz_operator.c)
908 switch( renderState
.CompositeOperation
)
910 case rendering::CompositeOperation::OVER
:
911 o_action
.meSrcBlendMode
=GL_ONE
;
912 o_action
.meDstBlendMode
=GL_ONE_MINUS_SRC_ALPHA
;
914 case rendering::CompositeOperation::CLEAR
:
915 o_action
.meSrcBlendMode
=GL_ZERO
;
916 o_action
.meDstBlendMode
=GL_ZERO
;
918 case rendering::CompositeOperation::SOURCE
:
919 o_action
.meSrcBlendMode
=GL_ONE
;
920 o_action
.meDstBlendMode
=GL_ZERO
;
922 case rendering::CompositeOperation::UNDER
:
923 // FALLTHROUGH intended - but correct?!
924 case rendering::CompositeOperation::DESTINATION
:
925 o_action
.meSrcBlendMode
=GL_ZERO
;
926 o_action
.meDstBlendMode
=GL_ONE
;
928 case rendering::CompositeOperation::INSIDE
:
929 o_action
.meSrcBlendMode
=GL_DST_ALPHA
;
930 o_action
.meDstBlendMode
=GL_ZERO
;
932 case rendering::CompositeOperation::INSIDE_REVERSE
:
933 o_action
.meSrcBlendMode
=GL_ONE_MINUS_DST_ALPHA
;
934 o_action
.meDstBlendMode
=GL_ZERO
;
936 case rendering::CompositeOperation::OUTSIDE
:
937 o_action
.meSrcBlendMode
=GL_ONE_MINUS_DST_ALPHA
;
938 o_action
.meDstBlendMode
=GL_ONE
;
940 case rendering::CompositeOperation::OUTSIDE_REVERSE
:
941 o_action
.meSrcBlendMode
=GL_ZERO
;
942 o_action
.meDstBlendMode
=GL_ONE_MINUS_SRC_ALPHA
;
944 case rendering::CompositeOperation::ATOP
:
945 o_action
.meSrcBlendMode
=GL_DST_ALPHA
;
946 o_action
.meDstBlendMode
=GL_ONE_MINUS_SRC_ALPHA
;
948 case rendering::CompositeOperation::ATOP_REVERSE
:
949 o_action
.meSrcBlendMode
=GL_ONE_MINUS_DST_ALPHA
;
950 o_action
.meDstBlendMode
=GL_SRC_ALPHA
;
952 case rendering::CompositeOperation::XOR
:
953 o_action
.meSrcBlendMode
=GL_ONE_MINUS_DST_ALPHA
;
954 o_action
.meDstBlendMode
=GL_ONE_MINUS_SRC_ALPHA
;
956 case rendering::CompositeOperation::ADD
:
957 o_action
.meSrcBlendMode
=GL_ONE
;
958 o_action
.meDstBlendMode
=GL_ONE
;
960 case rendering::CompositeOperation::SATURATE
:
961 o_action
.meSrcBlendMode
=GL_SRC_ALPHA_SATURATE
;
962 o_action
.meDstBlendMode
=GL_SRC_ALPHA_SATURATE
;
966 ENSURE_OR_THROW( false, "CanvasHelper::setupGraphicsState: unexpected mode" );
970 if (renderState
.DeviceColor
.getLength())
971 o_action
.maARGBColor
=
972 mpDevice
->getDeviceColorSpace()->convertToARGB(renderState
.DeviceColor
)[0];
975 bool CanvasHelper::renderRecordedActions() const
977 std::vector
<Action
>::const_iterator
aCurr(mpRecordedActions
->begin());
978 const std::vector
<Action
>::const_iterator
aEnd(mpRecordedActions
->end());
979 while( aCurr
!= aEnd
)
981 if( !aCurr
->maFunction( *this,
983 aCurr
->meSrcBlendMode
,
984 aCurr
->meDstBlendMode
,
986 aCurr
->maPolyPolys
) )
995 size_t CanvasHelper::getRecordedActionCount() const
997 return mpRecordedActions
->size();
1001 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */