/* $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