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