1 /*
2 * go-marker.c :
3 *
4 * Copyright (C) 2003-2007 Emmanuel Pacaud <emmanuel.pacaud@lapp.in2p3.fr>
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License as
8 * published by the Free Software Foundation; either version 2 of the
9 * License, or (at your option) version 3.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
19 * USA
20 */
21
22 #include <goffice/goffice-config.h>
23 #include <goffice/goffice.h>
24
25 #include <glib/gi18n-lib.h>
26 #include <gsf/gsf-impl-utils.h>
27
28 /**
29 * GOMarkerShape:
30 * @GO_MARKER_NONE: no mark.
31 * @GO_MARKER_SQUARE: square.
32 * @GO_MARKER_DIAMOND: diamond.
33 * @GO_MARKER_TRIANGLE_DOWN: triangle down.
34 * @GO_MARKER_TRIANGLE_UP: triangle up.
35 * @GO_MARKER_TRIANGLE_RIGHT: triangle right.
36 * @GO_MARKER_TRIANGLE_LEFT: triangle left.
37 * @GO_MARKER_CIRCLE: circle.
38 * @GO_MARKER_X: X.
39 * @GO_MARKER_CROSS: cross.
40 * @GO_MARKER_ASTERISK: asterisk.
41 * @GO_MARKER_BAR: horizontal bar.
42 * @GO_MARKER_HALF_BAR: right half bar.
43 * @GO_MARKER_BUTTERFLY: butterfly.
44 * @GO_MARKER_HOURGLASS: hourglass.
45 * @GO_MARKER_LEFT_HALF_BAR: left half bar.
46 * @GO_MARKER_MAX: maximum value, should not occur.
47 **/
48
49 #define MARKER_DEFAULT_SIZE 5
50 #define MARKER_OUTLINE_WIDTH 0.1
51
52 struct _GOMarker {
53 GObject base;
54
55 int size;
56 GOMarkerShape shape;
57 GOColor outline_color;
58 GOColor fill_color;
59 };
60
61 typedef struct {
62 GObjectClass base;
63 } GOMarkerClass;
64
65 #define GO_MARKER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS((o), GO_TYPE_MARKER, GOMarkerClass))
66
67 static char const square_path[] = "M-1,-1 L-1,1 1,1 1,-1 z";
68 static char const diamond_path[] = "M0,-1 L1,0 0,1 -1,0 z";
69 static char const triangle_down_path[] = "M-1,-1 L1,-1 0,1 z";
70 static char const triangle_up_path[] = "M0,-1 L1,1 -1,1 z";
71 static char const triangle_right_path[] = "M-1,-1 L1,0 -1,1 z";
72 static char const triangle_left_path[] = "M1,-1 L-1,0 1,1 z";
73 static char const circle_path[] = "M1,0 C1,0.56 0.56,1 0,1 C-0.56,1 -1,0.56 -1,0 "
74 "C-1,-0.56 -0.56,-1 0,-1 C0.56,-1 1,-0.56 1,0 L1,0 z";
75 static char const x_path[] = "M1,1 L-1,-1 M1,-1 L-1,1";
76 static char const cross_path[] = "M1,0 L-1,0 M0,1 L0,-1";
77 static char const asterisk_path[] = "M0.7,0.7 L-0.7,-0.7 M0.7,-0.7 L-0.7,0.7 M1,0 L-1,0 M0,1 L0,-1";
78 static char const bar_path[] = "M-1 -0.2 L 1 -0.2 L 1 0.2 L -1 0.2 z";
79 static char const half_bar_path[] = "M0,-0.2 L1,-0.2 1,0.2 0,0.2 z";
80 static char const butterfly_path[] = "M-1,-1 L-1,1 0,0 1,1 1,-1 0,0 z";
81 static char const hourglass_path[] = "M-1,-1 L1,-1 0,0 1,1 -1,1 0,0 z";
82 static char const left_half_bar_path[] = "M0,-0.2 L-1,-0.2 -1,0.2 0,0.2 z";
83
84 typedef struct
85 {
86 char const *name;
87 char const *str;
88 char const *outline_path;
89 char const *fill_path;
90 } MarkerShape;
91
92 #define MAKE_MARKER_SHAPE(name, str, path) {name, str, path, path}
93 #define MAKE_MARKER_SQUARED(name, str, path) {name, str, path, square_path}
94
95 static MarkerShape const marker_shapes[GO_MARKER_MAX] = {
96 MAKE_MARKER_SHAPE ( N_("none"), "none", NULL),
97 MAKE_MARKER_SHAPE ( N_("square"), "square", square_path),
98 MAKE_MARKER_SHAPE ( N_("diamond"), "diamond", diamond_path),
99 MAKE_MARKER_SHAPE ( N_("triangle down"), "triangle-down", triangle_down_path),
100 MAKE_MARKER_SHAPE ( N_("triangle up"), "triangle-up", triangle_up_path),
101 MAKE_MARKER_SHAPE ( N_("triangle right"), "triangle-right", triangle_right_path),
102 MAKE_MARKER_SHAPE ( N_("triangle left"), "triangle-left", triangle_left_path),
103 MAKE_MARKER_SHAPE ( N_("circle"), "circle", circle_path),
104 MAKE_MARKER_SQUARED ( N_("x"), "x", x_path),
105 MAKE_MARKER_SQUARED ( N_("cross"), "cross", cross_path),
106 MAKE_MARKER_SQUARED ( N_("asterisk"), "asterisk", asterisk_path),
107 MAKE_MARKER_SHAPE ( N_("bar"), "bar", bar_path),
108 MAKE_MARKER_SHAPE ( N_("half bar"), "half-bar", half_bar_path),
109 MAKE_MARKER_SHAPE ( N_("butterfly"), "butterfly", butterfly_path),
110 MAKE_MARKER_SHAPE ( N_("hourglass"), "hourglass", hourglass_path),
111 MAKE_MARKER_SHAPE ( N_("left half bar"), "lefthalf-bar", left_half_bar_path)
112 };
113
114 static GObjectClass *marker_parent_klass;
115
116 static void
go_marker_init(GOMarker * marker)117 go_marker_init (GOMarker * marker)
118 {
119 marker->shape = GO_MARKER_NONE;
120 marker->outline_color = GO_COLOR_BLACK;
121 marker->fill_color = GO_COLOR_WHITE;
122 marker->size = MARKER_DEFAULT_SIZE;
123 }
124
125 static void
go_marker_class_init(GObjectClass * gobject_klass)126 go_marker_class_init (GObjectClass *gobject_klass)
127 {
128 marker_parent_klass = g_type_class_peek_parent (gobject_klass);
129 }
130
131 GOMarkerShape
go_marker_shape_from_str(char const * str)132 go_marker_shape_from_str (char const *str)
133 {
134 unsigned i;
135 for (i = 0; i < GO_MARKER_MAX; i++)
136 if (g_ascii_strcasecmp (marker_shapes[i].str, str) == 0)
137 return (GOMarkerShape)i;
138 return GO_MARKER_NONE;
139 }
140
141 char const *
go_marker_shape_as_str(GOMarkerShape shape)142 go_marker_shape_as_str (GOMarkerShape shape)
143 {
144 return (unsigned)shape >= GO_MARKER_MAX
145 ? "pattern"
146 : marker_shapes[shape].str;
147 }
148
149 static void
go_marker_get_paths(GOMarker const * marker,char const ** outline_path,char const ** fill_path)150 go_marker_get_paths (GOMarker const *marker,
151 char const **outline_path,
152 char const **fill_path)
153 {
154 *outline_path = marker_shapes[marker->shape].outline_path;
155 *fill_path = marker_shapes[marker->shape].fill_path;
156 }
157
158 GOMarkerShape
go_marker_get_shape(GOMarker const * marker)159 go_marker_get_shape (GOMarker const *marker)
160 {
161 return marker->shape;
162 }
163
164 void
go_marker_set_shape(GOMarker * marker,GOMarkerShape shape)165 go_marker_set_shape (GOMarker *marker, GOMarkerShape shape)
166 {
167 g_return_if_fail (GO_IS_MARKER (marker));
168
169 if (marker->shape == shape)
170 return;
171 marker->shape = shape;
172 }
173
174 gboolean
go_marker_is_closed_shape(GOMarker const * m)175 go_marker_is_closed_shape (GOMarker const *m)
176 {
177 g_return_val_if_fail (GO_IS_MARKER (m), FALSE);
178 switch (m->shape) {
179 case GO_MARKER_X:
180 case GO_MARKER_CROSS:
181 case GO_MARKER_ASTERISK:
182 return FALSE;
183 default:
184 return TRUE;
185 }
186 }
187
188 GOColor
go_marker_get_outline_color(GOMarker const * marker)189 go_marker_get_outline_color (GOMarker const *marker)
190 {
191 return marker->outline_color;
192 }
193
194 void
go_marker_set_outline_color(GOMarker * marker,GOColor color)195 go_marker_set_outline_color (GOMarker *marker, GOColor color)
196 {
197 g_return_if_fail (GO_IS_MARKER (marker));
198 if (marker->outline_color == color)
199 return;
200 marker->outline_color = color;
201 }
202
203 GOColor
go_marker_get_fill_color(GOMarker const * marker)204 go_marker_get_fill_color (GOMarker const *marker)
205 {
206 return marker->fill_color;
207 }
208
209 void
go_marker_set_fill_color(GOMarker * marker,GOColor color)210 go_marker_set_fill_color (GOMarker *marker, GOColor color)
211 {
212 g_return_if_fail (GO_IS_MARKER (marker));
213 if (marker->fill_color == color)
214 return;
215 marker->fill_color = color;
216 }
217
218 int
go_marker_get_size(GOMarker const * marker)219 go_marker_get_size (GOMarker const *marker)
220 {
221 return marker->size;
222 }
223
224 double
go_marker_get_outline_width(GOMarker const * marker)225 go_marker_get_outline_width (GOMarker const *marker)
226 {
227 return (double)marker->size * MARKER_OUTLINE_WIDTH;
228 }
229
230 void
go_marker_set_size(GOMarker * marker,int size)231 go_marker_set_size (GOMarker *marker, int size)
232 {
233 g_return_if_fail (GO_IS_MARKER (marker));
234 g_return_if_fail (size >= 0);
235 if (marker->size == size)
236 return;
237 marker->size = size;
238 }
239
240 void
go_marker_assign(GOMarker * dst,GOMarker const * src)241 go_marker_assign (GOMarker *dst, GOMarker const *src)
242 {
243 if (src == dst)
244 return;
245
246 g_return_if_fail (GO_MARKER (src) != NULL);
247 g_return_if_fail (GO_MARKER (dst) != NULL);
248
249 dst->size = src->size;
250 dst->shape = src->shape;
251 dst->outline_color = src->outline_color;
252 dst->fill_color = src->fill_color;
253
254 }
255
256 /**
257 * go_marker_dup:
258 * @src: the #GOMarker to duplicate
259 *
260 * Duplicates @src.
261 * Returns: (transfer full): the duplicated marker.
262 **/
263 GOMarker *
go_marker_dup(GOMarker const * src)264 go_marker_dup (GOMarker const *src)
265 {
266 GOMarker *dst = go_marker_new ();
267 go_marker_assign (dst, src);
268 return dst;
269 }
270
271 GOMarker *
go_marker_new(void)272 go_marker_new (void)
273 {
274 return g_object_new (GO_TYPE_MARKER, NULL);
275 }
276
277 /**
278 * go_marker_render:
279 * @marker: a #GOMarker
280 * @cr: a cairo context
281 * @x: x position
282 * @y: y position
283 * @scale: current scale
284 *
285 * Renders @marker onto the @cairo target, using @x and @y for the position.
286 **/
287 void
go_marker_render(GOMarker const * marker,cairo_t * cr,double x,double y,double scale)288 go_marker_render (GOMarker const *marker, cairo_t *cr, double x, double y, double scale)
289 {
290 char const *outline_path_raw, *fill_path_raw;
291 double half_size;
292
293 go_marker_get_paths (marker, &outline_path_raw, &fill_path_raw);
294
295 if ((outline_path_raw == NULL) ||
296 (fill_path_raw == NULL))
297 return;
298
299 cairo_set_line_cap (cr, CAIRO_LINE_CAP_SQUARE);
300 cairo_set_line_join (cr, CAIRO_LINE_JOIN_MITER);
301
302 half_size = 0.5 * scale * go_marker_get_size (marker);
303
304 cairo_save (cr);
305 cairo_translate (cr, x, y);
306 cairo_scale (cr, half_size, half_size);
307
308 cairo_set_source_rgba (cr, GO_COLOR_TO_CAIRO (go_marker_get_fill_color (marker)));
309 go_cairo_emit_svg_path (cr, fill_path_raw);
310 cairo_fill (cr);
311
312 cairo_set_source_rgba (cr, GO_COLOR_TO_CAIRO (go_marker_get_outline_color (marker)));
313 cairo_set_line_width (cr, 2.0 * MARKER_OUTLINE_WIDTH);
314 cairo_set_dash (cr, NULL, 0, 0.);
315 go_cairo_emit_svg_path (cr, outline_path_raw);
316 cairo_stroke (cr);
317 cairo_restore (cr);
318 }
319
320 /**
321 * go_marker_create_cairo_surface:
322 * @marker: a #GOMarker
323 * @cr: a cairo context
324 * @scale: current context scale
325 * @width: a placeholder for the surface width
326 * @height: a placeholder for the surface height
327 *
328 * Creates a new cairo surface similar to the current target of @cr, and render
329 * @marker on it. @center will contain the coordinate of the center of the surface.
330 *
331 * Returns: a newly created #cairo_surface_t. This surface should be destroyed
332 * using cairo_surface_destroy after use.
333 **/
334 cairo_surface_t *
go_marker_create_cairo_surface(GOMarker const * marker,cairo_t * cr,double scale,double * width,double * height)335 go_marker_create_cairo_surface (GOMarker const *marker, cairo_t *cr, double scale,
336 double *width, double *height)
337 {
338 cairo_t *cr_tmp;
339 cairo_surface_t *cr_surface;
340 cairo_surface_t *current_cr_surface;
341 double half_size, offset;
342
343 g_return_val_if_fail (GO_IS_MARKER (marker), NULL);
344 g_return_val_if_fail (cr != NULL, NULL);
345
346 current_cr_surface = cairo_get_target (cr);
347
348 if (go_cairo_surface_is_vector (current_cr_surface)) {
349 half_size = scale * go_marker_get_size (marker) * 0.5;
350 offset = half_size + scale * go_marker_get_outline_width (marker);
351 } else {
352 half_size = rint (scale * go_marker_get_size (marker)) * 0.5;
353 offset = ceil (scale * go_marker_get_outline_width (marker) * 0.5) +
354 half_size + .5;
355 }
356
357 cr_surface = cairo_surface_create_similar (current_cr_surface,
358 CAIRO_CONTENT_COLOR_ALPHA,
359 ceil (2.0 * offset),
360 ceil (2.0 * offset));
361 cr_tmp = cairo_create (cr_surface);
362
363 go_marker_render (marker, cr_tmp, offset, offset, scale);
364
365 cairo_destroy (cr_tmp);
366
367 if (width != NULL)
368 *width = offset * 2.0;
369 if (height != NULL)
370 *height = offset * 2.0;
371
372 return cr_surface;
373 }
374
375 GSF_CLASS (GOMarker, go_marker,
376 go_marker_class_init, go_marker_init,
377 G_TYPE_OBJECT)
378