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