diff --git a/dlls/gdiplus/customlinecap.c b/dlls/gdiplus/customlinecap.c index ca81bee2174..7fbe8b6b898 100644 --- a/dlls/gdiplus/customlinecap.c +++ b/dlls/gdiplus/customlinecap.c @@ -17,6 +17,7 @@ */ #include +#include #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 9fff578a280..25b269ba351 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 bac80adbdb0..6a22c2668ad 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 #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);