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 16 #include <basegfx/matrix/b2dhommatrix.hxx> 17 #include <basegfx/polygon/b2dpolypolygon.hxx> 18 #include <basegfx/polygon/b2dpolypolygontools.hxx> 19 #include <basegfx/utils/canvastools.hxx> 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 72 struct CanvasHelper::Action 73 { 74 ::basegfx::B2DHomMatrix maTransform; 75 GLenum meSrcBlendMode; 76 GLenum meDstBlendMode; 77 rendering::ARGBColor maARGBColor; 78 ::basegfx::B2DPolyPolygonVector maPolyPolys; 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 { lcl_drawLine(const CanvasHelper &,const::basegfx::B2DHomMatrix & rTransform,GLenum eSrcBlend,GLenum eDstBlend,const rendering::ARGBColor & rColor,const geometry::RealPoint2D & rStartPoint,const geometry::RealPoint2D & rEndPoint)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 lcl_drawPolyPolygon(const CanvasHelper &,const::basegfx::B2DHomMatrix & rTransform,GLenum eSrcBlend,GLenum eDstBlend,const rendering::ARGBColor & rColor,const::basegfx::B2DPolyPolygonVector & rPolyPolygons)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 lcl_fillPolyPolygon(const CanvasHelper &,const::basegfx::B2DHomMatrix & rTransform,GLenum eSrcBlend,GLenum eDstBlend,const rendering::ARGBColor & rColor,const::basegfx::B2DPolyPolygonVector & rPolyPolygons)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 lcl_fillGradientPolyPolygon(const CanvasHelper & rHelper,const::basegfx::B2DHomMatrix & rTransform,GLenum eSrcBlend,GLenum eDstBlend,const::canvas::ParametricPolyPolygon::Values & rValues,const rendering::Texture & rTexture,const::basegfx::B2DPolyPolygonVector & rPolyPolygons)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 lcl_drawOwnBitmap(const CanvasHelper &,const::basegfx::B2DHomMatrix & rTransform,GLenum eSrcBlend,GLenum eDstBlend,const rendering::ARGBColor & rColor,const CanvasBitmap & rBitmap)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 lcl_drawGenericBitmap(const CanvasHelper & rHelper,const::basegfx::B2DHomMatrix & rTransform,GLenum eSrcBlend,GLenum eDstBlend,const rendering::ARGBColor & rColor,const geometry::IntegerSize2D & rPixelSize,const uno::Sequence<sal_Int8> & rPixelData,sal_uInt32 nPixelCrc32)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 lcl_fillTexturedPolyPolygon(const CanvasHelper & rHelper,const::basegfx::B2DHomMatrix & rTransform,GLenum eSrcBlend,GLenum eDstBlend,const rendering::Texture & rTexture,const geometry::IntegerSize2D & rPixelSize,const uno::Sequence<sal_Int8> & rPixelData,sal_uInt32 nPixelCrc32,const::basegfx::B2DPolyPolygonVector & rPolyPolygons)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 CanvasHelper()343 CanvasHelper::CanvasHelper() : 344 mpDevice( nullptr ), 345 mpDeviceHelper( nullptr ), 346 mpRecordedActions() 347 {} 348 ~CanvasHelper()349 CanvasHelper::~CanvasHelper() 350 {} 351 operator =(const CanvasHelper & rSrc)352 CanvasHelper& CanvasHelper::operator=( const CanvasHelper& rSrc ) 353 { 354 mpDevice = rSrc.mpDevice; 355 mpDeviceHelper = rSrc.mpDeviceHelper; 356 mpRecordedActions = rSrc.mpRecordedActions; 357 return *this; 358 } 359 disposing()360 void CanvasHelper::disposing() 361 { 362 RecordVectorT aThrowaway; 363 mpRecordedActions.swap( aThrowaway ); 364 mpDevice = nullptr; 365 mpDeviceHelper = nullptr; 366 } 367 init(rendering::XGraphicDevice & rDevice,SpriteDeviceHelper & rDeviceHelper)368 void CanvasHelper::init( rendering::XGraphicDevice& rDevice, 369 SpriteDeviceHelper& rDeviceHelper ) 370 { 371 mpDevice = &rDevice; 372 mpDeviceHelper = &rDeviceHelper; 373 } 374 clear()375 void CanvasHelper::clear() 376 { 377 mpRecordedActions->clear(); 378 } 379 drawLine(const rendering::XCanvas *,const geometry::RealPoint2D & aStartPoint,const geometry::RealPoint2D & aEndPoint,const rendering::ViewState & viewState,const rendering::RenderState & renderState)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 drawBezier(const rendering::XCanvas *,const geometry::RealBezierSegment2D & aBezierSegment,const geometry::RealPoint2D & aEndPoint,const rendering::ViewState & viewState,const rendering::RenderState & renderState)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 drawPolyPolygon(const rendering::XCanvas *,const uno::Reference<rendering::XPolyPolygon2D> & xPolyPolygon,const rendering::ViewState & viewState,const rendering::RenderState & renderState)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 strokePolyPolygon(const rendering::XCanvas *,const uno::Reference<rendering::XPolyPolygon2D> & xPolyPolygon,const rendering::ViewState & viewState,const rendering::RenderState & renderState,const rendering::StrokeAttributes &)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 strokeTexturedPolyPolygon(const rendering::XCanvas *,const uno::Reference<rendering::XPolyPolygon2D> &,const rendering::ViewState &,const rendering::RenderState &,const uno::Sequence<rendering::Texture> &,const rendering::StrokeAttributes &)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 strokeTextureMappedPolyPolygon(const rendering::XCanvas *,const uno::Reference<rendering::XPolyPolygon2D> &,const rendering::ViewState &,const rendering::RenderState &,const uno::Sequence<rendering::Texture> &,const uno::Reference<geometry::XMapping2D> &,const rendering::StrokeAttributes &)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 queryStrokeShapes(const rendering::XCanvas *,const uno::Reference<rendering::XPolyPolygon2D> &,const rendering::ViewState &,const rendering::RenderState &,const rendering::StrokeAttributes &)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 fillPolyPolygon(const rendering::XCanvas *,const uno::Reference<rendering::XPolyPolygon2D> & xPolyPolygon,const rendering::ViewState & viewState,const rendering::RenderState & renderState)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 fillTexturedPolyPolygon(const rendering::XCanvas *,const uno::Reference<rendering::XPolyPolygon2D> & xPolyPolygon,const rendering::ViewState & viewState,const rendering::RenderState & renderState,const uno::Sequence<rendering::Texture> & textures)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. 555 ::canvas::ParametricPolyPolygon* pGradient = 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, 606 canvas::tools::getStdColorSpace())); 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 fillTextureMappedPolyPolygon(const rendering::XCanvas *,const uno::Reference<rendering::XPolyPolygon2D> &,const rendering::ViewState &,const rendering::RenderState &,const uno::Sequence<rendering::Texture> &,const uno::Reference<geometry::XMapping2D> &)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 createFont(const rendering::XCanvas *,const rendering::FontRequest & fontRequest,const uno::Sequence<beans::PropertyValue> & extraFontProperties,const geometry::Matrix2D & fontMatrix)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 queryAvailableFonts(const rendering::XCanvas *,const rendering::FontInfo &,const uno::Sequence<beans::PropertyValue> &)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 drawText(const rendering::XCanvas *,const rendering::StringContext &,const uno::Reference<rendering::XCanvasFont> &,const rendering::ViewState &,const rendering::RenderState &,sal_Int8)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 drawTextLayout(const rendering::XCanvas *,const uno::Reference<rendering::XTextLayout> & xLayoutetText,const rendering::ViewState & viewState,const rendering::RenderState & renderState)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 { 679 ScopedVclPtrInstance< VirtualDevice > pVDev; 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 std::unique_ptr<tools::Long[]> pDXArray( new tools::Long[nLen] ); 734 for( sal_Int32 i=0; i<nLen; ++i ) 735 pDXArray[i] = basegfx::fround( aLogicalAdvancements[i] ); 736 737 // get the glyphs 738 pVDev->GetTextOutlines(rAct.maPolyPolys, 739 rTxt.Text, 740 0, 741 rTxt.StartPosition, 742 rTxt.Length, 743 0, 744 pDXArray.get() ); 745 } 746 else 747 { 748 // get the glyphs 749 pVDev->GetTextOutlines(rAct.maPolyPolys, 750 rTxt.Text, 751 0, 752 rTxt.StartPosition, 753 rTxt.Length ); 754 } 755 756 // own copy, for thread safety 757 for( auto& rPoly : rAct.maPolyPolys ) 758 rPoly.makeUnique(); 759 760 rAct.maFunction = &lcl_fillPolyPolygon; 761 } 762 } 763 764 // TODO 765 return uno::Reference< rendering::XCachedPrimitive >(nullptr); 766 } 767 drawBitmap(const rendering::XCanvas *,const uno::Reference<rendering::XBitmap> & xBitmap,const rendering::ViewState & viewState,const rendering::RenderState & renderState)768 uno::Reference< rendering::XCachedPrimitive > CanvasHelper::drawBitmap( const rendering::XCanvas* /*pCanvas*/, 769 const uno::Reference< rendering::XBitmap >& xBitmap, 770 const rendering::ViewState& viewState, 771 const rendering::RenderState& renderState ) 772 { 773 ENSURE_OR_THROW( xBitmap.is(), 774 "CanvasHelper::drawBitmap: bitmap is NULL"); 775 776 if( mpDevice ) 777 { 778 // own bitmap? 779 CanvasBitmap* pOwnBitmap=dynamic_cast<CanvasBitmap*>(xBitmap.get()); 780 if( pOwnBitmap ) 781 { 782 // insert as transformed copy of bitmap action vector - 783 // during rendering, this gets rendered into a temporary 784 // buffer, and then composited to the front 785 mpRecordedActions->push_back( Action() ); 786 Action& rAct=mpRecordedActions->back(); 787 788 setupGraphicsState( rAct, viewState, renderState ); 789 rAct.maFunction = std::bind(&lcl_drawOwnBitmap, 790 std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5, 791 *pOwnBitmap); 792 } 793 else 794 { 795 // TODO(P3): Highly inefficient - simply copies pixel data 796 797 uno::Reference< rendering::XIntegerReadOnlyBitmap > xIntegerBitmap( 798 xBitmap, uno::UNO_QUERY); 799 if( xIntegerBitmap.is() ) 800 { 801 const geometry::IntegerSize2D aSize=xBitmap->getSize(); 802 rendering::IntegerBitmapLayout aLayout; 803 uno::Sequence<sal_Int8> aPixelData= 804 xIntegerBitmap->getData( 805 aLayout, 806 geometry::IntegerRectangle2D(0,0,aSize.Width,aSize.Height)); 807 808 // force-convert color to ARGB8888 int color space 809 uno::Sequence<sal_Int8> aARGBBytes( 810 aLayout.ColorSpace->convertToIntegerColorSpace( 811 aPixelData, 812 canvas::tools::getStdColorSpace())); 813 814 mpRecordedActions->push_back( Action() ); 815 Action& rAct=mpRecordedActions->back(); 816 817 setupGraphicsState( rAct, viewState, renderState ); 818 rAct.maFunction = std::bind(&lcl_drawGenericBitmap, 819 std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5, 820 aSize, aARGBBytes, 821 rtl_crc32(0, 822 aARGBBytes.getConstArray(), 823 aARGBBytes.getLength())); 824 } 825 // TODO(F1): handle non-integer case 826 } 827 } 828 829 // TODO(P1): Provide caching here. 830 return uno::Reference< rendering::XCachedPrimitive >(nullptr); 831 } 832 drawBitmapModulated(const rendering::XCanvas * pCanvas,const uno::Reference<rendering::XBitmap> & xBitmap,const rendering::ViewState & viewState,const rendering::RenderState & renderState)833 uno::Reference< rendering::XCachedPrimitive > CanvasHelper::drawBitmapModulated( const rendering::XCanvas* pCanvas, 834 const uno::Reference< rendering::XBitmap >& xBitmap, 835 const rendering::ViewState& viewState, 836 const rendering::RenderState& renderState ) 837 { 838 // TODO(F3): remove this wart altogether 839 return drawBitmap(pCanvas, xBitmap, viewState, renderState); 840 } 841 842 setupGraphicsState(Action & o_action,const rendering::ViewState & viewState,const rendering::RenderState & renderState)843 void CanvasHelper::setupGraphicsState( Action& o_action, 844 const rendering::ViewState& viewState, 845 const rendering::RenderState& renderState ) 846 { 847 ENSURE_OR_THROW( mpDevice, 848 "CanvasHelper::setupGraphicsState: reference device invalid" ); 849 850 // TODO(F3): clipping 851 // TODO(P2): think about caching transformations between canvas calls 852 853 // setup overall transform only now. View clip above was 854 // relative to view transform 855 ::canvas::tools::mergeViewAndRenderTransform(o_action.maTransform, 856 viewState, 857 renderState); 858 // setup compositing - mapping courtesy David Reveman 859 // (glitz_operator.c) 860 switch( renderState.CompositeOperation ) 861 { 862 case rendering::CompositeOperation::OVER: 863 o_action.meSrcBlendMode=GL_ONE; 864 o_action.meDstBlendMode=GL_ONE_MINUS_SRC_ALPHA; 865 break; 866 case rendering::CompositeOperation::CLEAR: 867 o_action.meSrcBlendMode=GL_ZERO; 868 o_action.meDstBlendMode=GL_ZERO; 869 break; 870 case rendering::CompositeOperation::SOURCE: 871 o_action.meSrcBlendMode=GL_ONE; 872 o_action.meDstBlendMode=GL_ZERO; 873 break; 874 case rendering::CompositeOperation::UNDER: 875 case rendering::CompositeOperation::DESTINATION: 876 o_action.meSrcBlendMode=GL_ZERO; 877 o_action.meDstBlendMode=GL_ONE; 878 break; 879 case rendering::CompositeOperation::INSIDE: 880 o_action.meSrcBlendMode=GL_DST_ALPHA; 881 o_action.meDstBlendMode=GL_ZERO; 882 break; 883 case rendering::CompositeOperation::INSIDE_REVERSE: 884 o_action.meSrcBlendMode=GL_ONE_MINUS_DST_ALPHA; 885 o_action.meDstBlendMode=GL_ZERO; 886 break; 887 case rendering::CompositeOperation::OUTSIDE: 888 o_action.meSrcBlendMode=GL_ONE_MINUS_DST_ALPHA; 889 o_action.meDstBlendMode=GL_ONE; 890 break; 891 case rendering::CompositeOperation::OUTSIDE_REVERSE: 892 o_action.meSrcBlendMode=GL_ZERO; 893 o_action.meDstBlendMode=GL_ONE_MINUS_SRC_ALPHA; 894 break; 895 case rendering::CompositeOperation::ATOP: 896 o_action.meSrcBlendMode=GL_DST_ALPHA; 897 o_action.meDstBlendMode=GL_ONE_MINUS_SRC_ALPHA; 898 break; 899 case rendering::CompositeOperation::ATOP_REVERSE: 900 o_action.meSrcBlendMode=GL_ONE_MINUS_DST_ALPHA; 901 o_action.meDstBlendMode=GL_SRC_ALPHA; 902 break; 903 case rendering::CompositeOperation::XOR: 904 o_action.meSrcBlendMode=GL_ONE_MINUS_DST_ALPHA; 905 o_action.meDstBlendMode=GL_ONE_MINUS_SRC_ALPHA; 906 break; 907 case rendering::CompositeOperation::ADD: 908 o_action.meSrcBlendMode=GL_ONE; 909 o_action.meDstBlendMode=GL_ONE; 910 break; 911 case rendering::CompositeOperation::SATURATE: 912 o_action.meSrcBlendMode=GL_SRC_ALPHA_SATURATE; 913 o_action.meDstBlendMode=GL_SRC_ALPHA_SATURATE; 914 break; 915 916 default: 917 ENSURE_OR_THROW( false, "CanvasHelper::setupGraphicsState: unexpected mode" ); 918 break; 919 } 920 921 if (renderState.DeviceColor.hasElements()) 922 o_action.maARGBColor = 923 mpDevice->getDeviceColorSpace()->convertToARGB(renderState.DeviceColor)[0]; 924 } 925 renderRecordedActions() const926 bool CanvasHelper::renderRecordedActions() const 927 { 928 for( const auto& rRecordedAction : *mpRecordedActions ) 929 { 930 if( !rRecordedAction.maFunction( *this, 931 rRecordedAction.maTransform, 932 rRecordedAction.meSrcBlendMode, 933 rRecordedAction.meDstBlendMode, 934 rRecordedAction.maARGBColor, 935 rRecordedAction.maPolyPolys ) ) 936 return false; 937 } 938 939 return true; 940 } 941 getRecordedActionCount() const942 size_t CanvasHelper::getRecordedActionCount() const 943 { 944 return mpRecordedActions->size(); 945 } 946 } 947 948 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ 949