[1/2] gdiplus: change GdipDrawArc to properly handle open gdi path

Evan Stade estade at gmail.com
Wed Jun 27 19:15:09 CDT 2007


Hi,

Currently an application that mixes calls to gdi32 and gdiplus can run
into problems when trying to use gdiplus calls on a dc with an open
path.  To circumvent this problem I've introduced two functions that
save and restore an open gdi path (if there is one) before drawing on
that DC.  This will need to be done for every GdipDraw function but
this patch only does it for GdipDrawArc.

At this point it also seems convenient to pull out drawing logic from
the GdipDraw functions into separate helper functions.  This is
because it's not always the case that you will want to change all the
DC's settings when you draw on it (for example, selecting a different
pen, line join, saving gdi path, etc).

Changelog:
* added save_gdi_path, restore_gdi_path, PathState struct
* moved drawing logic from GdipDrawArc to draw_arc
* fixed GdipDrawArc

 dlls/gdiplus/gdiplus_private.h |    4 ++
 dlls/gdiplus/graphics.c        |  100 ++++++++++++++++++++++++++++++++++++----
 2 files changed, 95 insertions(+), 9 deletions(-)

-- 
Evan Stade
-------------- next part --------------
diff --git a/dlls/gdiplus/gdiplus_private.h b/dlls/gdiplus/gdiplus_private.h
index e221044..4511423 100644
--- a/dlls/gdiplus/gdiplus_private.h
+++ b/dlls/gdiplus/gdiplus_private.h
@@ -56,4 +56,8 @@ struct GpPath{
     BOOL newfigure; /* whether the next drawing action starts a new figure */
 };
 
+/* internal drawing functions */
+BOOL draw_arc(HDC hdc, REAL x, REAL y, REAL width, REAL height, REAL startAngle,
+    REAL sweepAngle);
+
 #endif
diff --git a/dlls/gdiplus/graphics.c b/dlls/gdiplus/graphics.c
index 5ac16f7..9d53f38 100644
--- a/dlls/gdiplus/graphics.c
+++ b/dlls/gdiplus/graphics.c
@@ -30,6 +30,13 @@ #include "wine/debug.h"
 /* looks-right constant */
 #define TENSION_CONST (0.3)
 
+typedef struct PathState{
+    INT count;
+    LPPOINT points;
+    LPBYTE types;
+    BOOL open;
+} PathState;
+
 static inline INT roundr(REAL x)
 {
     return (INT) floor(x+0.5);
@@ -108,6 +115,72 @@ static void calc_curve_bezier_endp(REAL 
     *y = roundr(tension * (yadj - yend) + yend);
 }
 
+/* Sets open to true if the path was open before it was saved.
+ * Saves the path (whether or not it was open) in points, types, count. */
+static void save_gdi_path(HDC hdc, PathState * state)
+{
+    state->open = EndPath(hdc);
+
+    state->count = GetPath(hdc, NULL, NULL, 0);
+    if(state->count){
+        state->points = GdipAlloc(state->count * sizeof(POINT));
+        state->types = GdipAlloc(state->count);
+        GetPath(hdc, state->points, state->types, state->count);
+    }
+}
+
+/* Assumes there is no open path in the hdc. Restores the path given by
+ * points, types, count. */
+static void restore_gdi_path(HDC hdc, PathState * state)
+{
+    INT num_bezier, i, count = state->count;
+    LPPOINT points = state->points;
+    LPBYTE types = state->types;
+    BOOL open = state->open;
+
+    BeginPath(hdc);
+
+    for(i = 0; i < count; i++){
+        switch(types[i] & ~PT_CLOSEFIGURE){
+            case PT_LINETO:
+
+                LineTo(hdc, points[i].x,  points[i].y);
+
+                break;
+
+            case PT_MOVETO:
+
+                MoveToEx(hdc, points[i].x,  points[i].y, NULL);
+
+                break;
+
+            case PT_BEZIERTO:
+
+                /* count number of consecutive bezier points */
+                for(num_bezier = 1; i + num_bezier < count; num_bezier++){
+                    if(((types[i + num_bezier] & ~PT_CLOSEFIGURE) != PT_BEZIERTO)
+                       || (types[i + num_bezier - 1] & PT_CLOSEFIGURE))
+                        break;
+                }
+
+                PolyBezierTo(hdc, &(points[i]), num_bezier);
+
+                i += num_bezier - 1;
+
+                break;
+        }
+        if(types[i] & PT_CLOSEFIGURE){
+            CloseFigure(hdc);
+        }
+    }
+
+    GdipFree(points);
+    GdipFree(types);
+
+    if(!open)
+        EndPath(hdc);
+}
+
 GpStatus WINGDIPAPI GdipCreateFromHDC(HDC hdc, GpGraphics **graphics)
 {
     if(hdc == NULL)
@@ -148,27 +221,36 @@ GpStatus WINGDIPAPI GdipDeleteGraphics(G
     return Ok;
 }
 
+BOOL draw_arc(HDC hdc, REAL x, REAL y, REAL width, REAL height, REAL startAngle,
+    REAL sweepAngle)
+{
+    REAL x_0, y_0, x_1, y_1, x_2, y_2;
+    x_0 = x + (width/2.0);
+    y_0 = y + (height/2.0);
+
+    deg2xy(startAngle+sweepAngle, x_0, y_0, &x_1, &y_1);
+    deg2xy(startAngle, x_0, y_0, &x_2, &y_2);
+
+    return Arc(hdc, roundr(x), roundr(y), roundr(x+width), roundr(y+height),
+        roundr(x_1), roundr(y_1), roundr(x_2), roundr(y_2));
+}
+
 GpStatus WINGDIPAPI GdipDrawArc(GpGraphics *graphics, GpPen *pen, REAL x,
     REAL y, REAL width, REAL height, REAL startAngle, REAL sweepAngle)
 {
     HGDIOBJ old_pen;
-    REAL x_0, y_0, x_1, y_1, x_2, y_2;
+    PathState state = {0, NULL, NULL, (BYTE) 0};
 
     if(!graphics || !pen)
         return InvalidParameter;
 
+    save_gdi_path(graphics->hdc, &state);
     old_pen = SelectObject(graphics->hdc, pen->gdipen);
 
-    x_0 = x + (width/2.0);
-    y_0 = y + (height/2.0);
-
-    deg2xy(startAngle+sweepAngle, x_0, y_0, &x_1, &y_1);
-    deg2xy(startAngle, x_0, y_0, &x_2, &y_2);
-
-    Arc(graphics->hdc, roundr(x), roundr(y), roundr(x+width), roundr(y+height),
-        roundr(x_1), roundr(y_1), roundr(x_2), roundr(y_2));
+    draw_arc(graphics->hdc, x, y, width, height, startAngle, sweepAngle);
 
     SelectObject(graphics->hdc, old_pen);
+    restore_gdi_path(graphics->hdc, &state);
 
     return Ok;
 }
-- 
1.4.1


More information about the wine-patches mailing list