Line data Source code
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 : * This file incorporates work covered by the following license notice:
10 : *
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 .
18 : */
19 :
20 :
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>
26 :
27 : namespace canvas
28 : {
29 :
30 :
31 : // Surface::Surface
32 :
33 :
34 0 : 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 0 : mbIsDirty(true)
44 : {
45 0 : }
46 :
47 :
48 : // Surface::~Surface
49 :
50 :
51 0 : Surface::~Surface()
52 : {
53 0 : if(mpFragment)
54 0 : mpPageManager->free(mpFragment);
55 0 : }
56 :
57 :
58 : // Surface::getUVCoords
59 :
60 :
61 0 : void Surface::setColorBufferDirty()
62 : {
63 0 : mbIsDirty=true;
64 0 : }
65 :
66 :
67 : // Surface::getUVCoords
68 :
69 :
70 0 : basegfx::B2DRectangle Surface::getUVCoords() const
71 : {
72 0 : ::basegfx::B2ISize aPageSize(mpPageManager->getPageSize());
73 0 : ::basegfx::B2IPoint aDestOffset;
74 0 : if( mpFragment )
75 0 : aDestOffset = mpFragment->getPos();
76 :
77 0 : const double pw( aPageSize.getX() );
78 0 : const double ph( aPageSize.getY() );
79 0 : const double ox( aDestOffset.getX() );
80 0 : const double oy( aDestOffset.getY() );
81 0 : const double sx( maSize.getX() );
82 0 : const double sy( maSize.getY() );
83 :
84 : return ::basegfx::B2DRectangle( ox/pw,
85 : oy/ph,
86 0 : (ox+sx)/pw,
87 0 : (oy+sy)/ph );
88 : }
89 :
90 :
91 : // Surface::getUVCoords
92 :
93 :
94 0 : basegfx::B2DRectangle Surface::getUVCoords( const ::basegfx::B2IPoint& rPos,
95 : const ::basegfx::B2ISize& rSize ) const
96 : {
97 0 : ::basegfx::B2ISize aPageSize(mpPageManager->getPageSize());
98 :
99 0 : const double pw( aPageSize.getX() );
100 0 : const double ph( aPageSize.getY() );
101 0 : const double ox( rPos.getX() );
102 0 : const double oy( rPos.getY() );
103 0 : const double sx( rSize.getX() );
104 0 : const double sy( rSize.getY() );
105 :
106 : return ::basegfx::B2DRectangle( ox/pw,
107 : oy/ph,
108 0 : (ox+sx)/pw,
109 0 : (oy+sy)/ph );
110 : }
111 :
112 :
113 : // Surface::draw
114 :
115 :
116 0 : bool Surface::draw( double fAlpha,
117 : const ::basegfx::B2DPoint& rPos,
118 : const ::basegfx::B2DHomMatrix& rTransform )
119 : {
120 0 : IRenderModuleSharedPtr pRenderModule(mpPageManager->getRenderModule());
121 :
122 0 : RenderModuleGuard aGuard( pRenderModule );
123 :
124 0 : prepareRendering();
125 :
126 : // convert size to normalized device coordinates
127 0 : const ::basegfx::B2DRectangle& rUV( getUVCoords() );
128 :
129 0 : const double u1(rUV.getMinX());
130 0 : const double v1(rUV.getMinY());
131 0 : const double u2(rUV.getMaxX());
132 0 : const double v2(rUV.getMaxY());
133 :
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 0 : maSourceOffset.getX(), maSourceOffset.getY()));
143 0 : aTransform = aTransform * rTransform;
144 0 : aTransform.translate(::basegfx::fround(rPos.getX()),
145 0 : ::basegfx::fround(rPos.getY()));
146 :
147 : /*
148 : ######################################
149 : ######################################
150 : ######################################
151 :
152 : Y
153 : ^+1
154 : |
155 : 2 | 3
156 : x------------x
157 : | | |
158 : | | |
159 : ------|-----O------|------>X
160 : -1 | | | +1
161 : | | |
162 : x------------x
163 : 1 | 0
164 : |
165 : |-1
166 :
167 : ######################################
168 : ######################################
169 : ######################################
170 : */
171 :
172 0 : const ::basegfx::B2DPoint& p0(aTransform * ::basegfx::B2DPoint(maSize.getX(),maSize.getY()));
173 0 : const ::basegfx::B2DPoint& p1(aTransform * ::basegfx::B2DPoint(0.0,maSize.getY()));
174 0 : const ::basegfx::B2DPoint& p2(aTransform * ::basegfx::B2DPoint(0.0,0.0));
175 0 : const ::basegfx::B2DPoint& p3(aTransform * ::basegfx::B2DPoint(maSize.getX(),0.0));
176 :
177 : canvas::Vertex vertex;
178 0 : vertex.r = 1.0f;
179 0 : vertex.g = 1.0f;
180 0 : vertex.b = 1.0f;
181 0 : vertex.a = static_cast<float>(fAlpha);
182 0 : vertex.z = 0.0f;
183 :
184 : {
185 0 : pRenderModule->beginPrimitive( canvas::IRenderModule::PRIMITIVE_TYPE_QUAD );
186 :
187 : // issue an endPrimitive() when leaving the scope
188 : const ::comphelper::ScopeGuard aScopeGuard(
189 : boost::bind( &::canvas::IRenderModule::endPrimitive,
190 0 : ::boost::ref(pRenderModule) ) );
191 :
192 0 : vertex.u=static_cast<float>(u2); vertex.v=static_cast<float>(v2);
193 0 : vertex.x=static_cast<float>(p0.getX()); vertex.y=static_cast<float>(p0.getY());
194 0 : pRenderModule->pushVertex(vertex);
195 :
196 0 : vertex.u=static_cast<float>(u1); vertex.v=static_cast<float>(v2);
197 0 : vertex.x=static_cast<float>(p1.getX()); vertex.y=static_cast<float>(p1.getY());
198 0 : pRenderModule->pushVertex(vertex);
199 :
200 0 : vertex.u=static_cast<float>(u1); vertex.v=static_cast<float>(v1);
201 0 : vertex.x=static_cast<float>(p2.getX()); vertex.y=static_cast<float>(p2.getY());
202 0 : pRenderModule->pushVertex(vertex);
203 :
204 0 : vertex.u=static_cast<float>(u2); vertex.v=static_cast<float>(v1);
205 0 : vertex.x=static_cast<float>(p3.getX()); vertex.y=static_cast<float>(p3.getY());
206 0 : pRenderModule->pushVertex(vertex);
207 : }
208 :
209 0 : return !(pRenderModule->isError());
210 : }
211 :
212 :
213 : // Surface::drawRectangularArea
214 :
215 :
216 0 : bool Surface::drawRectangularArea(
217 : double fAlpha,
218 : const ::basegfx::B2DPoint& rPos,
219 : const ::basegfx::B2DRectangle& rArea,
220 : const ::basegfx::B2DHomMatrix& rTransform )
221 : {
222 0 : if( rArea.isEmpty() )
223 0 : return true; // immediate exit for empty area
224 :
225 0 : IRenderModuleSharedPtr pRenderModule(mpPageManager->getRenderModule());
226 :
227 0 : RenderModuleGuard aGuard( pRenderModule );
228 :
229 0 : prepareRendering();
230 :
231 : // these positions are relative to the texture
232 : ::basegfx::B2IPoint aPos1(
233 : ::basegfx::fround(rArea.getMinimum().getX()),
234 0 : ::basegfx::fround(rArea.getMinimum().getY()));
235 : ::basegfx::B2IPoint aPos2(
236 : ::basegfx::fround(rArea.getMaximum().getX()),
237 0 : ::basegfx::fround(rArea.getMaximum().getY()) );
238 :
239 : // clip the positions to the area this surface covers
240 0 : aPos1.setX(::std::max(aPos1.getX(),maSourceOffset.getX()));
241 0 : aPos1.setY(::std::max(aPos1.getY(),maSourceOffset.getY()));
242 0 : aPos2.setX(::std::min(aPos2.getX(),maSourceOffset.getX()+maSize.getX()));
243 0 : aPos2.setY(::std::min(aPos2.getY(),maSourceOffset.getY()+maSize.getY()));
244 :
245 : // if the resulting area is empty, return immediately
246 0 : ::basegfx::B2IVector aSize(aPos2 - aPos1);
247 0 : if(aSize.getX() <= 0 || aSize.getY() <= 0)
248 0 : return true;
249 :
250 0 : ::basegfx::B2IPoint aDestOffset;
251 0 : if( mpFragment )
252 0 : aDestOffset = mpFragment->getPos();
253 :
254 : // convert size to normalized device coordinates
255 : const ::basegfx::B2DRectangle& rUV(
256 0 : getUVCoords(aPos1 - maSourceOffset + aDestOffset,
257 0 : aSize) );
258 0 : const double u1(rUV.getMinX());
259 0 : const double v1(rUV.getMinY());
260 0 : const double u2(rUV.getMaxX());
261 0 : const double v2(rUV.getMaxY());
262 :
263 : // concatenate transforms
264 : // 1) offset of surface subarea
265 : // 2) surface transform
266 : // 3) translation to output position [rPos]
267 0 : basegfx::B2DHomMatrix aTransform(basegfx::tools::createTranslateB2DHomMatrix(aPos1.getX(), aPos1.getY()));
268 0 : aTransform = aTransform * rTransform;
269 0 : aTransform.translate(::basegfx::fround(rPos.getX()),
270 0 : ::basegfx::fround(rPos.getY()));
271 :
272 :
273 : /*
274 : ######################################
275 : ######################################
276 : ######################################
277 :
278 : Y
279 : ^+1
280 : |
281 : 2 | 3
282 : x------------x
283 : | | |
284 : | | |
285 : ------|-----O------|------>X
286 : -1 | | | +1
287 : | | |
288 : x------------x
289 : 1 | 0
290 : |
291 : |-1
292 :
293 : ######################################
294 : ######################################
295 : ######################################
296 : */
297 :
298 0 : const ::basegfx::B2DPoint& p0(aTransform * ::basegfx::B2DPoint(aSize.getX(),aSize.getY()));
299 0 : const ::basegfx::B2DPoint& p1(aTransform * ::basegfx::B2DPoint(0.0, aSize.getY()));
300 0 : const ::basegfx::B2DPoint& p2(aTransform * ::basegfx::B2DPoint(0.0, 0.0));
301 0 : const ::basegfx::B2DPoint& p3(aTransform * ::basegfx::B2DPoint(aSize.getX(),0.0));
302 :
303 : canvas::Vertex vertex;
304 0 : vertex.r = 1.0f;
305 0 : vertex.g = 1.0f;
306 0 : vertex.b = 1.0f;
307 0 : vertex.a = static_cast<float>(fAlpha);
308 0 : vertex.z = 0.0f;
309 :
310 : {
311 0 : pRenderModule->beginPrimitive( canvas::IRenderModule::PRIMITIVE_TYPE_QUAD );
312 :
313 : // issue an endPrimitive() when leaving the scope
314 : const ::comphelper::ScopeGuard aScopeGuard(
315 : boost::bind( &::canvas::IRenderModule::endPrimitive,
316 0 : ::boost::ref(pRenderModule) ) );
317 :
318 0 : vertex.u=static_cast<float>(u2); vertex.v=static_cast<float>(v2);
319 0 : vertex.x=static_cast<float>(p0.getX()); vertex.y=static_cast<float>(p0.getY());
320 0 : pRenderModule->pushVertex(vertex);
321 :
322 0 : vertex.u=static_cast<float>(u1); vertex.v=static_cast<float>(v2);
323 0 : vertex.x=static_cast<float>(p1.getX()); vertex.y=static_cast<float>(p1.getY());
324 0 : pRenderModule->pushVertex(vertex);
325 :
326 0 : vertex.u=static_cast<float>(u1); vertex.v=static_cast<float>(v1);
327 0 : vertex.x=static_cast<float>(p2.getX()); vertex.y=static_cast<float>(p2.getY());
328 0 : pRenderModule->pushVertex(vertex);
329 :
330 0 : vertex.u=static_cast<float>(u2); vertex.v=static_cast<float>(v1);
331 0 : vertex.x=static_cast<float>(p3.getX()); vertex.y=static_cast<float>(p3.getY());
332 0 : pRenderModule->pushVertex(vertex);
333 : }
334 :
335 0 : return !(pRenderModule->isError());
336 : }
337 :
338 :
339 : // Surface::drawWithClip
340 :
341 :
342 0 : bool Surface::drawWithClip( double fAlpha,
343 : const ::basegfx::B2DPoint& rPos,
344 : const ::basegfx::B2DPolygon& rClipPoly,
345 : const ::basegfx::B2DHomMatrix& rTransform )
346 : {
347 0 : IRenderModuleSharedPtr pRenderModule(mpPageManager->getRenderModule());
348 :
349 0 : RenderModuleGuard aGuard( pRenderModule );
350 :
351 0 : prepareRendering();
352 :
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 0 : const double x1(maSourceOffset.getX());
357 0 : const double y1(maSourceOffset.getY());
358 0 : const double w(maSize.getX());
359 0 : const double h(maSize.getY());
360 0 : const double x2(x1+w);
361 0 : const double y2(y1+h);
362 0 : const ::basegfx::B2DRectangle aSurfaceClipRect(x1,y1,x2,y2);
363 :
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 0 : basegfx::B2DHomMatrix aTransform;
370 0 : aTransform = aTransform * rTransform;
371 0 : aTransform.translate(::basegfx::fround(rPos.getX()),
372 0 : ::basegfx::fround(rPos.getY()));
373 :
374 : /*
375 : ######################################
376 : ######################################
377 : ######################################
378 :
379 : Y
380 : ^+1
381 : |
382 : 2 | 3
383 : x------------x
384 : | | |
385 : | | |
386 : ------|-----O------|------>X
387 : -1 | | | +1
388 : | | |
389 : x------------x
390 : 1 | 0
391 : |
392 : |-1
393 :
394 : ######################################
395 : ######################################
396 : ######################################
397 : */
398 :
399 : // uv coordinates that map the surface rectangle
400 : // to the destination rectangle.
401 0 : const ::basegfx::B2DRectangle& rUV( getUVCoords() );
402 :
403 : basegfx::B2DPolygon rTriangleList(basegfx::tools::clipTriangleListOnRange(rClipPoly,
404 0 : aSurfaceClipRect));
405 :
406 : // Push vertices to backend renderer
407 0 : if(const sal_uInt32 nVertexCount = rTriangleList.count())
408 : {
409 : canvas::Vertex vertex;
410 0 : vertex.r = 1.0f;
411 0 : vertex.g = 1.0f;
412 0 : vertex.b = 1.0f;
413 0 : vertex.a = static_cast<float>(fAlpha);
414 0 : vertex.z = 0.0f;
415 :
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
421 :
422 0 : pRenderModule->beginPrimitive( canvas::IRenderModule::PRIMITIVE_TYPE_TRIANGLE );
423 :
424 : // issue an endPrimitive() when leaving the scope
425 : const ::comphelper::ScopeGuard aScopeGuard(
426 : boost::bind( &::canvas::IRenderModule::endPrimitive,
427 0 : ::boost::ref(pRenderModule) ) );
428 :
429 0 : for(sal_uInt32 nIndex=0; nIndex<nVertexCount; ++nIndex)
430 : {
431 0 : const basegfx::B2DPoint &aPoint = rTriangleList.getB2DPoint(nIndex);
432 0 : basegfx::B2DPoint aTransformedPoint(aTransform * aPoint);
433 0 : const double tu(((aPoint.getX()-aSurfaceClipRect.getMinX())*rUV.getWidth()/w)+rUV.getMinX());
434 0 : const double tv(((aPoint.getY()-aSurfaceClipRect.getMinY())*rUV.getHeight()/h)+rUV.getMinY());
435 0 : vertex.u=static_cast<float>(tu);
436 0 : vertex.v=static_cast<float>(tv);
437 0 : vertex.x=static_cast<float>(aTransformedPoint.getX());
438 0 : vertex.y=static_cast<float>(aTransformedPoint.getY());
439 0 : pRenderModule->pushVertex(vertex);
440 0 : }
441 : }
442 :
443 0 : return !(pRenderModule->isError());
444 : }
445 :
446 :
447 : // Surface::prepareRendering
448 :
449 :
450 0 : void Surface::prepareRendering()
451 : {
452 0 : mpPageManager->validatePages();
453 :
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 0 : if(!(mpFragment))
459 : {
460 0 : mpFragment = mpPageManager->allocateSpace(maSize);
461 0 : if( mpFragment )
462 : {
463 0 : mpFragment->setColorBuffer(mpColorBuffer);
464 0 : mpFragment->setSourceOffset(maSourceOffset);
465 : }
466 : }
467 :
468 0 : if( mpFragment )
469 : {
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 0 : if(!(mpFragment->select(mbIsDirty)))
476 0 : mpPageManager->nakedFragment(mpFragment);
477 :
478 : }
479 0 : mbIsDirty=false;
480 0 : }
481 3 : }
482 :
483 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|