|  | /* | 
|  | * Copyright (C) 2007 Google (Evan Stade) | 
|  | * | 
|  | * This library is free software; you can redistribute it and/or | 
|  | * modify it under the terms of the GNU Lesser General Public | 
|  | * License as published by the Free Software Foundation; either | 
|  | * version 2.1 of the License, or (at your option) any later version. | 
|  | * | 
|  | * This library is distributed in the hope that it will be useful, | 
|  | * but WITHOUT ANY WARRANTY; without even the implied warranty of | 
|  | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | 
|  | * Lesser General Public License for more details. | 
|  | * | 
|  | * You should have received a copy of the GNU Lesser General Public | 
|  | * 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 <stdarg.h> | 
|  |  | 
|  | #include "windef.h" | 
|  | #include "winbase.h" | 
|  | #include "wingdi.h" | 
|  |  | 
|  | #include "objbase.h" | 
|  |  | 
|  | #include "gdiplus.h" | 
|  | #include "gdiplus_private.h" | 
|  | #include "wine/debug.h" | 
|  |  | 
|  | WINE_DEFAULT_DEBUG_CHANNEL(gdiplus); | 
|  |  | 
|  | static DWORD gdip_to_gdi_dash(GpDashStyle dash) | 
|  | { | 
|  | switch(dash){ | 
|  | case DashStyleSolid: | 
|  | return PS_SOLID; | 
|  | case DashStyleDash: | 
|  | return PS_DASH; | 
|  | case DashStyleDot: | 
|  | return PS_DOT; | 
|  | case DashStyleDashDot: | 
|  | return PS_DASHDOT; | 
|  | case DashStyleDashDotDot: | 
|  | return PS_DASHDOTDOT; | 
|  | case DashStyleCustom: | 
|  | return PS_USERSTYLE; | 
|  | default: | 
|  | ERR("Not a member of GpDashStyle enumeration\n"); | 
|  | return 0; | 
|  | } | 
|  | } | 
|  |  | 
|  | static DWORD gdip_to_gdi_join(GpLineJoin join) | 
|  | { | 
|  | switch(join){ | 
|  | case LineJoinRound: | 
|  | return PS_JOIN_ROUND; | 
|  | case LineJoinBevel: | 
|  | return PS_JOIN_BEVEL; | 
|  | case LineJoinMiter: | 
|  | case LineJoinMiterClipped: | 
|  | return PS_JOIN_MITER; | 
|  | default: | 
|  | ERR("Not a member of GpLineJoin enumeration\n"); | 
|  | return 0; | 
|  | } | 
|  | } | 
|  |  | 
|  | static GpPenType bt_to_pt(GpBrushType bt) | 
|  | { | 
|  | switch(bt){ | 
|  | case BrushTypeSolidColor: | 
|  | return PenTypeSolidColor; | 
|  | case BrushTypeHatchFill: | 
|  | return PenTypeHatchFill; | 
|  | case BrushTypeTextureFill: | 
|  | return PenTypeTextureFill; | 
|  | case BrushTypePathGradient: | 
|  | return PenTypePathGradient; | 
|  | case BrushTypeLinearGradient: | 
|  | return PenTypeLinearGradient; | 
|  | default: | 
|  | return PenTypeUnknown; | 
|  | } | 
|  | } | 
|  |  | 
|  | GpStatus WINGDIPAPI GdipClonePen(GpPen *pen, GpPen **clonepen) | 
|  | { | 
|  | TRACE("(%p, %p)\n", pen, clonepen); | 
|  |  | 
|  | if(!pen || !clonepen) | 
|  | return InvalidParameter; | 
|  |  | 
|  | *clonepen = GdipAlloc(sizeof(GpPen)); | 
|  | if(!*clonepen)  return OutOfMemory; | 
|  |  | 
|  | **clonepen = *pen; | 
|  |  | 
|  | GdipCloneCustomLineCap(pen->customstart, &(*clonepen)->customstart); | 
|  | GdipCloneCustomLineCap(pen->customend, &(*clonepen)->customend); | 
|  | GdipCloneBrush(pen->brush, &(*clonepen)->brush); | 
|  |  | 
|  | return Ok; | 
|  | } | 
|  |  | 
|  | GpStatus WINGDIPAPI GdipCreatePen1(ARGB color, REAL width, GpUnit unit, | 
|  | GpPen **pen) | 
|  | { | 
|  | GpBrush *brush; | 
|  | GpStatus status; | 
|  |  | 
|  | TRACE("(%x, %.2f, %d, %p)\n", color, width, unit, pen); | 
|  |  | 
|  | GdipCreateSolidFill(color, (GpSolidFill **)(&brush)); | 
|  | status = GdipCreatePen2(brush, width, unit, pen); | 
|  | GdipDeleteBrush(brush); | 
|  | return status; | 
|  | } | 
|  |  | 
|  | GpStatus WINGDIPAPI GdipCreatePen2(GpBrush *brush, REAL width, GpUnit unit, | 
|  | GpPen **pen) | 
|  | { | 
|  | GpPen *gp_pen; | 
|  | GpBrush *clone_brush; | 
|  |  | 
|  | TRACE("(%p, %.2f, %d, %p)\n", brush, width, unit, pen); | 
|  |  | 
|  | if(!pen || !brush) | 
|  | return InvalidParameter; | 
|  |  | 
|  | gp_pen = GdipAlloc(sizeof(GpPen)); | 
|  | if(!gp_pen)    return OutOfMemory; | 
|  |  | 
|  | gp_pen->style = GP_DEFAULT_PENSTYLE; | 
|  | gp_pen->width = width; | 
|  | gp_pen->unit = unit; | 
|  | gp_pen->endcap = LineCapFlat; | 
|  | gp_pen->join = LineJoinMiter; | 
|  | gp_pen->miterlimit = 10.0; | 
|  | gp_pen->dash = DashStyleSolid; | 
|  | gp_pen->offset = 0.0; | 
|  | gp_pen->customstart = NULL; | 
|  | gp_pen->customend = NULL; | 
|  |  | 
|  | if(!((gp_pen->unit == UnitWorld) || (gp_pen->unit == UnitPixel))) { | 
|  | FIXME("UnitWorld, UnitPixel only supported units\n"); | 
|  | GdipFree(gp_pen); | 
|  | return NotImplemented; | 
|  | } | 
|  |  | 
|  | GdipCloneBrush(brush, &clone_brush); | 
|  | gp_pen->brush = clone_brush; | 
|  |  | 
|  | *pen = gp_pen; | 
|  |  | 
|  | return Ok; | 
|  | } | 
|  |  | 
|  | GpStatus WINGDIPAPI GdipDeletePen(GpPen *pen) | 
|  | { | 
|  | TRACE("(%p)\n", pen); | 
|  |  | 
|  | if(!pen)    return InvalidParameter; | 
|  |  | 
|  | GdipDeleteBrush(pen->brush); | 
|  | GdipDeleteCustomLineCap(pen->customstart); | 
|  | GdipDeleteCustomLineCap(pen->customend); | 
|  | GdipFree(pen->dashes); | 
|  | GdipFree(pen); | 
|  |  | 
|  | return Ok; | 
|  | } | 
|  |  | 
|  | GpStatus WINGDIPAPI GdipGetPenBrushFill(GpPen *pen, GpBrush **brush) | 
|  | { | 
|  | TRACE("(%p, %p)\n", pen, brush); | 
|  |  | 
|  | if(!pen || !brush) | 
|  | return InvalidParameter; | 
|  |  | 
|  | return GdipCloneBrush(pen->brush, brush); | 
|  | } | 
|  |  | 
|  | GpStatus WINGDIPAPI GdipGetPenColor(GpPen *pen, ARGB *argb) | 
|  | { | 
|  | TRACE("(%p, %p)\n", pen, argb); | 
|  |  | 
|  | if(!pen || !argb) | 
|  | return InvalidParameter; | 
|  |  | 
|  | if(pen->brush->bt != BrushTypeSolidColor) | 
|  | return NotImplemented; | 
|  |  | 
|  | return GdipGetSolidFillColor(((GpSolidFill*)pen->brush), argb); | 
|  | } | 
|  |  | 
|  | GpStatus WINGDIPAPI GdipGetPenCustomEndCap(GpPen *pen, GpCustomLineCap** customCap) | 
|  | { | 
|  | TRACE("(%p, %p)\n", pen, customCap); | 
|  |  | 
|  | if(!pen || !customCap) | 
|  | return InvalidParameter; | 
|  |  | 
|  | if(!pen->customend){ | 
|  | *customCap = NULL; | 
|  | return Ok; | 
|  | } | 
|  |  | 
|  | return GdipCloneCustomLineCap(pen->customend, customCap); | 
|  | } | 
|  |  | 
|  | GpStatus WINGDIPAPI GdipGetPenCustomStartCap(GpPen *pen, GpCustomLineCap** customCap) | 
|  | { | 
|  | TRACE("(%p, %p)\n", pen, customCap); | 
|  |  | 
|  | if(!pen || !customCap) | 
|  | return InvalidParameter; | 
|  |  | 
|  | if(!pen->customstart){ | 
|  | *customCap = NULL; | 
|  | return Ok; | 
|  | } | 
|  |  | 
|  | return GdipCloneCustomLineCap(pen->customstart, customCap); | 
|  | } | 
|  |  | 
|  | GpStatus WINGDIPAPI GdipGetPenDashArray(GpPen *pen, REAL *dash, INT count) | 
|  | { | 
|  | TRACE("(%p, %p, %d)\n", pen, dash, count); | 
|  |  | 
|  | if(!pen || !dash || count > pen->numdashes) | 
|  | return InvalidParameter; | 
|  |  | 
|  | /* note: if you pass a negative value for count, it crashes native gdiplus. */ | 
|  | if(count < 0) | 
|  | return GenericError; | 
|  |  | 
|  | memcpy(dash, pen->dashes, count * sizeof(REAL)); | 
|  |  | 
|  | return Ok; | 
|  | } | 
|  |  | 
|  | GpStatus WINGDIPAPI GdipGetPenDashCap197819(GpPen *pen, GpDashCap *dashCap) | 
|  | { | 
|  | TRACE("(%p, %p)\n", pen, dashCap); | 
|  |  | 
|  | if(!pen || !dashCap) | 
|  | return InvalidParameter; | 
|  |  | 
|  | *dashCap = pen->dashcap; | 
|  |  | 
|  | return Ok; | 
|  | } | 
|  |  | 
|  | GpStatus WINGDIPAPI GdipGetPenDashCount(GpPen *pen, INT *count) | 
|  | { | 
|  | TRACE("(%p, %p)\n", pen, count); | 
|  |  | 
|  | if(!pen || !count) | 
|  | return InvalidParameter; | 
|  |  | 
|  | *count = pen->numdashes; | 
|  |  | 
|  | return Ok; | 
|  | } | 
|  |  | 
|  | GpStatus WINGDIPAPI GdipGetPenDashOffset(GpPen *pen, REAL *offset) | 
|  | { | 
|  | TRACE("(%p, %p)\n", pen, offset); | 
|  |  | 
|  | if(!pen || !offset) | 
|  | return InvalidParameter; | 
|  |  | 
|  | *offset = pen->offset; | 
|  |  | 
|  | return Ok; | 
|  | } | 
|  |  | 
|  | GpStatus WINGDIPAPI GdipGetPenDashStyle(GpPen *pen, GpDashStyle *dash) | 
|  | { | 
|  | TRACE("(%p, %p)\n", pen, dash); | 
|  |  | 
|  | if(!pen || !dash) | 
|  | return InvalidParameter; | 
|  |  | 
|  | *dash = pen->dash; | 
|  |  | 
|  | return Ok; | 
|  | } | 
|  |  | 
|  | GpStatus WINGDIPAPI GdipGetPenEndCap(GpPen *pen, GpLineCap *endCap) | 
|  | { | 
|  | TRACE("(%p, %p)\n", pen, endCap); | 
|  |  | 
|  | if(!pen || !endCap) | 
|  | return InvalidParameter; | 
|  |  | 
|  | *endCap = pen->endcap; | 
|  |  | 
|  | return Ok; | 
|  | } | 
|  |  | 
|  | GpStatus WINGDIPAPI GdipGetPenFillType(GpPen *pen, GpPenType* type) | 
|  | { | 
|  | TRACE("(%p, %p)\n", pen, type); | 
|  |  | 
|  | if(!pen || !type) | 
|  | return InvalidParameter; | 
|  |  | 
|  | *type = bt_to_pt(pen->brush->bt); | 
|  |  | 
|  | return Ok; | 
|  | } | 
|  |  | 
|  | GpStatus WINGDIPAPI GdipGetPenLineJoin(GpPen *pen, GpLineJoin *lineJoin) | 
|  | { | 
|  | TRACE("(%p, %p)\n", pen, lineJoin); | 
|  |  | 
|  | if(!pen || !lineJoin) | 
|  | return InvalidParameter; | 
|  |  | 
|  | *lineJoin = pen->join; | 
|  |  | 
|  | return Ok; | 
|  | } | 
|  |  | 
|  | GpStatus WINGDIPAPI GdipGetPenMode(GpPen *pen, GpPenAlignment *mode) | 
|  | { | 
|  | TRACE("(%p, %p)\n", pen, mode); | 
|  |  | 
|  | if(!pen || !mode) | 
|  | return InvalidParameter; | 
|  |  | 
|  | *mode = pen->align; | 
|  |  | 
|  | return Ok; | 
|  | } | 
|  |  | 
|  | GpStatus WINGDIPAPI GdipGetPenMiterLimit(GpPen *pen, REAL *miterLimit) | 
|  | { | 
|  | TRACE("(%p, %p)\n", pen, miterLimit); | 
|  |  | 
|  | if(!pen || !miterLimit) | 
|  | return InvalidParameter; | 
|  |  | 
|  | *miterLimit = pen->miterlimit; | 
|  |  | 
|  | return Ok; | 
|  | } | 
|  |  | 
|  | GpStatus WINGDIPAPI GdipGetPenStartCap(GpPen *pen, GpLineCap *startCap) | 
|  | { | 
|  | TRACE("(%p, %p)\n", pen, startCap); | 
|  |  | 
|  | if(!pen || !startCap) | 
|  | return InvalidParameter; | 
|  |  | 
|  | *startCap = pen->startcap; | 
|  |  | 
|  | return Ok; | 
|  | } | 
|  |  | 
|  | GpStatus WINGDIPAPI GdipGetPenUnit(GpPen *pen, GpUnit *unit) | 
|  | { | 
|  | TRACE("(%p, %p)\n", pen, unit); | 
|  |  | 
|  | if(!pen || !unit) | 
|  | return InvalidParameter; | 
|  |  | 
|  | *unit = pen->unit; | 
|  |  | 
|  | return Ok; | 
|  | } | 
|  |  | 
|  | GpStatus WINGDIPAPI GdipGetPenWidth(GpPen *pen, REAL *width) | 
|  | { | 
|  | TRACE("(%p, %p)\n", pen, width); | 
|  |  | 
|  | if(!pen || !width) | 
|  | return InvalidParameter; | 
|  |  | 
|  | *width = pen->width; | 
|  |  | 
|  | return Ok; | 
|  | } | 
|  |  | 
|  | GpStatus WINGDIPAPI GdipResetPenTransform(GpPen *pen) | 
|  | { | 
|  | static int calls; | 
|  |  | 
|  | if(!pen) | 
|  | return InvalidParameter; | 
|  |  | 
|  | if(!(calls++)) | 
|  | FIXME("(%p) stub\n", pen); | 
|  |  | 
|  | return NotImplemented; | 
|  | } | 
|  |  | 
|  | GpStatus WINGDIPAPI GdipScalePenTransform(GpPen *pen, REAL sx, REAL sy, GpMatrixOrder order) | 
|  | { | 
|  | static int calls; | 
|  |  | 
|  | if(!pen) | 
|  | return InvalidParameter; | 
|  |  | 
|  | if(!(calls++)) | 
|  | FIXME("(%p, %.2f, %.2f, %d) stub\n", pen, sx, sy, order); | 
|  |  | 
|  | return NotImplemented; | 
|  | } | 
|  |  | 
|  | GpStatus WINGDIPAPI GdipSetPenBrushFill(GpPen *pen, GpBrush *brush) | 
|  | { | 
|  | TRACE("(%p, %p)\n", pen, brush); | 
|  |  | 
|  | if(!pen || !brush) | 
|  | return InvalidParameter; | 
|  |  | 
|  | GdipDeleteBrush(pen->brush); | 
|  | return GdipCloneBrush(brush, &pen->brush); | 
|  | } | 
|  |  | 
|  | GpStatus WINGDIPAPI GdipSetPenColor(GpPen *pen, ARGB argb) | 
|  | { | 
|  | TRACE("(%p, %x)\n", pen, argb); | 
|  |  | 
|  | if(!pen) | 
|  | return InvalidParameter; | 
|  |  | 
|  | if(pen->brush->bt != BrushTypeSolidColor) | 
|  | return NotImplemented; | 
|  |  | 
|  | return GdipSetSolidFillColor(((GpSolidFill*)pen->brush), argb); | 
|  | } | 
|  |  | 
|  | GpStatus WINGDIPAPI GdipSetPenCompoundArray(GpPen *pen, GDIPCONST REAL *dash, | 
|  | INT count) | 
|  | { | 
|  | FIXME("(%p, %p, %i): stub\n", pen, dash, count); | 
|  |  | 
|  | if (!pen || !dash || count < 2 || count%2 == 1) | 
|  | return InvalidParameter; | 
|  |  | 
|  | return NotImplemented; | 
|  | } | 
|  |  | 
|  | GpStatus WINGDIPAPI GdipSetPenCustomEndCap(GpPen *pen, GpCustomLineCap* customCap) | 
|  | { | 
|  | GpCustomLineCap * cap; | 
|  | GpStatus ret; | 
|  |  | 
|  | TRACE("(%p, %p)\n", pen, customCap); | 
|  |  | 
|  | /* native crashes on pen == NULL, customCap != NULL */ | 
|  | if(!customCap) return InvalidParameter; | 
|  |  | 
|  | if((ret = GdipCloneCustomLineCap(customCap, &cap)) == Ok){ | 
|  | GdipDeleteCustomLineCap(pen->customend); | 
|  | pen->endcap = LineCapCustom; | 
|  | pen->customend = cap; | 
|  | } | 
|  |  | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | GpStatus WINGDIPAPI GdipSetPenCustomStartCap(GpPen *pen, GpCustomLineCap* customCap) | 
|  | { | 
|  | GpCustomLineCap * cap; | 
|  | GpStatus ret; | 
|  |  | 
|  | TRACE("(%p, %p)\n", pen, customCap); | 
|  |  | 
|  | /* native crashes on pen == NULL, customCap != NULL */ | 
|  | if(!customCap) return InvalidParameter; | 
|  |  | 
|  | if((ret = GdipCloneCustomLineCap(customCap, &cap)) == Ok){ | 
|  | GdipDeleteCustomLineCap(pen->customstart); | 
|  | pen->startcap = LineCapCustom; | 
|  | pen->customstart = cap; | 
|  | } | 
|  |  | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | GpStatus WINGDIPAPI GdipSetPenDashArray(GpPen *pen, GDIPCONST REAL *dash, | 
|  | INT count) | 
|  | { | 
|  | INT i; | 
|  | REAL sum = 0; | 
|  |  | 
|  | TRACE("(%p, %p, %d)\n", pen, dash, count); | 
|  |  | 
|  | if(!pen || !dash) | 
|  | return InvalidParameter; | 
|  |  | 
|  | if(count <= 0) | 
|  | return OutOfMemory; | 
|  |  | 
|  | for(i = 0; i < count; i++){ | 
|  | sum += dash[i]; | 
|  | if(dash[i] < 0.0) | 
|  | return InvalidParameter; | 
|  | } | 
|  |  | 
|  | if(sum == 0.0 && count) | 
|  | return InvalidParameter; | 
|  |  | 
|  | GdipFree(pen->dashes); | 
|  | pen->dashes = NULL; | 
|  |  | 
|  | if(count > 0) | 
|  | pen->dashes = GdipAlloc(count * sizeof(REAL)); | 
|  | if(!pen->dashes){ | 
|  | pen->numdashes = 0; | 
|  | return OutOfMemory; | 
|  | } | 
|  |  | 
|  | GdipSetPenDashStyle(pen, DashStyleCustom); | 
|  | memcpy(pen->dashes, dash, count * sizeof(REAL)); | 
|  | pen->numdashes = count; | 
|  |  | 
|  | return Ok; | 
|  | } | 
|  |  | 
|  | GpStatus WINGDIPAPI GdipSetPenDashCap197819(GpPen *pen, GpDashCap dashCap) | 
|  | { | 
|  | TRACE("(%p, %d)\n", pen, dashCap); | 
|  |  | 
|  | if(!pen) | 
|  | return InvalidParameter; | 
|  |  | 
|  | pen->dashcap = dashCap; | 
|  |  | 
|  | return Ok; | 
|  | } | 
|  |  | 
|  | /* FIXME: dash offset not used */ | 
|  | GpStatus WINGDIPAPI GdipSetPenDashOffset(GpPen *pen, REAL offset) | 
|  | { | 
|  | TRACE("(%p, %.2f)\n", pen, offset); | 
|  |  | 
|  | if(!pen) | 
|  | return InvalidParameter; | 
|  |  | 
|  | pen->offset = offset; | 
|  |  | 
|  | return Ok; | 
|  | } | 
|  |  | 
|  | GpStatus WINGDIPAPI GdipSetPenDashStyle(GpPen *pen, GpDashStyle dash) | 
|  | { | 
|  | TRACE("(%p, %d)\n", pen, dash); | 
|  |  | 
|  | if(!pen) | 
|  | return InvalidParameter; | 
|  |  | 
|  | if(dash != DashStyleCustom){ | 
|  | GdipFree(pen->dashes); | 
|  | pen->dashes = NULL; | 
|  | pen->numdashes = 0; | 
|  | } | 
|  |  | 
|  | pen->dash = dash; | 
|  | pen->style &= ~(PS_ALTERNATE | PS_SOLID | PS_DASH | PS_DOT | PS_DASHDOT | | 
|  | PS_DASHDOTDOT | PS_NULL | PS_USERSTYLE | PS_INSIDEFRAME); | 
|  | pen->style |= gdip_to_gdi_dash(dash); | 
|  |  | 
|  | return Ok; | 
|  | } | 
|  |  | 
|  | GpStatus WINGDIPAPI GdipSetPenEndCap(GpPen *pen, GpLineCap cap) | 
|  | { | 
|  | TRACE("(%p, %d)\n", pen, cap); | 
|  |  | 
|  | if(!pen)    return InvalidParameter; | 
|  |  | 
|  | /* The old custom cap gets deleted even if the new style is LineCapCustom. */ | 
|  | GdipDeleteCustomLineCap(pen->customend); | 
|  | pen->customend = NULL; | 
|  | pen->endcap = cap; | 
|  |  | 
|  | return Ok; | 
|  | } | 
|  |  | 
|  | /* FIXME: startcap, dashcap not used. */ | 
|  | GpStatus WINGDIPAPI GdipSetPenLineCap197819(GpPen *pen, GpLineCap start, | 
|  | GpLineCap end, GpDashCap dash) | 
|  | { | 
|  | TRACE("%p, %d, %d, %d)\n", pen, start, end, dash); | 
|  |  | 
|  | if(!pen) | 
|  | return InvalidParameter; | 
|  |  | 
|  | GdipDeleteCustomLineCap(pen->customend); | 
|  | GdipDeleteCustomLineCap(pen->customstart); | 
|  | pen->customend = NULL; | 
|  | pen->customstart = NULL; | 
|  |  | 
|  | pen->startcap = start; | 
|  | pen->endcap = end; | 
|  | pen->dashcap = dash; | 
|  |  | 
|  | return Ok; | 
|  | } | 
|  |  | 
|  | /* FIXME: Miter line joins behave a bit differently than they do in windows. | 
|  | * Both kinds of miter joins clip if the angle is less than 11 degrees. */ | 
|  | GpStatus WINGDIPAPI GdipSetPenLineJoin(GpPen *pen, GpLineJoin join) | 
|  | { | 
|  | TRACE("(%p, %d)\n", pen, join); | 
|  |  | 
|  | if(!pen)    return InvalidParameter; | 
|  |  | 
|  | pen->join = join; | 
|  | pen->style &= ~(PS_JOIN_ROUND | PS_JOIN_BEVEL | PS_JOIN_MITER); | 
|  | pen->style |= gdip_to_gdi_join(join); | 
|  |  | 
|  | return Ok; | 
|  | } | 
|  |  | 
|  | GpStatus WINGDIPAPI GdipSetPenMiterLimit(GpPen *pen, REAL limit) | 
|  | { | 
|  | TRACE("(%p, %.2f)\n", pen, limit); | 
|  |  | 
|  | if(!pen) | 
|  | return InvalidParameter; | 
|  |  | 
|  | pen->miterlimit = limit; | 
|  |  | 
|  | return Ok; | 
|  | } | 
|  |  | 
|  | GpStatus WINGDIPAPI GdipSetPenStartCap(GpPen *pen, GpLineCap cap) | 
|  | { | 
|  | TRACE("(%p, %d)\n", pen, cap); | 
|  |  | 
|  | if(!pen)    return InvalidParameter; | 
|  |  | 
|  | GdipDeleteCustomLineCap(pen->customstart); | 
|  | pen->customstart = NULL; | 
|  | pen->startcap = cap; | 
|  |  | 
|  | return Ok; | 
|  | } | 
|  |  | 
|  | GpStatus WINGDIPAPI GdipSetPenWidth(GpPen *pen, REAL width) | 
|  | { | 
|  | TRACE("(%p, %.2f)\n", pen, width); | 
|  |  | 
|  | if(!pen)    return InvalidParameter; | 
|  |  | 
|  | pen->width = width; | 
|  |  | 
|  | return Ok; | 
|  | } | 
|  |  | 
|  | GpStatus WINGDIPAPI GdipSetPenMode(GpPen *pen, GpPenAlignment mode) | 
|  | { | 
|  | TRACE("(%p, %d)\n", pen, mode); | 
|  |  | 
|  | if(!pen)    return InvalidParameter; | 
|  |  | 
|  | pen->align = mode; | 
|  |  | 
|  | return Ok; | 
|  | } |