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