gdiplus: Associate a brush with a pen.
[wine] / dlls / gdiplus / pen.c
1 /*
2  * Copyright (C) 2007 Google (Evan Stade)
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
17  */
18
19 #include <stdarg.h>
20
21 #include "windef.h"
22 #include "winbase.h"
23 #include "wingdi.h"
24 #include "gdiplus.h"
25 #include "gdiplus_private.h"
26 #include "wine/debug.h"
27
28 WINE_DEFAULT_DEBUG_CHANNEL(gdiplus);
29
30 static DWORD gdip_to_gdi_dash(GpDashStyle dash)
31 {
32     switch(dash){
33         case DashStyleSolid:
34             return PS_SOLID;
35         case DashStyleDash:
36             return PS_DASH;
37         case DashStyleDot:
38             return PS_DOT;
39         case DashStyleDashDot:
40             return PS_DASHDOT;
41         case DashStyleDashDotDot:
42             return PS_DASHDOTDOT;
43         case DashStyleCustom:
44             FIXME("DashStyleCustom not implemented\n");
45             return PS_SOLID;
46         default:
47             ERR("Not a member of GpDashStyle enumeration\n");
48             return 0;
49     }
50 }
51
52 static DWORD gdip_to_gdi_join(GpLineJoin join)
53 {
54     switch(join){
55         case LineJoinRound:
56             return PS_JOIN_ROUND;
57         case LineJoinBevel:
58             return PS_JOIN_BEVEL;
59         case LineJoinMiter:
60         case LineJoinMiterClipped:
61             return PS_JOIN_MITER;
62         default:
63             ERR("Not a member of GpLineJoin enumeration\n");
64             return 0;
65     }
66 }
67
68 GpStatus WINGDIPAPI GdipClonePen(GpPen *pen, GpPen **clonepen)
69 {
70     LOGBRUSH lb;
71
72     if(!pen || !clonepen)
73         return InvalidParameter;
74
75     *clonepen = GdipAlloc(sizeof(GpPen));
76     if(!*clonepen)  return OutOfMemory;
77
78     memcpy(*clonepen, pen, sizeof(GpPen));
79
80     lb.lbStyle = BS_SOLID;
81     lb.lbColor = (*clonepen)->color;
82     lb.lbHatch = 0;
83
84     (*clonepen)->gdipen = ExtCreatePen((*clonepen)->style,
85                                        roundr((*clonepen)->width), &lb, 0, NULL);
86
87     return Ok;
88 }
89
90 GpStatus WINGDIPAPI GdipCreatePen1(ARGB color, FLOAT width, GpUnit unit,
91     GpPen **pen)
92 {
93     GpPen *gp_pen;
94
95     if(!pen)
96         return InvalidParameter;
97
98     gp_pen = GdipAlloc(sizeof(GpPen));
99     if(!gp_pen)    return OutOfMemory;
100
101     gp_pen->style = GP_DEFAULT_PENSTYLE;
102     gp_pen->color = ARGB2COLORREF(color);
103     gp_pen->width = width;
104     gp_pen->unit = unit;
105     gp_pen->endcap = LineCapFlat;
106     gp_pen->join = LineJoinMiter;
107     gp_pen->miterlimit = 10.0;
108     gp_pen->dash = DashStyleSolid;
109     GdipCreateSolidFill(color, (GpSolidFill **)(&gp_pen->brush));
110
111     if((gp_pen->unit == UnitWorld) || (gp_pen->unit == UnitPixel)) {
112         gp_pen->gdipen = ExtCreatePen(gp_pen->style, (INT) gp_pen->width,
113                                       &gp_pen->brush->lb, 0, NULL);
114     } else {
115         FIXME("UnitWorld, UnitPixel only supported units\n");
116         GdipFree(gp_pen);
117         return NotImplemented;
118     }
119
120     *pen = gp_pen;
121
122     return Ok;
123 }
124
125 GpStatus WINGDIPAPI GdipDeletePen(GpPen *pen)
126 {
127     if(!pen)    return InvalidParameter;
128     DeleteObject(pen->gdipen);
129
130     GdipDeleteBrush(pen->brush);
131     GdipDeleteCustomLineCap(pen->customstart);
132     GdipDeleteCustomLineCap(pen->customend);
133     GdipFree(pen);
134
135     return Ok;
136 }
137
138 GpStatus WINGDIPAPI GdipGetPenDashStyle(GpPen *pen, GpDashStyle *dash)
139 {
140     if(!pen || !dash)
141         return InvalidParameter;
142
143     *dash = pen->dash;
144
145     return Ok;
146 }
147
148 GpStatus WINGDIPAPI GdipSetPenCustomEndCap(GpPen *pen, GpCustomLineCap* customCap)
149 {
150     GpCustomLineCap * cap;
151     GpStatus ret;
152
153     if(!pen || !customCap) return InvalidParameter;
154
155     if((ret = GdipCloneCustomLineCap(customCap, &cap)) == Ok){
156         GdipDeleteCustomLineCap(pen->customend);
157         pen->endcap = LineCapCustom;
158         pen->customend = cap;
159     }
160
161     return ret;
162 }
163
164 GpStatus WINGDIPAPI GdipSetPenCustomStartCap(GpPen *pen, GpCustomLineCap* customCap)
165 {
166     GpCustomLineCap * cap;
167     GpStatus ret;
168
169     if(!pen || !customCap) return InvalidParameter;
170
171     if((ret = GdipCloneCustomLineCap(customCap, &cap)) == Ok){
172         GdipDeleteCustomLineCap(pen->customstart);
173         pen->startcap = LineCapCustom;
174         pen->customstart = cap;
175     }
176
177     return ret;
178 }
179
180 GpStatus WINGDIPAPI GdipSetPenDashStyle(GpPen *pen, GpDashStyle dash)
181 {
182     if(!pen)
183         return InvalidParameter;
184
185     DeleteObject(pen->gdipen);
186     pen->dash = dash;
187     pen->style &= ~(PS_ALTERNATE | PS_SOLID | PS_DASH | PS_DOT | PS_DASHDOT |
188                     PS_DASHDOTDOT | PS_NULL | PS_USERSTYLE | PS_INSIDEFRAME);
189     pen->style |= gdip_to_gdi_dash(dash);
190
191     pen->gdipen = ExtCreatePen(pen->style, (INT) pen->width, &pen->brush->lb, 0, NULL);
192
193     return Ok;
194 }
195
196 GpStatus WINGDIPAPI GdipSetPenEndCap(GpPen *pen, GpLineCap cap)
197 {
198     if(!pen)    return InvalidParameter;
199
200     /* The old custom cap gets deleted even if the new style is LineCapCustom. */
201     GdipDeleteCustomLineCap(pen->customend);
202     pen->customend = NULL;
203     pen->endcap = cap;
204
205     return Ok;
206 }
207
208 /* FIXME: startcap, dashcap not used. */
209 GpStatus WINGDIPAPI GdipSetPenLineCap197819(GpPen *pen, GpLineCap start,
210     GpLineCap end, GpDashCap dash)
211 {
212     if(!pen)
213         return InvalidParameter;
214
215     GdipDeleteCustomLineCap(pen->customend);
216     GdipDeleteCustomLineCap(pen->customstart);
217     pen->customend = NULL;
218     pen->customstart = NULL;
219
220     pen->startcap = start;
221     pen->endcap = end;
222     pen->dashcap = dash;
223
224     return Ok;
225 }
226
227 /* FIXME: Miter line joins behave a bit differently than they do in windows.
228  * Both kinds of miter joins clip if the angle is less than 11 degrees. */
229 GpStatus WINGDIPAPI GdipSetPenLineJoin(GpPen *pen, GpLineJoin join)
230 {
231     if(!pen)    return InvalidParameter;
232
233     DeleteObject(pen->gdipen);
234     pen->join = join;
235     pen->style &= ~(PS_JOIN_ROUND | PS_JOIN_BEVEL | PS_JOIN_MITER);
236     pen->style |= gdip_to_gdi_join(join);
237
238     pen->gdipen = ExtCreatePen(pen->style, (INT) pen->width, &pen->brush->lb, 0, NULL);
239
240     return Ok;
241 }
242
243 GpStatus WINGDIPAPI GdipSetPenMiterLimit(GpPen *pen, REAL limit)
244 {
245     if(!pen)
246         return InvalidParameter;
247
248     pen->miterlimit = limit;
249
250     return Ok;
251 }