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