user32/tests: Flush events before the key flags test.
[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 static GpPenType bt_to_pt(GpBrushType bt)
71 {
72     switch(bt){
73         case BrushTypeSolidColor:
74             return PenTypeSolidColor;
75         case BrushTypeHatchFill:
76             return PenTypeHatchFill;
77         case BrushTypeTextureFill:
78             return PenTypeTextureFill;
79         case BrushTypePathGradient:
80             return PenTypePathGradient;
81         case BrushTypeLinearGradient:
82             return PenTypeLinearGradient;
83         default:
84             return PenTypeUnknown;
85     }
86 }
87
88 GpStatus WINGDIPAPI GdipClonePen(GpPen *pen, GpPen **clonepen)
89 {
90     TRACE("(%p, %p)\n", pen, clonepen);
91
92     if(!pen || !clonepen)
93         return InvalidParameter;
94
95     *clonepen = GdipAlloc(sizeof(GpPen));
96     if(!*clonepen)  return OutOfMemory;
97
98     **clonepen = *pen;
99
100     GdipCloneCustomLineCap(pen->customstart, &(*clonepen)->customstart);
101     GdipCloneCustomLineCap(pen->customend, &(*clonepen)->customend);
102     GdipCloneBrush(pen->brush, &(*clonepen)->brush);
103
104     TRACE("<-- %p\n", *clonepen);
105
106     return Ok;
107 }
108
109 GpStatus WINGDIPAPI GdipCreatePen1(ARGB color, REAL width, GpUnit unit,
110     GpPen **pen)
111 {
112     GpBrush *brush;
113     GpStatus status;
114
115     TRACE("(%x, %.2f, %d, %p)\n", color, width, unit, pen);
116
117     GdipCreateSolidFill(color, (GpSolidFill **)(&brush));
118     status = GdipCreatePen2(brush, width, unit, pen);
119     GdipDeleteBrush(brush);
120     return status;
121 }
122
123 GpStatus WINGDIPAPI GdipCreatePen2(GpBrush *brush, REAL width, GpUnit unit,
124     GpPen **pen)
125 {
126     GpPen *gp_pen;
127     GpBrush *clone_brush;
128
129     TRACE("(%p, %.2f, %d, %p)\n", brush, width, unit, pen);
130
131     if(!pen || !brush)
132         return InvalidParameter;
133
134     gp_pen = GdipAlloc(sizeof(GpPen));
135     if(!gp_pen)    return OutOfMemory;
136
137     gp_pen->style = GP_DEFAULT_PENSTYLE;
138     gp_pen->width = width;
139     gp_pen->unit = unit;
140     gp_pen->endcap = LineCapFlat;
141     gp_pen->join = LineJoinMiter;
142     gp_pen->miterlimit = 10.0;
143     gp_pen->dash = DashStyleSolid;
144     gp_pen->offset = 0.0;
145     gp_pen->customstart = NULL;
146     gp_pen->customend = NULL;
147
148     if(!((gp_pen->unit == UnitWorld) || (gp_pen->unit == UnitPixel))) {
149         FIXME("UnitWorld, UnitPixel only supported units\n");
150         GdipFree(gp_pen);
151         return NotImplemented;
152     }
153
154     GdipCloneBrush(brush, &clone_brush);
155     gp_pen->brush = clone_brush;
156
157     *pen = gp_pen;
158
159     TRACE("<-- %p\n", *pen);
160
161     return Ok;
162 }
163
164 GpStatus WINGDIPAPI GdipDeletePen(GpPen *pen)
165 {
166     TRACE("(%p)\n", pen);
167
168     if(!pen)    return InvalidParameter;
169
170     GdipDeleteBrush(pen->brush);
171     GdipDeleteCustomLineCap(pen->customstart);
172     GdipDeleteCustomLineCap(pen->customend);
173     GdipFree(pen->dashes);
174     GdipFree(pen);
175
176     return Ok;
177 }
178
179 GpStatus WINGDIPAPI GdipGetPenBrushFill(GpPen *pen, GpBrush **brush)
180 {
181     TRACE("(%p, %p)\n", pen, brush);
182
183     if(!pen || !brush)
184         return InvalidParameter;
185
186     return GdipCloneBrush(pen->brush, brush);
187 }
188
189 GpStatus WINGDIPAPI GdipGetPenColor(GpPen *pen, ARGB *argb)
190 {
191     TRACE("(%p, %p)\n", pen, argb);
192
193     if(!pen || !argb)
194         return InvalidParameter;
195
196     if(pen->brush->bt != BrushTypeSolidColor)
197         return NotImplemented;
198
199     return GdipGetSolidFillColor(((GpSolidFill*)pen->brush), argb);
200 }
201
202 GpStatus WINGDIPAPI GdipGetPenCustomEndCap(GpPen *pen, GpCustomLineCap** customCap)
203 {
204     TRACE("(%p, %p)\n", pen, customCap);
205
206     if(!pen || !customCap)
207         return InvalidParameter;
208
209     if(!pen->customend){
210         *customCap = NULL;
211         return Ok;
212     }
213
214     return GdipCloneCustomLineCap(pen->customend, customCap);
215 }
216
217 GpStatus WINGDIPAPI GdipGetPenCustomStartCap(GpPen *pen, GpCustomLineCap** customCap)
218 {
219     TRACE("(%p, %p)\n", pen, customCap);
220
221     if(!pen || !customCap)
222         return InvalidParameter;
223
224     if(!pen->customstart){
225         *customCap = NULL;
226         return Ok;
227     }
228
229     return GdipCloneCustomLineCap(pen->customstart, customCap);
230 }
231
232 GpStatus WINGDIPAPI GdipGetPenDashArray(GpPen *pen, REAL *dash, INT count)
233 {
234     TRACE("(%p, %p, %d)\n", pen, dash, count);
235
236     if(!pen || !dash || count > pen->numdashes)
237         return InvalidParameter;
238
239     /* note: if you pass a negative value for count, it crashes native gdiplus. */
240     if(count < 0)
241         return GenericError;
242
243     memcpy(dash, pen->dashes, count * sizeof(REAL));
244
245     return Ok;
246 }
247
248 GpStatus WINGDIPAPI GdipGetPenDashCap197819(GpPen *pen, GpDashCap *dashCap)
249 {
250     TRACE("(%p, %p)\n", pen, dashCap);
251
252     if(!pen || !dashCap)
253         return InvalidParameter;
254
255     *dashCap = pen->dashcap;
256
257     return Ok;
258 }
259
260 GpStatus WINGDIPAPI GdipGetPenDashCount(GpPen *pen, INT *count)
261 {
262     TRACE("(%p, %p)\n", pen, count);
263
264     if(!pen || !count)
265         return InvalidParameter;
266
267     *count = pen->numdashes;
268
269     return Ok;
270 }
271
272 GpStatus WINGDIPAPI GdipGetPenDashOffset(GpPen *pen, REAL *offset)
273 {
274     TRACE("(%p, %p)\n", pen, offset);
275
276     if(!pen || !offset)
277         return InvalidParameter;
278
279     *offset = pen->offset;
280
281     return Ok;
282 }
283
284 GpStatus WINGDIPAPI GdipGetPenDashStyle(GpPen *pen, GpDashStyle *dash)
285 {
286     TRACE("(%p, %p)\n", pen, dash);
287
288     if(!pen || !dash)
289         return InvalidParameter;
290
291     *dash = pen->dash;
292
293     return Ok;
294 }
295
296 GpStatus WINGDIPAPI GdipGetPenEndCap(GpPen *pen, GpLineCap *endCap)
297 {
298     TRACE("(%p, %p)\n", pen, endCap);
299
300     if(!pen || !endCap)
301         return InvalidParameter;
302
303     *endCap = pen->endcap;
304
305     return Ok;
306 }
307
308 GpStatus WINGDIPAPI GdipGetPenFillType(GpPen *pen, GpPenType* type)
309 {
310     TRACE("(%p, %p)\n", pen, type);
311
312     if(!pen || !type)
313         return InvalidParameter;
314
315     *type = bt_to_pt(pen->brush->bt);
316
317     return Ok;
318 }
319
320 GpStatus WINGDIPAPI GdipGetPenLineJoin(GpPen *pen, GpLineJoin *lineJoin)
321 {
322     TRACE("(%p, %p)\n", pen, lineJoin);
323
324     if(!pen || !lineJoin)
325         return InvalidParameter;
326
327     *lineJoin = pen->join;
328
329     return Ok;
330 }
331
332 GpStatus WINGDIPAPI GdipGetPenMode(GpPen *pen, GpPenAlignment *mode)
333 {
334     TRACE("(%p, %p)\n", pen, mode);
335
336     if(!pen || !mode)
337         return InvalidParameter;
338
339     *mode = pen->align;
340
341     return Ok;
342 }
343
344 GpStatus WINGDIPAPI GdipGetPenMiterLimit(GpPen *pen, REAL *miterLimit)
345 {
346     TRACE("(%p, %p)\n", pen, miterLimit);
347
348     if(!pen || !miterLimit)
349         return InvalidParameter;
350
351     *miterLimit = pen->miterlimit;
352
353     return Ok;
354 }
355
356 GpStatus WINGDIPAPI GdipGetPenStartCap(GpPen *pen, GpLineCap *startCap)
357 {
358     TRACE("(%p, %p)\n", pen, startCap);
359
360     if(!pen || !startCap)
361         return InvalidParameter;
362
363     *startCap = pen->startcap;
364
365     return Ok;
366 }
367
368 GpStatus WINGDIPAPI GdipGetPenUnit(GpPen *pen, GpUnit *unit)
369 {
370     TRACE("(%p, %p)\n", pen, unit);
371
372     if(!pen || !unit)
373         return InvalidParameter;
374
375     *unit = pen->unit;
376
377     return Ok;
378 }
379
380 GpStatus WINGDIPAPI GdipGetPenWidth(GpPen *pen, REAL *width)
381 {
382     TRACE("(%p, %p)\n", pen, width);
383
384     if(!pen || !width)
385         return InvalidParameter;
386
387     *width = pen->width;
388
389     return Ok;
390 }
391
392 GpStatus WINGDIPAPI GdipResetPenTransform(GpPen *pen)
393 {
394     static int calls;
395
396     TRACE("(%p)\n", pen);
397
398     if(!pen)
399         return InvalidParameter;
400
401     if(!(calls++))
402         FIXME("(%p) stub\n", pen);
403
404     return NotImplemented;
405 }
406
407 GpStatus WINGDIPAPI GdipSetPenTransform(GpPen *pen, GpMatrix *matrix)
408 {
409     static int calls;
410
411     TRACE("(%p,%p)\n", pen, matrix);
412
413     if(!pen || !matrix)
414         return InvalidParameter;
415
416     if(!(calls++))
417         FIXME("not implemented\n");
418
419     return NotImplemented;
420 }
421
422 GpStatus WINGDIPAPI GdipGetPenTransform(GpPen *pen, GpMatrix *matrix)
423 {
424     static int calls;
425
426     TRACE("(%p,%p)\n", pen, matrix);
427
428     if(!pen || !matrix)
429         return InvalidParameter;
430
431     if(!(calls++))
432         FIXME("not implemented\n");
433
434     return NotImplemented;
435 }
436
437 GpStatus WINGDIPAPI GdipTranslatePenTransform(GpPen *pen, REAL dx, REAL dy, GpMatrixOrder order)
438 {
439     static int calls;
440
441     TRACE("(%p,%0.2f,%0.2f,%u)\n", pen, dx, dy, order);
442
443     if(!pen)
444         return InvalidParameter;
445
446     if(!(calls++))
447         FIXME("not implemented\n");
448
449     return NotImplemented;
450 }
451
452 GpStatus WINGDIPAPI GdipScalePenTransform(GpPen *pen, REAL sx, REAL sy, GpMatrixOrder order)
453 {
454     static int calls;
455
456     TRACE("(%p,%0.2f,%0.2f,%u)\n", pen, sx, sy, order);
457
458     if(!pen)
459         return InvalidParameter;
460
461     if(!(calls++))
462         FIXME("(%p, %.2f, %.2f, %d) stub\n", pen, sx, sy, order);
463
464     return NotImplemented;
465 }
466
467 GpStatus WINGDIPAPI GdipRotatePenTransform(GpPen *pen, REAL angle, GpMatrixOrder order)
468 {
469     static int calls;
470
471     TRACE("(%p,%0.2f,%u)\n", pen, angle, order);
472
473     if(!pen)
474         return InvalidParameter;
475
476     if(!(calls++))
477         FIXME("not implemented\n");
478
479     return NotImplemented;
480 }
481
482 GpStatus WINGDIPAPI GdipMultiplyPenTransform(GpPen *pen, GDIPCONST GpMatrix *matrix,
483     GpMatrixOrder order)
484 {
485     static int calls;
486
487     TRACE("(%p,%p,%u)\n", pen, matrix, order);
488
489     if(!pen)
490         return InvalidParameter;
491
492     if(!(calls++))
493         FIXME("not implemented\n");
494
495     return NotImplemented;
496 }
497
498 GpStatus WINGDIPAPI GdipSetPenBrushFill(GpPen *pen, GpBrush *brush)
499 {
500     TRACE("(%p, %p)\n", pen, brush);
501
502     if(!pen || !brush)
503         return InvalidParameter;
504
505     GdipDeleteBrush(pen->brush);
506     return GdipCloneBrush(brush, &pen->brush);
507 }
508
509 GpStatus WINGDIPAPI GdipSetPenColor(GpPen *pen, ARGB argb)
510 {
511     TRACE("(%p, %x)\n", pen, argb);
512
513     if(!pen)
514         return InvalidParameter;
515
516     if(pen->brush->bt != BrushTypeSolidColor)
517         return NotImplemented;
518
519     return GdipSetSolidFillColor(((GpSolidFill*)pen->brush), argb);
520 }
521
522 GpStatus WINGDIPAPI GdipGetPenCompoundCount(GpPen *pen, INT *count)
523 {
524     FIXME("(%p, %p): stub\n", pen, count);
525
526     if (!pen || !count)
527         return InvalidParameter;
528
529     return NotImplemented;
530 }
531
532 GpStatus WINGDIPAPI GdipSetPenCompoundArray(GpPen *pen, GDIPCONST REAL *dash,
533     INT count)
534 {
535     FIXME("(%p, %p, %i): stub\n", pen, dash, count);
536
537     if (!pen || !dash || count < 2 || count%2 == 1)
538         return InvalidParameter;
539
540     return NotImplemented;
541 }
542
543 GpStatus WINGDIPAPI GdipSetPenCustomEndCap(GpPen *pen, GpCustomLineCap* customCap)
544 {
545     GpCustomLineCap * cap;
546     GpStatus ret;
547
548     TRACE("(%p, %p)\n", pen, customCap);
549
550     /* native crashes on pen == NULL, customCap != NULL */
551     if(!customCap) return InvalidParameter;
552
553     if((ret = GdipCloneCustomLineCap(customCap, &cap)) == Ok){
554         GdipDeleteCustomLineCap(pen->customend);
555         pen->endcap = LineCapCustom;
556         pen->customend = cap;
557     }
558
559     return ret;
560 }
561
562 GpStatus WINGDIPAPI GdipSetPenCustomStartCap(GpPen *pen, GpCustomLineCap* customCap)
563 {
564     GpCustomLineCap * cap;
565     GpStatus ret;
566
567     TRACE("(%p, %p)\n", pen, customCap);
568
569     /* native crashes on pen == NULL, customCap != NULL */
570     if(!customCap) return InvalidParameter;
571
572     if((ret = GdipCloneCustomLineCap(customCap, &cap)) == Ok){
573         GdipDeleteCustomLineCap(pen->customstart);
574         pen->startcap = LineCapCustom;
575         pen->customstart = cap;
576     }
577
578     return ret;
579 }
580
581 GpStatus WINGDIPAPI GdipSetPenDashArray(GpPen *pen, GDIPCONST REAL *dash,
582     INT count)
583 {
584     INT i;
585     REAL sum = 0;
586
587     TRACE("(%p, %p, %d)\n", pen, dash, count);
588
589     if(!pen || !dash)
590         return InvalidParameter;
591
592     if(count <= 0)
593         return OutOfMemory;
594
595     for(i = 0; i < count; i++){
596         sum += dash[i];
597         if(dash[i] < 0.0)
598             return InvalidParameter;
599     }
600
601     if(sum == 0.0 && count)
602         return InvalidParameter;
603
604     GdipFree(pen->dashes);
605     pen->dashes = NULL;
606
607     if(count > 0)
608         pen->dashes = GdipAlloc(count * sizeof(REAL));
609     if(!pen->dashes){
610         pen->numdashes = 0;
611         return OutOfMemory;
612     }
613
614     GdipSetPenDashStyle(pen, DashStyleCustom);
615     memcpy(pen->dashes, dash, count * sizeof(REAL));
616     pen->numdashes = count;
617
618     return Ok;
619 }
620
621 GpStatus WINGDIPAPI GdipSetPenDashCap197819(GpPen *pen, GpDashCap dashCap)
622 {
623     TRACE("(%p, %d)\n", pen, dashCap);
624
625     if(!pen)
626         return InvalidParameter;
627
628     pen->dashcap = dashCap;
629
630     return Ok;
631 }
632
633 /* FIXME: dash offset not used */
634 GpStatus WINGDIPAPI GdipSetPenDashOffset(GpPen *pen, REAL offset)
635 {
636     TRACE("(%p, %.2f)\n", pen, offset);
637
638     if(!pen)
639         return InvalidParameter;
640
641     pen->offset = offset;
642
643     return Ok;
644 }
645
646 GpStatus WINGDIPAPI GdipSetPenDashStyle(GpPen *pen, GpDashStyle dash)
647 {
648     TRACE("(%p, %d)\n", pen, dash);
649
650     if(!pen)
651         return InvalidParameter;
652
653     if(dash != DashStyleCustom){
654         GdipFree(pen->dashes);
655         pen->dashes = NULL;
656         pen->numdashes = 0;
657     }
658
659     pen->dash = dash;
660     pen->style &= ~(PS_ALTERNATE | PS_SOLID | PS_DASH | PS_DOT | PS_DASHDOT |
661                     PS_DASHDOTDOT | PS_NULL | PS_USERSTYLE | PS_INSIDEFRAME);
662     pen->style |= gdip_to_gdi_dash(dash);
663
664     return Ok;
665 }
666
667 GpStatus WINGDIPAPI GdipSetPenEndCap(GpPen *pen, GpLineCap cap)
668 {
669     TRACE("(%p, %d)\n", pen, cap);
670
671     if(!pen)    return InvalidParameter;
672
673     /* The old custom cap gets deleted even if the new style is LineCapCustom. */
674     GdipDeleteCustomLineCap(pen->customend);
675     pen->customend = NULL;
676     pen->endcap = cap;
677
678     return Ok;
679 }
680
681 /* FIXME: startcap, dashcap not used. */
682 GpStatus WINGDIPAPI GdipSetPenLineCap197819(GpPen *pen, GpLineCap start,
683     GpLineCap end, GpDashCap dash)
684 {
685     TRACE("%p, %d, %d, %d)\n", pen, start, end, dash);
686
687     if(!pen)
688         return InvalidParameter;
689
690     GdipDeleteCustomLineCap(pen->customend);
691     GdipDeleteCustomLineCap(pen->customstart);
692     pen->customend = NULL;
693     pen->customstart = NULL;
694
695     pen->startcap = start;
696     pen->endcap = end;
697     pen->dashcap = dash;
698
699     return Ok;
700 }
701
702 /* FIXME: Miter line joins behave a bit differently than they do in windows.
703  * Both kinds of miter joins clip if the angle is less than 11 degrees. */
704 GpStatus WINGDIPAPI GdipSetPenLineJoin(GpPen *pen, GpLineJoin join)
705 {
706     TRACE("(%p, %d)\n", pen, join);
707
708     if(!pen)    return InvalidParameter;
709
710     pen->join = join;
711     pen->style &= ~(PS_JOIN_ROUND | PS_JOIN_BEVEL | PS_JOIN_MITER);
712     pen->style |= gdip_to_gdi_join(join);
713
714     return Ok;
715 }
716
717 GpStatus WINGDIPAPI GdipSetPenMiterLimit(GpPen *pen, REAL limit)
718 {
719     TRACE("(%p, %.2f)\n", pen, limit);
720
721     if(!pen)
722         return InvalidParameter;
723
724     pen->miterlimit = limit;
725
726     return Ok;
727 }
728
729 GpStatus WINGDIPAPI GdipSetPenStartCap(GpPen *pen, GpLineCap cap)
730 {
731     TRACE("(%p, %d)\n", pen, cap);
732
733     if(!pen)    return InvalidParameter;
734
735     GdipDeleteCustomLineCap(pen->customstart);
736     pen->customstart = NULL;
737     pen->startcap = cap;
738
739     return Ok;
740 }
741
742 GpStatus WINGDIPAPI GdipSetPenWidth(GpPen *pen, REAL width)
743 {
744     TRACE("(%p, %.2f)\n", pen, width);
745
746     if(!pen)    return InvalidParameter;
747
748     pen->width = width;
749
750     return Ok;
751 }
752
753 GpStatus WINGDIPAPI GdipSetPenMode(GpPen *pen, GpPenAlignment mode)
754 {
755     TRACE("(%p, %d)\n", pen, mode);
756
757     if(!pen)    return InvalidParameter;
758
759     pen->align = mode;
760
761     return Ok;
762 }