diff options
author | Kurt Zenker <kz@openoffice.org> | 2005-11-02 12:29:39 +0000 |
---|---|---|
committer | Kurt Zenker <kz@openoffice.org> | 2005-11-02 12:29:39 +0000 |
commit | 7fb7a608a10b515a822aefca7c94b9295fe46156 (patch) | |
tree | e7033aa74b54e636e59494ae80475b60594f285e /vcl/source/gdi/outdev.cxx | |
parent | 628820c311a9e36f3d77dc9dcc9da11d4b1fccb2 (diff) |
INTEGRATION: CWS canvas02 (1.33.84); FILE MERGED
2005/10/09 07:06:58 thb 1.33.84.7: RESYNC: (1.33-1.35); FILE MERGED
2005/09/07 14:58:23 mbu 1.33.84.6: SetTriangleClipRegion() now plays well with the rest of the clipping code
2005/08/19 15:15:26 mbu 1.33.84.5: platform independent float to integer truncation
2005/08/19 14:50:06 mbu 1.33.84.4: consolidating spans in triangle clipper
2005/08/19 13:57:03 thb 1.33.84.3: #i10000# Using non-asm version for non-Windows
2005/08/18 15:31:02 mbu 1.33.84.2: latest changes in ImplSetTriangleClipRegion()
2005/08/17 15:28:36 mbu 1.33.84.1: SetTriangleClipRegion()
Diffstat (limited to 'vcl/source/gdi/outdev.cxx')
-rw-r--r-- | vcl/source/gdi/outdev.cxx | 668 |
1 files changed, 666 insertions, 2 deletions
diff --git a/vcl/source/gdi/outdev.cxx b/vcl/source/gdi/outdev.cxx index 0e05587bebae..3447d359f71d 100644 --- a/vcl/source/gdi/outdev.cxx +++ b/vcl/source/gdi/outdev.cxx @@ -4,9 +4,9 @@ * * $RCSfile: outdev.cxx,v $ * - * $Revision: 1.36 $ + * $Revision: 1.37 $ * - * last change: $Author: rt $ $Date: 2005-10-17 14:01:55 $ + * last change: $Author: kz $ $Date: 2005-11-02 13:29:39 $ * * The Contents of this file are made available subject to * the terms of GNU Lesser General Public License Version 2.1. @@ -983,6 +983,634 @@ void OutputDevice::ImplSetClipRegion( const Region* pRegion ) // ----------------------------------------------------------------------- +namespace +{ + inline int iround( float x ) + { + sal_Int32 a = *reinterpret_cast<const sal_Int32 *>(&x); + sal_Int32 exponent = (127 + 31) - ((a >> 23) & 0xFF); + sal_Int32 r = ((sal_Int32(a) << 8) | (1U << 31)) >> exponent; + r &= ((exponent - 32) >> 31); + sal_Int32 sign = a >> 31; + return r = (r ^ sign) - sign; + } + + inline int floorDiv(int a, int b) + { + if(b == 0) + return 0x80000000; + if(a >= 0) + return a / b; + int q = -(-a / b); // quotient + int r = -a % b; // remainder + if(r) + q--; + return q; + } + + inline int floorMod( int a, int b ) + { + if(b == 0) + return 0x80000000; + if(a >= 0) + return a % b; + int r = -a % b; // remainder + if(r) + r = b - r; + return r; + } + + inline int ceilDiv( int a, int b ) + { + if(b == 0) + return 0x80000000; + a += - 1 + b; + if(a >= 0) + return a / b; + int q = -(-a / b); // quotient + int r = -a % b; // remainder + if(r) + q--; + return q; + } + + inline int ceilMod( int a, int b ) + { + if(b == 0) + return 0x80000000; + a += - 1 + b; + if(a >= 0) + return (a % b) + 1 - b; + int r = -a % b; + if(r) + r = b - r; + return r + 1 - b; + } + + inline int ceilFix4(int x) { return (x + 0xF) & 0xFFFFFFF0; } + + struct vertex + { + float x,y; + inline vertex( const Point &p ) + : x((float)p.getX()),y((float)p.getY()) {} + }; + + template<class T> inline void swap(T &a, T &b) { T t=a; a=b; b=t; } + + class SpanIterator + { + public: + + SpanIterator( sal_Int32 *pTable, size_t dwPitch, sal_Int32 dwNumScanlines ); + std::pair<sal_Int32,sal_Int32> GetNextSpan( void ); + sal_Int32 GetNumRemainingScanlines( void ); + sal_Int32 GetNumEqualScanlines( void ); + SpanIterator &operator++ (); + SpanIterator &Skip( sal_Int32 dwNumScanlines ); + sal_Int32 GetRemainingSpans( void ) const { return maNumSpans; } + + private: + + sal_Int32 *mpTable; + sal_Int32 *mpSpanArray; + sal_Int32 maNumSpans; + sal_Int32 maRemainingScanlines; + size_t maPitch; + }; + + inline SpanIterator::SpanIterator( sal_Int32 *pTable, size_t dwPitch, sal_Int32 dwNumScanlines ) + : mpTable(pTable),maPitch(dwPitch),maRemainingScanlines(dwNumScanlines) + { + sal_Int32 *pNumSpans = mpTable; + mpSpanArray = reinterpret_cast<sal_Int32 *>(pNumSpans+2); + maNumSpans = *pNumSpans; + } + + inline SpanIterator &SpanIterator::operator++ () + { + --maRemainingScanlines; + mpTable += maPitch; + sal_Int32 *pNumSpans = mpTable; + mpSpanArray = reinterpret_cast<sal_Int32 *>(pNumSpans+2); + maNumSpans = *pNumSpans; + return (*this); + } + + inline SpanIterator &SpanIterator::Skip( sal_Int32 dwNumScanlines ) + { + // don't skip more scanlines than there are... + if(dwNumScanlines > maRemainingScanlines) + dwNumScanlines = maRemainingScanlines; + + // skip in one fellow swoop... + maRemainingScanlines -= dwNumScanlines; + mpTable += maPitch * dwNumScanlines; + + // initialize necessary query fields... + sal_Int32 *pNumSpans = mpTable; + mpSpanArray = reinterpret_cast<sal_Int32 *>(pNumSpans+2); + maNumSpans = *pNumSpans; + return (*this); + } + + inline std::pair<sal_Int32,sal_Int32> SpanIterator::GetNextSpan( void ) + { + sal_Int32 x(0); + sal_Int32 w(0); + if(maNumSpans) + { + x = *mpSpanArray++; + w = *mpSpanArray++; + --maNumSpans; + } + return std::pair<sal_Int32,sal_Int32>(x,w); + } + + inline sal_Int32 SpanIterator::GetNumEqualScanlines( void ) + { + return mpTable[1]; + } + + inline sal_Int32 SpanIterator::GetNumRemainingScanlines( void ) + { + return maRemainingScanlines; + } + + class ScanlineContainer + { + + public: + + ScanlineContainer( sal_uInt32 dwNumScanlines, + sal_uInt32 dwNumSpansPerScanline ); + + ~ScanlineContainer( void ); + + void InsertSpan( sal_Int32 y, sal_Int32 lx, sal_Int32 rx ); + + SpanIterator Iterate( void ) const { return SpanIterator(mpTable,maPitch,maNumScanlines); } + + inline sal_uInt32 GetNumSpans( void ) const { return maNumberOfSpans; } + + void Consolidate( void ); + + private: + + // the span table will assist in determinate exactly how many clipping + // regions [that is *spans*] we will end up with. + // the counter for this purpose is right ahead. + sal_uInt32 maNumberOfSpans; + + struct span + { + sal_Int32 x; + sal_Int32 w; + }; + + sal_uInt32 maNumScanlines; + sal_uInt32 maNumSpansPerScanline; + sal_Int32 *mpTable; + size_t maPitch; + }; + + ScanlineContainer::ScanlineContainer( sal_uInt32 dwNumScanlines, + sal_uInt32 dwNumSpansPerScanline ) : maNumScanlines(dwNumScanlines), + maNumSpansPerScanline(dwNumSpansPerScanline) + { + // since each triangle could possibly add another span + // we can calculate the upper limit by [num scanlines * num triangles]. + const sal_uInt32 dwNumPossibleRegions = dwNumScanlines*dwNumSpansPerScanline; + + // calculate the number of bytes the span table will consume + const size_t dwTableSize = dwNumPossibleRegions*sizeof(span)+dwNumScanlines*(sizeof(sal_Int32)<<1); + + // allocate the span table [on the stack] + mpTable = static_cast<sal_Int32 *>(rtl_allocateMemory(dwTableSize)); + + // calculate the table pitch, that is how many int's do i need to get from a scanline to the next. + maPitch = (dwNumSpansPerScanline*sizeof(span)/sizeof(sal_Int32))+2; + + // we need to initialize the table here. + // the first *int* on each scanline tells us how many spans are on it. + sal_Int32 *pNumSpans = mpTable; + for(unsigned int i=0; i<dwNumScanlines; ++i) + { + pNumSpans[0] = 0; + pNumSpans[1] = 0; + pNumSpans += maPitch; + } + + maNumberOfSpans = 0; + } + + ScanlineContainer::~ScanlineContainer( void ) + { + rtl_freeMemory(mpTable); + } + + void ScanlineContainer::InsertSpan( sal_Int32 y, sal_Int32 lx, sal_Int32 rx ) + { + // there's new incoming span which we need to store in the table. + // first see if its width contributes a valid span. + if(sal_Int32 dwSpanWidth = rx-lx) + { + // first select the appropriate scanline the new span. + sal_Int32 *pNumSpans = mpTable+(y*maPitch); + span *pSpanArray = reinterpret_cast<span *>(pNumSpans+2); + + // retrieve the number of already contained spans. + sal_Int32 dwNumSpan = *pNumSpans; + + // since we need to sort the spans from top to bottom + // and left to right, we need to find the correct location + // in the table. + sal_Int32 dwIndex = 0; + while(dwIndex<dwNumSpan) + { + // since we would like to avoid unnecessary spans + // we try to consolidate them if possible. + // consolidate with right neighbour + if(pSpanArray[dwIndex].x == rx) + { + pSpanArray[dwIndex].x = lx; + pSpanArray[dwIndex].w += dwSpanWidth; + return; + } + + // consolidate with left neighbour + if((pSpanArray[dwIndex].x+pSpanArray[dwIndex].w) == lx) + { + pSpanArray[dwIndex].w += rx-lx; + return; + } + + // no consolidation possible, either this is a completely + // seperate span or it is the first in the list. + if(pSpanArray[dwIndex].x > lx) + break; + + // forward to next element in the list. + ++dwIndex; + } + + // if we reach here, the new span needs to be stored + // in the table, increase the number of spans in the + // current scanline. + *pNumSpans = dwNumSpan+1; + + // keep the list of spans in sorted order. 'dwIndex' + // is where we want to store the new span. 'dwNumSpan' + // is the number of spans already there. now we need + // to move the offending spans out of the way. + while(dwIndex != dwNumSpan) + { + pSpanArray[dwNumSpan].x = pSpanArray[dwNumSpan-1].x; + pSpanArray[dwNumSpan].w = pSpanArray[dwNumSpan-1].w; + --dwNumSpan; + } + + // insert the new span + pSpanArray[dwIndex].x = lx; + pSpanArray[dwIndex].w = rx-lx; + + // remember the total number of spans in the table. + ++maNumberOfSpans; + } + } + + void ScanlineContainer::Consolidate( void ) + { + sal_Int32 *pScanline = mpTable; + + sal_Int32 dwRemaining = maNumScanlines; + while(dwRemaining) + { + sal_Int32 dwNumSpans = pScanline[0]; + sal_Int32 *pSpanArray = pScanline+2; + + sal_Int32 dwRest = dwRemaining-1; + sal_Int32 *pNext = pScanline; + while(dwRest) + { + pNext += maPitch; + sal_Int32 dwNumNextSpans = pNext[0]; + sal_Int32 *pSpanArrayNext = pNext+2; + if(dwNumSpans != dwNumNextSpans) + break; + + sal_Int32 dwCompare = dwNumSpans<<1; + while(dwCompare) + { + if(pSpanArray[dwCompare-1] != pSpanArrayNext[dwCompare-1]) + break; + --dwCompare; + } + if(dwCompare) + break; + + --dwRest; + } + + const sal_Int32 dwNumEqualScanlines(dwRemaining-dwRest); + pScanline[1] = dwNumEqualScanlines; + pScanline += maPitch*dwNumEqualScanlines; + dwRemaining -= dwNumEqualScanlines; + + // since we track the total number of spans to generate, + // we need to account for consolidated scanlines here. + if(dwNumEqualScanlines > 1) + maNumberOfSpans -= dwNumSpans * (dwNumEqualScanlines-1); + } + } +} + +// TODO: we should consider passing a basegfx b2dpolypolygon here to +// ensure that the signature isn't misleading. +// if we could pass a b2dpolypolygon here, we could easily triangulate it. +void OutputDevice::ImplSetTriangleClipRegion( const PolyPolygon &rPolyPolygon ) +{ + DBG_TESTSOLARMUTEX(); + + if(!(IsDeviceOutputNecessary())) + return; + if(!(mpGraphics)) + if(!(ImplGetGraphics())) + return; + + // first of all we need to know the upper limit + // of the amount of possible clipping regions. + sal_Int32 maxy = SAL_MIN_INT32; + sal_Int32 miny = SAL_MAX_INT32; + sal_uInt32 dwNumTriangles = 0; + for(unsigned int i=0; i<rPolyPolygon.Count(); ++i) + { + const Polygon &rPoly = rPolyPolygon.GetObject(i); + const sal_Int32 dwNumVertices = rPoly.GetSize(); + if(!(dwNumVertices % 3)) + { + for(unsigned int j=0; j<rPoly.GetSize(); ++j) + { + const Point &p = rPoly.GetPoint(j); + if(p.Y() < miny) + miny = p.Y(); + if(p.Y() > maxy) + maxy = p.Y(); + } + dwNumTriangles += dwNumVertices / 3; + } + } + + const sal_uInt32 dwNumScanlines = (maxy-miny); + if(!(dwNumScanlines)) + { + // indicates that no output needs to be produced + // since the clipping region did not provide any + // visible areas. + mbOutputClipped = TRUE; + + // indicates that a clip region has been + // presented to the output device. + mbClipRegion = TRUE; + + // indicates that the set clipping region + // has been processed. + mbClipRegionSet = TRUE; + + // under 'normal' circumstances a new clipping region + // needs to be processed by ImplInitClipRegion(), + // which we need to circumvent. + mbInitClipRegion = FALSE; + return; + } + + // this container provides all services we need to + // efficiently store/retrieve spans from the table. + const sal_uInt32 dwNumSpansPerScanline = dwNumTriangles; + ScanlineContainer container(dwNumScanlines,dwNumSpansPerScanline); + + // convert the incoming polypolygon to spans, we assume that + // the polypolygon has already been triangulated since we don't + // want to use the basegfx-types here. this could be leveraged + // after the tools-types had been removed. + for(unsigned int i=0; i<rPolyPolygon.Count(); ++i) + { + const Polygon &rPoly = rPolyPolygon.GetObject(i); + const sal_uInt32 dwNumVertices = rPoly.GetSize(); + if(!(dwNumVertices % 3)) + { + for(unsigned int j=0; j<dwNumVertices; j+=3) + { + const Point &p0 = rPoly.GetPoint(j+0); + const Point &p1 = rPoly.GetPoint(j+1); + const Point &p2 = rPoly.GetPoint(j+2); + + // what now follows is an extremely fast triangle + // rasterizer from which all tricky and interesting + // parts were forcibly amputated. + // note: top.left fill-convention + vertex v0(p0); + vertex v1(p1); + vertex v2(p2); + + //sprintf(string,"[%f,%f] [%f,%f] [%f,%f]\n",v0.x,v0.y,v1.x,v1.y,v2.x,v2.y); + //OSL_TRACE(string); + + if(v0.y > v2.y) swap(v0, v2); + if(v1.y > v2.y) swap(v1, v2); + if(v0.y > v1.y) swap(v0, v1); + + const float float2fixed(16.0f); + const float fixed2float(1.0f/float2fixed); + + // vertex coordinates of the triangle [28.4 fixed-point] + const int i4x0 = iround(float2fixed * (v0.x - 0.5f)); + const int i4y0 = iround(float2fixed * (v0.y - 0.5f)); + const int i4x1 = iround(float2fixed * (v1.x - 0.5f)); + const int i4y1 = iround(float2fixed * (v1.y - 0.5f)); + const int i4x2 = iround(float2fixed * (v2.x - 0.5f)); + const int i4y2 = iround(float2fixed * (v2.y - 0.5f)); + + // vertex coordinate deltas [28.4 fixed-point] + const int i4dx12 = i4x1-i4x0; + const int i4dy12 = i4y1-i4y0; + const int i4dx13 = i4x2-i4x0; + const int i4dy13 = i4y2-i4y0; + const int i4dx23 = i4x2-i4x1; + const int i4dy23 = i4y2-i4y1; + + // vertex coordinate deltas [32-bit floating-point] + const float fdx12 = i4dx12 * fixed2float; + const float fdy12 = i4dy12 * fixed2float; + const float fdx13 = i4dx13 * fixed2float; + const float fdy13 = i4dy13 * fixed2float; + + // slope of edges [quotient,remainder] + const int mq12 = floorDiv(i4dx12 << 4, i4dy12 << 4); + const int mq13 = floorDiv(i4dx13 << 4, i4dy13 << 4); + const int mq23 = floorDiv(i4dx23 << 4, i4dy23 << 4); + const int mr12 = floorMod(i4dx12 << 4, i4dy12 << 4); + const int mr13 = floorMod(i4dx13 << 4, i4dy13 << 4); + const int mr23 = floorMod(i4dx23 << 4, i4dy23 << 4); + + // convert the vertical coordinates back to integers. + // according to the top-left fillrule we need to step + // the coordinates to the ceiling. + const int y0 = (i4y0+15)>>4; + const int y1 = (i4y1+15)>>4; + const int y2 = (i4y2+15)>>4; + + // calculate the value of the horizontal coordinate + // from the edge that 'spans' the triangle. + const int x = ceilDiv(i4dx13*i4dy12 + i4x0*i4dy13, i4dy13); + + // this will hold the horizontal coordinates + // of the seperate spans during the rasterization process. + int lx,rx; + + // this pair will serve as the error accumulator while + // we step along the edges. + int ld,rd,lD,rD; + + // these are the edge and error stepping values that + // will be used while stepping. + int lQ,rQ,lR,rR; + + if(i4x1 < x) + { + lx = ceilDiv(i4dx12 * (ceilFix4(i4y0) - i4y0) + i4x0 * i4dy12, i4dy12 << 4); + ld = ceilMod(i4dx12 * (ceilFix4(i4y0) - i4y0) + i4x0 * i4dy12, i4dy12 << 4); + rx = ceilDiv(i4dx13 * (ceilFix4(i4y0) - i4y0) + i4x0 * i4dy13, i4dy13 << 4); + rd = ceilMod(i4dx13 * (ceilFix4(i4y0) - i4y0) + i4x0 * i4dy13, i4dy13 << 4); + lQ = mq12; + rQ = mq13; + lR = mr12; + rR = mr13; + lD = i4dy12 << 4; + rD = i4dy13 << 4; + } + else + { + lx = ceilDiv(i4dx13 * (ceilFix4(i4y0) - i4y0) + i4x0 * i4dy13, i4dy13 << 4); + ld = ceilMod(i4dx13 * (ceilFix4(i4y0) - i4y0) + i4x0 * i4dy13, i4dy13 << 4); + rx = ceilDiv(i4dx12 * (ceilFix4(i4y0) - i4y0) + i4x0 * i4dy12, i4dy12 << 4); + rd = ceilMod(i4dx12 * (ceilFix4(i4y0) - i4y0) + i4x0 * i4dy12, i4dy12 << 4); + lQ = mq13; + rQ = mq12; + lR = mr13; + rR = mr12; + lD = i4dy13 << 4; + rD = i4dy12 << 4; + } + + for(signed int y=y0; y<y1; y++) + { + container.InsertSpan(y-miny,lx,rx); + + lx += lQ; ld += lR; + if(ld > 0) { ld -= lD; lx += 1; } + rx += rQ; rd += rR; + if(rd > 0) { rd -= rD; rx += 1; } + } + + if(i4x1 < x) + { + lx = ceilDiv(i4dx23 * (ceilFix4(i4y1) - i4y1) + i4x1 * i4dy23, i4dy23 << 4); + ld = ceilMod(i4dx23 * (ceilFix4(i4y1) - i4y1) + i4x1 * i4dy23, i4dy23 << 4); + rx = ceilDiv(i4dx13 * (ceilFix4(i4y1) - i4y0) + i4x0 * i4dy13, i4dy13 << 4); + rd = ceilMod(i4dx13 * (ceilFix4(i4y1) - i4y0) + i4x0 * i4dy13, i4dy13 << 4); + lQ = mq23; + lR = mr23; + lD = i4dy23 << 4; + } + else + { + rx = ceilDiv(i4dx23 * (ceilFix4(i4y1) - i4y1) + i4x1 * i4dy23, i4dy23 << 4); + rd = ceilMod(i4dx23 * (ceilFix4(i4y1) - i4y1) + i4x1 * i4dy23, i4dy23 << 4); + rQ = mq23; + rR = mr23; + rD = i4dy23 << 4; + } + + for(signed int y=y1; y<y2; y++) + { + container.InsertSpan(y-miny,lx,rx); + + lx += lQ; ld += lR; + if(ld > 0) { ld -= lD; lx += 1; } + rx += rQ; rd += rR; + if(rd > 0) { rd -= rD; rx += 1; } + } + } + } + } + + // now try to consolidate as many scanlines as possible. + // please note that this will probably change the number + // of spans [at least this is why we do all this hassle]. + // so, if you use 'consolidate' you should *use* this + // information during iteration, because the 'graphics' + // object we tell all those regions about is a bit, + // hm, how to say, *picky* if you supply not correctly + // the amount of regions. + container.Consolidate(); + + sal_Int32 offset_x = 0; + sal_Int32 offset_y = 0; + if ( GetOutDevType() == OUTDEV_WINDOW ) + { + offset_x = mnOutOffX+mnOutOffOrigX; + offset_y = mnOutOffY+mnOutOffOrigY; + } + + // now forward the spantable to the graphics handler. + SpanIterator it(container.Iterate()); + mpGraphics->BeginSetClipRegion( container.GetNumSpans() ); + while(miny < maxy) + { + const sal_Int32 dwNumEqual(it.GetNumEqualScanlines()); + while(it.GetRemainingSpans()) + { + // retrieve the next span [x-coordinate, width] from the current scanline. + std::pair<sal_Int32,sal_Int32> span(it.GetNextSpan()); + + // now forward this to the graphics object. + // the only part that is worth noting is that we use + // the number of equal spanlines [the current is always the + // first one of the equal bunch] as the height of the region. + mpGraphics->UnionClipRegion( offset_x+span.first, + offset_y+miny, + span.second, + dwNumEqual, + this ); + } + it.Skip(dwNumEqual); + miny += dwNumEqual; + } + mpGraphics->EndSetClipRegion(); + + // indicates that no output needs to be produced + // since the clipping region did not provide any + // visible areas. the clip covers the whole area + // if there's not a single region. + mbOutputClipped = (container.GetNumSpans() == 0); + + // indicates that a clip region has been + // presented to the output device. + mbClipRegion = TRUE; + + // indicates that the set clipping region + // has been processed. + mbClipRegionSet = TRUE; + + // under 'normal' circumstances a new clipping region + // needs to be processed by ImplInitClipRegion(), + // which we need to circumvent. + mbInitClipRegion = FALSE; +} + +// ----------------------------------------------------------------------- + void OutputDevice::SetClipRegion() { DBG_TRACE( "OutputDevice::SetClipRegion()" ); @@ -1022,6 +1650,42 @@ void OutputDevice::SetClipRegion( const Region& rRegion ) // ----------------------------------------------------------------------- +void OutputDevice::SetTriangleClipRegion( const PolyPolygon &rPolyPolygon ) +{ + DBG_TRACE( "OutputDevice::SetTriangleClipRegion( rPolyPolygon )" ); + DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); + + // in case the passed polypolygon is empty, use the + // existing SetClipRegion() method which gracefully + // unsets any previously set clipping region. + if(!(rPolyPolygon.Count())) + SetClipRegion(); + + sal_Int32 offset_x = 0; + sal_Int32 offset_y = 0; + if ( GetOutDevType() == OUTDEV_WINDOW ) + { + offset_x = mnOutOffX+mnOutOffOrigX; + offset_y = mnOutOffY+mnOutOffOrigY; + } + + // play nice with the rest of the system and provide an old-style region. + // the rest of this method does not rely on this. + maRegion = Region::GetRegionFromPolyPolygon( LogicToPixel(rPolyPolygon) ); + maRegion.Move(offset_x,offset_x); + + // feed region to metafile + if ( mpMetaFile ) + mpMetaFile->AddAction( new MetaClipRegionAction( maRegion, TRUE ) ); + + ImplSetTriangleClipRegion( rPolyPolygon ); + + if( mpAlphaVDev ) + mpAlphaVDev->SetTriangleClipRegion( rPolyPolygon ); +} + +// ----------------------------------------------------------------------- + Region OutputDevice::GetClipRegion() const { DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); |