Branch libreoffice-5-0-4
[LibreOffice.git] / canvas / source / opengl / ogl_canvashelper.cxx
blob501e7f83fe6b1044616c0b189e86882b2e7fb5c8
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/.
8 */
10 #include "ogl_canvashelper.hxx"
12 #include <rtl/crc.h>
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"
37 #include <GL/glew.h>
39 #include <boost/scoped_array.hpp>
42 using namespace ::com::sun::star;
44 namespace oglcanvas
46 /* Concepts:
47 =========
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
68 data over en bloc.
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,
86 const CanvasHelper&,
87 const ::basegfx::B2DHomMatrix&,
88 GLenum,
89 GLenum,
90 const rendering::ARGBColor&,
91 const ::basegfx::B2DPolyPolygonVector& > maFunction;
94 namespace
96 bool lcl_drawPoint( const CanvasHelper& /*rHelper*/,
97 const ::basegfx::B2DHomMatrix& rTransform,
98 GLenum eSrcBlend,
99 GLenum eDstBlend,
100 const rendering::ARGBColor& rColor,
101 const geometry::RealPoint2D& rPoint )
103 TransformationPreserver aPreserver;
104 setupState(rTransform, eSrcBlend, eDstBlend, rColor);
106 glBegin(GL_POINTS);
107 glVertex2d(rPoint.X, rPoint.Y);
108 glEnd();
110 return true;
113 bool lcl_drawLine( const CanvasHelper& /*rHelper*/,
114 const ::basegfx::B2DHomMatrix& rTransform,
115 GLenum eSrcBlend,
116 GLenum eDstBlend,
117 const rendering::ARGBColor& rColor,
118 const geometry::RealPoint2D& rStartPoint,
119 const geometry::RealPoint2D& rEndPoint )
121 TransformationPreserver aPreserver;
122 setupState(rTransform, eSrcBlend, eDstBlend, rColor);
124 glBegin(GL_LINES);
125 glVertex2d(rStartPoint.X, rStartPoint.Y);
126 glVertex2d(rEndPoint.X, rEndPoint.Y);
127 glEnd();
129 return true;
132 bool lcl_drawPolyPolygon( const CanvasHelper& /*rHelper*/,
133 const ::basegfx::B2DHomMatrix& rTransform,
134 GLenum eSrcBlend,
135 GLenum eDstBlend,
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++);
147 return true;
150 bool lcl_fillPolyPolygon( const CanvasHelper& /*rHelper*/,
151 const ::basegfx::B2DHomMatrix& rTransform,
152 GLenum eSrcBlend,
153 GLenum eDstBlend,
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++);
166 glEnd();
169 return true;
172 bool lcl_fillGradientPolyPolygon( const CanvasHelper& rHelper,
173 const ::basegfx::B2DHomMatrix& rTransform,
174 GLenum eSrcBlend,
175 GLenum eDstBlend,
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,
209 rValues.maStops,
210 aTextureTransform);
211 break;
213 case ::canvas::ParametricPolyPolygon::GRADIENT_ELLIPTICAL:
214 rHelper.getDeviceHelper()->useRadialGradientShader(pColors,
215 rValues.maStops,
216 aTextureTransform);
217 break;
219 case ::canvas::ParametricPolyPolygon::GRADIENT_RECTANGULAR:
220 rHelper.getDeviceHelper()->useRectangularGradientShader(pColors,
221 rValues.maStops,
222 aTextureTransform);
223 break;
225 default:
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++);
236 glEnd();
239 glUseProgram(0);
240 glLoadIdentity();
241 glMatrixMode(GL_MODELVIEW);
243 return true;
246 bool lcl_drawOwnBitmap( const CanvasHelper& /*rHelper*/,
247 const ::basegfx::B2DHomMatrix& rTransform,
248 GLenum eSrcBlend,
249 GLenum eDstBlend,
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,
261 GLenum eSrcBlend,
262 GLenum eDstBlend,
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,
278 GL_NEAREST);
279 glTexParameteri(GL_TEXTURE_2D,
280 GL_TEXTURE_MAG_FILTER,
281 GL_NEAREST);
282 glEnable(GL_BLEND);
283 glBlendFunc(GL_SRC_ALPHA,
284 GL_ONE_MINUS_SRC_ALPHA);
286 // blend against fixed vertex color; texture alpha is multiplied in
287 glColor4f(1,1,1,1);
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);
294 glEnd();
296 glBindTexture(GL_TEXTURE_2D, 0);
297 glDisable(GL_TEXTURE_2D);
299 return true;
302 bool lcl_fillTexturedPolyPolygon( const CanvasHelper& rHelper,
303 const ::basegfx::B2DHomMatrix& rTransform,
304 GLenum eSrcBlend,
305 GLenum eDstBlend,
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,
322 GL_NEAREST);
323 glTexParameteri(GL_TEXTURE_2D,
324 GL_TEXTURE_MAG_FILTER,
325 GL_NEAREST);
326 glEnable(GL_BLEND);
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,
349 0, 0, 1, 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++);
362 glEnd();
365 glLoadIdentity();
366 glMatrixMode(GL_MODELVIEW);
368 glBindTexture(GL_TEXTURE_2D, 0);
369 glDisable(GL_TEXTURE_2D);
371 return true;
375 CanvasHelper::CanvasHelper() :
376 mpDevice( NULL ),
377 mpDeviceHelper( NULL ),
378 mpRecordedActions()
381 CanvasHelper::~CanvasHelper()
384 CanvasHelper& CanvasHelper::operator=( const CanvasHelper& rSrc )
386 mpDevice = rSrc.mpDevice;
387 mpDeviceHelper = rSrc.mpDeviceHelper;
388 mpRecordedActions = rSrc.mpRecordedActions;
389 return *this;
392 void CanvasHelper::disposing()
394 RecordVectorT aThrowaway;
395 mpRecordedActions.swap( aThrowaway );
396 mpDevice = NULL;
397 mpDeviceHelper = NULL;
400 void CanvasHelper::init( rendering::XGraphicDevice& rDevice,
401 SpriteDeviceHelper& rDeviceHelper )
403 mpDevice = &rDevice;
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 )
417 if( mpDevice )
419 mpRecordedActions->push_back( Action() );
420 Action& rAct=mpRecordedActions->back();
422 setupGraphicsState( rAct, viewState, renderState );
423 rAct.maFunction = ::boost::bind(&lcl_drawPoint,
424 _1,_2,_3,_4,_5,
425 aPoint);
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 )
435 if( mpDevice )
437 mpRecordedActions->push_back( Action() );
438 Action& rAct=mpRecordedActions->back();
440 setupGraphicsState( rAct, viewState, renderState );
441 rAct.maFunction = ::boost::bind(&lcl_drawLine,
442 _1,_2,_3,_4,_5,
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 )
453 if( mpDevice )
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,
462 _1,_2,_3,_4,_5,
463 geometry::RealPoint2D(
464 aBezierSegment.Px,
465 aBezierSegment.Py),
466 aEndPoint);
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");
478 if( mpDevice )
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");
504 if( mpDevice )
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*/ )
529 // TODO
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*/ )
541 // TODO
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*/ )
551 // TODO
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");
563 if( mpDevice )
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");
589 if( mpDevice )
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() );
607 if( pGradient )
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,
615 _1,_2,_3,_4,
616 rValues,
617 textures[0],
618 _6);
620 else
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() )
629 // own bitmap?
630 CanvasBitmap* pOwnBitmap=dynamic_cast<CanvasBitmap*>(textures[0].Bitmap.get());
631 if( pOwnBitmap )
633 // TODO(F2): own texture bitmap
635 else
637 // TODO(P3): Highly inefficient - simply copies pixel data
639 uno::Reference< rendering::XIntegerReadOnlyBitmap > xIntegerBitmap(
640 textures[0].Bitmap,
641 uno::UNO_QUERY);
642 if( xIntegerBitmap.is() )
644 const geometry::IntegerSize2D aSize=xIntegerBitmap->getSize();
645 rendering::IntegerBitmapLayout aLayout;
646 uno::Sequence<sal_Int8> aPixelData=
647 xIntegerBitmap->getData(
648 aLayout,
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(
654 aPixelData,
655 canvas::tools::getStdColorSpace()));
657 rAct.maFunction = ::boost::bind(&lcl_fillTexturedPolyPolygon,
658 _1,_2,_3,_4,
659 textures[0],
660 aSize,
661 aARGBBytes,
662 rtl_crc32(0,
663 aARGBBytes.getConstArray(),
664 aARGBBytes.getLength()),
665 _6);
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*/ )
683 // TODO
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 )
692 if( mpDevice )
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*/ )
703 // TODO
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");
726 if( mpDevice )
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 )
735 // create the font
736 const rendering::FontRequest& rFontRequest = pFont->getFontRequest();
737 const geometry::Matrix2D& rFontMatrix = pFont->getFontMatrix();
738 vcl::Font aFont(
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 );
764 // set font
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] );
782 // get the glyphs
783 pVDev->GetTextOutlines(rAct.maPolyPolys,
784 rTxt.Text,
786 rTxt.StartPosition,
787 rTxt.Length,
788 true,
790 pDXArray.get() );
792 else
794 // get the glyphs
795 pVDev->GetTextOutlines(rAct.maPolyPolys,
796 rTxt.Text,
798 rTxt.StartPosition,
799 rTxt.Length );
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;
811 // TODO
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");
823 if( mpDevice )
825 // own bitmap?
826 CanvasBitmap* pOwnBitmap=dynamic_cast<CanvasBitmap*>(xBitmap.get());
827 if( pOwnBitmap )
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,
837 _1,_2,_3,_4,_5,
838 *pOwnBitmap);
840 else
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(
852 aLayout,
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(
858 aPixelData,
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,
866 _1,_2,_3,_4,_5,
867 aSize, aARGBBytes,
868 rtl_crc32(0,
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,
904 viewState,
905 renderState);
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;
913 break;
914 case rendering::CompositeOperation::CLEAR:
915 o_action.meSrcBlendMode=GL_ZERO;
916 o_action.meDstBlendMode=GL_ZERO;
917 break;
918 case rendering::CompositeOperation::SOURCE:
919 o_action.meSrcBlendMode=GL_ONE;
920 o_action.meDstBlendMode=GL_ZERO;
921 break;
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;
927 break;
928 case rendering::CompositeOperation::INSIDE:
929 o_action.meSrcBlendMode=GL_DST_ALPHA;
930 o_action.meDstBlendMode=GL_ZERO;
931 break;
932 case rendering::CompositeOperation::INSIDE_REVERSE:
933 o_action.meSrcBlendMode=GL_ONE_MINUS_DST_ALPHA;
934 o_action.meDstBlendMode=GL_ZERO;
935 break;
936 case rendering::CompositeOperation::OUTSIDE:
937 o_action.meSrcBlendMode=GL_ONE_MINUS_DST_ALPHA;
938 o_action.meDstBlendMode=GL_ONE;
939 break;
940 case rendering::CompositeOperation::OUTSIDE_REVERSE:
941 o_action.meSrcBlendMode=GL_ZERO;
942 o_action.meDstBlendMode=GL_ONE_MINUS_SRC_ALPHA;
943 break;
944 case rendering::CompositeOperation::ATOP:
945 o_action.meSrcBlendMode=GL_DST_ALPHA;
946 o_action.meDstBlendMode=GL_ONE_MINUS_SRC_ALPHA;
947 break;
948 case rendering::CompositeOperation::ATOP_REVERSE:
949 o_action.meSrcBlendMode=GL_ONE_MINUS_DST_ALPHA;
950 o_action.meDstBlendMode=GL_SRC_ALPHA;
951 break;
952 case rendering::CompositeOperation::XOR:
953 o_action.meSrcBlendMode=GL_ONE_MINUS_DST_ALPHA;
954 o_action.meDstBlendMode=GL_ONE_MINUS_SRC_ALPHA;
955 break;
956 case rendering::CompositeOperation::ADD:
957 o_action.meSrcBlendMode=GL_ONE;
958 o_action.meDstBlendMode=GL_ONE;
959 break;
960 case rendering::CompositeOperation::SATURATE:
961 o_action.meSrcBlendMode=GL_SRC_ALPHA_SATURATE;
962 o_action.meDstBlendMode=GL_SRC_ALPHA_SATURATE;
963 break;
965 default:
966 ENSURE_OR_THROW( false, "CanvasHelper::setupGraphicsState: unexpected mode" );
967 break;
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,
982 aCurr->maTransform,
983 aCurr->meSrcBlendMode,
984 aCurr->meDstBlendMode,
985 aCurr->maARGBColor,
986 aCurr->maPolyPolys ) )
987 return false;
989 ++aCurr;
992 return true;
995 size_t CanvasHelper::getRecordedActionCount() const
997 return mpRecordedActions->size();
1001 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */