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 :
10 : #include "ogl_canvashelper.hxx"
11 :
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>
19 :
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>
25 :
26 : #include <vcl/virdev.hxx>
27 : #include <vcl/metric.hxx>
28 : #include <vcl/font.hxx>
29 :
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"
36 :
37 : #include <GL/glew.h>
38 :
39 : #include <boost/scoped_array.hpp>
40 :
41 :
42 : using namespace ::com::sun::star;
43 :
44 : namespace oglcanvas
45 : {
46 : /* Concepts:
47 : =========
48 :
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).
60 :
61 : The drawback, of course, is that complex images churn a lot of
62 : GPU cycles on every re-rendering.
63 :
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.
69 :
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)
73 :
74 : Text: http://www.opengl.org/resources/features/fontsurvey/
75 : */
76 :
77 0 : struct CanvasHelper::Action
78 : {
79 : ::basegfx::B2DHomMatrix maTransform;
80 : GLenum meSrcBlendMode;
81 : GLenum meDstBlendMode;
82 : rendering::ARGBColor maARGBColor;
83 : ::basegfx::B2DPolyPolygonVector maPolyPolys;
84 :
85 : ::boost::function6< bool,
86 : const CanvasHelper&,
87 : const ::basegfx::B2DHomMatrix&,
88 : GLenum,
89 : GLenum,
90 : const rendering::ARGBColor&,
91 : const ::basegfx::B2DPolyPolygonVector& > maFunction;
92 : };
93 :
94 : namespace
95 : {
96 0 : 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 )
102 : {
103 0 : TransformationPreserver aPreserver;
104 0 : setupState(rTransform, eSrcBlend, eDstBlend, rColor);
105 :
106 0 : glBegin(GL_POINTS);
107 0 : glVertex2d(rPoint.X, rPoint.Y);
108 0 : glEnd();
109 :
110 0 : return true;
111 : }
112 :
113 0 : 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 )
120 : {
121 0 : TransformationPreserver aPreserver;
122 0 : setupState(rTransform, eSrcBlend, eDstBlend, rColor);
123 :
124 0 : glBegin(GL_LINES);
125 0 : glVertex2d(rStartPoint.X, rStartPoint.Y);
126 0 : glVertex2d(rEndPoint.X, rEndPoint.Y);
127 0 : glEnd();
128 :
129 0 : return true;
130 : }
131 :
132 0 : 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 )
138 : {
139 0 : TransformationPreserver aPreserver;
140 0 : setupState(rTransform, eSrcBlend, eDstBlend, rColor);
141 :
142 0 : ::basegfx::B2DPolyPolygonVector::const_iterator aCurr=rPolyPolygons.begin();
143 0 : const ::basegfx::B2DPolyPolygonVector::const_iterator aEnd=rPolyPolygons.end();
144 0 : while( aCurr != aEnd )
145 0 : renderPolyPolygon(*aCurr++);
146 :
147 0 : return true;
148 : }
149 :
150 0 : 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 )
156 : {
157 0 : TransformationPreserver aPreserver;
158 0 : setupState(rTransform, eSrcBlend, eDstBlend, rColor);
159 :
160 0 : ::basegfx::B2DPolyPolygonVector::const_iterator aCurr=rPolyPolygons.begin();
161 0 : const ::basegfx::B2DPolyPolygonVector::const_iterator aEnd=rPolyPolygons.end();
162 0 : while( aCurr != aEnd )
163 : {
164 0 : glBegin(GL_TRIANGLES);
165 0 : renderComplexPolyPolygon(*aCurr++);
166 0 : glEnd();
167 : }
168 :
169 0 : return true;
170 : }
171 :
172 0 : 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 )
179 : {
180 0 : TransformationPreserver aPreserver;
181 0 : setupState(rTransform, eSrcBlend, eDstBlend, rendering::ARGBColor());
182 :
183 : // convert to weird canvas textur coordinate system (not
184 : // [0,1]^2, but path coordinate system)
185 0 : ::basegfx::B2DHomMatrix aTextureTransform;
186 : ::basegfx::unotools::homMatrixFromAffineMatrix( aTextureTransform,
187 0 : rTexture.AffineTransform );
188 0 : ::basegfx::B2DRange aBounds;
189 0 : ::basegfx::B2DPolyPolygonVector::const_iterator aCurr=rPolyPolygons.begin();
190 0 : const ::basegfx::B2DPolyPolygonVector::const_iterator aEnd=rPolyPolygons.end();
191 0 : while( aCurr != aEnd )
192 0 : aBounds.expand(::basegfx::tools::getRange(*aCurr++));
193 0 : aTextureTransform.translate(-aBounds.getMinX(), -aBounds.getMinY());
194 0 : aTextureTransform.scale(1/aBounds.getWidth(), 1/aBounds.getHeight());
195 :
196 0 : const sal_Int32 nNumCols=rValues.maColors.getLength();
197 0 : uno::Sequence< rendering::ARGBColor > aColors(nNumCols);
198 0 : rendering::ARGBColor* const pColors=aColors.getArray();
199 0 : rendering::ARGBColor* pCurrCol=pColors;
200 0 : for( sal_Int32 i=0; i<nNumCols; ++i )
201 0 : *pCurrCol++ = rHelper.getDevice()->getDeviceColorSpace()->convertToARGB(rValues.maColors[i])[0];
202 :
203 : OSL_ASSERT(nNumCols == rValues.maStops.getLength());
204 :
205 0 : switch( rValues.meType )
206 : {
207 : case ::canvas::ParametricPolyPolygon::GRADIENT_LINEAR:
208 : rHelper.getDeviceHelper()->useLinearGradientShader(pColors,
209 : rValues.maStops,
210 0 : aTextureTransform);
211 0 : break;
212 :
213 : case ::canvas::ParametricPolyPolygon::GRADIENT_ELLIPTICAL:
214 : rHelper.getDeviceHelper()->useRadialGradientShader(pColors,
215 : rValues.maStops,
216 0 : aTextureTransform);
217 0 : break;
218 :
219 : case ::canvas::ParametricPolyPolygon::GRADIENT_RECTANGULAR:
220 : rHelper.getDeviceHelper()->useRectangularGradientShader(pColors,
221 : rValues.maStops,
222 0 : aTextureTransform);
223 0 : break;
224 :
225 : default:
226 0 : ENSURE_OR_THROW( false,
227 : "CanvasHelper lcl_fillGradientPolyPolygon(): Unexpected case" );
228 : }
229 :
230 :
231 0 : aCurr=rPolyPolygons.begin();
232 0 : while( aCurr != aEnd )
233 : {
234 0 : glBegin(GL_TRIANGLES);
235 0 : renderComplexPolyPolygon(*aCurr++);
236 0 : glEnd();
237 : }
238 :
239 0 : glUseProgram(0);
240 0 : glLoadIdentity();
241 0 : glMatrixMode(GL_MODELVIEW);
242 :
243 0 : return true;
244 : }
245 :
246 0 : 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 )
252 : {
253 0 : TransformationPreserver aPreserver;
254 0 : setupState(rTransform, eSrcBlend, eDstBlend, rColor);
255 :
256 0 : return rBitmap.renderRecordedActions();
257 : }
258 :
259 0 : 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 )
267 : {
268 0 : TransformationPreserver aPreserver;
269 0 : setupState(rTransform, eSrcBlend, eDstBlend, rColor);
270 :
271 0 : const unsigned int nTexId=rHelper.getDeviceHelper()->getTextureCache().getTexture(
272 0 : rPixelSize, rPixelData.getConstArray(), nPixelCrc32);
273 :
274 0 : glBindTexture(GL_TEXTURE_2D, nTexId);
275 0 : glEnable(GL_TEXTURE_2D);
276 : glTexParameteri(GL_TEXTURE_2D,
277 : GL_TEXTURE_MIN_FILTER,
278 0 : GL_NEAREST);
279 : glTexParameteri(GL_TEXTURE_2D,
280 : GL_TEXTURE_MAG_FILTER,
281 0 : GL_NEAREST);
282 0 : glEnable(GL_BLEND);
283 : glBlendFunc(GL_SRC_ALPHA,
284 0 : GL_ONE_MINUS_SRC_ALPHA);
285 :
286 : // blend against fixed vertex color; texture alpha is multiplied in
287 0 : glColor4f(1,1,1,1);
288 :
289 0 : glBegin(GL_TRIANGLE_STRIP);
290 0 : glTexCoord2f(0,0); glVertex2d(0,0);
291 0 : glTexCoord2f(0,1); glVertex2d(0, rPixelSize.Height);
292 0 : glTexCoord2f(1,0); glVertex2d(rPixelSize.Width,0);
293 0 : glTexCoord2f(1,1); glVertex2d(rPixelSize.Width,rPixelSize.Height);
294 0 : glEnd();
295 :
296 0 : glBindTexture(GL_TEXTURE_2D, 0);
297 0 : glDisable(GL_TEXTURE_2D);
298 :
299 0 : return true;
300 : }
301 :
302 0 : 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 )
311 : {
312 0 : TransformationPreserver aPreserver;
313 0 : setupState(rTransform, eSrcBlend, eDstBlend, rendering::ARGBColor());
314 :
315 0 : const unsigned int nTexId=rHelper.getDeviceHelper()->getTextureCache().getTexture(
316 0 : rPixelSize, rPixelData.getConstArray(), nPixelCrc32);
317 :
318 0 : glBindTexture(GL_TEXTURE_2D, nTexId);
319 0 : glEnable(GL_TEXTURE_2D);
320 : glTexParameteri(GL_TEXTURE_2D,
321 : GL_TEXTURE_MIN_FILTER,
322 0 : GL_NEAREST);
323 : glTexParameteri(GL_TEXTURE_2D,
324 : GL_TEXTURE_MAG_FILTER,
325 0 : GL_NEAREST);
326 0 : glEnable(GL_BLEND);
327 : glBlendFunc(GL_SRC_ALPHA,
328 0 : GL_ONE_MINUS_SRC_ALPHA);
329 :
330 : // convert to weird canvas textur coordinate system (not
331 : // [0,1]^2, but path coordinate system)
332 0 : ::basegfx::B2DHomMatrix aTextureTransform;
333 : ::basegfx::unotools::homMatrixFromAffineMatrix( aTextureTransform,
334 0 : rTexture.AffineTransform );
335 0 : ::basegfx::B2DRange aBounds;
336 0 : ::basegfx::B2DPolyPolygonVector::const_iterator aCurr=rPolyPolygons.begin();
337 0 : const ::basegfx::B2DPolyPolygonVector::const_iterator aEnd=rPolyPolygons.end();
338 0 : while( aCurr != aEnd )
339 0 : aBounds.expand(::basegfx::tools::getRange(*aCurr++));
340 0 : aTextureTransform.translate(-aBounds.getMinX(), -aBounds.getMinY());
341 0 : aTextureTransform.scale(1/aBounds.getWidth(), 1/aBounds.getHeight());
342 0 : aTextureTransform.invert();
343 :
344 0 : glMatrixMode(GL_TEXTURE);
345 : double aTexTransform[] =
346 : {
347 0 : aTextureTransform.get(0,0), aTextureTransform.get(1,0), 0, 0,
348 0 : aTextureTransform.get(0,1), aTextureTransform.get(1,1), 0, 0,
349 : 0, 0, 1, 0,
350 0 : aTextureTransform.get(0,2), aTextureTransform.get(1,2), 0, 1
351 0 : };
352 0 : glLoadMatrixd(aTexTransform);
353 :
354 : // blend against fixed vertex color; texture alpha is multiplied in
355 0 : glColor4f(1,1,1,rTexture.Alpha);
356 :
357 0 : aCurr=rPolyPolygons.begin();
358 0 : while( aCurr != aEnd )
359 : {
360 0 : glBegin(GL_TRIANGLES);
361 0 : renderComplexPolyPolygon(*aCurr++);
362 0 : glEnd();
363 : }
364 :
365 0 : glLoadIdentity();
366 0 : glMatrixMode(GL_MODELVIEW);
367 :
368 0 : glBindTexture(GL_TEXTURE_2D, 0);
369 0 : glDisable(GL_TEXTURE_2D);
370 :
371 0 : return true;
372 : }
373 : }
374 :
375 0 : CanvasHelper::CanvasHelper() :
376 : mpDevice( NULL ),
377 : mpDeviceHelper( NULL ),
378 0 : mpRecordedActions()
379 0 : {}
380 :
381 0 : CanvasHelper::~CanvasHelper()
382 0 : {}
383 :
384 0 : CanvasHelper& CanvasHelper::operator=( const CanvasHelper& rSrc )
385 : {
386 0 : mpDevice = rSrc.mpDevice;
387 0 : mpDeviceHelper = rSrc.mpDeviceHelper;
388 0 : mpRecordedActions = rSrc.mpRecordedActions;
389 0 : return *this;
390 : }
391 :
392 0 : void CanvasHelper::disposing()
393 : {
394 0 : RecordVectorT aThrowaway;
395 0 : mpRecordedActions.swap( aThrowaway );
396 0 : mpDevice = NULL;
397 0 : mpDeviceHelper = NULL;
398 0 : }
399 :
400 0 : void CanvasHelper::init( rendering::XGraphicDevice& rDevice,
401 : SpriteDeviceHelper& rDeviceHelper )
402 : {
403 0 : mpDevice = &rDevice;
404 0 : mpDeviceHelper = &rDeviceHelper;
405 0 : }
406 :
407 0 : void CanvasHelper::clear()
408 : {
409 0 : mpRecordedActions->clear();
410 0 : }
411 :
412 0 : void CanvasHelper::drawPoint( const rendering::XCanvas* /*pCanvas*/,
413 : const geometry::RealPoint2D& aPoint,
414 : const rendering::ViewState& viewState,
415 : const rendering::RenderState& renderState )
416 : {
417 0 : if( mpDevice )
418 : {
419 0 : mpRecordedActions->push_back( Action() );
420 0 : Action& rAct=mpRecordedActions->back();
421 :
422 0 : setupGraphicsState( rAct, viewState, renderState );
423 0 : rAct.maFunction = ::boost::bind(&lcl_drawPoint,
424 : _1,_2,_3,_4,_5,
425 0 : aPoint);
426 : }
427 0 : }
428 :
429 0 : 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 )
434 : {
435 0 : if( mpDevice )
436 : {
437 0 : mpRecordedActions->push_back( Action() );
438 0 : Action& rAct=mpRecordedActions->back();
439 :
440 0 : setupGraphicsState( rAct, viewState, renderState );
441 0 : rAct.maFunction = ::boost::bind(&lcl_drawLine,
442 : _1,_2,_3,_4,_5,
443 0 : aStartPoint,aEndPoint);
444 : }
445 0 : }
446 :
447 0 : 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 )
452 : {
453 0 : if( mpDevice )
454 : {
455 0 : mpRecordedActions->push_back( Action() );
456 0 : Action& rAct=mpRecordedActions->back();
457 :
458 0 : setupGraphicsState( rAct, viewState, renderState );
459 :
460 : // TODO(F2): subdivide&render whole curve
461 0 : rAct.maFunction = ::boost::bind(&lcl_drawLine,
462 : _1,_2,_3,_4,_5,
463 : geometry::RealPoint2D(
464 : aBezierSegment.Px,
465 : aBezierSegment.Py),
466 0 : aEndPoint);
467 : }
468 0 : }
469 :
470 0 : 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 )
474 : {
475 0 : ENSURE_OR_THROW( xPolyPolygon.is(),
476 : "CanvasHelper::drawPolyPolygon: polygon is NULL");
477 :
478 0 : if( mpDevice )
479 : {
480 0 : mpRecordedActions->push_back( Action() );
481 0 : Action& rAct=mpRecordedActions->back();
482 :
483 0 : setupGraphicsState( rAct, viewState, renderState );
484 : rAct.maPolyPolys.push_back(
485 0 : ::basegfx::unotools::b2DPolyPolygonFromXPolyPolygon2D(xPolyPolygon));
486 0 : rAct.maPolyPolys.back().makeUnique(); // own copy, for thread safety
487 :
488 0 : rAct.maFunction = &lcl_drawPolyPolygon;
489 : }
490 :
491 : // TODO(P1): Provide caching here.
492 0 : return uno::Reference< rendering::XCachedPrimitive >(NULL);
493 : }
494 :
495 0 : 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*/ )
500 : {
501 0 : ENSURE_OR_THROW( xPolyPolygon.is(),
502 : "CanvasHelper::strokePolyPolygon: polygon is NULL");
503 :
504 0 : if( mpDevice )
505 : {
506 0 : mpRecordedActions->push_back( Action() );
507 0 : Action& rAct=mpRecordedActions->back();
508 :
509 0 : setupGraphicsState( rAct, viewState, renderState );
510 : rAct.maPolyPolys.push_back(
511 0 : ::basegfx::unotools::b2DPolyPolygonFromXPolyPolygon2D(xPolyPolygon));
512 0 : rAct.maPolyPolys.back().makeUnique(); // own copy, for thread safety
513 :
514 : // TODO(F3): fallback to drawPolyPolygon currently
515 0 : rAct.maFunction = &lcl_drawPolyPolygon;
516 : }
517 :
518 : // TODO(P1): Provide caching here.
519 0 : return uno::Reference< rendering::XCachedPrimitive >(NULL);
520 : }
521 :
522 0 : 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*/ )
528 : {
529 : // TODO
530 0 : return uno::Reference< rendering::XCachedPrimitive >(NULL);
531 : }
532 :
533 0 : 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*/ )
540 : {
541 : // TODO
542 0 : return uno::Reference< rendering::XCachedPrimitive >(NULL);
543 : }
544 :
545 0 : 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*/ )
550 : {
551 : // TODO
552 0 : return uno::Reference< rendering::XPolyPolygon2D >(NULL);
553 : }
554 :
555 0 : 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 )
559 : {
560 0 : ENSURE_OR_THROW( xPolyPolygon.is(),
561 : "CanvasHelper::fillPolyPolygon: polygon is NULL");
562 :
563 0 : if( mpDevice )
564 : {
565 0 : mpRecordedActions->push_back( Action() );
566 0 : Action& rAct=mpRecordedActions->back();
567 :
568 0 : setupGraphicsState( rAct, viewState, renderState );
569 : rAct.maPolyPolys.push_back(
570 0 : ::basegfx::unotools::b2DPolyPolygonFromXPolyPolygon2D(xPolyPolygon));
571 0 : rAct.maPolyPolys.back().makeUnique(); // own copy, for thread safety
572 :
573 0 : rAct.maFunction = &lcl_fillPolyPolygon;
574 : }
575 :
576 : // TODO(P1): Provide caching here.
577 0 : return uno::Reference< rendering::XCachedPrimitive >(NULL);
578 : }
579 :
580 0 : 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 )
585 : {
586 0 : ENSURE_OR_THROW( xPolyPolygon.is(),
587 : "CanvasHelper::fillPolyPolygon: polygon is NULL");
588 :
589 0 : if( mpDevice )
590 : {
591 0 : mpRecordedActions->push_back( Action() );
592 0 : Action& rAct=mpRecordedActions->back();
593 :
594 0 : setupGraphicsState( rAct, viewState, renderState );
595 : rAct.maPolyPolys.push_back(
596 0 : ::basegfx::unotools::b2DPolyPolygonFromXPolyPolygon2D(xPolyPolygon));
597 0 : rAct.maPolyPolys.back().makeUnique(); // own copy, for thread safety
598 :
599 : // TODO(F1): Multi-texturing
600 0 : if( textures[0].Gradient.is() )
601 : {
602 : // try to cast XParametricPolyPolygon2D reference to
603 : // our implementation class.
604 : ::canvas::ParametricPolyPolygon* pGradient =
605 0 : dynamic_cast< ::canvas::ParametricPolyPolygon* >( textures[0].Gradient.get() );
606 :
607 0 : if( pGradient )
608 : {
609 : // copy state from Gradient polypoly locally
610 : // (given object might change!)
611 : const ::canvas::ParametricPolyPolygon::Values& rValues(
612 0 : pGradient->getValues() );
613 :
614 0 : rAct.maFunction = ::boost::bind(&lcl_fillGradientPolyPolygon,
615 : _1,_2,_3,_4,
616 : rValues,
617 0 : textures[0],
618 0 : _6);
619 : }
620 : else
621 : {
622 : // TODO(F1): The generic case is missing here
623 0 : ENSURE_OR_THROW( false,
624 : "CanvasHelper::fillTexturedPolyPolygon(): unknown parametric polygon encountered" );
625 : }
626 : }
627 0 : else if( textures[0].Bitmap.is() )
628 : {
629 : // own bitmap?
630 0 : CanvasBitmap* pOwnBitmap=dynamic_cast<CanvasBitmap*>(textures[0].Bitmap.get());
631 0 : if( pOwnBitmap )
632 : {
633 : // TODO(F2): own texture bitmap
634 : }
635 : else
636 : {
637 : // TODO(P3): Highly inefficient - simply copies pixel data
638 :
639 : uno::Reference< rendering::XIntegerReadOnlyBitmap > xIntegerBitmap(
640 0 : textures[0].Bitmap,
641 0 : uno::UNO_QUERY);
642 0 : if( xIntegerBitmap.is() )
643 : {
644 0 : const geometry::IntegerSize2D aSize=xIntegerBitmap->getSize();
645 0 : rendering::IntegerBitmapLayout aLayout;
646 : uno::Sequence<sal_Int8> aPixelData=
647 0 : xIntegerBitmap->getData(
648 : aLayout,
649 0 : geometry::IntegerRectangle2D(0,0,aSize.Width,aSize.Height));
650 :
651 : // force-convert color to ARGB8888 int color space
652 : uno::Sequence<sal_Int8> aARGBBytes(
653 0 : aLayout.ColorSpace->convertToIntegerColorSpace(
654 : aPixelData,
655 0 : canvas::tools::getStdColorSpace()));
656 :
657 0 : rAct.maFunction = ::boost::bind(&lcl_fillTexturedPolyPolygon,
658 : _1,_2,_3,_4,
659 0 : textures[0],
660 : aSize,
661 : aARGBBytes,
662 : rtl_crc32(0,
663 0 : aARGBBytes.getConstArray(),
664 0 : aARGBBytes.getLength()),
665 0 : _6);
666 0 : }
667 : // TODO(F1): handle non-integer case
668 : }
669 : }
670 : }
671 :
672 : // TODO(P1): Provide caching here.
673 0 : return uno::Reference< rendering::XCachedPrimitive >(NULL);
674 : }
675 :
676 0 : 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*/ )
682 : {
683 : // TODO
684 0 : return uno::Reference< rendering::XCachedPrimitive >(NULL);
685 : }
686 :
687 0 : 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 )
691 : {
692 0 : if( mpDevice )
693 : return uno::Reference< rendering::XCanvasFont >(
694 0 : new CanvasFont(fontRequest, extraFontProperties, fontMatrix ) );
695 :
696 0 : return uno::Reference< rendering::XCanvasFont >();
697 : }
698 :
699 0 : uno::Sequence< rendering::FontInfo > CanvasHelper::queryAvailableFonts( const rendering::XCanvas* /*pCanvas*/,
700 : const rendering::FontInfo& /*aFilter*/,
701 : const uno::Sequence< beans::PropertyValue >& /*aFontProperties*/ )
702 : {
703 : // TODO
704 0 : return uno::Sequence< rendering::FontInfo >();
705 : }
706 :
707 0 : 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*/ )
713 : {
714 : // TODO - but not used from slideshow
715 0 : return uno::Reference< rendering::XCachedPrimitive >(NULL);
716 : }
717 :
718 0 : 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 )
722 : {
723 0 : ENSURE_OR_THROW( xLayoutetText.is(),
724 : "CanvasHelper::drawTextLayout: text is NULL");
725 :
726 0 : if( mpDevice )
727 : {
728 0 : VirtualDevice aVDev;
729 0 : aVDev.EnableOutput(false);
730 :
731 0 : CanvasFont* pFont=dynamic_cast<CanvasFont*>(xLayoutetText->getFont().get());
732 0 : const rendering::StringContext& rTxt=xLayoutetText->getText();
733 0 : if( pFont && rTxt.Length )
734 : {
735 : // create the font
736 0 : const rendering::FontRequest& rFontRequest = pFont->getFontRequest();
737 0 : const geometry::Matrix2D& rFontMatrix = pFont->getFontMatrix();
738 : vcl::Font aFont(
739 : rFontRequest.FontDescription.FamilyName,
740 : rFontRequest.FontDescription.StyleName,
741 0 : Size( 0, ::basegfx::fround(rFontRequest.CellSize)));
742 :
743 0 : aFont.SetAlign( ALIGN_BASELINE );
744 0 : aFont.SetCharSet( (rFontRequest.FontDescription.IsSymbolFont==util::TriState_YES) ? RTL_TEXTENCODING_SYMBOL : RTL_TEXTENCODING_UNICODE );
745 0 : aFont.SetVertical( rFontRequest.FontDescription.IsVertical==util::TriState_YES );
746 0 : aFont.SetWeight( static_cast<FontWeight>(rFontRequest.FontDescription.FontDescription.Weight) );
747 0 : aFont.SetItalic( (rFontRequest.FontDescription.FontDescription.Letterform<=8) ? ITALIC_NONE : ITALIC_NORMAL );
748 :
749 : // adjust to stretched font
750 0 : if(!::rtl::math::approxEqual(rFontMatrix.m00, rFontMatrix.m11))
751 : {
752 0 : const Size aSize = aVDev.GetFontMetric( aFont ).GetSize();
753 0 : const double fDividend( rFontMatrix.m10 + rFontMatrix.m11 );
754 0 : double fStretch = (rFontMatrix.m00 + rFontMatrix.m01);
755 :
756 0 : if( !::basegfx::fTools::equalZero( fDividend) )
757 0 : fStretch /= fDividend;
758 :
759 0 : const sal_Int32 nNewWidth = ::basegfx::fround( aSize.Width() * fStretch );
760 :
761 0 : aFont.SetWidth( nNewWidth );
762 : }
763 :
764 : // set font
765 0 : aVDev.SetFont(aFont);
766 :
767 0 : mpRecordedActions->push_back( Action() );
768 0 : Action& rAct=mpRecordedActions->back();
769 :
770 0 : setupGraphicsState( rAct, viewState, renderState );
771 :
772 : // handle custom spacing, if there
773 0 : uno::Sequence<double> aLogicalAdvancements=xLayoutetText->queryLogicalAdvancements();
774 0 : if( aLogicalAdvancements.getLength() )
775 : {
776 : // create the DXArray
777 0 : const sal_Int32 nLen( aLogicalAdvancements.getLength() );
778 0 : ::boost::scoped_array<long> pDXArray( new long[nLen] );
779 0 : for( sal_Int32 i=0; i<nLen; ++i )
780 0 : pDXArray[i] = basegfx::fround( aLogicalAdvancements[i] );
781 :
782 : // get the glyphs
783 : aVDev.GetTextOutlines(rAct.maPolyPolys,
784 : rTxt.Text,
785 : 0,
786 : rTxt.StartPosition,
787 : rTxt.Length,
788 : true,
789 : 0,
790 0 : pDXArray.get() );
791 : }
792 : else
793 : {
794 : // get the glyphs
795 : aVDev.GetTextOutlines(rAct.maPolyPolys,
796 : rTxt.Text,
797 : 0,
798 : rTxt.StartPosition,
799 0 : rTxt.Length );
800 : }
801 :
802 : // own copy, for thread safety
803 : std::for_each(rAct.maPolyPolys.begin(),
804 : rAct.maPolyPolys.end(),
805 0 : ::boost::mem_fn(&::basegfx::B2DPolyPolygon::makeUnique));
806 :
807 0 : rAct.maFunction = &lcl_fillPolyPolygon;
808 0 : }
809 : }
810 :
811 : // TODO
812 0 : return uno::Reference< rendering::XCachedPrimitive >(NULL);
813 : }
814 :
815 0 : 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 )
819 : {
820 0 : ENSURE_OR_THROW( xBitmap.is(),
821 : "CanvasHelper::drawBitmap: bitmap is NULL");
822 :
823 0 : if( mpDevice )
824 : {
825 : // own bitmap?
826 0 : CanvasBitmap* pOwnBitmap=dynamic_cast<CanvasBitmap*>(xBitmap.get());
827 0 : if( pOwnBitmap )
828 : {
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 0 : mpRecordedActions->push_back( Action() );
833 0 : Action& rAct=mpRecordedActions->back();
834 :
835 0 : setupGraphicsState( rAct, viewState, renderState );
836 0 : rAct.maFunction = ::boost::bind(&lcl_drawOwnBitmap,
837 : _1,_2,_3,_4,_5,
838 0 : *pOwnBitmap);
839 : }
840 : else
841 : {
842 : // TODO(P3): Highly inefficient - simply copies pixel data
843 :
844 : uno::Reference< rendering::XIntegerReadOnlyBitmap > xIntegerBitmap(
845 0 : xBitmap, uno::UNO_QUERY);
846 0 : if( xIntegerBitmap.is() )
847 : {
848 0 : const geometry::IntegerSize2D aSize=xBitmap->getSize();
849 0 : rendering::IntegerBitmapLayout aLayout;
850 : uno::Sequence<sal_Int8> aPixelData=
851 0 : xIntegerBitmap->getData(
852 : aLayout,
853 0 : geometry::IntegerRectangle2D(0,0,aSize.Width,aSize.Height));
854 :
855 : // force-convert color to ARGB8888 int color space
856 : uno::Sequence<sal_Int8> aARGBBytes(
857 0 : aLayout.ColorSpace->convertToIntegerColorSpace(
858 : aPixelData,
859 0 : canvas::tools::getStdColorSpace()));
860 :
861 0 : mpRecordedActions->push_back( Action() );
862 0 : Action& rAct=mpRecordedActions->back();
863 :
864 0 : setupGraphicsState( rAct, viewState, renderState );
865 0 : rAct.maFunction = ::boost::bind(&lcl_drawGenericBitmap,
866 : _1,_2,_3,_4,_5,
867 : aSize, aARGBBytes,
868 : rtl_crc32(0,
869 0 : aARGBBytes.getConstArray(),
870 0 : aARGBBytes.getLength()));
871 0 : }
872 : // TODO(F1): handle non-integer case
873 : }
874 : }
875 :
876 : // TODO(P1): Provide caching here.
877 0 : return uno::Reference< rendering::XCachedPrimitive >(NULL);
878 : }
879 :
880 0 : 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 )
884 : {
885 : // TODO(F3): remove this wart altogether
886 0 : return drawBitmap(pCanvas, xBitmap, viewState, renderState);
887 : }
888 :
889 :
890 0 : void CanvasHelper::setupGraphicsState( Action& o_action,
891 : const rendering::ViewState& viewState,
892 : const rendering::RenderState& renderState )
893 : {
894 0 : ENSURE_OR_THROW( mpDevice,
895 : "CanvasHelper::setupGraphicsState: reference device invalid" );
896 :
897 : // TODO(F3): clipping
898 : // TODO(P2): think about caching transformations between canvas calls
899 :
900 : // setup overall transform only now. View clip above was
901 : // relative to view transform
902 0 : ::basegfx::B2DHomMatrix aTransform;
903 : ::canvas::tools::mergeViewAndRenderTransform(o_action.maTransform,
904 : viewState,
905 0 : renderState);
906 : // setup compositing - mapping courtesy David Reveman
907 : // (glitz_operator.c)
908 0 : switch( renderState.CompositeOperation )
909 : {
910 : case rendering::CompositeOperation::OVER:
911 0 : o_action.meSrcBlendMode=GL_ONE;
912 0 : o_action.meDstBlendMode=GL_ONE_MINUS_SRC_ALPHA;
913 0 : break;
914 : case rendering::CompositeOperation::CLEAR:
915 0 : o_action.meSrcBlendMode=GL_ZERO;
916 0 : o_action.meDstBlendMode=GL_ZERO;
917 0 : break;
918 : case rendering::CompositeOperation::SOURCE:
919 0 : o_action.meSrcBlendMode=GL_ONE;
920 0 : o_action.meDstBlendMode=GL_ZERO;
921 0 : break;
922 : case rendering::CompositeOperation::UNDER:
923 : // FALLTHROUGH intended - but correct?!
924 : case rendering::CompositeOperation::DESTINATION:
925 0 : o_action.meSrcBlendMode=GL_ZERO;
926 0 : o_action.meDstBlendMode=GL_ONE;
927 0 : break;
928 : case rendering::CompositeOperation::INSIDE:
929 0 : o_action.meSrcBlendMode=GL_DST_ALPHA;
930 0 : o_action.meDstBlendMode=GL_ZERO;
931 0 : break;
932 : case rendering::CompositeOperation::INSIDE_REVERSE:
933 0 : o_action.meSrcBlendMode=GL_ONE_MINUS_DST_ALPHA;
934 0 : o_action.meDstBlendMode=GL_ZERO;
935 0 : break;
936 : case rendering::CompositeOperation::OUTSIDE:
937 0 : o_action.meSrcBlendMode=GL_ONE_MINUS_DST_ALPHA;
938 0 : o_action.meDstBlendMode=GL_ONE;
939 0 : break;
940 : case rendering::CompositeOperation::OUTSIDE_REVERSE:
941 0 : o_action.meSrcBlendMode=GL_ZERO;
942 0 : o_action.meDstBlendMode=GL_ONE_MINUS_SRC_ALPHA;
943 0 : break;
944 : case rendering::CompositeOperation::ATOP:
945 0 : o_action.meSrcBlendMode=GL_DST_ALPHA;
946 0 : o_action.meDstBlendMode=GL_ONE_MINUS_SRC_ALPHA;
947 0 : break;
948 : case rendering::CompositeOperation::ATOP_REVERSE:
949 0 : o_action.meSrcBlendMode=GL_ONE_MINUS_DST_ALPHA;
950 0 : o_action.meDstBlendMode=GL_SRC_ALPHA;
951 0 : break;
952 : case rendering::CompositeOperation::XOR:
953 0 : o_action.meSrcBlendMode=GL_ONE_MINUS_DST_ALPHA;
954 0 : o_action.meDstBlendMode=GL_ONE_MINUS_SRC_ALPHA;
955 0 : break;
956 : case rendering::CompositeOperation::ADD:
957 0 : o_action.meSrcBlendMode=GL_ONE;
958 0 : o_action.meDstBlendMode=GL_ONE;
959 0 : break;
960 : case rendering::CompositeOperation::SATURATE:
961 0 : o_action.meSrcBlendMode=GL_SRC_ALPHA_SATURATE;
962 0 : o_action.meDstBlendMode=GL_SRC_ALPHA_SATURATE;
963 0 : break;
964 :
965 : default:
966 0 : ENSURE_OR_THROW( false, "CanvasHelper::setupGraphicsState: unexpected mode" );
967 : break;
968 : }
969 :
970 0 : if (renderState.DeviceColor.getLength())
971 : o_action.maARGBColor =
972 0 : mpDevice->getDeviceColorSpace()->convertToARGB(renderState.DeviceColor)[0];
973 0 : }
974 :
975 0 : void CanvasHelper::flush() const
976 : {
977 0 : }
978 :
979 0 : bool CanvasHelper::renderRecordedActions() const
980 : {
981 0 : std::vector<Action>::const_iterator aCurr(mpRecordedActions->begin());
982 0 : const std::vector<Action>::const_iterator aEnd(mpRecordedActions->end());
983 0 : while( aCurr != aEnd )
984 : {
985 0 : if( !aCurr->maFunction( *this,
986 0 : aCurr->maTransform,
987 0 : aCurr->meSrcBlendMode,
988 0 : aCurr->meDstBlendMode,
989 0 : aCurr->maARGBColor,
990 0 : aCurr->maPolyPolys ) )
991 0 : return false;
992 :
993 0 : ++aCurr;
994 : }
995 :
996 0 : return true;
997 : }
998 :
999 0 : size_t CanvasHelper::getRecordedActionCount() const
1000 : {
1001 0 : return mpRecordedActions->size();
1002 : }
1003 0 : }
1004 :
1005 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|