/* * 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 * 2011-04-01 Modified for use in Minecraft-Overviewer * * Copyright (c) 1996-2006 by Fredrik Lundh * Copyright (c) 1997-2006 by Secret Labs AB. * * This file is part of the Python Imaging Library * * By obtaining, using, and/or copying this software and/or its associated * documentation, you agree that you have read, understood, and will comply * with the following terms and conditions: * * Permission to use, copy, modify, and distribute this software and its * associated documentation for any purpose and without fee is hereby granted, * provided that the above copyright notice appears in all copies, and that * both that copyright notice and this permission notice appear in supporting * documentation, and that the name of Secret Labs AB or the author not be used * in advertising or publicity pertaining to distribution of the software * without specific, written prior permission. * * SECRET LABS AB AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. * IN NO EVENT SHALL SECRET LABS AB OR THE AUTHOR BE LIABLE FOR ANY SPECIAL, * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. * */ /* 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; 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 polygon8(Imaging im, int n, Edge *e, int ink, int eofill) { 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) hline8(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]) hline8(im, CEIL(xx[0]-0.5), ymin, FLOOR(xx[1]+0.5), ink); else hline8(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) hline8(im, CEIL(xx[i]-0.5), ymin, FLOOR(xx[i+1]+0.5), ink); } } free(xx); return 0; } static inline int polygon32(Imaging im, int n, Edge *e, int ink, int eofill) { 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) hline32(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]) hline32(im, CEIL(xx[0]-0.5), ymin, FLOOR(xx[1]+0.5), ink); else hline32(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) hline32(im, CEIL(xx[i]-0.5), ymin, FLOOR(xx[i+1]+0.5), ink); } } free(xx); return 0; } static inline int polygon32rgba(Imaging im, int n, Edge *e, int ink, int eofill) { 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) hline32rgba(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]) hline32rgba(im, CEIL(xx[0]-0.5), ymin, FLOOR(xx[1]+0.5), ink); else hline32rgba(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) hline32rgba(im, CEIL(xx[i]-0.5), ymin, FLOOR(xx[i+1]+0.5), ink); } } free(xx); return 0; } 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; } /* -------------------------------------------------------------------- */ /* standard shapes */ #define ARC 0 #define CHORD 1 #define PIESLICE 2 /* -------------------------------------------------------------------- */ /* 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; }; 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 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