1 /*
2 
3  rl2paint -- Cairco graphics functions
4 
5  version 0.1, 2013 September 29
6 
7  Author: Sandro Furieri a.furieri@lqt.it
8 
9  -----------------------------------------------------------------------------
10 
11  Version: MPL 1.1/GPL 2.0/LGPL 2.1
12 
13  The contents of this file are subject to the Mozilla Public License Version
14  1.1 (the "License"); you may not use this file except in compliance with
15  the License. You may obtain a copy of the License at
16  http://www.mozilla.org/MPL/
17 
18 Software distributed under the License is distributed on an "AS IS" basis,
19 WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
20 for the specific language governing rights and limitations under the
21 License.
22 
23 The Original Code is the RasterLite2 library
24 
25 The Initial Developer of the Original Code is Alessandro Furieri
26 
27 Portions created by the Initial Developer are Copyright (C) 2013
28 the Initial Developer. All Rights Reserved.
29 
30 Alternatively, the contents of this file may be used under the terms of
31 either the GNU General Public License Version 2 or later (the "GPL"), or
32 the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
33 in which case the provisions of the GPL or the LGPL are applicable instead
34 of those above. If you wish to allow use of your version of this file only
35 under the terms of either the GPL or the LGPL, and not to allow others to
36 use your version of this file under the terms of the MPL, indicate your
37 decision by deleting the provisions above and replace them with the notice
38 and other provisions required by the GPL or the LGPL. If you do not delete
39 the provisions above, a recipient may use your version of this file under
40 the terms of any one of the MPL, the GPL or the LGPL.
41 
42 */
43 #include <stdlib.h>
44 #include <stdio.h>
45 #include <string.h>
46 #include <math.h>
47 
48 #ifdef LOADABLE_EXTENSION
49 #include "rasterlite2/sqlite.h"
50 #endif
51 
52 #include "rasterlite2/rasterlite2.h"
53 #include "rasterlite2/rl2graphics.h"
54 #include "rasterlite2_private.h"
55 
56 #ifdef __ANDROID__		/* Android specific */
57 #include <cairo.h>
58 #include <cairo-svg.h>
59 #include <cairo-pdf.h>
60 #else /* any other standard platform (Win, Linux, Mac) */
61 #include <cairo/cairo.h>
62 #include <cairo/cairo-svg.h>
63 #include <cairo/cairo-pdf.h>
64 #endif /* end Android conditionals */
65 
66 #define RL2_SURFACE_IMG 2671
67 #define RL2_SURFACE_SVG 1267
68 #define RL2_SURFACE_PDF	1276
69 
70 struct rl2_graphics_pen
71 {
72 /* a struct wrapping a Cairo Pen */
73     double red;
74     double green;
75     double blue;
76     double alpha;
77     double width;
78     double lengths[4];
79     int lengths_count;
80 };
81 
82 struct rl2_graphics_brush
83 {
84 /* a struct wrapping a Cairo Brush */
85     int is_solid_color;
86     int is_linear_gradient;
87     int is_pattern;
88     double red;
89     double green;
90     double blue;
91     double alpha;
92     double x0;
93     double y0;
94     double x1;
95     double y1;
96     double red2;
97     double green2;
98     double blue2;
99     double alpha2;
100     cairo_pattern_t *pattern;
101 };
102 
103 typedef struct rl2_graphics_context
104 {
105 /* a Cairo based painting context */
106     int type;
107     cairo_surface_t *surface;
108     cairo_surface_t *clip_surface;
109     cairo_t *cairo;
110     cairo_t *clip_cairo;
111     struct rl2_graphics_pen current_pen;
112     struct rl2_graphics_brush current_brush;
113     double font_red;
114     double font_green;
115     double font_blue;
116     double font_alpha;
117     int is_font_outlined;
118     double font_outline_width;
119 } RL2GraphContext;
120 typedef RL2GraphContext *RL2GraphContextPtr;
121 
122 typedef struct rl2_graphics_pattern_brush
123 {
124 /* a Cairo based pattern brush */
125     int width;
126     int height;
127     unsigned char *rgba;
128     cairo_surface_t *bitmap;
129     cairo_pattern_t *pattern;
130 } RL2GraphPatternBrush;
131 typedef RL2GraphPatternBrush *RL2GraphPatternBrushPtr;
132 
133 typedef struct rl2_graphics_font
134 {
135 /* a struct wrapping a Cairo Font */
136     double size;
137     int is_outlined;
138     double outline_width;
139     int style;
140     int weight;
141     double red;
142     double green;
143     double blue;
144     double alpha;
145 } RL2GraphFont;
146 typedef RL2GraphFont *RL2GraphFontPtr;
147 
148 typedef struct rl2_graphics_bitmap
149 {
150 /* a Cairo based symbol bitmap */
151     int width;
152     int height;
153     unsigned char *rgba;
154     cairo_surface_t *bitmap;
155     cairo_pattern_t *pattern;
156 } RL2GraphBitmap;
157 typedef RL2GraphBitmap *RL2GraphBitmapPtr;
158 
159 RL2_DECLARE rl2GraphicsContextPtr
rl2_graph_create_context(int width,int height)160 rl2_graph_create_context (int width, int height)
161 {
162 /* creating a generic Graphics Context */
163     RL2GraphContextPtr ctx;
164 
165     ctx = malloc (sizeof (RL2GraphContext));
166     if (!ctx)
167 	return NULL;
168 
169     ctx->type = RL2_SURFACE_IMG;
170     ctx->clip_surface = NULL;
171     ctx->clip_cairo = NULL;
172     ctx->surface =
173 	cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height);
174     if (cairo_surface_status (ctx->surface) == CAIRO_STATUS_SUCCESS)
175 	;
176     else
177 	goto error1;
178     ctx->cairo = cairo_create (ctx->surface);
179     if (cairo_status (ctx->cairo) == CAIRO_STATUS_NO_MEMORY)
180 	goto error2;
181 
182 /* setting up a default Black Pen */
183     ctx->current_pen.red = 0.0;
184     ctx->current_pen.green = 0.0;
185     ctx->current_pen.blue = 0.0;
186     ctx->current_pen.alpha = 1.0;
187     ctx->current_pen.width = 1.0;
188     ctx->current_pen.lengths[0] = 1.0;
189     ctx->current_pen.lengths_count = 1;
190 
191 /* setting up a default Black Brush */
192     ctx->current_brush.is_solid_color = 1;
193     ctx->current_brush.is_linear_gradient = 0;
194     ctx->current_brush.is_pattern = 0;
195     ctx->current_brush.red = 0.0;
196     ctx->current_brush.green = 0.0;
197     ctx->current_brush.blue = 0.0;
198     ctx->current_brush.alpha = 1.0;
199     ctx->current_brush.pattern = NULL;
200 
201 /* priming a transparent background */
202     cairo_rectangle (ctx->cairo, 0, 0, width, height);
203     cairo_set_source_rgba (ctx->cairo, 0.0, 0.0, 0.0, 0.0);
204     cairo_fill (ctx->cairo);
205 
206 /* setting up default Font options */
207     ctx->font_red = 0.0;
208     ctx->font_green = 0.0;
209     ctx->font_blue = 0.0;
210     ctx->font_alpha = 1.0;
211     ctx->is_font_outlined = 0;
212     ctx->font_outline_width = 0.0;
213     return (rl2GraphicsContextPtr) ctx;
214   error2:
215     cairo_destroy (ctx->cairo);
216     cairo_surface_destroy (ctx->surface);
217     return NULL;
218   error1:
219     cairo_surface_destroy (ctx->surface);
220     return NULL;
221 }
222 
223 static void
destroy_context(RL2GraphContextPtr ctx)224 destroy_context (RL2GraphContextPtr ctx)
225 {
226 /* memory cleanup - destroying a Graphics Context */
227     if (ctx == NULL)
228 	return;
229     cairo_destroy (ctx->cairo);
230     cairo_surface_destroy (ctx->surface);
231     free (ctx);
232 }
233 
234 static void
destroy_svg_context(RL2GraphContextPtr ctx)235 destroy_svg_context (RL2GraphContextPtr ctx)
236 {
237 /* freeing an SVG Graphics Context */
238     if (ctx == NULL)
239 	return;
240     cairo_surface_show_page (ctx->surface);
241     cairo_destroy (ctx->cairo);
242     cairo_surface_finish (ctx->surface);
243     cairo_surface_destroy (ctx->surface);
244     free (ctx);
245 }
246 
247 static void
destroy_pdf_context(RL2GraphContextPtr ctx)248 destroy_pdf_context (RL2GraphContextPtr ctx)
249 {
250 /* freeing an PDF Graphics Context */
251     if (ctx == NULL)
252 	return;
253     cairo_surface_finish (ctx->clip_surface);
254     cairo_surface_destroy (ctx->clip_surface);
255     cairo_destroy (ctx->clip_cairo);
256     cairo_surface_show_page (ctx->surface);
257     cairo_destroy (ctx->cairo);
258     cairo_surface_finish (ctx->surface);
259     cairo_surface_destroy (ctx->surface);
260     free (ctx);
261 }
262 
263 RL2_DECLARE void
rl2_graph_destroy_context(rl2GraphicsContextPtr context)264 rl2_graph_destroy_context (rl2GraphicsContextPtr context)
265 {
266 /* memory cleanup - destroying a Graphics Context */
267     RL2GraphContextPtr ctx = (RL2GraphContextPtr) context;
268     if (ctx == NULL)
269 	return;
270     if (ctx->type == RL2_SURFACE_SVG)
271 	destroy_svg_context (ctx);
272     else if (ctx->type == RL2_SURFACE_PDF)
273 	destroy_pdf_context (ctx);
274     else
275 	destroy_context (ctx);
276 }
277 
278 RL2_DECLARE rl2GraphicsContextPtr
rl2_graph_create_svg_context(const char * path,int width,int height)279 rl2_graph_create_svg_context (const char *path, int width, int height)
280 {
281 /* creating an SVG Graphics Context */
282     RL2GraphContextPtr ctx;
283 
284     ctx = malloc (sizeof (RL2GraphContext));
285     if (!ctx)
286 	return NULL;
287 
288     ctx->type = RL2_SURFACE_SVG;
289     ctx->clip_surface = NULL;
290     ctx->clip_cairo = NULL;
291     ctx->surface =
292 	cairo_svg_surface_create (path, (double) width, (double) height);
293 
294     if (cairo_surface_status (ctx->surface) == CAIRO_STATUS_SUCCESS)
295 	;
296     else
297 	goto error1;
298     ctx->cairo = cairo_create (ctx->surface);
299     if (cairo_status (ctx->cairo) == CAIRO_STATUS_NO_MEMORY)
300 	goto error2;
301 
302 /* setting up a default Black Pen */
303     ctx->current_pen.red = 0.0;
304     ctx->current_pen.green = 0.0;
305     ctx->current_pen.blue = 0.0;
306     ctx->current_pen.alpha = 1.0;
307     ctx->current_pen.width = 1.0;
308     ctx->current_pen.lengths[0] = 1.0;
309     ctx->current_pen.lengths_count = 1;
310 
311 /* setting up a default Black Brush */
312     ctx->current_brush.is_solid_color = 1;
313     ctx->current_brush.is_linear_gradient = 0;
314     ctx->current_brush.is_pattern = 0;
315     ctx->current_brush.red = 0.0;
316     ctx->current_brush.green = 0.0;
317     ctx->current_brush.blue = 0.0;
318     ctx->current_brush.alpha = 1.0;
319     ctx->current_brush.pattern = NULL;
320 
321 /* priming a transparent background */
322     cairo_rectangle (ctx->cairo, 0, 0, width, height);
323     cairo_set_source_rgba (ctx->cairo, 0.0, 0.0, 0.0, 0.0);
324     cairo_fill (ctx->cairo);
325 
326 /* setting up default Font options */
327     ctx->font_red = 0.0;
328     ctx->font_green = 0.0;
329     ctx->font_blue = 0.0;
330     ctx->font_alpha = 1.0;
331     ctx->is_font_outlined = 0;
332     ctx->font_outline_width = 0.0;
333     return (rl2GraphicsContextPtr) ctx;
334   error2:
335     cairo_destroy (ctx->cairo);
336     cairo_surface_destroy (ctx->surface);
337     return NULL;
338   error1:
339     cairo_surface_destroy (ctx->surface);
340     return NULL;
341 }
342 
343 RL2_DECLARE rl2GraphicsContextPtr
rl2_graph_create_pdf_context(const char * path,int dpi,double page_width,double page_height,double margin_width,double margin_height)344 rl2_graph_create_pdf_context (const char *path, int dpi, double page_width,
345 			      double page_height, double margin_width,
346 			      double margin_height)
347 {
348 /* creating a PDF Graphics Context */
349     RL2GraphContextPtr ctx;
350     double scale = 72.0 / (double) dpi;
351     double page2_width = page_width * 72.0;
352     double page2_height = page_height * 72.0;
353     double horz_margin_sz = margin_width * 72.0;
354     double vert_margin_sz = margin_height * 72.0;
355     double img_width = (page_width - (margin_width * 2.0)) * 72.0;
356     double img_height = (page_height - (margin_height * 2.0)) * 72.0;
357 
358     ctx = malloc (sizeof (RL2GraphContext));
359     if (ctx == NULL)
360 	return NULL;
361 
362     ctx->type = RL2_SURFACE_PDF;
363     ctx->clip_surface = NULL;
364     ctx->clip_cairo = NULL;
365     ctx->surface = cairo_pdf_surface_create (path, page2_width, page2_height);
366     if (cairo_surface_status (ctx->surface) == CAIRO_STATUS_SUCCESS)
367 	;
368     else
369 	goto error1;
370     ctx->cairo = cairo_create (ctx->surface);
371     if (cairo_status (ctx->cairo) == CAIRO_STATUS_NO_MEMORY)
372 	goto error2;
373 
374 /* priming a transparent background */
375     cairo_rectangle (ctx->cairo, 0, 0, page2_width, page2_height);
376     cairo_set_source_rgba (ctx->cairo, 0.0, 0.0, 0.0, 0.0);
377     cairo_fill (ctx->cairo);
378 
379 /* clipped surface respecting free margins */
380     ctx->clip_surface =
381 	cairo_surface_create_for_rectangle (ctx->surface, horz_margin_sz,
382 					    vert_margin_sz, img_width,
383 					    img_height);
384     if (cairo_surface_status (ctx->clip_surface) == CAIRO_STATUS_SUCCESS)
385 	;
386     else
387 	goto error3;
388     ctx->clip_cairo = cairo_create (ctx->clip_surface);
389     if (cairo_status (ctx->clip_cairo) == CAIRO_STATUS_NO_MEMORY)
390 	goto error4;
391 
392 /* setting up a default Black Pen */
393     ctx->current_pen.red = 0.0;
394     ctx->current_pen.green = 0.0;
395     ctx->current_pen.blue = 0.0;
396     ctx->current_pen.alpha = 1.0;
397     ctx->current_pen.width = 1.0;
398     ctx->current_pen.lengths[0] = 1.0;
399     ctx->current_pen.lengths_count = 1;
400 
401 /* setting up a default Black Brush */
402     ctx->current_brush.is_solid_color = 1;
403     ctx->current_brush.is_linear_gradient = 0;
404     ctx->current_brush.is_pattern = 0;
405     ctx->current_brush.red = 0.0;
406     ctx->current_brush.green = 0.0;
407     ctx->current_brush.blue = 0.0;
408     ctx->current_brush.alpha = 1.0;
409     ctx->current_brush.pattern = NULL;
410 
411 /* scaling accordingly to DPI resolution */
412     cairo_scale (ctx->clip_cairo, scale, scale);
413 
414 /* setting up default Font options */
415     ctx->font_red = 0.0;
416     ctx->font_green = 0.0;
417     ctx->font_blue = 0.0;
418     ctx->font_alpha = 1.0;
419     ctx->is_font_outlined = 0;
420     ctx->font_outline_width = 0.0;
421     return (rl2GraphicsContextPtr) ctx;
422   error4:
423     cairo_destroy (ctx->clip_cairo);
424     cairo_surface_destroy (ctx->clip_surface);
425     cairo_destroy (ctx->cairo);
426     cairo_surface_destroy (ctx->surface);
427     return NULL;
428   error3:
429     cairo_surface_destroy (ctx->clip_surface);
430     cairo_destroy (ctx->cairo);
431     cairo_surface_destroy (ctx->surface);
432     return NULL;
433   error2:
434     cairo_destroy (ctx->cairo);
435     cairo_surface_destroy (ctx->surface);
436     return NULL;
437   error1:
438     cairo_surface_destroy (ctx->surface);
439     return NULL;
440 }
441 
442 static cairo_status_t
pdf_write_func(void * ptr,const unsigned char * data,unsigned int length)443 pdf_write_func (void *ptr, const unsigned char *data, unsigned int length)
444 {
445 /* writing into the in-memory PDF target */
446     rl2PrivMemPdfPtr mem = (rl2PrivMemPdfPtr) ptr;
447     if (mem == NULL)
448 	return CAIRO_STATUS_WRITE_ERROR;
449 
450     if (mem->write_offset + (int) length < mem->size)
451       {
452 	  /* inserting into the current buffer */
453 	  memcpy (mem->buffer + mem->write_offset, data, length);
454 	  mem->write_offset += length;
455       }
456     else
457       {
458 	  /* expanding the current buffer */
459 	  int new_sz = mem->size + length + (64 * 1024);
460 	  unsigned char *save = mem->buffer;
461 	  mem->buffer = realloc (mem->buffer, new_sz);
462 	  if (mem->buffer == NULL)
463 	    {
464 		free (save);
465 		return CAIRO_STATUS_WRITE_ERROR;
466 	    }
467 	  mem->size = new_sz;
468 	  memcpy (mem->buffer + mem->write_offset, data, length);
469 	  mem->write_offset += length;
470       }
471     return CAIRO_STATUS_SUCCESS;
472 }
473 
474 RL2_DECLARE rl2GraphicsContextPtr
rl2_graph_create_mem_pdf_context(rl2MemPdfPtr mem_pdf,int dpi,double page_width,double page_height,double margin_width,double margin_height)475 rl2_graph_create_mem_pdf_context (rl2MemPdfPtr mem_pdf, int dpi,
476 				  double page_width, double page_height,
477 				  double margin_width, double margin_height)
478 {
479 /* creating an in-memory PDF Graphics Context */
480     RL2GraphContextPtr ctx;
481     double scale = 72.0 / (double) dpi;
482     double page2_width = page_width * 72.0;
483     double page2_height = page_height * 72.0;
484     double horz_margin_sz = margin_width * 72.0;
485     double vert_margin_sz = margin_height * 72.0;
486     double img_width = (page_width - (margin_width * 2.0)) * 72.0;
487     double img_height = (page_height - (margin_height * 2.0)) * 72.0;
488 
489     ctx = malloc (sizeof (RL2GraphContext));
490     if (ctx == NULL)
491 	return NULL;
492 
493     ctx->type = RL2_SURFACE_PDF;
494     ctx->clip_surface = NULL;
495     ctx->clip_cairo = NULL;
496     ctx->surface =
497 	cairo_pdf_surface_create_for_stream (pdf_write_func, mem_pdf,
498 					     page2_width, page2_height);
499     if (cairo_surface_status (ctx->surface) == CAIRO_STATUS_SUCCESS)
500 	;
501     else
502 	goto error1;
503     ctx->cairo = cairo_create (ctx->surface);
504     if (cairo_status (ctx->cairo) == CAIRO_STATUS_NO_MEMORY)
505 	goto error2;
506 
507 /* priming a transparent background */
508     cairo_rectangle (ctx->cairo, 0, 0, page2_width, page2_height);
509     cairo_set_source_rgba (ctx->cairo, 0.0, 0.0, 0.0, 0.0);
510     cairo_fill (ctx->cairo);
511 
512 /* clipped surface respecting free margins */
513     ctx->clip_surface =
514 	cairo_surface_create_for_rectangle (ctx->surface, horz_margin_sz,
515 					    vert_margin_sz, img_width,
516 					    img_height);
517     if (cairo_surface_status (ctx->clip_surface) == CAIRO_STATUS_SUCCESS)
518 	;
519     else
520 	goto error3;
521     ctx->clip_cairo = cairo_create (ctx->clip_surface);
522     if (cairo_status (ctx->clip_cairo) == CAIRO_STATUS_NO_MEMORY)
523 	goto error4;
524 
525 /* setting up a default Black Pen */
526     ctx->current_pen.red = 0.0;
527     ctx->current_pen.green = 0.0;
528     ctx->current_pen.blue = 0.0;
529     ctx->current_pen.alpha = 1.0;
530     ctx->current_pen.width = 1.0;
531     ctx->current_pen.lengths[0] = 1.0;
532     ctx->current_pen.lengths_count = 1;
533 
534 /* setting up a default Black Brush */
535     ctx->current_brush.is_solid_color = 1;
536     ctx->current_brush.is_linear_gradient = 0;
537     ctx->current_brush.is_pattern = 0;
538     ctx->current_brush.red = 0.0;
539     ctx->current_brush.green = 0.0;
540     ctx->current_brush.blue = 0.0;
541     ctx->current_brush.alpha = 1.0;
542     ctx->current_brush.pattern = NULL;
543 
544 /* scaling accordingly to DPI resolution */
545     cairo_scale (ctx->clip_cairo, scale, scale);
546 
547 /* setting up default Font options */
548     ctx->font_red = 0.0;
549     ctx->font_green = 0.0;
550     ctx->font_blue = 0.0;
551     ctx->font_alpha = 1.0;
552     ctx->is_font_outlined = 0;
553     ctx->font_outline_width = 0.0;
554     return (rl2GraphicsContextPtr) ctx;
555   error4:
556     cairo_destroy (ctx->clip_cairo);
557     cairo_surface_destroy (ctx->clip_surface);
558     cairo_destroy (ctx->cairo);
559     cairo_surface_destroy (ctx->surface);
560     return NULL;
561   error3:
562     cairo_surface_destroy (ctx->clip_surface);
563     cairo_destroy (ctx->cairo);
564     cairo_surface_destroy (ctx->surface);
565     return NULL;
566   error2:
567     cairo_destroy (ctx->cairo);
568     cairo_surface_destroy (ctx->surface);
569     return NULL;
570   error1:
571     cairo_surface_destroy (ctx->surface);
572     return NULL;
573 }
574 
575 RL2_DECLARE int
rl2_graph_set_pen(rl2GraphicsContextPtr context,unsigned char red,unsigned char green,unsigned char blue,unsigned char alpha,double width,int style)576 rl2_graph_set_pen (rl2GraphicsContextPtr context, unsigned char red,
577 		   unsigned char green, unsigned char blue, unsigned char alpha,
578 		   double width, int style)
579 {
580 /* creating a Color Pen */
581     double d_red = (double) red / 255.0;
582     double d_green = (double) green / 255.0;
583     double d_blue = (double) blue / 255.0;
584     double d_alpha = (double) alpha / 255.0;
585     RL2GraphContextPtr ctx = (RL2GraphContextPtr) context;
586     if (ctx == NULL)
587 	return 0;
588 
589     ctx->current_pen.width = width;
590     ctx->current_pen.red = d_red;
591     ctx->current_pen.green = d_green;
592     ctx->current_pen.blue = d_blue;
593     ctx->current_pen.alpha = d_alpha;
594     switch (style)
595       {
596       case RL2_PENSTYLE_DOT:
597 	  ctx->current_pen.lengths[0] = 2;
598 	  ctx->current_pen.lengths[1] = 2;
599 	  ctx->current_pen.lengths_count = 2;
600 	  break;
601       case RL2_PENSTYLE_LONG_DASH:
602 	  ctx->current_pen.lengths[0] = 16;
603 	  ctx->current_pen.lengths[1] = 8;
604 	  ctx->current_pen.lengths_count = 2;
605 	  break;
606       case RL2_PENSTYLE_SHORT_DASH:
607 	  ctx->current_pen.lengths[0] = 8;
608 	  ctx->current_pen.lengths[1] = 4;
609 	  ctx->current_pen.lengths_count = 2;
610 	  break;
611       case RL2_PENSTYLE_DOT_DASH:
612 	  ctx->current_pen.lengths[0] = 8;
613 	  ctx->current_pen.lengths[1] = 4;
614 	  ctx->current_pen.lengths[2] = 2;
615 	  ctx->current_pen.lengths[3] = 4;
616 	  ctx->current_pen.lengths_count = 4;
617 	  break;
618       default:
619 	  ctx->current_pen.lengths[0] = 1;
620 	  ctx->current_pen.lengths[1] = 0;
621 	  ctx->current_pen.lengths_count = 2;
622       };
623     return 1;
624 }
625 
626 RL2_DECLARE int
rl2_graph_set_brush(rl2GraphicsContextPtr context,unsigned char red,unsigned char green,unsigned char blue,unsigned char alpha)627 rl2_graph_set_brush (rl2GraphicsContextPtr context, unsigned char red,
628 		     unsigned char green, unsigned char blue,
629 		     unsigned char alpha)
630 {
631 /* setting up a Color Brush */
632     double d_red = (double) red / 255.0;
633     double d_green = (double) green / 255.0;
634     double d_blue = (double) blue / 255.0;
635     double d_alpha = (double) alpha / 255.0;
636     RL2GraphContextPtr ctx = (RL2GraphContextPtr) context;
637     if (ctx == NULL)
638 	return 0;
639 
640     ctx->current_brush.is_solid_color = 1;
641     ctx->current_brush.is_linear_gradient = 0;
642     ctx->current_brush.is_pattern = 0;
643     ctx->current_brush.red = d_red;
644     ctx->current_brush.green = d_green;
645     ctx->current_brush.blue = d_blue;
646     ctx->current_brush.alpha = d_alpha;
647     return 1;
648 }
649 
650 RL2_DECLARE int
rl2_graph_set_linear_gradient_brush(rl2GraphicsContextPtr 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)651 rl2_graph_set_linear_gradient_brush (rl2GraphicsContextPtr context, double x,
652 				     double y, double width, double height,
653 				     unsigned char red1, unsigned char green1,
654 				     unsigned char blue1, unsigned char alpha1,
655 				     unsigned char red2, unsigned char green2,
656 				     unsigned char blue2, unsigned char alpha2)
657 {
658 /* setting up a Linear Gradient Brush */
659     double d_red = (double) red1 / 255.0;
660     double d_green = (double) green1 / 255.0;
661     double d_blue = (double) blue1 / 255.0;
662     double d_alpha = (double) alpha1 / 255.0;
663     RL2GraphContextPtr ctx = (RL2GraphContextPtr) context;
664     if (ctx == NULL)
665 	return 0;
666 
667     ctx->current_brush.is_solid_color = 0;
668     ctx->current_brush.is_linear_gradient = 1;
669     ctx->current_brush.is_pattern = 0;
670     ctx->current_brush.red = d_red;
671     ctx->current_brush.green = d_green;
672     ctx->current_brush.blue = d_blue;
673     ctx->current_brush.alpha = d_alpha;
674     ctx->current_brush.x0 = x;
675     ctx->current_brush.y0 = y;
676     ctx->current_brush.x1 = x + width;
677     ctx->current_brush.y1 = y + height;
678     d_red = (double) red2 / 255.0;
679     d_green = (double) green2 / 255.0;
680     d_blue = (double) blue2 / 255.0;
681     d_alpha = (double) alpha2 / 255.0;
682     ctx->current_brush.red2 = d_red;
683     ctx->current_brush.green2 = d_green;
684     ctx->current_brush.blue2 = d_blue;
685     ctx->current_brush.alpha2 = d_alpha;
686     return 1;
687 }
688 
689 RL2_DECLARE int
rl2_graph_set_pattern_brush(rl2GraphicsContextPtr context,rl2GraphicsPatternPtr brush)690 rl2_graph_set_pattern_brush (rl2GraphicsContextPtr context,
691 			     rl2GraphicsPatternPtr brush)
692 {
693 /* setting up a Pattern Brush */
694     RL2GraphPatternBrushPtr pattern = (RL2GraphPatternBrushPtr) brush;
695     RL2GraphContextPtr ctx = (RL2GraphContextPtr) context;
696 
697     if (ctx == NULL)
698 	return 0;
699     if (pattern == NULL)
700 	return 0;
701 
702     ctx->current_brush.is_solid_color = 0;
703     ctx->current_brush.is_linear_gradient = 0;
704     ctx->current_brush.is_pattern = 1;
705 
706     ctx->current_brush.pattern = pattern->pattern;
707     return 1;
708 }
709 
710 RL2_DECLARE int
rl2_graph_set_font(rl2GraphicsContextPtr context,rl2GraphicsFontPtr font)711 rl2_graph_set_font (rl2GraphicsContextPtr context, rl2GraphicsFontPtr font)
712 {
713 /* setting up the current font */
714     cairo_t *cairo;
715     int style = CAIRO_FONT_SLANT_NORMAL;
716     int weight = CAIRO_FONT_WEIGHT_NORMAL;
717     double size;
718     RL2GraphFontPtr fnt = (RL2GraphFontPtr) font;
719     RL2GraphContextPtr ctx = (RL2GraphContextPtr) context;
720 
721     if (ctx == NULL)
722 	return 0;
723     if (fnt == NULL)
724 	return 0;
725     if (ctx->type == RL2_SURFACE_PDF)
726 	cairo = ctx->clip_cairo;
727     else
728 	cairo = ctx->cairo;
729 
730     if (fnt->style == RL2_FONTSTYLE_ITALIC)
731 	style = CAIRO_FONT_SLANT_ITALIC;
732     if (fnt->weight == RL2_FONTWEIGHT_BOLD)
733 	weight = CAIRO_FONT_WEIGHT_BOLD;
734     cairo_select_font_face (cairo, "monospace", style, weight);
735     size = fnt->size;
736     if (fnt->is_outlined)
737 	size += fnt->outline_width;
738     cairo_set_font_size (cairo, size);
739     ctx->font_red = fnt->red;
740     ctx->font_green = fnt->green;
741     ctx->font_blue = fnt->blue;
742     ctx->font_alpha = fnt->alpha;
743     ctx->is_font_outlined = fnt->is_outlined;
744     ctx->font_outline_width = fnt->outline_width;
745 
746     return 1;
747 }
748 
749 static int
rl2cr_endian_arch()750 rl2cr_endian_arch ()
751 {
752 /* checking if target CPU is a little-endian one */
753     union cvt
754     {
755 	unsigned char byte[4];
756 	int int_value;
757     } convert;
758     convert.int_value = 1;
759     if (convert.byte[0] == 0)
760 	return 0;
761     return 1;
762 }
763 
764 static void
adjust_for_endianness(unsigned char * rgbaArray,int width,int height)765 adjust_for_endianness (unsigned char *rgbaArray, int width, int height)
766 {
767 /* Adjusting from RGBA to ARGB respecting platform endianness */
768     int x;
769     int y;
770     unsigned char red;
771     unsigned char green;
772     unsigned char blue;
773     unsigned char alpha;
774     unsigned char *p_in = rgbaArray;
775     unsigned char *p_out = rgbaArray;
776     int little_endian = rl2cr_endian_arch ();
777 
778     for (y = 0; y < height; y++)
779       {
780 	  for (x = 0; x < width; x++)
781 	    {
782 		red = *p_in++;
783 		green = *p_in++;
784 		blue = *p_in++;
785 		alpha = *p_in++;
786 		if (little_endian)
787 		  {
788 		      *p_out++ = blue;
789 		      *p_out++ = green;
790 		      *p_out++ = red;
791 		      *p_out++ = alpha;
792 		  }
793 		else
794 		  {
795 		      *p_out++ = alpha;
796 		      *p_out++ = red;
797 		      *p_out++ = green;
798 		      *p_out++ = blue;
799 		  }
800 	    }
801       }
802 }
803 
804 RL2_DECLARE rl2GraphicsPatternPtr
rl2_graph_create_pattern(unsigned char * rgbaArray,int width,int height)805 rl2_graph_create_pattern (unsigned char *rgbaArray, int width, int height)
806 {
807 /* creating a pattern brush */
808     RL2GraphPatternBrushPtr pattern;
809 
810     if (rgbaArray == NULL)
811 	return NULL;
812 
813     adjust_for_endianness (rgbaArray, width, height);
814     pattern = malloc (sizeof (RL2GraphPatternBrush));
815     if (pattern == NULL)
816 	return NULL;
817     pattern->width = width;
818     pattern->height = height;
819     pattern->rgba = rgbaArray;
820     pattern->bitmap =
821 	cairo_image_surface_create_for_data (rgbaArray, CAIRO_FORMAT_ARGB32,
822 					     width, height, width * 4);
823     pattern->pattern = cairo_pattern_create_for_surface (pattern->bitmap);
824     cairo_pattern_set_extend (pattern->pattern, CAIRO_EXTEND_REPEAT);
825     return (rl2GraphicsPatternPtr) pattern;
826 }
827 
828 RL2_DECLARE void
rl2_graph_destroy_pattern(rl2GraphicsPatternPtr brush)829 rl2_graph_destroy_pattern (rl2GraphicsPatternPtr brush)
830 {
831 /* destroying a pattern brush */
832     RL2GraphPatternBrushPtr pattern = (RL2GraphPatternBrushPtr) brush;
833 
834     if (pattern == NULL)
835 	return;
836 
837     cairo_pattern_destroy (pattern->pattern);
838     cairo_surface_destroy (pattern->bitmap);
839     if (pattern->rgba != NULL)
840 	free (pattern->rgba);
841     free (pattern);
842 }
843 
844 RL2_DECLARE rl2GraphicsFontPtr
rl2_graph_create_font(double size,int style,int weight)845 rl2_graph_create_font (double size, int style, int weight)
846 {
847 /* creating a font */
848     RL2GraphFontPtr fnt;
849 
850     fnt = malloc (sizeof (RL2GraphFont));
851     if (fnt == NULL)
852 	return NULL;
853     if (size < 1.0)
854 	fnt->size = 1.0;
855     else if (size > 32.0)
856 	fnt->size = 32.0;
857     else
858 	fnt->size = size;
859     if (style == RL2_FONTSTYLE_ITALIC)
860 	fnt->style = RL2_FONTSTYLE_ITALIC;
861     else
862 	fnt->style = RL2_FONTSTYLE_NORMAL;
863     if (weight == RL2_FONTWEIGHT_BOLD)
864 	fnt->weight = RL2_FONTWEIGHT_BOLD;
865     else
866 	fnt->weight = RL2_FONTWEIGHT_NORMAL;
867     fnt->is_outlined = 0;
868     fnt->outline_width = 0.0;
869     fnt->red = 0.0;
870     fnt->green = 0.0;
871     fnt->blue = 0.0;
872     fnt->alpha = 1.0;
873     return (rl2GraphicsFontPtr) fnt;
874 }
875 
876 RL2_DECLARE void
rl2_graph_destroy_font(rl2GraphicsFontPtr font)877 rl2_graph_destroy_font (rl2GraphicsFontPtr font)
878 {
879 /* destroying a font */
880     RL2GraphFontPtr fnt = (RL2GraphFontPtr) font;
881 
882     if (fnt == NULL)
883 	return;
884 
885     free (fnt);
886 }
887 
888 RL2_DECLARE int
rl2_graph_font_set_color(rl2GraphicsFontPtr font,unsigned char red,unsigned char green,unsigned char blue,unsigned char alpha)889 rl2_graph_font_set_color (rl2GraphicsFontPtr font, unsigned char red,
890 			  unsigned char green, unsigned char blue,
891 			  unsigned char alpha)
892 {
893 /* setting up the font color */
894     RL2GraphFontPtr fnt = (RL2GraphFontPtr) font;
895 
896     if (fnt == NULL)
897 	return 0;
898 
899     fnt->red = (double) red / 255.0;
900     fnt->green = (double) green / 255.0;
901     fnt->blue = (double) blue / 255.0;
902     fnt->alpha = (double) alpha / 255.0;
903     return 1;
904 }
905 
906 RL2_DECLARE int
rl2_graph_font_set_outline(rl2GraphicsFontPtr font,double width)907 rl2_graph_font_set_outline (rl2GraphicsFontPtr font, double width)
908 {
909 /* setting up the font outline */
910     RL2GraphFontPtr fnt = (RL2GraphFontPtr) font;
911 
912     if (fnt == NULL)
913 	return 0;
914 
915     if (width <= 0.0)
916       {
917 	  fnt->is_outlined = 0;
918 	  fnt->outline_width = 0.0;
919       }
920     else
921       {
922 	  fnt->is_outlined = 1;
923 	  fnt->outline_width = width;
924       }
925     return 1;
926 }
927 
928 RL2_DECLARE rl2GraphicsBitmapPtr
rl2_graph_create_bitmap(unsigned char * rgbaArray,int width,int height)929 rl2_graph_create_bitmap (unsigned char *rgbaArray, int width, int height)
930 {
931 /* creating a bitmap */
932     RL2GraphBitmapPtr bmp;
933 
934     if (rgbaArray == NULL)
935 	return NULL;
936 
937     adjust_for_endianness (rgbaArray, width, height);
938     bmp = malloc (sizeof (RL2GraphBitmap));
939     if (bmp == NULL)
940 	return NULL;
941     bmp->width = width;
942     bmp->height = height;
943     bmp->rgba = rgbaArray;
944     bmp->bitmap =
945 	cairo_image_surface_create_for_data (rgbaArray, CAIRO_FORMAT_ARGB32,
946 					     width, height, width * 4);
947     bmp->pattern = cairo_pattern_create_for_surface (bmp->bitmap);
948     return (rl2GraphicsBitmapPtr) bmp;
949 }
950 
951 RL2_DECLARE void
rl2_graph_destroy_bitmap(rl2GraphicsBitmapPtr bitmap)952 rl2_graph_destroy_bitmap (rl2GraphicsBitmapPtr bitmap)
953 {
954 /* destroying a bitmap */
955     RL2GraphBitmapPtr bmp = (RL2GraphBitmapPtr) bitmap;
956 
957     if (bmp == NULL)
958 	return;
959 
960     cairo_pattern_destroy (bmp->pattern);
961     cairo_surface_destroy (bmp->bitmap);
962     if (bmp->rgba != NULL)
963 	free (bmp->rgba);
964     free (bmp);
965 }
966 
967 static void
set_current_brush(RL2GraphContextPtr ctx)968 set_current_brush (RL2GraphContextPtr ctx)
969 {
970 /* setting up the current Brush */
971     cairo_t *cairo;
972     if (ctx->type == RL2_SURFACE_PDF)
973 	cairo = ctx->clip_cairo;
974     else
975 	cairo = ctx->cairo;
976     if (ctx->current_brush.is_solid_color)
977       {
978 	  /* using a Solid Color Brush */
979 	  cairo_set_source_rgba (cairo, ctx->current_brush.red,
980 				 ctx->current_brush.green,
981 				 ctx->current_brush.blue,
982 				 ctx->current_brush.alpha);
983       }
984     else if (ctx->current_brush.is_linear_gradient)
985       {
986 	  /* using a Linear Gradient Brush */
987 	  cairo_pattern_t *pattern =
988 	      cairo_pattern_create_linear (ctx->current_brush.x0,
989 					   ctx->current_brush.y0,
990 					   ctx->current_brush.x1,
991 					   ctx->current_brush.y1);
992 	  cairo_pattern_add_color_stop_rgba (pattern, 0.0,
993 					     ctx->current_brush.red,
994 					     ctx->current_brush.green,
995 					     ctx->current_brush.blue,
996 					     ctx->current_brush.alpha);
997 	  cairo_pattern_add_color_stop_rgba (pattern, 1.0,
998 					     ctx->current_brush.red2,
999 					     ctx->current_brush.green2,
1000 					     ctx->current_brush.blue2,
1001 					     ctx->current_brush.alpha2);
1002 	  cairo_set_source (cairo, pattern);
1003 	  cairo_pattern_destroy (pattern);
1004       }
1005     else if (ctx->current_brush.is_pattern)
1006       {
1007 	  /* using a Pattern Brush */
1008 	  cairo_set_source (cairo, ctx->current_brush.pattern);
1009       }
1010 }
1011 
1012 static void
set_current_pen(RL2GraphContextPtr ctx)1013 set_current_pen (RL2GraphContextPtr ctx)
1014 {
1015 /* setting up the current Pen */
1016     cairo_t *cairo;
1017     if (ctx->type == RL2_SURFACE_PDF)
1018 	cairo = ctx->clip_cairo;
1019     else
1020 	cairo = ctx->cairo;
1021     cairo_set_line_width (cairo, ctx->current_pen.width);
1022     cairo_set_source_rgba (cairo, ctx->current_pen.red,
1023 			   ctx->current_pen.green, ctx->current_pen.blue,
1024 			   ctx->current_pen.alpha);
1025     cairo_set_line_cap (cairo, CAIRO_LINE_CAP_BUTT);
1026     cairo_set_line_join (cairo, CAIRO_LINE_JOIN_MITER);
1027     cairo_set_dash (cairo, ctx->current_pen.lengths,
1028 		    ctx->current_pen.lengths_count, 0.0);
1029 }
1030 
1031 RL2_DECLARE int
rl2_graph_draw_rectangle(rl2GraphicsContextPtr context,double x,double y,double width,double height)1032 rl2_graph_draw_rectangle (rl2GraphicsContextPtr context, double x, double y,
1033 			  double width, double height)
1034 {
1035 /* Drawing a filled rectangle */
1036     cairo_t *cairo;
1037     RL2GraphContextPtr ctx = (RL2GraphContextPtr) context;
1038     if (ctx == NULL)
1039 	return 0;
1040     if (ctx->type == RL2_SURFACE_PDF)
1041 	cairo = ctx->clip_cairo;
1042     else
1043 	cairo = ctx->cairo;
1044 
1045     cairo_rectangle (cairo, x, y, width, height);
1046     set_current_brush (ctx);
1047     cairo_fill_preserve (cairo);
1048     set_current_pen (ctx);
1049     cairo_stroke (cairo);
1050     return 1;
1051 }
1052 
1053 RL2_DECLARE int
rl2_graph_draw_rounded_rectangle(rl2GraphicsContextPtr context,double x,double y,double width,double height,double radius)1054 rl2_graph_draw_rounded_rectangle (rl2GraphicsContextPtr context, double x,
1055 				  double y, double width, double height,
1056 				  double radius)
1057 {
1058 /* Drawing a filled rectangle with rounded corners */
1059     cairo_t *cairo;
1060     double degrees = M_PI / 180.0;
1061     RL2GraphContextPtr ctx = (RL2GraphContextPtr) context;
1062     if (ctx == NULL)
1063 	return 0;
1064     if (ctx->type == RL2_SURFACE_PDF)
1065 	cairo = ctx->clip_cairo;
1066     else
1067 	cairo = ctx->cairo;
1068 
1069     cairo_new_sub_path (cairo);
1070     cairo_arc (cairo, x + width - radius, y + radius, radius,
1071 	       -90 * degrees, 0 * degrees);
1072     cairo_arc (cairo, x + width - radius, y + height - radius, radius,
1073 	       0 * degrees, 90 * degrees);
1074     cairo_arc (cairo, x + radius, y + height - radius, radius,
1075 	       90 * degrees, 180 * degrees);
1076     cairo_arc (cairo, x + radius, y + radius, radius, 180 * degrees,
1077 	       270 * degrees);
1078     cairo_close_path (cairo);
1079     set_current_brush (ctx);
1080     cairo_fill_preserve (cairo);
1081     set_current_pen (ctx);
1082     cairo_stroke (cairo);
1083     return 1;
1084 }
1085 
1086 RL2_DECLARE int
rl2_graph_draw_ellipse(rl2GraphicsContextPtr context,double x,double y,double width,double height)1087 rl2_graph_draw_ellipse (rl2GraphicsContextPtr context, double x, double y,
1088 			double width, double height)
1089 {
1090 /* Drawing a filled ellipse */
1091     cairo_t *cairo;
1092     RL2GraphContextPtr ctx = (RL2GraphContextPtr) context;
1093     if (ctx == NULL)
1094 	return 0;
1095     if (ctx->type == RL2_SURFACE_PDF)
1096 	cairo = ctx->clip_cairo;
1097     else
1098 	cairo = ctx->cairo;
1099 
1100     cairo_save (cairo);
1101     cairo_translate (cairo, x + (width / 2.0), y + (height / 2.0));
1102     cairo_scale (cairo, width / 2.0, height / 2.0);
1103     cairo_arc (cairo, 0.0, 0.0, 1.0, 0.0, 2.0 * M_PI);
1104     cairo_restore (cairo);
1105     set_current_brush (ctx);
1106     cairo_fill_preserve (cairo);
1107     set_current_pen (ctx);
1108     cairo_stroke (cairo);
1109     return 1;
1110 }
1111 
1112 RL2_DECLARE int
rl2_graph_draw_circle_sector(rl2GraphicsContextPtr context,double center_x,double center_y,double radius,double from_angle,double to_angle)1113 rl2_graph_draw_circle_sector (rl2GraphicsContextPtr context, double center_x,
1114 			      double center_y, double radius, double from_angle,
1115 			      double to_angle)
1116 {
1117 /* drawing a filled circular sector */
1118     cairo_t *cairo;
1119     RL2GraphContextPtr ctx = (RL2GraphContextPtr) context;
1120     if (ctx == NULL)
1121 	return 0;
1122     if (ctx->type == RL2_SURFACE_PDF)
1123 	cairo = ctx->clip_cairo;
1124     else
1125 	cairo = ctx->cairo;
1126 
1127     cairo_move_to (cairo, center_x, center_y);
1128     cairo_arc (cairo, center_x, center_y, radius, from_angle, to_angle);
1129     cairo_line_to (cairo, center_x, center_y);
1130     set_current_brush (ctx);
1131     cairo_fill_preserve (cairo);
1132     set_current_pen (ctx);
1133     cairo_stroke (cairo);
1134     return 1;
1135 }
1136 
1137 RL2_DECLARE int
rl2_graph_stroke_line(rl2GraphicsContextPtr context,double x0,double y0,double x1,double y1)1138 rl2_graph_stroke_line (rl2GraphicsContextPtr context, double x0, double y0,
1139 		       double x1, double y1)
1140 {
1141 /* Stroking a line */
1142     cairo_t *cairo;
1143     RL2GraphContextPtr ctx = (RL2GraphContextPtr) context;
1144     if (ctx == NULL)
1145 	return 0;
1146     if (ctx->type == RL2_SURFACE_PDF)
1147 	cairo = ctx->clip_cairo;
1148     else
1149 	cairo = ctx->cairo;
1150 
1151     cairo_move_to (cairo, x0, y0);
1152     cairo_line_to (cairo, x1, y1);
1153     set_current_pen (ctx);
1154     cairo_stroke (cairo);
1155     return 1;
1156 }
1157 
1158 RL2_DECLARE int
rl2_graph_move_to_point(rl2GraphicsContextPtr context,double x,double y)1159 rl2_graph_move_to_point (rl2GraphicsContextPtr context, double x, double y)
1160 {
1161 /* Moving to a Path Point */
1162     cairo_t *cairo;
1163     RL2GraphContextPtr ctx = (RL2GraphContextPtr) context;
1164     if (ctx == NULL)
1165 	return 0;
1166     if (ctx->type == RL2_SURFACE_PDF)
1167 	cairo = ctx->clip_cairo;
1168     else
1169 	cairo = ctx->cairo;
1170     cairo_move_to (cairo, x, y);
1171     return 1;
1172 }
1173 
1174 RL2_DECLARE int
rl2_graph_add_line_to_path(rl2GraphicsContextPtr context,double x,double y)1175 rl2_graph_add_line_to_path (rl2GraphicsContextPtr context, double x, double y)
1176 {
1177 /* Adding a Line to a Path */
1178     cairo_t *cairo;
1179     RL2GraphContextPtr ctx = (RL2GraphContextPtr) context;
1180     if (ctx == NULL)
1181 	return 0;
1182     if (ctx->type == RL2_SURFACE_PDF)
1183 	cairo = ctx->clip_cairo;
1184     else
1185 	cairo = ctx->cairo;
1186     cairo_line_to (cairo, x, y);
1187     return 1;
1188 }
1189 
1190 RL2_DECLARE int
rl2_graph_close_subpath(rl2GraphicsContextPtr context)1191 rl2_graph_close_subpath (rl2GraphicsContextPtr context)
1192 {
1193 /* Closing a SubPath */
1194     cairo_t *cairo;
1195     RL2GraphContextPtr ctx = (RL2GraphContextPtr) context;
1196     if (ctx == NULL)
1197 	return 0;
1198     if (ctx->type == RL2_SURFACE_PDF)
1199 	cairo = ctx->clip_cairo;
1200     else
1201 	cairo = ctx->cairo;
1202     cairo_close_path (cairo);
1203     return 1;
1204 }
1205 
1206 RL2_DECLARE int
rl2_graph_stroke_path(rl2GraphicsContextPtr context,int preserve)1207 rl2_graph_stroke_path (rl2GraphicsContextPtr context, int preserve)
1208 {
1209 /* Stroking a path */
1210     cairo_t *cairo;
1211     RL2GraphContextPtr ctx = (RL2GraphContextPtr) context;
1212     if (ctx == NULL)
1213 	return 0;
1214     if (ctx->type == RL2_SURFACE_PDF)
1215 	cairo = ctx->clip_cairo;
1216     else
1217 	cairo = ctx->cairo;
1218 
1219     set_current_pen (ctx);
1220     if (preserve == RL2_PRESERVE_PATH)
1221 	cairo_stroke_preserve (cairo);
1222     else
1223 	cairo_stroke (cairo);
1224     return 1;
1225 }
1226 
1227 RL2_DECLARE int
rl2_graph_fill_path(rl2GraphicsContextPtr context,int preserve)1228 rl2_graph_fill_path (rl2GraphicsContextPtr context, int preserve)
1229 {
1230 /* Filling a path */
1231     cairo_t *cairo;
1232     RL2GraphContextPtr ctx = (RL2GraphContextPtr) context;
1233     if (ctx == NULL)
1234 	return 0;
1235     if (ctx->type == RL2_SURFACE_PDF)
1236 	cairo = ctx->clip_cairo;
1237     else
1238 	cairo = ctx->cairo;
1239 
1240     set_current_brush (ctx);
1241     if (preserve == RL2_PRESERVE_PATH)
1242 	cairo_fill_preserve (cairo);
1243     else
1244 	cairo_fill (cairo);
1245     return 1;
1246 }
1247 
1248 RL2_DECLARE int
rl2_graph_get_text_extent(rl2GraphicsContextPtr context,const char * text,double * pre_x,double * pre_y,double * width,double * height,double * post_x,double * post_y)1249 rl2_graph_get_text_extent (rl2GraphicsContextPtr context, const char *text,
1250 			   double *pre_x, double *pre_y, double *width,
1251 			   double *height, double *post_x, double *post_y)
1252 {
1253 /* measuring the text extent (using the current font) */
1254     cairo_t *cairo;
1255     cairo_text_extents_t extents;
1256     RL2GraphContextPtr ctx = (RL2GraphContextPtr) context;
1257 
1258     if (ctx == NULL)
1259 	return 0;
1260     if (ctx->type == RL2_SURFACE_PDF)
1261 	cairo = ctx->clip_cairo;
1262     else
1263 	cairo = ctx->cairo;
1264 
1265     cairo_text_extents (cairo, text, &extents);
1266     *pre_x = extents.x_bearing;
1267     *pre_y = extents.y_bearing;
1268     *width = extents.width;
1269     *height = extents.height;
1270     *post_x = extents.x_advance;
1271     *post_y = extents.y_advance;
1272     return 1;
1273 }
1274 
1275 RL2_DECLARE int
rl2_graph_draw_text(rl2GraphicsContextPtr context,const char * text,double x,double y,double angle)1276 rl2_graph_draw_text (rl2GraphicsContextPtr context, const char *text, double x,
1277 		     double y, double angle)
1278 {
1279 /* drawing a text string (using the current font) */
1280     cairo_t *cairo;
1281     RL2GraphContextPtr ctx = (RL2GraphContextPtr) context;
1282 
1283     if (ctx == NULL)
1284 	return 0;
1285     if (ctx->type == RL2_SURFACE_PDF)
1286 	cairo = ctx->clip_cairo;
1287     else
1288 	cairo = ctx->cairo;
1289 
1290     cairo_save (cairo);
1291     cairo_translate (cairo, x, y);
1292     cairo_rotate (cairo, angle);
1293     if (ctx->is_font_outlined)
1294       {
1295 	  /* outlined font */
1296 	  cairo_move_to (cairo, 0.0, 0.0);
1297 	  cairo_text_path (cairo, text);
1298 	  cairo_set_source_rgba (cairo, ctx->font_red, ctx->font_green,
1299 				 ctx->font_blue, ctx->font_alpha);
1300 	  cairo_fill_preserve (cairo);
1301 	  cairo_set_source_rgba (cairo, 1.0, 1.0, 1.0, ctx->font_alpha);
1302 	  cairo_set_line_width (cairo, ctx->font_outline_width);
1303 	  cairo_stroke (cairo);
1304       }
1305     else
1306       {
1307 	  /* no outline */
1308 	  cairo_set_source_rgba (cairo, ctx->font_red, ctx->font_green,
1309 				 ctx->font_blue, ctx->font_alpha);
1310 	  cairo_move_to (cairo, 0.0, 0.0);
1311 	  cairo_show_text (cairo, text);
1312       }
1313     cairo_restore (cairo);
1314     return 1;
1315 }
1316 
1317 RL2_DECLARE int
rl2_graph_draw_bitmap(rl2GraphicsContextPtr context,rl2GraphicsBitmapPtr bitmap,int x,int y)1318 rl2_graph_draw_bitmap (rl2GraphicsContextPtr context,
1319 		       rl2GraphicsBitmapPtr bitmap, int x, int y)
1320 {
1321 /* drawing a symbol bitmap */
1322     cairo_t *cairo;
1323     cairo_surface_t *surface;
1324     RL2GraphBitmapPtr bmp = (RL2GraphBitmapPtr) bitmap;
1325     RL2GraphContextPtr ctx = (RL2GraphContextPtr) context;
1326 
1327     if (ctx == NULL)
1328 	return 0;
1329     if (bmp == NULL)
1330 	return 0;
1331     if (ctx->type == RL2_SURFACE_PDF)
1332       {
1333 	  surface = ctx->clip_surface;
1334 	  cairo = ctx->clip_cairo;
1335       }
1336     else
1337       {
1338 	  surface = ctx->surface;
1339 	  cairo = ctx->cairo;
1340       }
1341 
1342     cairo_save (cairo);
1343     cairo_scale (cairo, 1, 1);
1344     cairo_translate (cairo, x, y);
1345     cairo_set_source (cairo, bmp->pattern);
1346     cairo_rectangle (cairo, 0, 0, bmp->width, bmp->height);
1347     cairo_fill (cairo);
1348     cairo_restore (cairo);
1349     cairo_surface_flush (surface);
1350     return 1;
1351 }
1352 
1353 RL2_DECLARE int
rl2_graph_draw_rescaled_bitmap(rl2GraphicsContextPtr context,rl2GraphicsBitmapPtr bitmap,double scale_x,double scale_y,int x,int y)1354 rl2_graph_draw_rescaled_bitmap (rl2GraphicsContextPtr context,
1355 				rl2GraphicsBitmapPtr bitmap, double scale_x,
1356 				double scale_y, int x, int y)
1357 {
1358 /* drawing a rescaled bitmap */
1359     cairo_t *cairo;
1360     cairo_surface_t *surface;
1361     RL2GraphBitmapPtr bmp = (RL2GraphBitmapPtr) bitmap;
1362     RL2GraphContextPtr ctx = (RL2GraphContextPtr) context;
1363 
1364     if (ctx == NULL)
1365 	return 0;
1366     if (bmp == NULL)
1367 	return 0;
1368     if (ctx->type == RL2_SURFACE_PDF)
1369       {
1370 	  surface = ctx->clip_surface;
1371 	  cairo = ctx->clip_cairo;
1372       }
1373     else
1374       {
1375 	  surface = ctx->surface;
1376 	  cairo = ctx->cairo;
1377       }
1378 
1379     cairo_save (cairo);
1380     cairo_translate (cairo, x, y);
1381     cairo_scale (cairo, scale_x, scale_y);
1382     cairo_set_source (cairo, bmp->pattern);
1383     cairo_paint (cairo);
1384     cairo_restore (cairo);
1385     cairo_surface_flush (surface);
1386     return 1;
1387 }
1388 
1389 RL2_DECLARE unsigned char *
rl2_graph_get_context_rgb_array(rl2GraphicsContextPtr context)1390 rl2_graph_get_context_rgb_array (rl2GraphicsContextPtr context)
1391 {
1392 /* creating an RGB buffer from the given Context */
1393     int width;
1394     int height;
1395     int x;
1396     int y;
1397     unsigned char *p_in;
1398     unsigned char *p_out;
1399     unsigned char *rgb;
1400     int little_endian = rl2cr_endian_arch ();
1401     RL2GraphContextPtr ctx = (RL2GraphContextPtr) context;
1402 
1403     if (ctx == NULL)
1404 	return NULL;
1405 
1406     width = cairo_image_surface_get_width (ctx->surface);
1407     height = cairo_image_surface_get_height (ctx->surface);
1408     rgb = malloc (width * height * 3);
1409     if (rgb == NULL)
1410 	return NULL;
1411 
1412     p_in = cairo_image_surface_get_data (ctx->surface);
1413     p_out = rgb;
1414     for (y = 0; y < height; y++)
1415       {
1416 	  for (x = 0; x < width; x++)
1417 	    {
1418 		unsigned char r;
1419 		unsigned char g;
1420 		unsigned char b;
1421 		if (little_endian)
1422 		  {
1423 		      b = *p_in++;
1424 		      g = *p_in++;
1425 		      r = *p_in++;
1426 		      p_in++;	/* skipping Alpha */
1427 		  }
1428 		else
1429 		  {
1430 		      p_in++;	/* skipping Alpha */
1431 		      r = *p_in++;
1432 		      g = *p_in++;
1433 		      b = *p_in++;
1434 		  }
1435 		*p_out++ = r;
1436 		*p_out++ = g;
1437 		*p_out++ = b;
1438 	    }
1439       }
1440     return rgb;
1441 }
1442 
1443 RL2_DECLARE unsigned char *
rl2_graph_get_context_alpha_array(rl2GraphicsContextPtr context)1444 rl2_graph_get_context_alpha_array (rl2GraphicsContextPtr context)
1445 {
1446 /* creating an Alpha buffer from the given Context */
1447     int width;
1448     int height;
1449     int x;
1450     int y;
1451     unsigned char *p_in;
1452     unsigned char *p_out;
1453     unsigned char *alpha;
1454     int little_endian = rl2cr_endian_arch ();
1455     RL2GraphContextPtr ctx = (RL2GraphContextPtr) context;
1456 
1457     if (ctx == NULL)
1458 	return NULL;
1459 
1460     width = cairo_image_surface_get_width (ctx->surface);
1461     height = cairo_image_surface_get_height (ctx->surface);
1462     alpha = malloc (width * height);
1463     if (alpha == NULL)
1464 	return NULL;
1465 
1466     p_in = cairo_image_surface_get_data (ctx->surface);
1467     p_out = alpha;
1468     for (y = 0; y < height; y++)
1469       {
1470 	  for (x = 0; x < width; x++)
1471 	    {
1472 		if (little_endian)
1473 		  {
1474 		      p_in += 3;	/* skipping RGB */
1475 		      *p_out++ = *p_in++;
1476 		  }
1477 		else
1478 		  {
1479 		      *p_out++ = *p_in++;
1480 		      p_in += 3;	/* skipping RGB */
1481 		  }
1482 	    }
1483       }
1484     return alpha;
1485 }
1486 
1487 RL2_DECLARE int
rl2_rgba_to_pdf(unsigned int width,unsigned int height,unsigned char * rgba,unsigned char ** pdf,int * pdf_size)1488 rl2_rgba_to_pdf (unsigned int width, unsigned int height,
1489 		 unsigned char *rgba, unsigned char **pdf, int *pdf_size)
1490 {
1491 /* attempting to create an RGB PDF map */
1492     rl2MemPdfPtr mem = NULL;
1493     rl2GraphicsContextPtr ctx = NULL;
1494     rl2GraphicsBitmapPtr bmp = NULL;
1495     int dpi;
1496     double w_150 = (double) width / 150.0;
1497     double h_150 = (double) height / 150.0;
1498     double w_300 = (double) width / 300.0;
1499     double h_300 = (double) height / 300.0;
1500     double w_600 = (double) width / 600.0;
1501     double h_600 = (double) height / 600.0;
1502     double page_width;
1503     double page_height;
1504 
1505     if (w_150 <= 6.3 && h_150 <= 9.7)
1506       {
1507 	  /* A4 portrait - 150 DPI */
1508 	  page_width = 8.3;
1509 	  page_height = 11.7;
1510 	  dpi = 150;
1511       }
1512     else if (w_150 <= 9.7 && h_150 < 6.3)
1513       {
1514 	  /* A4 landscape - 150 DPI */
1515 	  page_width = 11.7;
1516 	  page_height = 8.3;;
1517 	  dpi = 150;
1518       }
1519     else if (w_300 <= 6.3 && h_300 <= 9.7)
1520       {
1521 	  /* A4 portrait - 300 DPI */
1522 	  page_width = 8.3;
1523 	  page_height = 11.7;;
1524 	  dpi = 300;
1525       }
1526     else if (w_300 <= 9.7 && h_300 < 6.3)
1527       {
1528 	  /* A4 landscape - 300 DPI */
1529 	  page_width = 11.7;
1530 	  page_height = 8.3;;
1531 	  dpi = 300;
1532       }
1533     else if (w_600 <= 6.3 && h_600 <= 9.7)
1534       {
1535 	  /* A4 portrait - 600 DPI */
1536 	  page_width = 8.3;
1537 	  page_height = 11.7;;
1538 	  dpi = 600;
1539       }
1540     else
1541       {
1542 	  /* A4 landscape - 600 DPI */
1543 	  page_width = 11.7;
1544 	  page_height = 8.3;;
1545 	  dpi = 600;
1546       }
1547 
1548     mem = rl2_create_mem_pdf_target ();
1549     if (mem == NULL)
1550 	goto error;
1551 
1552     ctx =
1553 	rl2_graph_create_mem_pdf_context (mem, dpi, page_width, page_height,
1554 					  1.0, 1.0);
1555     if (ctx == NULL)
1556 	goto error;
1557     bmp = rl2_graph_create_bitmap (rgba, width, height);
1558     rgba = NULL;
1559     if (bmp == NULL)
1560 	goto error;
1561 /* rendering the Bitmap */
1562     if (ctx != NULL)
1563 	rl2_graph_draw_bitmap (ctx, bmp, 0, 0);
1564     rl2_graph_destroy_bitmap (bmp);
1565     rl2_graph_destroy_context (ctx);
1566 /* retrieving the PDF memory block */
1567     if (rl2_get_mem_pdf_buffer (mem, pdf, pdf_size) != RL2_OK)
1568 	goto error;
1569     rl2_destroy_mem_pdf_target (mem);
1570 
1571     return RL2_OK;
1572 
1573   error:
1574     if (bmp != NULL)
1575 	rl2_graph_destroy_bitmap (bmp);
1576     if (ctx != NULL)
1577 	rl2_graph_destroy_context (ctx);
1578     if (mem != NULL)
1579 	rl2_destroy_mem_pdf_target (mem);
1580     return RL2_ERROR;
1581 }
1582 
1583 RL2_DECLARE int
rl2_gray_pdf(unsigned int width,unsigned int height,unsigned char ** pdf,int * pdf_size)1584 rl2_gray_pdf (unsigned int width, unsigned int height, unsigned char **pdf,
1585 	      int *pdf_size)
1586 {
1587 /* attempting to create an all-Gray PDF */
1588     rl2MemPdfPtr mem = NULL;
1589     rl2GraphicsContextPtr ctx = NULL;
1590     int dpi;
1591     double w_150 = (double) width / 150.0;
1592     double h_150 = (double) height / 150.0;
1593     double w_300 = (double) width / 300.0;
1594     double h_300 = (double) height / 300.0;
1595     double w_600 = (double) width / 600.0;
1596     double h_600 = (double) height / 600.0;
1597     double page_width;
1598     double page_height;
1599 
1600     if (w_150 <= 6.3 && h_150 <= 9.7)
1601       {
1602 	  /* A4 portrait - 150 DPI */
1603 	  page_width = 8.3;
1604 	  page_height = 11.7;
1605 	  dpi = 150;
1606       }
1607     else if (w_150 <= 9.7 && h_150 < 6.3)
1608       {
1609 	  /* A4 landscape - 150 DPI */
1610 	  page_width = 11.7;
1611 	  page_height = 8.3;;
1612 	  dpi = 150;
1613       }
1614     else if (w_300 <= 6.3 && h_300 <= 9.7)
1615       {
1616 	  /* A4 portrait - 300 DPI */
1617 	  page_width = 8.3;
1618 	  page_height = 11.7;;
1619 	  dpi = 300;
1620       }
1621     else if (w_300 <= 9.7 && h_300 < 6.3)
1622       {
1623 	  /* A4 landscape - 300 DPI */
1624 	  page_width = 11.7;
1625 	  page_height = 8.3;;
1626 	  dpi = 300;
1627       }
1628     else if (w_600 <= 6.3 && h_600 <= 9.7)
1629       {
1630 	  /* A4 portrait - 600 DPI */
1631 	  page_width = 8.3;
1632 	  page_height = 11.7;;
1633 	  dpi = 600;
1634       }
1635     else
1636       {
1637 	  /* A4 landscape - 600 DPI */
1638 	  page_width = 11.7;
1639 	  page_height = 8.3;;
1640 	  dpi = 600;
1641       }
1642 
1643     mem = rl2_create_mem_pdf_target ();
1644     if (mem == NULL)
1645 	goto error;
1646 
1647     ctx =
1648 	rl2_graph_create_mem_pdf_context (mem, dpi, page_width, page_height,
1649 					  1.0, 1.0);
1650     if (ctx == NULL)
1651 	goto error;
1652     rl2_graph_set_pen (ctx, 255, 0, 0, 255, 2.0, RL2_PENSTYLE_SOLID);
1653     rl2_graph_set_brush (ctx, 128, 128, 128, 255);
1654     rl2_graph_draw_rounded_rectangle (ctx, 0, 0, width, height, width / 10.0);
1655 
1656     rl2_graph_destroy_context (ctx);
1657 /* retrieving the PDF memory block */
1658     if (rl2_get_mem_pdf_buffer (mem, pdf, pdf_size) != RL2_OK)
1659 	goto error;
1660     rl2_destroy_mem_pdf_target (mem);
1661 
1662     return RL2_OK;
1663 
1664   error:
1665     if (ctx != NULL)
1666 	rl2_graph_destroy_context (ctx);
1667     if (mem != NULL)
1668 	rl2_destroy_mem_pdf_target (mem);
1669     return RL2_ERROR;
1670 }
1671 
1672 RL2_DECLARE rl2MemPdfPtr
rl2_create_mem_pdf_target(void)1673 rl2_create_mem_pdf_target (void)
1674 {
1675 /* creating an initially empty in-memory PDF target */
1676     rl2PrivMemPdfPtr mem = malloc (sizeof (rl2PrivMemPdf));
1677     if (mem == NULL)
1678 	return NULL;
1679     mem->write_offset = 0;
1680     mem->size = 64 * 1024;
1681     mem->buffer = malloc (mem->size);
1682     if (mem->buffer == NULL)
1683       {
1684 	  free (mem);
1685 	  return NULL;
1686       }
1687     return (rl2MemPdfPtr) mem;
1688 }
1689 
1690 RL2_DECLARE void
rl2_destroy_mem_pdf_target(rl2MemPdfPtr target)1691 rl2_destroy_mem_pdf_target (rl2MemPdfPtr target)
1692 {
1693 /* memory cleanup - destroying an in-memory PDF target */
1694     rl2PrivMemPdfPtr mem = (rl2PrivMemPdfPtr) target;
1695     if (mem == NULL)
1696 	return;
1697     if (mem->buffer != NULL)
1698 	free (mem->buffer);
1699     free (mem);
1700 }
1701 
1702 RL2_DECLARE int
rl2_get_mem_pdf_buffer(rl2MemPdfPtr target,unsigned char ** buffer,int * size)1703 rl2_get_mem_pdf_buffer (rl2MemPdfPtr target, unsigned char **buffer, int *size)
1704 {
1705 /* exporting the internal buffer */
1706     rl2PrivMemPdfPtr mem = (rl2PrivMemPdfPtr) target;
1707     if (mem == NULL)
1708 	return RL2_ERROR;
1709     if (mem->buffer == NULL)
1710 	return RL2_ERROR;
1711     *buffer = mem->buffer;
1712     mem->buffer = NULL;
1713     *size = mem->write_offset;
1714     return RL2_OK;
1715 }
1716