xref: /trunk/main/canvas/source/tools/surface.cxx (revision 25ea7f45)
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