/* $Id: guppi-rgb.c,v 1.18 2002/01/20 05:21:09 trow Exp $ */

/*
 * guppi-rgb.c
 *
 * Copyright (C) 2000 EMC Capital Management, Inc.
 *
 * Developed by Jon Trowbridge <trow@gnu.org> and
 * Havoc Pennington <hp@pobox.com>.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
 * USA
 */

#include <stdio.h>
#include "guppi-rgb.h"

#include <config.h>
#include <math.h>
#include <ctype.h>
#include <guppi-convenient.h>


static GHashTable *rgb_hash = NULL;

static gboolean
populate_rgb_hash (const gchar * filename)
{
  FILE *in;
  gchar buffer[128];
  gint r, g, b;

  in = fopen (filename, "r");
  if (in == NULL)
    return FALSE;


  while (fgets (buffer, 127, in)) {
    if (sscanf (buffer, "%d %d %d", &r, &g, &b) == 3) {
      gchar *s = buffer;
      guint32 c = RGBA_TO_UINT (r, g, b, 0xff);

      g_strchomp (buffer);

      while (*s && !isalpha ((guchar)*s))
	++s;

      g_hash_table_insert (rgb_hash, guppi_strdup (s), GUINT_TO_POINTER (c));
    }
  }

  fclose (in);
  return TRUE;
}

static void
init_rgb_hash (void)
{
  const gchar *rgb_paths[] = {
    "/usr/X11R6/lib/X11/rgb.txt",
    "/usr/X/lib/X11/rgb.txt",
    "/usr/X/lib/rgb.txt",
    NULL
  };
  gint i;

  if (rgb_hash != NULL)
    return;

  if (rgb_hash == NULL)
    rgb_hash = g_hash_table_new (g_str_hash, g_str_equal);

  /* A horrible hack */
  for (i = 0; rgb_paths[i]; ++i)
    if (populate_rgb_hash (rgb_paths[i]))
      return;

  /* One way to get around this might be for Guppi to ship with its
     own copy of rgb.txt.  Ugly, but it would let us do color name->rgba
     conversions w/o needing an X server around... */

  g_warning ("Couldn't find rgb.txt!");
}

guint32
guppi_str2color_rgba (const gchar *color_name)
{
  gint r, g, b, a, rv;

  rv = sscanf (color_name, "#%2x%2x%2x%2x", &r, &g, &b, &a);
  if (rv == 3) {
    return RGB_TO_UINT (r, g, b);
  } else if (rv == 4) {
    return RGBA_TO_UINT (r, g, b, a);
  }

  /* If we have an X server, we try to use gdk. */
  if (gdk_init_check (NULL, NULL)) {
    GdkColor c;
    if (gdk_color_parse (color_name, &c)) {
      return RGBA_TO_UINT (c.red >> 8, c.green >> 8, c.blue >> 8, 0xff);
    } else {
      return 0;
    }
  } else {
    gpointer c;
    
    if (rgb_hash == NULL)
      init_rgb_hash ();

    c = g_hash_table_lookup (rgb_hash, color_name);

    return GPOINTER_TO_UINT (c);
  }
}

void
guppi_paint_horiz (GnomeCanvasBuf * buf, gint x0, gint x1, gint y,
		   guint32 color)
{
  guchar *p;
  guint r, g, b, a;
  gint a0, a1;

  g_return_if_fail (buf != NULL);

  if (!BUF_INBOUNDS_Y (buf, y))
    return;

  guppi_2sort_i (&x0, &x1);

  a0 = MAX (buf->rect.x0, x0);
  a1 = MIN (buf->rect.x1, x1);
  UINT_TO_RGBA (color, &r, &g, &b, &a);

  if (a0 < a1) {
    p = BUF_PTR (buf, a0, y);
    while (a0 < a1) {
      PIXEL_RGBA (p, r, g, b, a);
      ++a0;
      p += 3;
    }
  }
}

