/* * 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 #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; } // Imagine 2D plane and ellipse with center in (0, 0) and semi-major axes a and b. // Then quarter_* stuff approximates its top right quarter (x, y >= 0) with integer // points from set {(2x+x0, 2y+y0) | x,y in Z} where x0, y0 are from {0, 1} and // are such that point (a, b) is in the set. typedef struct { int32_t a, b, cx, cy, ex, ey; int64_t a2, b2, a2b2; int8_t finished; } quarter_state; void quarter_init(quarter_state* s, int32_t a, int32_t b) { if (a < 0 || b < 0) { s->finished = 1; } else { s->a = a; s->b = b; s->cx = a; s->cy = b % 2; s->ex = a % 2; s->ey = b; s->a2 = a * a; s->b2 = b * b; s->a2b2 = s->a2 * s->b2; s->finished = 0; } } // deviation of the point from ellipse curve, basically a substitution // of the point into the ellipse equation int64_t quarter_delta(quarter_state* s, int64_t x, int64_t y) { return llabs(s->a2 * y * y + s->b2 * x * x - s->a2b2); } int8_t quarter_next(quarter_state* s, int32_t* ret_x, int32_t* ret_y) { if (s->finished) { return -1; } *ret_x = s->cx; *ret_y = s->cy; if (s->cx == s->ex && s->cy == s->ey) { s->finished = 1; } else { // bresenham's algorithm, possible optimization: only consider 2 of 3 // next points depending on current slope int32_t nx = s->cx; int32_t ny = s->cy + 2; int64_t ndelta = quarter_delta(s, nx, ny); if (nx > 1) { int64_t newdelta = quarter_delta(s, s->cx - 2, s->cy + 2); if (ndelta > newdelta) { nx = s->cx - 2; ny = s->cy + 2; ndelta = newdelta; } newdelta = quarter_delta(s, s->cx - 2, s->cy); if (ndelta > newdelta) { nx = s->cx - 2; ny = s->cy; } } s->cx = nx; s->cy = ny; } return 0; } // quarter_* stuff can "draw" a quarter of an ellipse with thickness 1, great. // Now we use ellipse_* stuff to join all four quarters of two different sized // ellipses and recieve horizontal segments of a complete ellipse with // specified thickness. // // Still using integer grid with step 2 at this point (like in quarter_*) // to ease angle clipping in future. typedef struct { quarter_state st_o, st_i; int32_t py, pl, pr; int32_t cy[4], cl[4], cr[4]; int8_t bufcnt; int8_t finished; int8_t leftmost; } ellipse_state; void ellipse_init(ellipse_state* s, int32_t a, int32_t b, int32_t w) { s->bufcnt = 0; s->leftmost = a % 2; quarter_init(&s->st_o, a, b); if (w < 1 || quarter_next(&s->st_o, &s->pr, &s->py) == -1) { s->finished = 1; } else { s->finished = 0; quarter_init(&s->st_i, a - 2 * (w - 1), b - 2 * (w - 1)); s->pl = s->leftmost; } } int8_t ellipse_next(ellipse_state* s, int32_t* ret_x0, int32_t* ret_y, int32_t* ret_x1) { if (s->bufcnt == 0) { if (s->finished) { return -1; } int32_t y = s->py; int32_t l = s->pl; int32_t r = s->pr; int32_t cx = 0, cy = 0; int8_t next_ret; while ((next_ret = quarter_next(&s->st_o, &cx, &cy)) != -1 && cy <= y) { } if (next_ret == -1) { s->finished = 1; } else { s->pr = cx; s->py = cy; } while ((next_ret = quarter_next(&s->st_i, &cx, &cy)) != -1 && cy <= y) { l = cx; } s->pl = next_ret == -1 ? s->leftmost : cx; if ((l > 0 || l < r) && y > 0) { s->cl[s->bufcnt] = l == 0 ? 2 : l; s->cy[s->bufcnt] = y; s->cr[s->bufcnt] = r; ++s->bufcnt; } if (y > 0) { s->cl[s->bufcnt] = -r; s->cy[s->bufcnt] = y; s->cr[s->bufcnt] = -l; ++s->bufcnt; } if (l > 0 || l < r) { s->cl[s->bufcnt] = l == 0 ? 2 : l; s->cy[s->bufcnt] = -y; s->cr[s->bufcnt] = r; ++s->bufcnt; } s->cl[s->bufcnt] = -r; s->cy[s->bufcnt] = -y; s->cr[s->bufcnt] = -l; ++s->bufcnt; } --s->bufcnt; *ret_x0 = s->cl[s->bufcnt]; *ret_y = s->cy[s->bufcnt]; *ret_x1 = s->cr[s->bufcnt]; return 0; } static int ellipseNew(Imaging im, int x0, int y0, int x1, int y1, const void* ink_, int fill, int width, int op) { DRAW* draw; INT32 ink; DRAWINIT(); int a = x1 - x0; int b = y1 - y0; if (a < 0 || b < 0) return 0; if (fill) { width = a + b; } ellipse_state st; ellipse_init(&st, a, b, width); int32_t X0, Y, X1; while (ellipse_next(&st, &X0, &Y, &X1) != -1) { draw->hline(im, x0 + (X0 + a) / 2, y0 + (Y + b) / 2, x0 + (X1 + a) / 2, ink); } 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 ellipseNew(im, x0, y0, x1, y1, ink, fill, width, 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; }