/* * The Python Imaging Library. * $Id$ * * a simple drawing package for the Imaging library * * history: * 1996-04-13 fl Created. * 1996-04-30 fl Added transforms and polygon support. * 1996-08-12 fl Added filled polygons. * 1996-11-05 fl Fixed float/int confusion in polygon filler * 1997-07-04 fl Support 32-bit images (C++ would have been nice) * 1998-09-09 fl Eliminated qsort casts; improved rectangle clipping * 1998-09-10 fl Fixed fill rectangle to include lower edge (!) * 1998-12-29 fl Added arc, chord, and pieslice primitives * 1999-01-10 fl Added some level 2 ("arrow") stuff (experimental) * 1999-02-06 fl Added bitmap primitive * 1999-07-26 fl Eliminated a compiler warning * 1999-07-31 fl Pass ink as void* instead of int * 2002-12-10 fl Added experimental RGBA-on-RGB drawing * 2004-09-04 fl Support simple wide lines (no joins) * 2005-05-25 fl Fixed line width calculation * * Copyright (c) 1996-2006 by Fredrik Lundh * Copyright (c) 1997-2006 by Secret Labs AB. * * See the README file for information on usage and redistribution. */ /* FIXME: support fill/outline attribute for all filled shapes */ /* FIXME: support zero-winding fill */ /* FIXME: add drawing context, support affine transforms */ /* FIXME: support clip window (and mask?) */ #include "Imaging.h" #include #define CEIL(v) (int) ceil(v) #define FLOOR(v) ((v) >= 0.0 ? (int) (v) : (int) floor(v)) #define INK8(ink) (*(UINT8*)ink) /* * Rounds around zero (up=away from zero, down=towards zero) * This guarantees that ROUND_UP|DOWN(f) == -ROUND_UP|DOWN(-f) */ #define ROUND_UP(f) ((int) ((f) >= 0.0 ? floor((f) + 0.5F) : -floor(fabs(f) + 0.5F))) #define ROUND_DOWN(f) ((int) ((f) >= 0.0 ? ceil((f) - 0.5F) : -ceil(fabs(f) - 0.5F))) /* -------------------------------------------------------------------- */ /* Primitives */ /* -------------------------------------------------------------------- */ typedef struct { /* edge descriptor for polygon engine */ int d; int x0, y0; int xmin, ymin, xmax, ymax; float dx; } Edge; /* Type used in "polygon*" functions */ typedef void (*hline_handler)(Imaging, int, int, int, int); static inline void point8(Imaging im, int x, int y, int ink) { if (x >= 0 && x < im->xsize && y >= 0 && y < im->ysize) { if (strncmp(im->mode, "I;16", 4) == 0) { im->image8[y][x*2] = (UINT8) ink; im->image8[y][x*2+1] = (UINT8) ink; } else { im->image8[y][x] = (UINT8) ink; } } } static inline void point32(Imaging im, int x, int y, int ink) { if (x >= 0 && x < im->xsize && y >= 0 && y < im->ysize) { im->image32[y][x] = ink; } } static inline void point32rgba(Imaging im, int x, int y, int ink) { unsigned int tmp1; if (x >= 0 && x < im->xsize && y >= 0 && y < im->ysize) { UINT8* out = (UINT8*) im->image[y]+x*4; UINT8* in = (UINT8*) &ink; out[0] = BLEND(in[3], out[0], in[0], tmp1); out[1] = BLEND(in[3], out[1], in[1], tmp1); out[2] = BLEND(in[3], out[2], in[2], tmp1); } } static inline void hline8(Imaging im, int x0, int y0, int x1, int ink) { int tmp, pixelwidth; if (y0 >= 0 && y0 < im->ysize) { if (x0 > x1) { tmp = x0, x0 = x1, x1 = tmp; } if (x0 < 0) { x0 = 0; } else if (x0 >= im->xsize) { return; } if (x1 < 0) { return; } else if (x1 >= im->xsize) { x1 = im->xsize-1; } if (x0 <= x1) { pixelwidth = strncmp(im->mode, "I;16", 4) == 0 ? 2 : 1; memset(im->image8[y0] + x0 * pixelwidth, (UINT8) ink, (x1 - x0 + 1) * pixelwidth); } } } static inline void hline32(Imaging im, int x0, int y0, int x1, int ink) { int tmp; INT32* p; if (y0 >= 0 && y0 < im->ysize) { if (x0 > x1) { tmp = x0, x0 = x1, x1 = tmp; } if (x0 < 0) { x0 = 0; } else if (x0 >= im->xsize) { return; } if (x1 < 0) { return; } else if (x1 >= im->xsize) { x1 = im->xsize-1; } p = im->image32[y0]; while (x0 <= x1) { p[x0++] = ink; } } } static inline void hline32rgba(Imaging im, int x0, int y0, int x1, int ink) { int tmp; unsigned int tmp1; if (y0 >= 0 && y0 < im->ysize) { if (x0 > x1) { tmp = x0, x0 = x1, x1 = tmp; } if (x0 < 0) { x0 = 0; } else if (x0 >= im->xsize) { return; } if (x1 < 0) { return; } else if (x1 >= im->xsize) { x1 = im->xsize-1; } if (x0 <= x1) { UINT8* out = (UINT8*) im->image[y0]+x0*4; UINT8* in = (UINT8*) &ink; while (x0 <= x1) { out[0] = BLEND(in[3], out[0], in[0], tmp1); out[1] = BLEND(in[3], out[1], in[1], tmp1); out[2] = BLEND(in[3], out[2], in[2], tmp1); x0++; out += 4; } } } } static inline void line8(Imaging im, int x0, int y0, int x1, int y1, int ink) { int i, n, e; int dx, dy; int xs, ys; /* normalize coordinates */ dx = x1-x0; if (dx < 0) { dx = -dx, xs = -1; } else { xs = 1; } dy = y1-y0; if (dy < 0) { dy = -dy, ys = -1; } else { ys = 1; } n = (dx > dy) ? dx : dy; if (dx == 0) { /* vertical */ for (i = 0; i < dy; i++) { point8(im, x0, y0, ink); y0 += ys; } } else if (dy == 0) { /* horizontal */ for (i = 0; i < dx; i++) { point8(im, x0, y0, ink); x0 += xs; } } else if (dx > dy) { /* bresenham, horizontal slope */ n = dx; dy += dy; e = dy - dx; dx += dx; for (i = 0; i < n; i++) { point8(im, x0, y0, ink); if (e >= 0) { y0 += ys; e -= dx; } e += dy; x0 += xs; } } else { /* bresenham, vertical slope */ n = dy; dx += dx; e = dx - dy; dy += dy; for (i = 0; i < n; i++) { point8(im, x0, y0, ink); if (e >= 0) { x0 += xs; e -= dy; } e += dx; y0 += ys; } } } static inline void line32(Imaging im, int x0, int y0, int x1, int y1, int ink) { int i, n, e; int dx, dy; int xs, ys; /* normalize coordinates */ dx = x1-x0; if (dx < 0) { dx = -dx, xs = -1; } else { xs = 1; } dy = y1-y0; if (dy < 0) { dy = -dy, ys = -1; } else { ys = 1; } n = (dx > dy) ? dx : dy; if (dx == 0) { /* vertical */ for (i = 0; i < dy; i++) { point32(im, x0, y0, ink); y0 += ys; } } else if (dy == 0) { /* horizontal */ for (i = 0; i < dx; i++) { point32(im, x0, y0, ink); x0 += xs; } } else if (dx > dy) { /* bresenham, horizontal slope */ n = dx; dy += dy; e = dy - dx; dx += dx; for (i = 0; i < n; i++) { point32(im, x0, y0, ink); if (e >= 0) { y0 += ys; e -= dx; } e += dy; x0 += xs; } } else { /* bresenham, vertical slope */ n = dy; dx += dx; e = dx - dy; dy += dy; for (i = 0; i < n; i++) { point32(im, x0, y0, ink); if (e >= 0) { x0 += xs; e -= dy; } e += dx; y0 += ys; } } } static inline void line32rgba(Imaging im, int x0, int y0, int x1, int y1, int ink) { int i, n, e; int dx, dy; int xs, ys; /* normalize coordinates */ dx = x1-x0; if (dx < 0) { dx = -dx, xs = -1; } else { xs = 1; } dy = y1-y0; if (dy < 0) { dy = -dy, ys = -1; } else { ys = 1; } n = (dx > dy) ? dx : dy; if (dx == 0) { /* vertical */ for (i = 0; i < dy; i++) { point32rgba(im, x0, y0, ink); y0 += ys; } } else if (dy == 0) { /* horizontal */ for (i = 0; i < dx; i++) { point32rgba(im, x0, y0, ink); x0 += xs; } } else if (dx > dy) { /* bresenham, horizontal slope */ n = dx; dy += dy; e = dy - dx; dx += dx; for (i = 0; i < n; i++) { point32rgba(im, x0, y0, ink); if (e >= 0) { y0 += ys; e -= dx; } e += dy; x0 += xs; } } else { /* bresenham, vertical slope */ n = dy; dx += dx; e = dx - dy; dy += dy; for (i = 0; i < n; i++) { point32rgba(im, x0, y0, ink); if (e >= 0) { x0 += xs; e -= dy; } e += dx; y0 += ys; } } } static int x_cmp(const void *x0, const void *x1) { float diff = *((float*)x0) - *((float*)x1); if (diff < 0) { return -1; } else if (diff > 0) { return 1; } else { return 0; } } static void draw_horizontal_lines(Imaging im, int n, Edge *e, int ink, int *x_pos, int y, hline_handler hline) { int i; for (i = 0; i < n; i++) { if (e[i].ymin == y && e[i].ymin == e[i].ymax) { int xmax; int xmin = e[i].xmin; if (*x_pos < xmin) { // Line would be after the current position continue; } xmax = e[i].xmax; if (*x_pos > xmin) { // Line would be partway through x_pos, so increase the starting point xmin = *x_pos; if (xmax < xmin) { // Line would now end before it started continue; } } (*hline)(im, xmin, e[i].ymin, xmax, ink); *x_pos = xmax+1; } } } /* * Filled polygon draw function using scan line algorithm. */ static inline int polygon_generic(Imaging im, int n, Edge *e, int ink, int eofill, hline_handler hline) { Edge** edge_table; float* xx; int edge_count = 0; int ymin = im->ysize - 1; int ymax = 0; int i; if (n <= 0) { return 0; } /* Initialize the edge table and find polygon boundaries */ /* malloc check ok, using calloc */ edge_table = calloc(n, sizeof(Edge*)); if (!edge_table) { return -1; } for (i = 0; i < n; i++) { if (e[i].ymin == e[i].ymax) { continue; } if (ymin > e[i].ymin) { ymin = e[i].ymin; } if (ymax < e[i].ymax) { ymax = e[i].ymax; } edge_table[edge_count++] = (e + i); } if (ymin < 0) { ymin = 0; } if (ymax > im->ysize) { ymax = im->ysize; } /* Process the edge table with a scan line searching for intersections */ /* malloc check ok, using calloc */ xx = calloc(edge_count * 2, sizeof(float)); if (!xx) { free(edge_table); return -1; } for (; ymin <= ymax; ymin++) { int j = 0; int x_pos = 0; for (i = 0; i < edge_count; i++) { Edge* current = edge_table[i]; if (ymin >= current->ymin && ymin <= current->ymax) { xx[j++] = (ymin - current->y0) * current->dx + current->x0; } /* Needed to draw consistent polygons */ if (ymin == current->ymax && ymin < ymax) { xx[j] = xx[j - 1]; j++; } } qsort(xx, j, sizeof(float), x_cmp); for (i = 1; i < j; i += 2) { int x_end = ROUND_DOWN(xx[i]); if (x_end < x_pos) { // Line would be before the current position continue; } draw_horizontal_lines(im, n, e, ink, &x_pos, ymin, hline); if (x_end < x_pos) { // Line would be before the current position continue; } int x_start = ROUND_UP(xx[i-1]); if (x_pos > x_start) { // Line would be partway through x_pos, so increase the starting point x_start = x_pos; if (x_end < x_start) { // Line would now end before it started continue; } } (*hline)(im, x_start, ymin, x_end, ink); x_pos = x_end+1; } draw_horizontal_lines(im, n, e, ink, &x_pos, ymin, hline); } free(xx); free(edge_table); return 0; } static inline int polygon8(Imaging im, int n, Edge *e, int ink, int eofill) { return polygon_generic(im, n, e, ink, eofill, hline8); } static inline int polygon32(Imaging im, int n, Edge *e, int ink, int eofill) { return polygon_generic(im, n, e, ink, eofill, hline32); } static inline int polygon32rgba(Imaging im, int n, Edge *e, int ink, int eofill) { return polygon_generic(im, n, e, ink, eofill, hline32rgba); } static inline void add_edge(Edge *e, int x0, int y0, int x1, int y1) { /* printf("edge %d %d %d %d\n", x0, y0, x1, y1); */ if (x0 <= x1) { e->xmin = x0, e->xmax = x1; } else { e->xmin = x1, e->xmax = x0; } if (y0 <= y1) { e->ymin = y0, e->ymax = y1; } else { e->ymin = y1, e->ymax = y0; } if (y0 == y1) { e->d = 0; e->dx = 0.0; } else { e->dx = ((float)(x1-x0)) / (y1-y0); if (y0 == e->ymin) { e->d = 1; } else { e->d = -1; } } e->x0 = x0; e->y0 = y0; } typedef struct { void (*point)(Imaging im, int x, int y, int ink); void (*hline)(Imaging im, int x0, int y0, int x1, int ink); void (*line)(Imaging im, int x0, int y0, int x1, int y1, int ink); int (*polygon)(Imaging im, int n, Edge *e, int ink, int eofill); } DRAW; DRAW draw8 = { point8, hline8, line8, polygon8 }; DRAW draw32 = { point32, hline32, line32, polygon32 }; DRAW draw32rgba = { point32rgba, hline32rgba, line32rgba, polygon32rgba }; /* -------------------------------------------------------------------- */ /* Interface */ /* -------------------------------------------------------------------- */ #define DRAWINIT()\ if (im->image8) {\ draw = &draw8;\ ink = INK8(ink_);\ } else {\ draw = (op) ? &draw32rgba : &draw32; \ memcpy(&ink, ink_, sizeof(ink)); \ } int ImagingDrawPoint(Imaging im, int x0, int y0, const void* ink_, int op) { DRAW* draw; INT32 ink; DRAWINIT(); draw->point(im, x0, y0, ink); return 0; } int ImagingDrawLine(Imaging im, int x0, int y0, int x1, int y1, const void* ink_, int op) { DRAW* draw; INT32 ink; DRAWINIT(); draw->line(im, x0, y0, x1, y1, ink); return 0; } int ImagingDrawWideLine(Imaging im, int x0, int y0, int x1, int y1, const void* ink_, int width, int op) { DRAW* draw; INT32 ink; int dx, dy; double big_hypotenuse, small_hypotenuse, ratio_max, ratio_min; int dxmin, dxmax, dymin, dymax; Edge e[4]; DRAWINIT(); dx = x1-x0; dy = y1-y0; if (dx == 0 && dy == 0) { draw->point(im, x0, y0, ink); return 0; } big_hypotenuse = sqrt((double) (dx*dx + dy*dy)); small_hypotenuse = (width - 1) / 2.0; ratio_max = ROUND_UP(small_hypotenuse) / big_hypotenuse; ratio_min = ROUND_DOWN(small_hypotenuse) / big_hypotenuse; dxmin = ROUND_DOWN(ratio_min * dy); dxmax = ROUND_DOWN(ratio_max * dy); dymin = ROUND_DOWN(ratio_min * dx); dymax = ROUND_DOWN(ratio_max * dx); { int vertices[4][2] = { {x0 - dxmin, y0 + dymax}, {x1 - dxmin, y1 + dymax}, {x1 + dxmax, y1 - dymin}, {x0 + dxmax, y0 - dymin} }; add_edge(e+0, vertices[0][0], vertices[0][1], vertices[1][0], vertices[1][1]); add_edge(e+1, vertices[1][0], vertices[1][1], vertices[2][0], vertices[2][1]); add_edge(e+2, vertices[2][0], vertices[2][1], vertices[3][0], vertices[3][1]); add_edge(e+3, vertices[3][0], vertices[3][1], vertices[0][0], vertices[0][1]); draw->polygon(im, 4, e, ink, 0); } return 0; } int ImagingDrawRectangle(Imaging im, int x0, int y0, int x1, int y1, const void* ink_, int fill, int width, int op) { int i; int y; int tmp; DRAW* draw; INT32 ink; DRAWINIT(); if (y0 > y1) { tmp = y0, y0 = y1, y1 = tmp; } if (fill) { if (y0 < 0) { y0 = 0; } else if (y0 >= im->ysize) { return 0; } if (y1 < 0) { return 0; } else if (y1 > im->ysize) { y1 = im->ysize; } for (y = y0; y <= y1; y++) { draw->hline(im, x0, y, x1, ink); } } else { /* outline */ if (width == 0) { width = 1; } for (i = 0; i < width; i++) { draw->hline(im, x0, y0+i, x1, ink); draw->hline(im, x0, y1-i, x1, ink); draw->line(im, x1-i, y0, x1-i, y1, ink); draw->line(im, x0+i, y1, x0+i, y0, ink); } } return 0; } int ImagingDrawPolygon(Imaging im, int count, int* xy, const void* ink_, int fill, int op) { int i, n; DRAW* draw; INT32 ink; if (count <= 0) { return 0; } DRAWINIT(); if (fill) { /* Build edge list */ /* malloc check ok, using calloc */ Edge* e = calloc(count, sizeof(Edge)); if (!e) { (void) ImagingError_MemoryError(); return -1; } for (i = n = 0; i < count-1; i++) { add_edge(&e[n++], xy[i+i], xy[i+i+1], xy[i+i+2], xy[i+i+3]); } if (xy[i+i] != xy[0] || xy[i+i+1] != xy[1]) { add_edge(&e[n++], xy[i+i], xy[i+i+1], xy[0], xy[1]); } draw->polygon(im, n, e, ink, 0); free(e); } else { /* Outline */ for (i = 0; i < count-1; i++) { draw->line(im, xy[i+i], xy[i+i+1], xy[i+i+2], xy[i+i+3], ink); } draw->line(im, xy[i+i], xy[i+i+1], xy[0], xy[1], ink); } return 0; } int ImagingDrawBitmap(Imaging im, int x0, int y0, Imaging bitmap, const void* ink, int op) { return ImagingFill2( im, ink, bitmap, x0, y0, x0 + bitmap->xsize, y0 + bitmap->ysize ); } /* -------------------------------------------------------------------- */ /* standard shapes */ #define ARC 0 #define CHORD 1 #define PIESLICE 2 static void ellipsePoint(int cx, int cy, int w, int h, float i, int *x, int *y) { float i_cos, i_sin; float x_f, y_f; double modf_int; i_cos = cos(i*M_PI/180); i_sin = sin(i*M_PI/180); x_f = (i_cos * w/2) + cx; y_f = (i_sin * h/2) + cy; if (modf(x_f, &modf_int) == 0.5) { *x = i_cos > 0 ? FLOOR(x_f) : CEIL(x_f); } else { *x = FLOOR(x_f + 0.5); } if (modf(y_f, &modf_int) == 0.5) { *y = i_sin > 0 ? FLOOR(y_f) : CEIL(y_f); } else { *y = FLOOR(y_f + 0.5); } } static int ellipse(Imaging im, int x0, int y0, int x1, int y1, float start, float end, const void* ink_, int fill, int width, int mode, int op) { float i; int inner; int n; int maxEdgeCount; int w, h; int x, y; int cx, cy; int lx = 0, ly = 0; int sx = 0, sy = 0; int lx_inner = 0, ly_inner = 0; int sx_inner = 0, sy_inner = 0; DRAW* draw; INT32 ink; Edge* e; DRAWINIT(); while (end < start) { end += 360; } if (end - start > 360) { // no need to go in loops end = start + 361; } w = x1 - x0; h = y1 - y0; if (w <= 0 || h <= 0) { return 0; } cx = (x0 + x1) / 2; cy = (y0 + y1) / 2; if (!fill && width <= 1) { for (i = start; i < end+1; i++) { if (i > end) { i = end; } ellipsePoint(cx, cy, w, h, i, &x, &y); if (i != start) { draw->line(im, lx, ly, x, y, ink); } else { sx = x, sy = y; } lx = x, ly = y; } if (i != start) { if (mode == PIESLICE) { if (x != cx || y != cy) { draw->line(im, x, y, cx, cy, ink); draw->line(im, cx, cy, sx, sy, ink); } } else if (mode == CHORD) { if (x != sx || y != sy) { draw->line(im, x, y, sx, sy, ink); } } } } else { inner = (mode == ARC || !fill) ? 1 : 0; // Build edge list // malloc check UNDONE, FLOAT? maxEdgeCount = ceil(end - start); if (inner) { maxEdgeCount *= 2; } maxEdgeCount += 3; e = calloc(maxEdgeCount, sizeof(Edge)); if (!e) { ImagingError_MemoryError(); return -1; } // Outer circle n = 0; for (i = start; i < end+1; i++) { if (i > end) { i = end; } ellipsePoint(cx, cy, w, h, i, &x, &y); if (i == start) { sx = x, sy = y; } else { add_edge(&e[n++], lx, ly, x, y); } lx = x, ly = y; } if (n == 0) return 0; if (inner) { // Inner circle x0 += width - 1; y0 += width - 1; x1 -= width - 1; y1 -= width - 1; w = x1 - x0; h = y1 - y0; if (w <= 0 || h <= 0) { // ARC with no gap in the middle is a PIESLICE mode = PIESLICE; inner = 0; } else { for (i = start; i < end+1; i++) { if (i > end) { i = end; } ellipsePoint(cx, cy, w, h, i, &x, &y); if (i == start) { sx_inner = x, sy_inner = y; } else { add_edge(&e[n++], lx_inner, ly_inner, x, y); } lx_inner = x, ly_inner = y; } } } if (end - start < 360) { // Close polygon if (mode == PIESLICE) { if (x != cx || y != cy) { add_edge(&e[n++], sx, sy, cx, cy); add_edge(&e[n++], cx, cy, lx, ly); if (inner) { ImagingDrawWideLine(im, sx, sy, cx, cy, &ink, width, op); ImagingDrawWideLine(im, cx, cy, lx, ly, &ink, width, op); } } } else if (mode == CHORD) { add_edge(&e[n++], sx, sy, lx, ly); if (inner) { add_edge(&e[n++], sx_inner, sy_inner, lx_inner, ly_inner); } } else if (mode == ARC) { add_edge(&e[n++], sx, sy, sx_inner, sy_inner); add_edge(&e[n++], lx, ly, lx_inner, ly_inner); } } draw->polygon(im, n, e, ink, 0); free(e); } return 0; } int ImagingDrawArc(Imaging im, int x0, int y0, int x1, int y1, float start, float end, const void* ink, int width, int op) { return ellipse(im, x0, y0, x1, y1, start, end, ink, 0, width, ARC, op); } int ImagingDrawChord(Imaging im, int x0, int y0, int x1, int y1, float start, float end, const void* ink, int fill, int width, int op) { return ellipse(im, x0, y0, x1, y1, start, end, ink, fill, width, CHORD, op); } int ImagingDrawEllipse(Imaging im, int x0, int y0, int x1, int y1, const void* ink, int fill, int width, int op) { return ellipse(im, x0, y0, x1, y1, 0, 360, ink, fill, width, CHORD, op); } int ImagingDrawPieslice(Imaging im, int x0, int y0, int x1, int y1, float start, float end, const void* ink, int fill, int width, int op) { return ellipse(im, x0, y0, x1, y1, start, end, ink, fill, width, PIESLICE, op); } /* -------------------------------------------------------------------- */ /* experimental level 2 ("arrow") graphics stuff. this implements portions of the arrow api on top of the Edge structure. the semantics are ok, except that "curve" flattens the bezier curves by itself */ struct ImagingOutlineInstance { float x0, y0; float x, y; int count; Edge *edges; int size; }; ImagingOutline ImagingOutlineNew(void) { ImagingOutline outline; outline = calloc(1, sizeof(struct ImagingOutlineInstance)); if (!outline) { return (ImagingOutline) ImagingError_MemoryError(); } outline->edges = NULL; outline->count = outline->size = 0; ImagingOutlineMove(outline, 0, 0); return outline; } void ImagingOutlineDelete(ImagingOutline outline) { if (!outline) { return; } if (outline->edges) { free(outline->edges); } free(outline); } static Edge* allocate(ImagingOutline outline, int extra) { Edge* e; if (outline->count + extra > outline->size) { /* expand outline buffer */ outline->size += extra + 25; if (!outline->edges) { /* malloc check ok, uses calloc for overflow */ e = calloc(outline->size, sizeof(Edge)); } else { if (outline->size > INT_MAX / sizeof(Edge)) { return NULL; } /* malloc check ok, overflow checked above */ e = realloc(outline->edges, outline->size * sizeof(Edge)); } if (!e) { return NULL; } outline->edges = e; } e = outline->edges + outline->count; outline->count += extra; return e; } int ImagingOutlineMove(ImagingOutline outline, float x0, float y0) { outline->x = outline->x0 = x0; outline->y = outline->y0 = y0; return 0; } int ImagingOutlineLine(ImagingOutline outline, float x1, float y1) { Edge* e; e = allocate(outline, 1); if (!e) { return -1; /* out of memory */ } add_edge(e, (int) outline->x, (int) outline->y, (int) x1, (int) y1); outline->x = x1; outline->y = y1; return 0; } int ImagingOutlineCurve(ImagingOutline outline, float x1, float y1, float x2, float y2, float x3, float y3) { Edge* e; int i; float xo, yo; #define STEPS 32 e = allocate(outline, STEPS); if (!e) { return -1; /* out of memory */ } xo = outline->x; yo = outline->y; /* flatten the bezier segment */ for (i = 1; i <= STEPS; i++) { float t = ((float) i) / STEPS; float t2 = t*t; float t3 = t2*t; float u = 1.0F - t; float u2 = u*u; float u3 = u2*u; float x = outline->x*u3 + 3*(x1*t*u2 + x2*t2*u) + x3*t3 + 0.5; float y = outline->y*u3 + 3*(y1*t*u2 + y2*t2*u) + y3*t3 + 0.5; add_edge(e++, xo, yo, (int) x, (int) y); xo = x, yo = y; } outline->x = xo; outline->y = yo; return 0; } int ImagingOutlineClose(ImagingOutline outline) { if (outline->x == outline->x0 && outline->y == outline->y0) { return 0; } return ImagingOutlineLine(outline, outline->x0, outline->y0); } int ImagingOutlineTransform(ImagingOutline outline, double a[6]) { Edge *eIn; Edge *eOut; int i, n; int x0, y0, x1, y1; int X0, Y0, X1, Y1; double a0 = a[0]; double a1 = a[1]; double a2 = a[2]; double a3 = a[3]; double a4 = a[4]; double a5 = a[5]; eIn = outline->edges; n = outline->count; /* FIXME: ugly! */ outline->edges = NULL; outline->count = outline->size = 0; eOut = allocate(outline, n); if (!eOut) { outline->edges = eIn; outline->count = outline->size = n; ImagingError_MemoryError(); return -1; } for (i = 0; i < n; i++) { x0 = eIn->x0; y0 = eIn->y0; /* FIXME: ouch! */ if (eIn->x0 == eIn->xmin) { x1 = eIn->xmax; } else { x1 = eIn->xmin; } if (eIn->y0 == eIn->ymin) { y1 = eIn->ymax; } else { y1 = eIn->ymin; } /* full moon tonight! if this doesn't work, you may need to upgrade your compiler (make sure you have the right service pack) */ X0 = (int) (a0*x0 + a1*y0 + a2); Y0 = (int) (a3*x0 + a4*y0 + a5); X1 = (int) (a0*x1 + a1*y1 + a2); Y1 = (int) (a3*x1 + a4*y1 + a5); add_edge(eOut, X0, Y0, X1, Y1); eIn++; eOut++; } free(eIn); return 0; } int ImagingDrawOutline(Imaging im, ImagingOutline outline, const void* ink_, int fill, int op) { DRAW* draw; INT32 ink; DRAWINIT(); draw->polygon(im, outline->count, outline->edges, ink, 0); return 0; }