void
guppi_paint_vert (GnomeCanvasBuf * buf, gint x, gint y0, gint y1,
		  guint32 color)
{
  guchar *p;
  guint r, g, b, a;
  gint b0, b1;

  g_return_if_fail (buf != NULL);

  if (!BUF_INBOUNDS_X (buf, x))
    return;

  guppi_2sort_i (&y0, &y1);

  b0 = MAX (buf->rect.y0, y0);
  b1 = MIN (buf->rect.y1, y1);
  UINT_TO_RGBA (color, &r, &g, &b, &a);

  if (b0 < b1) {
    p = BUF_PTR (buf, x, b0);
    while (b0 < b1) {
      PIXEL_RGBA (p, r, g, b, a);
      ++b0;
      p += buf->buf_rowstride;
    }
  }
}


void
guppi_paint_box (GnomeCanvasBuf * buf,
		 gint x0, gint y0, gint x1, gint y1, guint32 color)
{
  guchar *p;
  guchar *pp;
  guint r, g, b, a;
  gint a0, a1, b0, b1, i, j;

  g_return_if_fail (buf != NULL);

  if (x0 > x1) {
    gint t = x0;
    x0 = x1;
    x1 = t;
  }

  if (y0 > y1) {
    gint t = y0;
    y0 = y1;
    y1 = t;
  }

  a0 = MAX (buf->rect.x0, x0);
  a1 = MIN (buf->rect.x1, x1);
  b0 = MAX (buf->rect.y0, y0);
  b1 = MIN (buf->rect.y1, y1);

  UINT_TO_RGBA (color, &r, &g, &b, &a);

  if (a0 < a1 && b0 < b1) {
    p = BUF_PTR (buf, a0, b0);
    for (j = b0; j < b1; ++j) {
      pp = p;
      for (i = a0; i < a1; ++i) {
	PIXEL_RGBA (pp, r, g, b, a);
	pp += 3;
      }
      p += buf->buf_rowstride;
    }
  }
}

/* This could be optimized a lot.
   Right now it is optimized for correctness. */
void
guppi_paint_soft_box (GnomeCanvasBuf * buf,
		      double x0, double y0, double x1, double y1,
		      guint32 color)
{
  guint r, g, b, a, aa;
  gint out_x0, out_x1, out_y0, out_y1;
  gint in_x0, in_x1, in_y0, in_y1;
  gboolean edge_w, edge_e, edge_n, edge_s;

  g_return_if_fail (buf != NULL);

  if (x0 > x1) {
    double t = x0;
    x0 = x1;
    x1 = t;
  }

  if (y0 > y1) {
    double t = y0;
    y0 = y1;
    y1 = t;
  }

  UINT_TO_RGBA (color, &r, &g, &b, &a);

  out_x0 = (gint) floor (x0);
  out_x1 = (gint) ceil (x1);
  out_y0 = (gint) floor (y0);
  out_y1 = (gint) ceil (y1);

  in_x0 = (gint) ceil (x0);
  in_x1 = (gint) floor (x1);
  in_y0 = (gint) ceil (y0);
  in_y1 = (gint) floor (y1);

  edge_w = (in_x0 != out_x0);
  edge_e = (in_x1 != out_x1);
  edge_n = (in_y0 != out_y0);
  edge_s = (in_y1 != out_y1);

  guppi_paint_box (buf, in_x0, in_y0, in_x1, in_y1, color);

  if (edge_w) {
    aa = (guint) rint (fabs (x0 - in_x0) * a);
    guppi_paint_vert (buf, out_x0, in_y0, in_y1,
		      UINT_RGBA_CHANGE_A (color, aa));
  }

  if (edge_e) {
    aa = (guint) rint (fabs (x1 - in_x1) * a);
    guppi_paint_vert (buf, out_x1 - 1, in_y0, in_y1,
		      UINT_RGBA_CHANGE_A (color, aa));
  }

  if (edge_n) {
    aa = (guint) rint (fabs (y0 - in_y0) * a);
    guppi_paint_horiz (buf, in_x0, in_x1, out_y0,
		       UINT_RGBA_CHANGE_A (color, aa));
  }

  if (edge_s) {
    aa = (guint) rint (fabs (y1 - in_y1) * a);
    guppi_paint_horiz (buf, in_x0, in_x1, out_y1 - 1,
		       UINT_RGBA_CHANGE_A (color, aa));
  }

  if (edge_w && edge_n) {
    aa = (guint) rint (fabs ((x0 - in_x0) * (y0 - in_y0)) * a);
    PAINT_DOT (buf, r, g, b, aa, out_x0, out_y0);
  }

  if (edge_e && edge_n) {
    aa = (guint) rint (fabs ((x1 - in_x1) * (y0 - in_y0)) * a);
    PAINT_DOT (buf, r, g, b, aa, out_x1 - 1, out_y0);
  }

  if (edge_w && edge_s) {
    aa = (guint) rint (fabs ((x0 - in_x0) * (y1 - in_y1)) * a);
    PAINT_DOT (buf, r, g, b, aa, out_x0, out_y1 - 1);
  }

  if (edge_e && edge_s) {
    aa = (guint) rint (fabs ((x1 - in_x1) * (y1 - in_y1)) * a);
    PAINT_DOT (buf, r, g, b, aa, out_x1 - 1, out_y1 - 1);
  }
}

