xref: /trunk/main/canvas/source/tools/surface.cxx (revision cdf0e10c4e3984b49a9502b011690b615761d4a3)
1 /*************************************************************************
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * Copyright 2000, 2010 Oracle and/or its affiliates.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * This file is part of OpenOffice.org.
10  *
11  * OpenOffice.org is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License version 3
13  * only, as published by the Free Software Foundation.
14  *
15  * OpenOffice.org is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Lesser General Public License version 3 for more details
19  * (a copy is included in the LICENSE file that accompanied this code).
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * version 3 along with OpenOffice.org.  If not, see
23  * <http://www.openoffice.org/license.html>
24  * for a copy of the LGPLv3 License.
25  *
26  ************************************************************************/
27 
28 // MARKER(update_precomp.py): autogen include statement, do not remove
29 #include "precompiled_canvas.hxx"
30 
31 #include "surface.hxx"
32 #include <basegfx/polygon/b2dpolygonclipper.hxx>
33 #include <basegfx/matrix/b2dhommatrixtools.hxx>
34 #include <comphelper/scopeguard.hxx>
35 #include <boost/bind.hpp>
36 
37 namespace canvas
38 {
39 
40     //////////////////////////////////////////////////////////////////////////////////
41     // Surface::Surface
42     //////////////////////////////////////////////////////////////////////////////////
43 
44     Surface::Surface( const PageManagerSharedPtr&  rPageManager,
45                       const IColorBufferSharedPtr& rColorBuffer,
46                       const ::basegfx::B2IPoint&   rPos,
47                       const ::basegfx::B2ISize&    rSize ) :
48         mpColorBuffer(rColorBuffer),
49         mpPageManager(rPageManager),
50         mpFragment(),
51         maSourceOffset(rPos),
52         maSize(rSize),
53         mbIsDirty(true)
54     {
55     }
56 
57     //////////////////////////////////////////////////////////////////////////////////
58     // Surface::~Surface
59     //////////////////////////////////////////////////////////////////////////////////
60 
61     Surface::~Surface()
62     {
63         if(mpFragment)
64             mpPageManager->free(mpFragment);
65     }
66 
67     //////////////////////////////////////////////////////////////////////////////////
68     // Surface::getUVCoords
69     //////////////////////////////////////////////////////////////////////////////////
70 
71     void Surface::setColorBufferDirty()
72     {
73         mbIsDirty=true;
74     }
75 
76     //////////////////////////////////////////////////////////////////////////////////
77     // Surface::getUVCoords
78     //////////////////////////////////////////////////////////////////////////////////
79 
80     basegfx::B2DRectangle Surface::getUVCoords() const
81     {
82         ::basegfx::B2ISize aPageSize(mpPageManager->getPageSize());
83         ::basegfx::B2IPoint aDestOffset;
84         if( mpFragment )
85             aDestOffset = mpFragment->getPos();
86 
87         const double pw( aPageSize.getX() );
88         const double ph( aPageSize.getY() );
89         const double ox( aDestOffset.getX() );
90         const double oy( aDestOffset.getY() );
91         const double sx( maSize.getX() );
92         const double sy( maSize.getY() );
93 
94         return ::basegfx::B2DRectangle( ox/pw,
95                                         oy/ph,
96                                         (ox+sx)/pw,
97                                         (oy+sy)/ph );
98     }
99 
100     //////////////////////////////////////////////////////////////////////////////////
101     // Surface::getUVCoords
102     //////////////////////////////////////////////////////////////////////////////////
103 
104     basegfx::B2DRectangle Surface::getUVCoords( const ::basegfx::B2IPoint& rPos,
105                                                 const ::basegfx::B2ISize&  rSize ) const
106     {
107         ::basegfx::B2ISize aPageSize(mpPageManager->getPageSize());
108 
109         const double pw( aPageSize.getX() );
110         const double ph( aPageSize.getY() );
111         const double ox( rPos.getX() );
112         const double oy( rPos.getY() );
113         const double sx( rSize.getX() );
114         const double sy( rSize.getY() );
115 
116         return ::basegfx::B2DRectangle( ox/pw,
117                                         oy/ph,
118                                         (ox+sx)/pw,
119                                         (oy+sy)/ph );
120     }
121 
122     //////////////////////////////////////////////////////////////////////////////////
123     // Surface::draw
124     //////////////////////////////////////////////////////////////////////////////////
125 
126     bool Surface::draw( double                          fAlpha,
127                         const ::basegfx::B2DPoint&      rPos,
128                         const ::basegfx::B2DHomMatrix&  rTransform )
129     {
130         IRenderModuleSharedPtr pRenderModule(mpPageManager->getRenderModule());
131 
132         RenderModuleGuard aGuard( pRenderModule );
133 
134         prepareRendering();
135 
136         // convert size to normalized device coordinates
137         const ::basegfx::B2DRectangle& rUV( getUVCoords() );
138 
139         const double u1(rUV.getMinX());
140         const double v1(rUV.getMinY());
141         const double u2(rUV.getMaxX());
142         const double v2(rUV.getMaxY());
143 
144         // concat transforms
145         // 1) offset of surface subarea
146         // 2) surface transform
147         // 3) translation to output position [rPos]
148         // 4) scale to normalized device coordinates
149         // 5) flip y-axis
150         // 6) translate to account for viewport transform
151         basegfx::B2DHomMatrix aTransform(basegfx::tools::createTranslateB2DHomMatrix(
152             maSourceOffset.getX(), maSourceOffset.getY()));
153         aTransform = aTransform * rTransform;
154         aTransform.translate(::basegfx::fround(rPos.getX()),
155                              ::basegfx::fround(rPos.getY()));
156 
157         /*
158             ######################################
159             ######################################
160             ######################################
161 
162                            Y
163                            ^+1
164                            |
165                    2       |       3
166                      x------------x
167                      |     |      |
168                      |     |      |
169                ------|-----O------|------>X
170                -1    |     |      |     +1
171                      |     |      |
172                      x------------x
173                     1      |       0
174                            |
175                            |-1
176 
177             ######################################
178             ######################################
179             ######################################
180         */
181 
182         const ::basegfx::B2DPoint& p0(aTransform * ::basegfx::B2DPoint(maSize.getX(),maSize.getY()));
183         const ::basegfx::B2DPoint& p1(aTransform * ::basegfx::B2DPoint(0.0,maSize.getY()));
184         const ::basegfx::B2DPoint& p2(aTransform * ::basegfx::B2DPoint(0.0,0.0));
185         const ::basegfx::B2DPoint& p3(aTransform * ::basegfx::B2DPoint(maSize.getX(),0.0));
186 
187         canvas::Vertex vertex;
188         vertex.r = 1.0f;
189         vertex.g = 1.0f;
190         vertex.b = 1.0f;
191         vertex.a = static_cast<float>(fAlpha);
192         vertex.z = 0.0f;
193 
194         {
195             pRenderModule->beginPrimitive( canvas::IRenderModule::PRIMITIVE_TYPE_QUAD );
196 
197             // issue an endPrimitive() when leaving the scope
198             const ::comphelper::ScopeGuard aScopeGuard(
199                 boost::bind( &::canvas::IRenderModule::endPrimitive,
200                              ::boost::ref(pRenderModule) ) );
201 
202             vertex.u=static_cast<float>(u2); vertex.v=static_cast<float>(v2);
203             vertex.x=static_cast<float>(p0.getX()); vertex.y=static_cast<float>(p0.getY());
204             pRenderModule->pushVertex(vertex);
205 
206             vertex.u=static_cast<float>(u1); vertex.v=static_cast<float>(v2);
207             vertex.x=static_cast<float>(p1.getX()); vertex.y=static_cast<float>(p1.getY());
208             pRenderModule->pushVertex(vertex);
209 
210             vertex.u=static_cast<float>(u1); vertex.v=static_cast<float>(v1);
211             vertex.x=static_cast<float>(p2.getX()); vertex.y=static_cast<float>(p2.getY());
212             pRenderModule->pushVertex(vertex);
213 
214             vertex.u=static_cast<float>(u2); vertex.v=static_cast<float>(v1);
215             vertex.x=static_cast<float>(p3.getX()); vertex.y=static_cast<float>(p3.getY());
216             pRenderModule->pushVertex(vertex);
217         }
218 
219         return !(pRenderModule->isError());
220     }
221 
222     //////////////////////////////////////////////////////////////////////////////////
223     // Surface::drawRectangularArea
224     //////////////////////////////////////////////////////////////////////////////////
225 
226     bool Surface::drawRectangularArea(
227                         double                         fAlpha,
228                         const ::basegfx::B2DPoint&     rPos,
229                         const ::basegfx::B2DRectangle& rArea,
230                         const ::basegfx::B2DHomMatrix& rTransform )
231     {
232         if( rArea.isEmpty() )
233             return true; // immediate exit for empty area
234 
235         IRenderModuleSharedPtr pRenderModule(mpPageManager->getRenderModule());
236 
237         RenderModuleGuard aGuard( pRenderModule );
238 
239         prepareRendering();
240 
241         // these positions are relative to the texture
242         ::basegfx::B2IPoint aPos1(
243             ::basegfx::fround(rArea.getMinimum().getX()),
244             ::basegfx::fround(rArea.getMinimum().getY()));
245         ::basegfx::B2IPoint aPos2(
246             ::basegfx::fround(rArea.getMaximum().getX()),
247             ::basegfx::fround(rArea.getMaximum().getY()) );
248 
249         // clip the positions to the area this surface covers
250         aPos1.setX(::std::max(aPos1.getX(),maSourceOffset.getX()));
251         aPos1.setY(::std::max(aPos1.getY(),maSourceOffset.getY()));
252         aPos2.setX(::std::min(aPos2.getX(),maSourceOffset.getX()+maSize.getX()));
253         aPos2.setY(::std::min(aPos2.getY(),maSourceOffset.getY()+maSize.getY()));
254 
255         // if the resulting area is empty, return immediately
256         ::basegfx::B2IVector aSize(aPos2 - aPos1);
257         if(aSize.getX() <= 0 || aSize.getY() <= 0)
258             return true;
259 
260         ::basegfx::B2IPoint aDestOffset;
261         if( mpFragment )
262             aDestOffset = mpFragment->getPos();
263 
264         // convert size to normalized device coordinates
265         const ::basegfx::B2DRectangle& rUV(
266             getUVCoords(aPos1 - maSourceOffset + aDestOffset,
267                         aSize) );
268         const double u1(rUV.getMinX());
269         const double v1(rUV.getMinY());
270         const double u2(rUV.getMaxX());
271         const double v2(rUV.getMaxY());
272 
273         // concatenate transforms
274         // 1) offset of surface subarea
275         // 2) surface transform
276         // 3) translation to output position [rPos]
277         basegfx::B2DHomMatrix aTransform(basegfx::tools::createTranslateB2DHomMatrix(aPos1.getX(), aPos1.getY()));
278         aTransform = aTransform * rTransform;
279         aTransform.translate(::basegfx::fround(rPos.getX()),
280                              ::basegfx::fround(rPos.getY()));
281 
282 
283         /*
284             ######################################
285             ######################################
286             ######################################
287 
288                            Y
289                            ^+1
290                            |
291                    2       |       3
292                      x------------x
293                      |     |      |
294                      |     |      |
295                ------|-----O------|------>X
296                -1    |     |      |     +1
297                      |     |      |
298                      x------------x
299                     1      |       0
300                            |
301                            |-1
302 
303             ######################################
304             ######################################
305             ######################################
306         */
307 
308         const ::basegfx::B2DPoint& p0(aTransform * ::basegfx::B2DPoint(aSize.getX(),aSize.getY()));
309         const ::basegfx::B2DPoint& p1(aTransform * ::basegfx::B2DPoint(0.0,         aSize.getY()));
310         const ::basegfx::B2DPoint& p2(aTransform * ::basegfx::B2DPoint(0.0,         0.0));
311         const ::basegfx::B2DPoint& p3(aTransform * ::basegfx::B2DPoint(aSize.getX(),0.0));
312 
313         canvas::Vertex vertex;
314         vertex.r = 1.0f;
315         vertex.g = 1.0f;
316         vertex.b = 1.0f;
317         vertex.a = static_cast<float>(fAlpha);
318         vertex.z = 0.0f;
319 
320         {
321             pRenderModule->beginPrimitive( canvas::IRenderModule::PRIMITIVE_TYPE_QUAD );
322 
323             // issue an endPrimitive() when leaving the scope
324             const ::comphelper::ScopeGuard aScopeGuard(
325                 boost::bind( &::canvas::IRenderModule::endPrimitive,
326                              ::boost::ref(pRenderModule) ) );
327 
328             vertex.u=static_cast<float>(u2); vertex.v=static_cast<float>(v2);
329             vertex.x=static_cast<float>(p0.getX()); vertex.y=static_cast<float>(p0.getY());
330             pRenderModule->pushVertex(vertex);
331 
332             vertex.u=static_cast<float>(u1); vertex.v=static_cast<float>(v2);
333             vertex.x=static_cast<float>(p1.getX()); vertex.y=static_cast<float>(p1.getY());
334             pRenderModule->pushVertex(vertex);
335 
336             vertex.u=static_cast<float>(u1); vertex.v=static_cast<float>(v1);
337             vertex.x=static_cast<float>(p2.getX()); vertex.y=static_cast<float>(p2.getY());
338             pRenderModule->pushVertex(vertex);
339 
340             vertex.u=static_cast<float>(u2); vertex.v=static_cast<float>(v1);
341             vertex.x=static_cast<float>(p3.getX()); vertex.y=static_cast<float>(p3.getY());
342             pRenderModule->pushVertex(vertex);
343         }
344 
345         return !(pRenderModule->isError());
346     }
347 
348     //////////////////////////////////////////////////////////////////////////////////
349     // Surface::drawWithClip
350     //////////////////////////////////////////////////////////////////////////////////
351 
352     bool Surface::drawWithClip( double                          fAlpha,
353                                 const ::basegfx::B2DPoint&      rPos,
354                                 const ::basegfx::B2DPolygon&    rClipPoly,
355                                 const ::basegfx::B2DHomMatrix&  rTransform )
356     {
357         IRenderModuleSharedPtr pRenderModule(mpPageManager->getRenderModule());
358 
359         RenderModuleGuard aGuard( pRenderModule );
360 
361         prepareRendering();
362 
363         // untransformed surface rectangle, relative to the whole
364         // image (note: this surface might actually only be a tile of
365         // the whole image, with non-zero maSourceOffset)
366         const double x1(maSourceOffset.getX());
367         const double y1(maSourceOffset.getY());
368         const double w(maSize.getX());
369         const double h(maSize.getY());
370         const double x2(x1+w);
371         const double y2(y1+h);
372         const ::basegfx::B2DRectangle aSurfaceClipRect(x1,y1,x2,y2);
373 
374         // concatenate transforms
375         // we use 'fround' here to avoid rounding errors. the vertices will
376         // be transformed by the overall transform and uv coordinates will
377         // be calculated from the result, and this is why we need to use
378         // integer coordinates here...
379         basegfx::B2DHomMatrix aTransform;
380         aTransform = aTransform * rTransform;
381         aTransform.translate(::basegfx::fround(rPos.getX()),
382                              ::basegfx::fround(rPos.getY()));
383 
384         /*
385             ######################################
386             ######################################
387             ######################################
388 
389                            Y
390                            ^+1
391                            |
392                    2       |       3
393                      x------------x
394                      |     |      |
395                      |     |      |
396                ------|-----O------|------>X
397                -1    |     |      |     +1
398                      |     |      |
399                      x------------x
400                     1      |       0
401                            |
402                            |-1
403 
404             ######################################
405             ######################################
406             ######################################
407         */
408 
409         // uv coordinates that map the surface rectangle
410         // to the destination rectangle.
411         const ::basegfx::B2DRectangle& rUV( getUVCoords() );
412 
413         basegfx::B2DPolygon rTriangleList(basegfx::tools::clipTriangleListOnRange(rClipPoly,
414                                                                                   aSurfaceClipRect));
415 
416         // Push vertices to backend renderer
417         if(const sal_uInt32 nVertexCount = rTriangleList.count())
418         {
419             canvas::Vertex vertex;
420             vertex.r = 1.0f;
421             vertex.g = 1.0f;
422             vertex.b = 1.0f;
423             vertex.a = static_cast<float>(fAlpha);
424             vertex.z = 0.0f;
425 
426 #if defined(TRIANGLE_LOG) && defined(DBG_UTIL)
427             OSL_TRACE( "Surface::draw(): numvertices %d numtriangles %d\n",
428                         nVertexCount,
429                         nVertexCount/3 );
430 #endif
431 
432             pRenderModule->beginPrimitive( canvas::IRenderModule::PRIMITIVE_TYPE_TRIANGLE );
433 
434             // issue an endPrimitive() when leaving the scope
435             const ::comphelper::ScopeGuard aScopeGuard(
436                 boost::bind( &::canvas::IRenderModule::endPrimitive,
437                                 ::boost::ref(pRenderModule) ) );
438 
439             for(sal_uInt32 nIndex=0; nIndex<nVertexCount; ++nIndex)
440             {
441                 const basegfx::B2DPoint &aPoint = rTriangleList.getB2DPoint(nIndex);
442                 basegfx::B2DPoint aTransformedPoint(aTransform * aPoint);
443                 const double tu(((aPoint.getX()-aSurfaceClipRect.getMinX())*rUV.getWidth()/w)+rUV.getMinX());
444                 const double tv(((aPoint.getY()-aSurfaceClipRect.getMinY())*rUV.getHeight()/h)+rUV.getMinY());
445                 vertex.u=static_cast<float>(tu);
446                 vertex.v=static_cast<float>(tv);
447                 vertex.x=static_cast<float>(aTransformedPoint.getX());
448                 vertex.y=static_cast<float>(aTransformedPoint.getY());
449                 pRenderModule->pushVertex(vertex);
450             }
451         }
452 
453         return !(pRenderModule->isError());
454     }
455 
456     //////////////////////////////////////////////////////////////////////////////////
457     // Surface::prepareRendering
458     //////////////////////////////////////////////////////////////////////////////////
459 
460     void Surface::prepareRendering()
461     {
462         mpPageManager->validatePages();
463 
464         // clients requested to draw from this surface, therefore one
465         // of the above implemented concrete rendering operations
466         // was triggered. we therefore need to ask the pagemanager
467         // to allocate some space for the fragment we're dedicated to.
468         if(!(mpFragment))
469         {
470             mpFragment = mpPageManager->allocateSpace(maSize);
471             if( mpFragment )
472             {
473                 mpFragment->setColorBuffer(mpColorBuffer);
474                 mpFragment->setSourceOffset(maSourceOffset);
475             }
476         }
477 
478         if( mpFragment )
479         {
480             // now we need to 'select' the fragment, which will in turn
481             // pull informations from the image on demand.
482             // in case this fragment is still not located on any of the
483             // available pages ['naked'], we force the page manager to
484             // do it now, no way to defer this any longer...
485             if(!(mpFragment->select(mbIsDirty)))
486                 mpPageManager->nakedFragment(mpFragment);
487 
488         }
489         mbIsDirty=false;
490     }
491 
492     //////////////////////////////////////////////////////////////////////////////////
493     // End of file
494     //////////////////////////////////////////////////////////////////////////////////
495 }
496 
497