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>
23 #include <tools/diagnose_ex.h>
24 #include <vcl/font.hxx>
25 #include <vcl/metric.hxx>
26 #include <vcl/virdev.hxx>
27 
28 #include "ogl_canvasbitmap.hxx"
29 #include "ogl_canvasfont.hxx"
30 #include "ogl_canvastools.hxx"
31 #include "ogl_texturecache.hxx"
32 #include "ogl_tools.hxx"
33 
34 #include "ogl_canvashelper.hxx"
35 
36 using namespace ::com::sun::star;
37 using namespace std::placeholders;
38 
39 namespace oglcanvas
40 {
41  /* Concepts:
42  =========
43 
44  This OpenGL canvas implementation tries to keep all render
45  output as high-level as possible, i.e. geometry data and
46  externally-provided bitmaps. Therefore, calls at the
47  XCanvas-interfaces are not immediately transformed into colored
48  pixel inside some GL buffer, but are retained simply with their
49  call parameters. Only after XSpriteCanvas::updateScreen() has
50  been called, this all gets transferred to the OpenGL subsystem
51  and converted to a visible scene. The big advantage is, this
52  makes sprite modifications practically zero-overhead, and saves
53  a lot on texture memory (compared to the directx canvas, which
54  immediately dumps every render call into a texture).
55 
56  The drawback, of course, is that complex images churn a lot of
57  GPU cycles on every re-rendering.
58 
59  For the while, I'll be using immediate mode, i.e. transfer data
60  over and over again to the OpenGL subsystem. Alternatively,
61  there are display lists, which at least keep the data on the
62  server, or even better, vertex buffers, which copy geometry
63  data over en bloc.
64 
65  Next todo: put polygon geometry into vertex buffer (LRU cache
66  necessary?) - or, rather, buffer objects! prune entries older
67  than one updateScreen() call)
68 
69  Text: http://www.opengl.org/resources/features/fontsurvey/
70  */
71 
73  {
77  rendering::ARGBColor maARGBColor;
79 
80  std::function< bool (
81  const CanvasHelper&,
82  const ::basegfx::B2DHomMatrix&,
83  GLenum,
84  GLenum,
85  const rendering::ARGBColor&,
86  const ::basegfx::B2DPolyPolygonVector&)> maFunction;
87  };
88 
89  namespace
90  {
91  bool lcl_drawLine( const CanvasHelper& /*rHelper*/,
92  const ::basegfx::B2DHomMatrix& rTransform,
93  GLenum eSrcBlend,
94  GLenum eDstBlend,
95  const rendering::ARGBColor& rColor,
96  const geometry::RealPoint2D& rStartPoint,
97  const geometry::RealPoint2D& rEndPoint )
98  {
99  TransformationPreserver aPreserver;
100  setupState(rTransform, eSrcBlend, eDstBlend, rColor);
101 
102  glBegin(GL_LINES);
103  glVertex2d(rStartPoint.X, rStartPoint.Y);
104  glVertex2d(rEndPoint.X, rEndPoint.Y);
105  glEnd();
106 
107  return true;
108  }
109 
110  bool lcl_drawPolyPolygon( const CanvasHelper& /*rHelper*/,
111  const ::basegfx::B2DHomMatrix& rTransform,
112  GLenum eSrcBlend,
113  GLenum eDstBlend,
114  const rendering::ARGBColor& rColor,
115  const ::basegfx::B2DPolyPolygonVector& rPolyPolygons )
116  {
117  TransformationPreserver aPreserver;
118  setupState(rTransform, eSrcBlend, eDstBlend, rColor);
119 
120  for( const auto& rPoly : rPolyPolygons )
121  renderPolyPolygon( rPoly );
122 
123  return true;
124  }
125 
126  bool lcl_fillPolyPolygon( const CanvasHelper& /*rHelper*/,
127  const ::basegfx::B2DHomMatrix& rTransform,
128  GLenum eSrcBlend,
129  GLenum eDstBlend,
130  const rendering::ARGBColor& rColor,
131  const ::basegfx::B2DPolyPolygonVector& rPolyPolygons )
132  {
133  TransformationPreserver aPreserver;
134  setupState(rTransform, eSrcBlend, eDstBlend, rColor);
135 
136  for( const auto& rPoly : rPolyPolygons )
137  {
138  glBegin( GL_TRIANGLES );
139  renderComplexPolyPolygon( rPoly );
140  glEnd();
141  }
142 
143  return true;
144  }
145 
146  bool lcl_fillGradientPolyPolygon( const CanvasHelper& rHelper,
147  const ::basegfx::B2DHomMatrix& rTransform,
148  GLenum eSrcBlend,
149  GLenum eDstBlend,
150  const ::canvas::ParametricPolyPolygon::Values& rValues,
151  const rendering::Texture& rTexture,
152  const ::basegfx::B2DPolyPolygonVector& rPolyPolygons )
153  {
154  TransformationPreserver aPreserver;
155  setupState(rTransform, eSrcBlend, eDstBlend, rendering::ARGBColor());
156 
157  // convert to weird canvas textur coordinate system (not
158  // [0,1]^2, but path coordinate system)
159  ::basegfx::B2DHomMatrix aTextureTransform;
160  ::basegfx::unotools::homMatrixFromAffineMatrix( aTextureTransform,
161  rTexture.AffineTransform );
162  ::basegfx::B2DRange aBounds;
163  for( const auto& rPoly : rPolyPolygons )
164  aBounds.expand( ::basegfx::utils::getRange( rPoly ) );
165  aTextureTransform.translate(-aBounds.getMinX(), -aBounds.getMinY());
166  aTextureTransform.scale(1/aBounds.getWidth(), 1/aBounds.getHeight());
167 
168  const sal_Int32 nNumCols=rValues.maColors.getLength();
169  uno::Sequence< rendering::ARGBColor > aColors(nNumCols);
170  rendering::ARGBColor* const pColors=aColors.getArray();
171  rendering::ARGBColor* pCurrCol=pColors;
172  for( sal_Int32 i=0; i<nNumCols; ++i )
173  *pCurrCol++ = rHelper.getDevice()->getDeviceColorSpace()->convertToARGB(rValues.maColors[i])[0];
174 
175  OSL_ASSERT(nNumCols == rValues.maStops.getLength());
176 
177  switch( rValues.meType )
178  {
179  case ::canvas::ParametricPolyPolygon::GradientType::Linear:
180  rHelper.getDeviceHelper()->useLinearGradientShader(pColors,
181  rValues.maStops,
182  aTextureTransform);
183  break;
184 
185  case ::canvas::ParametricPolyPolygon::GradientType::Elliptical:
186  rHelper.getDeviceHelper()->useRadialGradientShader(pColors,
187  rValues.maStops,
188  aTextureTransform);
189  break;
190 
191  case ::canvas::ParametricPolyPolygon::GradientType::Rectangular:
192  rHelper.getDeviceHelper()->useRectangularGradientShader(pColors,
193  rValues.maStops,
194  aTextureTransform);
195  break;
196 
197  default:
198  ENSURE_OR_THROW( false,
199  "CanvasHelper lcl_fillGradientPolyPolygon(): Unexpected case" );
200  }
201 
202 
203  for( const auto& rPoly : rPolyPolygons )
204  {
205  glBegin(GL_TRIANGLES);
206  renderComplexPolyPolygon( rPoly );
207  glEnd();
208  }
209 
210  glUseProgram(0);
211  glLoadIdentity();
212  glMatrixMode(GL_MODELVIEW);
213 
214  return true;
215  }
216 
217  bool lcl_drawOwnBitmap( const CanvasHelper& /*rHelper*/,
218  const ::basegfx::B2DHomMatrix& rTransform,
219  GLenum eSrcBlend,
220  GLenum eDstBlend,
221  const rendering::ARGBColor& rColor,
222  const CanvasBitmap& rBitmap )
223  {
224  TransformationPreserver aPreserver;
225  setupState(rTransform, eSrcBlend, eDstBlend, rColor);
226 
227  return rBitmap.renderRecordedActions();
228  }
229 
230  bool lcl_drawGenericBitmap( const CanvasHelper& rHelper,
231  const ::basegfx::B2DHomMatrix& rTransform,
232  GLenum eSrcBlend,
233  GLenum eDstBlend,
234  const rendering::ARGBColor& rColor,
235  const geometry::IntegerSize2D& rPixelSize,
236  const uno::Sequence<sal_Int8>& rPixelData,
237  sal_uInt32 nPixelCrc32 )
238  {
239  TransformationPreserver aPreserver;
240  setupState(rTransform, eSrcBlend, eDstBlend, rColor);
241 
242  const unsigned int nTexId=rHelper.getDeviceHelper()->getTextureCache().getTexture(
243  rPixelSize, rPixelData.getConstArray(), nPixelCrc32);
244 
245  glBindTexture(GL_TEXTURE_2D, nTexId);
246  glEnable(GL_TEXTURE_2D);
247  glTexParameteri(GL_TEXTURE_2D,
248  GL_TEXTURE_MIN_FILTER,
249  GL_NEAREST);
250  glTexParameteri(GL_TEXTURE_2D,
251  GL_TEXTURE_MAG_FILTER,
252  GL_NEAREST);
253  glEnable(GL_BLEND);
254  glBlendFunc(GL_SRC_ALPHA,
255  GL_ONE_MINUS_SRC_ALPHA);
256 
257  // blend against fixed vertex color; texture alpha is multiplied in
258  glColor4f(1,1,1,1);
259 
260  glBegin(GL_TRIANGLE_STRIP);
261  glTexCoord2f(0,0); glVertex2d(0,0);
262  glTexCoord2f(0,1); glVertex2d(0, rPixelSize.Height);
263  glTexCoord2f(1,0); glVertex2d(rPixelSize.Width,0);
264  glTexCoord2f(1,1); glVertex2d(rPixelSize.Width,rPixelSize.Height);
265  glEnd();
266 
267  glBindTexture(GL_TEXTURE_2D, 0);
268  glDisable(GL_TEXTURE_2D);
269 
270  return true;
271  }
272 
273  bool lcl_fillTexturedPolyPolygon( const CanvasHelper& rHelper,
274  const ::basegfx::B2DHomMatrix& rTransform,
275  GLenum eSrcBlend,
276  GLenum eDstBlend,
277  const rendering::Texture& rTexture,
278  const geometry::IntegerSize2D& rPixelSize,
279  const uno::Sequence<sal_Int8>& rPixelData,
280  sal_uInt32 nPixelCrc32,
281  const ::basegfx::B2DPolyPolygonVector& rPolyPolygons )
282  {
283  TransformationPreserver aPreserver;
284  setupState(rTransform, eSrcBlend, eDstBlend, rendering::ARGBColor());
285 
286  const unsigned int nTexId=rHelper.getDeviceHelper()->getTextureCache().getTexture(
287  rPixelSize, rPixelData.getConstArray(), nPixelCrc32);
288 
289  glBindTexture(GL_TEXTURE_2D, nTexId);
290  glEnable(GL_TEXTURE_2D);
291  glTexParameteri(GL_TEXTURE_2D,
292  GL_TEXTURE_MIN_FILTER,
293  GL_NEAREST);
294  glTexParameteri(GL_TEXTURE_2D,
295  GL_TEXTURE_MAG_FILTER,
296  GL_NEAREST);
297  glEnable(GL_BLEND);
298  glBlendFunc(GL_SRC_ALPHA,
299  GL_ONE_MINUS_SRC_ALPHA);
300 
301  // convert to weird canvas textur coordinate system (not
302  // [0,1]^2, but path coordinate system)
303  ::basegfx::B2DHomMatrix aTextureTransform;
304  ::basegfx::unotools::homMatrixFromAffineMatrix( aTextureTransform,
305  rTexture.AffineTransform );
306  ::basegfx::B2DRange aBounds;
307  for( const auto& rPolyPolygon : rPolyPolygons )
308  aBounds.expand( ::basegfx::utils::getRange( rPolyPolygon ) );
309  aTextureTransform.translate(-aBounds.getMinX(), -aBounds.getMinY());
310  aTextureTransform.scale(1/aBounds.getWidth(), 1/aBounds.getHeight());
311  aTextureTransform.invert();
312 
313  glMatrixMode(GL_TEXTURE);
314  double aTexTransform[] =
315  {
316  aTextureTransform.get(0,0), aTextureTransform.get(1,0), 0, 0,
317  aTextureTransform.get(0,1), aTextureTransform.get(1,1), 0, 0,
318  0, 0, 1, 0,
319  aTextureTransform.get(0,2), aTextureTransform.get(1,2), 0, 1
320  };
321  glLoadMatrixd(aTexTransform);
322 
323  // blend against fixed vertex color; texture alpha is multiplied in
324  glColor4f(1,1,1,rTexture.Alpha);
325 
326  for( const auto& rPolyPolygon : rPolyPolygons )
327  {
328  glBegin(GL_TRIANGLES);
329  renderComplexPolyPolygon( rPolyPolygon );
330  glEnd();
331  }
332 
333  glLoadIdentity();
334  glMatrixMode(GL_MODELVIEW);
335 
336  glBindTexture(GL_TEXTURE_2D, 0);
337  glDisable(GL_TEXTURE_2D);
338 
339  return true;
340  }
341  }
342 
343  CanvasHelper::CanvasHelper() :
344  mpDevice( nullptr ),
345  mpDeviceHelper( nullptr ),
346  mpRecordedActions()
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(
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(
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(
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(
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  CanvasFont* pFont=dynamic_cast<CanvasFont*>(xLayoutetText->getFont().get());
683  const rendering::StringContext& rTxt=xLayoutetText->getText();
684  if( pFont && rTxt.Length )
685  {
686  // create the font
687  const rendering::FontRequest& rFontRequest = pFont->getFontRequest();
688  const geometry::Matrix2D& rFontMatrix = pFont->getFontMatrix();
689  vcl::Font aFont(
690  rFontRequest.FontDescription.FamilyName,
691  rFontRequest.FontDescription.StyleName,
692  Size( 0, ::basegfx::fround(rFontRequest.CellSize)));
693 
694  aFont.SetAlignment( ALIGN_BASELINE );
695  aFont.SetCharSet( (rFontRequest.FontDescription.IsSymbolFont==util::TriState_YES) ? RTL_TEXTENCODING_SYMBOL : RTL_TEXTENCODING_UNICODE );
696  aFont.SetVertical( rFontRequest.FontDescription.IsVertical==util::TriState_YES );
697  aFont.SetWeight( static_cast<FontWeight>(rFontRequest.FontDescription.FontDescription.Weight) );
698  aFont.SetItalic( (rFontRequest.FontDescription.FontDescription.Letterform<=8) ? ITALIC_NONE : ITALIC_NORMAL );
699 
700  if (pFont->getEmphasisMark())
701  aFont.SetEmphasisMark(FontEmphasisMark(pFont->getEmphasisMark()));
702 
703  // adjust to stretched font
704  if(!::rtl::math::approxEqual(rFontMatrix.m00, rFontMatrix.m11))
705  {
706  const Size aSize = pVDev->GetFontMetric( aFont ).GetFontSize();
707  const double fDividend( rFontMatrix.m10 + rFontMatrix.m11 );
708  double fStretch = rFontMatrix.m00 + rFontMatrix.m01;
709 
710  if( !::basegfx::fTools::equalZero( fDividend) )
711  fStretch /= fDividend;
712 
713  const sal_Int32 nNewWidth = ::basegfx::fround( aSize.Width() * fStretch );
714 
715  aFont.SetAverageFontWidth( nNewWidth );
716  }
717 
718  // set font
719  pVDev->SetFont(aFont);
720 
721  mpRecordedActions->push_back( Action() );
722  Action& rAct=mpRecordedActions->back();
723 
724  setupGraphicsState( rAct, viewState, renderState );
725 
726  // handle custom spacing, if there
727  uno::Sequence<double> aLogicalAdvancements=xLayoutetText->queryLogicalAdvancements();
728  if( aLogicalAdvancements.hasElements() )
729  {
730  // create the DXArray
731  const sal_Int32 nLen( aLogicalAdvancements.getLength() );
732  std::unique_ptr<long[]> pDXArray( new long[nLen] );
733  for( sal_Int32 i=0; i<nLen; ++i )
734  pDXArray[i] = basegfx::fround( aLogicalAdvancements[i] );
735 
736  // get the glyphs
737  pVDev->GetTextOutlines(rAct.maPolyPolys,
738  rTxt.Text,
739  0,
740  rTxt.StartPosition,
741  rTxt.Length,
742  0,
743  pDXArray.get() );
744  }
745  else
746  {
747  // get the glyphs
748  pVDev->GetTextOutlines(rAct.maPolyPolys,
749  rTxt.Text,
750  0,
751  rTxt.StartPosition,
752  rTxt.Length );
753  }
754 
755  // own copy, for thread safety
756  for( auto& rPoly : rAct.maPolyPolys )
757  rPoly.makeUnique();
758 
759  rAct.maFunction = &lcl_fillPolyPolygon;
760  }
761  }
762 
763  // TODO
764  return uno::Reference< rendering::XCachedPrimitive >(nullptr);
765  }
766 
767  uno::Reference< rendering::XCachedPrimitive > CanvasHelper::drawBitmap( const rendering::XCanvas* /*pCanvas*/,
768  const uno::Reference< rendering::XBitmap >& xBitmap,
769  const rendering::ViewState& viewState,
770  const rendering::RenderState& renderState )
771  {
772  ENSURE_OR_THROW( xBitmap.is(),
773  "CanvasHelper::drawBitmap: bitmap is NULL");
774 
775  if( mpDevice )
776  {
777  // own bitmap?
778  CanvasBitmap* pOwnBitmap=dynamic_cast<CanvasBitmap*>(xBitmap.get());
779  if( pOwnBitmap )
780  {
781  // insert as transformed copy of bitmap action vector -
782  // during rendering, this gets rendered into a temporary
783  // buffer, and then composited to the front
784  mpRecordedActions->push_back( Action() );
785  Action& rAct=mpRecordedActions->back();
786 
787  setupGraphicsState( rAct, viewState, renderState );
788  rAct.maFunction = std::bind(&lcl_drawOwnBitmap,
789  std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5,
790  *pOwnBitmap);
791  }
792  else
793  {
794  // TODO(P3): Highly inefficient - simply copies pixel data
795 
796  uno::Reference< rendering::XIntegerReadOnlyBitmap > xIntegerBitmap(
797  xBitmap, uno::UNO_QUERY);
798  if( xIntegerBitmap.is() )
799  {
800  const geometry::IntegerSize2D aSize=xBitmap->getSize();
801  rendering::IntegerBitmapLayout aLayout;
802  uno::Sequence<sal_Int8> aPixelData=
803  xIntegerBitmap->getData(
804  aLayout,
805  geometry::IntegerRectangle2D(0,0,aSize.Width,aSize.Height));
806 
807  // force-convert color to ARGB8888 int color space
808  uno::Sequence<sal_Int8> aARGBBytes(
809  aLayout.ColorSpace->convertToIntegerColorSpace(
810  aPixelData,
812 
813  mpRecordedActions->push_back( Action() );
814  Action& rAct=mpRecordedActions->back();
815 
816  setupGraphicsState( rAct, viewState, renderState );
817  rAct.maFunction = std::bind(&lcl_drawGenericBitmap,
818  std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5,
819  aSize, aARGBBytes,
820  rtl_crc32(0,
821  aARGBBytes.getConstArray(),
822  aARGBBytes.getLength()));
823  }
824  // TODO(F1): handle non-integer case
825  }
826  }
827 
828  // TODO(P1): Provide caching here.
829  return uno::Reference< rendering::XCachedPrimitive >(nullptr);
830  }
831 
832  uno::Reference< rendering::XCachedPrimitive > CanvasHelper::drawBitmapModulated( const rendering::XCanvas* pCanvas,
833  const uno::Reference< rendering::XBitmap >& xBitmap,
834  const rendering::ViewState& viewState,
835  const rendering::RenderState& renderState )
836  {
837  // TODO(F3): remove this wart altogether
838  return drawBitmap(pCanvas, xBitmap, viewState, renderState);
839  }
840 
841 
843  const rendering::ViewState& viewState,
844  const rendering::RenderState& renderState )
845  {
847  "CanvasHelper::setupGraphicsState: reference device invalid" );
848 
849  // TODO(F3): clipping
850  // TODO(P2): think about caching transformations between canvas calls
851 
852  // setup overall transform only now. View clip above was
853  // relative to view transform
855  viewState,
856  renderState);
857  // setup compositing - mapping courtesy David Reveman
858  // (glitz_operator.c)
859  switch( renderState.CompositeOperation )
860  {
861  case rendering::CompositeOperation::OVER:
862  o_action.meSrcBlendMode=GL_ONE;
863  o_action.meDstBlendMode=GL_ONE_MINUS_SRC_ALPHA;
864  break;
865  case rendering::CompositeOperation::CLEAR:
866  o_action.meSrcBlendMode=GL_ZERO;
867  o_action.meDstBlendMode=GL_ZERO;
868  break;
869  case rendering::CompositeOperation::SOURCE:
870  o_action.meSrcBlendMode=GL_ONE;
871  o_action.meDstBlendMode=GL_ZERO;
872  break;
873  case rendering::CompositeOperation::UNDER:
874  case rendering::CompositeOperation::DESTINATION:
875  o_action.meSrcBlendMode=GL_ZERO;
876  o_action.meDstBlendMode=GL_ONE;
877  break;
878  case rendering::CompositeOperation::INSIDE:
879  o_action.meSrcBlendMode=GL_DST_ALPHA;
880  o_action.meDstBlendMode=GL_ZERO;
881  break;
882  case rendering::CompositeOperation::INSIDE_REVERSE:
883  o_action.meSrcBlendMode=GL_ONE_MINUS_DST_ALPHA;
884  o_action.meDstBlendMode=GL_ZERO;
885  break;
886  case rendering::CompositeOperation::OUTSIDE:
887  o_action.meSrcBlendMode=GL_ONE_MINUS_DST_ALPHA;
888  o_action.meDstBlendMode=GL_ONE;
889  break;
890  case rendering::CompositeOperation::OUTSIDE_REVERSE:
891  o_action.meSrcBlendMode=GL_ZERO;
892  o_action.meDstBlendMode=GL_ONE_MINUS_SRC_ALPHA;
893  break;
894  case rendering::CompositeOperation::ATOP:
895  o_action.meSrcBlendMode=GL_DST_ALPHA;
896  o_action.meDstBlendMode=GL_ONE_MINUS_SRC_ALPHA;
897  break;
898  case rendering::CompositeOperation::ATOP_REVERSE:
899  o_action.meSrcBlendMode=GL_ONE_MINUS_DST_ALPHA;
900  o_action.meDstBlendMode=GL_SRC_ALPHA;
901  break;
902  case rendering::CompositeOperation::XOR:
903  o_action.meSrcBlendMode=GL_ONE_MINUS_DST_ALPHA;
904  o_action.meDstBlendMode=GL_ONE_MINUS_SRC_ALPHA;
905  break;
906  case rendering::CompositeOperation::ADD:
907  o_action.meSrcBlendMode=GL_ONE;
908  o_action.meDstBlendMode=GL_ONE;
909  break;
910  case rendering::CompositeOperation::SATURATE:
911  o_action.meSrcBlendMode=GL_SRC_ALPHA_SATURATE;
912  o_action.meDstBlendMode=GL_SRC_ALPHA_SATURATE;
913  break;
914 
915  default:
916  ENSURE_OR_THROW( false, "CanvasHelper::setupGraphicsState: unexpected mode" );
917  break;
918  }
919 
920  if (renderState.DeviceColor.hasElements())
921  o_action.maARGBColor =
922  mpDevice->getDeviceColorSpace()->convertToARGB(renderState.DeviceColor)[0];
923  }
924 
926  {
927  for( const auto& rRecordedAction : *mpRecordedActions )
928  {
929  if( !rRecordedAction.maFunction( *this,
930  rRecordedAction.maTransform,
931  rRecordedAction.meSrcBlendMode,
932  rRecordedAction.meDstBlendMode,
933  rRecordedAction.maARGBColor,
934  rRecordedAction.maPolyPolys ) )
935  return false;
936  }
937 
938  return true;
939  }
940 
942  {
943  return mpRecordedActions->size();
944  }
945 }
946 
947 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
long Width() const
void expand(const B2DTuple &rTuple)
std::function< bool(const CanvasHelper &, const ::basegfx::B2DHomMatrix &, GLenum, GLenum, const rendering::ARGBColor &, const ::basegfx::B2DPolyPolygonVector &)> maFunction
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)
void setupGraphicsState(Action &o_action, const css::rendering::ViewState &viewState, const css::rendering::RenderState &renderState)
double getHeight() const
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)
::basegfx::B2DPolyPolygonVector maPolyPolys
::basegfx::B2DHomMatrix & mergeViewAndRenderTransform(::basegfx::B2DHomMatrix &combinedTransform, const rendering::ViewState &viewState, const rendering::RenderState &renderState)
signed char sal_Int8
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)
FontEmphasisMark
Helper class for basic canvas functionality.
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::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 > drawBitmapModulated(const css::rendering::XCanvas *pCanvas, const css::uno::Reference< css::rendering::XBitmap > &xBitmap, const css::rendering::ViewState &viewState, const css::rendering::RenderState &renderState)
void setupState(const ::basegfx::B2DHomMatrix &rTransform, GLenum eSrcBlend, GLenum eDstBlend, const rendering::ARGBColor &rColor)
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)
void swap(cow_wrapper &r)
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)
double get(sal_uInt16 nRow, sal_uInt16 nColumn) const
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)
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)
double getWidth() const
COMReference< IDirect3DDevice9 > mpDevice
Definition: dx_9rm.cxx:169
SpriteDeviceHelper * mpDeviceHelper
Internal helper - used for a few global GL objects, e.g.
void disposing()
Release all references.
B2IRange fround(const B2DRange &rRange)
::basegfx::B2DHomMatrix maTransform
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)
css::rendering::XGraphicDevice * mpDevice
Phyical output device.
int i
static bool equalZero(const double &rfVal)
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)
void scale(double fX, double fY)
::basegfx::B2DPolyPolygon b2DPolyPolygonFromXPolyPolygon2D(const uno::Reference< rendering::XPolyPolygon2D > &xPoly)
RecordVectorT mpRecordedActions
Ptr to array of recorded render calls.
bool renderRecordedActions() const
Write out recorded actions.
B2DRange getRange(const B2DPolygon &rCandidate)
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)
#define ENSURE_OR_THROW(c, m)
double getMinY() const
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 renderComplexPolyPolygon(const ::basegfx::B2DPolyPolygon &rPolyPoly)
triangulates polygon before
void init(css::rendering::XGraphicDevice &rDevice, SpriteDeviceHelper &rDeviceHelper)
Initialize canvas helper.
void translate(double fX, double fY)
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)
CanvasHelper & operator=(const CanvasHelper &)
double getMinX() const
Values getValues() const
Query all defining values of this object atomically.
size_t getRecordedActionCount() const
Retrieve number of recorded actions.
void renderPolyPolygon(const ::basegfx::B2DPolyPolygon &rPolyPoly)
only use this for line polygons.
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)
::std::vector< B2DPolyPolygon > B2DPolyPolygonVector
uno::Reference< rendering::XIntegerBitmapColorSpace > const & getStdColorSpace()
Return a color space for a default RGBA integer format.