/**********************************************************************/

void
guppi_paint_sharp_box (GnomeCanvasBuf *buf,
		       double x0, double y0, double x1, double y1,
		       guint32 color)
{
  int w, h, x, y;

  g_return_if_fail (buf != NULL);

  guppi_2sort (&x0, &x1);
  guppi_2sort (&y0, &y1);

  if (x1 - x0 < 1e-8 || y1 - y0 < 1e-8)
    return;

  w = (gint) rint (x1 - x0);
  h = (gint) rint (y1 - y0);
  x = (gint) rint ((x0 + x1) / 2) - w / 2;
  y = (gint) rint ((y0 + y1) / 2) - h / 2;

  if (w < 1)
    w = 1;
  if (h < 1)
    h  = 1;

  if (x1 - x0 < 1 || y1 - y0 < 1) {
    guint new_a = (guint) rint (UINT_RGBA_A (color) * MIN (x1 - x0, y1 - y0));
    color = UINT_RGBA_CHANGE_A (color, new_a);
  }

  guppi_paint_box (buf, x, y, x+w, y+h, color);
}

/**********************************************************************/

static gboolean
line_segment_window_query (double seg_x0, double seg_y0,
			   double seg_x1, double seg_y1,
			   double x0, double y0,
			   double x1, double y1)
{
  double Ax, Ay, Bx, By, Cx, Cy, num1, num2, denom;
  gboolean miss;

  /* If either end-point of the line segment lies inside of the window,
     we intersect. */
  if ((x0 <= seg_x0 && seg_x0 <= x1 && y0 <= seg_y0 && seg_y0 <= y1) ||
      (x0 <= seg_x1 && seg_x1 <= x1 && y0 <= seg_y1 && seg_y1 <= y1))
    return TRUE;

  /* This is the algorithm from "Graphics Gems III" */

  /*
     P1 = (seg_x0, seg_y0)
     P2 = (seg_x1, seg_y1)
   */

  Ax = seg_x1 - seg_x0;
  Ay = seg_y1 - seg_y0;

  /*
     Check #1
     P3 = (x0, y0)
     P4 = (x1, y0)
   */
  Bx = x0 - x1;
  /* By = 0; */
  Cx = seg_x0 - x0;
  Cy = seg_y0 - y0;

  num1 = -Bx * Cy;
  num2 = Ax * Cy - Ay * Cx;
  denom = Ay * Bx;

  miss = (((denom > 0) &&
	   (num1 < 0 || num1 > denom || num2 < 0 || num2 > denom)) ||
	  ((denom < 0) &&
	   (num1 > 0 || num1 < denom || num2 > 0 || num2 < denom)));

  if (!miss)
    return TRUE;

  /*
     Check #2
     P3 = (x0, y0)
     P4 = (x0, y1)
   */
  /* Bx = 0; */
  By = y0 - y1;
  Cx = seg_x0 - x0;
  Cy = seg_y0 - y0;

  num1 = By * Cx;
  num2 = Ax * Cy - Ay * Cx;
  denom = -Ax * By;

  miss = (((denom > 0) &&
	   (num1 < 0 || num1 > denom || num2 < 0 || num2 > denom)) ||
	  ((denom < 0) &&
	   (num1 > 0 || num1 < denom || num2 > 0 || num2 < denom)));

  if (!miss)
    return TRUE;


  /*
     Check #3
     P3 = (x1, y1)
     P4 = (x1, y0)
   */
  /* Bx = 0; */
  By = y1 - y0;
  Cx = seg_x0 - x1;
  Cy = seg_y0 - y1;

  num1 = By * Cx;
  num2 = Ax * Cy - Ay * Cx;
  denom = -Ax * By;

  miss = (((denom > 0) &&
	   (num1 < 0 || num1 > denom || num2 < 0 || num2 > denom)) ||
	  ((denom < 0) &&
	   (num1 > 0 || num1 < denom || num2 > 0 || num2 < denom)));

  if (!miss)
    return TRUE;


  /*
     Check #4
     P3 = (x1, y1)
     P4 = (x0, y1)
   */
  Bx = x1 - x0;
  /* By = 0; */
  Cx = seg_x0 - x1;
  Cy = seg_y0 - y1;

  num1 = -Bx * Cy;
  num2 = Ax * Cy - Ay * Cx;
  denom = Ay * Bx;

  miss = (((denom > 0) &&
	   (num1 < 0 || num1 > denom || num2 < 0 || num2 > denom)) ||
	  ((denom < 0) &&
	   (num1 > 0 || num1 < denom || num2 > 0 || num2 < denom)));

  if (!miss)
    return TRUE;

  return FALSE;
}

