Commit 2425bc4a authored by Raph Levien's avatar Raph Levien

Checking in some of the antialiased renderer code. However, more aggressive

bbox culling is indicated here.
parent 46588bd1
Mon Nov 30 01:30:25 1998 Raph Levien <raph@gimp.org>
* testart.c: added vector path rendering stuff. Some of it needs
to go out of the test framework and into the module, but the
api hasn't settled down entirely yet (in the real code, all
x's in the step field are within bounds).
* art_svp_render_aa.c, art_svp_render_aa.c.h: added.
* art_svp_vpath.c, art_svp_vpath.h: added.
* art_pathcode.h: added ART_MOVETO_OPEN (libart uses an
ART_MOVETO_OPEN code at the beginning to indicate an open path,
while PostScript uses the lack of a closepath at the end).
* art_vpath_bpath.c, art_vpath_bpath.h: fixed it up, added
flatness arg to api.
* Makefile.am: added new source files.
Wed Nov 25 17:19:44 1998 Raph Levien <raph@gimp.org>
* art_svp.h, art_svp.c: added, basic constructors for sorted
vector paths.
Sun Nov 22 23:21:09 1998 Raph Levien <raph@gimp.org>
* Makefile.am (libart_lgplincdir): Fixed stupid bug in naming of
......
......@@ -13,11 +13,15 @@ libart_lgpl_la_SOURCES = \
art_misc.c \
art_rect.c \
art_rect_uta.c \
art_svp.c \
art_svp_render_aa.c \
art_svp_vpath.c \
art_uta.c \
art_uta_ops.c \
art_uta_rect.c \
art_uta_vpath.c \
art_vpath.c
art_vpath.c \
art_vpath_bpath.c
libart_lgpl_la_LDFLAGS = -version-info @LIBART_VERSION_INFO@
......@@ -29,11 +33,17 @@ libart_lgplinc_HEADERS = \
art_point.h \
art_rect.h \
art_rect_uta.h \
art_svp.h \
art_svp_render_aa.h \
art_svp_vpath.h \
art_uta.h \
art_uta_ops.h \
art_uta_rect.h \
art_uta_vpath.h \
art_vpath.h
art_vpath.h \
art_vpath_bpath.h
INCLUDES = -I$(top_srcdir)/..
DEPS = $(top_builddir)/libart_lgpl.la
LDADDS = $(top_builddir)/libart_lgpl.la
......
......@@ -24,9 +24,9 @@
extern "C" {
#endif /* __cplusplus */
/* I probably want to add an ART_MOVETO_OPEN */
typedef enum {
ART_MOVETO,
ART_MOVETO_OPEN,
ART_CURVETO,
ART_LINETO,
ART_END
......
/* Libart_LGPL - library of basic graphic primitives
* Copyright (C) 1998 Raph Levien
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
/* The spiffy antialiased renderer for sorted vector paths. */
#include <math.h>
#include "art_misc.h"
#include "art_rect.h"
#include "art_svp.h"
#include "art_svp_render_aa.h"
static void
art_svp_render_insert_active (int i, int *active_segs, int n_active_segs,
double *seg_x, double *seg_dx)
{
int j;
double x;
int tmp1, tmp2;
/* this is a cheap hack to get ^'s sorted correctly */
x = seg_x[i] + 0.001 * seg_dx[i];
for (j = 0; j < n_active_segs && seg_x[active_segs[j]] < x; j++);
tmp1 = i;
while (j <= n_active_segs)
{
tmp2 = active_segs[j];
active_segs[j] = tmp1;
tmp1 = tmp2;
j++;
}
}
static void
art_svp_render_delete_active (int *active_segs, int j, int n_active_segs)
{
int k;
for (k = j; k < n_active_segs; k++)
active_segs[k] = active_segs[k + 1];
}
static int
art_svp_render_step_compare (const void *s1, const void *s2)
{
const ArtSVPRenderAAStep *step1 = s1;
const ArtSVPRenderAAStep *step2 = s2;
return step1->x - step2->x;
}
void
art_svp_render_aa (const ArtSVP *svp,
int x0, int y0, int x1, int y1,
void (*callback) (void *callback_data,
int y,
int start,
ArtSVPRenderAAStep *steps, int n_steps),
void *callback_data)
{
int *active_segs;
int n_active_segs;
int *cursor;
double *seg_x;
double *seg_dx;
int i, j;
int y;
int seg_index;
int x;
ArtSVPRenderAAStep *steps;
int n_steps;
int n_steps_max;
double y_top, y_bot;
double x_top, x_bot;
double x_min, x_max;
int ix_min, ix_max;
double delta, last, this; /* delta should be int too? */
int xdelta;
double rslope, drslope;
int start;
const ArtSVPSeg *seg;
int curs;
active_segs = art_new (int, svp->n_segs);
cursor = art_new (int, svp->n_segs);
seg_x = art_new (double, svp->n_segs);
seg_dx = art_new (double, svp->n_segs);
n_steps_max = 256;
steps = art_new (ArtSVPRenderAAStep, n_steps_max);
n_active_segs = 0;
i = 0;
/* iterate through the scanlines */
for (y = y0; y < y1; y++)
{
/* insert new active segments */
for (; i < svp->n_segs && svp->segs[i].bbox.y0 < y + 1; i++)
{
if (svp->segs[i].bbox.y1 > y)
{
seg = &svp->segs[i];
/* move cursor to topmost vector which overlaps [y,y+1) */
for (curs = 0; seg->points[curs + 1].y < y; curs++);
cursor[i] = curs;
seg_dx[i] = (seg->points[curs + 1].x - seg->points[curs].x) /
(seg->points[curs + 1].y - seg->points[curs].y);
seg_x[i] = seg->points[curs].x +
(y - seg->points[curs].y) * seg_dx[i];
art_svp_render_insert_active (i, active_segs, n_active_segs++,
seg_x, seg_dx);
}
}
n_steps = 0;
/* render the runlengths, advancing and deleting as we go */
start = 0x8000;
for (j = 0; j < n_active_segs; j++)
{
seg_index = active_segs[j];
seg = &svp->segs[seg_index];
curs = cursor[seg_index];
while (curs != seg->n_points - 1 &&
seg->points[curs].y < y + 1)
{
y_top = y;
if (y_top < seg->points[curs].y)
y_top = seg->points[curs].y;
y_bot = y + 1;
if (y_bot > seg->points[curs + 1].y)
y_bot = seg->points[curs + 1].y;
if (y_top != y_bot) {
delta = (seg->dir ? 16711680.0 : -16711680.0) *
(y_bot - y_top);
x_top = seg_x[seg_index] + (y_top - y) * seg_dx[seg_index];
x_bot = seg_x[seg_index] + (y_bot - y) * seg_dx[seg_index];
if (x_top < x_bot)
{
x_min = x_top;
x_max = x_bot;
}
else
{
x_min = x_bot;
x_max = x_top;
}
ix_min = floor (x_min);
ix_max = floor (x_max);
if (ix_min >= x1)
{
/* skip; it starts to the right of the render region */
}
else if (ix_max < x0)
/* it ends to the left of the render region */
start += delta;
else if (ix_min == ix_max)
{
/* case 1, antialias a single pixel */
if (n_steps + 2 > n_steps_max)
art_expand (steps, ArtSVPRenderAAStep, n_steps_max);
xdelta = (ix_min + 1 - (x_min + x_max) * 0.5) * delta;
steps[n_steps].x = ix_min;
steps[n_steps].delta = xdelta;
n_steps++;
xdelta = delta - xdelta;
steps[n_steps].x = ix_min + 1;
steps[n_steps].delta = xdelta;
n_steps++;
}
else
{
/* case 2, antialias a run */
if (n_steps + x_max + 1 - x_min > n_steps_max)
{
do
n_steps_max <<= 1;
while (n_steps + x_max + 1 - x_min > n_steps_max);
steps = art_renew (steps, ArtSVPRenderAAStep,
n_steps_max);
}
rslope = 1.0 / fabs (seg_dx[seg_index]);
drslope = delta * rslope;
last =
drslope * 0.5 *
(ix_min + 1 - x_min) * (ix_min + 1 - x_min);
xdelta = last;
steps[n_steps].x = ix_min;
steps[n_steps].delta = last;
n_steps++;
for (x = ix_min + 1; x < ix_max; x++)
{
this = (seg->dir ? 16711680.0 : -16711680.0) * rslope * (x + 0.5 - x_min);
xdelta = this - last;
last = this;
steps[n_steps].x = x;
steps[n_steps].delta = xdelta;
n_steps++;
}
this =
delta * (1 - 0.5 *
(x_max - ix_max) * (x_max - ix_max) *
rslope);
xdelta = this - last;
last = this;
steps[n_steps].x = ix_max;
steps[n_steps].delta = xdelta;
n_steps++;
xdelta = delta - last;
steps[n_steps].x = ix_max + 1;
steps[n_steps].delta = xdelta;
n_steps++;
}
}
curs++;
if (curs != seg->n_points - 1 &&
seg->points[curs].y < y + 1)
{
seg_dx[seg_index] = (seg->points[curs + 1].x -
seg->points[curs].x) /
(seg->points[curs + 1].y - seg->points[curs].y);
seg_x[seg_index] = seg->points[curs].x +
(y - seg->points[curs].y) * seg_dx[seg_index];
}
/* break here, instead of duplicating predicate in while? */
}
if (seg->points[curs].y >= y + 1)
{
curs--;
cursor[seg_index] = curs;
seg_x[seg_index] += seg_dx[seg_index];
}
else
{
art_svp_render_delete_active (active_segs, j--,
--n_active_segs);
}
}
/* sort the steps */
if (n_steps)
qsort (steps, n_steps, sizeof(ArtSVPRenderAAStep),
art_svp_render_step_compare);
(*callback) (callback_data, y, start, steps, n_steps);
}
art_free (steps);
art_free (seg_dx);
art_free (seg_x);
art_free (cursor);
art_free (active_segs);
}
/* Libart_LGPL - library of basic graphic primitives
* Copyright (C) 1998 Raph Levien
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#ifndef __ART_SVP_RENDER_AA_H__
#define __ART_SVP_RENDER_AA_H__
/* The spiffy antialiased renderer for sorted vector paths. */
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
typedef struct _ArtSVPRenderAAStep ArtSVPRenderAAStep;
struct _ArtSVPRenderAAStep {
int x;
int delta; /* stored with 16 fractional bits */
};
void
art_svp_render_aa (const ArtSVP *svp,
int x0, int y0, int x1, int y1,
void (*callback) (void *callback_data,
int y,
int start,
ArtSVPRenderAAStep *steps, int n_steps),
void *callback_data);
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* __ART_SVP_RENDER_AA_H__ */
/* Libart_LGPL - library of basic graphic primitives
* Copyright (C) 1998 Raph Levien
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
/* Sort vector paths into sorted vector paths */
#include <stdlib.h>
#include <math.h>
#include "art_misc.h"
#include "art_vpath.h"
#include "art_svp.h"
static int
art_svp_seg_compare (const void *s1, const void *s2);
/* reverse a list of points in place */
static void
reverse_points (ArtPoint *points, int n_points)
{
int i;
ArtPoint tmp_p;
for (i = 0; i < (n_points >> 1); i++)
{
tmp_p = points[i];
points[i] = points[n_points - (i + 1)];
points[n_points - (i + 1)] = tmp_p;
}
}
/* convert a vec_path to sorted form */
ArtSVP *
art_svp_from_vpath (ArtVpath *vpath)
{
int n_segs, n_segs_max;
ArtSVP *svp;
int dir;
int new_dir;
int i;
ArtPoint *points;
int n_points, n_points_max;
double x, y;
double x_min, x_max;
n_segs = 0;
n_segs_max = 16;
svp = (ArtSVP *)art_alloc (sizeof(ArtSVP) +
(n_segs_max - 1) * sizeof(ArtSVPSeg));
dir = 0;
n_points = 0;
n_points_max = 0;
points = NULL;
i = 0;
x = y = 0; /* unnecessary, given "first code must not be LINETO" invariant,
but it makes gcc -Wall -ansi -pedantic happier */
x_min = x_max = 0; /* same */
while (vpath[i].code != ART_END) {
if (vpath[i].code == ART_MOVETO || vpath[i].code == ART_MOVETO_OPEN)
{
if (points != NULL && n_points >= 2)
{
if (n_segs == n_segs_max)
{
n_segs_max <<= 1;
svp = (ArtSVP *)art_realloc (svp, sizeof(ArtSVP) +
(n_segs_max - 1) *
sizeof(ArtSVPSeg));
}
svp->segs[n_segs].n_points = n_points;
svp->segs[n_segs].dir = (dir > 0);
if (dir < 0)
reverse_points (points, n_points);
svp->segs[n_segs].points = points;
n_segs++;
}
else if (points == NULL)
{
n_points_max = 4;
points = art_new (ArtPoint, n_points_max);
}
n_points = 1;
points[0].x = x = vpath[i].x;
points[0].y = y = vpath[i].y;
x_min = x;
x_max = x;
dir = 0;
}
else /* must be LINETO */
{
new_dir = (vpath[i].y > y ||
(vpath[i].y == y && vpath[i].x > x)) ? 1 : -1;
if (dir && dir != new_dir)
{
/* new segment */
x = points[n_points - 1].x;
y = points[n_points - 1].y;
if (n_segs == n_segs_max)
{
n_segs_max <<= 1;
svp = (ArtSVP *)art_realloc (svp, sizeof(ArtSVP) +
(n_segs_max - 1) *
sizeof(ArtSVPSeg));
}
svp->segs[n_segs].n_points = n_points;
svp->segs[n_segs].dir = (dir > 0);
if (dir < 0)
reverse_points (points, n_points);
svp->segs[n_segs].points = points;
svp->segs[n_segs].bbox.x0 = x_min;
svp->segs[n_segs].bbox.x1 = x_max;
svp->segs[n_segs].bbox.y0 = points[0].y;
svp->segs[n_segs].bbox.y1 = points[n_points - 1].y;
n_segs++;
n_points = 1;
n_points_max = 4;
points = art_new (ArtPoint, n_points_max);
points[0].x = x;
points[0].y = y;
x_min = x;
x_max = x;
}
if (points != NULL)
{
if (n_points == n_points_max)
art_expand (points, ArtPoint, n_points_max);
points[n_points].x = x = vpath[i].x;
points[n_points].y = y = vpath[i].y;
if (x < x_min) x_min = x;
else if (x > x_max) x_max = x;
n_points++;
}
dir = new_dir;
}
i++;
}
if (points != NULL)
{
if (n_points >= 2)
{
if (n_segs == n_segs_max)
{
n_segs_max <<= 1;
svp = (ArtSVP *)art_realloc (svp, sizeof(ArtSVP) +
(n_segs_max - 1) *
sizeof(ArtSVPSeg));
}
svp->segs[n_segs].n_points = n_points;
svp->segs[n_segs].dir = (dir > 0);
if (dir < 0)
reverse_points (points, n_points);
svp->segs[n_segs].points = points;
svp->segs[n_segs].bbox.x0 = x_min;
svp->segs[n_segs].bbox.x1 = x_max;
svp->segs[n_segs].bbox.y0 = points[0].y;
svp->segs[n_segs].bbox.y1 = points[n_points - 1].y;
n_segs++;
}
else
art_free (points);
}
svp->n_segs = n_segs;
qsort (&svp->segs, n_segs, sizeof (ArtSVPSeg), art_svp_seg_compare);
return svp;
}
#define EPSILON 1e-6
/* It may be that this one should move out into art_svp or somesuch. */
static int
art_svp_seg_compare (const void *s1, const void *s2)
{
const ArtSVPSeg *seg1 = s1;
const ArtSVPSeg *seg2 = s2;
if (seg1->points[0].y - EPSILON > seg2->points[0].y) return 1;
else if (seg1->points[0].y + EPSILON < seg2->points[0].y) return -1;
else if (seg1->points[0].x - EPSILON > seg2->points[0].x) return 1;
else if (seg1->points[0].x + EPSILON < seg2->points[0].x) return -1;
else if ((seg1->points[1].x - seg1->points[0].x) *
(seg2->points[1].y - seg2->points[0].y) -
(seg1->points[1].y - seg1->points[0].y) *
(seg2->points[1].x - seg2->points[0].x) > 0) return 1;
else return -1;
}
/* Libart_LGPL - library of basic graphic primitives
* Copyright (C) 1998 Raph Levien
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#ifndef __ART_SVP_VPATH_H__
#define __ART_SVP_VPATH_H__
/* Sort vector paths into sorted vector paths. */
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
ArtSVP *
art_svp_from_vpath (ArtVpath *vpath);
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* __ART_SVP_VPATH_H__ */
......@@ -84,45 +84,56 @@ art_bezier_to_vec (double x0, double y0,
#define RENDER_LEVEL 4
#define RENDER_SIZE (1 << (RENDER_LEVEL))
/* Creates a new vector path, given a bezier path. Bezier subdivision
level is (currently) hardcoded. */
/* Creates a new vector path, given a bezier path. The flatness
argument is present in the api but is not used. A value of 0.5
should usually be appropriate for antialiased display (1 for "lego"
displays) - at least if the resulting vpath is not going to be
scaled. */
/* We could scan first and allocate to fit, but we don't. */
vec_path_el *
art_bez_path_to_vec (bez_path_el *bez)
ArtVpath *
art_bez_path_to_vec (ArtBpath *bez, double flatness)
{
vec_path_el *vec;
ArtVpath *vec;
int vec_n, vec_n_max;
int bez_index;
int space_needed;
double x, y;
point seg[RENDER_SIZE];
ArtPoint seg[RENDER_SIZE];
int i;
vec_n = 0;
vec_n_max = RENDER_SIZE;
vec = z_new (vec_path_el, vec_n_max);
vec = art_new (ArtVpath, vec_n_max);
/* Initialization is unnecessary because of the precondition that the
bezier path does not begin with LINETO or CURVETO, but is here
to make the code warning-free. */
x = 0;
y = 0;
bez_index = 0;
do
{
#ifdef VERBOSE
printf ("%s %g %g\n",
bez[bez_index].code == CURVETO ? "curveto" :
bez[bez_index].code == LINETO ? "lineto" :
bez[bez_index].code == MOVETO ? "moveto" :
bez[bez_index].code == ART_CURVETO ? "curveto" :
bez[bez_index].code == ART_LINETO ? "lineto" :
bez[bez_index].code == ART_MOVETO ? "moveto" :
bez[bez_index].code == ART_MOVETO_OPEN ? "moveto-open" :
"end", bez[bez_index].x3, bez[bez_index].y3);
#endif
if (bez[bez_index].code == CURVETO)
if (bez[bez_index].code == ART_CURVETO)
space_needed = RENDER_SIZE;
else
space_needed = 1;
if (vec_n + space_needed > vec_n_max)
/* We know a simple doubling will do it because space_needed
is always <= vec_n_max */
z_double (vec, vec_path_el, vec_n_max);
art_expand (vec, ArtVpath, vec_n_max);
switch (bez[bez_index].code)
{
case ART_MOVETO_OPEN:
case ART_MOVETO:
case ART_LINETO:
x = bez[bez_index].x3;
......
......@@ -31,7 +31,7 @@ ArtPoint *art_bezier_to_vec (double x0, double y0,
ArtPoint *p,
int level);
ArtVpath *art_bez_path_to_vec (ArtBpath *bez);
ArtVpath *art_bez_path_to_vec (ArtBpath *bez, double flatness);
};
......
......@@ -17,10 +17,150 @@
* Boston, MA 02111-1307, USA.
*/
#include <stdio.h>
#include <string.h>
#include <math.h>
#include "art_misc.h"
#include "art_vpath.h"
#include "art_svp.h"
#include "art_svp_vpath.h"
#include "art_svp_render_aa.h"
/* This function needs to go into libart proper. */
typedef struct _byte_data byte_data;
struct _byte_data {