/* * 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) #define INK32(ink) (*(INT32*)ink) /* like (a * b + 127) / 255), but much faster on most platforms */ #define MULDIV255(a, b, tmp)\ (tmp = (a) * (b) + 128, ((((tmp) >> 8) + (tmp)) >> 8)) #define BLEND(mask, in1, in2, tmp1, tmp2)\ (MULDIV255(in1, 255 - mask, tmp1) + MULDIV255(in2, mask, tmp2)) /* -------------------------------------------------------------------- */ /* 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) 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, tmp2; 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, tmp2); out[1] = BLEND(in[3], out[1], in[1], tmp1, tmp2); out[2] = BLEND(in[3], out[2], in[2], tmp1, tmp2); } } static inline void hline8(Imaging im, int x0, int y0, int x1, int ink) { int tmp; 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) memset(im->image8[y0] + x0, (UINT8) ink, x1 - x0 + 1); } } 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, tmp2; 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, tmp2); out[1] = BLEND(in[3], out[1], in[1], tmp1, tmp2); out[2] = BLEND(in[3], out[2], in[2], tmp1, tmp2); 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 inline int polygon_generic(Imaging im, int n, Edge *e, int ink, int eofill, hline_handler handler) { int i, j; float *xx; int ymin, ymax; float y; if (n <= 0) return 0; /* Find upper and lower polygon boundary (within image) */ ymin = e[0].ymin; ymax = e[0].ymax; for (i = 1; i < n; i++) { if (e[i].ymin < ymin) ymin = e[i].ymin; if (e[i].ymax > ymax) ymax = e[i].ymax; } if (ymin < 0) ymin = 0; if (ymax >= im->ysize) ymax = im->ysize-1; /* Process polygon edges */ xx = malloc(n * sizeof(float)); if (!xx) return -1; for (;ymin <= ymax; ymin++) { y = ymin+0.5F; for (i = j = 0; i < n; i++) { if (y >= e[i].ymin && y <= e[i].ymax) { if (e[i].d == 0) (*handler)(im, e[i].xmin, ymin, e[i].xmax, ink); else xx[j++] = (y-e[i].y0) * e[i].dx + e[i].x0; } } if (j == 2) { if (xx[0] < xx[1]) (*handler)(im, CEIL(xx[0]-0.5), ymin, FLOOR(xx[1]+0.5), ink); else (*handler)(im, CEIL(xx[1]-0.5), ymin, FLOOR(xx[0]+0.5), ink); } else { qsort(xx, j, sizeof(float), x_cmp); for (i = 0; i < j-1 ; i += 2) (*handler)(im, CEIL(xx[i]-0.5), ymin, FLOOR(xx[i+1]+0.5), ink); } } free(xx); 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; \ ink = INK32(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; Edge e[4]; int dx, dy; double d; DRAWINIT(); if (width <= 1) { draw->line(im, x0, y0, x1, y1, ink); return 0; } dx = x1-x0; dy = y1-y0; if (dx == 0 && dy == 0) { draw->point(im, x0, y0, ink); return 0; } d = width / sqrt((float) (dx*dx + dy*dy)) / 2.0; dx = (int) floor(d * (y1-y0) + 0.5); dy = (int) floor(d * (x1-x0) + 0.5); add_edge(e+0, x0 - dx, y0 + dy, x1 - dx, y1 + dy); add_edge(e+1, x1 - dx, y1 + dy, x1 + dx, y1 - dy); add_edge(e+2, x1 + dx, y1 - dy, x0 + dx, y0 - dy); add_edge(e+3, x0 + dx, y0 - dy, x0 - dx, y0 + dy); 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 op) { 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 */ draw->line(im, x0, y0, x1, y0, ink); draw->line(im, x1, y0, x1, y1, ink); draw->line(im, x1, y1, x0, y1, ink); draw->line(im, x0, y1, x0, 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 */ Edge* e = malloc(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 int ellipse(Imaging im, int x0, int y0, int x1, int y1, int start, int end, const void* ink_, int fill, int mode, int op) { int i, n; int cx, cy; int w, h; int x = 0, y = 0; int lx = 0, ly = 0; int sx = 0, sy = 0; DRAW* draw; INT32 ink; w = x1 - x0; h = y1 - y0; if (w < 0 || h < 0) return 0; DRAWINIT(); cx = (x0 + x1) / 2; cy = (y0 + y1) / 2; while (end < start) end += 360; if (mode != ARC && fill) { /* Build edge list */ Edge* e = malloc((end - start + 3) * sizeof(Edge)); if (!e) { ImagingError_MemoryError(); return -1; } n = 0; for (i = start; i <= end; i++) { x = FLOOR((cos(i*M_PI/180) * w/2) + cx + 0.5); y = FLOOR((sin(i*M_PI/180) * h/2) + cy + 0.5); if (i != start) add_edge(&e[n++], lx, ly, x, y); else sx = x, sy = y; lx = x, ly = y; } if (n > 0) { /* close and draw polygon */ if (mode == PIESLICE) { if (x != cx || y != cy) { add_edge(&e[n++], x, y, cx, cy); add_edge(&e[n++], cx, cy, sx, sy); } } else { if (x != sx || y != sy) add_edge(&e[n++], x, y, sx, sy); } draw->polygon(im, n, e, ink, 0); } free(e); } else { for (i = start; i <= end; i++) { x = FLOOR((cos(i*M_PI/180) * w/2) + cx + 0.5); y = FLOOR((sin(i*M_PI/180) * h/2) + cy + 0.5); 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); } } } return 0; } int ImagingDrawArc(Imaging im, int x0, int y0, int x1, int y1, int start, int end, const void* ink, int op) { return ellipse(im, x0, y0, x1, y1, start, end, ink, 0, ARC, op); } int ImagingDrawChord(Imaging im, int x0, int y0, int x1, int y1, int start, int end, const void* ink, int fill, int op) { return ellipse(im, x0, y0, x1, y1, start, end, ink, fill, CHORD, op); } int ImagingDrawEllipse(Imaging im, int x0, int y0, int x1, int y1, const void* ink, int fill, int op) { return ellipse(im, x0, y0, x1, y1, 0, 360, ink, fill, CHORD, op); } int ImagingDrawPieslice(Imaging im, int x0, int y0, int x1, int y1, int start, int end, const void* ink, int fill, int op) { return ellipse(im, x0, y0, x1, y1, start, end, ink, fill, 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 */ #if 1 /* ARROW_GRAPHICS */ 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) e = malloc(outline->size * sizeof(Edge)); else 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 ImagingOutlineCurve2(ImagingOutline outline, float cx, float cy, float x3, float y3) { /* add bezier curve based on three control points (as in the Flash file format) */ return ImagingOutlineCurve( outline, (outline->x + cx + cx)/3, (outline->y + cy + cy)/3, (cx + cx + x3)/3, (cy + cy + y3)/3, x3, y3); } 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; } #endif