gdiplus: Implement GdipCreateMatrix3I.
[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     GdipCreateSolidFill(color, (GpSolidFill **)(&brush));
92     return GdipCreatePen2(brush, width, unit, pen);
93 }
94
95 GpStatus WINGDIPAPI GdipCreatePen2(GpBrush *brush, REAL width, GpUnit unit,
96     GpPen **pen)
97 {
98     GpPen *gp_pen;
99
100     if(!pen || !brush)
101         return InvalidParameter;
102
103     gp_pen = GdipAlloc(sizeof(GpPen));
104     if(!gp_pen)    return OutOfMemory;
105
106     gp_pen->style = GP_DEFAULT_PENSTYLE;
107     gp_pen->width = width;
108     gp_pen->unit = unit;
109     gp_pen->endcap = LineCapFlat;
110     gp_pen->join = LineJoinMiter;
111     gp_pen->miterlimit = 10.0;
112     gp_pen->dash = DashStyleSolid;
113     gp_pen->offset = 0.0;
114     gp_pen->brush = brush;
115
116     if(!((gp_pen->unit == UnitWorld) || (gp_pen->unit == UnitPixel))) {
117         FIXME("UnitWorld, UnitPixel only supported units\n");
118         GdipFree(gp_pen);
119         return NotImplemented;
120     }
121
122     *pen = gp_pen;
123
124     return Ok;
125 }
126
127 GpStatus WINGDIPAPI GdipDeletePen(GpPen *pen)
128 {
129     if(!pen)    return InvalidParameter;
130
131     GdipDeleteBrush(pen->brush);
132     GdipDeleteCustomLineCap(pen->customstart);
133     GdipDeleteCustomLineCap(pen->customend);
134     GdipFree(pen->dashes);
135     GdipFree(pen);
136
137     return Ok;
138 }
139
140 GpStatus WINGDIPAPI GdipGetPenBrushFill(GpPen *pen, GpBrush **brush)
141 {
142     if(!pen || !brush)
143         return InvalidParameter;
144
145     return GdipCloneBrush(pen->brush, brush);
146 }
147
148 GpStatus WINGDIPAPI GdipGetPenColor(GpPen *pen, ARGB *argb)
149 {
150     if(!pen || !argb)
151         return InvalidParameter;
152
153     if(pen->brush->bt != BrushTypeSolidColor)
154         return NotImplemented;
155
156     return GdipGetSolidFillColor(((GpSolidFill*)pen->brush), argb);
157 }
158
159 GpStatus WINGDIPAPI GdipGetPenDashArray(GpPen *pen, REAL *dash, INT count)
160 {
161     if(!pen || !dash || count > pen->numdashes)
162         return InvalidParameter;
163
164     /* note: if you pass a negative value for count, it crashes native gdiplus. */
165     if(count < 0)
166         return GenericError;
167
168     memcpy(dash, pen->dashes, count * sizeof(REAL));
169
170     return Ok;
171 }
172
173 GpStatus WINGDIPAPI GdipGetPenDashOffset(GpPen *pen, REAL *offset)
174 {
175     if(!pen || !offset)
176         return InvalidParameter;
177
178     *offset = pen->offset;
179
180     return Ok;
181 }
182
183 GpStatus WINGDIPAPI GdipGetPenDashStyle(GpPen *pen, GpDashStyle *dash)
184 {
185     if(!pen || !dash)
186         return InvalidParameter;
187
188     *dash = pen->dash;
189
190     return Ok;
191 }
192
193 GpStatus WINGDIPAPI GdipSetPenBrushFill(GpPen *pen, GpBrush *brush)
194 {
195     if(!pen || !brush)
196         return InvalidParameter;
197
198     GdipDeleteBrush(pen->brush);
199     return GdipCloneBrush(brush, &pen->brush);
200 }
201
202 GpStatus WINGDIPAPI GdipSetPenColor(GpPen *pen, ARGB argb)
203 {
204     if(!pen)
205         return InvalidParameter;
206
207     if(pen->brush->bt != BrushTypeSolidColor)
208         return NotImplemented;
209
210     return GdipSetSolidFillColor(((GpSolidFill*)pen->brush), argb);
211 }
212
213 GpStatus WINGDIPAPI GdipSetPenCustomEndCap(GpPen *pen, GpCustomLineCap* customCap)
214 {
215     GpCustomLineCap * cap;
216     GpStatus ret;
217
218     if(!pen || !customCap) return InvalidParameter;
219
220     if((ret = GdipCloneCustomLineCap(customCap, &cap)) == Ok){
221         GdipDeleteCustomLineCap(pen->customend);
222         pen->endcap = LineCapCustom;
223         pen->customend = cap;
224     }
225
226     return ret;
227 }
228
229 GpStatus WINGDIPAPI GdipSetPenCustomStartCap(GpPen *pen, GpCustomLineCap* customCap)
230 {
231     GpCustomLineCap * cap;
232     GpStatus ret;
233
234     if(!pen || !customCap) return InvalidParameter;
235
236     if((ret = GdipCloneCustomLineCap(customCap, &cap)) == Ok){
237         GdipDeleteCustomLineCap(pen->customstart);
238         pen->startcap = LineCapCustom;
239         pen->customstart = cap;
240     }
241
242     return ret;
243 }
244
245 GpStatus WINGDIPAPI GdipSetPenDashArray(GpPen *pen, GDIPCONST REAL *dash,
246     INT count)
247 {
248     INT i;
249     REAL sum = 0;
250
251     if(!pen || !dash)
252         return InvalidParameter;
253
254     for(i = 0; i < count; i++){
255         sum += dash[i];
256         if(dash[i] < 0.0)
257             return InvalidParameter;
258     }
259
260     if(sum == 0.0 && count)
261         return InvalidParameter;
262
263     GdipFree(pen->dashes);
264     pen->dashes = NULL;
265
266     if(count > 0)
267         pen->dashes = GdipAlloc(count * sizeof(REAL));
268     if(!pen->dashes){
269         pen->numdashes = 0;
270         return OutOfMemory;
271     }
272
273     GdipSetPenDashStyle(pen, DashStyleCustom);
274     memcpy(pen->dashes, dash, count * sizeof(REAL));
275     pen->numdashes = count;
276
277     return Ok;
278 }
279
280 /* FIXME: dash offset not used */
281 GpStatus WINGDIPAPI GdipSetPenDashOffset(GpPen *pen, REAL offset)
282 {
283     if(!pen)
284         return InvalidParameter;
285
286     pen->offset = offset;
287
288     return Ok;
289 }
290
291 GpStatus WINGDIPAPI GdipSetPenDashStyle(GpPen *pen, GpDashStyle dash)
292 {
293     if(!pen)
294         return InvalidParameter;
295
296     if(dash != DashStyleCustom){
297         GdipFree(pen->dashes);
298         pen->dashes = NULL;
299         pen->numdashes = 0;
300     }
301
302     pen->dash = dash;
303     pen->style &= ~(PS_ALTERNATE | PS_SOLID | PS_DASH | PS_DOT | PS_DASHDOT |
304                     PS_DASHDOTDOT | PS_NULL | PS_USERSTYLE | PS_INSIDEFRAME);
305     pen->style |= gdip_to_gdi_dash(dash);
306
307     return Ok;
308 }
309
310 GpStatus WINGDIPAPI GdipSetPenEndCap(GpPen *pen, GpLineCap cap)
311 {
312     if(!pen)    return InvalidParameter;
313
314     /* The old custom cap gets deleted even if the new style is LineCapCustom. */
315     GdipDeleteCustomLineCap(pen->customend);
316     pen->customend = NULL;
317     pen->endcap = cap;
318
319     return Ok;
320 }
321
322 /* FIXME: startcap, dashcap not used. */
323 GpStatus WINGDIPAPI GdipSetPenLineCap197819(GpPen *pen, GpLineCap start,
324     GpLineCap end, GpDashCap dash)
325 {
326     if(!pen)
327         return InvalidParameter;
328
329     GdipDeleteCustomLineCap(pen->customend);
330     GdipDeleteCustomLineCap(pen->customstart);
331     pen->customend = NULL;
332     pen->customstart = NULL;
333
334     pen->startcap = start;
335     pen->endcap = end;
336     pen->dashcap = dash;
337
338     return Ok;
339 }
340
341 /* FIXME: Miter line joins behave a bit differently than they do in windows.
342  * Both kinds of miter joins clip if the angle is less than 11 degrees. */
343 GpStatus WINGDIPAPI GdipSetPenLineJoin(GpPen *pen, GpLineJoin join)
344 {
345     if(!pen)    return InvalidParameter;
346
347     pen->join = join;
348     pen->style &= ~(PS_JOIN_ROUND | PS_JOIN_BEVEL | PS_JOIN_MITER);
349     pen->style |= gdip_to_gdi_join(join);
350
351     return Ok;
352 }
353
354 GpStatus WINGDIPAPI GdipSetPenMiterLimit(GpPen *pen, REAL limit)
355 {
356     if(!pen)
357         return InvalidParameter;
358
359     pen->miterlimit = limit;
360
361     return Ok;
362 }
363
364 GpStatus WINGDIPAPI GdipSetPenStartCap(GpPen *pen, GpLineCap cap)
365 {
366     if(!pen)    return InvalidParameter;
367
368     GdipDeleteCustomLineCap(pen->customstart);
369     pen->customstart = NULL;
370     pen->startcap = cap;
371
372     return Ok;
373 }
374
375 GpStatus WINGDIPAPI GdipSetPenWidth(GpPen *pen, REAL width)
376 {
377     if(!pen)    return InvalidParameter;
378
379     pen->width = width;
380
381     return Ok;
382 }
383
384
385 GpStatus WINGDIPAPI GdipSetPenMode(GpPen *pen, GpPenAlignment penAlignment)
386 {
387     if(!pen)    return InvalidParameter;
388
389     FIXME("stub (%d)\n", penAlignment);
390
391     return Ok;
392 }