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