| /************************************************************** |
| * |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| * |
| *************************************************************/ |
| |
| |
| |
| // MARKER(update_precomp.py): autogen include statement, do not remove |
| #include "precompiled_basegfx.hxx" |
| |
| #include <basegfx/raster/rasterconvert3d.hxx> |
| #include <basegfx/polygon/b3dpolygon.hxx> |
| #include <basegfx/polygon/b3dpolypolygon.hxx> |
| #include <basegfx/point/b3dpoint.hxx> |
| |
| ////////////////////////////////////////////////////////////////////////////// |
| // implementations of the 3D raster converter |
| |
| namespace basegfx |
| { |
| void RasterConverter3D::addArea(const B3DPolygon& rFill, const B3DHomMatrix* pViewToEye) |
| { |
| const sal_uInt32 nPointCount(rFill.count()); |
| |
| for(sal_uInt32 a(0); a < nPointCount; a++) |
| { |
| addEdge(rFill, a, (a + 1) % nPointCount, pViewToEye); |
| } |
| } |
| |
| void RasterConverter3D::addArea(const B3DPolyPolygon& rFill, const B3DHomMatrix* pViewToEye) |
| { |
| const sal_uInt32 nPolyCount(rFill.count()); |
| |
| for(sal_uInt32 a(0); a < nPolyCount; a++) |
| { |
| addArea(rFill.getB3DPolygon(a), pViewToEye); |
| } |
| } |
| |
| RasterConverter3D::RasterConverter3D() |
| : InterpolatorProvider3D(), |
| maLineEntries() |
| {} |
| |
| RasterConverter3D::~RasterConverter3D() |
| {} |
| |
| void RasterConverter3D::rasterconvertB3DArea(sal_Int32 nStartLine, sal_Int32 nStopLine) |
| { |
| if(maLineEntries.size()) |
| { |
| OSL_ENSURE(nStopLine >= nStartLine, "nStopLine is bigger than nStartLine (!)"); |
| |
| // sort global entries by Y, X once. After this, the vector |
| // is seen as frozen. Pointers to it's entries will be used in the following code. |
| ::std::sort(maLineEntries.begin(), maLineEntries.end()); |
| |
| // local parameters |
| ::std::vector< RasterConversionLineEntry3D >::iterator aCurrentEntry(maLineEntries.begin()); |
| ::std::vector< RasterConversionLineEntry3D* > aCurrentLine; |
| ::std::vector< RasterConversionLineEntry3D* > aNextLine; |
| ::std::vector< RasterConversionLineEntry3D* >::iterator aRasterConversionLineEntry3D; |
| sal_uInt32 nPairCount(0); |
| |
| // get scanlines first LineNumber as start |
| sal_Int32 nLineNumber(::std::max(aCurrentEntry->getY(), nStartLine)); |
| |
| while((aCurrentLine.size() || aCurrentEntry != maLineEntries.end()) && (nLineNumber < nStopLine)) |
| { |
| // add all entries which start at current line to current scanline |
| while(aCurrentEntry != maLineEntries.end()) |
| { |
| const sal_Int32 nCurrentLineNumber(aCurrentEntry->getY()); |
| |
| if(nCurrentLineNumber > nLineNumber) |
| { |
| // line is below current one, done (since array is sorted) |
| break; |
| } |
| else |
| { |
| // less or equal. Line is above or at current one. Advance it exactly to |
| // current line |
| const sal_uInt32 nStep(nLineNumber - nCurrentLineNumber); |
| |
| if(!nStep || aCurrentEntry->decrementRasterConversionLineEntry3D(nStep)) |
| { |
| // add when exactly on current line or when incremet to it did not |
| // completely consume it |
| if(nStep) |
| { |
| aCurrentEntry->incrementRasterConversionLineEntry3D(nStep, *this); |
| } |
| |
| aCurrentLine.push_back(&(*(aCurrentEntry))); |
| } |
| } |
| |
| aCurrentEntry++; |
| } |
| |
| // sort current scanline using comparator. Only X is used there |
| // since all entries are already in one processed line. This needs to be done |
| // every time since not only new spans may have benn added or old removed, |
| // but incrementing may also have changed the order |
| ::std::sort(aCurrentLine.begin(), aCurrentLine.end(), lineComparator()); |
| |
| // process current scanline |
| aRasterConversionLineEntry3D = aCurrentLine.begin(); |
| aNextLine.clear(); |
| nPairCount = 0; |
| |
| while(aRasterConversionLineEntry3D != aCurrentLine.end()) |
| { |
| RasterConversionLineEntry3D& rPrevScanRasterConversionLineEntry3D(**aRasterConversionLineEntry3D++); |
| |
| // look for 2nd span |
| if(aRasterConversionLineEntry3D != aCurrentLine.end()) |
| { |
| // work on span from rPrevScanRasterConversionLineEntry3D to aRasterConversionLineEntry3D, fLineNumber is valid |
| processLineSpan(rPrevScanRasterConversionLineEntry3D, **aRasterConversionLineEntry3D, nLineNumber, nPairCount++); |
| } |
| |
| // increment to next line |
| if(rPrevScanRasterConversionLineEntry3D.decrementRasterConversionLineEntry3D(1)) |
| { |
| rPrevScanRasterConversionLineEntry3D.incrementRasterConversionLineEntry3D(1, *this); |
| aNextLine.push_back(&rPrevScanRasterConversionLineEntry3D); |
| } |
| } |
| |
| // copy back next scanline if count has changed |
| if(aNextLine.size() != aCurrentLine.size()) |
| { |
| aCurrentLine = aNextLine; |
| } |
| |
| // increment fLineNumber |
| nLineNumber++; |
| } |
| } |
| } |
| |
| void RasterConverter3D::addEdge(const B3DPolygon& rFill, sal_uInt32 a, sal_uInt32 b, const B3DHomMatrix* pViewToEye) |
| { |
| B3DPoint aStart(rFill.getB3DPoint(a)); |
| B3DPoint aEnd(rFill.getB3DPoint(b)); |
| sal_Int32 nYStart(fround(aStart.getY())); |
| sal_Int32 nYEnd(fround(aEnd.getY())); |
| |
| if(nYStart != nYEnd) |
| { |
| if(nYStart > nYEnd) |
| { |
| ::std::swap(aStart, aEnd); |
| ::std::swap(nYStart, nYEnd); |
| ::std::swap(a, b); |
| } |
| |
| const sal_uInt32 nYDelta(nYEnd - nYStart); |
| const double fInvYDelta(1.0 / nYDelta); |
| maLineEntries.push_back(RasterConversionLineEntry3D( |
| aStart.getX(), (aEnd.getX() - aStart.getX()) * fInvYDelta, |
| aStart.getZ(), (aEnd.getZ() - aStart.getZ()) * fInvYDelta, |
| nYStart, nYDelta)); |
| |
| // if extra interpolation data is used, add it to the last created entry |
| RasterConversionLineEntry3D& rEntry = maLineEntries[maLineEntries.size() - 1]; |
| |
| if(rFill.areBColorsUsed()) |
| { |
| rEntry.setColorIndex(addColorInterpolator(rFill.getBColor(a), rFill.getBColor(b), fInvYDelta)); |
| } |
| |
| if(rFill.areNormalsUsed()) |
| { |
| rEntry.setNormalIndex(addNormalInterpolator(rFill.getNormal(a), rFill.getNormal(b), fInvYDelta)); |
| } |
| |
| if(rFill.areTextureCoordinatesUsed()) |
| { |
| if(pViewToEye) |
| { |
| const double fEyeA(((*pViewToEye) * aStart).getZ()); |
| const double fEyeB(((*pViewToEye) * aEnd).getZ()); |
| |
| rEntry.setInverseTextureIndex(addInverseTextureInterpolator( |
| rFill.getTextureCoordinate(a), |
| rFill.getTextureCoordinate(b), |
| fEyeA, fEyeB, fInvYDelta)); |
| } |
| else |
| { |
| rEntry.setTextureIndex(addTextureInterpolator( |
| rFill.getTextureCoordinate(a), |
| rFill.getTextureCoordinate(b), |
| fInvYDelta)); |
| } |
| } |
| } |
| } |
| |
| void RasterConverter3D::rasterconvertB3DEdge(const B3DPolygon& rLine, sal_uInt32 nA, sal_uInt32 nB, sal_Int32 nStartLine, sal_Int32 nStopLine, sal_uInt16 nLineWidth) |
| { |
| B3DPoint aStart(rLine.getB3DPoint(nA)); |
| B3DPoint aEnd(rLine.getB3DPoint(nB)); |
| const double fZBufferLineAdd(0x00ff); |
| static bool bForceToPolygon(false); |
| |
| if(nLineWidth > 1 || bForceToPolygon) |
| { |
| // this is not a hairline anymore, in most cases since it's an oversampled |
| // hairline to get e.g. AA for Z-Buffering. Create fill geometry. |
| if(!aStart.equal(aEnd)) |
| { |
| reset(); |
| maLineEntries.clear(); |
| |
| B2DVector aVector(aEnd.getX() - aStart.getX(), aEnd.getY() - aStart.getY()); |
| aVector.normalize(); |
| const B2DVector aPerpend(getPerpendicular(aVector) * ((static_cast<double>(nLineWidth) + 0.5) * 0.5)); |
| const double fZStartWithAdd(aStart.getZ() + fZBufferLineAdd); |
| const double fZEndWithAdd(aEnd.getZ() + fZBufferLineAdd); |
| |
| B3DPolygon aPolygon; |
| aPolygon.append(B3DPoint(aStart.getX() + aPerpend.getX(), aStart.getY() + aPerpend.getY(), fZStartWithAdd)); |
| aPolygon.append(B3DPoint(aEnd.getX() + aPerpend.getX(), aEnd.getY() + aPerpend.getY(), fZEndWithAdd)); |
| aPolygon.append(B3DPoint(aEnd.getX() - aPerpend.getX(), aEnd.getY() - aPerpend.getY(), fZEndWithAdd)); |
| aPolygon.append(B3DPoint(aStart.getX() - aPerpend.getX(), aStart.getY() - aPerpend.getY(), fZStartWithAdd)); |
| aPolygon.setClosed(true); |
| |
| addArea(aPolygon, 0); |
| } |
| } |
| else |
| { |
| // it's a hairline. Use direct RasterConversionLineEntry creation to |
| // rasterconvert lines as similar to areas as possible to avoid Z-Fighting |
| sal_Int32 nYStart(fround(aStart.getY())); |
| sal_Int32 nYEnd(fround(aEnd.getY())); |
| |
| if(nYStart == nYEnd) |
| { |
| // horizontal line, check X |
| const sal_Int32 nXStart(static_cast<sal_Int32>(aStart.getX())); |
| const sal_Int32 nXEnd(static_cast<sal_Int32>(aEnd.getX())); |
| |
| if(nXStart != nXEnd) |
| { |
| reset(); |
| maLineEntries.clear(); |
| |
| // horizontal line, create vertical entries. These will be sorted by |
| // X anyways, so no need to distinguish the case here |
| maLineEntries.push_back(RasterConversionLineEntry3D( |
| aStart.getX(), 0.0, |
| aStart.getZ() + fZBufferLineAdd, 0.0, |
| nYStart, 1)); |
| maLineEntries.push_back(RasterConversionLineEntry3D( |
| aEnd.getX(), 0.0, |
| aEnd.getZ() + fZBufferLineAdd, 0.0, |
| nYStart, 1)); |
| } |
| } |
| else |
| { |
| reset(); |
| maLineEntries.clear(); |
| |
| if(nYStart > nYEnd) |
| { |
| ::std::swap(aStart, aEnd); |
| ::std::swap(nYStart, nYEnd); |
| } |
| |
| const sal_uInt32 nYDelta(static_cast<sal_uInt32>(nYEnd - nYStart)); |
| const double fInvYDelta(1.0 / nYDelta); |
| |
| // non-horizontal line, create two parallell entries. These will be sorted by |
| // X anyways, so no need to distinguish the case here |
| maLineEntries.push_back(RasterConversionLineEntry3D( |
| aStart.getX(), (aEnd.getX() - aStart.getX()) * fInvYDelta, |
| aStart.getZ() + fZBufferLineAdd, (aEnd.getZ() - aStart.getZ()) * fInvYDelta, |
| nYStart, nYDelta)); |
| |
| RasterConversionLineEntry3D& rEntry = maLineEntries[maLineEntries.size() - 1]; |
| |
| // need to choose a X-Distance for the 2nd edge which guarantees all pixels |
| // of the line to be set. This is exactly the X-Increment for one Y-Step. |
| // Same is true for Z, so in both cases, add one increment to them. To also |
| // guarantee one pixel per line, add a minimum of one for X. |
| const double fDistanceX(fabs(rEntry.getX().getInc()) >= 1.0 ? rEntry.getX().getInc() : 1.0); |
| |
| maLineEntries.push_back(RasterConversionLineEntry3D( |
| rEntry.getX().getVal() + fDistanceX, rEntry.getX().getInc(), |
| rEntry.getZ().getVal() + rEntry.getZ().getInc(), rEntry.getZ().getInc(), |
| nYStart, nYDelta)); |
| } |
| } |
| |
| if(maLineEntries.size()) |
| { |
| rasterconvertB3DArea(nStartLine, nStopLine); |
| } |
| } |
| |
| void RasterConverter3D::rasterconvertB3DPolyPolygon(const B3DPolyPolygon& rFill, const B3DHomMatrix* pViewToEye, sal_Int32 nStartLine, sal_Int32 nStopLine) |
| { |
| reset(); |
| maLineEntries.clear(); |
| addArea(rFill, pViewToEye); |
| rasterconvertB3DArea(nStartLine, nStopLine); |
| } |
| |
| void RasterConverter3D::rasterconvertB3DPolygon(const B3DPolygon& rLine, sal_Int32 nStartLine, sal_Int32 nStopLine, sal_uInt16 nLineWidth) |
| { |
| const sal_uInt32 nPointCount(rLine.count()); |
| |
| if(nPointCount) |
| { |
| const sal_uInt32 nEdgeCount(rLine.isClosed() ? nPointCount : nPointCount - 1); |
| |
| for(sal_uInt32 a(0); a < nEdgeCount; a++) |
| { |
| rasterconvertB3DEdge(rLine, a, (a + 1) % nPointCount, nStartLine, nStopLine, nLineWidth); |
| } |
| } |
| } |
| } // end of namespace basegfx |
| |
| ////////////////////////////////////////////////////////////////////////////// |
| // eof |