[PATCH 2/4] gdiplus: Partially implement arrow caps

Nikolay Sivov nsivov at codeweavers.com
Sun May 13 23:59:35 CDT 2018


Signed-off-by: Nikolay Sivov <nsivov at codeweavers.com>
---
 dlls/gdiplus/customlinecap.c       | 196 ++++++++++++++++++++---------
 dlls/gdiplus/gdiplus_private.h     |   3 +
 dlls/gdiplus/tests/customlinecap.c |  79 ++++++++++--
 3 files changed, 208 insertions(+), 70 deletions(-)

diff --git a/dlls/gdiplus/customlinecap.c b/dlls/gdiplus/customlinecap.c
index ca81bee217..7fbe8b6b89 100644
--- a/dlls/gdiplus/customlinecap.c
+++ b/dlls/gdiplus/customlinecap.c
@@ -17,6 +17,7 @@
  */
 
 #include <stdarg.h>
+#include <assert.h>
 
 #include "windef.h"
 #include "winbase.h"
@@ -38,11 +39,20 @@ GpStatus WINGDIPAPI GdipCloneCustomLineCap(GpCustomLineCap* from,
     if(!from || !to)
         return InvalidParameter;
 
-    *to = heap_alloc_zero(sizeof(GpCustomLineCap));
-    if(!*to)   return OutOfMemory;
+    if (from->type == CustomLineCapTypeDefault)
+        *to = heap_alloc_zero(sizeof(GpCustomLineCap));
+    else
+        *to = heap_alloc_zero(sizeof(GpAdjustableArrowCap));
 
-    memcpy(*to, from, sizeof(GpCustomLineCap));
+    if (!*to)
+        return OutOfMemory;
+
+    if (from->type == CustomLineCapTypeDefault)
+        **to = *from;
+    else
+        *(GpAdjustableArrowCap *)*to = *(GpAdjustableArrowCap *)from;
 
+    /* Duplicate path data */
     (*to)->pathdata.Points = heap_alloc_zero(from->pathdata.Count * sizeof(PointF));
     (*to)->pathdata.Types = heap_alloc_zero(from->pathdata.Count);
 
@@ -62,12 +72,44 @@ GpStatus WINGDIPAPI GdipCloneCustomLineCap(GpCustomLineCap* from,
     return Ok;
 }
 
+static GpStatus init_custom_linecap(GpCustomLineCap *cap, GpPathData *pathdata, BOOL fill, GpLineCap basecap,
+    REAL base_inset)
+{
+    cap->fill = fill;
+
+    cap->pathdata.Points = heap_alloc_zero(pathdata->Count * sizeof(PointF));
+    cap->pathdata.Types = heap_alloc_zero(pathdata->Count);
+
+    if ((!cap->pathdata.Types || !cap->pathdata.Points) && pathdata->Count)
+    {
+        heap_free(cap->pathdata.Points);
+        heap_free(cap->pathdata.Types);
+        cap->pathdata.Points = NULL;
+        cap->pathdata.Types = NULL;
+        return OutOfMemory;
+    }
+
+    if (pathdata->Points)
+        memcpy(cap->pathdata.Points, pathdata->Points, pathdata->Count * sizeof(PointF));
+    if (pathdata->Types)
+        memcpy(cap->pathdata.Types, pathdata->Types, pathdata->Count);
+    cap->pathdata.Count = pathdata->Count;
+
+    cap->inset = base_inset;
+    cap->cap = basecap;
+    cap->join = LineJoinMiter;
+    cap->scale = 1.0;
+
+    return Ok;
+}
+
 /* FIXME: Sometimes when fillPath is non-null and stroke path is null, the native
  * version of this function returns NotImplemented. I cannot figure out why. */
 GpStatus WINGDIPAPI GdipCreateCustomLineCap(GpPath* fillPath, GpPath* strokePath,
     GpLineCap baseCap, REAL baseInset, GpCustomLineCap **customCap)
 {
     GpPathData *pathdata;
+    GpStatus stat;
 
     TRACE("%p %p %d %f %p\n", fillPath, strokePath, baseCap, baseInset, customCap);
 
@@ -77,37 +119,18 @@ GpStatus WINGDIPAPI GdipCreateCustomLineCap(GpPath* fillPath, GpPath* strokePath
     *customCap = heap_alloc_zero(sizeof(GpCustomLineCap));
     if(!*customCap)    return OutOfMemory;
 
-    (*customCap)->type = CustomLineCapTypeDefault;
-    if(strokePath){
-        (*customCap)->fill = FALSE;
+    if (strokePath)
         pathdata = &strokePath->pathdata;
-    }
-    else{
-        (*customCap)->fill = TRUE;
+    else
         pathdata = &fillPath->pathdata;
-    }
 
-    (*customCap)->pathdata.Points = heap_alloc_zero(pathdata->Count * sizeof(PointF));
-    (*customCap)->pathdata.Types = heap_alloc_zero(pathdata->Count);
-
-    if((!(*customCap)->pathdata.Types || !(*customCap)->pathdata.Points) &&
-        pathdata->Count){
-        heap_free((*customCap)->pathdata.Points);
-        heap_free((*customCap)->pathdata.Types);
+    stat = init_custom_linecap(*customCap, pathdata, fillPath != NULL, baseCap, baseInset);
+    if (stat != Ok)
+    {
         heap_free(*customCap);
-        return OutOfMemory;
+        return stat;
     }
 
-    memcpy((*customCap)->pathdata.Points, pathdata->Points, pathdata->Count
-           * sizeof(PointF));
-    memcpy((*customCap)->pathdata.Types, pathdata->Types, pathdata->Count);
-    (*customCap)->pathdata.Count = pathdata->Count;
-
-    (*customCap)->inset = baseInset;
-    (*customCap)->cap = baseCap;
-    (*customCap)->join = LineJoinMiter;
-    (*customCap)->scale = 1.0;
-
     TRACE("<-- %p\n", *customCap);
 
     return Ok;
@@ -257,17 +280,69 @@ GpStatus WINGDIPAPI GdipGetCustomLineCapType(GpCustomLineCap *customCap, CustomL
     return Ok;
 }
 
+static void arrowcap_update_path(GpAdjustableArrowCap *cap)
+{
+    GpPointF *points;
+
+    assert(cap->cap.pathdata.Count == 4);
+
+    points = cap->cap.pathdata.Points;
+    points[0].X = 0.0;
+    points[0].Y = 0.0;
+    points[1].X = -cap->width / 2.0;
+    points[1].Y = -cap->height;
+    points[2].X = 0.0;
+    points[2].Y = -cap->height - cap->middle_inset;
+    points[3].X = cap->width / 2.0;
+    points[3].Y = -cap->height;
+
+    if (cap->width == 0.0)
+        cap->cap.inset = 0.0;
+    else
+        cap->cap.inset = cap->height / cap->width;
+}
+
 GpStatus WINGDIPAPI GdipCreateAdjustableArrowCap(REAL height, REAL width, BOOL fill,
     GpAdjustableArrowCap **cap)
 {
-    static int calls;
+    GpPathData pathdata;
+    BYTE types[4];
+    GpStatus stat;
 
     TRACE("(%0.2f,%0.2f,%i,%p)\n", height, width, fill, cap);
 
-    if(!(calls++))
-        FIXME("not implemented\n");
+    if (!cap)
+        return InvalidParameter;
 
-    return NotImplemented;
+    if (!fill)
+        FIXME("Arrows without fills are not supported.\n");
+
+    *cap = heap_alloc_zero(sizeof(**cap));
+    if (!*cap)
+        return OutOfMemory;
+
+    types[0] = PathPointTypeStart;
+    types[1] = PathPointTypeLine;
+    types[2] = PathPointTypeLine;
+    types[3] = PathPointTypeLine | PathPointTypeCloseSubpath;
+
+    pathdata.Count = 4;
+    pathdata.Points = NULL;
+    pathdata.Types = types;
+    stat = init_custom_linecap(&(*cap)->cap, &pathdata, TRUE, LineCapTriangle, width != 0.0 ? height / width : 0.0);
+    if (stat != Ok)
+    {
+        heap_free(*cap);
+        return stat;
+    }
+
+    (*cap)->cap.type = CustomLineCapTypeAdjustableArrow;
+    (*cap)->height = height;
+    (*cap)->width = width;
+    (*cap)->middle_inset = 0.0;
+    arrowcap_update_path(*cap);
+
+    return Ok;
 }
 
 GpStatus WINGDIPAPI GdipGetAdjustableArrowCapFillState(GpAdjustableArrowCap* cap, BOOL* fill)
@@ -284,38 +359,35 @@ GpStatus WINGDIPAPI GdipGetAdjustableArrowCapFillState(GpAdjustableArrowCap* cap
 
 GpStatus WINGDIPAPI GdipGetAdjustableArrowCapHeight(GpAdjustableArrowCap* cap, REAL* height)
 {
-    static int calls;
-
     TRACE("(%p,%p)\n", cap, height);
 
-    if(!(calls++))
-        FIXME("not implemented\n");
+    if (!cap || !height)
+        return InvalidParameter;
 
-    return NotImplemented;
+    *height = cap->height;
+    return Ok;
 }
 
 GpStatus WINGDIPAPI GdipGetAdjustableArrowCapMiddleInset(GpAdjustableArrowCap* cap, REAL* middle)
 {
-    static int calls;
-
     TRACE("(%p,%p)\n", cap, middle);
 
-    if(!(calls++))
-        FIXME("not implemented\n");
+    if (!cap || !middle)
+        return InvalidParameter;
 
-    return NotImplemented;
+    *middle = cap->middle_inset;
+    return Ok;
 }
 
 GpStatus WINGDIPAPI GdipGetAdjustableArrowCapWidth(GpAdjustableArrowCap* cap, REAL* width)
 {
-    static int calls;
-
     TRACE("(%p,%p)\n", cap, width);
 
-    if(!(calls++))
-        FIXME("not implemented\n");
+    if (!cap || !width)
+        return InvalidParameter;
 
-    return NotImplemented;
+    *width = cap->width;
+    return Ok;
 }
 
 GpStatus WINGDIPAPI GdipSetAdjustableArrowCapFillState(GpAdjustableArrowCap* cap, BOOL fill)
@@ -332,36 +404,36 @@ GpStatus WINGDIPAPI GdipSetAdjustableArrowCapFillState(GpAdjustableArrowCap* cap
 
 GpStatus WINGDIPAPI GdipSetAdjustableArrowCapHeight(GpAdjustableArrowCap* cap, REAL height)
 {
-    static int calls;
-
     TRACE("(%p,%0.2f)\n", cap, height);
 
-    if(!(calls++))
-        FIXME("not implemented\n");
+    if (!cap)
+        return InvalidParameter;
 
-    return NotImplemented;
+    cap->height = height;
+    arrowcap_update_path(cap);
+    return Ok;
 }
 
 GpStatus WINGDIPAPI GdipSetAdjustableArrowCapMiddleInset(GpAdjustableArrowCap* cap, REAL middle)
 {
-    static int calls;
-
     TRACE("(%p,%0.2f)\n", cap, middle);
 
-    if(!(calls++))
-        FIXME("not implemented\n");
+    if (!cap)
+        return InvalidParameter;
 
-    return NotImplemented;
+    cap->middle_inset = middle;
+    arrowcap_update_path(cap);
+    return Ok;
 }
 
 GpStatus WINGDIPAPI GdipSetAdjustableArrowCapWidth(GpAdjustableArrowCap* cap, REAL width)
 {
-    static int calls;
-
     TRACE("(%p,%0.2f)\n", cap, width);
 
-    if(!(calls++))
-        FIXME("not implemented\n");
+    if (!cap)
+        return InvalidParameter;
 
-    return NotImplemented;
+    cap->width = width;
+    arrowcap_update_path(cap);
+    return Ok;
 }
diff --git a/dlls/gdiplus/gdiplus_private.h b/dlls/gdiplus/gdiplus_private.h
index 9fff578a28..25b269ba35 100644
--- a/dlls/gdiplus/gdiplus_private.h
+++ b/dlls/gdiplus/gdiplus_private.h
@@ -343,6 +343,9 @@ struct GpCustomLineCap{
 
 struct GpAdjustableArrowCap{
     GpCustomLineCap cap;
+    REAL middle_inset;
+    REAL height;
+    REAL width;
 };
 
 struct GpImage{
diff --git a/dlls/gdiplus/tests/customlinecap.c b/dlls/gdiplus/tests/customlinecap.c
index bac80adbdb..6a22c2668a 100644
--- a/dlls/gdiplus/tests/customlinecap.c
+++ b/dlls/gdiplus/tests/customlinecap.c
@@ -17,6 +17,7 @@
  * License along with this library; if not, write to the Free Software
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
  */
+#include <limits.h>
 
 #include "objbase.h"
 #include "gdiplus.h"
@@ -25,6 +26,22 @@
 #define expect(expected, got) ok(got == expected, "Expected %.8x, got %.8x\n", expected, got)
 #define expectf(expected, got) ok(got == expected, "Expected %.2f, got %.2f\n", expected, got)
 
+static BOOL compare_float(float f, float g, unsigned int ulps)
+{
+    int x = *(int *)&f;
+    int y = *(int *)&g;
+
+    if (x < 0)
+        x = INT_MIN - x;
+    if (y < 0)
+        y = INT_MIN - y;
+
+    if (abs(x - y) > ulps)
+        return FALSE;
+
+    return TRUE;
+}
+
 static void test_constructor_destructor(void)
 {
     GpCustomLineCap *custom;
@@ -219,21 +236,43 @@ static void test_scale(void)
 
 static void test_create_adjustable_cap(void)
 {
+    REAL inset, scale, height, width;
     GpAdjustableArrowCap *cap;
-    REAL inset, scale;
     GpLineJoin join;
     GpStatus stat;
     GpLineCap base;
+    BOOL ret;
 
     stat = GdipCreateAdjustableArrowCap(10.0, 10.0, TRUE, NULL);
-todo_wine
     ok(stat == InvalidParameter, "Unexpected return code, %d\n", stat);
 
     stat = GdipCreateAdjustableArrowCap(17.0, 15.0, TRUE, &cap);
-todo_wine
     ok(stat == Ok, "Failed to create adjustable cap, %d\n", stat);
-    if (stat != Ok)
-        return;
+
+    stat = GdipGetAdjustableArrowCapFillState(cap, NULL);
+todo_wine
+    ok(stat == InvalidParameter, "Unexpected return code, %d\n", stat);
+
+    ret = FALSE;
+    stat = GdipGetAdjustableArrowCapFillState(cap, &ret);
+todo_wine
+{
+    ok(stat == Ok, "Unexpected return code, %d\n", stat);
+    ok(ret, "Unexpected fill state %d\n", ret);
+}
+    stat = GdipGetAdjustableArrowCapHeight(cap, NULL);
+    ok(stat == InvalidParameter, "Unexpected return code, %d\n", stat);
+
+    stat = GdipGetAdjustableArrowCapHeight(cap, &height);
+    ok(stat == Ok, "Unexpected return code, %d\n", stat);
+    ok(height == 17.0, "Unexpected cap height %f\n", height);
+
+    stat = GdipGetAdjustableArrowCapWidth(cap, NULL);
+    ok(stat == InvalidParameter, "Unexpected return code, %d\n", stat);
+
+    stat = GdipGetAdjustableArrowCapWidth(cap, &width);
+    ok(stat == Ok, "Unexpected return code, %d\n", stat);
+    ok(width == 15.0, "Unexpected cap width %f\n", width);
 
     stat = GdipGetAdjustableArrowCapMiddleInset(cap, NULL);
     ok(stat == InvalidParameter, "Unexpected return code, %d\n", stat);
@@ -247,14 +286,41 @@ todo_wine
     ok(base == LineCapTriangle, "Unexpected base cap %d\n", base);
 
     stat = GdipSetCustomLineCapBaseCap((GpCustomLineCap*)cap, LineCapSquare);
+todo_wine
     ok(stat == Ok, "Unexpected return code, %d\n", stat);
 
     stat = GdipGetCustomLineCapBaseCap((GpCustomLineCap*)cap, &base);
     ok(stat == Ok, "Unexpected return code, %d\n", stat);
+todo_wine
     ok(base == LineCapSquare, "Unexpected base cap %d\n", base);
 
+    /* Base inset */
+    stat = GdipGetAdjustableArrowCapWidth(cap, &width);
+    ok(stat == Ok, "Unexpected return code, %d\n", stat);
+
+    stat = GdipGetAdjustableArrowCapHeight(cap, &height);
+    ok(stat == Ok, "Unexpected return code, %d\n", stat);
+
+    inset = 0.0;
+    stat = GdipGetCustomLineCapBaseInset((GpCustomLineCap*)cap, &inset);
+    ok(stat == Ok, "Unexpected return code, %d\n", stat);
+    ok(compare_float(inset, height / width, 1), "Unexpected inset %f\n", inset);
+
+    stat = GdipSetAdjustableArrowCapMiddleInset(cap, 1.0);
+    ok(stat == Ok, "Unexpected return code, %d\n", stat);
+
+    inset = 0.0;
+    stat = GdipGetCustomLineCapBaseInset((GpCustomLineCap*)cap, &inset);
+    ok(stat == Ok, "Unexpected return code, %d\n", stat);
+    ok(compare_float(inset, height / width, 1), "Unexpected inset %f\n", inset);
+
+    stat = GdipSetAdjustableArrowCapHeight(cap, 2.0 * height);
+    ok(stat == Ok, "Unexpected return code, %d\n", stat);
+
+    inset = 0.0;
     stat = GdipGetCustomLineCapBaseInset((GpCustomLineCap*)cap, &inset);
     ok(stat == Ok, "Unexpected return code, %d\n", stat);
+    ok(compare_float(inset, 2.0 * height / width, 1), "Unexpected inset %f\n", inset);
 
     stat = GdipGetCustomLineCapWidthScale((GpCustomLineCap*)cap, &scale);
     ok(stat == Ok, "Unexpected return code, %d\n", stat);
@@ -299,10 +365,7 @@ static void test_captype(void)
 
     /* arrow cap */
     stat = GdipCreateAdjustableArrowCap(17.0, 15.0, TRUE, &arrowcap);
-todo_wine
     ok(stat == Ok, "Failed to create adjustable cap, %d\n", stat);
-    if (stat != Ok)
-        return;
 
     stat = GdipGetCustomLineCapType((GpCustomLineCap*)arrowcap, &type);
     ok(stat == Ok, "Failed to get cap type, %d\n", stat);
-- 
2.17.0




More information about the wine-devel mailing list