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_basegfx.hxx"
30 
31 #include <basegfx/raster/rasterconvert3d.hxx>
32 #include <basegfx/polygon/b3dpolygon.hxx>
33 #include <basegfx/polygon/b3dpolypolygon.hxx>
34 #include <basegfx/point/b3dpoint.hxx>
35 
36 //////////////////////////////////////////////////////////////////////////////
37 // implementations of the 3D raster converter
38 
39 namespace basegfx
40 {
41     void RasterConverter3D::addArea(const B3DPolygon& rFill, const B3DHomMatrix* pViewToEye)
42     {
43 	    const sal_uInt32 nPointCount(rFill.count());
44 
45 	    for(sal_uInt32 a(0); a < nPointCount; a++)
46 	    {
47 		    addEdge(rFill, a, (a + 1) % nPointCount, pViewToEye);
48 	    }
49     }
50 
51     void RasterConverter3D::addArea(const B3DPolyPolygon& rFill, const B3DHomMatrix* pViewToEye)
52     {
53 	    const sal_uInt32 nPolyCount(rFill.count());
54 
55 	    for(sal_uInt32 a(0); a < nPolyCount; a++)
56 	    {
57 		    addArea(rFill.getB3DPolygon(a), pViewToEye);
58 	    }
59     }
60 
61     RasterConverter3D::RasterConverter3D()
62     :	InterpolatorProvider3D(),
63 	    maLineEntries()
64     {}
65 
66     RasterConverter3D::~RasterConverter3D()
67     {}
68 
69     void RasterConverter3D::rasterconvertB3DArea(sal_Int32 nStartLine, sal_Int32 nStopLine)
70     {
71 	    if(maLineEntries.size())
72 	    {
73 		    OSL_ENSURE(nStopLine >= nStartLine, "nStopLine is bigger than nStartLine (!)");
74 
75             // sort global entries by Y, X once. After this, the vector
76 		    // is seen as frozen. Pointers to it's entries will be used in the following code.
77 		    ::std::sort(maLineEntries.begin(), maLineEntries.end());
78 
79 		    // local parameters
80 		    ::std::vector< RasterConversionLineEntry3D >::iterator aCurrentEntry(maLineEntries.begin());
81 		    ::std::vector< RasterConversionLineEntry3D* > aCurrentLine;
82 		    ::std::vector< RasterConversionLineEntry3D* > aNextLine;
83 		    ::std::vector< RasterConversionLineEntry3D* >::iterator aRasterConversionLineEntry3D;
84 		    sal_uInt32 nPairCount(0);
85 
86 		    // get scanlines first LineNumber as start
87 		    sal_Int32 nLineNumber(::std::max(aCurrentEntry->getY(), nStartLine));
88 
89 		    while((aCurrentLine.size() || aCurrentEntry != maLineEntries.end()) && (nLineNumber < nStopLine))
90 		    {
91 			    // add all entries which start at current line to current scanline
92 			    while(aCurrentEntry != maLineEntries.end())
93 			    {
94 				    const sal_Int32 nCurrentLineNumber(aCurrentEntry->getY());
95 
96 				    if(nCurrentLineNumber > nLineNumber)
97 				    {
98 					    // line is below current one, done (since array is sorted)
99 					    break;
100 				    }
101 				    else
102 				    {
103 					    // less or equal. Line is above or at current one. Advance it exactly to
104 					    // current line
105 					    const sal_uInt32 nStep(nLineNumber - nCurrentLineNumber);
106 
107 					    if(!nStep || aCurrentEntry->decrementRasterConversionLineEntry3D(nStep))
108 					    {
109 						    // add when exactly on current line or when incremet to it did not
110 						    // completely consume it
111 						    if(nStep)
112 						    {
113 							    aCurrentEntry->incrementRasterConversionLineEntry3D(nStep, *this);
114 						    }
115 
116 						    aCurrentLine.push_back(&(*(aCurrentEntry)));
117 					    }
118 				    }
119 
120 				    aCurrentEntry++;
121 			    }
122 
123 			    // sort current scanline using comparator. Only X is used there
124 			    // since all entries are already in one processed line. This needs to be done
125 			    // everytime since not only new spans may have benn added or old removed,
126 			    // but incrementing may also have changed the order
127 			    ::std::sort(aCurrentLine.begin(), aCurrentLine.end(), lineComparator());
128 
129 			    // process current scanline
130 			    aRasterConversionLineEntry3D = aCurrentLine.begin();
131 			    aNextLine.clear();
132 			    nPairCount = 0;
133 
134 			    while(aRasterConversionLineEntry3D != aCurrentLine.end())
135 			    {
136 				    RasterConversionLineEntry3D& rPrevScanRasterConversionLineEntry3D(**aRasterConversionLineEntry3D++);
137 
138 				    // look for 2nd span
139 				    if(aRasterConversionLineEntry3D != aCurrentLine.end())
140 				    {
141 					    // work on span from rPrevScanRasterConversionLineEntry3D to aRasterConversionLineEntry3D, fLineNumber is valid
142 					    processLineSpan(rPrevScanRasterConversionLineEntry3D, **aRasterConversionLineEntry3D, nLineNumber, nPairCount++);
143 				    }
144 
145 				    // increment to next line
146 				    if(rPrevScanRasterConversionLineEntry3D.decrementRasterConversionLineEntry3D(1))
147 				    {
148 					    rPrevScanRasterConversionLineEntry3D.incrementRasterConversionLineEntry3D(1, *this);
149 					    aNextLine.push_back(&rPrevScanRasterConversionLineEntry3D);
150 				    }
151 			    }
152 
153 			    // copy back next scanline if count has changed
154 			    if(aNextLine.size() != aCurrentLine.size())
155 			    {
156 				    aCurrentLine = aNextLine;
157 			    }
158 
159 			    // increment fLineNumber
160 			    nLineNumber++;
161 		    }
162 	    }
163     }
164 
165     void RasterConverter3D::addEdge(const B3DPolygon& rFill, sal_uInt32 a, sal_uInt32 b, const B3DHomMatrix* pViewToEye)
166     {
167 	    B3DPoint aStart(rFill.getB3DPoint(a));
168 	    B3DPoint aEnd(rFill.getB3DPoint(b));
169 	    sal_Int32 nYStart(fround(aStart.getY()));
170 	    sal_Int32 nYEnd(fround(aEnd.getY()));
171 
172 	    if(nYStart != nYEnd)
173 	    {
174 		    if(nYStart > nYEnd)
175 		    {
176 			    ::std::swap(aStart, aEnd);
177 			    ::std::swap(nYStart, nYEnd);
178 			    ::std::swap(a, b);
179 		    }
180 
181 		    const sal_uInt32 nYDelta(nYEnd - nYStart);
182 		    const double fInvYDelta(1.0 / nYDelta);
183 		    maLineEntries.push_back(RasterConversionLineEntry3D(
184 			    aStart.getX(), (aEnd.getX() - aStart.getX()) * fInvYDelta,
185 			    aStart.getZ(), (aEnd.getZ() - aStart.getZ()) * fInvYDelta,
186 			    nYStart, nYDelta));
187 
188 		    // if extra interpolation data is used, add it to the last created entry
189 		    RasterConversionLineEntry3D& rEntry = maLineEntries[maLineEntries.size() - 1];
190 
191 		    if(rFill.areBColorsUsed())
192 		    {
193 			    rEntry.setColorIndex(addColorInterpolator(rFill.getBColor(a), rFill.getBColor(b), fInvYDelta));
194 		    }
195 
196 		    if(rFill.areNormalsUsed())
197 		    {
198 			    rEntry.setNormalIndex(addNormalInterpolator(rFill.getNormal(a), rFill.getNormal(b), fInvYDelta));
199 		    }
200 
201 		    if(rFill.areTextureCoordinatesUsed())
202 		    {
203 			    if(pViewToEye)
204 			    {
205 				    const double fEyeA(((*pViewToEye) * aStart).getZ());
206 				    const double fEyeB(((*pViewToEye) * aEnd).getZ());
207 
208 				    rEntry.setInverseTextureIndex(addInverseTextureInterpolator(
209 					    rFill.getTextureCoordinate(a),
210 					    rFill.getTextureCoordinate(b),
211 					    fEyeA, fEyeB, fInvYDelta));
212 			    }
213 			    else
214 			    {
215 				    rEntry.setTextureIndex(addTextureInterpolator(
216 					    rFill.getTextureCoordinate(a),
217 					    rFill.getTextureCoordinate(b),
218 					    fInvYDelta));
219 			    }
220 		    }
221 	    }
222     }
223 
224     void RasterConverter3D::rasterconvertB3DEdge(const B3DPolygon& rLine, sal_uInt32 nA, sal_uInt32 nB, sal_Int32 nStartLine, sal_Int32 nStopLine, sal_uInt16 nLineWidth)
225     {
226         B3DPoint aStart(rLine.getB3DPoint(nA));
227         B3DPoint aEnd(rLine.getB3DPoint(nB));
228         const double fZBufferLineAdd(0x00ff);
229 		static bool bForceToPolygon(false);
230 
231         if(nLineWidth > 1 || bForceToPolygon)
232         {
233             // this is not a hairline anymore, in most cases since it's an oversampled
234             // hairline to get e.g. AA for Z-Buffering. Create fill geometry.
235             if(!aStart.equal(aEnd))
236             {
237 		        reset();
238 		        maLineEntries.clear();
239 
240                 B2DVector aVector(aEnd.getX() - aStart.getX(), aEnd.getY() - aStart.getY());
241                 aVector.normalize();
242             	const B2DVector aPerpend(getPerpendicular(aVector) * ((static_cast<double>(nLineWidth) + 0.5) * 0.5));
243                 const double fZStartWithAdd(aStart.getZ() + fZBufferLineAdd);
244                 const double fZEndWithAdd(aEnd.getZ() + fZBufferLineAdd);
245 
246                 B3DPolygon aPolygon;
247                 aPolygon.append(B3DPoint(aStart.getX() + aPerpend.getX(), aStart.getY() + aPerpend.getY(), fZStartWithAdd));
248                 aPolygon.append(B3DPoint(aEnd.getX() + aPerpend.getX(), aEnd.getY() + aPerpend.getY(), fZEndWithAdd));
249                 aPolygon.append(B3DPoint(aEnd.getX() - aPerpend.getX(), aEnd.getY() - aPerpend.getY(), fZEndWithAdd));
250                 aPolygon.append(B3DPoint(aStart.getX() - aPerpend.getX(), aStart.getY() - aPerpend.getY(), fZStartWithAdd));
251                 aPolygon.setClosed(true);
252 
253                 addArea(aPolygon, 0);
254             }
255         }
256         else
257         {
258             // it's a hairline. Use direct RasterConversionLineEntry creation to
259             // rasterconvert lines as similar to areas as possible to avoid Z-Fighting
260 	        sal_Int32 nYStart(fround(aStart.getY()));
261 	        sal_Int32 nYEnd(fround(aEnd.getY()));
262 
263 	        if(nYStart == nYEnd)
264 	        {
265 		        // horizontal line, check X
266 		        const sal_Int32 nXStart(static_cast<sal_Int32>(aStart.getX()));
267 		        const sal_Int32 nXEnd(static_cast<sal_Int32>(aEnd.getX()));
268 
269 		        if(nXStart != nXEnd)
270 		        {
271 			        reset();
272 			        maLineEntries.clear();
273 
274 			        // horizontal line, create vertical entries. These will be sorted by
275 			        // X anyways, so no need to distinguish the case here
276 			        maLineEntries.push_back(RasterConversionLineEntry3D(
277 				        aStart.getX(), 0.0,
278 				        aStart.getZ() + fZBufferLineAdd, 0.0,
279 				        nYStart, 1));
280 			        maLineEntries.push_back(RasterConversionLineEntry3D(
281 				        aEnd.getX(), 0.0,
282 				        aEnd.getZ() + fZBufferLineAdd, 0.0,
283 				        nYStart, 1));
284 		        }
285 	        }
286 	        else
287 	        {
288 		        reset();
289 		        maLineEntries.clear();
290 
291 		        if(nYStart > nYEnd)
292 		        {
293 			        ::std::swap(aStart, aEnd);
294 			        ::std::swap(nYStart, nYEnd);
295 		        }
296 
297 		        const sal_uInt32 nYDelta(static_cast<sal_uInt32>(nYEnd - nYStart));
298 		        const double fInvYDelta(1.0 / nYDelta);
299 
300 		        // non-horizontal line, create two parallell entries. These will be sorted by
301 		        // X anyways, so no need to distinguish the case here
302 		        maLineEntries.push_back(RasterConversionLineEntry3D(
303 			        aStart.getX(), (aEnd.getX() - aStart.getX()) * fInvYDelta,
304 			        aStart.getZ() + fZBufferLineAdd, (aEnd.getZ() - aStart.getZ()) * fInvYDelta,
305 			        nYStart, nYDelta));
306 
307 		        RasterConversionLineEntry3D& rEntry = maLineEntries[maLineEntries.size() - 1];
308 
309 		        // need to choose a X-Distance for the 2nd edge which guarantees all pixels
310 		        // of the line to be set. This is exactly the X-Increment for one Y-Step.
311 		        // Same is true for Z, so in both cases, add one increment to them. To also
312 		        // guarantee one pixel per line, add a minimum of one for X.
313 		        const double fDistanceX(fabs(rEntry.getX().getInc()) >= 1.0 ? rEntry.getX().getInc() : 1.0);
314 
315 		        maLineEntries.push_back(RasterConversionLineEntry3D(
316 			        rEntry.getX().getVal() + fDistanceX, rEntry.getX().getInc(),
317 			        rEntry.getZ().getVal() + rEntry.getZ().getInc(), rEntry.getZ().getInc(),
318 			        nYStart, nYDelta));
319 	        }
320         }
321 
322         if(maLineEntries.size())
323         {
324 	        rasterconvertB3DArea(nStartLine, nStopLine);
325         }
326     }
327 
328     void RasterConverter3D::rasterconvertB3DPolyPolygon(const B3DPolyPolygon& rFill, const B3DHomMatrix* pViewToEye, sal_Int32 nStartLine, sal_Int32 nStopLine)
329     {
330 	    reset();
331 	    maLineEntries.clear();
332 	    addArea(rFill, pViewToEye);
333 	    rasterconvertB3DArea(nStartLine, nStopLine);
334     }
335 
336     void RasterConverter3D::rasterconvertB3DPolygon(const B3DPolygon& rLine, sal_Int32 nStartLine, sal_Int32 nStopLine, sal_uInt16 nLineWidth)
337     {
338 	    const sal_uInt32 nPointCount(rLine.count());
339 
340 	    if(nPointCount)
341 	    {
342 		    const sal_uInt32 nEdgeCount(rLine.isClosed() ? nPointCount : nPointCount - 1);
343 
344 		    for(sal_uInt32 a(0); a < nEdgeCount; a++)
345 		    {
346 			    rasterconvertB3DEdge(rLine, a, (a + 1) % nPointCount, nStartLine, nStopLine, nLineWidth);
347 		    }
348 	    }
349     }
350 } // end of namespace basegfx
351 
352 //////////////////////////////////////////////////////////////////////////////
353 // eof
354