1 /*
2 / gaiagraphics_paint.c
3 /
4 / painting helpers
5 /
6 / version 1.0, 2010 October 19
7 /
8 / Author: Sandro Furieri a.furieri@lqt.it
9 /
10 / Copyright (C) 2009  Alessandro Furieri
11 /
12 /    This program is free software: you can redistribute it and/or modify
13 /    it under the terms of the GNU Lesser General Public License as published by
14 /    the Free Software Foundation, either version 3 of the License, or
15 /    (at your option) any later version.
16 /
17 /    This program is distributed in the hope that it will be useful,
18 /    but WITHOUT ANY WARRANTY; without even the implied warranty of
19 /    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20 /    GNU Lesser General Public License for more details.
21 /
22 /    You should have received a copy of the GNU Lesser General Public License
23 /    along with this program.  If not, see <http://www.gnu.org/licenses/>.
24 /
25 */
26 
27 #include <stdlib.h>
28 #include <stdio.h>
29 #include <string.h>
30 #include <math.h>
31 
32 #include "gaiagraphics.h"
33 #include "gaiagraphics_internals.h"
34 
35 GGRAPH_DECLARE int
gGraphCreateContext(int width,int height,const void ** context)36 gGraphCreateContext (int width, int height, const void **context)
37 {
38 /* creating a Graphics Context */
39     gGraphContextPtr ctx;
40 
41     *context = NULL;
42 
43     ctx = malloc (sizeof (gGraphContext));
44     if (!ctx)
45 	return GGRAPH_INSUFFICIENT_MEMORY;
46     ctx->signature = GG_GRAPHICS_CONTEXT_MAGIC_SIGNATURE;
47     ctx->surface =
48 	cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height);
49     if (cairo_surface_status (ctx->surface) == CAIRO_STATUS_SUCCESS)
50 	;
51     else
52 	goto error1;
53     ctx->cairo = cairo_create (ctx->surface);
54     if (cairo_status (ctx->cairo) == CAIRO_STATUS_NO_MEMORY)
55 	goto error2;
56 
57 /* setting up a default Black Pen */
58     ctx->current_pen.red = 0.0;
59     ctx->current_pen.green = 0.0;
60     ctx->current_pen.blue = 0.0;
61     ctx->current_pen.alpha = 1.0;
62     ctx->current_pen.width = 1.0;
63     ctx->current_pen.lengths[0] = 1.0;
64     ctx->current_pen.lengths_count = 1;
65 
66 /* setting up a default Black Brush */
67     ctx->current_brush.is_solid_color = 1;
68     ctx->current_brush.is_linear_gradient = 0;
69     ctx->current_brush.is_pattern = 0;
70     ctx->current_brush.red = 0.0;
71     ctx->current_brush.green = 0.0;
72     ctx->current_brush.blue = 0.0;
73     ctx->current_brush.alpha = 1.0;
74     ctx->current_brush.pattern = NULL;
75 
76 /* priming a transparent background */
77     cairo_rectangle (ctx->cairo, 0, 0, width, height);
78     cairo_set_source_rgba (ctx->cairo, 0.0, 0.0, 0.0, 0.0);
79     cairo_fill (ctx->cairo);
80 
81 /* setting up default Font options */
82     ctx->font_red = 0.0;
83     ctx->font_green = 0.0;
84     ctx->font_blue = 0.0;
85     ctx->font_alpha = 1.0;
86     ctx->is_font_outlined = 0;
87     ctx->font_outline_width = 0.0;
88 
89     *context = ctx;
90     return GGRAPH_OK;
91   error2:
92     cairo_destroy (ctx->cairo);
93     cairo_surface_destroy (ctx->surface);
94     return GGRAPH_ERROR;
95   error1:
96     cairo_surface_destroy (ctx->surface);
97     return GGRAPH_ERROR;
98 }
99 
100 GGRAPH_DECLARE int
gGraphDestroyContext(const void * context)101 gGraphDestroyContext (const void *context)
102 {
103 /* freeing a Graphics Context */
104     gGraphContextPtr ctx = (gGraphContextPtr) context;
105     if (!ctx)
106 	return GGRAPH_INVALID_PAINT_CONTEXT;
107     if (ctx->signature != GG_GRAPHICS_CONTEXT_MAGIC_SIGNATURE)
108 	return GGRAPH_INVALID_PAINT_CONTEXT;
109     cairo_destroy (ctx->cairo);
110     cairo_surface_destroy (ctx->surface);
111     free (ctx);
112     return GGRAPH_OK;
113 }
114 
115 GGRAPH_DECLARE int
gGraphCreateSvgContext(const char * path,int width,int height,const void ** context)116 gGraphCreateSvgContext (const char *path, int width, int height,
117 			const void **context)
118 {
119 /* creating an SVG Graphics Context */
120     gGraphContextPtr ctx;
121 
122     *context = NULL;
123 
124     ctx = malloc (sizeof (gGraphContext));
125     if (!ctx)
126 	return GGRAPH_INSUFFICIENT_MEMORY;
127     ctx->signature = GG_GRAPHICS_SVG_CONTEXT_MAGIC_SIGNATURE;
128 
129     ctx->surface =
130 	cairo_svg_surface_create (path, (double) width, (double) height);
131 
132     if (cairo_surface_status (ctx->surface) == CAIRO_STATUS_SUCCESS)
133 	;
134     else
135 	goto error1;
136     ctx->cairo = cairo_create (ctx->surface);
137     if (cairo_status (ctx->cairo) == CAIRO_STATUS_NO_MEMORY)
138 	goto error2;
139 
140 /* setting up a default Black Pen */
141     ctx->current_pen.red = 0.0;
142     ctx->current_pen.green = 0.0;
143     ctx->current_pen.blue = 0.0;
144     ctx->current_pen.alpha = 1.0;
145     ctx->current_pen.width = 1.0;
146     ctx->current_pen.lengths[0] = 1.0;
147     ctx->current_pen.lengths_count = 1;
148 
149 /* setting up a default Black Brush */
150     ctx->current_brush.is_solid_color = 1;
151     ctx->current_brush.is_linear_gradient = 0;
152     ctx->current_brush.is_pattern = 0;
153     ctx->current_brush.red = 0.0;
154     ctx->current_brush.green = 0.0;
155     ctx->current_brush.blue = 0.0;
156     ctx->current_brush.alpha = 1.0;
157     ctx->current_brush.pattern = NULL;
158 
159 /* priming a transparent background */
160     cairo_rectangle (ctx->cairo, 0, 0, width, height);
161     cairo_set_source_rgba (ctx->cairo, 0.0, 0.0, 0.0, 0.0);
162     cairo_fill (ctx->cairo);
163 
164 /* setting up default Font options */
165     ctx->font_red = 0.0;
166     ctx->font_green = 0.0;
167     ctx->font_blue = 0.0;
168     ctx->font_alpha = 1.0;
169     ctx->is_font_outlined = 0;
170     ctx->font_outline_width = 0.0;
171 
172     *context = ctx;
173     return GGRAPH_OK;
174   error2:
175     cairo_destroy (ctx->cairo);
176     cairo_surface_destroy (ctx->surface);
177     return GGRAPH_ERROR;
178   error1:
179     cairo_surface_destroy (ctx->surface);
180     return GGRAPH_ERROR;
181 }
182 
183 GGRAPH_DECLARE int
gGraphDestroySvgContext(const void * context)184 gGraphDestroySvgContext (const void *context)
185 {
186 /* freeing an SVG Graphics Context */
187     gGraphContextPtr ctx = (gGraphContextPtr) context;
188     if (!ctx)
189 	return GGRAPH_INVALID_PAINT_CONTEXT;
190     if (ctx->signature != GG_GRAPHICS_SVG_CONTEXT_MAGIC_SIGNATURE)
191 	return GGRAPH_INVALID_PAINT_CONTEXT;
192     cairo_surface_show_page (ctx->surface);
193     cairo_destroy (ctx->cairo);
194     cairo_surface_finish (ctx->surface);
195     cairo_surface_destroy (ctx->surface);
196     free (ctx);
197     return GGRAPH_OK;
198 }
199 
200 GGRAPH_DECLARE int
gGraphCreatePdfContext(const char * path,int page_width,int page_height,int width,int height,const void ** context)201 gGraphCreatePdfContext (const char *path, int page_width, int page_height,
202 			int width, int height, const void **context)
203 {
204 /* creating an PDF Graphics Context */
205     int base_x = (page_width - width) / 2;
206     int base_y = (page_height - height) / 2;
207     gGraphContextPtr ctx;
208 
209     *context = NULL;
210 
211     ctx = malloc (sizeof (gGraphContext));
212     if (!ctx)
213 	return GGRAPH_INSUFFICIENT_MEMORY;
214     ctx->signature = GG_GRAPHICS_PDF_CONTEXT_MAGIC_SIGNATURE;
215     ctx->surface =
216 	cairo_pdf_surface_create (path, (double) page_width,
217 				  (double) page_height);
218     if (cairo_surface_status (ctx->surface) == CAIRO_STATUS_SUCCESS)
219 	;
220     else
221 	goto error1;
222     ctx->cairo = cairo_create (ctx->surface);
223     if (cairo_status (ctx->cairo) == CAIRO_STATUS_NO_MEMORY)
224 	goto error2;
225 
226 /* setting up a default Black Pen */
227     ctx->current_pen.red = 0.0;
228     ctx->current_pen.green = 0.0;
229     ctx->current_pen.blue = 0.0;
230     ctx->current_pen.alpha = 1.0;
231     ctx->current_pen.width = 1.0;
232     ctx->current_pen.lengths[0] = 1.0;
233     ctx->current_pen.lengths_count = 1;
234 
235 /* setting up a default Black Brush */
236     ctx->current_brush.is_solid_color = 1;
237     ctx->current_brush.is_linear_gradient = 0;
238     ctx->current_brush.is_pattern = 0;
239     ctx->current_brush.red = 0.0;
240     ctx->current_brush.green = 0.0;
241     ctx->current_brush.blue = 0.0;
242     ctx->current_brush.alpha = 1.0;
243     ctx->current_brush.pattern = NULL;
244 
245 /* priming a transparent background */
246     cairo_rectangle (ctx->cairo, 0, 0, width, height);
247     cairo_set_source_rgba (ctx->cairo, 0.0, 0.0, 0.0, 0.0);
248     cairo_fill (ctx->cairo);
249 
250 /* setting up default Font options */
251     ctx->font_red = 0.0;
252     ctx->font_green = 0.0;
253     ctx->font_blue = 0.0;
254     ctx->font_alpha = 1.0;
255     ctx->is_font_outlined = 0;
256     ctx->font_outline_width = 0.0;
257 
258     cairo_translate (ctx->cairo, base_x, base_y);
259     *context = ctx;
260     return GGRAPH_OK;
261   error2:
262     cairo_destroy (ctx->cairo);
263     cairo_surface_destroy (ctx->surface);
264     return GGRAPH_ERROR;
265   error1:
266     cairo_surface_destroy (ctx->surface);
267     return GGRAPH_ERROR;
268 }
269 
270 GGRAPH_DECLARE int
gGraphDestroyPdfContext(const void * context)271 gGraphDestroyPdfContext (const void *context)
272 {
273 /* freeing an PDF Graphics Context */
274     gGraphContextPtr ctx = (gGraphContextPtr) context;
275     if (!ctx)
276 	return GGRAPH_INVALID_PAINT_CONTEXT;
277     if (ctx->signature != GG_GRAPHICS_PDF_CONTEXT_MAGIC_SIGNATURE)
278 	return GGRAPH_INVALID_PAINT_CONTEXT;
279     cairo_surface_show_page (ctx->surface);
280     cairo_destroy (ctx->cairo);
281     cairo_surface_finish (ctx->surface);
282     cairo_surface_destroy (ctx->surface);
283     free (ctx);
284     return GGRAPH_OK;
285 }
286 
287 GGRAPH_DECLARE int
gGraphSetPen(const void * context,unsigned char red,unsigned char green,unsigned char blue,unsigned char alpha,double width,int style)288 gGraphSetPen (const void *context, unsigned char red, unsigned char green,
289 	      unsigned char blue, unsigned char alpha, double width, int style)
290 {
291 /* creating a Color Pen */
292     double d_red = (double) red / 255.0;
293     double d_green = (double) green / 255.0;
294     double d_blue = (double) blue / 255.0;
295     double d_alpha = (double) alpha / 255.0;
296     gGraphContextPtr ctx = (gGraphContextPtr) context;
297     if (!ctx)
298 	return GGRAPH_INVALID_PAINT_CONTEXT;
299     if (ctx->signature == GG_GRAPHICS_CONTEXT_MAGIC_SIGNATURE ||
300 	ctx->signature == GG_GRAPHICS_SVG_CONTEXT_MAGIC_SIGNATURE ||
301 	ctx->signature == GG_GRAPHICS_PDF_CONTEXT_MAGIC_SIGNATURE)
302 	;
303     else
304 	return GGRAPH_INVALID_PAINT_CONTEXT;
305 
306     ctx->current_pen.width = width;
307     ctx->current_pen.red = d_red;
308     ctx->current_pen.green = d_green;
309     ctx->current_pen.blue = d_blue;
310     ctx->current_pen.alpha = d_alpha;
311     switch (style)
312       {
313       case GGRAPH_PENSTYLE_DOT:
314 	  ctx->current_pen.lengths[0] = 2;
315 	  ctx->current_pen.lengths[1] = 2;
316 	  ctx->current_pen.lengths_count = 2;
317 	  break;
318       case GGRAPH_PENSTYLE_LONG_DASH:
319 	  ctx->current_pen.lengths[0] = 16;
320 	  ctx->current_pen.lengths[1] = 8;
321 	  ctx->current_pen.lengths_count = 2;
322 	  break;
323       case GGRAPH_PENSTYLE_SHORT_DASH:
324 	  ctx->current_pen.lengths[0] = 8;
325 	  ctx->current_pen.lengths[1] = 4;
326 	  ctx->current_pen.lengths_count = 2;
327 	  break;
328       case GGRAPH_PENSTYLE_DOT_DASH:
329 	  ctx->current_pen.lengths[0] = 8;
330 	  ctx->current_pen.lengths[1] = 4;
331 	  ctx->current_pen.lengths[2] = 2;
332 	  ctx->current_pen.lengths[3] = 4;
333 	  ctx->current_pen.lengths_count = 4;
334 	  break;
335       default:
336 	  ctx->current_pen.lengths[0] = 1;
337 	  ctx->current_pen.lengths[1] = 0;
338 	  ctx->current_pen.lengths_count = 2;
339       };
340     return GGRAPH_OK;
341 }
342 
343 GGRAPH_DECLARE int
gGraphSetBrush(const void * context,unsigned char red,unsigned char green,unsigned char blue,unsigned char alpha)344 gGraphSetBrush (const void *context, unsigned char red, unsigned char green,
345 		unsigned char blue, unsigned char alpha)
346 {
347 /* setting up a Color Brush */
348     double d_red = (double) red / 255.0;
349     double d_green = (double) green / 255.0;
350     double d_blue = (double) blue / 255.0;
351     double d_alpha = (double) alpha / 255.0;
352     gGraphContextPtr ctx = (gGraphContextPtr) context;
353     if (!ctx)
354 	return GGRAPH_INVALID_PAINT_CONTEXT;
355     if (ctx->signature == GG_GRAPHICS_CONTEXT_MAGIC_SIGNATURE ||
356 	ctx->signature == GG_GRAPHICS_SVG_CONTEXT_MAGIC_SIGNATURE ||
357 	ctx->signature == GG_GRAPHICS_PDF_CONTEXT_MAGIC_SIGNATURE)
358 	;
359     else
360 	return GGRAPH_INVALID_PAINT_CONTEXT;
361 
362     ctx->current_brush.is_solid_color = 1;
363     ctx->current_brush.is_linear_gradient = 0;
364     ctx->current_brush.is_pattern = 0;
365     ctx->current_brush.red = d_red;
366     ctx->current_brush.green = d_green;
367     ctx->current_brush.blue = d_blue;
368     ctx->current_brush.alpha = d_alpha;
369     return GGRAPH_OK;
370 }
371 
372 GGRAPH_DECLARE int
gGraphSetLinearGradientBrush(const void * context,double x,double y,double width,double height,unsigned char red1,unsigned char green1,unsigned char blue1,unsigned char alpha1,unsigned char red2,unsigned char green2,unsigned char blue2,unsigned char alpha2)373 gGraphSetLinearGradientBrush (const void *context, double x, double y,
374 			      double width, double height, unsigned char red1,
375 			      unsigned char green1, unsigned char blue1,
376 			      unsigned char alpha1, unsigned char red2,
377 			      unsigned char green2, unsigned char blue2,
378 			      unsigned char alpha2)
379 {
380 /* setting up a Linear Gradient Brush */
381     double d_red = (double) red1 / 255.0;
382     double d_green = (double) green1 / 255.0;
383     double d_blue = (double) blue1 / 255.0;
384     double d_alpha = (double) alpha1 / 255.0;
385     gGraphContextPtr ctx = (gGraphContextPtr) context;
386     if (!ctx)
387 	return GGRAPH_INVALID_PAINT_CONTEXT;
388     if (ctx->signature == GG_GRAPHICS_CONTEXT_MAGIC_SIGNATURE ||
389 	ctx->signature == GG_GRAPHICS_SVG_CONTEXT_MAGIC_SIGNATURE ||
390 	ctx->signature == GG_GRAPHICS_PDF_CONTEXT_MAGIC_SIGNATURE)
391 	;
392     else
393 	return GGRAPH_INVALID_PAINT_CONTEXT;
394 
395     ctx->current_brush.is_solid_color = 0;
396     ctx->current_brush.is_linear_gradient = 1;
397     ctx->current_brush.is_pattern = 0;
398     ctx->current_brush.red = d_red;
399     ctx->current_brush.green = d_green;
400     ctx->current_brush.blue = d_blue;
401     ctx->current_brush.alpha = d_alpha;
402     ctx->current_brush.x0 = x;
403     ctx->current_brush.y0 = y;
404     ctx->current_brush.x1 = x + width;
405     ctx->current_brush.y1 = y + height;
406     d_red = (double) red2 / 255.0;
407     d_green = (double) green2 / 255.0;
408     d_blue = (double) blue2 / 255.0;
409     d_alpha = (double) alpha2 / 255.0;
410     ctx->current_brush.red2 = d_red;
411     ctx->current_brush.green2 = d_green;
412     ctx->current_brush.blue2 = d_blue;
413     ctx->current_brush.alpha2 = d_alpha;
414     return GGRAPH_OK;
415 }
416 
417 GGRAPH_DECLARE int
gGraphSetPatternBrush(const void * context,const void * brush)418 gGraphSetPatternBrush (const void *context, const void *brush)
419 {
420 /* setting up a Pattern Brush */
421     gGraphPatternBrushPtr pattern = (gGraphPatternBrushPtr) brush;
422     gGraphContextPtr ctx = (gGraphContextPtr) context;
423 
424     if (!ctx)
425 	return GGRAPH_INVALID_PAINT_CONTEXT;
426     if (ctx->signature == GG_GRAPHICS_CONTEXT_MAGIC_SIGNATURE ||
427 	ctx->signature == GG_GRAPHICS_SVG_CONTEXT_MAGIC_SIGNATURE ||
428 	ctx->signature == GG_GRAPHICS_PDF_CONTEXT_MAGIC_SIGNATURE)
429 	;
430     else
431 	return GGRAPH_INVALID_PAINT_CONTEXT;
432 
433     if (!pattern)
434 	return GGRAPH_INVALID_PAINT_BRUSH;
435     if (pattern->signature != GG_GRAPHICS_BRUSH_MAGIC_SIGNATURE)
436 	return GGRAPH_INVALID_PAINT_BRUSH;
437 
438     ctx->current_brush.is_solid_color = 0;
439     ctx->current_brush.is_linear_gradient = 0;
440     ctx->current_brush.is_pattern = 1;
441 
442     ctx->current_brush.pattern = pattern->pattern;
443     return GGRAPH_OK;
444 }
445 
446 static void
set_current_brush(gGraphContextPtr ctx)447 set_current_brush (gGraphContextPtr ctx)
448 {
449 /* setting up the current Brush */
450     if (ctx->current_brush.is_solid_color)
451       {
452 	  /* using a Solid Color Brush */
453 	  cairo_set_source_rgba (ctx->cairo, ctx->current_brush.red,
454 				 ctx->current_brush.green,
455 				 ctx->current_brush.blue,
456 				 ctx->current_brush.alpha);
457       }
458     else if (ctx->current_brush.is_linear_gradient)
459       {
460 	  /* using a Linear Gradient Brush */
461 	  cairo_pattern_t *pattern =
462 	      cairo_pattern_create_linear (ctx->current_brush.x0,
463 					   ctx->current_brush.y0,
464 					   ctx->current_brush.x1,
465 					   ctx->current_brush.y1);
466 	  cairo_pattern_add_color_stop_rgba (pattern, 0.0,
467 					     ctx->current_brush.red,
468 					     ctx->current_brush.green,
469 					     ctx->current_brush.blue,
470 					     ctx->current_brush.alpha);
471 	  cairo_pattern_add_color_stop_rgba (pattern, 1.0,
472 					     ctx->current_brush.red2,
473 					     ctx->current_brush.green2,
474 					     ctx->current_brush.blue2,
475 					     ctx->current_brush.alpha2);
476 	  cairo_set_source (ctx->cairo, pattern);
477 	  cairo_pattern_destroy (pattern);
478       }
479     else if (ctx->current_brush.is_pattern)
480       {
481 	  /* using a Pattern Brush */
482 	  cairo_set_source (ctx->cairo, ctx->current_brush.pattern);
483       }
484 }
485 
486 GGRAPH_DECLARE int
gGraphFillPath(const void * context,int preserve)487 gGraphFillPath (const void *context, int preserve)
488 {
489 /* Filling a path */
490     gGraphContextPtr ctx = (gGraphContextPtr) context;
491     if (!ctx)
492 	return GGRAPH_INVALID_PAINT_CONTEXT;
493     if (ctx->signature == GG_GRAPHICS_CONTEXT_MAGIC_SIGNATURE ||
494 	ctx->signature == GG_GRAPHICS_SVG_CONTEXT_MAGIC_SIGNATURE ||
495 	ctx->signature == GG_GRAPHICS_PDF_CONTEXT_MAGIC_SIGNATURE)
496 	;
497     else
498 	return GGRAPH_INVALID_PAINT_CONTEXT;
499 
500     set_current_brush (ctx);
501     if (preserve == GGRAPH_PRESERVE_PATH)
502 	cairo_fill_preserve (ctx->cairo);
503     else
504 	cairo_fill (ctx->cairo);
505     return GGRAPH_OK;
506 }
507 
508 static void
set_current_pen(gGraphContextPtr ctx)509 set_current_pen (gGraphContextPtr ctx)
510 {
511 /* setting up the current Pen */
512     cairo_set_line_width (ctx->cairo, ctx->current_pen.width);
513     cairo_set_source_rgba (ctx->cairo, ctx->current_pen.red,
514 			   ctx->current_pen.green, ctx->current_pen.blue,
515 			   ctx->current_pen.alpha);
516     cairo_set_line_cap (ctx->cairo, CAIRO_LINE_CAP_BUTT);
517     cairo_set_line_join (ctx->cairo, CAIRO_LINE_JOIN_MITER);
518     cairo_set_dash (ctx->cairo, ctx->current_pen.lengths,
519 		    ctx->current_pen.lengths_count, 0.0);
520 }
521 
522 GGRAPH_DECLARE int
gGraphStrokePath(const void * context,int preserve)523 gGraphStrokePath (const void *context, int preserve)
524 {
525 /* Stroking a path */
526     gGraphContextPtr ctx = (gGraphContextPtr) context;
527     if (!ctx)
528 	return GGRAPH_INVALID_PAINT_CONTEXT;
529     if (ctx->signature == GG_GRAPHICS_CONTEXT_MAGIC_SIGNATURE ||
530 	ctx->signature == GG_GRAPHICS_SVG_CONTEXT_MAGIC_SIGNATURE ||
531 	ctx->signature == GG_GRAPHICS_PDF_CONTEXT_MAGIC_SIGNATURE)
532 	;
533     else
534 	return GGRAPH_INVALID_PAINT_CONTEXT;
535 
536     set_current_pen (ctx);
537     if (preserve == GGRAPH_PRESERVE_PATH)
538 	cairo_stroke_preserve (ctx->cairo);
539     else
540 	cairo_stroke (ctx->cairo);
541     return GGRAPH_OK;
542 }
543 
544 GGRAPH_DECLARE int
gGraphMoveToPoint(const void * context,double x,double y)545 gGraphMoveToPoint (const void *context, double x, double y)
546 {
547 /* Moving to a Path Point */
548     gGraphContextPtr ctx = (gGraphContextPtr) context;
549     if (!ctx)
550 	return GGRAPH_INVALID_PAINT_CONTEXT;
551     if (ctx->signature == GG_GRAPHICS_CONTEXT_MAGIC_SIGNATURE ||
552 	ctx->signature == GG_GRAPHICS_SVG_CONTEXT_MAGIC_SIGNATURE ||
553 	ctx->signature == GG_GRAPHICS_PDF_CONTEXT_MAGIC_SIGNATURE)
554 	;
555     else
556 	return GGRAPH_INVALID_PAINT_CONTEXT;
557     cairo_move_to (ctx->cairo, x, y);
558     return GGRAPH_OK;
559 }
560 
561 GGRAPH_DECLARE int
gGraphAddLineToPath(const void * context,double x,double y)562 gGraphAddLineToPath (const void *context, double x, double y)
563 {
564 /* Adding a Lint to a Path */
565     gGraphContextPtr ctx = (gGraphContextPtr) context;
566     if (!ctx)
567 	return GGRAPH_INVALID_PAINT_CONTEXT;
568     if (ctx->signature == GG_GRAPHICS_CONTEXT_MAGIC_SIGNATURE ||
569 	ctx->signature == GG_GRAPHICS_SVG_CONTEXT_MAGIC_SIGNATURE ||
570 	ctx->signature == GG_GRAPHICS_PDF_CONTEXT_MAGIC_SIGNATURE)
571 	;
572     else
573 	return GGRAPH_INVALID_PAINT_CONTEXT;
574     cairo_line_to (ctx->cairo, x, y);
575     return GGRAPH_OK;
576 }
577 
578 GGRAPH_DECLARE int
gGraphCloseSubpath(const void * context)579 gGraphCloseSubpath (const void *context)
580 {
581 /* Closing a SubPath */
582     gGraphContextPtr ctx = (gGraphContextPtr) context;
583     if (!ctx)
584 	return GGRAPH_INVALID_PAINT_CONTEXT;
585     if (ctx->signature == GG_GRAPHICS_CONTEXT_MAGIC_SIGNATURE ||
586 	ctx->signature == GG_GRAPHICS_SVG_CONTEXT_MAGIC_SIGNATURE ||
587 	ctx->signature == GG_GRAPHICS_PDF_CONTEXT_MAGIC_SIGNATURE)
588 	;
589     else
590 	return GGRAPH_INVALID_PAINT_CONTEXT;
591     cairo_close_path (ctx->cairo);
592     return GGRAPH_OK;
593 }
594 
595 GGRAPH_DECLARE int
gGraphStrokeLine(const void * context,double x0,double y0,double x1,double y1)596 gGraphStrokeLine (const void *context, double x0, double y0, double x1,
597 		  double y1)
598 {
599 /* Stroking a line */
600     gGraphContextPtr ctx = (gGraphContextPtr) context;
601     if (!ctx)
602 	return GGRAPH_INVALID_PAINT_CONTEXT;
603     if (ctx->signature == GG_GRAPHICS_CONTEXT_MAGIC_SIGNATURE ||
604 	ctx->signature == GG_GRAPHICS_SVG_CONTEXT_MAGIC_SIGNATURE ||
605 	ctx->signature == GG_GRAPHICS_PDF_CONTEXT_MAGIC_SIGNATURE)
606 	;
607     else
608 	return GGRAPH_INVALID_PAINT_CONTEXT;
609     cairo_move_to (ctx->cairo, x0, y0);
610     cairo_line_to (ctx->cairo, x1, y1);
611     set_current_pen (ctx);
612     cairo_stroke (ctx->cairo);
613     return GGRAPH_OK;
614 }
615 
616 GGRAPH_DECLARE int
gGraphDrawEllipse(const void * context,double x,double y,double width,double height)617 gGraphDrawEllipse (const void *context, double x, double y, double width,
618 		   double height)
619 {
620 /* Drawing a filled ellipse */
621     gGraphContextPtr ctx = (gGraphContextPtr) context;
622     if (!ctx)
623 	return GGRAPH_INVALID_PAINT_CONTEXT;
624     if (ctx->signature == GG_GRAPHICS_CONTEXT_MAGIC_SIGNATURE ||
625 	ctx->signature == GG_GRAPHICS_SVG_CONTEXT_MAGIC_SIGNATURE ||
626 	ctx->signature == GG_GRAPHICS_PDF_CONTEXT_MAGIC_SIGNATURE)
627 	;
628     else
629 	return GGRAPH_INVALID_PAINT_CONTEXT;
630     cairo_save (ctx->cairo);
631     cairo_translate (ctx->cairo, x + (width / 2.0), y + (height / 2.0));
632     cairo_scale (ctx->cairo, width / 2.0, height / 2.0);
633     cairo_arc (ctx->cairo, 0.0, 0.0, 1.0, 0.0, 2.0 * M_PI);
634     cairo_restore (ctx->cairo);
635     set_current_brush (ctx);
636     cairo_fill_preserve (ctx->cairo);
637     set_current_pen (ctx);
638     cairo_stroke (ctx->cairo);
639     return GGRAPH_OK;
640 }
641 
642 GGRAPH_DECLARE int
gGraphDrawCircleSector(const void * context,double center_x,double center_y,double radius,double from_angle,double to_angle)643 gGraphDrawCircleSector (const void *context, double center_x,
644 			double center_y, double radius, double from_angle,
645 			double to_angle)
646 {
647 /* drawing a filled circular sector */
648     gGraphContextPtr ctx = (gGraphContextPtr) context;
649     if (!ctx)
650 	return GGRAPH_INVALID_PAINT_CONTEXT;
651     if (ctx->signature == GG_GRAPHICS_CONTEXT_MAGIC_SIGNATURE ||
652 	ctx->signature == GG_GRAPHICS_SVG_CONTEXT_MAGIC_SIGNATURE ||
653 	ctx->signature == GG_GRAPHICS_PDF_CONTEXT_MAGIC_SIGNATURE)
654 	;
655     else
656 	return GGRAPH_INVALID_PAINT_CONTEXT;
657     cairo_move_to (ctx->cairo, center_x, center_y);
658     cairo_arc (ctx->cairo, center_x, center_y, radius, from_angle, to_angle);
659     cairo_line_to (ctx->cairo, center_x, center_y);
660     set_current_brush (ctx);
661     cairo_fill_preserve (ctx->cairo);
662     set_current_pen (ctx);
663     cairo_stroke (ctx->cairo);
664     return GGRAPH_OK;
665 }
666 
667 GGRAPH_DECLARE int
gGraphDrawRectangle(const void * context,double x,double y,double width,double height)668 gGraphDrawRectangle (const void *context, double x, double y, double width,
669 		     double height)
670 {
671 /* Drawing a filled rectangle */
672     gGraphContextPtr ctx = (gGraphContextPtr) context;
673     if (!ctx)
674 	return GGRAPH_INVALID_PAINT_CONTEXT;
675     if (ctx->signature == GG_GRAPHICS_CONTEXT_MAGIC_SIGNATURE ||
676 	ctx->signature == GG_GRAPHICS_SVG_CONTEXT_MAGIC_SIGNATURE ||
677 	ctx->signature == GG_GRAPHICS_PDF_CONTEXT_MAGIC_SIGNATURE)
678 	;
679     else
680 	return GGRAPH_INVALID_PAINT_CONTEXT;
681     cairo_rectangle (ctx->cairo, x, y, width, height);
682     set_current_brush (ctx);
683     cairo_fill_preserve (ctx->cairo);
684     set_current_pen (ctx);
685     cairo_stroke (ctx->cairo);
686     return GGRAPH_OK;
687 }
688 
689 GGRAPH_DECLARE int
gGraphDrawRoundedRectangle(const void * context,double x,double y,double width,double height,double radius)690 gGraphDrawRoundedRectangle (const void *context, double x, double y,
691 			    double width, double height, double radius)
692 {
693 /* Drawing a filled rectangle with rounded corners */
694     double degrees = M_PI / 180.0;
695     gGraphContextPtr ctx = (gGraphContextPtr) context;
696     if (!ctx)
697 	return GGRAPH_INVALID_PAINT_CONTEXT;
698     if (ctx->signature == GG_GRAPHICS_CONTEXT_MAGIC_SIGNATURE ||
699 	ctx->signature == GG_GRAPHICS_SVG_CONTEXT_MAGIC_SIGNATURE ||
700 	ctx->signature == GG_GRAPHICS_PDF_CONTEXT_MAGIC_SIGNATURE)
701 	;
702     else
703 	return GGRAPH_INVALID_PAINT_CONTEXT;
704     cairo_new_sub_path (ctx->cairo);
705     cairo_arc (ctx->cairo, x + width - radius, y + radius, radius,
706 	       -90 * degrees, 0 * degrees);
707     cairo_arc (ctx->cairo, x + width - radius, y + height - radius, radius,
708 	       0 * degrees, 90 * degrees);
709     cairo_arc (ctx->cairo, x + radius, y + height - radius, radius,
710 	       90 * degrees, 180 * degrees);
711     cairo_arc (ctx->cairo, x + radius, y + radius, radius, 180 * degrees,
712 	       270 * degrees);
713     cairo_close_path (ctx->cairo);
714     set_current_brush (ctx);
715     cairo_fill_preserve (ctx->cairo);
716     set_current_pen (ctx);
717     cairo_stroke (ctx->cairo);
718     return GGRAPH_OK;
719 }
720 
721 GGRAPH_DECLARE int
gGraphGetContextRgbArray(const void * context,unsigned char ** rgbArray)722 gGraphGetContextRgbArray (const void *context, unsigned char **rgbArray)
723 {
724 /* creating an RGB buffer from the given Context */
725     int width;
726     int height;
727     int x;
728     int y;
729     unsigned char *p_in;
730     unsigned char *p_out;
731     unsigned char *rgb;
732     int little_endian = gg_endian_arch ();
733     gGraphContextPtr ctx = (gGraphContextPtr) context;
734 
735     *rgbArray = NULL;
736     if (!ctx)
737 	return GGRAPH_INVALID_PAINT_CONTEXT;
738     if (ctx->signature != GG_GRAPHICS_CONTEXT_MAGIC_SIGNATURE)
739 	return GGRAPH_INVALID_PAINT_CONTEXT;
740 
741     width = cairo_image_surface_get_width (ctx->surface);
742     height = cairo_image_surface_get_height (ctx->surface);
743     rgb = malloc (width * height * 3);
744     if (!rgb)
745 	return GGRAPH_INSUFFICIENT_MEMORY;
746 
747     p_in = cairo_image_surface_get_data (ctx->surface);
748     p_out = rgb;
749     for (y = 0; y < height; y++)
750       {
751 	  for (x = 0; x < width; x++)
752 	    {
753 		unsigned char r;
754 		unsigned char g;
755 		unsigned char b;
756 		if (little_endian)
757 		  {
758 		      b = *p_in++;
759 		      g = *p_in++;
760 		      r = *p_in++;
761 		      p_in++;	/* skipping Alpha */
762 		  }
763 		else
764 		  {
765 		      p_in++;	/* skipping Alpha */
766 		      r = *p_in++;
767 		      g = *p_in++;
768 		      b = *p_in++;
769 		  }
770 		*p_out++ = r;
771 		*p_out++ = g;
772 		*p_out++ = b;
773 	    }
774       }
775     *rgbArray = rgb;
776     return GGRAPH_OK;
777 }
778 
779 GGRAPH_DECLARE int
gGraphGetContextAlphaArray(const void * context,unsigned char ** alphaArray)780 gGraphGetContextAlphaArray (const void *context, unsigned char **alphaArray)
781 {
782 /* creating an Alpha buffer from the given Context */
783     int width;
784     int height;
785     int x;
786     int y;
787     unsigned char *p_in;
788     unsigned char *p_out;
789     unsigned char *alpha;
790     gGraphContextPtr ctx = (gGraphContextPtr) context;
791 
792     *alphaArray = NULL;
793     if (!ctx)
794 	return GGRAPH_INVALID_PAINT_CONTEXT;
795     if (ctx->signature != GG_GRAPHICS_CONTEXT_MAGIC_SIGNATURE)
796 	return GGRAPH_INVALID_PAINT_CONTEXT;
797 
798     width = cairo_image_surface_get_width (ctx->surface);
799     height = cairo_image_surface_get_height (ctx->surface);
800     alpha = malloc (width * height);
801     if (!alpha)
802 	return GGRAPH_INSUFFICIENT_MEMORY;
803 
804     p_in = cairo_image_surface_get_data (ctx->surface);
805     p_out = alpha;
806     for (y = 0; y < height; y++)
807       {
808 	  for (x = 0; x < width; x++)
809 	    {
810 		p_in += 3;	/* skipping RGB */
811 		*p_out++ = *p_in++;
812 	    }
813       }
814     *alphaArray = alpha;
815     return GGRAPH_OK;
816 }
817 
818 GGRAPH_DECLARE int
gGraphDrawBitmap(const void * context,const void * bitmap,int x,int y)819 gGraphDrawBitmap (const void *context, const void *bitmap, int x, int y)
820 {
821 /* drawing a symbol bitmap */
822     gGraphBitmapPtr bmp = (gGraphBitmapPtr) bitmap;
823     gGraphContextPtr ctx = (gGraphContextPtr) context;
824 
825     if (!ctx)
826 	return GGRAPH_INVALID_PAINT_CONTEXT;
827     if (ctx->signature == GG_GRAPHICS_CONTEXT_MAGIC_SIGNATURE ||
828 	ctx->signature == GG_GRAPHICS_SVG_CONTEXT_MAGIC_SIGNATURE ||
829 	ctx->signature == GG_GRAPHICS_PDF_CONTEXT_MAGIC_SIGNATURE)
830 	;
831     else
832 	return GGRAPH_INVALID_PAINT_CONTEXT;
833 
834     if (!bmp)
835 	return GGRAPH_INVALID_PAINT_BITMAP;
836     if (bmp->signature != GG_GRAPHICS_BITMAP_MAGIC_SIGNATURE)
837 	return GGRAPH_INVALID_PAINT_BITMAP;
838 
839     cairo_save (ctx->cairo);
840     cairo_scale (ctx->cairo, 1, 1);
841     cairo_translate (ctx->cairo, x, y);
842     cairo_set_source (ctx->cairo, bmp->pattern);
843     cairo_rectangle (ctx->cairo, 0, 0, bmp->width, bmp->height);
844     cairo_fill (ctx->cairo);
845     cairo_restore (ctx->cairo);
846     return GGRAPH_OK;
847 }
848 
849 static void
adjust_for_endianness(unsigned char * rgbaArray,int width,int height)850 adjust_for_endianness (unsigned char *rgbaArray, int width, int height)
851 {
852 /* Adjusting from RGBA to ARGB respecting platform endianness */
853     int x;
854     int y;
855     unsigned char red;
856     unsigned char green;
857     unsigned char blue;
858     unsigned char alpha;
859     unsigned char *p_in = rgbaArray;
860     unsigned char *p_out = rgbaArray;
861     int little_endian = gg_endian_arch ();
862 
863     for (y = 0; y < height; y++)
864       {
865 	  for (x = 0; x < width; x++)
866 	    {
867 		red = *p_in++;
868 		green = *p_in++;
869 		blue = *p_in++;
870 		alpha = *p_in++;
871 		if (alpha == 0)
872 		  {
873 		      *p_out++ = 0;
874 		      *p_out++ = 0;
875 		      *p_out++ = 0;
876 		      *p_out++ = 0;
877 		  }
878 		else
879 		  {
880 		      if (little_endian)
881 			{
882 			    *p_out++ = blue;
883 			    *p_out++ = green;
884 			    *p_out++ = red;
885 			    *p_out++ = alpha;
886 			}
887 		      else
888 			{
889 			    *p_out++ = alpha;
890 			    *p_out++ = red;
891 			    *p_out++ = green;
892 			    *p_out++ = blue;
893 			}
894 		  }
895 	    }
896       }
897 }
898 
899 GGRAPH_DECLARE int
gGraphCreateBitmap(unsigned char * rgbaArray,int width,int height,const void ** bitmap)900 gGraphCreateBitmap (unsigned char *rgbaArray, int width, int height,
901 		    const void **bitmap)
902 {
903 /* creating a symbol bitmap */
904     gGraphBitmapPtr bmp;
905 
906     *bitmap = NULL;
907     if (!rgbaArray)
908 	return GGRAPH_ERROR;
909 
910     adjust_for_endianness (rgbaArray, width, height);
911     bmp = malloc (sizeof (gGraphBitmap));
912     if (!bmp)
913 	return GGRAPH_INSUFFICIENT_MEMORY;
914     bmp->signature = GG_GRAPHICS_BITMAP_MAGIC_SIGNATURE;
915     bmp->width = width;
916     bmp->height = height;
917     bmp->bitmap =
918 	cairo_image_surface_create_for_data (rgbaArray, CAIRO_FORMAT_ARGB32,
919 					     width, height, width * 4);
920     bmp->pattern = cairo_pattern_create_for_surface (bmp->bitmap);
921     *bitmap = bmp;
922     return GGRAPH_OK;
923 }
924 
925 GGRAPH_DECLARE int
gGraphDestroyBitmap(const void * bitmap)926 gGraphDestroyBitmap (const void *bitmap)
927 {
928 /* destroying a symbol bitmap */
929     gGraphBitmapPtr bmp = (gGraphBitmapPtr) bitmap;
930 
931     if (!bmp)
932 	return GGRAPH_INVALID_PAINT_BITMAP;
933     if (bmp->signature != GG_GRAPHICS_BITMAP_MAGIC_SIGNATURE)
934 	return GGRAPH_INVALID_PAINT_BITMAP;
935 
936     cairo_pattern_destroy (bmp->pattern);
937     cairo_surface_destroy (bmp->bitmap);
938     free (bmp);
939     return GGRAPH_OK;
940 }
941 
942 GGRAPH_DECLARE int
gGraphCreateBrush(unsigned char * rgbaArray,int width,int height,const void ** brush)943 gGraphCreateBrush (unsigned char *rgbaArray, int width, int height,
944 		   const void **brush)
945 {
946 /* creating a pattern brush */
947     gGraphPatternBrushPtr pattern;
948 
949     *brush = NULL;
950     if (!rgbaArray)
951 	return GGRAPH_ERROR;
952 
953     adjust_for_endianness (rgbaArray, width, height);
954     pattern = malloc (sizeof (gGraphPatternBrush));
955     if (!pattern)
956 	return GGRAPH_INSUFFICIENT_MEMORY;
957     pattern->signature = GG_GRAPHICS_BRUSH_MAGIC_SIGNATURE;
958     pattern->width = width;
959     pattern->height = height;
960     pattern->bitmap =
961 	cairo_image_surface_create_for_data (rgbaArray, CAIRO_FORMAT_ARGB32,
962 					     width, height, width * 4);
963     pattern->pattern = cairo_pattern_create_for_surface (pattern->bitmap);
964     cairo_pattern_set_extend (pattern->pattern, CAIRO_EXTEND_REPEAT);
965     *brush = pattern;
966     return GGRAPH_OK;
967 }
968 
969 GGRAPH_DECLARE int
gGraphDestroyBrush(const void * brush)970 gGraphDestroyBrush (const void *brush)
971 {
972 /* destroying a pattern brush */
973     gGraphPatternBrushPtr pattern = (gGraphPatternBrushPtr) brush;
974 
975     if (!pattern)
976 	return GGRAPH_INVALID_PAINT_BRUSH;
977     if (pattern->signature != GG_GRAPHICS_BRUSH_MAGIC_SIGNATURE)
978 	return GGRAPH_INVALID_PAINT_BRUSH;
979 
980     cairo_pattern_destroy (pattern->pattern);
981     cairo_surface_destroy (pattern->bitmap);
982     free (pattern);
983     return GGRAPH_OK;
984 }
985 
986 GGRAPH_DECLARE int
gGraphCreateFont(double size,int style,int weight,const void ** font)987 gGraphCreateFont (double size, int style, int weight, const void **font)
988 {
989 /* creating a font */
990     gGraphFontPtr fnt;
991 
992     *font = NULL;
993     fnt = malloc (sizeof (gGraphFont));
994     if (!fnt)
995 	return GGRAPH_INSUFFICIENT_MEMORY;
996     fnt->signature = GG_GRAPHICS_FONT_MAGIC_SIGNATURE;
997     if (size < 1.0)
998 	fnt->size = 1.0;
999     else if (size > 32.0)
1000 	fnt->size = 32.0;
1001     else
1002 	fnt->size = size;
1003     if (style == GGRAPH_FONTSTYLE_ITALIC)
1004 	fnt->style = GGRAPH_FONTSTYLE_ITALIC;
1005     else
1006 	fnt->style = GGRAPH_FONTSTYLE_NORMAL;
1007     if (weight == GGRAPH_FONTWEIGHT_BOLD)
1008 	fnt->weight = GGRAPH_FONTWEIGHT_BOLD;
1009     else
1010 	fnt->weight = GGRAPH_FONTWEIGHT_NORMAL;
1011     fnt->is_outlined = 0;
1012     fnt->outline_width = 0.0;
1013     fnt->red = 0.0;
1014     fnt->green = 0.0;
1015     fnt->blue = 0.0;
1016     fnt->alpha = 1.0;
1017     *font = fnt;
1018     return GGRAPH_OK;
1019 }
1020 
1021 GGRAPH_DECLARE int
gGraphDestroyFont(const void * font)1022 gGraphDestroyFont (const void *font)
1023 {
1024 /* destroying a font */
1025     gGraphFontPtr fnt = (gGraphFontPtr) font;
1026 
1027     if (!fnt)
1028 	return GGRAPH_INVALID_PAINT_FONT;
1029     if (fnt->signature != GG_GRAPHICS_FONT_MAGIC_SIGNATURE)
1030 	return GGRAPH_INVALID_PAINT_FONT;
1031 
1032     free (fnt);
1033     return GGRAPH_OK;
1034 }
1035 
1036 GGRAPH_DECLARE int
gGraphFontSetColor(const void * font,unsigned char red,unsigned char green,unsigned char blue,unsigned char alpha)1037 gGraphFontSetColor (const void *font, unsigned char red, unsigned char green,
1038 		    unsigned char blue, unsigned char alpha)
1039 {
1040 /* setting up the font color */
1041     gGraphFontPtr fnt = (gGraphFontPtr) font;
1042 
1043     if (!fnt)
1044 	return GGRAPH_INVALID_PAINT_FONT;
1045     if (fnt->signature != GG_GRAPHICS_FONT_MAGIC_SIGNATURE)
1046 	return GGRAPH_INVALID_PAINT_FONT;
1047 
1048     fnt->red = (double) red / 255.0;
1049     fnt->green = (double) green / 255.0;
1050     fnt->blue = (double) blue / 255.0;
1051     fnt->alpha = (double) alpha / 255.0;
1052     return GGRAPH_OK;
1053 }
1054 
1055 GGRAPH_DECLARE int
gGraphFontSetOutline(const void * font,double width)1056 gGraphFontSetOutline (const void *font, double width)
1057 {
1058 /* setting up the font outline */
1059     gGraphFontPtr fnt = (gGraphFontPtr) font;
1060 
1061     if (!fnt)
1062 	return GGRAPH_INVALID_PAINT_FONT;
1063     if (fnt->signature != GG_GRAPHICS_FONT_MAGIC_SIGNATURE)
1064 	return GGRAPH_INVALID_PAINT_FONT;
1065 
1066     if (width <= 0.0)
1067       {
1068 	  fnt->is_outlined = 0;
1069 	  fnt->outline_width = 0.0;
1070       }
1071     else
1072       {
1073 	  fnt->is_outlined = 1;
1074 	  fnt->outline_width = width;
1075       }
1076     return GGRAPH_OK;
1077 }
1078 
1079 GGRAPH_DECLARE int
gGraphSetFont(const void * context,const void * font)1080 gGraphSetFont (const void *context, const void *font)
1081 {
1082 /* setting up the current font */
1083     int style = CAIRO_FONT_SLANT_NORMAL;
1084     int weight = CAIRO_FONT_WEIGHT_NORMAL;
1085     double size;
1086     gGraphFontPtr fnt = (gGraphFontPtr) font;
1087     gGraphContextPtr ctx = (gGraphContextPtr) context;
1088 
1089     if (!ctx)
1090 	return GGRAPH_INVALID_PAINT_CONTEXT;
1091     if (ctx->signature == GG_GRAPHICS_CONTEXT_MAGIC_SIGNATURE ||
1092 	ctx->signature == GG_GRAPHICS_SVG_CONTEXT_MAGIC_SIGNATURE ||
1093 	ctx->signature == GG_GRAPHICS_PDF_CONTEXT_MAGIC_SIGNATURE)
1094 	;
1095     else
1096 	return GGRAPH_INVALID_PAINT_CONTEXT;
1097 
1098     if (!fnt)
1099 	return GGRAPH_INVALID_PAINT_FONT;
1100     if (fnt->signature != GG_GRAPHICS_FONT_MAGIC_SIGNATURE)
1101 	return GGRAPH_INVALID_PAINT_FONT;
1102 
1103     if (fnt->style == GGRAPH_FONTSTYLE_ITALIC)
1104 	style = CAIRO_FONT_SLANT_ITALIC;
1105     if (fnt->weight == GGRAPH_FONTWEIGHT_BOLD)
1106 	weight = CAIRO_FONT_WEIGHT_BOLD;
1107     cairo_select_font_face (ctx->cairo, "monospace", style, weight);
1108     size = fnt->size;
1109     if (fnt->is_outlined)
1110 	size += fnt->outline_width;
1111     cairo_set_font_size (ctx->cairo, size);
1112     ctx->font_red = fnt->red;
1113     ctx->font_green = fnt->green;
1114     ctx->font_blue = fnt->blue;
1115     ctx->font_alpha = fnt->alpha;
1116     ctx->is_font_outlined = fnt->is_outlined;
1117     ctx->font_outline_width = fnt->outline_width;
1118 
1119     return GGRAPH_OK;
1120 }
1121 
1122 GGRAPH_DECLARE int
gGraphGetTextExtent(const void * context,const char * text,double * pre_x,double * pre_y,double * width,double * height,double * post_x,double * post_y)1123 gGraphGetTextExtent (const void *context, const char *text, double *pre_x,
1124 		     double *pre_y, double *width, double *height,
1125 		     double *post_x, double *post_y)
1126 {
1127 /* measuring the text extent (using the current font) */
1128     cairo_text_extents_t extents;
1129     gGraphContextPtr ctx = (gGraphContextPtr) context;
1130 
1131     if (!ctx)
1132 	return GGRAPH_INVALID_PAINT_CONTEXT;
1133     if (ctx->signature == GG_GRAPHICS_CONTEXT_MAGIC_SIGNATURE ||
1134 	ctx->signature == GG_GRAPHICS_SVG_CONTEXT_MAGIC_SIGNATURE ||
1135 	ctx->signature == GG_GRAPHICS_PDF_CONTEXT_MAGIC_SIGNATURE)
1136 	;
1137     else
1138 	return GGRAPH_INVALID_PAINT_CONTEXT;
1139 
1140     cairo_text_extents (ctx->cairo, text, &extents);
1141     *pre_x = extents.x_bearing;
1142     *pre_y = extents.y_bearing;
1143     *width = extents.width;
1144     *height = extents.height;
1145     *post_x = extents.x_advance;
1146     *post_y = extents.y_advance;
1147     return GGRAPH_OK;
1148 }
1149 
1150 GGRAPH_DECLARE int
gGraphDrawText(const void * context,const char * text,double x,double y,double angle)1151 gGraphDrawText (const void *context, const char *text, double x, double y,
1152 		double angle)
1153 {
1154 /* drawing a text string (using the current font) */
1155     gGraphContextPtr ctx = (gGraphContextPtr) context;
1156 
1157     if (!ctx)
1158 	return GGRAPH_INVALID_PAINT_CONTEXT;
1159     if (ctx->signature == GG_GRAPHICS_CONTEXT_MAGIC_SIGNATURE ||
1160 	ctx->signature == GG_GRAPHICS_SVG_CONTEXT_MAGIC_SIGNATURE ||
1161 	ctx->signature == GG_GRAPHICS_PDF_CONTEXT_MAGIC_SIGNATURE)
1162 	;
1163     else
1164 	return GGRAPH_INVALID_PAINT_CONTEXT;
1165 
1166     cairo_save (ctx->cairo);
1167     cairo_translate (ctx->cairo, x, y);
1168     cairo_rotate (ctx->cairo, angle);
1169     if (ctx->is_font_outlined)
1170       {
1171 	  /* outlined font */
1172 	  cairo_move_to (ctx->cairo, 0.0, 0.0);
1173 	  cairo_text_path (ctx->cairo, text);
1174 	  cairo_set_source_rgba (ctx->cairo, ctx->font_red, ctx->font_green,
1175 				 ctx->font_blue, ctx->font_alpha);
1176 	  cairo_fill_preserve (ctx->cairo);
1177 	  cairo_set_source_rgba (ctx->cairo, 1.0, 1.0, 1.0, ctx->font_alpha);
1178 	  cairo_set_line_width (ctx->cairo, ctx->font_outline_width);
1179 	  cairo_stroke (ctx->cairo);
1180       }
1181     else
1182       {
1183 	  /* no outline */
1184 	  cairo_set_source_rgba (ctx->cairo, ctx->font_red, ctx->font_green,
1185 				 ctx->font_blue, ctx->font_alpha);
1186 	  cairo_move_to (ctx->cairo, 0.0, 0.0);
1187 	  cairo_show_text (ctx->cairo, text);
1188       }
1189     cairo_restore (ctx->cairo);
1190     return GGRAPH_OK;
1191 }
1192