void
guppi_paint_wide_line (GnomeCanvasBuf * buf,
		       double x0, double y0, double x1, double y1,
		       double width, guint32 color)
{
  static gboolean broken_warning = FALSE;

  double d, ax, ay, dx, dy;
  gint x, y, xdest, ydest;
  gint sx, sy;
  guint r, g, b, a;

  g_return_if_fail (buf != NULL);
  g_return_if_fail (width >= 0);

  if (width == 0)
    return;

  if (!broken_warning) {

    fprintf (stderr,
	     "\nguppi_paint_wide_line() is currently very broken, and\n");
    fprintf (stderr,
	     "will only paint 1-pixel thick non-anti-aliased lines.\n");
    fprintf (stderr, "This will eventually be remedied.\n\n");

    broken_warning = TRUE;
  }

  UINT_TO_RGBA (color, &r, &g, &b, &a);

  dx = x1 - x0;
  ax = fabs (dx) * 2;
  sx = dx > 0 ? 1 : -1;
  dy = y1 - y0;
  ay = fabs (dy) * 2;
  sy = dy > 0 ? 1 : -1;

  x = (gint) rint (x0);
  y = (gint) rint (y0);
  xdest = (gint) rint (x1);
  ydest = (gint) rint (y1);

  if (ax > ay) {

    d = ay - ax / 2;

    while (x != xdest) {

      if (BUF_INBOUNDS_X (buf, x) && BUF_INBOUNDS_Y (buf, y)) {
	PAINT_DOT (buf, r, g, b, a, x, y);
      }

      if (d > 0) {
	y += sy;
	d = d - ax;
      }
      x += sx;
      d = d + ay;
    }
  } else {

    d = ax - ay / 2;

    while (y != ydest) {

      if (BUF_INBOUNDS_X (buf, x) && BUF_INBOUNDS_Y (buf, y)) {
	PAINT_DOT (buf, r, g, b, a, x, y);
      }

      if (d > 0) {
	x += sx;
	d = d - ay;
      }
      y += sy;
      d = d + ax;
    }
  }
}

#define MIN_LINE_WIDTH 0.1
#define MAX_LINE_WIDTH 20

