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 .
20 #include <sal/config.h>
22 #include <basegfx/matrix/b2dhommatrixtools.hxx>
23 #include <basegfx/polygon/b2dpolygonclipper.hxx>
24 #include <comphelper/scopeguard.hxx>
27 #include "surface.hxx"
31 Surface::Surface( PageManagerSharedPtr rPageManager
,
32 std::shared_ptr
<IColorBuffer
> xColorBuffer
,
33 const ::basegfx::B2IPoint
& rPos
,
34 const ::basegfx::B2ISize
& rSize
) :
35 mpColorBuffer(std::move(xColorBuffer
)),
36 mpPageManager(std::move(rPageManager
)),
46 mpPageManager
->free(mpFragment
);
49 void Surface::setColorBufferDirty()
54 basegfx::B2DRectangle
Surface::getUVCoords() const
56 ::basegfx::B2ISize
aPageSize(mpPageManager
->getPageSize());
57 ::basegfx::B2IPoint aDestOffset
;
59 aDestOffset
= mpFragment
->getPos();
61 const double pw( aPageSize
.getWidth() );
62 const double ph( aPageSize
.getHeight() );
63 const double ox( aDestOffset
.getX() );
64 const double oy( aDestOffset
.getY() );
65 const double sx( maSize
.getWidth() );
66 const double sy( maSize
.getHeight() );
68 return ::basegfx::B2DRectangle( ox
/pw
,
74 basegfx::B2DRectangle
Surface::getUVCoords( const ::basegfx::B2IPoint
& rPos
,
75 const ::basegfx::B2ISize
& rSize
) const
77 ::basegfx::B2ISize
aPageSize(mpPageManager
->getPageSize());
79 const double pw( aPageSize
.getWidth() );
80 const double ph( aPageSize
.getHeight() );
81 const double ox( rPos
.getX() );
82 const double oy( rPos
.getY() );
83 const double sx( rSize
.getWidth() );
84 const double sy( rSize
.getHeight() );
86 return ::basegfx::B2DRectangle( ox
/pw
,
92 bool Surface::draw( double fAlpha
,
93 const ::basegfx::B2DPoint
& rPos
,
94 const ::basegfx::B2DHomMatrix
& rTransform
)
96 std::shared_ptr
<IRenderModule
> pRenderModule(mpPageManager
->getRenderModule());
98 RenderModuleGuard
aGuard( pRenderModule
);
102 // convert size to normalized device coordinates
103 const ::basegfx::B2DRectangle
aUV( getUVCoords() );
105 const double u1(aUV
.getMinX());
106 const double v1(aUV
.getMinY());
107 const double u2(aUV
.getMaxX());
108 const double v2(aUV
.getMaxY());
111 // 1) offset of surface subarea
112 // 2) surface transform
113 // 3) translation to output position [rPos]
114 // 4) scale to normalized device coordinates
116 // 6) translate to account for viewport transform
117 basegfx::B2DHomMatrix
aTransform(basegfx::utils::createTranslateB2DHomMatrix(
118 maSourceOffset
.getX(), maSourceOffset
.getY()));
119 aTransform
= aTransform
* rTransform
;
120 aTransform
.translate(rPos
);
123 ######################################
124 ######################################
125 ######################################
134 ------|-----O------|------>X
142 ######################################
143 ######################################
144 ######################################
147 const ::basegfx::B2DPoint
p0(aTransform
* ::basegfx::B2DPoint(maSize
.getWidth(),maSize
.getHeight()));
148 const ::basegfx::B2DPoint
p1(aTransform
* ::basegfx::B2DPoint(0.0,maSize
.getHeight()));
149 const ::basegfx::B2DPoint
p2(aTransform
* ::basegfx::B2DPoint(0.0,0.0));
150 const ::basegfx::B2DPoint
p3(aTransform
* ::basegfx::B2DPoint(maSize
.getWidth(),0.0));
152 canvas::Vertex vertex
;
156 vertex
.a
= static_cast<float>(fAlpha
);
160 pRenderModule
->beginPrimitive( canvas::IRenderModule::PrimitiveType::Quad
);
162 // issue an endPrimitive() when leaving the scope
163 const ::comphelper::ScopeGuard
aScopeGuard(
164 [&pRenderModule
]() mutable { pRenderModule
->endPrimitive(); } );
166 vertex
.u
=static_cast<float>(u2
); vertex
.v
=static_cast<float>(v2
);
167 vertex
.x
=static_cast<float>(p0
.getX()); vertex
.y
=static_cast<float>(p0
.getY());
168 pRenderModule
->pushVertex(vertex
);
170 vertex
.u
=static_cast<float>(u1
); vertex
.v
=static_cast<float>(v2
);
171 vertex
.x
=static_cast<float>(p1
.getX()); vertex
.y
=static_cast<float>(p1
.getY());
172 pRenderModule
->pushVertex(vertex
);
174 vertex
.u
=static_cast<float>(u1
); vertex
.v
=static_cast<float>(v1
);
175 vertex
.x
=static_cast<float>(p2
.getX()); vertex
.y
=static_cast<float>(p2
.getY());
176 pRenderModule
->pushVertex(vertex
);
178 vertex
.u
=static_cast<float>(u2
); vertex
.v
=static_cast<float>(v1
);
179 vertex
.x
=static_cast<float>(p3
.getX()); vertex
.y
=static_cast<float>(p3
.getY());
180 pRenderModule
->pushVertex(vertex
);
183 return !(pRenderModule
->isError());
186 bool Surface::drawRectangularArea(
188 const ::basegfx::B2DPoint
& rPos
,
189 const ::basegfx::B2DRectangle
& rArea
,
190 const ::basegfx::B2DHomMatrix
& rTransform
)
192 if( rArea
.isEmpty() )
193 return true; // immediate exit for empty area
195 std::shared_ptr
<IRenderModule
> pRenderModule(mpPageManager
->getRenderModule());
197 RenderModuleGuard
aGuard( pRenderModule
);
201 // these positions are relative to the texture
202 ::basegfx::B2IPoint
aPos1(
203 ::basegfx::fround(rArea
.getMinimum().getX()),
204 ::basegfx::fround(rArea
.getMinimum().getY()));
205 ::basegfx::B2IPoint
aPos2(
206 ::basegfx::fround(rArea
.getMaximum().getX()),
207 ::basegfx::fround(rArea
.getMaximum().getY()) );
209 // clip the positions to the area this surface covers
210 aPos1
.setX(std::max(aPos1
.getX(), maSourceOffset
.getX()));
211 aPos1
.setY(std::max(aPos1
.getY(), maSourceOffset
.getY()));
212 aPos2
.setX(std::min(aPos2
.getX(), maSourceOffset
.getX() + maSize
.getWidth()));
213 aPos2
.setY(std::min(aPos2
.getY(), maSourceOffset
.getY() + maSize
.getHeight()));
215 // if the resulting area is empty, return immediately
216 ::basegfx::B2IVector
aSize(aPos2
- aPos1
);
217 if(aSize
.getX() <= 0 || aSize
.getY() <= 0)
220 ::basegfx::B2IPoint aDestOffset
;
222 aDestOffset
= mpFragment
->getPos();
224 // convert size to normalized device coordinates
225 const ::basegfx::B2DRectangle
aUV(
226 getUVCoords(aPos1
- maSourceOffset
+ aDestOffset
,
227 basegfx::B2ISize(aSize
.getX(), aSize
.getY())) );
228 const double u1(aUV
.getMinX());
229 const double v1(aUV
.getMinY());
230 const double u2(aUV
.getMaxX());
231 const double v2(aUV
.getMaxY());
233 // concatenate transforms
234 // 1) offset of surface subarea
235 // 2) surface transform
236 // 3) translation to output position [rPos]
237 basegfx::B2DHomMatrix
aTransform(basegfx::utils::createTranslateB2DHomMatrix(aPos1
.getX(), aPos1
.getY()));
238 aTransform
= aTransform
* rTransform
;
239 aTransform
.translate(rPos
);
243 ######################################
244 ######################################
245 ######################################
254 ------|-----O------|------>X
262 ######################################
263 ######################################
264 ######################################
267 const ::basegfx::B2DPoint
p0(aTransform
* ::basegfx::B2DPoint(aSize
.getX(),aSize
.getY()));
268 const ::basegfx::B2DPoint
p1(aTransform
* ::basegfx::B2DPoint(0.0, aSize
.getY()));
269 const ::basegfx::B2DPoint
p2(aTransform
* ::basegfx::B2DPoint(0.0, 0.0));
270 const ::basegfx::B2DPoint
p3(aTransform
* ::basegfx::B2DPoint(aSize
.getX(),0.0));
272 canvas::Vertex vertex
;
276 vertex
.a
= static_cast<float>(fAlpha
);
280 pRenderModule
->beginPrimitive( canvas::IRenderModule::PrimitiveType::Quad
);
282 // issue an endPrimitive() when leaving the scope
283 const ::comphelper::ScopeGuard
aScopeGuard(
284 [&pRenderModule
]() mutable { pRenderModule
->endPrimitive(); } );
286 vertex
.u
=static_cast<float>(u2
); vertex
.v
=static_cast<float>(v2
);
287 vertex
.x
=static_cast<float>(p0
.getX()); vertex
.y
=static_cast<float>(p0
.getY());
288 pRenderModule
->pushVertex(vertex
);
290 vertex
.u
=static_cast<float>(u1
); vertex
.v
=static_cast<float>(v2
);
291 vertex
.x
=static_cast<float>(p1
.getX()); vertex
.y
=static_cast<float>(p1
.getY());
292 pRenderModule
->pushVertex(vertex
);
294 vertex
.u
=static_cast<float>(u1
); vertex
.v
=static_cast<float>(v1
);
295 vertex
.x
=static_cast<float>(p2
.getX()); vertex
.y
=static_cast<float>(p2
.getY());
296 pRenderModule
->pushVertex(vertex
);
298 vertex
.u
=static_cast<float>(u2
); vertex
.v
=static_cast<float>(v1
);
299 vertex
.x
=static_cast<float>(p3
.getX()); vertex
.y
=static_cast<float>(p3
.getY());
300 pRenderModule
->pushVertex(vertex
);
303 return !(pRenderModule
->isError());
306 bool Surface::drawWithClip( double fAlpha
,
307 const ::basegfx::B2DPoint
& rPos
,
308 const ::basegfx::B2DPolygon
& rClipPoly
,
309 const ::basegfx::B2DHomMatrix
& rTransform
)
311 std::shared_ptr
<IRenderModule
> pRenderModule(mpPageManager
->getRenderModule());
313 RenderModuleGuard
aGuard( pRenderModule
);
317 // untransformed surface rectangle, relative to the whole
318 // image (note: this surface might actually only be a tile of
319 // the whole image, with non-zero maSourceOffset)
320 const double x1(maSourceOffset
.getX());
321 const double y1(maSourceOffset
.getY());
322 const double w(maSize
.getWidth());
323 const double h(maSize
.getHeight());
324 const double x2(x1
+w
);
325 const double y2(y1
+h
);
326 const ::basegfx::B2DRectangle
aSurfaceClipRect(x1
,y1
,x2
,y2
);
328 // concatenate transforms
329 // we use 'fround' here to avoid rounding errors. the vertices will
330 // be transformed by the overall transform and uv coordinates will
331 // be calculated from the result, and this is why we need to use
332 // integer coordinates here...
333 basegfx::B2DHomMatrix aTransform
= rTransform
;
334 aTransform
.translate(rPos
);
337 ######################################
338 ######################################
339 ######################################
348 ------|-----O------|------>X
356 ######################################
357 ######################################
358 ######################################
361 // uv coordinates that map the surface rectangle
362 // to the destination rectangle.
363 const ::basegfx::B2DRectangle
aUV( getUVCoords() );
365 basegfx::B2DPolygon
rTriangleList(basegfx::utils::clipTriangleListOnRange(rClipPoly
,
368 // Push vertices to backend renderer
369 if(const sal_uInt32 nVertexCount
= rTriangleList
.count())
371 canvas::Vertex vertex
;
375 vertex
.a
= static_cast<float>(fAlpha
);
378 pRenderModule
->beginPrimitive( canvas::IRenderModule::PrimitiveType::Triangle
);
380 // issue an endPrimitive() when leaving the scope
381 const ::comphelper::ScopeGuard
aScopeGuard(
382 [&pRenderModule
]() mutable { pRenderModule
->endPrimitive(); } );
384 for(sal_uInt32 nIndex
=0; nIndex
<nVertexCount
; ++nIndex
)
386 const basegfx::B2DPoint
&aPoint
= rTriangleList
.getB2DPoint(nIndex
);
387 basegfx::B2DPoint
aTransformedPoint(aTransform
* aPoint
);
388 const double tu(((aPoint
.getX()-aSurfaceClipRect
.getMinX())*aUV
.getWidth()/w
)+aUV
.getMinX());
389 const double tv(((aPoint
.getY()-aSurfaceClipRect
.getMinY())*aUV
.getHeight()/h
)+aUV
.getMinY());
390 vertex
.u
=static_cast<float>(tu
);
391 vertex
.v
=static_cast<float>(tv
);
392 vertex
.x
=static_cast<float>(aTransformedPoint
.getX());
393 vertex
.y
=static_cast<float>(aTransformedPoint
.getY());
394 pRenderModule
->pushVertex(vertex
);
398 return !(pRenderModule
->isError());
401 void Surface::prepareRendering()
403 mpPageManager
->validatePages();
405 // clients requested to draw from this surface, therefore one
406 // of the above implemented concrete rendering operations
407 // was triggered. we therefore need to ask the pagemanager
408 // to allocate some space for the fragment we're dedicated to.
411 mpFragment
= mpPageManager
->allocateSpace(maSize
);
414 mpFragment
->setColorBuffer(mpColorBuffer
);
415 mpFragment
->setSourceOffset(maSourceOffset
);
421 // now we need to 'select' the fragment, which will in turn
422 // pull information from the image on demand.
423 // in case this fragment is still not located on any of the
424 // available pages ['naked'], we force the page manager to
425 // do it now, no way to defer this any longer...
426 if(!(mpFragment
->select(mbIsDirty
)))
427 mpPageManager
->nakedFragment(mpFragment
);
434 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */