gschem

gschem_cairo.c

Go to the documentation of this file.
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 }
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Defines