Updated copyright text/header in most source files.
[geda-gaf/peter-b.git] / gschem / src / gschem_cairo.c
blobc9d05f34e5280a34bc377a0c3c86fe6836d8e808
1 /* gEDA - GPL Electronic Design Automation
2 * gschem - gEDA Schematic Capture
3 * Copyright (C) 1998-2010 Ales Hvezda
4 * Copyright (C) 1998-2010 gEDA Contributors (see ChangeLog for details)
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
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.
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., 59 Temple Place, Suite 330, Boston, MA 02111 USA
22 #include <config.h>
24 #include <cairo.h>
25 #include <math.h>
27 #include "gschem.h"
29 #ifdef HAVE_LIBDMALLOC
30 #include <dmalloc.h>
31 #endif
34 static inline int screen_width (GSCHEM_TOPLEVEL *w_current, int w_width)
36 int width = SCREENabs (w_current, w_width);
37 if (width < 1)
38 width = 1;
40 return width;
43 void gschem_cairo_line (GSCHEM_TOPLEVEL *w_current, int line_end,
44 int w_line_width,
45 int w_x1, int w_y1, int w_x2, int w_y2)
47 int x1, y1, x2, y2, line_width;
48 double offset;
49 double xoffset = 0;
50 double yoffset = 0;
51 int horizontal = 0;
52 int vertical = 0;
54 WORLDtoSCREEN (w_current, w_x1, w_y1, &x1, &y1);
55 WORLDtoSCREEN (w_current, w_x2, w_y2, &x2, &y2);
56 line_width = screen_width (w_current, w_line_width);
57 offset = ((line_width % 2) == 0) ? 0 : 0.5;
59 if (y1 == y2) horizontal = 1;
60 if (x1 == x2) vertical = 1;
62 /* Hint so the length of the line runs along a pixel boundary */
64 if (horizontal)
65 yoffset = offset;
66 else if (vertical)
67 xoffset = offset;
68 else
69 xoffset = yoffset = offset;
71 /* Now hint the ends of the lines */
73 switch (line_end) {
74 case END_NONE:
75 /* Line terminates at the passed coordinate */
77 /* Add an extra pixel to give an inclusive span */
78 if (horizontal) {
79 if (x1 > x2) x1 += 1; else x2 += 1;
80 } else if (vertical) {
81 if (y1 > y2) y1 += 1; else y2 += 1;
83 break;
85 case END_SQUARE:
86 case END_ROUND:
87 /* Line terminates half a width away from the passed coordinate */
88 if (horizontal) {
89 xoffset = offset;
90 } else if (vertical) {
91 yoffset = offset;
93 break;
96 cairo_move_to (w_current->cr, x1 + xoffset, y1 + yoffset);
97 cairo_line_to (w_current->cr, x2 + xoffset, y2 + yoffset);
101 void gschem_cairo_box (GSCHEM_TOPLEVEL *w_current, int line_width,
102 int x1, int y1, int x2, int y2)
104 int s_line_width;
105 int s_x1, s_y1, s_x2, s_y2;
106 double offset;
108 WORLDtoSCREEN (w_current, x1, y1, &s_x1, &s_y1);
109 WORLDtoSCREEN (w_current, x2, y2, &s_x2, &s_y2);
110 s_line_width = screen_width (w_current, line_width);
111 offset = (line_width == -1 || (s_line_width % 2) == 0) ? 0 : 0.5;
113 /* Allow filled boxes (inferred from line_width == -1)
114 * to touch an extra pixel, so the filled span is inclusive */
115 if (line_width == -1) {
116 if (x1 > x2) x1 += 1; else x2 += 1;
117 if (y1 > y2) y1 += 1; else y2 += 1;
120 cairo_move_to (w_current->cr, s_x2 + offset, s_y2 + offset);
121 cairo_line_to (w_current->cr, s_x1 + offset, s_y2 + offset);
122 cairo_line_to (w_current->cr, s_x1 + offset, s_y1 + offset);
123 cairo_line_to (w_current->cr, s_x2 + offset, s_y1 + offset);
124 cairo_close_path (w_current->cr);
128 void gschem_cairo_center_box (GSCHEM_TOPLEVEL *w_current,
129 int center_width,
130 int line_width, int x, int y,
131 int half_width, int half_height)
133 int s_center_width, s_line_width;
134 int s_width, s_height;
135 double s_half_width, s_half_height;
136 int s_x, s_y;
137 int even_center_width;
138 int even_line_width;
139 int even_width, even_height;
140 double x1, y1, x2, y2;
141 double center_offset;
142 int do_width_hint = TRUE;
143 int do_height_hint = TRUE;
145 WORLDtoSCREEN (w_current, x, y, &s_x, &s_y);
146 s_width = SCREENabs (w_current, 2 * half_width);
147 s_height = SCREENabs (w_current, 2 * half_height);
148 even_width = (s_width % 2 == 0);
149 even_height = (s_width % 2 == 0);
150 s_half_width = (double) s_width / 2.;
151 s_half_height = (double) s_height / 2.;
153 #if 0 /* Not as nice an effect as with arcs */
154 /* Switch off radius hinting for small radii. If we don't, then we get
155 * a very abrupt transition once the box reaches a single pixel size. */
156 if (s_half_width <= 1.) do_width_hint = FALSE;
157 if (s_half_height <= 1.) do_height_hint = FALSE;
158 #endif
160 /* Hint the center of the box based on where a line
161 * of thickness center_width (world) would drawn */
162 s_center_width = screen_width (w_current, center_width);
163 even_center_width = (center_width == -1 || (s_center_width % 2) == 0);
164 center_offset = even_center_width ? 0. : 0.5;
166 /* Hint the half-widths to land the stroke on the pixel grid */
167 s_line_width = screen_width (w_current, line_width);
168 even_line_width = (line_width == -1 || (s_line_width % 2) == 0);
169 if (do_width_hint)
170 s_half_width += ((even_center_width ==
171 even_line_width) == even_width ) ? 0. : 0.5;
172 if (do_height_hint)
173 s_half_height += ((even_center_width ==
174 even_line_width) == even_height) ? 0. : 0.5;
176 x1 = (double) s_x + center_offset - s_half_width;
177 y1 = (double) s_y + center_offset - s_half_height;
178 x2 = (double) s_x + center_offset + s_half_width;
179 y2 = (double) s_y + center_offset + s_half_height;
181 /* Allow filled boxes (inferred from line_width == -1)
182 * to touch an extra pixel, so the filled span is inclusive */
183 if (line_width == -1) {
184 x2 += 1; y2 += 1;
187 cairo_move_to (w_current->cr, x2, y2);
188 cairo_line_to (w_current->cr, x1, y2);
189 cairo_line_to (w_current->cr, x1, y1);
190 cairo_line_to (w_current->cr, x2, y1);
191 cairo_close_path (w_current->cr);
195 static inline void do_arc (cairo_t *cr, double x, double y, double radius,
196 int start_angle, int end_angle)
198 cairo_new_sub_path (cr);
199 if (start_angle > start_angle + end_angle) {
200 cairo_arc (cr, x, y, radius, -start_angle * (M_PI / 180.),
201 (-start_angle - end_angle) * (M_PI / 180.));
202 } else {
203 cairo_arc_negative (cr, x, y, radius, -start_angle * (M_PI / 180.),
204 (-start_angle - end_angle) * (M_PI / 180.));
209 void gschem_cairo_arc (GSCHEM_TOPLEVEL *w_current,
210 int width, int x, int y,
211 int radius, int start_angle, int end_angle)
213 int s_width;
214 int x1, y1, x2, y2;
215 double s_x, s_y, s_radius;
216 double offset;
218 WORLDtoSCREEN (w_current, x - radius, y + radius, &x1, &y1);
219 WORLDtoSCREEN (w_current, x + radius, y - radius, &x2, &y2);
220 s_width = screen_width (w_current, width);
221 offset = ((s_width % 2) == 0) ? 0 : 0.5;
223 s_x = (double)(x1 + x2) / 2.;
224 s_y = (double)(y1 + y2) / 2.;
225 s_radius = (double)(y2 - y1) / 2.;
227 cairo_save (w_current->cr);
228 cairo_translate (w_current->cr, s_x + offset, s_y + offset);
230 /* Adjust for non-uniform X/Y scale factor. Note that the + 1
231 allows for the case where x2 == x1 or y2 == y1 */
232 cairo_scale (w_current->cr, (double)(x2 - x1 + 1) /
233 (double)(y2 - y1 + 1), 1.);
235 do_arc (w_current->cr, 0., 0., (double) s_radius, start_angle, end_angle);
237 cairo_restore (w_current->cr);
241 void gschem_cairo_center_arc (GSCHEM_TOPLEVEL *w_current,
242 int center_width,
243 int line_width, int x, int y,
244 int radius, int start_angle, int end_angle)
246 int s_center_width, s_line_width;
247 int s_x, s_y, s_diameter;
248 int even_center_width;
249 int even_line_width;
250 int even_diameter;
251 double center_offset;
252 double s_radius;
253 int do_radius_hint = TRUE;
255 WORLDtoSCREEN (w_current, x, y, &s_x, &s_y);
256 s_diameter = SCREENabs (w_current, 2 * radius);
257 even_diameter = ((s_diameter % 2) == 0);
258 s_radius = (double) s_diameter / 2.;
260 /* Switch off radius hinting for small radii. If we don't, then we get
261 * a very abrupt transition once the arc reaches a single pixel size. */
262 if (s_radius <= 1.) do_radius_hint = FALSE;
264 /* Hint the center of the arc based on where a line
265 * of thickness center_width (world) would drawn */
266 s_center_width = screen_width (w_current, center_width);
267 even_center_width = (center_width == -1 || (s_center_width % 2) == 0);
268 center_offset = even_center_width ? 0. : 0.5;
270 /* Hint the radius to land its extermity on the pixel grid */
271 s_line_width = screen_width (w_current, line_width);
272 even_line_width = (line_width == -1 || (s_line_width % 2) == 0);
273 if (do_radius_hint)
274 s_radius += ((even_center_width ==
275 even_line_width) == even_diameter) ? 0. : 0.5;
277 do_arc (w_current->cr, (double) s_x + center_offset,
278 (double) s_y + center_offset,
279 (double) s_radius,
280 start_angle, end_angle);
284 void gschem_cairo_stroke (GSCHEM_TOPLEVEL *w_current, int line_type, int line_end,
285 int wwidth, int wlength, int wspace)
287 double offset;
288 double dashes[4];
289 cairo_line_cap_t cap;
290 cairo_line_cap_t round_cap_if_legible;
291 int num_dashes;
292 int width, length, space;
294 width = screen_width (w_current, wwidth);
295 length = screen_width (w_current, wlength);
296 space = screen_width (w_current, wspace);
297 offset = ((width % 2) == 0) ? 0 : 0.5;
299 cairo_set_line_width (w_current->cr, width);
300 cairo_set_line_join (w_current->cr, CAIRO_LINE_JOIN_MITER);
302 round_cap_if_legible = (width <= 1) ? CAIRO_LINE_CAP_SQUARE :
303 CAIRO_LINE_CAP_ROUND;
305 switch (line_end) {
306 case END_NONE: cap = CAIRO_LINE_CAP_BUTT; break;
307 case END_SQUARE: cap = CAIRO_LINE_CAP_SQUARE; break;
308 case END_ROUND: cap = round_cap_if_legible; break;
309 default:
310 fprintf(stderr, _("Unknown end for line (%d)\n"), line_end);
311 cap = CAIRO_LINE_CAP_BUTT;
312 break;
315 switch (line_type) {
317 default:
318 fprintf(stderr, _("Unknown type for stroke (%d) !\n"), line_type);
319 /* Fall through */
321 case TYPE_SOLID:
322 num_dashes = 0;
324 cairo_set_dash (w_current->cr, dashes, num_dashes, 0.);
325 cairo_set_line_cap (w_current->cr, cap);
326 cairo_stroke (w_current->cr);
327 break;
329 case TYPE_DOTTED:
330 dashes[0] = 0; /* DOT */
331 dashes[1] = space;
332 num_dashes = 2;
334 cairo_set_dash (w_current->cr, dashes, num_dashes, offset);
335 cairo_set_line_cap (w_current->cr, round_cap_if_legible);
336 cairo_stroke (w_current->cr);
337 break;
339 case TYPE_DASHED:
340 dashes[0] = length; /* DASH */
341 dashes[1] = space;
342 num_dashes = 2;
344 cairo_set_dash (w_current->cr, dashes, num_dashes, 0.);
345 cairo_set_line_cap (w_current->cr, CAIRO_LINE_CAP_BUTT);
346 cairo_stroke (w_current->cr);
347 break;
349 case TYPE_CENTER:
350 dashes[0] = length; /* DASH */
351 dashes[1] = 2 * space;
352 num_dashes = 2;
354 cairo_set_dash (w_current->cr, dashes, num_dashes, 0.);
355 cairo_set_line_cap (w_current->cr, CAIRO_LINE_CAP_BUTT);
356 cairo_stroke_preserve (w_current->cr);
358 dashes[0] = 0; /* DOT */
359 dashes[1] = 2 * space + length;
360 num_dashes = 2;
362 cairo_set_dash (w_current->cr, dashes, num_dashes, -length - space + offset);
363 cairo_set_line_cap (w_current->cr, round_cap_if_legible);
364 cairo_stroke (w_current->cr);
365 break;
367 case TYPE_PHANTOM:
368 dashes[0] = length; /* DASH */
369 dashes[1] = 3 * space;
370 num_dashes = 2;
372 cairo_set_dash (w_current->cr, dashes, num_dashes, 0.);
373 cairo_set_line_cap (w_current->cr, CAIRO_LINE_CAP_BUTT);
374 cairo_stroke_preserve (w_current->cr);
376 dashes[0] = 0; /* DOT */
377 dashes[1] = space;
378 dashes[2] = 0; /* DOT */
379 dashes[3] = 2 * space + length;
380 num_dashes = 4;
382 cairo_set_dash (w_current->cr, dashes, num_dashes, -length - space + offset);
383 cairo_set_line_cap (w_current->cr, round_cap_if_legible);
384 cairo_stroke (w_current->cr);
385 break;
388 cairo_set_dash (w_current->cr, NULL, 0, 0.);
392 void gschem_cairo_set_source_color (GSCHEM_TOPLEVEL *w_current, COLOR *color)
394 cairo_set_source_rgba (w_current->cr, (double)color->r / 255.0,
395 (double)color->g / 255.0,
396 (double)color->b / 255.0,
397 (double)color->a / 255.0);