mshtml: Added IHTMLElement::get_offsetHeight implementation.
[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 GdipGetPenDashCap197819(GpPen *pen, GpDashCap *dashCap)
181 {
182     if(!pen || !dashCap)
183         return InvalidParameter;
184
185     *dashCap = pen->dashcap;
186
187     return Ok;
188 }
189
190 GpStatus WINGDIPAPI GdipGetPenDashCount(GpPen *pen, INT *count)
191 {
192     if(!pen || !count)
193         return InvalidParameter;
194
195     *count = pen->numdashes;
196
197     return Ok;
198 }
199
200 GpStatus WINGDIPAPI GdipGetPenDashOffset(GpPen *pen, REAL *offset)
201 {
202     if(!pen || !offset)
203         return InvalidParameter;
204
205     *offset = pen->offset;
206
207     return Ok;
208 }
209
210 GpStatus WINGDIPAPI GdipGetPenDashStyle(GpPen *pen, GpDashStyle *dash)
211 {
212     if(!pen || !dash)
213         return InvalidParameter;
214
215     *dash = pen->dash;
216
217     return Ok;
218 }
219
220 GpStatus WINGDIPAPI GdipGetPenEndCap(GpPen *pen, GpLineCap *endCap)
221 {
222     if(!pen || !endCap)
223         return InvalidParameter;
224
225     *endCap = pen->endcap;
226
227     return Ok;
228 }
229
230 GpStatus WINGDIPAPI GdipGetPenLineJoin(GpPen *pen, GpLineJoin *lineJoin)
231 {
232     if(!pen || !lineJoin)
233         return InvalidParameter;
234
235     *lineJoin = pen->join;
236
237     return Ok;
238 }
239
240 GpStatus WINGDIPAPI GdipGetPenMode(GpPen *pen, GpPenAlignment *mode)
241 {
242     if(!pen || !mode)
243         return InvalidParameter;
244
245     *mode = pen->align;
246
247     return Ok;
248 }
249
250 GpStatus WINGDIPAPI GdipGetPenMiterLimit(GpPen *pen, REAL *miterLimit)
251 {
252     if(!pen || !miterLimit)
253         return InvalidParameter;
254
255     *miterLimit = pen->miterlimit;
256
257     return Ok;
258 }
259
260 GpStatus WINGDIPAPI GdipGetPenStartCap(GpPen *pen, GpLineCap *startCap)
261 {
262     if(!pen || !startCap)
263         return InvalidParameter;
264
265     *startCap = pen->startcap;
266
267     return Ok;
268 }
269
270 GpStatus WINGDIPAPI GdipGetPenUnit(GpPen *pen, GpUnit *unit)
271 {
272     if(!pen || !unit)
273         return InvalidParameter;
274
275     *unit = pen->unit;
276
277     return Ok;
278 }
279
280 GpStatus WINGDIPAPI GdipGetPenWidth(GpPen *pen, REAL *width)
281 {
282     if(!pen || !width)
283         return InvalidParameter;
284
285     *width = pen->width;
286
287     return Ok;
288 }
289
290 GpStatus WINGDIPAPI GdipSetPenBrushFill(GpPen *pen, GpBrush *brush)
291 {
292     if(!pen || !brush)
293         return InvalidParameter;
294
295     GdipDeleteBrush(pen->brush);
296     return GdipCloneBrush(brush, &pen->brush);
297 }
298
299 GpStatus WINGDIPAPI GdipSetPenColor(GpPen *pen, ARGB argb)
300 {
301     if(!pen)
302         return InvalidParameter;
303
304     if(pen->brush->bt != BrushTypeSolidColor)
305         return NotImplemented;
306
307     return GdipSetSolidFillColor(((GpSolidFill*)pen->brush), argb);
308 }
309
310 GpStatus WINGDIPAPI GdipSetPenCustomEndCap(GpPen *pen, GpCustomLineCap* customCap)
311 {
312     GpCustomLineCap * cap;
313     GpStatus ret;
314
315     if(!pen || !customCap) return InvalidParameter;
316
317     if((ret = GdipCloneCustomLineCap(customCap, &cap)) == Ok){
318         GdipDeleteCustomLineCap(pen->customend);
319         pen->endcap = LineCapCustom;
320         pen->customend = cap;
321     }
322
323     return ret;
324 }
325
326 GpStatus WINGDIPAPI GdipSetPenCustomStartCap(GpPen *pen, GpCustomLineCap* customCap)
327 {
328     GpCustomLineCap * cap;
329     GpStatus ret;
330
331     if(!pen || !customCap) return InvalidParameter;
332
333     if((ret = GdipCloneCustomLineCap(customCap, &cap)) == Ok){
334         GdipDeleteCustomLineCap(pen->customstart);
335         pen->startcap = LineCapCustom;
336         pen->customstart = cap;
337     }
338
339     return ret;
340 }
341
342 GpStatus WINGDIPAPI GdipSetPenDashArray(GpPen *pen, GDIPCONST REAL *dash,
343     INT count)
344 {
345     INT i;
346     REAL sum = 0;
347
348     if(!pen || !dash)
349         return InvalidParameter;
350
351     if(count <= 0)
352         return OutOfMemory;
353
354     for(i = 0; i < count; i++){
355         sum += dash[i];
356         if(dash[i] < 0.0)
357             return InvalidParameter;
358     }
359
360     if(sum == 0.0 && count)
361         return InvalidParameter;
362
363     GdipFree(pen->dashes);
364     pen->dashes = NULL;
365
366     if(count > 0)
367         pen->dashes = GdipAlloc(count * sizeof(REAL));
368     if(!pen->dashes){
369         pen->numdashes = 0;
370         return OutOfMemory;
371     }
372
373     GdipSetPenDashStyle(pen, DashStyleCustom);
374     memcpy(pen->dashes, dash, count * sizeof(REAL));
375     pen->numdashes = count;
376
377     return Ok;
378 }
379
380 GpStatus WINGDIPAPI GdipSetPenDashCap197819(GpPen *pen, GpDashCap dashCap)
381 {
382     if(!pen)
383         return InvalidParameter;
384
385     pen->dashcap = dashCap;
386
387     return Ok;
388 }
389
390 /* FIXME: dash offset not used */
391 GpStatus WINGDIPAPI GdipSetPenDashOffset(GpPen *pen, REAL offset)
392 {
393     if(!pen)
394         return InvalidParameter;
395
396     pen->offset = offset;
397
398     return Ok;
399 }
400
401 GpStatus WINGDIPAPI GdipSetPenDashStyle(GpPen *pen, GpDashStyle dash)
402 {
403     if(!pen)
404         return InvalidParameter;
405
406     if(dash != DashStyleCustom){
407         GdipFree(pen->dashes);
408         pen->dashes = NULL;
409         pen->numdashes = 0;
410     }
411
412     pen->dash = dash;
413     pen->style &= ~(PS_ALTERNATE | PS_SOLID | PS_DASH | PS_DOT | PS_DASHDOT |
414                     PS_DASHDOTDOT | PS_NULL | PS_USERSTYLE | PS_INSIDEFRAME);
415     pen->style |= gdip_to_gdi_dash(dash);
416
417     return Ok;
418 }
419
420 GpStatus WINGDIPAPI GdipSetPenEndCap(GpPen *pen, GpLineCap cap)
421 {
422     if(!pen)    return InvalidParameter;
423
424     /* The old custom cap gets deleted even if the new style is LineCapCustom. */
425     GdipDeleteCustomLineCap(pen->customend);
426     pen->customend = NULL;
427     pen->endcap = cap;
428
429     return Ok;
430 }
431
432 /* FIXME: startcap, dashcap not used. */
433 GpStatus WINGDIPAPI GdipSetPenLineCap197819(GpPen *pen, GpLineCap start,
434     GpLineCap end, GpDashCap dash)
435 {
436     if(!pen)
437         return InvalidParameter;
438
439     GdipDeleteCustomLineCap(pen->customend);
440     GdipDeleteCustomLineCap(pen->customstart);
441     pen->customend = NULL;
442     pen->customstart = NULL;
443
444     pen->startcap = start;
445     pen->endcap = end;
446     pen->dashcap = dash;
447
448     return Ok;
449 }
450
451 /* FIXME: Miter line joins behave a bit differently than they do in windows.
452  * Both kinds of miter joins clip if the angle is less than 11 degrees. */
453 GpStatus WINGDIPAPI GdipSetPenLineJoin(GpPen *pen, GpLineJoin join)
454 {
455     if(!pen)    return InvalidParameter;
456
457     pen->join = join;
458     pen->style &= ~(PS_JOIN_ROUND | PS_JOIN_BEVEL | PS_JOIN_MITER);
459     pen->style |= gdip_to_gdi_join(join);
460
461     return Ok;
462 }
463
464 GpStatus WINGDIPAPI GdipSetPenMiterLimit(GpPen *pen, REAL limit)
465 {
466     if(!pen)
467         return InvalidParameter;
468
469     pen->miterlimit = limit;
470
471     return Ok;
472 }
473
474 GpStatus WINGDIPAPI GdipSetPenStartCap(GpPen *pen, GpLineCap cap)
475 {
476     if(!pen)    return InvalidParameter;
477
478     GdipDeleteCustomLineCap(pen->customstart);
479     pen->customstart = NULL;
480     pen->startcap = cap;
481
482     return Ok;
483 }
484
485 GpStatus WINGDIPAPI GdipSetPenWidth(GpPen *pen, REAL width)
486 {
487     if(!pen)    return InvalidParameter;
488
489     pen->width = width;
490
491     return Ok;
492 }
493
494 GpStatus WINGDIPAPI GdipSetPenMode(GpPen *pen, GpPenAlignment mode)
495 {
496     if(!pen)    return InvalidParameter;
497
498     pen->align = mode;
499
500     return Ok;
501 }