gdiplus: Added GdipGetPenStartCap.
[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
25 #include "objbase.h"
26
27 #include "gdiplus.h"
28 #include "gdiplus_private.h"
29 #include "wine/debug.h"
30
31 WINE_DEFAULT_DEBUG_CHANNEL(gdiplus);
32
33 static DWORD gdip_to_gdi_dash(GpDashStyle dash)
34 {
35     switch(dash){
36         case DashStyleSolid:
37             return PS_SOLID;
38         case DashStyleDash:
39             return PS_DASH;
40         case DashStyleDot:
41             return PS_DOT;
42         case DashStyleDashDot:
43             return PS_DASHDOT;
44         case DashStyleDashDotDot:
45             return PS_DASHDOTDOT;
46         case DashStyleCustom:
47             return PS_USERSTYLE;
48         default:
49             ERR("Not a member of GpDashStyle enumeration\n");
50             return 0;
51     }
52 }
53
54 static DWORD gdip_to_gdi_join(GpLineJoin join)
55 {
56     switch(join){
57         case LineJoinRound:
58             return PS_JOIN_ROUND;
59         case LineJoinBevel:
60             return PS_JOIN_BEVEL;
61         case LineJoinMiter:
62         case LineJoinMiterClipped:
63             return PS_JOIN_MITER;
64         default:
65             ERR("Not a member of GpLineJoin enumeration\n");
66             return 0;
67     }
68 }
69
70 GpStatus WINGDIPAPI GdipClonePen(GpPen *pen, GpPen **clonepen)
71 {
72     if(!pen || !clonepen)
73         return InvalidParameter;
74
75     *clonepen = GdipAlloc(sizeof(GpPen));
76     if(!*clonepen)  return OutOfMemory;
77
78     **clonepen = *pen;
79
80     GdipCloneCustomLineCap(pen->customstart, &(*clonepen)->customstart);
81     GdipCloneCustomLineCap(pen->customend, &(*clonepen)->customend);
82     GdipCloneBrush(pen->brush, &(*clonepen)->brush);
83
84     return Ok;
85 }
86
87 GpStatus WINGDIPAPI GdipCreatePen1(ARGB color, REAL width, GpUnit unit,
88     GpPen **pen)
89 {
90     GpBrush *brush;
91     GpStatus status;
92
93     GdipCreateSolidFill(color, (GpSolidFill **)(&brush));
94     status = GdipCreatePen2(brush, width, unit, pen);
95     GdipDeleteBrush(brush);
96     return status;
97 }
98
99 GpStatus WINGDIPAPI GdipCreatePen2(GpBrush *brush, REAL width, GpUnit unit,
100     GpPen **pen)
101 {
102     GpPen *gp_pen;
103     GpBrush *clone_brush;
104
105     if(!pen || !brush)
106         return InvalidParameter;
107
108     gp_pen = GdipAlloc(sizeof(GpPen));
109     if(!gp_pen)    return OutOfMemory;
110
111     gp_pen->style = GP_DEFAULT_PENSTYLE;
112     gp_pen->width = width;
113     gp_pen->unit = unit;
114     gp_pen->endcap = LineCapFlat;
115     gp_pen->join = LineJoinMiter;
116     gp_pen->miterlimit = 10.0;
117     gp_pen->dash = DashStyleSolid;
118     gp_pen->offset = 0.0;
119
120     if(!((gp_pen->unit == UnitWorld) || (gp_pen->unit == UnitPixel))) {
121         FIXME("UnitWorld, UnitPixel only supported units\n");
122         GdipFree(gp_pen);
123         return NotImplemented;
124     }
125
126     GdipCloneBrush(brush, &clone_brush);
127     gp_pen->brush = clone_brush;
128
129     *pen = gp_pen;
130
131     return Ok;
132 }
133
134 GpStatus WINGDIPAPI GdipDeletePen(GpPen *pen)
135 {
136     if(!pen)    return InvalidParameter;
137
138     GdipDeleteBrush(pen->brush);
139     GdipDeleteCustomLineCap(pen->customstart);
140     GdipDeleteCustomLineCap(pen->customend);
141     GdipFree(pen->dashes);
142     GdipFree(pen);
143
144     return Ok;
145 }
146
147 GpStatus WINGDIPAPI GdipGetPenBrushFill(GpPen *pen, GpBrush **brush)
148 {
149     if(!pen || !brush)
150         return InvalidParameter;
151
152     return GdipCloneBrush(pen->brush, brush);
153 }
154
155 GpStatus WINGDIPAPI GdipGetPenColor(GpPen *pen, ARGB *argb)
156 {
157     if(!pen || !argb)
158         return InvalidParameter;
159
160     if(pen->brush->bt != BrushTypeSolidColor)
161         return NotImplemented;
162
163     return GdipGetSolidFillColor(((GpSolidFill*)pen->brush), argb);
164 }
165
166 GpStatus WINGDIPAPI GdipGetPenDashArray(GpPen *pen, REAL *dash, INT count)
167 {
168     if(!pen || !dash || count > pen->numdashes)
169         return InvalidParameter;
170
171     /* note: if you pass a negative value for count, it crashes native gdiplus. */
172     if(count < 0)
173         return GenericError;
174
175     memcpy(dash, pen->dashes, count * sizeof(REAL));
176
177     return Ok;
178 }
179
180 GpStatus WINGDIPAPI GdipGetPenDashOffset(GpPen *pen, REAL *offset)
181 {
182     if(!pen || !offset)
183         return InvalidParameter;
184
185     *offset = pen->offset;
186
187     return Ok;
188 }
189
190 GpStatus WINGDIPAPI GdipGetPenDashStyle(GpPen *pen, GpDashStyle *dash)
191 {
192     if(!pen || !dash)
193         return InvalidParameter;
194
195     *dash = pen->dash;
196
197     return Ok;
198 }
199
200 GpStatus WINGDIPAPI GdipGetPenEndCap(GpPen *pen, GpLineCap *endCap)
201 {
202     if(!pen || !endCap)
203         return InvalidParameter;
204
205     *endCap = pen->endcap;
206
207     return Ok;
208 }
209
210 GpStatus WINGDIPAPI GdipGetPenLineJoin(GpPen *pen, GpLineJoin *lineJoin)
211 {
212     if(!pen || !lineJoin)
213         return InvalidParameter;
214
215     *lineJoin = pen->join;
216
217     return Ok;
218 }
219
220 GpStatus WINGDIPAPI GdipGetPenMiterLimit(GpPen *pen, REAL *miterLimit)
221 {
222     if(!pen || !miterLimit)
223         return InvalidParameter;
224
225     *miterLimit = pen->miterlimit;
226
227     return Ok;
228 }
229
230 GpStatus WINGDIPAPI GdipGetPenStartCap(GpPen *pen, GpLineCap *startCap)
231 {
232     if(!pen || !startCap)
233         return InvalidParameter;
234
235     *startCap = pen->startcap;
236
237     return Ok;
238 }
239
240 GpStatus WINGDIPAPI GdipSetPenBrushFill(GpPen *pen, GpBrush *brush)
241 {
242     if(!pen || !brush)
243         return InvalidParameter;
244
245     GdipDeleteBrush(pen->brush);
246     return GdipCloneBrush(brush, &pen->brush);
247 }
248
249 GpStatus WINGDIPAPI GdipSetPenColor(GpPen *pen, ARGB argb)
250 {
251     if(!pen)
252         return InvalidParameter;
253
254     if(pen->brush->bt != BrushTypeSolidColor)
255         return NotImplemented;
256
257     return GdipSetSolidFillColor(((GpSolidFill*)pen->brush), argb);
258 }
259
260 GpStatus WINGDIPAPI GdipSetPenCustomEndCap(GpPen *pen, GpCustomLineCap* customCap)
261 {
262     GpCustomLineCap * cap;
263     GpStatus ret;
264
265     if(!pen || !customCap) return InvalidParameter;
266
267     if((ret = GdipCloneCustomLineCap(customCap, &cap)) == Ok){
268         GdipDeleteCustomLineCap(pen->customend);
269         pen->endcap = LineCapCustom;
270         pen->customend = cap;
271     }
272
273     return ret;
274 }
275
276 GpStatus WINGDIPAPI GdipSetPenCustomStartCap(GpPen *pen, GpCustomLineCap* customCap)
277 {
278     GpCustomLineCap * cap;
279     GpStatus ret;
280
281     if(!pen || !customCap) return InvalidParameter;
282
283     if((ret = GdipCloneCustomLineCap(customCap, &cap)) == Ok){
284         GdipDeleteCustomLineCap(pen->customstart);
285         pen->startcap = LineCapCustom;
286         pen->customstart = cap;
287     }
288
289     return ret;
290 }
291
292 GpStatus WINGDIPAPI GdipSetPenDashArray(GpPen *pen, GDIPCONST REAL *dash,
293     INT count)
294 {
295     INT i;
296     REAL sum = 0;
297
298     if(!pen || !dash)
299         return InvalidParameter;
300
301     if(count <= 0)
302         return OutOfMemory;
303
304     for(i = 0; i < count; i++){
305         sum += dash[i];
306         if(dash[i] < 0.0)
307             return InvalidParameter;
308     }
309
310     if(sum == 0.0 && count)
311         return InvalidParameter;
312
313     GdipFree(pen->dashes);
314     pen->dashes = NULL;
315
316     if(count > 0)
317         pen->dashes = GdipAlloc(count * sizeof(REAL));
318     if(!pen->dashes){
319         pen->numdashes = 0;
320         return OutOfMemory;
321     }
322
323     GdipSetPenDashStyle(pen, DashStyleCustom);
324     memcpy(pen->dashes, dash, count * sizeof(REAL));
325     pen->numdashes = count;
326
327     return Ok;
328 }
329
330 /* FIXME: dash offset not used */
331 GpStatus WINGDIPAPI GdipSetPenDashOffset(GpPen *pen, REAL offset)
332 {
333     if(!pen)
334         return InvalidParameter;
335
336     pen->offset = offset;
337
338     return Ok;
339 }
340
341 GpStatus WINGDIPAPI GdipSetPenDashStyle(GpPen *pen, GpDashStyle dash)
342 {
343     if(!pen)
344         return InvalidParameter;
345
346     if(dash != DashStyleCustom){
347         GdipFree(pen->dashes);
348         pen->dashes = NULL;
349         pen->numdashes = 0;
350     }
351
352     pen->dash = dash;
353     pen->style &= ~(PS_ALTERNATE | PS_SOLID | PS_DASH | PS_DOT | PS_DASHDOT |
354                     PS_DASHDOTDOT | PS_NULL | PS_USERSTYLE | PS_INSIDEFRAME);
355     pen->style |= gdip_to_gdi_dash(dash);
356
357     return Ok;
358 }
359
360 GpStatus WINGDIPAPI GdipSetPenEndCap(GpPen *pen, GpLineCap cap)
361 {
362     if(!pen)    return InvalidParameter;
363
364     /* The old custom cap gets deleted even if the new style is LineCapCustom. */
365     GdipDeleteCustomLineCap(pen->customend);
366     pen->customend = NULL;
367     pen->endcap = cap;
368
369     return Ok;
370 }
371
372 /* FIXME: startcap, dashcap not used. */
373 GpStatus WINGDIPAPI GdipSetPenLineCap197819(GpPen *pen, GpLineCap start,
374     GpLineCap end, GpDashCap dash)
375 {
376     if(!pen)
377         return InvalidParameter;
378
379     GdipDeleteCustomLineCap(pen->customend);
380     GdipDeleteCustomLineCap(pen->customstart);
381     pen->customend = NULL;
382     pen->customstart = NULL;
383
384     pen->startcap = start;
385     pen->endcap = end;
386     pen->dashcap = dash;
387
388     return Ok;
389 }
390
391 /* FIXME: Miter line joins behave a bit differently than they do in windows.
392  * Both kinds of miter joins clip if the angle is less than 11 degrees. */
393 GpStatus WINGDIPAPI GdipSetPenLineJoin(GpPen *pen, GpLineJoin join)
394 {
395     if(!pen)    return InvalidParameter;
396
397     pen->join = join;
398     pen->style &= ~(PS_JOIN_ROUND | PS_JOIN_BEVEL | PS_JOIN_MITER);
399     pen->style |= gdip_to_gdi_join(join);
400
401     return Ok;
402 }
403
404 GpStatus WINGDIPAPI GdipSetPenMiterLimit(GpPen *pen, REAL limit)
405 {
406     if(!pen)
407         return InvalidParameter;
408
409     pen->miterlimit = limit;
410
411     return Ok;
412 }
413
414 GpStatus WINGDIPAPI GdipSetPenStartCap(GpPen *pen, GpLineCap cap)
415 {
416     if(!pen)    return InvalidParameter;
417
418     GdipDeleteCustomLineCap(pen->customstart);
419     pen->customstart = NULL;
420     pen->startcap = cap;
421
422     return Ok;
423 }
424
425 GpStatus WINGDIPAPI GdipSetPenWidth(GpPen *pen, REAL width)
426 {
427     if(!pen)    return InvalidParameter;
428
429     pen->width = width;
430
431     return Ok;
432 }
433
434
435 GpStatus WINGDIPAPI GdipSetPenMode(GpPen *pen, GpPenAlignment penAlignment)
436 {
437     if(!pen)    return InvalidParameter;
438
439     FIXME("stub (%d)\n", penAlignment);
440
441     return Ok;
442 }