1 /* GIMP - The GNU Image Manipulation Program
2  * Copyright (C) 1995 Spencer Kimball and Peter Mattis
3  *
4  * gimp-cairo.c
5  * Copyright (C) 2010-2012  Michael Natterer <mitch@gimp.org>
6  *
7  * Some code here is based on code from librsvg that was originally
8  * written by Raph Levien <raph@artofcode.com> for Gill.
9  *
10  * This program is free software: you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 3 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
22  */
23 
24 #include "config.h"
25 
26 #include <cairo.h>
27 #include <gegl.h>
28 #include <gdk-pixbuf/gdk-pixbuf.h>
29 
30 #include "libgimpmath/gimpmath.h"
31 #include "libgimpcolor/gimpcolor.h"
32 
33 #include "core-types.h"
34 
35 #include "gimp-cairo.h"
36 
37 
38 #define REV (2.0 * G_PI)
39 
40 
41 static cairo_user_data_key_t surface_data_key = { 0, };
42 
43 
44 cairo_pattern_t *
gimp_cairo_pattern_create_stipple(const GimpRGB * fg,const GimpRGB * bg,gint index,gdouble offset_x,gdouble offset_y)45 gimp_cairo_pattern_create_stipple (const GimpRGB *fg,
46                                    const GimpRGB *bg,
47                                    gint           index,
48                                    gdouble        offset_x,
49                                    gdouble        offset_y)
50 {
51   cairo_surface_t *surface;
52   cairo_pattern_t *pattern;
53   guchar          *data;
54   guchar          *d;
55   guchar           fg_r, fg_g, fg_b, fg_a;
56   guchar           bg_r, bg_g, bg_b, bg_a;
57   gint             x, y;
58 
59   g_return_val_if_fail (fg != NULL, NULL);
60   g_return_val_if_fail (bg != NULL, NULL);
61 
62   data = g_malloc (8 * 8 * 4);
63 
64   gimp_rgba_get_uchar (fg, &fg_r, &fg_g, &fg_b, &fg_a);
65   gimp_rgba_get_uchar (bg, &bg_r, &bg_g, &bg_b, &bg_a);
66 
67   d = data;
68 
69   for (y = 0; y < 8; y++)
70     {
71       for (x = 0; x < 8; x++)
72         {
73           if ((x + y + index) % 8 >= 4)
74             GIMP_CAIRO_ARGB32_SET_PIXEL (d, fg_r, fg_g, fg_b, fg_a);
75           else
76             GIMP_CAIRO_ARGB32_SET_PIXEL (d, bg_r, bg_g, bg_b, bg_a);
77 
78           d += 4;
79         }
80     }
81 
82   surface = cairo_image_surface_create_for_data (data,
83                                                  CAIRO_FORMAT_ARGB32,
84                                                  8, 8, 8 * 4);
85   cairo_surface_set_user_data (surface, &surface_data_key,
86                                data, (cairo_destroy_func_t) g_free);
87 
88   pattern = cairo_pattern_create_for_surface (surface);
89   cairo_pattern_set_extend (pattern, CAIRO_EXTEND_REPEAT);
90 
91   cairo_surface_destroy (surface);
92 
93   if (offset_x != 0.0 || offset_y != 0.0)
94     {
95       cairo_matrix_t matrix;
96 
97       cairo_matrix_init_translate (&matrix,
98                                    fmod (offset_x, 8),
99                                    fmod (offset_y, 8));
100       cairo_pattern_set_matrix (pattern, &matrix);
101     }
102 
103   return pattern;
104 }
105 
106 void
gimp_cairo_arc(cairo_t * cr,gdouble center_x,gdouble center_y,gdouble radius,gdouble start_angle,gdouble slice_angle)107 gimp_cairo_arc (cairo_t *cr,
108                 gdouble  center_x,
109                 gdouble  center_y,
110                 gdouble  radius,
111                 gdouble  start_angle,
112                 gdouble  slice_angle)
113 {
114   g_return_if_fail (cr != NULL);
115 
116   if (slice_angle >= 0)
117     {
118       cairo_arc_negative (cr, center_x, center_y, radius,
119                           - start_angle,
120                           - start_angle - slice_angle);
121     }
122   else
123     {
124       cairo_arc (cr, center_x, center_y, radius,
125                  - start_angle,
126                  - start_angle - slice_angle);
127     }
128 }
129 
130 void
gimp_cairo_rounded_rectangle(cairo_t * cr,gdouble x,gdouble y,gdouble width,gdouble height,gdouble corner_radius)131 gimp_cairo_rounded_rectangle (cairo_t *cr,
132                               gdouble  x,
133                               gdouble  y,
134                               gdouble  width,
135                               gdouble  height,
136                               gdouble  corner_radius)
137 {
138   g_return_if_fail (cr != NULL);
139 
140   if (width < 0.0)
141     {
142       x     += width;
143       width  = -width;
144     }
145 
146   if (height < 0.0)
147     {
148       y      += height;
149       height  = -height;
150     }
151 
152   corner_radius = CLAMP (corner_radius, 0.0, MIN (width, height) / 2.0);
153 
154   if (corner_radius == 0.0)
155     {
156       cairo_rectangle (cr, x, y, width, height);
157 
158       return;
159     }
160 
161   cairo_new_sub_path (cr);
162 
163   cairo_arc     (cr,
164                  x + corner_radius, y + corner_radius,
165                  corner_radius,
166                  0.50 * REV, 0.75 * REV);
167   cairo_line_to (cr,
168                  x + width - corner_radius, y);
169 
170   cairo_arc     (cr,
171                  x + width - corner_radius, y + corner_radius,
172                  corner_radius,
173                  0.75 * REV, 1.00 * REV);
174   cairo_line_to (cr,
175                  x + width, y + height - corner_radius);
176 
177   cairo_arc     (cr,
178                  x + width - corner_radius, y + height - corner_radius,
179                  corner_radius,
180                  0.00 * REV, 0.25 * REV);
181   cairo_line_to (cr,
182                  x + corner_radius, y + height);
183 
184   cairo_arc     (cr,
185                  x + corner_radius, y + height - corner_radius,
186                  corner_radius,
187                  0.25 * REV, 0.50 * REV);
188   cairo_line_to (cr,
189                  x, y + corner_radius);
190 
191   cairo_close_path (cr);
192 }
193 
194 void
gimp_cairo_segments(cairo_t * cr,GimpSegment * segs,gint n_segs)195 gimp_cairo_segments (cairo_t     *cr,
196                      GimpSegment *segs,
197                      gint         n_segs)
198 {
199   gint i;
200 
201   g_return_if_fail (cr != NULL);
202   g_return_if_fail (segs != NULL && n_segs > 0);
203 
204   for (i = 0; i < n_segs; i++)
205     {
206       if (segs[i].x1 == segs[i].x2)
207         {
208           cairo_move_to (cr, segs[i].x1 + 0.5, segs[i].y1 + 0.5);
209           cairo_line_to (cr, segs[i].x2 + 0.5, segs[i].y2 - 0.5);
210         }
211       else
212         {
213           cairo_move_to (cr, segs[i].x1 + 0.5, segs[i].y1 + 0.5);
214           cairo_line_to (cr, segs[i].x2 - 0.5, segs[i].y2 + 0.5);
215         }
216     }
217 }
218