1 /* gEDA - GPL Electronic Design Automation
2 * libgedacairo - Rendering gEDA schematics with Cairo
3 * Copyright (C) 1998-2019 gEDA Contributors (see ChangeLog for details)
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 USA
25 #include <libgeda/libgeda.h>
28 /* We don't use gettext */
32 screen_width (cairo_t
*cr
, double width
)
35 cairo_user_to_device_distance (cr
, &width
, &dummy
);
43 SCREENabs (cairo_t
*cr
, double dist
)
46 cairo_user_to_device_distance (cr
, &dist
, &dummy
);
51 WORLDtoSCREEN (cairo_t
*cr
, double wx
, double wy
, double *sx
, double *sy
)
53 cairo_user_to_device (cr
, &wx
, &wy
);
54 *sx
= round (wx
); *sy
= round (wy
);
58 eda_cairo_set_source_color (cairo_t
*cr
, int color
, GArray
*map
)
62 g_return_if_fail (color
>= 0);
63 g_return_if_fail (map
!= NULL
);
64 g_return_if_fail ((color
>= 0) && (map
->len
> color
));
66 c
= g_array_index (map
, COLOR
, color
);
68 cairo_set_source_rgba (cr
, (double)c
.r
/ 255.0,
75 eda_cairo_line (cairo_t
*cr
, int flags
, int line_end
,
77 double w_x1
, double w_y1
, double w_x2
, double w_y2
)
79 double x1
, y1
, x2
, y2
;
84 double horizontal
= 0;
87 if (!(flags
& EDA_CAIRO_ENABLE_HINTS
)) {
88 cairo_move_to (cr
, w_x1
, w_y1
);
89 cairo_line_to (cr
, w_x2
, w_y2
);
93 WORLDtoSCREEN (cr
, w_x1
, w_y1
, &x1
, &y1
);
94 WORLDtoSCREEN (cr
, w_x2
, w_y2
, &x2
, &y2
);
95 line_width
= screen_width (cr
, w_line_width
);
96 offset
= ((line_width
% 2) == 0) ? 0 : 0.5;
98 if (y1
== y2
) horizontal
= 1;
99 if (x1
== x2
) vertical
= 1;
101 /* Hint so the length of the line runs along a pixel boundary */
108 xoffset
= yoffset
= offset
;
110 /* Now hint the ends of the lines */
114 /* Line terminates at the passed coordinate */
116 /* Add an extra pixel to give an inclusive span */
118 if (x1
> x2
) x1
+= 1; else x2
+= 1;
119 } else if (vertical
) {
120 if (y1
> y2
) y1
+= 1; else y2
+= 1;
126 /* Line terminates half a width away from the passed coordinate */
129 } else if (vertical
) {
135 x1
+= xoffset
; y1
+= yoffset
;
136 x2
+= xoffset
; y2
+= yoffset
;
137 cairo_device_to_user (cr
, &x1
, &y1
);
138 cairo_device_to_user (cr
, &x2
, &y2
);
139 cairo_move_to (cr
, x1
, y1
);
140 cairo_line_to (cr
, x2
, y2
);
145 eda_cairo_box (cairo_t
*cr
, int flags
, double line_width
,
146 double x1
, double y1
, double x2
, double y2
)
149 double s_x1
, s_y1
, s_x2
, s_y2
;
152 if (!(flags
& EDA_CAIRO_ENABLE_HINTS
)) {
153 cairo_rectangle (cr
, x1
, y1
, (x2
- x1
), (y2
- y1
));
157 WORLDtoSCREEN (cr
, x1
, y1
, &s_x1
, &s_y1
);
158 WORLDtoSCREEN (cr
, x2
, y2
, &s_x2
, &s_y2
);
159 s_line_width
= screen_width (cr
, line_width
);
160 offset
= (line_width
== -1 || (s_line_width
% 2) == 0) ? 0 : 0.5;
162 /* Allow filled boxes (inferred from line_width == -1)
163 * to touch an extra pixel, so the filled span is inclusive */
164 if (line_width
== -1) {
165 if (s_x1
> s_x2
) s_x1
+= 1; else s_x2
+= 1;
166 if (s_y1
> s_y2
) s_y1
+= 1; else s_y2
+= 1;
169 s_x1
+= offset
; s_y1
+= offset
;
170 s_x2
+= offset
; s_y2
+= offset
;
171 cairo_device_to_user (cr
, &s_x1
, &s_y1
);
172 cairo_device_to_user (cr
, &s_x2
, &s_y2
);
173 cairo_move_to (cr
, s_x2
, s_y2
);
174 cairo_line_to (cr
, s_x1
, s_y2
);
175 cairo_line_to (cr
, s_x1
, s_y1
);
176 cairo_line_to (cr
, s_x2
, s_y1
);
177 cairo_close_path (cr
);
182 eda_cairo_center_box (cairo_t
*cr
, int flags
,
184 double line_width
, double x
, double y
,
185 double half_width
, double half_height
)
187 int s_center_width
, s_line_width
;
188 int s_width
, s_height
;
189 double s_half_width
, s_half_height
;
191 double even_center_width
;
192 double even_line_width
;
193 double even_width
, even_height
;
194 double x1
, y1
, x2
, y2
;
195 double center_offset
;
196 int do_width_hint
= TRUE
;
197 int do_height_hint
= TRUE
;
199 if (!(flags
& EDA_CAIRO_ENABLE_HINTS
)) {
200 cairo_rectangle (cr
, (x
- half_width
), (y
- half_height
),
201 2*half_width
, 2*half_height
);
205 WORLDtoSCREEN (cr
, x
, y
, &s_x
, &s_y
);
206 s_width
= SCREENabs (cr
, 2 * half_width
);
207 s_height
= SCREENabs (cr
, 2 * half_height
);
208 even_width
= (s_width
% 2 == 0);
209 even_height
= (s_width
% 2 == 0);
210 s_half_width
= (double) s_width
/ 2.;
211 s_half_height
= (double) s_height
/ 2.;
213 #if 0 /* Not as nice an effect as with arcs */
214 /* Switch off radius hinting for small radii. If we don't, then we get
215 * a very abrupt transition once the box reaches a single pixel size. */
216 if (s_half_width
<= 1.) do_width_hint
= FALSE
;
217 if (s_half_height
<= 1.) do_height_hint
= FALSE
;
220 /* Hint the center of the box based on where a line
221 * of thickness center_width (world) would drawn */
222 s_center_width
= screen_width (cr
, center_width
);
223 even_center_width
= (center_width
== -1 || (s_center_width
% 2) == 0);
224 center_offset
= even_center_width
? 0. : 0.5;
226 /* Hint the half-widths to land the stroke on the pixel grid */
227 s_line_width
= screen_width (cr
, line_width
);
228 even_line_width
= (line_width
== -1 || (s_line_width
% 2) == 0);
230 s_half_width
+= ((even_center_width
==
231 even_line_width
) == even_width
) ? 0. : 0.5;
233 s_half_height
+= ((even_center_width
==
234 even_line_width
) == even_height
) ? 0. : 0.5;
236 x1
= (double) s_x
+ center_offset
- s_half_width
;
237 y1
= (double) s_y
+ center_offset
- s_half_height
;
238 x2
= (double) s_x
+ center_offset
+ s_half_width
;
239 y2
= (double) s_y
+ center_offset
+ s_half_height
;
241 /* Allow filled boxes (inferred from line_width == -1)
242 * to touch an extra pixel, so the filled span is inclusive */
243 if (line_width
== -1) {
247 cairo_device_to_user (cr
, &x1
, &y1
);
248 cairo_device_to_user (cr
, &x2
, &y2
);
249 cairo_move_to (cr
, x2
, y2
);
250 cairo_line_to (cr
, x1
, y2
);
251 cairo_line_to (cr
, x1
, y1
);
252 cairo_line_to (cr
, x2
, y1
);
253 cairo_close_path (cr
);
258 do_arc (cairo_t
*cr
, double x
, double y
, double radius
,
259 double start_angle
, double sweep_angle
)
261 cairo_new_sub_path (cr
);
262 if (sweep_angle
> 0) {
263 cairo_arc (cr
, x
, y
, radius
, start_angle
* (M_PI
/ 180.),
264 (start_angle
+ sweep_angle
) * (M_PI
/ 180.));
266 cairo_arc_negative (cr
, x
, y
, radius
, start_angle
* (M_PI
/ 180.),
267 (start_angle
+ sweep_angle
) * (M_PI
/ 180.));
273 eda_cairo_arc (cairo_t
*cr
, int flags
,
274 double width
, double x
, double y
,
275 double radius
, double start_angle
, double sweep_angle
)
278 double x1
, y1
, x2
, y2
;
279 double s_x
, s_y
, s_radius
;
280 double offset
, dummy
= 0;
282 if (!(flags
& EDA_CAIRO_ENABLE_HINTS
)) {
283 do_arc (cr
, x
, y
, radius
, start_angle
, sweep_angle
);
287 WORLDtoSCREEN (cr
, x
- radius
, y
+ radius
, &x1
, &y1
);
288 WORLDtoSCREEN (cr
, x
+ radius
, y
- radius
, &x2
, &y2
);
289 s_width
= screen_width (cr
, width
);
290 offset
= ((s_width
% 2) == 0) ? 0 : 0.5;
292 s_x
= (double)(x1
+ x2
) / 2.;
293 s_y
= (double)(y1
+ y2
) / 2.;
294 s_radius
= (double)(y2
- y1
) / 2.;
296 cairo_device_to_user (cr
, &s_x
, &s_y
);
297 cairo_device_to_user_distance (cr
, &offset
, &dummy
);
298 cairo_device_to_user_distance (cr
, &s_radius
, &dummy
);
300 do_arc (cr
, s_x
+ offset
, s_y
+ offset
,
301 s_radius
, start_angle
, sweep_angle
);
306 eda_cairo_center_arc (cairo_t
*cr
, int flags
,
308 double line_width
, double x
, double y
,
309 double radius
, double start_angle
, double sweep_angle
)
311 int s_center_width
, s_line_width
;
312 double s_x
, s_y
, dummy
= 0;
314 double even_center_width
;
315 double even_line_width
;
316 double even_diameter
;
317 double center_offset
;
319 int do_radius_hint
= TRUE
;
321 if (!(flags
& EDA_CAIRO_ENABLE_HINTS
)) {
322 do_arc (cr
, x
, y
, radius
, start_angle
, sweep_angle
);
326 WORLDtoSCREEN (cr
, x
, y
, &s_x
, &s_y
);
327 s_diameter
= SCREENabs (cr
, 2 * radius
);
328 even_diameter
= ((s_diameter
% 2) == 0);
329 s_radius
= (double) s_diameter
/ 2.;
331 /* Switch off radius hinting for small radii. If we don't, then we get
332 * a very abrupt transition once the arc reaches a single pixel size. */
333 if (s_radius
<= 1.) do_radius_hint
= FALSE
;
335 /* Hint the center of the arc based on where a line
336 * of thickness center_width (world) would drawn */
337 s_center_width
= screen_width (cr
, center_width
);
338 even_center_width
= (center_width
== -1 || (s_center_width
% 2) == 0);
339 center_offset
= even_center_width
? 0. : 0.5;
341 /* Hint the radius to land its extermity on the pixel grid */
342 s_line_width
= screen_width (cr
, line_width
);
343 even_line_width
= (line_width
== -1 || (s_line_width
% 2) == 0);
345 s_radius
+= ((even_center_width
==
346 even_line_width
) == even_diameter
) ? 0. : 0.5;
348 s_x
+= center_offset
;
349 s_y
+= center_offset
;
350 cairo_device_to_user (cr
, &s_x
, &s_y
);
351 cairo_device_to_user_distance (cr
, &s_radius
, &dummy
);
353 do_arc (cr
, s_x
, s_y
, s_radius
, start_angle
, sweep_angle
);
358 eda_cairo_stroke (cairo_t
*cr
, int flags
, int line_type
, int line_end
,
359 double wwidth
, double wlength
, double wspace
)
364 cairo_line_cap_t cap
;
365 cairo_line_cap_t round_cap_if_legible
= CAIRO_LINE_CAP_ROUND
;
368 double width
= wwidth
, length
= wlength
, space
= wspace
;
370 if (flags
& EDA_CAIRO_ENABLE_HINTS
) {
371 width
= iwidth
= screen_width (cr
, wwidth
);
372 length
= screen_width (cr
, wlength
);
373 space
= screen_width (cr
, wspace
);
374 cairo_device_to_user_distance (cr
, &width
, &dummy
);
375 cairo_device_to_user_distance (cr
, &length
, &dummy
);
376 cairo_device_to_user_distance (cr
, &space
, &dummy
);
378 offset
= ((iwidth
% 2) == 0) ? 0 : 0.5;
380 round_cap_if_legible
=
381 (iwidth
<= 1) ? CAIRO_LINE_CAP_SQUARE
: CAIRO_LINE_CAP_ROUND
;
384 cairo_set_line_width (cr
, width
);
385 cairo_set_line_join (cr
, CAIRO_LINE_JOIN_MITER
);
388 case END_NONE
: cap
= CAIRO_LINE_CAP_BUTT
; break;
389 case END_SQUARE
: cap
= CAIRO_LINE_CAP_SQUARE
; break;
390 case END_ROUND
: cap
= round_cap_if_legible
; break;
392 g_warn_if_reached ();
393 cap
= CAIRO_LINE_CAP_BUTT
;
400 g_warn_if_reached ();
406 cairo_set_dash (cr
, dashes
, num_dashes
, 0.);
407 cairo_set_line_cap (cr
, cap
);
412 dashes
[0] = 0; /* DOT */
416 cairo_set_dash (cr
, dashes
, num_dashes
, offset
);
417 cairo_set_line_cap (cr
, round_cap_if_legible
);
422 dashes
[0] = length
; /* DASH */
426 cairo_set_dash (cr
, dashes
, num_dashes
, 0.);
427 cairo_set_line_cap (cr
, CAIRO_LINE_CAP_BUTT
);
432 dashes
[0] = length
; /* DASH */
433 dashes
[1] = 2 * space
;
436 cairo_set_dash (cr
, dashes
, num_dashes
, 0.);
437 cairo_set_line_cap (cr
, CAIRO_LINE_CAP_BUTT
);
438 cairo_stroke_preserve (cr
);
440 dashes
[0] = 0; /* DOT */
441 dashes
[1] = 2 * space
+ length
;
444 cairo_set_dash (cr
, dashes
, num_dashes
, -length
- space
+ offset
);
445 cairo_set_line_cap (cr
, round_cap_if_legible
);
450 dashes
[0] = length
; /* DASH */
451 dashes
[1] = 3 * space
;
454 cairo_set_dash (cr
, dashes
, num_dashes
, 0.);
455 cairo_set_line_cap (cr
, CAIRO_LINE_CAP_BUTT
);
456 cairo_stroke_preserve (cr
);
458 dashes
[0] = 0; /* DOT */
460 dashes
[2] = 0; /* DOT */
461 dashes
[3] = 2 * space
+ length
;
464 cairo_set_dash (cr
, dashes
, num_dashes
, -length
- space
+ offset
);
465 cairo_set_line_cap (cr
, round_cap_if_legible
);
470 cairo_set_dash (cr
, NULL
, 0, 0.);
474 eda_cairo_path_hint (cairo_t
*cr
, int flags
,
475 double *x
, double *y
, int width
)
478 if (flags
& EDA_CAIRO_ENABLE_HINTS
) {
479 cairo_user_to_device (cr
, x
, y
);
480 offset
= ((width
% 2) == 0) ? 0 : 0.5;
481 *x
+= offset
; *y
+= offset
;
482 cairo_device_to_user (cr
, x
, y
);
487 eda_cairo_path (cairo_t
*cr
, int flags
, double line_width
,
488 int nsections
, PATH_SECTION
*sections
)
494 if (flags
& EDA_CAIRO_ENABLE_HINTS
) {
495 s_line_width
= screen_width (cr
, line_width
);
497 cairo_user_to_device (cr
, &line_width
, &dummy
);
498 s_line_width
= line_width
;
501 for (i
= 0; i
< nsections
; i
++) {
502 PATH_SECTION
*section
= sections
+ i
;
503 double x1
= section
->x1
;
504 double x2
= section
->x2
;
505 double x3
= section
->x3
;
506 double y1
= section
->y1
;
507 double y2
= section
->y2
;
508 double y3
= section
->y3
;
510 switch (section
->code
) {
512 /* Two control point grips */
513 eda_cairo_path_hint (cr
, flags
, &x1
, &y1
, s_line_width
);
514 eda_cairo_path_hint (cr
, flags
, &x2
, &y2
, s_line_width
);
517 case PATH_MOVETO_OPEN
:
519 /* Destination point grip */
520 eda_cairo_path_hint (cr
, flags
, &x3
, &y3
, s_line_width
);
525 switch (section
->code
) {
527 cairo_close_path (cr
);
529 case PATH_MOVETO_OPEN
:
530 cairo_move_to (cr
, x3
, y3
);
533 cairo_curve_to (cr
, x1
, y1
, x2
, y2
, x3
, y3
);
536 cairo_line_to (cr
, x3
, y3
);
539 cairo_close_path (cr
);