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