1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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>
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
),
54 mpPageManager
->free(mpFragment
);
58 // Surface::getUVCoords
61 void Surface::setColorBufferDirty()
67 // Surface::getUVCoords
70 basegfx::B2DRectangle
Surface::getUVCoords() const
72 ::basegfx::B2ISize
aPageSize(mpPageManager
->getPageSize());
73 ::basegfx::B2IPoint aDestOffset
;
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
,
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
,
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
);
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());
135 // 1) offset of surface subarea
136 // 2) surface transform
137 // 3) translation to output position [rPos]
138 // 4) scale to normalized device coordinates
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 ######################################
159 ------|-----O------|------>X
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
;
181 vertex
.a
= static_cast<float>(fAlpha
);
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(
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
);
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)
250 ::basegfx::B2IPoint aDestOffset
;
252 aDestOffset
= mpFragment
->getPos();
254 // convert size to normalized device coordinates
255 const ::basegfx::B2DRectangle
& rUV(
256 getUVCoords(aPos1
- maSourceOffset
+ aDestOffset
,
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 ######################################
285 ------|-----O------|------>X
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
;
307 vertex
.a
= static_cast<float>(fAlpha
);
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
);
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 ######################################
386 ------|-----O------|------>X
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
,
406 // Push vertices to backend renderer
407 if(const sal_uInt32 nVertexCount
= rTriangleList
.count())
409 canvas::Vertex vertex
;
413 vertex
.a
= static_cast<float>(fAlpha
);
416 #if defined(TRIANGLE_LOG) && defined(DBG_UTIL)
417 OSL_TRACE( "Surface::draw(): numvertices %d numtriangles %d\n",
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.
460 mpFragment
= mpPageManager
->allocateSpace(maSize
);
463 mpFragment
->setColorBuffer(mpColorBuffer
);
464 mpFragment
->setSourceOffset(maSourceOffset
);
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
);
483 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */