gdi32 : completes WidenPath implementation

Laurent Vromman laurent at vromman.org
Fri Apr 13 01:18:32 CDT 2007


Changelog :
Completes WidenPath Implementation
WidenPath recognizes PS_JOINs and PS_ENDCAPs

 dlls/gdi32/path.c |  300 
+++++++++++++++++++++++++++++++++++++++++------------
 1 files changed, 234 insertions(+), 66 deletions(-)

-------------- next part --------------
diff --git a/dlls/gdi32/path.c b/dlls/gdi32/path.c
index 22c153f..d6789b3 100644
--- a/dlls/gdi32/path.c
+++ b/dlls/gdi32/path.c
@@ -1767,14 +1767,16 @@ static BOOL PATH_StrokePath(DC *dc, GdiPath *pPath)
     return ret;
 }
 
+#define round(x) (int)((x)>0?(x)+0.5:(x)-0.5)
 
 static BOOL PATH_WidenPath(DC *dc)
 {
-    INT i, j, numStrokes, nLinePts, penWidth, penWidthIn, penWidthOut, size;
+    INT i, j, numStrokes, nLinePts, penWidth, penWidthIn, penWidthOut, size, penStyle;
     BOOL ret = FALSE;
     GdiPath *pPath, *pNewPath, **pStrokes, *pUpPath, *pDownPath;
     EXTLOGPEN *elp;
-    FLOAT fCos, fSin, nPente;
+    LOGPEN *lp;
+    DWORD obj_type, joint, endcap, penType;
 
     pPath = &dc->path;
 
@@ -1786,14 +1788,41 @@ static BOOL PATH_WidenPath(DC *dc)
     PATH_FlattenPath(pPath);
 
     size = GetObjectW( dc->hPen, 0, NULL );
-    if (!size) return FALSE;
+    if (!size) {
+        SetLastError(ERROR_CAN_NOT_COMPLETE);
+        return FALSE;
+    }
 
     elp = HeapAlloc( GetProcessHeap(), 0, size );
-
+    lp = HeapAlloc( GetProcessHeap(), 0, sizeof(LOGPEN));
     GetObjectW( dc->hPen, size, elp );
+
+    obj_type = GetObjectType(dc->hPen);
+    if(obj_type == OBJ_PEN) {
+        memcpy(lp, elp, sizeof(LOGPEN));
+        penStyle = lp->lopnStyle;
+    }
+    else if(obj_type == OBJ_EXTPEN) {
+        penStyle = elp->elpPenStyle;
+    }
+    else {
+        SetLastError(ERROR_CAN_NOT_COMPLETE);
+        return FALSE;
+    }
+    endcap = (PS_ENDCAP_MASK & penStyle);
+    joint = (PS_JOIN_MASK & penStyle);
+    penType = (PS_TYPE_MASK & penStyle);
+
+    /* The function cannot apply to cosmetic pens */
+    if(obj_type == OBJ_EXTPEN && penType == PS_COSMETIC) {
+        SetLastError(ERROR_CAN_NOT_COMPLETE);
+        return FALSE;
+    }
+
     /* FIXME: add support for user style pens */
     penWidth = elp->elpWidth;
     HeapFree( GetProcessHeap(), 0, elp );
+    HeapFree( GetProcessHeap(), 0, lp );
 
     /* pen width must be strictly higher than 1 */
     if(penWidth == 1) {
@@ -1817,6 +1846,7 @@ static BOOL PATH_WidenPath(DC *dc)
     pStrokes[0]->numEntriesUsed = 0;
 
     for(i = 0, j = 0; i < pPath->numEntriesUsed; i++, j++) {
+        POINT point;
         if((i == 0 || (pPath->pFlags[i-1] & PT_CLOSEFIGURE)) &&
             (pPath->pFlags[i] != PT_MOVETO)) {
             ERR("Expected PT_MOVETO %s, got path flag %c\n",
@@ -1826,27 +1856,20 @@ static BOOL PATH_WidenPath(DC *dc)
         }
         switch(pPath->pFlags[i]) {
             case PT_MOVETO:
+                if(numStrokes > 0) {
+                    pStrokes[numStrokes - 1]->state = PATH_Closed;
+                }
                 numStrokes++;
                 j = 0;
                 pStrokes = HeapReAlloc(GetProcessHeap(), 0, pStrokes, numStrokes * sizeof(GdiPath*));
                 pStrokes[numStrokes - 1] = HeapAlloc(GetProcessHeap(), 0, sizeof(GdiPath));
                 PATH_InitGdiPath(pStrokes[numStrokes - 1]);
-                pStrokes[numStrokes - 1]->pFlags = HeapAlloc(GetProcessHeap(), 0, pPath->numEntriesUsed * sizeof(INT));
-                pStrokes[numStrokes - 1]->pPoints = HeapAlloc(GetProcessHeap(), 0, pPath->numEntriesUsed * sizeof(POINT));
-                pStrokes[numStrokes - 1]->numEntriesUsed = 0;
-
-                pStrokes[numStrokes - 1]->pFlags[j] = pPath->pFlags[i];
-                pStrokes[numStrokes - 1]->pPoints[j].x = pPath->pPoints[i].x;
-                pStrokes[numStrokes - 1]->pPoints[j].y = pPath->pPoints[i].y;
-                pStrokes[numStrokes - 1]->numEntriesUsed++;
-
-                break;
+                pStrokes[numStrokes - 1]->state = PATH_Open;
             case PT_LINETO:
             case (PT_LINETO | PT_CLOSEFIGURE):
-                pStrokes[numStrokes - 1]->pFlags[j] = pPath->pFlags[i];
-                pStrokes[numStrokes - 1]->pPoints[j].x = pPath->pPoints[i].x;
-                pStrokes[numStrokes - 1]->pPoints[j].y = pPath->pPoints[i].y;
-                pStrokes[numStrokes - 1]->numEntriesUsed++;
+                point.x = pPath->pPoints[i].x;
+                point.y = pPath->pPoints[i].y;
+                PATH_AddEntry(pStrokes[numStrokes - 1], &point, pPath->pFlags[i]);
                 break;
             case PT_BEZIERTO:
                 /* should never happen because of the FlattenPath call */
@@ -1860,65 +1883,211 @@ static BOOL PATH_WidenPath(DC *dc)
 
     pNewPath = HeapAlloc(GetProcessHeap(), 0, sizeof(GdiPath));
     PATH_InitGdiPath(pNewPath);
-    pNewPath->pFlags = HeapAlloc(GetProcessHeap(), 0, 4 * pPath->numEntriesUsed * sizeof(INT));
-    pNewPath->pPoints = HeapAlloc(GetProcessHeap(), 0, 4 * pPath->numEntriesUsed * sizeof(POINT));
-    pNewPath->numEntriesUsed = 0;
-    pNewPath->numEntriesAllocated = 4 * pPath->numEntriesUsed;
+    pNewPath->state = PATH_Open;
 
     for(i = 0; i < numStrokes; i++) {
         pUpPath = HeapAlloc(GetProcessHeap(), 0, sizeof(GdiPath));
         PATH_InitGdiPath(pUpPath);
-        pUpPath->pFlags = HeapAlloc(GetProcessHeap(), 0, 2 * pStrokes[i]->numEntriesUsed * sizeof(INT));
-        pUpPath->pPoints = HeapAlloc(GetProcessHeap(), 0, 2 * pStrokes[i]->numEntriesUsed * sizeof(POINT));
-        pUpPath->numEntriesUsed = 0;
+        pUpPath->state = PATH_Open; 
         pDownPath = HeapAlloc(GetProcessHeap(), 0, sizeof(GdiPath));
         PATH_InitGdiPath(pDownPath);
-        pDownPath->pFlags = HeapAlloc(GetProcessHeap(), 0, 2 * pStrokes[i]->numEntriesUsed * sizeof(INT));
-        pDownPath->pPoints = HeapAlloc(GetProcessHeap(), 0, 2 * pStrokes[i]->numEntriesUsed * sizeof(POINT));
-        pDownPath->numEntriesUsed = 0;
-        for(j = 0; j < pStrokes[i]->numEntriesUsed - 1; j++) {
-            if(pStrokes[i]->pPoints[j+1].x != pStrokes[i]->pPoints[j].x) {
-                nPente = (pStrokes[i]->pPoints[j+1].y - pStrokes[i]->pPoints[j].y) / (pStrokes[i]->pPoints[j+1].x - pStrokes[i]->pPoints[j].x);
-                fCos = cos(atan(nPente));
-                fSin = sin(atan(nPente));
-            }
-            else if(pStrokes[i]->pPoints[j+1].y > pStrokes[i]->pPoints[j].y) {
-                fCos = 0;
-                fSin = -1;
+        pDownPath->state = PATH_Open;
+
+        for(j = 0; j < pStrokes[i]->numEntriesUsed; j++) {
+            /* Beginning or end of the path if not closed */
+            if((!(pStrokes[i]->pFlags[pStrokes[i]->numEntriesUsed - 1] & PT_CLOSEFIGURE)) && (j == 0 || j == pStrokes[i]->numEntriesUsed - 1) ) {
+                /* Compute segment angle */
+                FLOAT xo, yo, xa, ya;
+                POINT pt;
+                FLOAT theta, scalarProduct;
+                FLOAT_POINT corners[2];
+                if(j == 0) {
+                    xo = pStrokes[i]->pPoints[j].x;
+                    yo = pStrokes[i]->pPoints[j].y;
+                    xa = pStrokes[i]->pPoints[1].x;
+                    ya = pStrokes[i]->pPoints[1].y;
+                }
+                else {
+                    xa = pStrokes[i]->pPoints[j - 1].x;
+                    ya = pStrokes[i]->pPoints[j - 1].y;
+                    xo = pStrokes[i]->pPoints[j].x;
+                    yo = pStrokes[i]->pPoints[j].y;
+                }
+                scalarProduct = (xa - xo) /sqrt(pow((xa - xo), 2) + pow((ya - yo), 2));
+                theta = acos(scalarProduct);
+                if( (ya - yo) < 0) {
+                    theta = -theta;
+                }
+                switch(endcap) {
+                    case PS_ENDCAP_SQUARE :
+                        pt.x = xo + round(sqrt(2) * penWidthOut * cos(M_PI_4 + theta));
+                        pt.y = yo + round(sqrt(2) * penWidthOut * sin(M_PI_4 + theta));
+                        PATH_AddEntry(pUpPath, &pt, (j == 0 ? PT_MOVETO : PT_LINETO) );
+                        pt.x = xo + round(sqrt(2) * penWidthIn * cos(- M_PI_4 + theta));
+                        pt.y = yo + round(sqrt(2) * penWidthIn * sin(- M_PI_4 + theta));
+                        PATH_AddEntry(pUpPath, &pt, PT_LINETO);
+                        break;
+                    case PS_ENDCAP_FLAT :
+                        pt.x = xo + round( penWidthOut * cos(theta + M_PI_2) );
+                        pt.y = yo + round( penWidthOut * sin(theta + M_PI_2) );
+                        PATH_AddEntry(pUpPath, &pt, (j == 0 ? PT_MOVETO : PT_LINETO));
+                        pt.x = xo - round( penWidthIn * cos(theta + M_PI_2) );
+                        pt.y = yo - round( penWidthIn * sin(theta + M_PI_2) );
+                        PATH_AddEntry(pUpPath, &pt, PT_LINETO);
+                        break;
+                    case PS_ENDCAP_ROUND :
+                    default :
+                        corners[0].x = xo - penWidthIn;
+                        corners[0].y = yo - penWidthIn;
+                        corners[1].x = xo + penWidthOut;
+                        corners[1].y = yo + penWidthOut;
+                        PATH_DoArcPart(pUpPath ,corners, theta + M_PI_2 , theta + 3 * M_PI_4, (j == 0 ? TRUE : FALSE));
+                        PATH_DoArcPart(pUpPath ,corners, theta + 3 * M_PI_4 , theta + M_PI, FALSE);
+                        PATH_DoArcPart(pUpPath ,corners, theta + M_PI, theta +  5 * M_PI_4, FALSE);
+                        PATH_DoArcPart(pUpPath ,corners, theta + 5 * M_PI_4 , theta + 3 * M_PI_2, FALSE);
+                        break;
+                }
             }
+            /* Corpse of the path */
             else {
-                fCos = 0;
-                fSin = 1;
+                /* Compute angle */
+                INT previous, next;
+                FLOAT xa, ya, xb, yb, xo, yo;
+                FLOAT alpha, theta;
+                FLOAT scalarProduct, oa, ob, miterWidth;
+                DWORD _joint = joint;
+                POINT pt;
+		GdiPath *pInsidePath, *pOutsidePath;
+                if(j > 0 && j < pStrokes[i]->numEntriesUsed - 1) {
+                    previous = j - 1;
+                    next = j + 1;
+                }
+                else if (j == 0) {
+                    previous = pStrokes[i]->numEntriesUsed - 1;
+                    next = j + 1;
+                }
+                else {
+                    previous = j - 1;
+                    next = 0;
+                }
+                xo = pStrokes[i]->pPoints[j].x;
+                yo = pStrokes[i]->pPoints[j].y;
+                xa = pStrokes[i]->pPoints[previous].x;
+                ya = pStrokes[i]->pPoints[previous].y;
+                xb = pStrokes[i]->pPoints[next].x;
+                yb = pStrokes[i]->pPoints[next].y;
+                oa = sqrt(pow((xa - xo), 2) + pow((ya - yo), 2));
+                ob = sqrt(pow((xb - xo), 2) + pow((yb - yo), 2));
+                scalarProduct = ((xa - xo) * (xb - xo) + (ya - yo) * (yb - yo))/ (oa * ob);
+                alpha = acos(scalarProduct);
+                if(( (xa - xo) * (yb - yo) - (ya - yo) * (xb - xo) ) < 0) {
+                    alpha = -alpha;
+                }
+                scalarProduct = (xo - xa) / oa;
+                theta = acos(scalarProduct);
+                if( (yo - ya) < 0) {
+                    theta = -theta;
+                }
+                if(_joint == PS_JOIN_MITER && dc->miterLimit < fabs(1 / sin(alpha/2))) {
+                    _joint = PS_JOIN_BEVEL;
+                }
+                if(alpha > 0) {
+                    pInsidePath = pUpPath;
+                    pOutsidePath = pDownPath;
+                }
+                else if(alpha < 0) {
+                    pInsidePath = pDownPath;
+                    pOutsidePath = pUpPath;
+                }
+                else {
+                    continue;
+                }
+                /* Inside angle points */
+                if(alpha > 0) {
+                    pt.x = xo - round( penWidthIn * cos(theta + M_PI_2) );
+                    pt.y = yo - round( penWidthIn * sin(theta + M_PI_2) );
+                }
+                else {
+                    pt.x = xo + round( penWidthIn * cos(theta + M_PI_2) );
+                    pt.y = yo + round( penWidthIn * sin(theta + M_PI_2) );
+                }
+                PATH_AddEntry(pInsidePath, &pt, PT_LINETO);
+                if(alpha > 0) {
+                    pt.x = xo + round( penWidthIn * cos(M_PI_2 + alpha + theta) );
+                    pt.y = yo + round( penWidthIn * sin(M_PI_2 + alpha + theta) );
+                }
+                else {
+                    pt.x = xo - round( penWidthIn * cos(M_PI_2 + alpha + theta) );
+                    pt.y = yo - round( penWidthIn * sin(M_PI_2 + alpha + theta) );
+                }
+                PATH_AddEntry(pInsidePath, &pt, PT_LINETO);
+                /* Outside angle point */
+                switch(_joint) {
+                     case PS_JOIN_MITER :
+                        miterWidth = fabs(penWidthOut / cos(M_PI_2 - fabs(alpha) / 2));
+                        pt.x = xo + round( miterWidth * cos(theta + alpha / 2) );
+                        pt.y = yo + round( miterWidth * sin(theta + alpha / 2) );
+                        PATH_AddEntry(pOutsidePath, &pt, PT_LINETO);
+                        break;
+                    case PS_JOIN_BEVEL :
+                        if(alpha > 0) {
+                            pt.x = xo + round( penWidthOut * cos(theta + M_PI_2) );
+                            pt.y = yo + round( penWidthOut * sin(theta + M_PI_2) );
+                        }
+                        else {
+                            pt.x = xo - round( penWidthOut * cos(theta + M_PI_2) );
+                            pt.y = yo - round( penWidthOut * sin(theta + M_PI_2) );
+                        }
+                        PATH_AddEntry(pOutsidePath, &pt, PT_LINETO);
+                        if(alpha > 0) {
+                            pt.x = xo - round( penWidthOut * cos(M_PI_2 + alpha + theta) );
+                            pt.y = yo - round( penWidthOut * sin(M_PI_2 + alpha + theta) );
+                        }
+                        else {
+                            pt.x = xo + round( penWidthOut * cos(M_PI_2 + alpha + theta) );
+                            pt.y = yo + round( penWidthOut * sin(M_PI_2 + alpha + theta) );
+                        }
+                        PATH_AddEntry(pOutsidePath, &pt, PT_LINETO);
+                        break;
+                    case PS_JOIN_ROUND :
+                    default :
+                        if(alpha > 0) {
+                            pt.x = xo + round( penWidthOut * cos(theta + M_PI_2) );
+                            pt.y = yo + round( penWidthOut * sin(theta + M_PI_2) );
+                        }
+                        else {
+                            pt.x = xo - round( penWidthOut * cos(theta + M_PI_2) );
+                            pt.y = yo - round( penWidthOut * sin(theta + M_PI_2) );
+                        }
+                        PATH_AddEntry(pOutsidePath, &pt, PT_BEZIERTO);
+                        pt.x = xo + round( penWidthOut * cos(theta + alpha / 2) );
+                        pt.y = yo + round( penWidthOut * sin(theta + alpha / 2) );
+                        PATH_AddEntry(pOutsidePath, &pt, PT_BEZIERTO);
+                        if(alpha > 0) {
+                            pt.x = xo - round( penWidthOut * cos(M_PI_2 + alpha + theta) );
+                            pt.y = yo - round( penWidthOut * sin(M_PI_2 + alpha + theta) );
+                        }
+                        else {
+                            pt.x = xo + round( penWidthOut * cos(M_PI_2 + alpha + theta) );
+                            pt.y = yo + round( penWidthOut * sin(M_PI_2 + alpha + theta) );
+                        }
+                        PATH_AddEntry(pOutsidePath, &pt, PT_BEZIERTO);
+                        break;
+                }
             }
-
-            /* FIXME : Improve corners */
-            pUpPath->pPoints[2 * j].x = pStrokes[i]->pPoints[j].x + penWidthOut * fSin;
-            pUpPath->pPoints[2 * j].y = pStrokes[i]->pPoints[j].y + penWidthOut * fCos;
-            pUpPath->pFlags[2 * j] = pStrokes[i]->pFlags[j];
-            pUpPath->pPoints[2 * j + 1] .x = pStrokes[i]->pPoints[j+1].x + penWidthOut * fSin;
-            pUpPath->pPoints[2 * j + 1] .y = pStrokes[i]->pPoints[j+1].y + penWidthOut * fCos;
-            pUpPath->pFlags[2 * j + 1] = PT_LINETO;
-            pUpPath->numEntriesUsed = pUpPath->numEntriesUsed + 2;
-
-            pDownPath->pPoints[2 * j].x = pStrokes[i]->pPoints[j].x - penWidthIn * fSin;
-            pDownPath->pPoints[2 * j].y = pStrokes[i]->pPoints[j].y - penWidthIn * fCos;
-            pDownPath->pFlags[2 * j] = PT_LINETO;
-            pDownPath->pPoints[2 * j + 1] .x = pStrokes[i]->pPoints[j+1].x - penWidthIn * fSin;
-            pDownPath->pPoints[2 * j + 1] .y = pStrokes[i]->pPoints[j+1].y - penWidthIn * fCos;
-            pDownPath->pFlags[2 * j + 1] = PT_LINETO;
-            pDownPath->numEntriesUsed = pUpPath->numEntriesUsed;
         }
-
         for(j = 0; j < pUpPath->numEntriesUsed; j++) {
-            pNewPath->pPoints[pNewPath->numEntriesUsed + j].x = pUpPath->pPoints[j].x;
-            pNewPath->pPoints[pNewPath->numEntriesUsed + j].y = pUpPath->pPoints[j].y;
-            pNewPath->pFlags[pNewPath->numEntriesUsed + j] = pUpPath->pFlags[j];
-            pNewPath->pPoints[pNewPath->numEntriesUsed + pUpPath->numEntriesUsed + j].x = pDownPath->pPoints[pUpPath->numEntriesUsed - j - 1].x;
-            pNewPath->pPoints[pNewPath->numEntriesUsed + pUpPath->numEntriesUsed + j].y = pDownPath->pPoints[pUpPath->numEntriesUsed - j - 1].y;
-            pNewPath->pFlags[pNewPath->numEntriesUsed + pUpPath->numEntriesUsed + j] = pDownPath->pFlags[pUpPath->numEntriesUsed - j - 1];
+            POINT pt;
+            pt.x = pUpPath->pPoints[j].x;
+            pt.y = pUpPath->pPoints[j].y;
+            PATH_AddEntry(pNewPath, &pt, (j == 0 ? PT_MOVETO : PT_LINETO));
+        }
+        for(j = 0; j < pDownPath->numEntriesUsed; j++) {
+            POINT pt;
+            pt.x = pDownPath->pPoints[pDownPath->numEntriesUsed - j - 1].x;
+            pt.y = pDownPath->pPoints[pDownPath->numEntriesUsed - j - 1].y;
+            PATH_AddEntry(pNewPath, &pt, ( (j == 0 && (pStrokes[i]->pFlags[pStrokes[i]->numEntriesUsed - 1] & PT_CLOSEFIGURE)) ? PT_MOVETO : PT_LINETO));
         }
-        pNewPath->numEntriesUsed += 2 * pUpPath->numEntriesUsed;
-        pNewPath->pFlags[pNewPath->numEntriesUsed - 1] = PT_CLOSEFIGURE | PT_LINETO;
 
         PATH_DestroyGdiPath(pStrokes[i]);
         HeapFree(GetProcessHeap(), 0, pStrokes[i]);
@@ -2007,6 +2176,5 @@ BOOL WINAPI WidenPath(HDC hdc)
    else
       ret = PATH_WidenPath(dc);
    GDI_ReleaseObj( hdc );
-   FIXME("partially implemented\n");
    return ret;
 }


More information about the wine-patches mailing list