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
29 #ifdef HAVE_LIBDMALLOC
34 static inline int screen_width (GSCHEM_TOPLEVEL
*w_current
, int w_width
)
36 int width
= SCREENabs (w_current
, w_width
);
43 void gschem_cairo_line (GSCHEM_TOPLEVEL
*w_current
, int line_end
,
45 int w_x1
, int w_y1
, int w_x2
, int w_y2
)
47 int x1
, y1
, x2
, y2
, line_width
;
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 */
69 xoffset
= yoffset
= offset
;
71 /* Now hint the ends of the lines */
75 /* Line terminates at the passed coordinate */
77 /* Add an extra pixel to give an inclusive span */
79 if (x1
> x2
) x1
+= 1; else x2
+= 1;
80 } else if (vertical
) {
81 if (y1
> y2
) y1
+= 1; else y2
+= 1;
87 /* Line terminates half a width away from the passed coordinate */
90 } else if (vertical
) {
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
)
105 int s_x1
, s_y1
, s_x2
, s_y2
;
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
,
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
;
137 int even_center_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
;
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);
170 s_half_width
+= ((even_center_width
==
171 even_line_width
) == even_width
) ? 0. : 0.5;
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) {
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.));
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
)
215 double s_x
, s_y
, s_radius
;
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
,
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
;
251 double center_offset
;
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);
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
,
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
)
289 cairo_line_cap_t cap
;
290 cairo_line_cap_t round_cap_if_legible
;
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
;
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;
310 fprintf(stderr
, _("Unknown end for line (%d)\n"), line_end
);
311 cap
= CAIRO_LINE_CAP_BUTT
;
318 fprintf(stderr
, _("Unknown type for stroke (%d) !\n"), line_type
);
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
);
330 dashes
[0] = 0; /* DOT */
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
);
340 dashes
[0] = length
; /* DASH */
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
);
350 dashes
[0] = length
; /* DASH */
351 dashes
[1] = 2 * space
;
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
;
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
);
368 dashes
[0] = length
; /* DASH */
369 dashes
[1] = 3 * space
;
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 */
378 dashes
[2] = 0; /* DOT */
379 dashes
[3] = 2 * space
+ length
;
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
);
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);