/************************************************************************* * * OpenOffice.org - a multi-platform office productivity suite * * $RCSfile: defaultprocessor3d.cxx,v $ * * $Revision: 1.5 $ * * last change: $Author: aw $ $Date: 2007-03-06 12:35:55 $ * * The Contents of this file are made available subject to * the terms of GNU Lesser General Public License Version 2.1. * * * GNU Lesser General Public License Version 2.1 * ============================================= * Copyright 2005 by Sun Microsystems, Inc. * 901 San Antonio Road, Palo Alto, CA 94303, USA * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License version 2.1, as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, * MA 02111-1307 USA * ************************************************************************/ ////////////////////////////////////////////////////////////////////////////// // includes for namespace basegfx #ifndef INCLUDED_DRAWINGLAYER_PROCESSOR3D_DEFAULTPROCESSOR3D_HXX #include #endif #ifndef _BGFX_POLYGON_B3DPOLYGON_HXX #include #endif #ifndef _BGFX_RASTER_BZPIXELRASTER_HXX #include #endif #ifndef _SV_BMPACC_HXX #include #endif #ifndef INCLUDED_DRAWINGLAYER_ATTRIBUTE_MATERIALATTRIBUTE3D_HXX #include #endif ////////////////////////////////////////////////////////////////////////////// // includes for namespace drawinglayer #ifndef INCLUDED_DRAWINGLAYER_TEXTURE_TEXTURE_HXX #include #endif #ifndef INCLUDED_DRAWINGLAYER_ATTRIBUTE_SDRATTRIBUTE3D_HXX #include #endif #ifndef INCLUDED_DRAWINGLAYER_ATTRIBUTE_FILLATTRIBUTE_HXX #include #endif #ifndef INCLUDED_DRAWINGLAYER_PRIMITIVE3D_HATCHTEXTUREPRIMITIVE3D_HXX #include #endif #ifndef INCLUDED_DRAWINGLAYER_PRIMITIVE3D_MODIFIEDCOLORPRIMITIVE3D_HXX #include #endif #ifndef INCLUDED_DRAWINGLAYER_PRIMITIVE3D_POLYGONPRIMITIVE3D_HXX #include #endif #ifndef _BGFX_POLYGON_B3DPOLYGONTOOLS_HXX #include #endif #ifndef INCLUDED_DRAWINGLAYER_PRIMITIVE3D_POLYPOLYGONPRIMITIVE3D_HXX #include #endif #ifndef _BGFX_POLYPOLYGON_B3DPOLYGONTOOLS_HXX #include #endif #ifndef INCLUDED_DRAWINGLAYER_PRIMITIVE3D_TRANSFORMPRIMITIVE3D_HXX #include #endif #ifndef INCLUDED_DRAWINGLAYER_PRIMITIVE3D_SDRLABELPRIMITIVE3D_HXX #include #endif #ifndef _BGFX_TOOLS_CANVASTOOLS_HXX #include #endif #ifndef INCLUDED_DRAWINGLAYER_GEOMETRY_VIEWINFORMATION2D_HXX #include #endif #ifndef INCLUDED_DRAWINGLAYER_PRIMITIVE3D_PRIMITIVETYPES3D_HXX #include #endif ////////////////////////////////////////////////////////////////////////////// namespace basegfx { class BDInterpolator { protected: double mfVal; double mfInc; public: BDInterpolator() {} BDInterpolator(double fValA, double fValB, sal_uInt32 nCount) : mfVal(fValA), mfInc((fValB - fValA) / (double)nCount) {} double getVal() const { return mfVal; } double getInc() const { return mfInc; } void increment() { mfVal += mfInc; } void increment(double fStep) { mfVal += fStep * mfInc; } }; class BColorInterpolator { protected: BColor maVal; BColor maInc; public: BColorInterpolator() {} BColorInterpolator(const BColor& rValA, const BColor& rValB, sal_uInt32 nCount) : maVal(rValA), maInc((rValB - rValA) / (double)nCount) {} const BColor& getVal() const { return maVal; } const BColor& getInc() const { return maInc; } void increment() { maVal += maInc; } void increment(double fStep) { maVal += fStep * maInc; } }; class B3DVectorInterpolator { protected: B3DVector maVal; B3DVector maInc; public: B3DVectorInterpolator() {} B3DVectorInterpolator(const B3DVector& rValA, const B3DVector& rValB, sal_uInt32 nCount) : maVal(rValA), maInc((rValB - rValA) / (double)nCount) {} const B3DVector& getVal() const { return maVal; } const B3DVector& getInc() const { return maInc; } void increment() { maVal += maInc; } void increment(double fStep) { maVal += fStep * maInc; } }; class B2DPointInterpolator { protected: B2DPoint maVal; B2DPoint maInc; BDInterpolator maZInv; public: B2DPointInterpolator() {} B2DPointInterpolator(const B2DPoint& rValA, const B2DPoint& rValB, double fInvZEyeA, double fInvZEyeB, sal_uInt32 nCount) : maVal(rValA), maInc((rValB - rValA) / (double)nCount), maZInv(fInvZEyeA, fInvZEyeB, nCount) {} const B2DPoint& getVal() const { return maVal; } const B2DPoint& getInc() const { return maInc; } double getZVal() const { return maZInv.getVal(); } double getZInc() const { return maZInv.getInc(); } void increment() { maVal += maInc; maZInv.increment(); } void increment(double fStep) { maVal += fStep * maInc; maZInv.increment(fStep); } }; } // end of namespace basegfx ////////////////////////////////////////////////////////////////////////////// #define SCANLINE_EMPTY_INDEX (0xffffffff) namespace basegfx { class B3DScanlineEntry { protected: sal_Int32 mnLine; sal_uInt32 mnLineCount; BDInterpolator maX; BDInterpolator maZ; // values for normal and texture coordinate interpolation are optional, // held simply by an index. Empty is marked by SCANLINE_EMPTY_INDEX to which it needs // to be initialized sal_uInt32 mnBColorIndex; sal_uInt32 mnNormalIndex; sal_uInt32 mnTextureCoordinateIndex; public: B3DScanlineEntry() {} // data write access void setLine(sal_Int32 nLine) { mnLine = nLine; } void setLineCount(sal_uInt32 nLineCount) { mnLineCount = nLineCount; } void setXInterpolator(const BDInterpolator& rInt) { maX = rInt; } void setZInterpolator(const BDInterpolator& rInt) { maZ = rInt; } void setBColorIndex(sal_uInt32 nIndex) { mnBColorIndex = nIndex; } void setNormalIndex(sal_uInt32 nIndex) { mnNormalIndex = nIndex; } void setTextureCoordinateIndex(sal_uInt32 nIndex) { mnTextureCoordinateIndex = nIndex; } // data read access sal_Int32 getLine() const { return mnLine; } sal_uInt32 getLineCount() const { return mnLineCount; } const BDInterpolator& getXInterpolator() const { return maX; } const BDInterpolator& getZInterpolator() const { return maZ; } sal_uInt32 getBColorIndex() const {return mnBColorIndex; } sal_uInt32 getNormalIndex() const {return mnNormalIndex; } sal_uInt32 getTextureCoordinateIndex() const {return mnTextureCoordinateIndex; } bool simpleLineSortComparator(const B3DScanlineEntry& rComp) const { return (maX.getVal() < rComp.maX.getVal()); } bool operator<(const B3DScanlineEntry& rComp) const { if(mnLine == rComp.mnLine) { return simpleLineSortComparator(rComp); } return (mnLine < rComp.mnLine); } bool isValid() const { return (0L != mnLineCount); } bool decrement(sal_uInt32 nCount = 1L) { if(mnLineCount > nCount) { mnLineCount -= nCount; return true; } else { mnLineCount = 0L; return false; } } void increment() { maX.increment(); maZ.increment(); } void increment(double fStep) { maX.increment(fStep); maZ.increment(fStep); } }; } // end of namespace basegfx ////////////////////////////////////////////////////////////////////////////// namespace basegfx { class B3DPolyPolygonRasterConverter { protected: ::std::vector< B3DScanlineEntry > maGlobalEntries; ::std::vector< BColorInterpolator > maGlobalBColorInterpolators; ::std::vector< B3DVectorInterpolator > maGlobalNormalInterpolators; ::std::vector< B2DPointInterpolator > maGlobalTextureCoordinateInterpolators; // bitfield unsigned mbAreaMode : 1; struct scanlineEntryComparator { bool operator()( const B3DScanlineEntry* pA, const B3DScanlineEntry* pB) { // here, Y is the same anyways (sorting a scanline), so simple compare is enough OSL_ENSURE(pA && pB, "scanlineEntryComparator: empty pointer (!)"); return pA->simpleLineSortComparator(*pB); } }; // add BColor interpolator, return index sal_uInt32 addBColor(const BColor& rA, const BColor& rB, sal_uInt32 nDelta) { maGlobalBColorInterpolators.push_back(BColorInterpolator(rA, rB, nDelta)); return (maGlobalBColorInterpolators.size() - 1L); } // add normal interpolator, return index sal_uInt32 addNormal(const B3DVector& rA, const B3DVector& rB, sal_uInt32 nDelta) { maGlobalNormalInterpolators.push_back(B3DVectorInterpolator(rA, rB, nDelta)); return (maGlobalNormalInterpolators.size() - 1L); } // add texture interpolator, return index sal_uInt32 addTextureCoordinate(const B2DPoint& rA, const B2DPoint& rB, double fZEyeA, double fZEyeB, sal_uInt32 nDelta) { const double fInvZEyeA(fTools::equalZero(fZEyeA) ? fZEyeA : 1.0 / fZEyeA); const double fInvZEyeB(fTools::equalZero(fZEyeB) ? fZEyeB : 1.0 / fZEyeB); maGlobalTextureCoordinateInterpolators.push_back(B2DPointInterpolator(rA * fInvZEyeA, rB * fInvZEyeB, fInvZEyeA, fInvZEyeB, nDelta)); return (maGlobalTextureCoordinateInterpolators.size() - 1L); } void increment(B3DScanlineEntry& rScanEntry) { rScanEntry.increment(); const sal_uInt32 nBColor(rScanEntry.getBColorIndex()); if(SCANLINE_EMPTY_INDEX != nBColor) { maGlobalBColorInterpolators[nBColor].increment(); } const sal_uInt32 nNormal(rScanEntry.getNormalIndex()); if(SCANLINE_EMPTY_INDEX != nNormal) { maGlobalNormalInterpolators[nNormal].increment(); } const sal_uInt32 nTextureCoordinate(rScanEntry.getTextureCoordinateIndex()); if(SCANLINE_EMPTY_INDEX != nTextureCoordinate) { maGlobalTextureCoordinateInterpolators[nTextureCoordinate].increment(); } } void increment(B3DScanlineEntry& rScanEntry, double fStep) { rScanEntry.increment(fStep); const sal_uInt32 nBColor(rScanEntry.getBColorIndex()); if(SCANLINE_EMPTY_INDEX != nBColor) { maGlobalBColorInterpolators[nBColor].increment(fStep); } const sal_uInt32 nNormal(rScanEntry.getNormalIndex()); if(SCANLINE_EMPTY_INDEX != nNormal) { maGlobalNormalInterpolators[nNormal].increment(fStep); } const sal_uInt32 nTextureCoordinate(rScanEntry.getTextureCoordinateIndex()); if(SCANLINE_EMPTY_INDEX != nTextureCoordinate) { maGlobalTextureCoordinateInterpolators[nTextureCoordinate].increment(fStep); } } void addEdge(const B3DPolygon& rPolygon, const B3DHomMatrix& rInvEyeToView, sal_uInt32 nIndA, sal_uInt32 nIndB) { B3DPoint aPntA(rPolygon.getB3DPoint(nIndA)); B3DPoint aPntB(rPolygon.getB3DPoint(nIndB)); sal_Int32 nLineA((sal_Int32)(aPntA.getY())); sal_Int32 nLineB((sal_Int32)(aPntB.getY())); if(nLineA == nLineB) { if(!mbAreaMode) { B3DScanlineEntry aNewEntry; aNewEntry.setLine(nLineA); aNewEntry.setLineCount(0L); aNewEntry.setXInterpolator(BDInterpolator(aPntA.getX(), aPntB.getX(), 1L)); aNewEntry.setZInterpolator(BDInterpolator(aPntA.getZ(), aPntB.getZ(), 1L)); aNewEntry.setBColorIndex(SCANLINE_EMPTY_INDEX); aNewEntry.setNormalIndex(SCANLINE_EMPTY_INDEX); aNewEntry.setTextureCoordinateIndex(SCANLINE_EMPTY_INDEX); maGlobalEntries.push_back(aNewEntry); } } else { if(nLineB < nLineA) { ::std::swap(aPntA, aPntB); ::std::swap(nLineA, nLineB); ::std::swap(nIndA, nIndB); } const sal_uInt32 nLineDelta(nLineB - nLineA); B3DScanlineEntry aNewEntry; aNewEntry.setLine(nLineA); aNewEntry.setLineCount(nLineDelta); aNewEntry.setXInterpolator(BDInterpolator(aPntA.getX(), aPntB.getX(), nLineDelta)); aNewEntry.setZInterpolator(BDInterpolator(aPntA.getZ(), aPntB.getZ(), nLineDelta)); const bool bColUsed(mbAreaMode && rPolygon.areBColorsUsed()); aNewEntry.setBColorIndex(bColUsed ? addBColor(rPolygon.getBColor(nIndA), rPolygon.getBColor(nIndB), nLineDelta) : SCANLINE_EMPTY_INDEX); const bool bNrmUsed(mbAreaMode && rPolygon.areNormalsUsed()); aNewEntry.setNormalIndex(bNrmUsed ? addNormal(rPolygon.getNormal(nIndA), rPolygon.getNormal(nIndB), nLineDelta) : SCANLINE_EMPTY_INDEX); const bool bTexUsed(mbAreaMode && rPolygon.areTextureCoordinatesUsed()); if(bTexUsed) { const double fEyeA((rInvEyeToView * aPntA).getZ()); const double fEyeB((rInvEyeToView * aPntB).getZ()); aNewEntry.setTextureCoordinateIndex(addTextureCoordinate( rPolygon.getTextureCoordinate(nIndA), rPolygon.getTextureCoordinate(nIndB), fEyeA, fEyeB, nLineDelta)); } else { aNewEntry.setTextureCoordinateIndex(SCANLINE_EMPTY_INDEX); } maGlobalEntries.push_back(aNewEntry); } } // virtual rasterconverter virtual void processSpan(const B3DScanlineEntry& rA, const B3DScanlineEntry& rB, sal_Int32 nLine, sal_uInt32 nSpanCount) = 0; virtual void processLine(const B3DScanlineEntry& rEntry, sal_Int32 nLine) = 0; public: B3DPolyPolygonRasterConverter(bool bArea) : mbAreaMode(bArea) { } virtual ~B3DPolyPolygonRasterConverter() { } void addPolygon(const B3DPolygon& rPolygon, const B3DHomMatrix& rInvEyeToView) { const sal_uInt32 nPointCount(rPolygon.count()); if(nPointCount) { const sal_uInt32 nLoopCount((mbAreaMode || rPolygon.isClosed()) ? nPointCount : nPointCount - 1L); const bool bEnoughEdges(mbAreaMode ? (nLoopCount > 2L) : (nLoopCount > 0L)); if(bEnoughEdges) { for(sal_uInt32 a(0L); a < nLoopCount; a++) { addEdge(rPolygon, rInvEyeToView, a, (a + 1L) % nPointCount); } } } } void rasterconvert(sal_Int32 nStartLine, sal_Int32 nStopLine) { OSL_ENSURE(nStartLine <= nStopLine, "rasterconvert: wrong start/stop line (!)"); if(maGlobalEntries.size()) { // sort global entries by YStart, xPos once ::std::sort(maGlobalEntries.begin(), maGlobalEntries.end()); // local parameters ::std::vector< B3DScanlineEntry >::iterator aCurrentGlobalEntry(maGlobalEntries.begin()); ::std::vector< B3DScanlineEntry* > aCurrentScanLine; ::std::vector< B3DScanlineEntry* > aNextScanLine; ::std::vector< B3DScanlineEntry* >::iterator aScanLineEntry; sal_Int32 nLineNumber(nStartLine); sal_uInt32 nPairCount; while((nLineNumber < nStopLine) && (aCurrentScanLine.size() || aCurrentGlobalEntry != maGlobalEntries.end())) { // add all global entries which start at current Y to current scanline while(aCurrentGlobalEntry != maGlobalEntries.end() && aCurrentGlobalEntry->getLine() <= nLineNumber) { if(aCurrentGlobalEntry->getLine() < nLineNumber) { // adapt to current line in nLineNumber by decrementing count incrementing accordigly const sal_uInt32 nLineDiff(nLineNumber - aCurrentGlobalEntry->getLine()); if(aCurrentGlobalEntry->decrement(nLineDiff)) { increment(*aCurrentGlobalEntry, (double)nLineDiff); aCurrentScanLine.push_back(&(*(aCurrentGlobalEntry))); } } else { aCurrentScanLine.push_back(&(*(aCurrentGlobalEntry))); } aCurrentGlobalEntry++; } if(mbAreaMode) { // sort current scanline using simple comparator (only X is compared) ::std::sort(aCurrentScanLine.begin(), aCurrentScanLine.end(), scanlineEntryComparator()); } // process current scanline aScanLineEntry = aCurrentScanLine.begin(); aNextScanLine.clear(); nPairCount = 0L; while(aScanLineEntry != aCurrentScanLine.end()) { B3DScanlineEntry& rPrevScanLineEntry(**aScanLineEntry++); if(mbAreaMode) { // area mode, look for 2nd span if(aScanLineEntry != aCurrentScanLine.end()) { // work on span from rPrevScanLineEntry to aScanLineEntry, nLineNumber is valid processSpan(rPrevScanLineEntry, **aScanLineEntry, nLineNumber, nPairCount++); } } else { // work on line position rPrevScanLineEntry processLine(rPrevScanLineEntry, nLineNumber); } // do decrement counter and (evtl) increment to next line if(rPrevScanLineEntry.decrement()) { increment(rPrevScanLineEntry); aNextScanLine.push_back(&rPrevScanLineEntry); } } // copy back next scanline if count has changed if(aNextScanLine.size() != aCurrentScanLine.size()) { aCurrentScanLine = aNextScanLine; } // increment nLineNumber nLineNumber++; } } } }; } // end of namespace basegfx ////////////////////////////////////////////////////////////////////////////// namespace basegfx { BitmapEx getBitmapEx(const BPixelRaster& rRaster) { BitmapEx aRetval; const sal_uInt32 nWidth(rRaster.getWidth()); const sal_uInt32 nHeight(rRaster.getHeight()); if(nWidth && nHeight) { sal_uInt8 nInitAlpha(255); Bitmap aContent(Size(nWidth, nHeight), 24); AlphaMask aAlpha(Size(nWidth, nHeight), &nInitAlpha); BitmapWriteAccess* pContent = aContent.AcquireWriteAccess(); BitmapWriteAccess* pAlpha = aAlpha.AcquireWriteAccess(); if(pContent && pAlpha) { sal_uInt32 nIndex(0L); for(sal_uInt32 y(0L); y < nHeight; y++) { for(sal_uInt32 x(0L); x < nWidth; x++) { const BPixel& rPixel(rRaster.getBPixel(nIndex++)); if(rPixel.getOpacity()) { pContent->SetPixel(y, x, BitmapColor(rPixel.getRed(), rPixel.getGreen(), rPixel.getBlue())); pAlpha->SetPixel(y, x, BitmapColor(255 - rPixel.getOpacity())); } } } delete pContent; delete pAlpha; } aRetval = BitmapEx(aContent, aAlpha); } return aRetval; } } // end of namespace basegfx ////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// namespace drawinglayer { namespace { class BZPolyRaCon : public basegfx::B3DPolyPolygonRasterConverter { protected: basegfx::BZPixelRaster& mrBuffer; const attribute::MaterialAttribute3D& mrMaterial; const processor3d::DefaultProcessor3D& mrProcessor; // virtual rasterconverter virtual void processSpan(const basegfx::B3DScanlineEntry& rA, const basegfx::B3DScanlineEntry& rB, sal_Int32 nLine, sal_uInt32 nSpanCount); virtual void processLine(const basegfx::B3DScanlineEntry& rEntry, sal_Int32 nLine); public: BZPolyRaCon( bool bArea, basegfx::BZPixelRaster& rBuffer, const attribute::MaterialAttribute3D& rMaterial, const processor3d::DefaultProcessor3D& rProcessor) : B3DPolyPolygonRasterConverter(bArea), mrBuffer(rBuffer), mrMaterial(rMaterial), mrProcessor(rProcessor) {} }; void BZPolyRaCon::processSpan(const basegfx::B3DScanlineEntry& rA, const basegfx::B3DScanlineEntry& rB, sal_Int32 nLine, sal_uInt32 nSpanCount) { if(!(nSpanCount & 0x0001)) { if(nLine >= 0L && nLine < (sal_Int32)mrBuffer.getHeight()) { sal_Int32 nXA((sal_Int32)(rA.getXInterpolator().getVal())); sal_Int32 nXB((sal_Int32)(rB.getXInterpolator().getVal())); OSL_ENSURE(nXB >= nXA,"processSpan: positive run expected (!)"); if(nXB > nXA) { // initialize Z interpolator const sal_uInt32 nSpanLength(nXB - nXA); basegfx::BDInterpolator aZ(rA.getZInterpolator().getVal(), rB.getZInterpolator().getVal(), nSpanLength); // prepare some references to used variables const basegfx::BColor& rColor(mrMaterial.getColor()); const basegfx::BColor& rSpecular(mrMaterial.getSpecular()); const basegfx::BColor& rEmission(mrMaterial.getEmission()); const sal_uInt16 nSpecularIntensity(mrMaterial.getSpecularIntensity()); // get bools and init other interpolators on demand accordingly const bool bUseTex((mrProcessor.getGeoTexSvx() || mrProcessor.getTransparenceGeoTexSvx()) && rA.getTextureCoordinateIndex() != SCANLINE_EMPTY_INDEX && rB.getTextureCoordinateIndex() != SCANLINE_EMPTY_INDEX); const bool bUseColorTex(bUseTex && mrProcessor.getGeoTexSvx()); const bool bNeedOthers(!bUseColorTex || (bUseColorTex && mrProcessor.getModulate())); const bool bUseNrm(bNeedOthers && rA.getNormalIndex() != SCANLINE_EMPTY_INDEX && rB.getNormalIndex() != SCANLINE_EMPTY_INDEX); const bool bUseCol(!bUseNrm && bNeedOthers && rA.getBColorIndex() != SCANLINE_EMPTY_INDEX && rB.getBColorIndex() != SCANLINE_EMPTY_INDEX); const bool bModifyColor(mrProcessor.getBColorModifierStack().count()); basegfx::B2DPointInterpolator aTex; basegfx::B3DVectorInterpolator aNrm; basegfx::BColorInterpolator aCol; if(bUseTex) { const basegfx::B2DPointInterpolator& rLA(maGlobalTextureCoordinateInterpolators[rA.getTextureCoordinateIndex()]); const basegfx::B2DPointInterpolator& rLB(maGlobalTextureCoordinateInterpolators[rB.getTextureCoordinateIndex()]); aTex = basegfx::B2DPointInterpolator(rLA.getVal(), rLB.getVal(), rLA.getZVal(), rLB.getZVal(), nSpanLength); } if(bUseNrm) { aNrm = basegfx::B3DVectorInterpolator( maGlobalNormalInterpolators[rA.getNormalIndex()].getVal(), maGlobalNormalInterpolators[rB.getNormalIndex()].getVal(), nSpanLength); } if(bUseCol) { aCol = basegfx::BColorInterpolator( maGlobalBColorInterpolators[rA.getBColorIndex()].getVal(), maGlobalBColorInterpolators[rB.getBColorIndex()].getVal(), nSpanLength); } if(nXA < 0L) { const double fIncrement(-nXA); nXA = 0L; aZ.increment(fIncrement); if(bUseTex) { aTex.increment(fIncrement); } if(bUseNrm) { aNrm.increment(fIncrement); } if(bUseCol) { aCol.increment(fIncrement); } } if(nXB > (sal_Int32)mrBuffer.getWidth()) { nXB = mrBuffer.getWidth(); } if(nXA < nXB) { sal_uInt32 nScanlineIndex(mrBuffer.getIndexFromXY((sal_uInt32)nXA, (sal_uInt32)nLine)); while(nXA < nXB) { // get old and new Z to see if we need to do somethng at all sal_uInt16& rOldZ(mrBuffer.getZ(nScanlineIndex)); const sal_uInt16 nNewZ((sal_uInt16)(aZ.getVal())); if(nNewZ > rOldZ) { // prepare color basegfx::BColor aNewColor(rColor); double fOpacity(1.0); bool bOpacity(true); if(bUseTex) { // get texture coor const basegfx::B2DPoint aTexCoor(aTex.getVal() / aTex.getZVal()); if(mrProcessor.getGeoTexSvx()) { // calc color in spot mrProcessor.getGeoTexSvx()->modifyBColor(aTexCoor, aNewColor, fOpacity); bOpacity = basegfx::fTools::more(fOpacity, 0.0); } if(bOpacity && mrProcessor.getTransparenceGeoTexSvx()) { // calc opacity mrProcessor.getTransparenceGeoTexSvx()->modifyOpacity(aTexCoor, fOpacity); bOpacity = basegfx::fTools::more(fOpacity, 0.0); } } if(bOpacity) { if(mrProcessor.getGeoTexSvx()) { if(bUseNrm) { // blend texture with phong aNewColor = mrProcessor.getSdrLightingAttribute().solveColorModel(aNrm.getVal(), aNewColor, rSpecular, rEmission, nSpecularIntensity); } else if(bUseCol) { // blend texture with gouraud aNewColor *= aCol.getVal(); } else if(mrProcessor.getModulate()) { // blend texture with single material color aNewColor *= rColor; } } else { if(bUseNrm) { // modify color with phong aNewColor = mrProcessor.getSdrLightingAttribute().solveColorModel(aNrm.getVal(), rColor, rSpecular, rEmission, nSpecularIntensity); } else if(bUseCol) { // modify color with gouraud aNewColor = aCol.getVal(); } } if(bModifyColor) { aNewColor = mrProcessor.getBColorModifierStack().getModifiedColor(aNewColor); } if(basegfx::fTools::moreOrEqual(fOpacity, 1.0)) { // full opacity, set z and color rOldZ = nNewZ; mrBuffer.getBPixel(nScanlineIndex) = basegfx::BPixel(aNewColor, 0xff); } else { basegfx::BPixel& rDest = mrBuffer.getBPixel(nScanlineIndex); if(rDest.getOpacity()) { // mix color and existing color const double fOld(1.0 - fOpacity); fOpacity *= 255.0; rDest.setRed((sal_uInt8)(((double)rDest.getRed() * fOld) + (aNewColor.getRed() * fOpacity))); rDest.setGreen((sal_uInt8)(((double)rDest.getGreen() * fOld) + (aNewColor.getGreen() * fOpacity))); rDest.setBlue((sal_uInt8)(((double)rDest.getBlue() * fOld) + (aNewColor.getBlue() * fOpacity))); if((sal_uInt8)255 != rDest.getOpacity()) { // mix opacities by adding double fNewOpacity(rDest.getOpacity() + fOpacity); if(fNewOpacity > 255.0) { // set full opacity rDest.setOpacity(0xff); } else { // set new opacity which is still transparent, so set no z rDest.setOpacity((sal_uInt8)(fNewOpacity)); } } } else { // set color and opacity rDest = basegfx::BPixel(aNewColor, (sal_uInt8)(fOpacity * 255.0)); } } } } // increments { nScanlineIndex++; nXA++; aZ.increment(); if(bUseTex) { aTex.increment(); } if(bUseNrm) { aNrm.increment(); } if(bUseCol) { aCol.increment(); } } } } } } } } void BZPolyRaCon::processLine(const basegfx::B3DScanlineEntry& rEntry, sal_Int32 nLine) { if(nLine >= 0L && nLine < (sal_Int32)mrBuffer.getHeight()) { sal_Int32 nXA((sal_uInt32)(rEntry.getXInterpolator().getVal())); sal_Int32 nXB((sal_uInt32)(rEntry.getXInterpolator().getVal() + rEntry.getXInterpolator().getInc())); if(nXA == nXB) { // only one position, get values and set direct if(nXA >= 0L && nXA < (sal_Int32)mrBuffer.getWidth()) { const sal_uInt32 nScanlineIndex(mrBuffer.getIndexFromXY((sal_uInt32)nXA, (sal_uInt32)nLine)); sal_uInt16& rOldZ(mrBuffer.getZ(nScanlineIndex)); const sal_uInt16 nNewZ((sal_uInt16)(rEntry.getZInterpolator().getVal()) + 0x00ff); if(nNewZ > rOldZ) { rOldZ = nNewZ; mrBuffer.getBPixel(nScanlineIndex) = basegfx::BPixel(mrMaterial.getColor(), 0xff); } } } else { double fZStart(rEntry.getZInterpolator().getVal()); double fZStop(fZStart + rEntry.getZInterpolator().getInc()); if(nXB < nXA) { ::std::swap(nXB, nXA); ::std::swap(fZStart, fZStop); } const basegfx::BPixel aPixel(mrMaterial.getColor(), 0xff); const sal_uInt32 nSpanLength(nXB - nXA); basegfx::BDInterpolator aZ(fZStart, fZStop, nSpanLength); if(nXA < 0L) { const double fIncrement(-nXA); nXA = 0L; aZ.increment(fIncrement); } if(nXB > (sal_Int32)mrBuffer.getWidth()) { nXB = mrBuffer.getWidth(); } if(nXA < nXB) { sal_uInt32 nScanlineIndex(mrBuffer.getIndexFromXY((sal_uInt32)nXA, (sal_uInt32)nLine)); while(nXA < nXB) { sal_uInt16& rOldZ(mrBuffer.getZ(nScanlineIndex)); const sal_uInt16 nNewZ((sal_uInt16)(aZ.getVal()) + 0x00ff); if(nNewZ > rOldZ) { rOldZ = nNewZ; mrBuffer.getBPixel(nScanlineIndex) = aPixel; } nScanlineIndex++; nXA++; aZ.increment(); } } } } } } // end of anonymous namespace } // end of namespace drawinglayer ////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// namespace drawinglayer { namespace texture { class GeoTexSvxMono : public GeoTexSvx { protected: basegfx::BColor maSingleColor; double mfOpacity; public: GeoTexSvxMono(const basegfx::BColor& rSingleColor, double fOpacity); // compare operator virtual bool operator==(const GeoTexSvx& rGeoTexSvx) const; virtual void modifyBColor(const basegfx::B2DPoint& rUV, basegfx::BColor& rBColor, double& rfOpacity) const; virtual void modifyOpacity(const basegfx::B2DPoint& rUV, double& rfOpacity) const; }; GeoTexSvxMono::GeoTexSvxMono(const basegfx::BColor& rSingleColor, double fOpacity) : maSingleColor(rSingleColor), mfOpacity(fOpacity) { } bool GeoTexSvxMono::operator==(const GeoTexSvx& rGeoTexSvx) const { const GeoTexSvxMono* pCompare = dynamic_cast< const GeoTexSvxMono* >(&rGeoTexSvx); return (pCompare && maSingleColor == pCompare->maSingleColor && mfOpacity == pCompare->mfOpacity); } void GeoTexSvxMono::modifyBColor(const basegfx::B2DPoint& /*rUV*/, basegfx::BColor& rBColor, double& /*rfOpacity*/) const { rBColor = maSingleColor; } void GeoTexSvxMono::modifyOpacity(const basegfx::B2DPoint& /*rUV*/, double& rfOpacity) const { rfOpacity = mfOpacity; } } // end of namespace texture } // end of namespace drawinglayer ////////////////////////////////////////////////////////////////////////////// namespace drawinglayer { namespace texture { class GeoTexSvxBitmap : public GeoTexSvx { protected: Bitmap maBitmap; BitmapReadAccess* mpRead; basegfx::B2DPoint maTopLeft; basegfx::B2DVector maSize; double mfMulX; double mfMulY; // helpers bool impIsValid(const basegfx::B2DPoint& rUV, sal_Int32& rX, sal_Int32& rY) const; public: GeoTexSvxBitmap(const Bitmap& rBitmap, const basegfx::B2DPoint& rTopLeft, const basegfx::B2DVector& rSize); virtual ~GeoTexSvxBitmap(); virtual void modifyBColor(const basegfx::B2DPoint& rUV, basegfx::BColor& rBColor, double& rfOpacity) const; virtual void modifyOpacity(const basegfx::B2DPoint& rUV, double& rfOpacity) const; }; GeoTexSvxBitmap::GeoTexSvxBitmap(const Bitmap& rBitmap, const basegfx::B2DPoint& rTopLeft, const basegfx::B2DVector& rSize) : maBitmap(rBitmap), mpRead(0L), maTopLeft(rTopLeft), maSize(rSize), mfMulX(0.0), mfMulY(0.0) { mpRead = maBitmap.AcquireReadAccess(); OSL_ENSURE(mpRead, "GeoTexSvxBitmap: Got no read access to Bitmap (!)"); mfMulX = (double)mpRead->Width() / maSize.getX(); mfMulY = (double)mpRead->Height() / maSize.getY(); } GeoTexSvxBitmap::~GeoTexSvxBitmap() { delete mpRead; } bool GeoTexSvxBitmap::impIsValid(const basegfx::B2DPoint& rUV, sal_Int32& rX, sal_Int32& rY) const { if(mpRead) { rX = (sal_Int32)((rUV.getX() - maTopLeft.getX()) * mfMulX); if(rX >= 0L && rX < mpRead->Width()) { rY = (sal_Int32)((rUV.getY() - maTopLeft.getY()) * mfMulY); return (rY >= 0L && rY < mpRead->Height()); } } return false; } void GeoTexSvxBitmap::modifyBColor(const basegfx::B2DPoint& rUV, basegfx::BColor& rBColor, double& rfOpacity) const { sal_Int32 nX, nY; if(impIsValid(rUV, nX, nY)) { const double fConvertColor(1.0 / 255.0); const BitmapColor aBMCol(mpRead->GetColor(nY, nX)); const basegfx::BColor aBSource( (double)aBMCol.GetRed() * fConvertColor, (double)aBMCol.GetGreen() * fConvertColor, (double)aBMCol.GetBlue() * fConvertColor); rBColor = aBSource; } else { rfOpacity = 0.0; } } void GeoTexSvxBitmap::modifyOpacity(const basegfx::B2DPoint& rUV, double& rfOpacity) const { sal_Int32 nX, nY; if(impIsValid(rUV, nX, nY)) { const BitmapColor aBMCol(mpRead->GetColor(nY, nX)); const Color aColor(aBMCol.GetRed(), aBMCol.GetGreen(), aBMCol.GetBlue()); rfOpacity = ((double)(0xff - aColor.GetLuminance()) * (1.0 / 255.0)); } else { rfOpacity = 0.0; } } } // end of namespace texture } // end of namespace drawinglayer ////////////////////////////////////////////////////////////////////////////// namespace drawinglayer { namespace texture { class GeoTexSvxBitmapTiled : public GeoTexSvxBitmap { protected: // helpers basegfx::B2DPoint impGetCorrected(const basegfx::B2DPoint& rUV) const { double fX(fmod(rUV.getX() - maTopLeft.getX(), maSize.getX())); double fY(fmod(rUV.getY() - maTopLeft.getY(), maSize.getY())); if(fX < 0.0) { fX += maSize.getX(); } if(fY < 0.0) { fY += maSize.getY(); } return basegfx::B2DPoint(fX + maTopLeft.getX(), fY + maTopLeft.getY()); } public: GeoTexSvxBitmapTiled(const Bitmap& rBitmap, const basegfx::B2DPoint& rTopLeft, const basegfx::B2DVector& rSize); virtual void modifyBColor(const basegfx::B2DPoint& rUV, basegfx::BColor& rBColor, double& rfOpacity) const; virtual void modifyOpacity(const basegfx::B2DPoint& rUV, double& rfOpacity) const; }; GeoTexSvxBitmapTiled::GeoTexSvxBitmapTiled(const Bitmap& rBitmap, const basegfx::B2DPoint& rTopLeft, const basegfx::B2DVector& rSize) : GeoTexSvxBitmap(rBitmap, rTopLeft, rSize) { } void GeoTexSvxBitmapTiled::modifyBColor(const basegfx::B2DPoint& rUV, basegfx::BColor& rBColor, double& rfOpacity) const { if(mpRead) { GeoTexSvxBitmap::modifyBColor(impGetCorrected(rUV), rBColor, rfOpacity); } } void GeoTexSvxBitmapTiled::modifyOpacity(const basegfx::B2DPoint& rUV, double& rfOpacity) const { if(mpRead) { GeoTexSvxBitmap::modifyOpacity(impGetCorrected(rUV), rfOpacity); } } } // end of namespace texture } // end of namespace drawinglayer ////////////////////////////////////////////////////////////////////////////// namespace drawinglayer { namespace texture { class GeoTexSvxMultiHatch : public GeoTexSvx { protected: basegfx::BColor maColor; double mfLogicPixelSize; GeoTexSvxHatch* mp0; GeoTexSvxHatch* mp1; GeoTexSvxHatch* mp2; // bitfield unsigned mbFillBackground : 1; // helpers bool impIsOnHatch(const basegfx::B2DPoint& rUV) const; public: GeoTexSvxMultiHatch(const primitive3d::HatchTexturePrimitive3D& rPrimitive, double fLogicPixelSize); virtual ~GeoTexSvxMultiHatch(); virtual void modifyBColor(const basegfx::B2DPoint& rUV, basegfx::BColor& rBColor, double& rfOpacity) const; virtual void modifyOpacity(const basegfx::B2DPoint& rUV, double& rfOpacity) const; // dada access bool getFillBackground() const { return mbFillBackground; } }; GeoTexSvxMultiHatch::GeoTexSvxMultiHatch(const primitive3d::HatchTexturePrimitive3D& rPrimitive, double fLogicPixelSize) : mfLogicPixelSize(fLogicPixelSize), mp0(0L), mp1(0L), mp2(0L) { const attribute::FillHatchAttribute& rHatch(rPrimitive.getHatch()); const basegfx::B2DRange aOutlineRange(0.0, 0.0, rPrimitive.getTextureSize().getX(), rPrimitive.getTextureSize().getY()); const double fAngleA(-rHatch.getAngle()); maColor = rHatch.getColor(); mbFillBackground = rHatch.isFillBackground(); mp0 = new GeoTexSvxHatch(aOutlineRange, rHatch.getDistance(), fAngleA); if(attribute::HATCHSTYLE_DOUBLE == rHatch.getStyle() || attribute::HATCHSTYLE_TRIPLE == rHatch.getStyle()) { mp1 = new GeoTexSvxHatch(aOutlineRange, rHatch.getDistance(), fAngleA + F_PI2); } if(attribute::HATCHSTYLE_TRIPLE == rHatch.getStyle()) { mp2 = new GeoTexSvxHatch(aOutlineRange, rHatch.getDistance(), fAngleA + F_PI4); } } GeoTexSvxMultiHatch::~GeoTexSvxMultiHatch() { delete mp0; delete mp1; delete mp2; } bool GeoTexSvxMultiHatch::impIsOnHatch(const basegfx::B2DPoint& rUV) const { double fSmallestDistance(); if(mp0->getDistanceToHatch(rUV) < mfLogicPixelSize) { return true; } if(mp1 && mp1->getDistanceToHatch(rUV) < mfLogicPixelSize) { return true; } if(mp2 && mp2->getDistanceToHatch(rUV) < mfLogicPixelSize) { return true; } return false; } void GeoTexSvxMultiHatch::modifyBColor(const basegfx::B2DPoint& rUV, basegfx::BColor& rBColor, double& rfOpacity) const { if(impIsOnHatch(rUV)) { rBColor = maColor; } else if(!mbFillBackground) { rfOpacity = 0.0; } } void GeoTexSvxMultiHatch::modifyOpacity(const basegfx::B2DPoint& rUV, double& rfOpacity) const { if(mbFillBackground || impIsOnHatch(rUV)) { rfOpacity = 1.0; } else { rfOpacity = 0.0; } } } // end of namespace texture } // end of namespace drawinglayer ////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// namespace drawinglayer { namespace processor3d { void DefaultProcessor3D::impRender_GRX3(const primitive3d::GradientTexturePrimitive3D& rPrimitive, bool bTransparence) { const primitive3d::Primitive3DSequence& rSubSequence = rPrimitive.getChildren(); if(rSubSequence.hasElements()) { // rescue values const bool bOldModulate(mbModulate); mbModulate = rPrimitive.getModulate(); const bool bOldFilter(mbFilter); mbFilter = rPrimitive.getFilter(); texture::GeoTexSvx* pOldTex = (bTransparence) ? mpTransparenceGeoTexSvx : mpGeoTexSvx; // create texture const attribute::FillGradientAttribute& rFillGradient = rPrimitive.getGradient(); const basegfx::B2DRange aOutlineRange(0.0, 0.0, rPrimitive.getTextureSize().getX(), rPrimitive.getTextureSize().getY()); const attribute::GradientStyle aGradientStyle(rFillGradient.getStyle()); sal_uInt32 nSteps(rFillGradient.getSteps()); const basegfx::BColor aStart(rFillGradient.getStartColor()); const basegfx::BColor aEnd(rFillGradient.getEndColor()); const sal_uInt32 nMaxSteps(sal_uInt32((aStart.getMaximumDistance(aEnd) * 127.5) + 0.5)); texture::GeoTexSvx* pNewTex = 0L; if(nMaxSteps) { // there IS a color distance if(nSteps == 0L) { nSteps = nMaxSteps; } if(nSteps < 2L) { nSteps = 2L; } if(nSteps > nMaxSteps) { nSteps = nMaxSteps; } switch(aGradientStyle) { case attribute::GRADIENTSTYLE_LINEAR: { pNewTex = new texture::GeoTexSvxGradientLinear(aOutlineRange, aStart, aEnd, nSteps, rFillGradient.getBorder(), -rFillGradient.getAngle()); break; } case attribute::GRADIENTSTYLE_AXIAL: { pNewTex = new texture::GeoTexSvxGradientAxial(aOutlineRange, aStart, aEnd, nSteps, rFillGradient.getBorder(), -rFillGradient.getAngle()); break; } case attribute::GRADIENTSTYLE_RADIAL: { pNewTex = new texture::GeoTexSvxGradientRadial(aOutlineRange, aStart, aEnd, nSteps, rFillGradient.getBorder(), rFillGradient.getOffsetX(), rFillGradient.getOffsetY()); break; } case attribute::GRADIENTSTYLE_ELLIPTICAL: { pNewTex = new texture::GeoTexSvxGradientElliptical(aOutlineRange, aStart, aEnd, nSteps, rFillGradient.getBorder(), rFillGradient.getOffsetX(), rFillGradient.getOffsetY(), -rFillGradient.getAngle()); break; } case attribute::GRADIENTSTYLE_SQUARE: { pNewTex = new texture::GeoTexSvxGradientSquare(aOutlineRange, aStart, aEnd, nSteps, rFillGradient.getBorder(), rFillGradient.getOffsetX(), rFillGradient.getOffsetY(), -rFillGradient.getAngle()); break; } case attribute::GRADIENTSTYLE_RECT: { pNewTex = new texture::GeoTexSvxGradientRect(aOutlineRange, aStart, aEnd, nSteps, rFillGradient.getBorder(), rFillGradient.getOffsetX(), rFillGradient.getOffsetY(), -rFillGradient.getAngle()); break; } } } else { // no color distance -> same color, use simple texture pNewTex = new texture::GeoTexSvxMono(aStart, 1.0 - aStart.luminance()); } // set created texture if(bTransparence) { mpTransparenceGeoTexSvx = pNewTex; } else { mpGeoTexSvx = pNewTex; } // process sub-list process(rSubSequence); // delete texture delete pNewTex; // restore values mbModulate = bOldModulate; mbFilter = bOldFilter; if(bTransparence) { mpTransparenceGeoTexSvx = pOldTex; } else { mpGeoTexSvx = pOldTex; } } } void DefaultProcessor3D::impRender_HAX3(const primitive3d::HatchTexturePrimitive3D& rPrimitive) { const primitive3d::Primitive3DSequence& rSubSequence = rPrimitive.getChildren(); if(rSubSequence.hasElements()) { // rescue values const bool bOldModulate(mbModulate); mbModulate = rPrimitive.getModulate(); const bool bOldFilter(mbFilter); mbFilter = rPrimitive.getFilter(); texture::GeoTexSvx* pOldTex = mpGeoTexSvx; // calculate logic pixel size in world coordinates const basegfx::B3DPoint aZero(maInvWorldToView * basegfx::B3DPoint(0.0, 0.0, 0.0)); const basegfx::B3DPoint aOne(maInvWorldToView * basegfx::B3DPoint(1.0, 1.0, 1.0)); const basegfx::B3DVector aLogicPixelSizeWorld(aOne - aZero); double fLogicPixelSizeWorld(fabs(aLogicPixelSizeWorld.getX())); if(fabs(aLogicPixelSizeWorld.getY()) > fLogicPixelSizeWorld) { fLogicPixelSizeWorld = fabs(aLogicPixelSizeWorld.getY()); } if(fabs(aLogicPixelSizeWorld.getZ()) > fLogicPixelSizeWorld) { fLogicPixelSizeWorld = fabs(aLogicPixelSizeWorld.getZ()); } // calculate logic pixel size in texture coordinates const double fLogicTexSizeX(fLogicPixelSizeWorld / rPrimitive.getTextureSize().getX()); const double fLogicTexSizeY(fLogicPixelSizeWorld / rPrimitive.getTextureSize().getY()); const double fLogicTexSize(fLogicTexSizeX > fLogicTexSizeY ? fLogicTexSizeX : fLogicTexSizeY); // create texture and set texture::GeoTexSvxMultiHatch* pNewTex = new texture::GeoTexSvxMultiHatch(rPrimitive, fLogicTexSize); mpGeoTexSvx = pNewTex; // process sub-list process(rSubSequence); // delete texture delete mpGeoTexSvx; // restore values mbModulate = bOldModulate; mbFilter = bOldFilter; mpGeoTexSvx = pOldTex; } } void DefaultProcessor3D::impRender_BMX3(const primitive3d::BitmapTexturePrimitive3D& rPrimitive) { const primitive3d::Primitive3DSequence& rSubSequence = rPrimitive.getChildren(); if(rSubSequence.hasElements()) { // rescue values const bool bOldModulate(mbModulate); mbModulate = rPrimitive.getModulate(); const bool bOldFilter(mbFilter); mbFilter = rPrimitive.getFilter(); texture::GeoTexSvx* pOldTex = mpGeoTexSvx; // create texture const attribute::FillBitmapAttribute& rFillBitmapAttribute = rPrimitive.getBitmap(); if(rFillBitmapAttribute.getTiling()) { mpGeoTexSvx = new texture::GeoTexSvxBitmapTiled( rFillBitmapAttribute.getBitmap(), rFillBitmapAttribute.getTopLeft() * rPrimitive.getTextureSize(), rFillBitmapAttribute.getSize() * rPrimitive.getTextureSize()); } else { mpGeoTexSvx = new texture::GeoTexSvxBitmap( rFillBitmapAttribute.getBitmap(), rFillBitmapAttribute.getTopLeft() * rPrimitive.getTextureSize(), rFillBitmapAttribute.getSize() * rPrimitive.getTextureSize()); } // process sub-list process(rSubSequence); // delete texture delete mpGeoTexSvx; // restore values mbModulate = bOldModulate; mbFilter = bOldFilter; mpGeoTexSvx = pOldTex; } } void DefaultProcessor3D::impRender_MCOL(const primitive3d::ModifiedColorPrimitive3D& rModifiedCandidate) { const primitive3d::Primitive3DSequence& rSubSequence = rModifiedCandidate.getChildren(); if(rSubSequence.hasElements()) { maBColorModifierStack.push(rModifiedCandidate.getColorModifier()); process(rModifiedCandidate.getChildren()); maBColorModifierStack.pop(); } } void DefaultProcessor3D::impRender_POH3(const primitive3d::PolygonHairlinePrimitive3D& rPrimitive) { basegfx::B3DPolygon aHairline(rPrimitive.getB3DPolygon()); if(aHairline.count() && mpBZPixelRaster) { // hairlines need no extra data, clear it aHairline.clearTextureCoordinates(); aHairline.clearNormals(); aHairline.clearBColors(); // transform to device coordinates (-1.0 .. 1.0) and check for visibility aHairline.transform(maWorldToView); const basegfx::B3DRange a3DRange(basegfx::tools::getRange(aHairline)); const basegfx::B2DRange a2DRange(a3DRange.getMinX(), a3DRange.getMinY(), a3DRange.getMaxX(), a3DRange.getMaxY()); if(a2DRange.overlaps(maRasterRange)) { const attribute::MaterialAttribute3D aMaterial(rPrimitive.getBColor()); BZPolyRaCon aNewRaCon(false, *mpBZPixelRaster, aMaterial, *this); aNewRaCon.addPolygon(aHairline, maInvEyeToView); aNewRaCon.rasterconvert(0L, mpBZPixelRaster->getHeight()); } } } void DefaultProcessor3D::impRender_POM3(const primitive3d::PolyPolygonMaterialPrimitive3D& rPrimitive) { basegfx::B3DPolyPolygon aFill(rPrimitive.getB3DPolyPolygon()); basegfx::BColor aObjectColor(rPrimitive.getMaterial().getColor()); bool bPaintIt(aFill.count() && mpBZPixelRaster); if(bPaintIt) { // get rid of texture coordinates if there is no texture if(aFill.areTextureCoordinatesUsed() && !mpGeoTexSvx && !mpTransparenceGeoTexSvx) { aFill.clearTextureCoordinates(); } // transform to device coordinates (-1.0 .. 1.0) and check for visibility aFill.transform(maWorldToView); const basegfx::B3DRange a3DRange(basegfx::tools::getRange(aFill)); const basegfx::B2DRange a2DRange(a3DRange.getMinX(), a3DRange.getMinY(), a3DRange.getMaxX(), a3DRange.getMaxY()); bPaintIt = a2DRange.overlaps(maRasterRange); } // check if it shall be painted regarding hiding of normals (backface culling) if(bPaintIt && !rPrimitive.getDoubleSided()) { // get plane normal of polygon in view coordinates (with ZBuffer values), // left-handed coordinate system const basegfx::B3DVector aPlaneNormal(aFill.getB3DPolygon(0L).getNormal()); if(aPlaneNormal.getZ() > 0.0) { bPaintIt = false; } } if(bPaintIt) { ::com::sun::star::drawing::ShadeMode aShadeMode(mrSdrSceneAttribute.getShadeMode()); basegfx::B3DHomMatrix aNormalTransform(maWorldToEye); if(mrSdrSceneAttribute.getTwoSidedLighting()) { // get plane normal of polygon in view coordinates (with ZBuffer values), // left-handed coordinate system const basegfx::B3DVector aPlaneNormal(aFill.getB3DPolygon(0L).getNormal()); if(aPlaneNormal.getZ() > 0.0) { // mirror normals aNormalTransform.scale(-1.0, -1.0, -1.0); } } if(::com::sun::star::drawing::ShadeMode_PHONG == aShadeMode) { // phong shading if(aFill.areNormalsUsed()) { // transform normals to eye coor aFill.transformNormals(aNormalTransform); } else { // fallback to gouraud when no normals available aShadeMode = ::com::sun::star::drawing::ShadeMode_SMOOTH; } } if(::com::sun::star::drawing::ShadeMode_SMOOTH == aShadeMode) { // gouraud shading if(aFill.areNormalsUsed()) { // transform normals to eye coor aFill.transformNormals(aNormalTransform); // prepare color model parameters, evtl. use blend color const basegfx::BColor aColor(mbModulate ? basegfx::BColor(1.0, 1.0, 1.0) : rPrimitive.getMaterial().getColor()); const basegfx::BColor& rSpecular(rPrimitive.getMaterial().getSpecular()); const basegfx::BColor& rEmission(rPrimitive.getMaterial().getEmission()); const sal_uInt16 nSpecularIntensity(rPrimitive.getMaterial().getSpecularIntensity()); // solve color model for each normal vector, set colors at points. Clear normals. for(sal_uInt32 a(0L); a < aFill.count(); a++) { basegfx::B3DPolygon aPartFill(aFill.getB3DPolygon(a)); for(sal_uInt32 b(0L); b < aPartFill.count(); b++) { // solve color model. Transform normal to eye coor const basegfx::B3DVector aNormal(aPartFill.getNormal(b)); const basegfx::BColor aSolvedColor(mrSdrLightingAttribute.solveColorModel(aNormal, aColor, rSpecular, rEmission, nSpecularIntensity)); aPartFill.setBColor(b, aSolvedColor); } // clear normals on this part polygon and write it back aPartFill.clearNormals(); aFill.setB3DPolygon(a, aPartFill); } } else { // fallback to flat when no normals available aShadeMode = ::com::sun::star::drawing::ShadeMode_FLAT; } } if(::com::sun::star::drawing::ShadeMode_FLAT == aShadeMode) { // flat shading. Clear normals and colors aFill.clearNormals(); aFill.clearBColors(); // get plane vector in eye coordinates const basegfx::B3DVector aPlaneEyeNormal(aNormalTransform * rPrimitive.getB3DPolyPolygon().getB3DPolygon(0L).getNormal()); // prepare color model parameters, evtl. use blend color const basegfx::BColor aColor(mbModulate ? basegfx::BColor(1.0, 1.0, 1.0) : rPrimitive.getMaterial().getColor()); const basegfx::BColor& rSpecular(rPrimitive.getMaterial().getSpecular()); const basegfx::BColor& rEmission(rPrimitive.getMaterial().getEmission()); const sal_uInt16 nSpecularIntensity(rPrimitive.getMaterial().getSpecularIntensity()); // solve color model for plane vector and use that color for whole plane aObjectColor = mrSdrLightingAttribute.solveColorModel(aPlaneEyeNormal, aColor, rSpecular, rEmission, nSpecularIntensity); } if(::com::sun::star::drawing::ShadeMode_DRAFT == aShadeMode) { // draft, just use object color which is already set. Delete all other infos aFill.clearNormals(); aFill.clearBColors(); } } if(bPaintIt) { // draw it to ZBuffer const attribute::MaterialAttribute3D aMaterial( aObjectColor, rPrimitive.getMaterial().getSpecular(), rPrimitive.getMaterial().getEmission(), rPrimitive.getMaterial().getSpecularIntensity()); BZPolyRaCon aNewRaCon(true, *mpBZPixelRaster, aMaterial, *this); for(sal_uInt32 a(0L); a < aFill.count(); a++) { aNewRaCon.addPolygon(aFill.getB3DPolygon(a), maInvEyeToView); } aNewRaCon.rasterconvert(0L, mpBZPixelRaster->getHeight()); } } void DefaultProcessor3D::impRender_TRN3(const primitive3d::TransformPrimitive3D& rTransformCandidate) { // remember current transformations basegfx::B3DHomMatrix aLastWorldToView(maWorldToView); basegfx::B3DHomMatrix aLastWorldToEye(maWorldToEye); basegfx::B3DHomMatrix aLastInvWorldToView(maInvWorldToView); // create new transformations maWorldToView = maWorldToView * rTransformCandidate.getTransformation(); maWorldToEye = maWorldToEye * rTransformCandidate.getTransformation(); maInvWorldToView = maWorldToView; maInvWorldToView.invert(); // let break down process(rTransformCandidate.getChildren()); // restore transformations maWorldToView = aLastWorldToView; maWorldToEye = aLastWorldToEye; maInvWorldToView = aLastInvWorldToView; } void DefaultProcessor3D::process(const primitive3d::Primitive3DSequence& rSource) { if(rSource.hasElements()) { const sal_Int32 nCount(rSource.getLength()); for(sal_Int32 a(0L); a < nCount; a++) { // get reference const primitive3d::Primitive3DReference xReference(rSource[a]); if(xReference.is()) { // try to cast to BasePrimitive3D implementation const primitive3d::BasePrimitive3D* pBasePrimitive = dynamic_cast< const primitive3d::BasePrimitive3D* >(xReference.get()); if(pBasePrimitive) { // it is a BasePrimitive3D implementation, use getPrimitiveID() call for switch switch(pBasePrimitive->getPrimitiveID()) { case PRIMITIVE3D_ID_GRADIENTTEXTUREPRIMITIVE3D : { // GradientTexturePrimitive3D const primitive3d::GradientTexturePrimitive3D& rPrimitive = static_cast< const primitive3d::GradientTexturePrimitive3D& >(*pBasePrimitive); impRender_GRX3(rPrimitive, false); break; } case PRIMITIVE3D_ID_HATCHTEXTUREPRIMITIVE3D : { // HatchTexturePrimitive3D static bool bDoHatchDecomposition(true); if(bDoHatchDecomposition) { // let break down process(pBasePrimitive->get3DDecomposition(getTime())); } else { // hatchTexturePrimitive3D const primitive3d::HatchTexturePrimitive3D& rPrimitive = static_cast< const primitive3d::HatchTexturePrimitive3D& >(*pBasePrimitive); impRender_HAX3(rPrimitive); } break; } case PRIMITIVE3D_ID_BITMAPTEXTUREPRIMITIVE3D : { // BitmapTexturePrimitive3D const primitive3d::BitmapTexturePrimitive3D& rPrimitive = static_cast< const primitive3d::BitmapTexturePrimitive3D& >(*pBasePrimitive); impRender_BMX3(rPrimitive); break; } case PRIMITIVE3D_ID_ALPHATEXTUREPRIMITIVE3D : { // AlphaTexturePrimitive3D const primitive3d::AlphaTexturePrimitive3D& rPrimitive = static_cast< const primitive3d::AlphaTexturePrimitive3D& >(*pBasePrimitive); if(mbProcessTransparent) { impRender_GRX3(rPrimitive, true); } else { mbContainsTransparent = true; } break; } case PRIMITIVE3D_ID_MODIFIEDCOLORPRIMITIVE3D : { // ModifiedColorPrimitive3D // Force output to unified color. const primitive3d::ModifiedColorPrimitive3D& rPrimitive = static_cast< const primitive3d::ModifiedColorPrimitive3D& >(*pBasePrimitive); impRender_MCOL(rPrimitive); break; } case PRIMITIVE3D_ID_POLYGONHAIRLINEPRIMITIVE3D : { // directdraw of PolygonHairlinePrimitive3D const primitive3d::PolygonHairlinePrimitive3D& rPrimitive = static_cast< const primitive3d::PolygonHairlinePrimitive3D& >(*pBasePrimitive); if((bool)mbProcessTransparent == (0L != mpTransparenceGeoTexSvx)) { impRender_POH3(rPrimitive); } break; } case PRIMITIVE3D_ID_POLYPOLYGONMATERIALPRIMITIVE3D : { // directdraw of PolyPolygonMaterialPrimitive3D const primitive3d::PolyPolygonMaterialPrimitive3D& rPrimitive = static_cast< const primitive3d::PolyPolygonMaterialPrimitive3D& >(*pBasePrimitive); if((bool)mbProcessTransparent == (0L != mpTransparenceGeoTexSvx)) { impRender_POM3(rPrimitive); } break; } case PRIMITIVE3D_ID_TRANSFORMPRIMITIVE3D : { // transform group (TransformPrimitive3D) impRender_TRN3(static_cast< const primitive3d::TransformPrimitive3D& >(*pBasePrimitive)); break; } case PRIMITIVE3D_ID_SDRLABELPRIMITIVE3D : { // SdrLabelPrimitive3D. Accept, but ignore. Is handled by the scenePrimitive decompose // method which creates 2d text objects at the 3d-projection-dependent positions. break; } default: { // process recursively process(pBasePrimitive->get3DDecomposition(getTime())); break; } } } else { // unknown implementation, use UNO API call instead and process recursively com::sun::star::graphic::Primitive3DParameters aPrimitive3DParameters; aPrimitive3DParameters.Time = getTime(); process(xReference->getDecomposition(aPrimitive3DParameters)); } } } } } void DefaultProcessor3D::processNonTransparent(const primitive3d::Primitive3DSequence& rSource) { mbProcessTransparent = false; mbContainsTransparent = false; process(rSource); } void DefaultProcessor3D::processTransparent(const primitive3d::Primitive3DSequence& rSource) { if(mbContainsTransparent) { mbProcessTransparent = true; process(rSource); } } DefaultProcessor3D::DefaultProcessor3D( const geometry::ViewInformation2D& rViewInformation, const geometry::Transformation3D& rTransformation3D, const attribute::SdrSceneAttribute& rSdrSceneAttribute, const attribute::SdrLightingAttribute& rSdrLightingAttribute, double fSizeX, double fSizeY, const basegfx::B2DRange& rVisiblePart) : BaseProcessor3D(rViewInformation.getViewTime()), mrSdrSceneAttribute(rSdrSceneAttribute), mrSdrLightingAttribute(rSdrLightingAttribute), mpGeoTexSvx(0L), mpTransparenceGeoTexSvx(0L), mbModulate(false), mbFilter(false), mbProcessTransparent(false), mbContainsTransparent(false) { // generate ViewSizes const double fFullViewSizeX((rViewInformation.getViewTransformation() * basegfx::B2DVector(fSizeX, 0.0)).getLength()); const double fFullViewSizeY((rViewInformation.getViewTransformation() * basegfx::B2DVector(0.0, fSizeY)).getLength()); const double fViewSizeX(fFullViewSizeX * rVisiblePart.getWidth()); const double fViewSizeY(fFullViewSizeY * rVisiblePart.getHeight()); const sal_uInt32 nViewSizeX((sal_uInt32)floor(fViewSizeX)); const sal_uInt32 nViewSizeY((sal_uInt32)floor(fViewSizeY)); if(nViewSizeX && nViewSizeY) { // create view unit buffer mpBZPixelRaster = new basegfx::BZPixelRaster(nViewSizeX + 1L, nViewSizeY + 1L); OSL_ENSURE(mpBZPixelRaster, "DefaultProcessor3D: Could not allocate basegfx::BZPixelRaster (!)"); // create DeviceToView // outcome is [-1.0 .. 1.0] in X,Y and Z. { // step one: // // bring from [-1.0 .. 1.0] in X,Y and Z to [0.0 .. 1.0]. Also // necessary to // - flip Y due to screen orientation // - flip Z due to Z-Buffer orientation from back to front maDeviceToView.scale(0.5, -0.5, -0.5); maDeviceToView.translate(0.5, 0.5, 0.5); } { // step two: // // bring from [0.0 .. 1.0] in X,Y and Z to view cordinates. also: // - scale Z to [0.0 .. fMaxZDepth] const double fMaxZDepth(double(0x0000ff00)); maDeviceToView.translate(-rVisiblePart.getMinX(), -rVisiblePart.getMinY(), 0.0); maDeviceToView.scale(fFullViewSizeX, fFullViewSizeY, fMaxZDepth); } // create world to eye transformation maWorldToEye = rTransformation3D.getOrientation() * rTransformation3D.getTransformation(); // create EyeToView transformation maWorldToView = maDeviceToView * rTransformation3D.getProjection() * maWorldToEye; // create inverse EyeToView transformation maInvEyeToView = maDeviceToView * rTransformation3D.getProjection(); maInvEyeToView.invert(); // create inverse WorldToView transformation maInvWorldToView = maWorldToView; maInvWorldToView.invert(); // prepare maRasterRange maRasterRange.expand(basegfx::B2DPoint(0.0, 0.0)); maRasterRange.expand(basegfx::B2DPoint(mpBZPixelRaster->getWidth(), mpBZPixelRaster->getHeight())); } } DefaultProcessor3D::~DefaultProcessor3D() { if(mpBZPixelRaster) { delete mpBZPixelRaster; } } BitmapEx DefaultProcessor3D::getBitmapEx() const { if(mpBZPixelRaster) { return basegfx::getBitmapEx(*mpBZPixelRaster); } return BitmapEx(); } } // end of namespace processor3d } // end of namespace drawinglayer ////////////////////////////////////////////////////////////////////////////// // eof