void
guppi_paint_wide_line_alt (GnomeCanvasBuf *buf,
			   double d_x0, double d_y0, double d_x1, double d_y1,
			   double d_width,
			   guint32 color0, guint32 color1)
{
  gint x0 = (gint) d_x0;
  gint x1 = (gint) d_x1;
  gint y0 = (gint) d_y0;
  gint y1 = (gint) d_y1;
  gint dx, dy;
  gint xstep, ystep;
  guchar *pixelPtr;
  gint pixelXstep, pixelYstep;

  guint ir0, ig0, ib0, ia0, ir1, ig1, ib1, ia1;
  double r0, g0, b0, a0, dr, dg, db, da;

  gint width, min, max;

  UINT_TO_RGBA (color0, &ir0, &ig0, &ib0, &ia0);
  UINT_TO_RGBA (color1, &ir1, &ig1, &ib1, &ia1);

  r0 = ir0 / 255.0;
  g0 = ig0 / 255.0;
  b0 = ib0 / 255.0;
  a0 = ia0 / 255.0;

  dr = ir1 / 255.0 - r0;
  dg = ig1 / 255.0 - g0;
  db = ib1 / 255.0 - b0;
  da = ia1 / 255.0 - a0;
  

  width = (gint) CLAMP( d_width, MIN_LINE_WIDTH, MAX_LINE_WIDTH );
  min = (width-1) / -2;
  max = min + width - 1;

  dx = x1 - x0;
  dy = y1 - y0;
  if (dx==0 && dy==0) {
    return;
  }

  pixelPtr = BUF_PTR (buf, x0, y0);

  if (dx<0) {
    dx = -dx;   /* make positive */
    xstep = -1;
    pixelXstep = -3;
  } else {
    xstep = 1;
    pixelXstep = 3;
  }

  if (dy<0) {
    dy = -dy;   /* make positive */
    ystep = -1;
    pixelYstep = -buf->buf_rowstride;
  } else {
    ystep = 1;
    pixelYstep = buf->buf_rowstride;
  }

  /*
   * Draw
   */
  
  if (dx>dy) {
    /* X-major line */
    gint i;
    gint errorInc = dy+dy;
    gint error = errorInc-dx;
    gint errorDec = error-dx;

    dr /= dx;   /* convert from whole line delta to per-pixel delta */
    dg /= dx;
    db /= dx;
    da /= dx;

    for (i=0;i<dx;i++) {
      gint yy;
      gint ymin = y0 + min;
      gint ymax = y0 + max;

      ir0 = (gint)(r0 * 255);
      ig0 = (gint)(g0 * 255);
      ib0 = (gint)(b0 * 255);
      ia0 = (gint)(a0 * 255);

      if (BUF_INBOUNDS_X (buf, x0)) {
	guchar *p = pixelPtr + min * buf->buf_rowstride;

	for (yy=ymin;yy<=ymax;yy++) {
	  if (BUF_INBOUNDS_Y (buf, yy)) {
	    //p = BUF_PTR (buf, x0, yy);
	    PIXEL_RGBA (p, ir0, ig0, ib0, ia0);
	  }
	  p += buf->buf_rowstride;
	}
      }

      x0 += xstep;

      r0 += dr;
      g0 += dg;
      b0 += db;
      a0 += da;

      pixelPtr += pixelXstep;
      
      if (error<0) {
	error += errorInc;
      }	else {
	error += errorDec;
	y0 += ystep;

	pixelPtr += pixelYstep;
      }
    }
  } else {
      /* Y-major line */
    gint i;
    gint errorInc = dx+dx;
    gint error = errorInc-dy;
    gint errorDec = error-dy;
    
    dr /= dy;   /* convert from whole line delta to per-pixel delta */
    dg /= dy;
    db /= dy;
    da /= dy;

    for (i=0;i<dy;i++) {
      gint xx;
      gint xmin = x0 + min;
      gint xmax = x0 + max;

      ir0 = (gint)(r0 * 255);
      ig0 = (gint)(g0 * 255);
      ib0 = (gint)(b0 * 255);
      ia0 = (gint)(a0 * 255);

      if (BUF_INBOUNDS_Y (buf, y0)) {
	guchar *p = pixelPtr + 3*min;
	for (xx=xmin;xx<=xmax;xx++) {
	  if (BUF_INBOUNDS_X (buf, xx)) {
	    //p = BUF_PTR (buf, xx, y0);
	    PIXEL_RGBA (p, ir0, ig0, ib0, ia0);
	  }
	  p += 3;
	}
      }

      y0 += ystep;
      r0 += dr;
      g0 += dg;
      b0 += db;
      a0 += da;

      pixelPtr += pixelYstep;
      
      if (error<0) {
	error += errorInc;
      } else {
	error += errorDec;
	x0 += xstep;
	pixelPtr += pixelXstep;
      }
    }
  }
}

