Branch libreoffice-5-0-4
[LibreOffice.git] / canvas / source / tools / surface.cxx
blob717f8722dc98dd283d589115e0b3dfb6e3512243
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
21 #include "surface.hxx"
22 #include <basegfx/polygon/b2dpolygonclipper.hxx>
23 #include <basegfx/matrix/b2dhommatrixtools.hxx>
24 #include <comphelper/scopeguard.hxx>
25 #include <boost/bind.hpp>
27 namespace canvas
31 // Surface::Surface
34 Surface::Surface( const PageManagerSharedPtr& rPageManager,
35 const IColorBufferSharedPtr& rColorBuffer,
36 const ::basegfx::B2IPoint& rPos,
37 const ::basegfx::B2ISize& rSize ) :
38 mpColorBuffer(rColorBuffer),
39 mpPageManager(rPageManager),
40 mpFragment(),
41 maSourceOffset(rPos),
42 maSize(rSize),
43 mbIsDirty(true)
48 // Surface::~Surface
51 Surface::~Surface()
53 if(mpFragment)
54 mpPageManager->free(mpFragment);
58 // Surface::getUVCoords
61 void Surface::setColorBufferDirty()
63 mbIsDirty=true;
67 // Surface::getUVCoords
70 basegfx::B2DRectangle Surface::getUVCoords() const
72 ::basegfx::B2ISize aPageSize(mpPageManager->getPageSize());
73 ::basegfx::B2IPoint aDestOffset;
74 if( mpFragment )
75 aDestOffset = mpFragment->getPos();
77 const double pw( aPageSize.getX() );
78 const double ph( aPageSize.getY() );
79 const double ox( aDestOffset.getX() );
80 const double oy( aDestOffset.getY() );
81 const double sx( maSize.getX() );
82 const double sy( maSize.getY() );
84 return ::basegfx::B2DRectangle( ox/pw,
85 oy/ph,
86 (ox+sx)/pw,
87 (oy+sy)/ph );
91 // Surface::getUVCoords
94 basegfx::B2DRectangle Surface::getUVCoords( const ::basegfx::B2IPoint& rPos,
95 const ::basegfx::B2ISize& rSize ) const
97 ::basegfx::B2ISize aPageSize(mpPageManager->getPageSize());
99 const double pw( aPageSize.getX() );
100 const double ph( aPageSize.getY() );
101 const double ox( rPos.getX() );
102 const double oy( rPos.getY() );
103 const double sx( rSize.getX() );
104 const double sy( rSize.getY() );
106 return ::basegfx::B2DRectangle( ox/pw,
107 oy/ph,
108 (ox+sx)/pw,
109 (oy+sy)/ph );
113 // Surface::draw
116 bool Surface::draw( double fAlpha,
117 const ::basegfx::B2DPoint& rPos,
118 const ::basegfx::B2DHomMatrix& rTransform )
120 IRenderModuleSharedPtr pRenderModule(mpPageManager->getRenderModule());
122 RenderModuleGuard aGuard( pRenderModule );
124 prepareRendering();
126 // convert size to normalized device coordinates
127 const ::basegfx::B2DRectangle& rUV( getUVCoords() );
129 const double u1(rUV.getMinX());
130 const double v1(rUV.getMinY());
131 const double u2(rUV.getMaxX());
132 const double v2(rUV.getMaxY());
134 // concat transforms
135 // 1) offset of surface subarea
136 // 2) surface transform
137 // 3) translation to output position [rPos]
138 // 4) scale to normalized device coordinates
139 // 5) flip y-axis
140 // 6) translate to account for viewport transform
141 basegfx::B2DHomMatrix aTransform(basegfx::tools::createTranslateB2DHomMatrix(
142 maSourceOffset.getX(), maSourceOffset.getY()));
143 aTransform = aTransform * rTransform;
144 aTransform.translate(::basegfx::fround(rPos.getX()),
145 ::basegfx::fround(rPos.getY()));
148 ######################################
149 ######################################
150 ######################################
155 2 | 3
156 x------------x
157 | | |
158 | | |
159 ------|-----O------|------>X
160 -1 | | | +1
161 | | |
162 x------------x
163 1 | 0
167 ######################################
168 ######################################
169 ######################################
172 const ::basegfx::B2DPoint& p0(aTransform * ::basegfx::B2DPoint(maSize.getX(),maSize.getY()));
173 const ::basegfx::B2DPoint& p1(aTransform * ::basegfx::B2DPoint(0.0,maSize.getY()));
174 const ::basegfx::B2DPoint& p2(aTransform * ::basegfx::B2DPoint(0.0,0.0));
175 const ::basegfx::B2DPoint& p3(aTransform * ::basegfx::B2DPoint(maSize.getX(),0.0));
177 canvas::Vertex vertex;
178 vertex.r = 1.0f;
179 vertex.g = 1.0f;
180 vertex.b = 1.0f;
181 vertex.a = static_cast<float>(fAlpha);
182 vertex.z = 0.0f;
185 pRenderModule->beginPrimitive( canvas::IRenderModule::PRIMITIVE_TYPE_QUAD );
187 // issue an endPrimitive() when leaving the scope
188 const ::comphelper::ScopeGuard aScopeGuard(
189 boost::bind( &::canvas::IRenderModule::endPrimitive,
190 ::boost::ref(pRenderModule) ) );
192 vertex.u=static_cast<float>(u2); vertex.v=static_cast<float>(v2);
193 vertex.x=static_cast<float>(p0.getX()); vertex.y=static_cast<float>(p0.getY());
194 pRenderModule->pushVertex(vertex);
196 vertex.u=static_cast<float>(u1); vertex.v=static_cast<float>(v2);
197 vertex.x=static_cast<float>(p1.getX()); vertex.y=static_cast<float>(p1.getY());
198 pRenderModule->pushVertex(vertex);
200 vertex.u=static_cast<float>(u1); vertex.v=static_cast<float>(v1);
201 vertex.x=static_cast<float>(p2.getX()); vertex.y=static_cast<float>(p2.getY());
202 pRenderModule->pushVertex(vertex);
204 vertex.u=static_cast<float>(u2); vertex.v=static_cast<float>(v1);
205 vertex.x=static_cast<float>(p3.getX()); vertex.y=static_cast<float>(p3.getY());
206 pRenderModule->pushVertex(vertex);
209 return !(pRenderModule->isError());
213 // Surface::drawRectangularArea
216 bool Surface::drawRectangularArea(
217 double fAlpha,
218 const ::basegfx::B2DPoint& rPos,
219 const ::basegfx::B2DRectangle& rArea,
220 const ::basegfx::B2DHomMatrix& rTransform )
222 if( rArea.isEmpty() )
223 return true; // immediate exit for empty area
225 IRenderModuleSharedPtr pRenderModule(mpPageManager->getRenderModule());
227 RenderModuleGuard aGuard( pRenderModule );
229 prepareRendering();
231 // these positions are relative to the texture
232 ::basegfx::B2IPoint aPos1(
233 ::basegfx::fround(rArea.getMinimum().getX()),
234 ::basegfx::fround(rArea.getMinimum().getY()));
235 ::basegfx::B2IPoint aPos2(
236 ::basegfx::fround(rArea.getMaximum().getX()),
237 ::basegfx::fround(rArea.getMaximum().getY()) );
239 // clip the positions to the area this surface covers
240 aPos1.setX(::std::max(aPos1.getX(),maSourceOffset.getX()));
241 aPos1.setY(::std::max(aPos1.getY(),maSourceOffset.getY()));
242 aPos2.setX(::std::min(aPos2.getX(),maSourceOffset.getX()+maSize.getX()));
243 aPos2.setY(::std::min(aPos2.getY(),maSourceOffset.getY()+maSize.getY()));
245 // if the resulting area is empty, return immediately
246 ::basegfx::B2IVector aSize(aPos2 - aPos1);
247 if(aSize.getX() <= 0 || aSize.getY() <= 0)
248 return true;
250 ::basegfx::B2IPoint aDestOffset;
251 if( mpFragment )
252 aDestOffset = mpFragment->getPos();
254 // convert size to normalized device coordinates
255 const ::basegfx::B2DRectangle& rUV(
256 getUVCoords(aPos1 - maSourceOffset + aDestOffset,
257 aSize) );
258 const double u1(rUV.getMinX());
259 const double v1(rUV.getMinY());
260 const double u2(rUV.getMaxX());
261 const double v2(rUV.getMaxY());
263 // concatenate transforms
264 // 1) offset of surface subarea
265 // 2) surface transform
266 // 3) translation to output position [rPos]
267 basegfx::B2DHomMatrix aTransform(basegfx::tools::createTranslateB2DHomMatrix(aPos1.getX(), aPos1.getY()));
268 aTransform = aTransform * rTransform;
269 aTransform.translate(::basegfx::fround(rPos.getX()),
270 ::basegfx::fround(rPos.getY()));
274 ######################################
275 ######################################
276 ######################################
281 2 | 3
282 x------------x
283 | | |
284 | | |
285 ------|-----O------|------>X
286 -1 | | | +1
287 | | |
288 x------------x
289 1 | 0
293 ######################################
294 ######################################
295 ######################################
298 const ::basegfx::B2DPoint& p0(aTransform * ::basegfx::B2DPoint(aSize.getX(),aSize.getY()));
299 const ::basegfx::B2DPoint& p1(aTransform * ::basegfx::B2DPoint(0.0, aSize.getY()));
300 const ::basegfx::B2DPoint& p2(aTransform * ::basegfx::B2DPoint(0.0, 0.0));
301 const ::basegfx::B2DPoint& p3(aTransform * ::basegfx::B2DPoint(aSize.getX(),0.0));
303 canvas::Vertex vertex;
304 vertex.r = 1.0f;
305 vertex.g = 1.0f;
306 vertex.b = 1.0f;
307 vertex.a = static_cast<float>(fAlpha);
308 vertex.z = 0.0f;
311 pRenderModule->beginPrimitive( canvas::IRenderModule::PRIMITIVE_TYPE_QUAD );
313 // issue an endPrimitive() when leaving the scope
314 const ::comphelper::ScopeGuard aScopeGuard(
315 boost::bind( &::canvas::IRenderModule::endPrimitive,
316 ::boost::ref(pRenderModule) ) );
318 vertex.u=static_cast<float>(u2); vertex.v=static_cast<float>(v2);
319 vertex.x=static_cast<float>(p0.getX()); vertex.y=static_cast<float>(p0.getY());
320 pRenderModule->pushVertex(vertex);
322 vertex.u=static_cast<float>(u1); vertex.v=static_cast<float>(v2);
323 vertex.x=static_cast<float>(p1.getX()); vertex.y=static_cast<float>(p1.getY());
324 pRenderModule->pushVertex(vertex);
326 vertex.u=static_cast<float>(u1); vertex.v=static_cast<float>(v1);
327 vertex.x=static_cast<float>(p2.getX()); vertex.y=static_cast<float>(p2.getY());
328 pRenderModule->pushVertex(vertex);
330 vertex.u=static_cast<float>(u2); vertex.v=static_cast<float>(v1);
331 vertex.x=static_cast<float>(p3.getX()); vertex.y=static_cast<float>(p3.getY());
332 pRenderModule->pushVertex(vertex);
335 return !(pRenderModule->isError());
339 // Surface::drawWithClip
342 bool Surface::drawWithClip( double fAlpha,
343 const ::basegfx::B2DPoint& rPos,
344 const ::basegfx::B2DPolygon& rClipPoly,
345 const ::basegfx::B2DHomMatrix& rTransform )
347 IRenderModuleSharedPtr pRenderModule(mpPageManager->getRenderModule());
349 RenderModuleGuard aGuard( pRenderModule );
351 prepareRendering();
353 // untransformed surface rectangle, relative to the whole
354 // image (note: this surface might actually only be a tile of
355 // the whole image, with non-zero maSourceOffset)
356 const double x1(maSourceOffset.getX());
357 const double y1(maSourceOffset.getY());
358 const double w(maSize.getX());
359 const double h(maSize.getY());
360 const double x2(x1+w);
361 const double y2(y1+h);
362 const ::basegfx::B2DRectangle aSurfaceClipRect(x1,y1,x2,y2);
364 // concatenate transforms
365 // we use 'fround' here to avoid rounding errors. the vertices will
366 // be transformed by the overall transform and uv coordinates will
367 // be calculated from the result, and this is why we need to use
368 // integer coordinates here...
369 basegfx::B2DHomMatrix aTransform;
370 aTransform = aTransform * rTransform;
371 aTransform.translate(::basegfx::fround(rPos.getX()),
372 ::basegfx::fround(rPos.getY()));
375 ######################################
376 ######################################
377 ######################################
382 2 | 3
383 x------------x
384 | | |
385 | | |
386 ------|-----O------|------>X
387 -1 | | | +1
388 | | |
389 x------------x
390 1 | 0
394 ######################################
395 ######################################
396 ######################################
399 // uv coordinates that map the surface rectangle
400 // to the destination rectangle.
401 const ::basegfx::B2DRectangle& rUV( getUVCoords() );
403 basegfx::B2DPolygon rTriangleList(basegfx::tools::clipTriangleListOnRange(rClipPoly,
404 aSurfaceClipRect));
406 // Push vertices to backend renderer
407 if(const sal_uInt32 nVertexCount = rTriangleList.count())
409 canvas::Vertex vertex;
410 vertex.r = 1.0f;
411 vertex.g = 1.0f;
412 vertex.b = 1.0f;
413 vertex.a = static_cast<float>(fAlpha);
414 vertex.z = 0.0f;
416 #if defined(TRIANGLE_LOG) && defined(DBG_UTIL)
417 OSL_TRACE( "Surface::draw(): numvertices %d numtriangles %d\n",
418 nVertexCount,
419 nVertexCount/3 );
420 #endif
422 pRenderModule->beginPrimitive( canvas::IRenderModule::PRIMITIVE_TYPE_TRIANGLE );
424 // issue an endPrimitive() when leaving the scope
425 const ::comphelper::ScopeGuard aScopeGuard(
426 boost::bind( &::canvas::IRenderModule::endPrimitive,
427 ::boost::ref(pRenderModule) ) );
429 for(sal_uInt32 nIndex=0; nIndex<nVertexCount; ++nIndex)
431 const basegfx::B2DPoint &aPoint = rTriangleList.getB2DPoint(nIndex);
432 basegfx::B2DPoint aTransformedPoint(aTransform * aPoint);
433 const double tu(((aPoint.getX()-aSurfaceClipRect.getMinX())*rUV.getWidth()/w)+rUV.getMinX());
434 const double tv(((aPoint.getY()-aSurfaceClipRect.getMinY())*rUV.getHeight()/h)+rUV.getMinY());
435 vertex.u=static_cast<float>(tu);
436 vertex.v=static_cast<float>(tv);
437 vertex.x=static_cast<float>(aTransformedPoint.getX());
438 vertex.y=static_cast<float>(aTransformedPoint.getY());
439 pRenderModule->pushVertex(vertex);
443 return !(pRenderModule->isError());
447 // Surface::prepareRendering
450 void Surface::prepareRendering()
452 mpPageManager->validatePages();
454 // clients requested to draw from this surface, therefore one
455 // of the above implemented concrete rendering operations
456 // was triggered. we therefore need to ask the pagemanager
457 // to allocate some space for the fragment we're dedicated to.
458 if(!(mpFragment))
460 mpFragment = mpPageManager->allocateSpace(maSize);
461 if( mpFragment )
463 mpFragment->setColorBuffer(mpColorBuffer);
464 mpFragment->setSourceOffset(maSourceOffset);
468 if( mpFragment )
470 // now we need to 'select' the fragment, which will in turn
471 // pull information from the image on demand.
472 // in case this fragment is still not located on any of the
473 // available pages ['naked'], we force the page manager to
474 // do it now, no way to defer this any longer...
475 if(!(mpFragment->select(mbIsDirty)))
476 mpPageManager->nakedFragment(mpFragment);
479 mbIsDirty=false;
483 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */