(GdipDrawRectangle): Add call to gdip_pen_setup so
[mono.git] / mcs / class / System.Drawing / gdiplus / graphics.c
1 /*
2  * graphics.c
3  *
4  * Copyright (c) 2003 Alexandre Pigolkine
5  * 
6  * Permission is hereby granted, free of charge, to any person obtaining a copy of this software
7  * and associated documentation files (the "Software"), to deal in the Software without restriction,
8  * including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
9  * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
10  * subject to the following conditions:
11  * 
12  * The above copyright notice and this permission notice shall be included in all copies or substantial
13  * portions of the Software.
14  * 
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
16  * NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
17  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
18  * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
19  * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20  * 
21  * Authors:
22  *   Alexandre Pigolkine(pigolkine@gmx.de)
23  *   Duncan Mak (duncan@ximian.com)
24  *
25  */
26
27 #include "gdip_main.h"
28 #include "gdip_win32.h"
29 #include <math.h>
30
31 void
32 gdip_graphics_init (GpGraphics *graphics)
33 {
34         graphics->ct = cairo_create ();
35         graphics->copy_of_ctm = cairo_matrix_create ();
36         cairo_matrix_set_identity (graphics->copy_of_ctm);
37         graphics->hdc = 0;
38         graphics->hdc_busy_count = 0;
39         graphics->image = 0;
40         graphics->type = gtUndefined;
41         cairo_select_font (graphics->ct, "serif:12");
42         /* cairo_select_font (graphics->ct, "serif:12", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL); */
43 }
44
45 GpGraphics *
46 gdip_graphics_new ()
47 {
48         GpGraphics *result = (GpGraphics *) GdipAlloc (sizeof (GpGraphics));
49         gdip_graphics_init (result);
50         return result;
51 }
52
53 void
54 gdip_graphics_attach_bitmap (GpGraphics *graphics, GpBitmap *image)
55 {
56         cairo_set_target_image (graphics->ct, image->data.Scan0, image->cairo_format,
57                                 image->data.Width, image->data.Height, image->data.Stride);
58         graphics->image = image;
59         graphics->type = gtMemoryBitmap;
60 }
61
62 void 
63 gdip_graphics_detach_bitmap (GpGraphics *graphics, GpBitmap *image)
64 {
65         printf ("Implement graphics_detach_bitmap");
66         /* FIXME: implement me */
67 }
68
69 #define C1 0.552
70
71 static void 
72 make_ellipse (GpGraphics *graphics, float x, float y, float width, float height)
73 {
74         double rx = width / 2;
75         double ry = height / 2;
76         double cx = x + rx;
77         double cy = y + ry;
78
79         cairo_move_to (graphics->ct, cx + rx, cy);
80
81         /* an approximate of the ellipse by drawing a curve in each
82          * quartrant */
83         cairo_curve_to (graphics->ct,
84                         cx + rx, cy - C1 * ry,
85                         cx + C1 * rx, cy - ry,
86                         cx, cy - ry);
87         cairo_curve_to (graphics->ct,
88                         cx - C1 * rx, cy - ry,
89                         cx - rx, cy - C1 * ry,
90                         cx - rx, cy);
91         cairo_curve_to (graphics->ct,
92                         cx - rx, cy + C1 * ry,
93                         cx - C1 * rx, cy + ry,
94                         cx, cy + ry);
95         cairo_curve_to (graphics->ct,
96                         cx + C1 * rx, cy + ry,
97                         cx + rx, cy + C1 * ry,
98                         cx + rx, cy);
99
100         cairo_close_path (graphics->ct);        
101 }
102
103 static void
104 make_polygon (GpGraphics *graphics, GpPointF *points, int count)
105 {
106         int i;
107         cairo_move_to (graphics->ct, points [0].X, points [0].Y);
108
109         for (i = 0; i < count; i++)
110                 cairo_line_to (graphics->ct, points [i].X, points [i].Y);
111
112         /*
113          * Draw a line from the last point back to the first point if
114          * they're not the same
115          */
116         if (points [0].X != points [count].X && points [0].Y != points [count].Y)
117                 cairo_line_to (graphics->ct, points [0].X, points [0].Y);
118
119         cairo_close_path (graphics->ct);
120 }
121
122 static void
123 make_polygon_from_integers (
124         GpGraphics *graphics, GpPoint *points, int count)
125 {
126         int i;
127         cairo_move_to (graphics->ct, points [0].X, points [0].Y);
128
129         for (i = 0; i < count; i++)
130                 cairo_line_to (graphics->ct, points [i].X, points [i].Y);
131
132         /*
133          * Draw a line from the last point back to the first point if
134          * they're not the same
135          */
136         if (points [0].X != points [count].X && points [0].Y != points [count].Y)
137                 cairo_line_to (graphics->ct, points [0].X, points [0].Y);
138
139         cairo_close_path (graphics->ct);
140 }
141
142 static void
143 make_pie (GpGraphics *graphics, float x, float y, float width,
144                 float height, float startAngle, float sweepAngle)
145 {
146         float ax, ay;           /* the first intersection */
147         float bx, by;           /* the second intersection */
148         float cx, cy;           /* center of the bounding rect */
149         float f1, f2;           /* x coord. of foci */
150         float c;                /* distance between center and focus */
151         
152         /*
153          * we'll assume that we're working on a circle 
154          * and transform back into an ellipse after the calculation
155          */
156         float radius = width / 2;
157         float scale = height / width;
158
159         ax = radius * cos (startAngle);
160         ay = radius * sin (startAngle) * scale; 
161         
162         bx = radius * cos (startAngle + sweepAngle);        
163         by = radius * sin (startAngle + sweepAngle) * scale;
164         
165         cx = x + (width / 2);
166         cy = y + (height / 2);
167
168         c = sqrt (-cy * cy + cx * cx);
169         f1 = cx - c;
170         f2 = cx + c;        
171
172         cairo_move_to (graphics->ct, cx, cy);
173         cairo_line_to (graphics->ct, ax, ay);
174
175         cairo_curve_to (graphics->ct,
176                         cx, f1, cx, f2, bx, by);
177
178         cairo_line_to (graphics->ct, cx, cy);
179
180         cairo_close_path (graphics->ct);
181 }
182
183 static cairo_fill_rule_t
184 convert_fill_mode (GpFillMode fill_mode)
185 {
186         if (fill_mode == FillModeAlternate) 
187                 return CAIRO_FILL_RULE_EVEN_ODD;
188         else
189                 return CAIRO_FILL_RULE_WINDING;
190 }
191
192
193 GpStatus 
194 GdipCreateFromHDC (int hDC, GpGraphics **graphics)
195 {
196         DC* dc = _get_DC_by_HDC (hDC);
197         
198         /* printf ("GdipCreateFromHDC. in %d, DC %p\n", hDC, dc); */
199         if (dc == 0) return NotImplemented;
200         
201         *graphics = gdip_graphics_new ();
202         cairo_set_target_drawable ((*graphics)->ct, GDIP_display, dc->physDev->drawable);
203         _release_hdc (hDC);
204         (*graphics)->hdc = (void*)hDC;
205         (*graphics)->type = gtX11Drawable;
206         /* printf ("GdipCreateFromHDC. graphics %p, ct %p\n", (*graphics), (*graphics)->ct); */
207         return Ok;
208 }
209
210 GpStatus 
211 GdipDeleteGraphics (GpGraphics *graphics)
212 {
213         /* FIXME: attention to surface (image, etc.) */
214         /* printf ("GdipDeleteGraphics. graphics %p\n", graphics); */
215         cairo_matrix_destroy (graphics->copy_of_ctm);
216         cairo_destroy (graphics->ct);
217         GdipFree (graphics);
218         return Ok;
219 }
220
221 GpStatus 
222 GdipGetDC (GpGraphics *graphics, int *hDC)
223 {
224         if (graphics->hdc == 0) {
225                 if (graphics->image != 0) {
226                         /* Create DC */
227                         graphics->hdc = gdip_image_create_Win32_HDC (graphics->image);
228                         if (graphics->hdc != 0) {
229                                 ++graphics->hdc_busy_count;
230                         }
231                 }
232         }
233         *hDC = (int)graphics->hdc;
234         return Ok;
235 }
236
237 GpStatus 
238 GdipReleaseDC (GpGraphics *graphics, int hDC)
239 {
240         if (graphics->hdc != (void *)hDC) return InvalidParameter;
241         if (graphics->hdc_busy_count > 0) {
242                 --graphics->hdc_busy_count;
243                 if (graphics->hdc_busy_count == 0) {
244                         /* Destroy DC */
245                         gdip_image_destroy_Win32_HDC (graphics->image, (void*)hDC);
246                         graphics->hdc = 0;
247                 }
248         }
249         return Ok;
250 }
251
252 #define MAX_GRAPHICS_STATE_STACK 100
253
254 GpState saved_stack [MAX_GRAPHICS_STATE_STACK];
255 int current_stack_pos = 0;
256
257 GpStatus 
258 GdipRestoreGraphics (GpGraphics *graphics, unsigned int graphicsState)
259 {
260         if (graphicsState < MAX_GRAPHICS_STATE_STACK) {
261                 cairo_matrix_copy (graphics->copy_of_ctm, saved_stack[graphicsState].matrix);
262                 cairo_set_matrix (graphics->ct, graphics->copy_of_ctm);
263         }
264         else {
265                 return InvalidParameter;
266         }
267         return Ok;
268 }
269
270 GpStatus 
271 GdipSaveGraphics(GpGraphics *graphics, unsigned int *state)
272 {
273         if (current_stack_pos < MAX_GRAPHICS_STATE_STACK) {
274                 saved_stack[current_stack_pos].matrix = cairo_matrix_create ();
275                 cairo_matrix_copy (saved_stack[current_stack_pos].matrix, graphics->copy_of_ctm);
276                 *state = current_stack_pos;
277                 ++current_stack_pos;
278         }
279         else {
280                 return OutOfMemory;
281         }
282         return Ok;
283 }
284
285 #define PI 3.14159265358979323846
286 #define GRADTORAD PI / 180.0
287
288 GpStatus 
289 GdipRotateWorldTransform (GpGraphics *graphics, float angle, int order)
290 {
291         cairo_matrix_t *mtx = cairo_matrix_create ();
292         cairo_matrix_rotate (mtx, angle * GRADTORAD);
293         cairo_matrix_multiply (graphics->copy_of_ctm, mtx, graphics->copy_of_ctm );
294         cairo_matrix_destroy ( mtx);
295         cairo_set_matrix (graphics->ct, graphics->copy_of_ctm);
296         return Ok;
297 }
298
299 GpStatus 
300 GdipTranslateWorldTransform (GpGraphics *graphics, float dx, float dy, int order)
301 {
302         /* FIXME: consider order here */
303         cairo_matrix_translate (graphics->copy_of_ctm, dx, dy);
304         cairo_set_matrix (graphics->ct, graphics->copy_of_ctm);
305         return Ok;
306 }
307
308 /* XXX: TODO */
309 GpStatus
310 GdipDrawArc (GpGraphics *graphics, GpPen *pen, 
311                 float x, float y, float width, float height, 
312                 float startAngle, float sweepAngle)
313 {
314         gdip_pen_setup (graphics, pen);
315
316         return NotImplemented;
317 }
318
319 GpStatus
320 GdipDrawArcI (GpGraphics *graphics, GpPen *pen, 
321                 int x, int y, int width, int height, 
322                 int startAngle, int sweepAngle)
323 {
324         gdip_pen_setup (graphics, pen);
325
326         return NotImplemented;
327 }
328
329 GpStatus 
330 GdipDrawBezier (GpGraphics *graphics, GpPen *pen, 
331                 float x1, float y1, float x2, float y2,
332                 float x3, float y3, float x4, float y4)
333 {
334         gdip_pen_setup (graphics, pen);        
335         cairo_move_to (graphics->ct, x1, y1);
336         cairo_curve_to (graphics->ct, x2, y2, x3, y3, x4, y4);
337         cairo_stroke (graphics->ct);
338
339         return gdip_get_status (graphics->ct);
340 }
341
342 GpStatus GdipDrawBezierI (GpGraphics *graphics, GpPen *pen, 
343                 int x1, int y1, int x2, int y2,
344                 int x3, int y3, int x4, int y4)
345 {
346         return GdipDrawBezier (graphics, pen,
347                         x1, y1, x2, y2, x3, y3, x4, y4);
348 }
349
350 GpStatus 
351 GdipDrawBeziers (GpGraphics *graphics, GpPen *pen,
352                 GpPointF *points, int count)
353 {
354         int i, j, k;
355         
356         if (count == 0)
357                 return Ok;
358
359         gdip_pen_setup (graphics, pen);
360         cairo_move_to (graphics->ct, points [0].X, points [0].Y);
361
362         for (i = 0; i < count; i += 3) {
363                 j = i + 1;
364                 k = i + 2;
365                 cairo_curve_to (graphics->ct,
366                                 points [i].X, points [i].Y,
367                                 points [j].X, points [j].Y,
368                                 points [k].X, points [k].Y);
369         }
370
371         cairo_stroke (graphics->ct);
372
373         return gdip_get_status (graphics->ct);
374 }
375
376 GpStatus
377 GdipDrawBeziersI (GpGraphics *graphics, GpPen *pen,
378                 GpPoint *points, int count)
379 {
380         int i, j, k;
381         
382         if (count == 0)
383                 return Ok;
384
385         gdip_pen_setup (graphics, pen);
386         cairo_move_to (graphics->ct, points [0].X, points [0].Y);
387
388         for (i = 0; i < count; i += 3) {
389                 j = i + 1;
390                 k = i + 2;
391                 cairo_curve_to (graphics->ct,
392                                 points [i].X, points [i].Y,
393                                 points [j].X, points [j].Y,
394                                 points [k].X, points [k].Y);
395         }
396
397         cairo_stroke (graphics->ct);
398
399         return gdip_get_status (graphics->ct);
400 }
401
402 GpStatus 
403 GdipDrawEllipse (GpGraphics *graphics, GpPen *pen, 
404                 float x, float y, float width, float height)
405 {
406         gdip_pen_setup (graphics, pen);
407         make_ellipse (graphics, x, y, width, height);
408         cairo_stroke (graphics->ct);
409
410         return gdip_get_status (graphics->ct);
411 }
412
413 GpStatus
414 GdipDrawEllipseI (GpGraphics *graphics, GpPen *pen,
415                 int x, int y, int width, int height)
416 {
417         return GdipDrawEllipse (graphics, pen, x, y, width, height);
418 }
419
420 GpStatus
421 GdipDrawLine (GpGraphics *graphics, GpPen *pen,
422                 float x1, float y1, float x2, float y2)
423 {
424         gdip_pen_setup (graphics, pen);
425
426         cairo_move_to (graphics->ct, x1, y1);
427         cairo_line_to (graphics->ct, x2, y2);
428
429         cairo_stroke (graphics->ct);
430
431         return gdip_get_status (graphics->ct);
432 }
433
434 GpStatus 
435 GdipDrawLineI (GpGraphics *graphics, GpPen *pen, 
436                 int x1, int y1, int x2, int y2)
437 {
438         return GdipDrawLine (graphics, pen, x1, y1, x2, y2);
439 }
440
441 GpStatus 
442 GdipDrawLines (GpGraphics *graphics, GpPen *pen, GpPointF *points, int count)
443 {
444         GpStatus s;
445         int i, j;
446
447         for (i = 0; i < count - 1; i++) {
448                 j = i + 1;
449                 s = GdipDrawLine (graphics, pen, 
450                                 points [i].X, points [i].Y,
451                                 points [j].X, points [j].Y);
452
453                 if (s != Ok) return s;
454         }
455
456         return Ok;
457 }
458
459 GpStatus 
460 GdipDrawLinesI (GpGraphics *graphics, GpPen *pen,
461                 GpPoint *points, int count)
462 {
463         GpStatus s;
464         int i, j;
465
466         for (i = 0; i < count - 1; i++) {
467                 j = i + 1;
468                 s = GdipDrawLineI (graphics, pen, 
469                                 points [i].X, points [i].Y,
470                                 points [j].X, points [j].Y);
471
472                 if (s != Ok) return s;
473         }
474
475         return Ok;
476 }
477
478 GpStatus
479 GdipDrawPie (GpGraphics *graphics, GpPen *pen, float x, float y, 
480                 float width, float height, float startAngle, float sweepAngle)
481 {
482         gdip_pen_setup (graphics, pen);
483         make_pie (graphics, x, y, width, height, startAngle, sweepAngle);
484         cairo_stroke (graphics->ct);
485         cairo_close_path (graphics->ct);
486         
487         return gdip_get_status (graphics->ct);
488 }
489
490 GpStatus
491 GdipDrawPieI (GpGraphics *graphics, GpPen *pen, int x, int y, 
492                 int width, int height, float startAngle, float sweepAngle)
493 {
494         gdip_pen_setup (graphics, pen);
495         make_pie (graphics, x, y, width, height, startAngle, sweepAngle);
496         cairo_stroke (graphics->ct);
497         cairo_close_path (graphics->ct);
498         
499         return gdip_get_status (graphics->ct);
500 }
501
502 GpStatus
503 GdipDrawPolygon (GpGraphics *graphics, GpPen *pen, GpPointF *points, int count)
504 {
505         gdip_pen_setup (graphics, pen);
506         make_polygon (graphics, points, count);
507         cairo_stroke (graphics->ct);
508
509         return gdip_get_status (graphics->ct);
510 }
511
512 GpStatus
513 GdipDrawPolygonI (GpGraphics *graphics, GpPen *pen, GpPoint *points, int count)
514 {
515         gdip_pen_setup (graphics, pen);
516         make_polygon_from_integers (graphics, points, count);
517         cairo_stroke (graphics->ct);
518
519         return gdip_get_status (graphics->ct);
520 }
521
522 GpStatus
523 GdipDrawRectangle (GpGraphics *graphics, GpPen *pen,
524                 float x, float y, float width, float height)
525 {
526         gdip_pen_setup (graphics, pen);
527         cairo_rectangle (graphics->ct, x, y, width, height);
528         cairo_stroke (graphics->ct);
529
530         return gdip_get_status (graphics->ct);
531 }
532
533 GpStatus
534 GdipDrawRectangleI (GpGraphics *graphics, GpPen *pen,
535                 int x, int y, int width, int height)
536 {
537         return GdipDrawRectangle (graphics, pen, x, y, width, height);
538 }
539
540 GpStatus
541 GdipFillEllipse (GpGraphics *graphics, GpBrush *brush,
542                 float x, float y, float width, float height)
543 {
544         gdip_brush_setup (graphics, brush);
545         make_ellipse (graphics, x, y, width, height);
546         cairo_fill (graphics->ct);
547
548         return gdip_get_status (graphics->ct);
549 }
550
551 GpStatus
552 GdipFillEllipseI (GpGraphics *graphics, GpBrush *brush,
553                 int x, int y, int width, int height)
554 {
555         return GdipFillEllipse (graphics, brush, x, y, width, height);
556 }
557
558 GpStatus 
559 GdipFillRectangle (GpGraphics *graphics, GpBrush *brush, 
560                 float x, float y, float width, float height)
561 {
562         gdip_brush_setup (graphics, brush);
563         cairo_rectangle (graphics->ct, x, y, width, height);
564         cairo_fill (graphics->ct);
565         return gdip_get_status (graphics->ct);
566 }
567
568 GpStatus
569 GdipFillPolygon (GpGraphics *graphics, GpBrush *brush, 
570                 GpPointF *points, int count, GpFillMode fillMode)
571 {
572         gdip_brush_setup (graphics, brush);
573         make_polygon (graphics, points, count);
574
575         cairo_set_fill_rule (
576                 graphics->ct,
577                 convert_fill_mode (fillMode));
578
579         cairo_fill (graphics->ct);
580
581         return gdip_get_status (graphics->ct);
582 }
583
584 GpStatus
585 GdipFillPolygonI (GpGraphics *graphics, GpBrush *brush, 
586                 GpPoint *points, int count, GpFillMode fillMode)
587 {
588         gdip_brush_setup (graphics, brush);
589         make_polygon_from_integers (graphics, points, count);
590
591         cairo_set_fill_rule (
592                 graphics->ct,
593                 convert_fill_mode (fillMode));
594         
595         cairo_fill (graphics->ct);
596
597         return gdip_get_status (graphics->ct);
598 }
599
600 GpStatus
601 GdipFillPolygon2 (GpGraphics *graphics, GpBrush *brush, GpPointF *points, int count)
602 {
603         return GdipFillPolygon (graphics, brush, points, count, FillModeAlternate);
604 }
605
606 GpStatus
607 GdipFillPolygon2I (GpGraphics *graphics, GpBrush *brush, GpPoint *points, int count)
608 {
609         return GdipFillPolygonI (graphics, brush, points, count, FillModeAlternate);
610 }
611
612 GpStatus 
613 GdipDrawString (GpGraphics *graphics, const char *string,
614                 int len, void *font, RectF *rc, void *format, GpBrush *brush)
615 {
616         cairo_save (graphics->ct);
617         if (brush) {
618                 gdip_brush_setup (graphics, brush);
619         } else {
620                 cairo_set_rgb_color (graphics->ct, 0., 0., 0.);
621         }
622         cairo_move_to (graphics->ct, rc->left, rc->top + 12);
623         cairo_scale_font (graphics->ct, 12);
624         cairo_show_text (graphics->ct, string);
625         cairo_restore(graphics->ct);
626
627         return gdip_get_status (graphics->ct);
628 }
629
630 GpStatus 
631 GdipSetRenderingOrigin (GpGraphics *graphics, int x, int y)
632 {
633         cairo_move_to (graphics->ct, x, y);
634         cairo_close_path (graphics->ct);
635
636         return gdip_get_status (graphics->ct);
637 }
638
639 /*
640  * FIXME: cairo_current_point does not reflect changes made from
641  * cairo_move_to.
642  */
643 GpStatus 
644 GdipGetRenderingOrigin (GpGraphics *graphics, int *x, int *y)
645 {
646         double cx, cy;
647         cairo_current_point (graphics->ct, &cx, &cy);
648
649         *x = (int) cx;
650         *y = (int) cy;
651
652         return gdip_get_status (graphics->ct);
653 }
654