gschem
|
00001 /* gEDA - GPL Electronic Design Automation 00002 * gschem - gEDA Schematic Capture 00003 * Copyright (C) 1998-2010 Ales Hvezda 00004 * Copyright (C) 1998-2010 gEDA Contributors (see ChangeLog for details) 00005 * 00006 * This program is free software; you can redistribute it and/or modify 00007 * it under the terms of the GNU General Public License as published by 00008 * the Free Software Foundation; either version 2 of the License, or 00009 * (at your option) any later version. 00010 * 00011 * This program is distributed in the hope that it will be useful, 00012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00014 * GNU General Public License for more details. 00015 * 00016 * You should have received a copy of the GNU General Public License 00017 * along with this program; if not, write to the Free Software 00018 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 00019 */ 00020 00021 00022 #include <config.h> 00023 00024 #include <cairo.h> 00025 #include <math.h> 00026 00027 #include "gschem.h" 00028 00029 #ifdef HAVE_LIBDMALLOC 00030 #include <dmalloc.h> 00031 #endif 00032 00033 00034 static inline int screen_width (GSCHEM_TOPLEVEL *w_current, int w_width) 00035 { 00036 int width = SCREENabs (w_current, w_width); 00037 if (width < 1) 00038 width = 1; 00039 00040 return width; 00041 } 00042 00043 void gschem_cairo_line (GSCHEM_TOPLEVEL *w_current, int line_end, 00044 int w_line_width, 00045 int w_x1, int w_y1, int w_x2, int w_y2) 00046 { 00047 int x1, y1, x2, y2, line_width; 00048 double offset; 00049 double xoffset = 0; 00050 double yoffset = 0; 00051 int horizontal = 0; 00052 int vertical = 0; 00053 00054 WORLDtoSCREEN (w_current, w_x1, w_y1, &x1, &y1); 00055 WORLDtoSCREEN (w_current, w_x2, w_y2, &x2, &y2); 00056 line_width = screen_width (w_current, w_line_width); 00057 offset = ((line_width % 2) == 0) ? 0 : 0.5; 00058 00059 if (y1 == y2) horizontal = 1; 00060 if (x1 == x2) vertical = 1; 00061 00062 /* Hint so the length of the line runs along a pixel boundary */ 00063 00064 if (horizontal) 00065 yoffset = offset; 00066 else if (vertical) 00067 xoffset = offset; 00068 else 00069 xoffset = yoffset = offset; 00070 00071 /* Now hint the ends of the lines */ 00072 00073 switch (line_end) { 00074 case END_NONE: 00075 /* Line terminates at the passed coordinate */ 00076 00077 /* Add an extra pixel to give an inclusive span */ 00078 if (horizontal) { 00079 if (x1 > x2) x1 += 1; else x2 += 1; 00080 } else if (vertical) { 00081 if (y1 > y2) y1 += 1; else y2 += 1; 00082 } 00083 break; 00084 00085 case END_SQUARE: 00086 case END_ROUND: 00087 /* Line terminates half a width away from the passed coordinate */ 00088 if (horizontal) { 00089 xoffset = offset; 00090 } else if (vertical) { 00091 yoffset = offset; 00092 } 00093 break; 00094 } 00095 00096 cairo_move_to (w_current->cr, x1 + xoffset, y1 + yoffset); 00097 cairo_line_to (w_current->cr, x2 + xoffset, y2 + yoffset); 00098 } 00099 00100 00101 void gschem_cairo_box (GSCHEM_TOPLEVEL *w_current, int line_width, 00102 int x1, int y1, int x2, int y2) 00103 { 00104 int s_line_width; 00105 int s_x1, s_y1, s_x2, s_y2; 00106 double offset; 00107 00108 WORLDtoSCREEN (w_current, x1, y1, &s_x1, &s_y1); 00109 WORLDtoSCREEN (w_current, x2, y2, &s_x2, &s_y2); 00110 s_line_width = screen_width (w_current, line_width); 00111 offset = (line_width == -1 || (s_line_width % 2) == 0) ? 0 : 0.5; 00112 00113 /* Allow filled boxes (inferred from line_width == -1) 00114 * to touch an extra pixel, so the filled span is inclusive */ 00115 if (line_width == -1) { 00116 if (x1 > x2) x1 += 1; else x2 += 1; 00117 if (y1 > y2) y1 += 1; else y2 += 1; 00118 } 00119 00120 cairo_move_to (w_current->cr, s_x2 + offset, s_y2 + offset); 00121 cairo_line_to (w_current->cr, s_x1 + offset, s_y2 + offset); 00122 cairo_line_to (w_current->cr, s_x1 + offset, s_y1 + offset); 00123 cairo_line_to (w_current->cr, s_x2 + offset, s_y1 + offset); 00124 cairo_close_path (w_current->cr); 00125 } 00126 00127 00128 void gschem_cairo_center_box (GSCHEM_TOPLEVEL *w_current, 00129 int center_width, 00130 int line_width, int x, int y, 00131 int half_width, int half_height) 00132 { 00133 int s_center_width, s_line_width; 00134 int s_width, s_height; 00135 double s_half_width, s_half_height; 00136 int s_x, s_y; 00137 int even_center_width; 00138 int even_line_width; 00139 int even_width, even_height; 00140 double x1, y1, x2, y2; 00141 double center_offset; 00142 int do_width_hint = TRUE; 00143 int do_height_hint = TRUE; 00144 00145 WORLDtoSCREEN (w_current, x, y, &s_x, &s_y); 00146 s_width = SCREENabs (w_current, 2 * half_width); 00147 s_height = SCREENabs (w_current, 2 * half_height); 00148 even_width = (s_width % 2 == 0); 00149 even_height = (s_width % 2 == 0); 00150 s_half_width = (double) s_width / 2.; 00151 s_half_height = (double) s_height / 2.; 00152 00153 #if 0 /* Not as nice an effect as with arcs */ 00154 /* Switch off radius hinting for small radii. If we don't, then we get 00155 * a very abrupt transition once the box reaches a single pixel size. */ 00156 if (s_half_width <= 1.) do_width_hint = FALSE; 00157 if (s_half_height <= 1.) do_height_hint = FALSE; 00158 #endif 00159 00160 /* Hint the center of the box based on where a line 00161 * of thickness center_width (world) would drawn */ 00162 s_center_width = screen_width (w_current, center_width); 00163 even_center_width = (center_width == -1 || (s_center_width % 2) == 0); 00164 center_offset = even_center_width ? 0. : 0.5; 00165 00166 /* Hint the half-widths to land the stroke on the pixel grid */ 00167 s_line_width = screen_width (w_current, line_width); 00168 even_line_width = (line_width == -1 || (s_line_width % 2) == 0); 00169 if (do_width_hint) 00170 s_half_width += ((even_center_width == 00171 even_line_width) == even_width ) ? 0. : 0.5; 00172 if (do_height_hint) 00173 s_half_height += ((even_center_width == 00174 even_line_width) == even_height) ? 0. : 0.5; 00175 00176 x1 = (double) s_x + center_offset - s_half_width; 00177 y1 = (double) s_y + center_offset - s_half_height; 00178 x2 = (double) s_x + center_offset + s_half_width; 00179 y2 = (double) s_y + center_offset + s_half_height; 00180 00181 /* Allow filled boxes (inferred from line_width == -1) 00182 * to touch an extra pixel, so the filled span is inclusive */ 00183 if (line_width == -1) { 00184 x2 += 1; y2 += 1; 00185 } 00186 00187 cairo_move_to (w_current->cr, x2, y2); 00188 cairo_line_to (w_current->cr, x1, y2); 00189 cairo_line_to (w_current->cr, x1, y1); 00190 cairo_line_to (w_current->cr, x2, y1); 00191 cairo_close_path (w_current->cr); 00192 } 00193 00194 00195 static inline void do_arc (cairo_t *cr, double x, double y, double radius, 00196 int start_angle, int end_angle) 00197 { 00198 cairo_new_sub_path (cr); 00199 if (start_angle > start_angle + end_angle) { 00200 cairo_arc (cr, x, y, radius, -start_angle * (M_PI / 180.), 00201 (-start_angle - end_angle) * (M_PI / 180.)); 00202 } else { 00203 cairo_arc_negative (cr, x, y, radius, -start_angle * (M_PI / 180.), 00204 (-start_angle - end_angle) * (M_PI / 180.)); 00205 } 00206 } 00207 00208 00209 void gschem_cairo_arc (GSCHEM_TOPLEVEL *w_current, 00210 int width, int x, int y, 00211 int radius, int start_angle, int end_angle) 00212 { 00213 int s_width; 00214 int x1, y1, x2, y2; 00215 double s_x, s_y, s_radius; 00216 double offset; 00217 00218 WORLDtoSCREEN (w_current, x - radius, y + radius, &x1, &y1); 00219 WORLDtoSCREEN (w_current, x + radius, y - radius, &x2, &y2); 00220 s_width = screen_width (w_current, width); 00221 offset = ((s_width % 2) == 0) ? 0 : 0.5; 00222 00223 s_x = (double)(x1 + x2) / 2.; 00224 s_y = (double)(y1 + y2) / 2.; 00225 s_radius = (double)(y2 - y1) / 2.; 00226 00227 cairo_save (w_current->cr); 00228 cairo_translate (w_current->cr, s_x + offset, s_y + offset); 00229 00230 /* Adjust for non-uniform X/Y scale factor. Note that the + 1 00231 allows for the case where x2 == x1 or y2 == y1 */ 00232 cairo_scale (w_current->cr, (double)(x2 - x1 + 1) / 00233 (double)(y2 - y1 + 1), 1.); 00234 00235 do_arc (w_current->cr, 0., 0., (double) s_radius, start_angle, end_angle); 00236 00237 cairo_restore (w_current->cr); 00238 } 00239 00240 00241 void gschem_cairo_center_arc (GSCHEM_TOPLEVEL *w_current, 00242 int center_width, 00243 int line_width, int x, int y, 00244 int radius, int start_angle, int end_angle) 00245 { 00246 int s_center_width, s_line_width; 00247 int s_x, s_y, s_diameter; 00248 int even_center_width; 00249 int even_line_width; 00250 int even_diameter; 00251 double center_offset; 00252 double s_radius; 00253 int do_radius_hint = TRUE; 00254 00255 WORLDtoSCREEN (w_current, x, y, &s_x, &s_y); 00256 s_diameter = SCREENabs (w_current, 2 * radius); 00257 even_diameter = ((s_diameter % 2) == 0); 00258 s_radius = (double) s_diameter / 2.; 00259 00260 /* Switch off radius hinting for small radii. If we don't, then we get 00261 * a very abrupt transition once the arc reaches a single pixel size. */ 00262 if (s_radius <= 1.) do_radius_hint = FALSE; 00263 00264 /* Hint the center of the arc based on where a line 00265 * of thickness center_width (world) would drawn */ 00266 s_center_width = screen_width (w_current, center_width); 00267 even_center_width = (center_width == -1 || (s_center_width % 2) == 0); 00268 center_offset = even_center_width ? 0. : 0.5; 00269 00270 /* Hint the radius to land its extermity on the pixel grid */ 00271 s_line_width = screen_width (w_current, line_width); 00272 even_line_width = (line_width == -1 || (s_line_width % 2) == 0); 00273 if (do_radius_hint) 00274 s_radius += ((even_center_width == 00275 even_line_width) == even_diameter) ? 0. : 0.5; 00276 00277 do_arc (w_current->cr, (double) s_x + center_offset, 00278 (double) s_y + center_offset, 00279 (double) s_radius, 00280 start_angle, end_angle); 00281 } 00282 00283 00284 void gschem_cairo_stroke (GSCHEM_TOPLEVEL *w_current, int line_type, int line_end, 00285 int wwidth, int wlength, int wspace) 00286 { 00287 double offset; 00288 double dashes[4]; 00289 cairo_line_cap_t cap; 00290 cairo_line_cap_t round_cap_if_legible; 00291 int num_dashes; 00292 int width, length, space; 00293 00294 width = screen_width (w_current, wwidth); 00295 length = screen_width (w_current, wlength); 00296 space = screen_width (w_current, wspace); 00297 offset = ((width % 2) == 0) ? 0 : 0.5; 00298 00299 cairo_set_line_width (w_current->cr, width); 00300 cairo_set_line_join (w_current->cr, CAIRO_LINE_JOIN_MITER); 00301 00302 round_cap_if_legible = (width <= 1) ? CAIRO_LINE_CAP_SQUARE : 00303 CAIRO_LINE_CAP_ROUND; 00304 00305 switch (line_end) { 00306 case END_NONE: cap = CAIRO_LINE_CAP_BUTT; break; 00307 case END_SQUARE: cap = CAIRO_LINE_CAP_SQUARE; break; 00308 case END_ROUND: cap = round_cap_if_legible; break; 00309 default: 00310 fprintf(stderr, _("Unknown end for line (%d)\n"), line_end); 00311 cap = CAIRO_LINE_CAP_BUTT; 00312 break; 00313 } 00314 00315 switch (line_type) { 00316 00317 default: 00318 fprintf(stderr, _("Unknown type for stroke (%d) !\n"), line_type); 00319 /* Fall through */ 00320 00321 case TYPE_SOLID: 00322 num_dashes = 0; 00323 00324 cairo_set_dash (w_current->cr, dashes, num_dashes, 0.); 00325 cairo_set_line_cap (w_current->cr, cap); 00326 cairo_stroke (w_current->cr); 00327 break; 00328 00329 case TYPE_DOTTED: 00330 dashes[0] = 0; /* DOT */ 00331 dashes[1] = space; 00332 num_dashes = 2; 00333 00334 cairo_set_dash (w_current->cr, dashes, num_dashes, offset); 00335 cairo_set_line_cap (w_current->cr, round_cap_if_legible); 00336 cairo_stroke (w_current->cr); 00337 break; 00338 00339 case TYPE_DASHED: 00340 dashes[0] = length; /* DASH */ 00341 dashes[1] = space; 00342 num_dashes = 2; 00343 00344 cairo_set_dash (w_current->cr, dashes, num_dashes, 0.); 00345 cairo_set_line_cap (w_current->cr, CAIRO_LINE_CAP_BUTT); 00346 cairo_stroke (w_current->cr); 00347 break; 00348 00349 case TYPE_CENTER: 00350 dashes[0] = length; /* DASH */ 00351 dashes[1] = 2 * space; 00352 num_dashes = 2; 00353 00354 cairo_set_dash (w_current->cr, dashes, num_dashes, 0.); 00355 cairo_set_line_cap (w_current->cr, CAIRO_LINE_CAP_BUTT); 00356 cairo_stroke_preserve (w_current->cr); 00357 00358 dashes[0] = 0; /* DOT */ 00359 dashes[1] = 2 * space + length; 00360 num_dashes = 2; 00361 00362 cairo_set_dash (w_current->cr, dashes, num_dashes, -length - space + offset); 00363 cairo_set_line_cap (w_current->cr, round_cap_if_legible); 00364 cairo_stroke (w_current->cr); 00365 break; 00366 00367 case TYPE_PHANTOM: 00368 dashes[0] = length; /* DASH */ 00369 dashes[1] = 3 * space; 00370 num_dashes = 2; 00371 00372 cairo_set_dash (w_current->cr, dashes, num_dashes, 0.); 00373 cairo_set_line_cap (w_current->cr, CAIRO_LINE_CAP_BUTT); 00374 cairo_stroke_preserve (w_current->cr); 00375 00376 dashes[0] = 0; /* DOT */ 00377 dashes[1] = space; 00378 dashes[2] = 0; /* DOT */ 00379 dashes[3] = 2 * space + length; 00380 num_dashes = 4; 00381 00382 cairo_set_dash (w_current->cr, dashes, num_dashes, -length - space + offset); 00383 cairo_set_line_cap (w_current->cr, round_cap_if_legible); 00384 cairo_stroke (w_current->cr); 00385 break; 00386 } 00387 00388 cairo_set_dash (w_current->cr, NULL, 0, 0.); 00389 } 00390 00391 00392 void gschem_cairo_set_source_color (GSCHEM_TOPLEVEL *w_current, COLOR *color) 00393 { 00394 cairo_set_source_rgba (w_current->cr, (double)color->r / 255.0, 00395 (double)color->g / 255.0, 00396 (double)color->b / 255.0, 00397 (double)color->a / 255.0); 00398 }