void
guppi_paint_wide_curve (GnomeCanvasBuf *buf,
			ArtVpath *path,
			double width,
			guint32 color)
{
  double d, ax, ay, dx, dy, x0=0, y0=0, x1=0, y1=0;
  gint wx0, wy0, wx1, wy1;
  gint x, y, xdest, ydest, i;
  guint sx, sy;
  guint r, g, b, a;
  gboolean stroking = FALSE;

  g_return_if_fail (buf != NULL);
  g_return_if_fail (width >= 0);

  if (width == 0)
    return;

  UINT_TO_RGBA (color, &r, &g, &b, &a);
  if (a == 0)
    return;

  i = 0;

  wx0 = buf->rect.x0-1;
  wy0 = buf->rect.y0-1;
  wx1 = buf->rect.x1+1;
  wy1 = buf->rect.y1+1;

  while (path[i].code != ART_END) {

    if (path[i].code == ART_MOVETO || path[i].code == ART_MOVETO_OPEN) {
      
      stroking = FALSE;

    } else if (i > 0 && (path[i].code == ART_LINETO || path[i].code == ART_CURVETO)) {

      x0 = path[i-1].x;
      y0 = path[i-1].y;
      x1 = path[i].x;
      y1 = path[i].y;

      stroking = TRUE;

    } else {
      /* Bad path code. */
      g_assert_not_reached ();
    }

    if (stroking
	&& (gint)rint (x0) == (gint)rint (x1)
	&& (gint)rint (y0) == (gint)rint (y1)) 
      stroking = FALSE;

    if (stroking
	&& !line_segment_window_query (x0, y0, x1, y1,
					   buf->rect.x0, buf->rect.y0,
					   buf->rect.x1, buf->rect.y1))
      stroking = FALSE;
				      
    
    if (stroking) {
      double A = y0 - y1;
      double B = x1 - x0;
      double C = x1 * y0 - x0 * y1;

      /* Clipping */
      if (B != 0) {
	if (x0 < wx0) {
	  x0 = wx0;
	  y0 = (C - A*wx0)/B;
	} else if (wx1 < x0) {
	  x0 = wx1;
	  y0 = (C - A*wx1)/B;
	}

	if (x1 < wx0) {
	  x1 = wx0;
	  y1 = (C - A*wx0)/B;
	} else if (wx1 < x1) {
	  x1 = wx1;
	  y1 = (C - A*wx1)/B;
	}
      }

      if (A != 0) {

	if (y0 < wy0) {
	  x0 = (C - B*wy0)/A;
	  y0 = wy0;
	} else if (wy1 < y0) {
	  x0 = (C - B*wy1)/A;
	  y0 = wy1;
	}

	if (y1 < wy0) {
	  x1 = (C - B*wy0)/A;
	  y1 = wy0;
	} else if (wy1 < y1) {
	  x1 = (C - B*wy1)/A;
	  y1 = wy1;
	}

      }

      /* The line-drawing algorithm */
      
      dx = x1 - x0;
      ax = fabs (dx) * 2;
      sx = dx > 0 ? +1 : -1;

      dy = y1 - y0;
      ay = fabs (dy) * 2;
      sy = dy > 0 ? +1 : -1;

      x = (gint) rint(x0);
      y = (gint) rint(y0);

      xdest = (gint) rint (x1);
      ydest = (gint) rint (y1);

      if (ax > ay) {
	
	d = ay - ax / 2;

	while (x != xdest) {
      
	  if (BUF_INBOUNDS_X (buf, x) && BUF_INBOUNDS_Y (buf, y)) {
	    PAINT_DOT (buf, r, g, b, a, x, y);
	  }
	  
	  if (d > 0) {
	    y += sy;
	    d -= ax;
	  }
	  x += sx;
	  d += ay;
	}
      } else {
	
	d = ax - ay / 2;

	while (y != ydest) {
	
	  if (BUF_INBOUNDS_X (buf, x) && BUF_INBOUNDS_Y (buf, y)) {
	    PAINT_DOT (buf, r, g, b, a, x, y);
	  }

	  if (d > 0) {
	    x += sx;
	    d -= ay;
	  }
	  y += sy;
	  d += ax;
	}
      }

      stroking = FALSE;
    }
    
    ++i;
  }
}


/* $Id: guppi-rgb.c,v 1.18 2002/01/20 05:21:09 trow Exp $ */


syntax highlighted by Code2HTML, v. 0.9.1