23245f3322
git-svn-id: https://svn.disconnected-by-peer.at/svn/linamh/trunk/linamh@1438 6952d904-891a-0410-993b-d76249ca496b
464 lines
14 KiB
Diff
464 lines
14 KiB
Diff
DIB Engine: Implement Polygon
|
|
|
|
From: Massimo Del Fedele <max@veneto.com>
|
|
|
|
|
|
---
|
|
|
|
dlls/winedib.drv/graphics.c | 405 ++++++++++++++++++++++++++++++++++++++++++-
|
|
1 files changed, 395 insertions(+), 10 deletions(-)
|
|
|
|
|
|
diff --git a/dlls/winedib.drv/graphics.c b/dlls/winedib.drv/graphics.c
|
|
index 8dda082..e416488 100644
|
|
--- a/dlls/winedib.drv/graphics.c
|
|
+++ b/dlls/winedib.drv/graphics.c
|
|
@@ -36,6 +36,281 @@ static inline void OrderInt(int *i1, int *i2)
|
|
}
|
|
}
|
|
|
|
+#define LEFT_SIDE 1
|
|
+#define TOP_SIDE 2
|
|
+#define RIGHT_SIDE 4
|
|
+#define BOTTOM_SIDE 8
|
|
+
|
|
+/* clips a line segment by a rectangular window */
|
|
+static inline BYTE outCodes(const POINT *p, const RECT *r)
|
|
+{
|
|
+ BYTE Code = 0;
|
|
+
|
|
+ if(p->y < r->top)
|
|
+ Code |= TOP_SIDE;
|
|
+ else if(p->y >= r->bottom)
|
|
+ Code |= BOTTOM_SIDE;
|
|
+ if(p->x >= r->right)
|
|
+ Code |= RIGHT_SIDE;
|
|
+ else if(p->x < r->left)
|
|
+ Code |= LEFT_SIDE;
|
|
+ return Code;
|
|
+}
|
|
+
|
|
+static BOOL ClipLine(const POINT *p1, const POINT *p2, const RECT *r, POINT *pc1, POINT *pc2)
|
|
+{
|
|
+ BYTE outCode1,outCode2;
|
|
+ int tmp;
|
|
+ BYTE tmpCode;
|
|
+
|
|
+ pc1->x = p1->x; pc1->y = p1->y;
|
|
+ pc2->x = p2->x; pc2->y = p2->y;
|
|
+ while(TRUE)
|
|
+ {
|
|
+ outCode1 = outCodes(pc1, r);
|
|
+ outCode2 = outCodes(pc2, r);
|
|
+ if(outCode1 & outCode2)
|
|
+ return FALSE;
|
|
+ if(!outCode1 && !outCode2)
|
|
+ return TRUE;
|
|
+ if(!outCode1)
|
|
+ {
|
|
+ tmp = pc1->x; pc1->x = pc2->x; pc2->x = tmp;
|
|
+ tmp = pc1->y; pc1->y = pc2->y; pc2->y = tmp;
|
|
+ tmpCode = outCode1; outCode1 = outCode2; outCode2 = tmpCode;
|
|
+ }
|
|
+ if(outCode1 & TOP_SIDE)
|
|
+ {
|
|
+ pc1->x += MulDiv(pc2->x - pc1->x, r->top - pc1->y, pc2->y - pc1->y);
|
|
+ pc1->y = r->top;
|
|
+ }
|
|
+ else if(outCode1 & BOTTOM_SIDE)
|
|
+ {
|
|
+ pc1->x += MulDiv(pc2->x - pc1->x, r->bottom - 1 - pc1->y, pc2->y - pc1->y);
|
|
+ pc1->y = r->bottom - 1;
|
|
+ }
|
|
+ else if(outCode1 & RIGHT_SIDE)
|
|
+ {
|
|
+ pc1->y += MulDiv(pc2->y - pc1->y, r->right - 1 - pc1->x, pc2->x - pc1->x);
|
|
+ pc1->x = r->right - 1;
|
|
+ }
|
|
+ else if(outCode1 & LEFT_SIDE)
|
|
+ {
|
|
+ pc1->y += MulDiv(pc2->y - pc1->y, r->left - pc1->x, pc2->x - pc1->x);
|
|
+ pc1->x = r->left;
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
+/* Clips a polygon by an horizontal/vertical line
|
|
+ which indicates the side :
|
|
+*/
|
|
+static inline BOOL PointInside(const POINT *p, const RECT *r, BYTE side)
|
|
+{
|
|
+ switch(side)
|
|
+ {
|
|
+ case 1: /* left */
|
|
+ return p->x >= r->left;
|
|
+ case 2: /* top */
|
|
+ return p->y >= r->top;
|
|
+ case 4: /* right */
|
|
+ return p->x < r->right;
|
|
+ case 8: /* bottom */
|
|
+ return p->y < r->bottom;
|
|
+ default:
|
|
+ return FALSE;
|
|
+ }
|
|
+}
|
|
+
|
|
+static inline void SideIntersect(const POINT *p1, const POINT *p2, const RECT *r, BYTE side, POINT *inters)
|
|
+{
|
|
+ switch( side )
|
|
+ {
|
|
+ case LEFT_SIDE: /* left */
|
|
+ inters->x = r->left;
|
|
+ inters->y = MulDiv(p2->y - p1->y, r->left - p1->x, p2->x - p1->x) + p1->y;
|
|
+ break;
|
|
+ case TOP_SIDE: /* top */
|
|
+ inters->x = MulDiv(p2->x - p1->x, r->top - p1->y, p2->y - p1->y) + p1->x;
|
|
+ inters->y = r->bottom;
|
|
+ break;
|
|
+ case RIGHT_SIDE: /* right */
|
|
+ inters->x = r->right - 1;
|
|
+ inters->y = MulDiv(p2->y - p1->y, r->right - 1 - p1->x, p2->x - p1->x) + p1->y;
|
|
+ break;
|
|
+ case BOTTOM_SIDE: /* bottom */
|
|
+ inters->x = MulDiv(p2->x - p1->x, r->bottom - 1 - p1->y, p2->y - p1->y) + p1->x;
|
|
+ inters->y = r->bottom - 1;
|
|
+ break;
|
|
+ default:
|
|
+ break;
|
|
+ }
|
|
+}
|
|
+
|
|
+
|
|
+static BOOL ClipPolygonBySide(const POINT *pt, int count, const RECT *r, BYTE side, POINT **clipped, int *clippedCount)
|
|
+{
|
|
+ int iPoint;
|
|
+ const POINT *p1, *p2;
|
|
+ POINT *pOut;
|
|
+
|
|
+ if(!(*clipped = HeapAlloc(GetProcessHeap(), 0, sizeof(POINT) * count * 2)))
|
|
+ return FALSE;
|
|
+ pOut = *clipped;
|
|
+ *clippedCount = 0;
|
|
+
|
|
+ p1 = pt + count - 1;
|
|
+ p2 = pt;
|
|
+ for(iPoint = 0 ; iPoint < count ; iPoint++)
|
|
+ {
|
|
+ if(PointInside(p2, r, side))
|
|
+ {
|
|
+ /* point p is "inside" */
|
|
+ if(!PointInside(p1, r, side))
|
|
+ {
|
|
+ /* p is "inside" and s is "outside" */
|
|
+ SideIntersect(p2, p1, r, side, pOut++);
|
|
+ (*clippedCount)++;
|
|
+ }
|
|
+ pOut->x = p2->x;
|
|
+ pOut->y = p2->y;
|
|
+ pOut++;
|
|
+ (*clippedCount)++;
|
|
+ }
|
|
+ else if(PointInside( p1, r, side ))
|
|
+ {
|
|
+ /* s is "inside" and p is "outside" */
|
|
+ SideIntersect(p1, p2, r, side, pOut++);
|
|
+ (*clippedCount)++;
|
|
+ }
|
|
+ p1 = p2++;
|
|
+ }
|
|
+ return *clippedCount;
|
|
+}
|
|
+
|
|
+
|
|
+/* Clips a polygon by a rectangular window - returns a new polygon */
|
|
+static BOOL ClipPolygon(const POINT* pt, int count, const RECT *r, POINT **newPt, int *newCount)
|
|
+{
|
|
+ POINT *pc1, *pc2;
|
|
+ int count1, count2;
|
|
+ BOOL res;
|
|
+
|
|
+ if(!ClipPolygonBySide(pt, count, r, LEFT_SIDE, &pc1, &count1))
|
|
+ return FALSE;
|
|
+ res = ClipPolygonBySide(pc1, count1, r, TOP_SIDE, &pc2, &count2);
|
|
+ HeapFree(GetProcessHeap(), 0, pc1);
|
|
+ if(!res)
|
|
+ return FALSE;
|
|
+ res = ClipPolygonBySide(pc2, count2, r, RIGHT_SIDE, &pc1, &count1);
|
|
+ HeapFree(GetProcessHeap(), 0, pc2);
|
|
+ if(!res)
|
|
+ return FALSE;
|
|
+ res = ClipPolygonBySide(pc1, count1, r, BOTTOM_SIDE, &pc2, &count2);
|
|
+ HeapFree(GetProcessHeap(), 0, pc1);
|
|
+ if(!res)
|
|
+ return FALSE;
|
|
+
|
|
+ *newPt = pc2;
|
|
+ *newCount = count2;
|
|
+ return TRUE;
|
|
+}
|
|
+
|
|
+/* Intersects a line given by 2 points with an horizontal scan line at height y */
|
|
+static BOOL ScanLine(const POINT *p1, const POINT *p2, int ys, POINT *pRes)
|
|
+{
|
|
+ if(!pRes)
|
|
+ return FALSE;
|
|
+
|
|
+ /* if line lies completely over or under scan line, no intersection */
|
|
+ if((p1->y < ys && p2->y < ys) || (p1->y > ys && p2->y > ys))
|
|
+ return FALSE;
|
|
+
|
|
+ /* if line is parallel to x axis, we consider it not intersecting */
|
|
+ if(p1->y == p2->y)
|
|
+ return FALSE;
|
|
+
|
|
+ pRes->x = MulDiv(p2->x - p1->x, ys - p1->y, p2->y - p1->y) + p1->x;
|
|
+ pRes->y = ys;
|
|
+ return TRUE;
|
|
+}
|
|
+
|
|
+/* Gets an x-ordered list of intersection points of a scanline at position y
|
|
+ with a polygon/polyline */
|
|
+static BOOL ScanPolygon(const POINT *pt, int count, int ys, POINT **scans, int *scanCount)
|
|
+{
|
|
+ const POINT *p1, *p2;
|
|
+ POINT *pDest;
|
|
+ int iPoint;
|
|
+ POINT *ps1, *ps2;
|
|
+ int i, j, tmp;
|
|
+
|
|
+ /* if not at least 2 points, nothing to return */
|
|
+ if(count < 2)
|
|
+ return FALSE;
|
|
+
|
|
+ /* intersections count is AT MOST 'count'; we don't care to
|
|
+ allocate exact memory needed */
|
|
+ *scans = HeapAlloc(GetProcessHeap(), 0, sizeof(POINT)*count);
|
|
+ if(!*scans)
|
|
+ return FALSE;
|
|
+
|
|
+ /* builds unordered intersections */
|
|
+ pDest = *scans;
|
|
+ *scanCount = 0;
|
|
+ p2 = pt;
|
|
+ for(iPoint = 0; iPoint < count-1; iPoint++)
|
|
+ {
|
|
+ p1 = p2;
|
|
+ p2++;
|
|
+ if(ScanLine(p1, p2, ys, pDest))
|
|
+ {
|
|
+ pDest++;
|
|
+ (*scanCount)++;
|
|
+ }
|
|
+ }
|
|
+ p1 = p2;
|
|
+ p2 = pt;
|
|
+ if(ScanLine(p1, p2, ys, pDest))
|
|
+ {
|
|
+ pDest++;
|
|
+ (*scanCount)++;
|
|
+ }
|
|
+
|
|
+ /* now we sort the list -- duped point are left into
|
|
+ as they're needed for the scanline fill algorithm */
|
|
+ for(i = 0, ps1 = *scans; i < *scanCount -1; i++, ps1++)
|
|
+ for(j = i+1, ps2 = ps1+1; j < *scanCount; j++, ps2++)
|
|
+ if(ps2->x < ps1->x)
|
|
+ {
|
|
+ tmp = ps2->x;
|
|
+ ps2->x = ps1->x;
|
|
+ ps1->x = tmp;
|
|
+ tmp = ps2->y;
|
|
+ ps2->y = ps1->y;
|
|
+ ps1->y = tmp;
|
|
+ }
|
|
+
|
|
+ return TRUE;
|
|
+}
|
|
+
|
|
+/* gets bounding box of a polygon */
|
|
+void PolygonBoundingBox(const POINT *pt, int count, RECT *bBox)
|
|
+{
|
|
+ const POINT *p;
|
|
+ int iPoint;
|
|
+
|
|
+ bBox->left = MAXLONG; bBox->right = -MAXLONG;
|
|
+ bBox->top = MAXLONG; bBox->bottom = -MAXLONG;
|
|
+ for(p = pt, iPoint = 0; iPoint < count; iPoint++, p++)
|
|
+ {
|
|
+ if(p->x < bBox->left ) bBox->left = p->x;
|
|
+ if(p->x > bBox->right ) bBox->right = p->x;
|
|
+ if(p->y < bBox->top ) bBox->top = p->y;
|
|
+ if(p->y > bBox->bottom) bBox->bottom = p->y;
|
|
+ }
|
|
+}
|
|
+
|
|
/***********************************************************************
|
|
* DIBDRV_Arc
|
|
*/
|
|
@@ -291,45 +566,155 @@ BOOL DIBDRV_Pie( DIBDRVPHYSDEV *physDev, int left, int top, int right, int botto
|
|
/**********************************************************************
|
|
* DIBDRV_Polygon
|
|
*/
|
|
-BOOL DIBDRV_Polygon( DIBDRVPHYSDEV *physDev, const POINT* pt, int count )
|
|
+BOOL DIBDRV_Polygon( DIBDRVPHYSDEV *physDev, const POINT* ptw, int count )
|
|
{
|
|
BOOL res;
|
|
+ POINT *pt;
|
|
+ RECT *r;
|
|
+ int iRec;
|
|
+ POINT *clipped;
|
|
+ int clippedCount;
|
|
+ RECT bBox;
|
|
+ int ys;
|
|
+ POINT *scans;
|
|
+ int scanCount, iScan;
|
|
+ const POINT *p1, *p2;
|
|
+ int iPoint;
|
|
+ POINT pc1, pc2;
|
|
|
|
- MAYBE(TRACE("physDev:%p, pt:%p, count:%d\n", physDev, pt, count));
|
|
+ MAYBE(TRACE("physDev:%p, pt:%p, count:%d\n", physDev, ptw, count));
|
|
|
|
if(physDev->hasDIB)
|
|
{
|
|
/* DIB section selected in, use DIB Engine */
|
|
- ONCE(FIXME("STUB\n"));
|
|
- res = TRUE;
|
|
+
|
|
+ res = FALSE;
|
|
+
|
|
+ /* first converts all points to device coords */
|
|
+ if(!(pt = HeapAlloc(GetProcessHeap(), 0, sizeof(POINT) * count)))
|
|
+ goto fin;
|
|
+ memcpy(pt, ptw, sizeof(POINT) * count);
|
|
+ LPtoDP(physDev->hdc, pt, count);
|
|
+
|
|
+ /* cycle on all current clipping rectangles */
|
|
+ r = physDev->regionRects;
|
|
+ for(iRec = 0; iRec < physDev->regionRectCount; iRec++, r++)
|
|
+ {
|
|
+ /* filled area */
|
|
+ if(ClipPolygon(pt, count, r, &clipped, &clippedCount))
|
|
+ {
|
|
+ /* gets polygon bounding box -- for ytop and ybottom */
|
|
+ PolygonBoundingBox(clipped, clippedCount, &bBox);
|
|
+
|
|
+ /* gets all ordered intersections of polygon with
|
|
+ current scanline */
|
|
+ for(ys = bBox.top; ys < bBox.bottom; ys++)
|
|
+ {
|
|
+ if(ScanPolygon(clipped, clippedCount, ys, &scans, &scanCount))
|
|
+ {
|
|
+ if(scanCount >= 2)
|
|
+ {
|
|
+ res = TRUE;
|
|
+ p1 = scans;
|
|
+ p2 = p1+1;
|
|
+ iScan = 0;
|
|
+ while(iScan < scanCount - 1)
|
|
+ {
|
|
+ physDev->brushHLine(physDev, p1->x, p2->x, ys);
|
|
+ p1 +=2;
|
|
+ p2 +=2;
|
|
+ iScan +=2;
|
|
+ }
|
|
+ }
|
|
+ HeapFree(GetProcessHeap(), 0, scans);
|
|
+ }
|
|
+ }
|
|
+ HeapFree(GetProcessHeap(), 0, clipped);
|
|
+ }
|
|
+
|
|
+ /* perimeter -- don't use PolyLine for speed */
|
|
+ p2 = pt;
|
|
+ for(iPoint = 0; iPoint < count -1; iPoint++)
|
|
+ {
|
|
+ p1 = p2++;
|
|
+ if(ClipLine(p1, p2, r, &pc1, &pc2))
|
|
+ {
|
|
+ res = TRUE;
|
|
+ physDev->penLine(physDev, pc1.x, pc1.y, pc2.x, pc2.y);
|
|
+ }
|
|
+ }
|
|
+ p1 = p2;
|
|
+ p2 = pt;
|
|
+ if(ClipLine(p1, p2, r, &pc1, &pc2))
|
|
+ {
|
|
+ res = TRUE;
|
|
+ physDev->penLine(physDev, pc1.x, pc1.y, pc2.x, pc2.y);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ HeapFree(GetProcessHeap(), 0, pt);
|
|
+
|
|
}
|
|
else
|
|
{
|
|
/* DDB selected in, use X11 driver */
|
|
- res = _DIBDRV_GetDisplayDriver()->pPolygon(physDev->X11PhysDev, pt, count);
|
|
+ res = _DIBDRV_GetDisplayDriver()->pPolygon(physDev->X11PhysDev, ptw, count);
|
|
}
|
|
+fin:
|
|
return res;
|
|
}
|
|
|
|
/**********************************************************************
|
|
* DIBDRV_Polyline
|
|
*/
|
|
-BOOL DIBDRV_Polyline( DIBDRVPHYSDEV *physDev, const POINT* pt, int count )
|
|
+BOOL DIBDRV_Polyline( DIBDRVPHYSDEV *physDev, const POINT* ptw, int count )
|
|
{
|
|
BOOL res;
|
|
|
|
- MAYBE(TRACE("physDev:%p, pt:%p, count:%d\n", physDev, pt, count));
|
|
+ MAYBE(TRACE("physDev:%p, pt:%p, count:%d\n", physDev, ptw, count));
|
|
|
|
if(physDev->hasDIB)
|
|
{
|
|
/* DIB section selected in, use DIB Engine */
|
|
- ONCE(FIXME("STUB\n"));
|
|
- res = TRUE;
|
|
+ POINT *pt;
|
|
+ RECT *r;
|
|
+ POINT pc1, pc2;
|
|
+ int iRec, iPoint;
|
|
+
|
|
+ if(count < 2)
|
|
+ return FALSE;
|
|
+ res = FALSE;
|
|
+
|
|
+ /* first converts all points to device coords */
|
|
+ if(!(pt = HeapAlloc(GetProcessHeap(), 0, sizeof(POINT) * count)))
|
|
+ return FALSE;
|
|
+ memcpy(pt, ptw, sizeof(POINT) * count);
|
|
+ LPtoDP(physDev->hdc, pt, count);
|
|
+
|
|
+ r = physDev->regionRects;
|
|
+ for(iRec = 0; iRec < physDev->regionRectCount; iRec++)
|
|
+ {
|
|
+ const POINT *p2 = pt, *p1;
|
|
+ for(iPoint = 0; iPoint < count -1; iPoint++)
|
|
+ {
|
|
+ p1 = p2++;
|
|
+ if(ClipLine(p1, p2, r, &pc1, &pc2))
|
|
+ {
|
|
+ res = TRUE;
|
|
+ physDev->penLine(physDev, pc1.x, pc1.y, pc2.x, pc2.y);
|
|
+ }
|
|
+ }
|
|
+ r++;
|
|
+ }
|
|
+
|
|
+ HeapFree(GetProcessHeap(), 0, pt);
|
|
+
|
|
+ return res;
|
|
}
|
|
else
|
|
{
|
|
/* DDB selected in, use X11 driver */
|
|
- res = _DIBDRV_GetDisplayDriver()->pPolyline(physDev->X11PhysDev, pt, count);
|
|
+ res = _DIBDRV_GetDisplayDriver()->pPolyline(physDev->X11PhysDev, ptw, count);
|
|
}
|
|
return res;
|
|
}
|