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