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