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  {}
347 
349  {}
350 
352  {
353  mpDevice = rSrc.mpDevice;
356  return *this;
357  }
358 
360  {
361  RecordVectorT aThrowaway;
362  mpRecordedActions.swap( aThrowaway );
363  mpDevice = nullptr;
364  mpDeviceHelper = nullptr;
365  }
366 
367  void CanvasHelper::init( rendering::XGraphicDevice& rDevice,
368  SpriteDeviceHelper& rDeviceHelper )
369  {
370  mpDevice = &rDevice;
371  mpDeviceHelper = &rDeviceHelper;
372  }
373 
375  {
376  mpRecordedActions->clear();
377  }
378 
379  void CanvasHelper::drawLine( const rendering::XCanvas* /*pCanvas*/,
380  const geometry::RealPoint2D& aStartPoint,
381  const geometry::RealPoint2D& aEndPoint,
382  const rendering::ViewState& viewState,
383  const rendering::RenderState& renderState )
384  {
385  if( mpDevice )
386  {
387  mpRecordedActions->push_back( Action() );
388  Action& rAct=mpRecordedActions->back();
389 
390  setupGraphicsState( rAct, viewState, renderState );
391  rAct.maFunction = std::bind(&lcl_drawLine,
392  std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5,
393  aStartPoint, aEndPoint);
394  }
395  }
396 
397  void CanvasHelper::drawBezier( const rendering::XCanvas* /*pCanvas*/,
398  const geometry::RealBezierSegment2D& aBezierSegment,
399  const geometry::RealPoint2D& aEndPoint,
400  const rendering::ViewState& viewState,
401  const rendering::RenderState& renderState )
402  {
403  if( !mpDevice )
404  return;
405 
406  mpRecordedActions->push_back( Action() );
407  Action& rAct=mpRecordedActions->back();
408 
409  setupGraphicsState( rAct, viewState, renderState );
410 
411  // TODO(F2): subdivide&render whole curve
412  rAct.maFunction = std::bind(&lcl_drawLine,
413  std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5,
414  geometry::RealPoint2D(
415  aBezierSegment.Px,
416  aBezierSegment.Py),
417  aEndPoint);
418  }
419 
420  uno::Reference< rendering::XCachedPrimitive > CanvasHelper::drawPolyPolygon( const rendering::XCanvas* /*pCanvas*/,
421  const uno::Reference< rendering::XPolyPolygon2D >& xPolyPolygon,
422  const rendering::ViewState& viewState,
423  const rendering::RenderState& renderState )
424  {
425  ENSURE_OR_THROW( xPolyPolygon.is(),
426  "CanvasHelper::drawPolyPolygon: polygon is NULL");
427 
428  if( mpDevice )
429  {
430  mpRecordedActions->push_back( Action() );
431  Action& rAct=mpRecordedActions->back();
432 
433  setupGraphicsState( rAct, viewState, renderState );
434  rAct.maPolyPolys.push_back(
436  rAct.maPolyPolys.back().makeUnique(); // own copy, for thread safety
437 
438  rAct.maFunction = &lcl_drawPolyPolygon;
439  }
440 
441  // TODO(P1): Provide caching here.
442  return uno::Reference< rendering::XCachedPrimitive >(nullptr);
443  }
444 
445  uno::Reference< rendering::XCachedPrimitive > CanvasHelper::strokePolyPolygon( const rendering::XCanvas* /*pCanvas*/,
446  const uno::Reference< rendering::XPolyPolygon2D >& xPolyPolygon,
447  const rendering::ViewState& viewState,
448  const rendering::RenderState& renderState,
449  const rendering::StrokeAttributes& /*strokeAttributes*/ )
450  {
451  ENSURE_OR_THROW( xPolyPolygon.is(),
452  "CanvasHelper::strokePolyPolygon: polygon is NULL");
453 
454  if( mpDevice )
455  {
456  mpRecordedActions->push_back( Action() );
457  Action& rAct=mpRecordedActions->back();
458 
459  setupGraphicsState( rAct, viewState, renderState );
460  rAct.maPolyPolys.push_back(
462  rAct.maPolyPolys.back().makeUnique(); // own copy, for thread safety
463 
464  // TODO(F3): fallback to drawPolyPolygon currently
465  rAct.maFunction = &lcl_drawPolyPolygon;
466  }
467 
468  // TODO(P1): Provide caching here.
469  return uno::Reference< rendering::XCachedPrimitive >(nullptr);
470  }
471 
472  uno::Reference< rendering::XCachedPrimitive > CanvasHelper::strokeTexturedPolyPolygon( const rendering::XCanvas* /*pCanvas*/,
473  const uno::Reference< rendering::XPolyPolygon2D >& /*xPolyPolygon*/,
474  const rendering::ViewState& /*viewState*/,
475  const rendering::RenderState& /*renderState*/,
476  const uno::Sequence< rendering::Texture >& /*textures*/,
477  const rendering::StrokeAttributes& /*strokeAttributes*/ )
478  {
479  // TODO
480  return uno::Reference< rendering::XCachedPrimitive >(nullptr);
481  }
482 
483  uno::Reference< rendering::XCachedPrimitive > CanvasHelper::strokeTextureMappedPolyPolygon( const rendering::XCanvas* /*pCanvas*/,
484  const uno::Reference< rendering::XPolyPolygon2D >& /*xPolyPolygon*/,
485  const rendering::ViewState& /*viewState*/,
486  const rendering::RenderState& /*renderState*/,
487  const uno::Sequence< rendering::Texture >& /*textures*/,
488  const uno::Reference< geometry::XMapping2D >& /*xMapping*/,
489  const rendering::StrokeAttributes& /*strokeAttributes*/ )
490  {
491  // TODO
492  return uno::Reference< rendering::XCachedPrimitive >(nullptr);
493  }
494 
495  uno::Reference< rendering::XPolyPolygon2D > CanvasHelper::queryStrokeShapes( const rendering::XCanvas* /*pCanvas*/,
496  const uno::Reference< rendering::XPolyPolygon2D >& /*xPolyPolygon*/,
497  const rendering::ViewState& /*viewState*/,
498  const rendering::RenderState& /*renderState*/,
499  const rendering::StrokeAttributes& /*strokeAttributes*/ )
500  {
501  // TODO
502  return uno::Reference< rendering::XPolyPolygon2D >(nullptr);
503  }
504 
505  uno::Reference< rendering::XCachedPrimitive > CanvasHelper::fillPolyPolygon( const rendering::XCanvas* /*pCanvas*/,
506  const uno::Reference< rendering::XPolyPolygon2D >& xPolyPolygon,
507  const rendering::ViewState& viewState,
508  const rendering::RenderState& renderState )
509  {
510  ENSURE_OR_THROW( xPolyPolygon.is(),
511  "CanvasHelper::fillPolyPolygon: polygon is NULL");
512 
513  if( mpDevice )
514  {
515  mpRecordedActions->push_back( Action() );
516  Action& rAct=mpRecordedActions->back();
517 
518  setupGraphicsState( rAct, viewState, renderState );
519  rAct.maPolyPolys.push_back(
521  rAct.maPolyPolys.back().makeUnique(); // own copy, for thread safety
522 
523  rAct.maFunction = &lcl_fillPolyPolygon;
524  }
525 
526  // TODO(P1): Provide caching here.
527  return uno::Reference< rendering::XCachedPrimitive >(nullptr);
528  }
529 
530  uno::Reference< rendering::XCachedPrimitive > CanvasHelper::fillTexturedPolyPolygon( const rendering::XCanvas* /*pCanvas*/,
531  const uno::Reference< rendering::XPolyPolygon2D >& xPolyPolygon,
532  const rendering::ViewState& viewState,
533  const rendering::RenderState& renderState,
534  const uno::Sequence< rendering::Texture >& textures )
535  {
536  ENSURE_OR_THROW( xPolyPolygon.is(),
537  "CanvasHelper::fillPolyPolygon: polygon is NULL");
538 
539  if( mpDevice )
540  {
541  mpRecordedActions->push_back( Action() );
542  Action& rAct=mpRecordedActions->back();
543 
544  setupGraphicsState( rAct, viewState, renderState );
545  rAct.maPolyPolys.push_back(
547  rAct.maPolyPolys.back().makeUnique(); // own copy, for thread safety
548 
549  // TODO(F1): Multi-texturing
550  if( textures[0].Gradient.is() )
551  {
552  // try to cast XParametricPolyPolygon2D reference to
553  // our implementation class.
555  dynamic_cast< ::canvas::ParametricPolyPolygon* >( textures[0].Gradient.get() );
556 
557  if( pGradient )
558  {
559  // copy state from Gradient polypoly locally
560  // (given object might change!)
561  const ::canvas::ParametricPolyPolygon::Values& rValues(
562  pGradient->getValues() );
563 
564  rAct.maFunction = std::bind(&lcl_fillGradientPolyPolygon,
565  std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4,
566  rValues,
567  textures[0],
568  std::placeholders::_6);
569  }
570  else
571  {
572  // TODO(F1): The generic case is missing here
573  ENSURE_OR_THROW( false,
574  "CanvasHelper::fillTexturedPolyPolygon(): unknown parametric polygon encountered" );
575  }
576  }
577  else if( textures[0].Bitmap.is() )
578  {
579  // own bitmap?
580  CanvasBitmap* pOwnBitmap=dynamic_cast<CanvasBitmap*>(textures[0].Bitmap.get());
581  if( pOwnBitmap )
582  {
583  // TODO(F2): own texture bitmap
584  }
585  else
586  {
587  // TODO(P3): Highly inefficient - simply copies pixel data
588 
589  uno::Reference< rendering::XIntegerReadOnlyBitmap > xIntegerBitmap(
590  textures[0].Bitmap,
591  uno::UNO_QUERY);
592  if( xIntegerBitmap.is() )
593  {
594  const geometry::IntegerSize2D aSize=xIntegerBitmap->getSize();
595  rendering::IntegerBitmapLayout aLayout;
596  uno::Sequence<sal_Int8> aPixelData=
597  xIntegerBitmap->getData(
598  aLayout,
599  geometry::IntegerRectangle2D(0,0,aSize.Width,aSize.Height));
600 
601  // force-convert color to ARGB8888 int color space
602  uno::Sequence<sal_Int8> aARGBBytes(
603  aLayout.ColorSpace->convertToIntegerColorSpace(
604  aPixelData,
606 
607  rAct.maFunction = std::bind(&lcl_fillTexturedPolyPolygon,
608  std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4,
609  textures[0],
610  aSize,
611  aARGBBytes,
612  rtl_crc32(0,
613  aARGBBytes.getConstArray(),
614  aARGBBytes.getLength()),
615  std::placeholders::_6);
616  }
617  // TODO(F1): handle non-integer case
618  }
619  }
620  }
621 
622  // TODO(P1): Provide caching here.
623  return uno::Reference< rendering::XCachedPrimitive >(nullptr);
624  }
625 
626  uno::Reference< rendering::XCachedPrimitive > CanvasHelper::fillTextureMappedPolyPolygon( const rendering::XCanvas* /*pCanvas*/,
627  const uno::Reference< rendering::XPolyPolygon2D >& /*xPolyPolygon*/,
628  const rendering::ViewState& /*viewState*/,
629  const rendering::RenderState& /*renderState*/,
630  const uno::Sequence< rendering::Texture >& /*textures*/,
631  const uno::Reference< geometry::XMapping2D >& /*xMapping*/ )
632  {
633  // TODO
634  return uno::Reference< rendering::XCachedPrimitive >(nullptr);
635  }
636 
637  uno::Reference< rendering::XCanvasFont > CanvasHelper::createFont( const rendering::XCanvas* /*pCanvas*/,
638  const rendering::FontRequest& fontRequest,
639  const uno::Sequence< beans::PropertyValue >& extraFontProperties,
640  const geometry::Matrix2D& fontMatrix )
641  {
642  if( mpDevice )
643  return uno::Reference< rendering::XCanvasFont >(
644  new CanvasFont(fontRequest, extraFontProperties, fontMatrix ) );
645 
646  return uno::Reference< rendering::XCanvasFont >();
647  }
648 
649  uno::Sequence< rendering::FontInfo > CanvasHelper::queryAvailableFonts( const rendering::XCanvas* /*pCanvas*/,
650  const rendering::FontInfo& /*aFilter*/,
651  const uno::Sequence< beans::PropertyValue >& /*aFontProperties*/ )
652  {
653  // TODO
654  return uno::Sequence< rendering::FontInfo >();
655  }
656 
657  uno::Reference< rendering::XCachedPrimitive > CanvasHelper::drawText( const rendering::XCanvas* /*pCanvas*/,
658  const rendering::StringContext& /*text*/,
659  const uno::Reference< rendering::XCanvasFont >& /*xFont*/,
660  const rendering::ViewState& /*viewState*/,
661  const rendering::RenderState& /*renderState*/,
662  sal_Int8 /*textDirection*/ )
663  {
664  // TODO - but not used from slideshow
665  return uno::Reference< rendering::XCachedPrimitive >(nullptr);
666  }
667 
668  uno::Reference< rendering::XCachedPrimitive > CanvasHelper::drawTextLayout( const rendering::XCanvas* /*pCanvas*/,
669  const uno::Reference< rendering::XTextLayout >& xLayoutetText,
670  const rendering::ViewState& viewState,
671  const rendering::RenderState& renderState )
672  {
673  ENSURE_OR_THROW( xLayoutetText.is(),
674  "CanvasHelper::drawTextLayout: text is NULL");
675 
676  if( mpDevice )
677  {
679  pVDev->EnableOutput(false);
680 
681  auto pLayoutFont = xLayoutetText->getFont();
682  CanvasFont* pFont=dynamic_cast<CanvasFont*>(pLayoutFont.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<tools::Long[]> pDXArray( new tools::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: */
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)
bool equalZero(const T &rfVal)
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)
long Long
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)
constexpr tools::Long Width() const
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
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
sal::systools::COMReference< IDirect3DDevice9 > mpDevice
Definition: dx_9rm.cxx:169
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.