xref: /trunk/main/basegfx/source/raster/rasterconvert3d.cxx (revision 1ecadb572e7010ff3b3382ad9bf179dbc6efadbb)
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