From a30e4155978e719fede8960f2d49cfdaa1127de8 Mon Sep 17 00:00:00 2001 From: Andrew Chin Date: Thu, 31 Mar 2011 22:37:18 -0400 Subject: [PATCH 1/7] Render edge lines --- setup.py | 1 + src/Draw.c | 902 ++++++++++++++++++++++++++++++++++++++++ src/rendermode-normal.c | 32 ++ 3 files changed, 935 insertions(+) create mode 100644 src/Draw.c diff --git a/setup.py b/setup.py index 62cc86e..fba44d4 100644 --- a/setup.py +++ b/setup.py @@ -52,6 +52,7 @@ except AttributeError: c_overviewer_files = ['src/main.c', 'src/composite.c', 'src/iterate.c', 'src/endian.c'] c_overviewer_files += ['src/rendermodes.c', 'src/rendermode-normal.c', 'src/rendermode-lighting.c', 'src/rendermode-night.c', 'src/rendermode-spawn.c'] +c_overviewer_files += ['src/Draw.c'] c_overviewer_includes = ['src/overviewer.h', 'src/rendermodes.h'] setup_kwargs['ext_modules'].append(Extension('c_overviewer', c_overviewer_files, include_dirs=['.', numpy_include], depends=c_overviewer_includes, extra_link_args=[])) # tell build_ext to build the extension in-place diff --git a/src/Draw.c b/src/Draw.c new file mode 100644 index 0000000..fca0518 --- /dev/null +++ b/src/Draw.c @@ -0,0 +1,902 @@ +/* + * 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 diff --git a/src/rendermode-normal.c b/src/rendermode-normal.c index e38118d..a5e9996 100644 --- a/src/rendermode-normal.c +++ b/src/rendermode-normal.c @@ -161,6 +161,38 @@ rendermode_normal_draw(void *data, RenderState *state, PyObject *src, PyObject * tint_with_mask(state->img, r, g, b, facemask, state->imgx, state->imgy, 0, 0); } } + + + /* Draw some edge lines! */ + // draw.line(((imgx+12,imgy+increment), (imgx+22,imgy+5+increment)), fill=(0,0,0), width=1) + if (!is_transparent(state->block)) { + int increment=0; + if (state->block == 44) + increment=6; + else if (state->block == 78) + increment=9; + + Imaging img_i = imaging_python_to_c(state->img); + uint8_t ink[] = {0,0,0,40}; + if ((state->x == 15) && (state->up_right_blocks != Py_None) && is_transparent(getArrayByte3D(state->up_right_blocks, 0, state->y, state->z))) { + ImagingDrawLine(img_i, state->imgx+12, state->imgy+1+increment, state->imgx+22+1, state->imgy+5+1+increment, &ink, 1); + ImagingDrawLine(img_i, state->imgx+12, state->imgy+increment, state->imgx+22+1, state->imgy+5+increment, &ink, 1); + } else if ((state->x != 15) && is_transparent(getArrayByte3D(state->blocks, state->x+1, state->y, state->z))) { + ImagingDrawLine(img_i, state->imgx+12, state->imgy+1+increment, state->imgx+22+1, state->imgy+5+1+increment, &ink, 1); + ImagingDrawLine(img_i, state->imgx+12, state->imgy+increment, state->imgx+22+1, state->imgy+5+increment, &ink, 1); + } + // if y != 0 and blocks[x,y-1,z] == 0 + + // chunk boundries are annoying + if ((state->y == 0) && (state->up_left_blocks != Py_None) && is_transparent(getArrayByte3D(state->up_left_blocks, state->x, 15, state->z))) { + ImagingDrawLine(img_i, state->imgx, state->imgy+6+1+increment, state->imgx+12+1, state->imgy+1+increment, &ink, 1); + ImagingDrawLine(img_i, state->imgx, state->imgy+6+increment, state->imgx+12+1, state->imgy+increment, &ink, 1); + } else if ((state->y != 0) && is_transparent(getArrayByte3D(state->blocks, state->x, state->y-1, state->z))) { + // draw.line(((imgx,imgy+6+increment), (imgx+12,imgy+increment)), fill=(0,0,0), width=1) + ImagingDrawLine(img_i, state->imgx, state->imgy+6+1+increment, state->imgx+12+1, state->imgy+1+increment, &ink, 1); + ImagingDrawLine(img_i, state->imgx, state->imgy+6+increment, state->imgx+12+1, state->imgy+increment, &ink, 1); + } + } } RenderModeInterface rendermode_normal = { From 50a0186b4f02263ea80dcb72932e7cd4c087176f Mon Sep 17 00:00:00 2001 From: Andrew Chin Date: Sat, 2 Apr 2011 00:38:41 -0400 Subject: [PATCH 2/7] Bump extension version --- src/overviewer.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/overviewer.h b/src/overviewer.h index c978773..6eac848 100644 --- a/src/overviewer.h +++ b/src/overviewer.h @@ -26,7 +26,7 @@ // increment this value if you've made a change to the c extesion // and want to force users to rebuild -#define OVERVIEWER_EXTENSION_VERSION 2 +#define OVERVIEWER_EXTENSION_VERSION 3 /* Python PIL, and numpy headers */ #include From 7cf8add231ecc09f7e032407c0eb7976b0ca1ebb Mon Sep 17 00:00:00 2001 From: Andrew Chin Date: Sat, 2 Apr 2011 01:00:33 -0400 Subject: [PATCH 3/7] Fix win86_32 build issues --- src/rendermode-normal.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/rendermode-normal.c b/src/rendermode-normal.c index a5e9996..d4d17dd 100644 --- a/src/rendermode-normal.c +++ b/src/rendermode-normal.c @@ -166,14 +166,15 @@ rendermode_normal_draw(void *data, RenderState *state, PyObject *src, PyObject * /* Draw some edge lines! */ // draw.line(((imgx+12,imgy+increment), (imgx+22,imgy+5+increment)), fill=(0,0,0), width=1) if (!is_transparent(state->block)) { + Imaging img_i = imaging_python_to_c(state->img); + unsigned char ink[] = {0,0,0,40}; + int increment=0; if (state->block == 44) increment=6; else if (state->block == 78) increment=9; - Imaging img_i = imaging_python_to_c(state->img); - uint8_t ink[] = {0,0,0,40}; if ((state->x == 15) && (state->up_right_blocks != Py_None) && is_transparent(getArrayByte3D(state->up_right_blocks, 0, state->y, state->z))) { ImagingDrawLine(img_i, state->imgx+12, state->imgy+1+increment, state->imgx+22+1, state->imgy+5+1+increment, &ink, 1); ImagingDrawLine(img_i, state->imgx+12, state->imgy+increment, state->imgx+22+1, state->imgy+5+increment, &ink, 1); From 6c1d85f69933eda5378da9a172221301bce8cc6b Mon Sep 17 00:00:00 2001 From: Andrew Chin Date: Sat, 2 Apr 2011 14:30:44 -0400 Subject: [PATCH 4/7] Don't crash if a biome-chunk is missing --- src/rendermode-normal.c | 33 ++++++++++++++++++++++----------- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/src/rendermode-normal.c b/src/rendermode-normal.c index d4d17dd..d5fb87d 100644 --- a/src/rendermode-normal.c +++ b/src/rendermode-normal.c @@ -49,17 +49,28 @@ rendermode_normal_start(void *data, RenderState *state) { self->biome_data = PyObject_CallMethod(state->textures, "getBiomeData", "OOO", worlddir, chunk_x_py, chunk_y_py); - self->foliagecolor = PyObject_GetAttrString(state->textures, "foliagecolor"); - self->grasscolor = PyObject_GetAttrString(state->textures, "grasscolor"); - - self->leaf_texture = PyObject_GetAttrString(state->textures, "biome_leaf_texture"); - self->grass_texture = PyObject_GetAttrString(state->textures, "biome_grass_texture"); - - facemasks_py = PyObject_GetAttrString(state->chunk, "facemasks"); - /* borrowed reference, needs to be incref'd if we keep it */ - self->facemask_top = PyTuple_GetItem(facemasks_py, 0); - Py_INCREF(self->facemask_top); - Py_DECREF(facemasks_py); + if (self->biome_data == Py_None) { + self->biome_data = NULL; + self->foliagecolor = NULL; + self->grasscolor = NULL; + + self->leaf_texture = NULL; + self->grass_texture = NULL; + self->facemask_top = NULL; + } else { + + self->foliagecolor = PyObject_GetAttrString(state->textures, "foliagecolor"); + self->grasscolor = PyObject_GetAttrString(state->textures, "grasscolor"); + + self->leaf_texture = PyObject_GetAttrString(state->textures, "biome_leaf_texture"); + self->grass_texture = PyObject_GetAttrString(state->textures, "biome_grass_texture"); + + facemasks_py = PyObject_GetAttrString(state->chunk, "facemasks"); + /* borrowed reference, needs to be incref'd if we keep it */ + self->facemask_top = PyTuple_GetItem(facemasks_py, 0); + Py_INCREF(self->facemask_top); + Py_DECREF(facemasks_py); + } } else { self->biome_data = NULL; self->foliagecolor = NULL; From 9e24d87ba2a4909d5078bc586b151e295abe8322 Mon Sep 17 00:00:00 2001 From: Andrew Chin Date: Sat, 2 Apr 2011 15:32:24 -0400 Subject: [PATCH 5/7] Started to update the README Also added a dummy sample settings file (which needs to be expanded into something useful) --- README.rst | 97 ++++++++++++++++++---------------------------- sample.settings.py | 1 + 2 files changed, 38 insertions(+), 60 deletions(-) create mode 100644 sample.settings.py diff --git a/README.rst b/README.rst index 5acc920..2b07d45 100644 --- a/README.rst +++ b/README.rst @@ -32,7 +32,7 @@ Features * Renders efficiently in parallel, using as many simultaneous processes as you want! -* Utilizes 2 levels of caching to speed up subsequent renderings of your world. +* Utilizes caching to speed up subsequent renderings of your world. * Throw the output directory up on a web server to share your Minecraft world with everyone! @@ -46,6 +46,9 @@ This program requires: * Numpy * Either the Minecraft client installed, or a terrain.png file. See the `Textures`_ section below. +* A C compiler. + +If you download a binary package, then some or all of these may not be required. I develop and test this on Linux, but need help testing it on Windows and Mac. If something doesn't work, let me know. @@ -97,15 +100,14 @@ will use the biome data to tint grass and leaves automatically -- there is no command line option to turn this feature on. If this folder does not exist, then the Overviewer will use a static tinting for grass and leaves. -Compiling the C Extension (optional) ------------------------------------- -The C Extension for Overviewer is completely optional. It provides a higher -quality image compositing function that looks better on maps with lighting -enabled, and a slight performance boost. +Compiling the C Extension +------------------------- +The C Extension for Overviewer is no longer optional. In addition to providing +a higher quality image compositing function that looks better on maps with lighting +enabled, it now does the bulk of the rendering. -If you downloaded Overviewer as a binary package, this extension may be already -compiled for you. Overviewer emits a warning if the extension is not found, but -will still work fine. +If you downloaded Overviewer as a binary package, this extension will already be +compiled for you. If you have a C compiler and the Python development libraries set up, you can compile this extension like this:: @@ -119,6 +121,9 @@ you get errors complaining about them, you can get them from the PIL source, or at . Just put them in the same directory as "_composite.c". +For more detailed instructions, check the wiki: +https://github.com/brownan/Minecraft-Overviewer/wiki/Build-Instructions + Running ------- To generate a set of Google Map tiles, use the overviewer.py script like this:: @@ -130,11 +135,6 @@ set of image tiles for your world in the directory you choose. When it's done, you will find an index.html file in the same directory that you can use to view it. -**Important note about Caches** - -The Overviewer will put a cached image for every chunk *directly in your world -directory by default*. If you do not like this behavior, you can specify -another location with the --cachedir option. See below for details. Options ------- @@ -142,19 +142,6 @@ Options -h, --help Shows the list of options and exits ---cachedir=CACHEDIR - By default, the Overviewer will save in your world directory one image - file for every chunk in your world. If you do backups of your world, - you may not want these images in your world directory. - - Use this option to specify an alternate location to put the rendered - chunk images. You must specify this same directory each rendering so - that it doesn't have to render every chunk from scratch every time. - - Example:: - - python overviewer.py --cachedir= - --imgformat=FORMAT Set the output image format used for the tiles. The default is 'png', but 'jpg' is also supported. Note that regardless of what you choose, @@ -200,39 +187,22 @@ Options -d, --delete This option changes the mode of execution. No tiles are rendered, and - instead, cache files are deleted. + instead, files are deleted. - Explanation: The Overviewer keeps two levels of cache: it saves each - chunk rendered as a png, and it keeps a hash file along side each tile - in your output directory. Using these cache files allows the Overviewer - to skip rendering of any tile image that has not changed. + *Note*: Currently only the overviewer.dat file is deleted when you run with + this option - By default, the chunk images are saved in your world directory. This - example will remove them:: - - python overviewer.py -d - - You can also delete the tile cache as well. This will force a full - re-render, useful if you've changed texture packs and want your world - to look uniform. Here's an example:: - - python overviewer.py -d <# / path> - - Be warned, this will cause the next rendering of your map to take - significantly longer, since it is having to re-generate the files you just - deleted. - ---chunklist=CHUNKLIST - Use this option to specify manually a list of chunks to consider for - updating. Without this option, every chunk is checked for update and if - necessary, re-rendered. If this option points to a file containing, 1 per - line, the path to a chunk data file, then only those in the list will be - considered for update. +--regionlist=regionlist + Use this option to specify manually a list of regions to consider for + updating. Without this option, every chunk in every region is checked for + update and if necessary, re-rendered. If this option points to a file + containing, 1 per line, the path to a region data file, then only those + in the list will be considered for update. It's up to you to build such a list. On Linux or Mac, try using the "find" - command. You could, for example, output all chunk files that are older than + command. You could, for example, output all region files that are older than a certain date. Or perhaps you can incrementally update your map by passing - in a subset of chunks each time. It's up to you! + in a subset of regions each time. It's up to you! --lighting This option enables map lighting, using lighting information stored by @@ -255,6 +225,18 @@ Options The script should be executable, and it should accept one argument: the path to the output directory. + +Settings +-------- + +You can optionally store settings in a file named settings.py. It is a regular +python script, so you can use any python functions or modules you want. + +This section needs to be expanded + +For a sample settings file, look at sample.settings.py + + Viewing the Results ------------------- Within the output directory you will find two things: an index.html file, and a @@ -308,10 +290,5 @@ An incomplete list of things I want to do soon is: * Improve efficiency -* Rendering non-cube blocks, such as torches, flowers, mine tracks, fences, - doors, and the like. Right now they are either not rendered at all, or - rendered as if they were a cube, so it looks funny. - * Some kind of graphical interface. -* A Windows exe for easier access for Windows users. diff --git a/sample.settings.py b/sample.settings.py new file mode 100644 index 0000000..7ee5011 --- /dev/null +++ b/sample.settings.py @@ -0,0 +1 @@ +# TODO: put something useful in this file! From 4276176227150cf595e80d7f71dd54283f771f4d Mon Sep 17 00:00:00 2001 From: Andrew Chin Date: Sat, 2 Apr 2011 17:29:17 -0400 Subject: [PATCH 6/7] add support for a PIL_INCLUDE_DIR environment variable --- setup.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/setup.py b/setup.py index fba44d4..0a36bcf 100644 --- a/setup.py +++ b/setup.py @@ -49,12 +49,16 @@ try: except AttributeError: numpy_include = numpy.get_numpy_include() +try: + pil_include = os.environ['PIL_INCLUDE_DIR'].split(os.pathsep) +except: + pil_include = [] c_overviewer_files = ['src/main.c', 'src/composite.c', 'src/iterate.c', 'src/endian.c'] c_overviewer_files += ['src/rendermodes.c', 'src/rendermode-normal.c', 'src/rendermode-lighting.c', 'src/rendermode-night.c', 'src/rendermode-spawn.c'] c_overviewer_files += ['src/Draw.c'] c_overviewer_includes = ['src/overviewer.h', 'src/rendermodes.h'] -setup_kwargs['ext_modules'].append(Extension('c_overviewer', c_overviewer_files, include_dirs=['.', numpy_include], depends=c_overviewer_includes, extra_link_args=[])) +setup_kwargs['ext_modules'].append(Extension('c_overviewer', c_overviewer_files, include_dirs=['.', numpy_include] + pil_include, depends=c_overviewer_includes, extra_link_args=[])) # tell build_ext to build the extension in-place # (NOT in build/) setup_kwargs['options']['build_ext'] = {'inplace' : 1} From e5660bf925a818074410e73208221b9811526761 Mon Sep 17 00:00:00 2001 From: Aaron Griffith Date: Sun, 3 Apr 2011 00:15:48 -0400 Subject: [PATCH 7/7] fixed snow/half-step edge lines --- src/rendermode-normal.c | 44 ++++++++++++++++++++++++++--------------- 1 file changed, 28 insertions(+), 16 deletions(-) diff --git a/src/rendermode-normal.c b/src/rendermode-normal.c index d5fb87d..1381eb0 100644 --- a/src/rendermode-normal.c +++ b/src/rendermode-normal.c @@ -176,33 +176,45 @@ rendermode_normal_draw(void *data, RenderState *state, PyObject *src, PyObject * /* Draw some edge lines! */ // draw.line(((imgx+12,imgy+increment), (imgx+22,imgy+5+increment)), fill=(0,0,0), width=1) - if (!is_transparent(state->block)) { + if (state->block == 44 || state->block == 78 || !is_transparent(state->block)) { Imaging img_i = imaging_python_to_c(state->img); unsigned char ink[] = {0,0,0,40}; int increment=0; - if (state->block == 44) + if (state->block == 44) // half-step increment=6; - else if (state->block == 78) + else if (state->block == 78) // snow increment=9; - if ((state->x == 15) && (state->up_right_blocks != Py_None) && is_transparent(getArrayByte3D(state->up_right_blocks, 0, state->y, state->z))) { - ImagingDrawLine(img_i, state->imgx+12, state->imgy+1+increment, state->imgx+22+1, state->imgy+5+1+increment, &ink, 1); - ImagingDrawLine(img_i, state->imgx+12, state->imgy+increment, state->imgx+22+1, state->imgy+5+increment, &ink, 1); - } else if ((state->x != 15) && is_transparent(getArrayByte3D(state->blocks, state->x+1, state->y, state->z))) { - ImagingDrawLine(img_i, state->imgx+12, state->imgy+1+increment, state->imgx+22+1, state->imgy+5+1+increment, &ink, 1); - ImagingDrawLine(img_i, state->imgx+12, state->imgy+increment, state->imgx+22+1, state->imgy+5+increment, &ink, 1); + if ((state->x == 15) && (state->up_right_blocks != Py_None)) { + unsigned char side_block = getArrayByte3D(state->up_right_blocks, 0, state->y, state->z); + if (side_block != state->block && is_transparent(side_block)) { + ImagingDrawLine(img_i, state->imgx+12, state->imgy+1+increment, state->imgx+22+1, state->imgy+5+1+increment, &ink, 1); + ImagingDrawLine(img_i, state->imgx+12, state->imgy+increment, state->imgx+22+1, state->imgy+5+increment, &ink, 1); + } + } else if (state->x != 15) { + unsigned char side_block = getArrayByte3D(state->blocks, state->x+1, state->y, state->z); + if (side_block != state->block && is_transparent(side_block)) { + ImagingDrawLine(img_i, state->imgx+12, state->imgy+1+increment, state->imgx+22+1, state->imgy+5+1+increment, &ink, 1); + ImagingDrawLine(img_i, state->imgx+12, state->imgy+increment, state->imgx+22+1, state->imgy+5+increment, &ink, 1); + } } // if y != 0 and blocks[x,y-1,z] == 0 // chunk boundries are annoying - if ((state->y == 0) && (state->up_left_blocks != Py_None) && is_transparent(getArrayByte3D(state->up_left_blocks, state->x, 15, state->z))) { - ImagingDrawLine(img_i, state->imgx, state->imgy+6+1+increment, state->imgx+12+1, state->imgy+1+increment, &ink, 1); - ImagingDrawLine(img_i, state->imgx, state->imgy+6+increment, state->imgx+12+1, state->imgy+increment, &ink, 1); - } else if ((state->y != 0) && is_transparent(getArrayByte3D(state->blocks, state->x, state->y-1, state->z))) { - // draw.line(((imgx,imgy+6+increment), (imgx+12,imgy+increment)), fill=(0,0,0), width=1) - ImagingDrawLine(img_i, state->imgx, state->imgy+6+1+increment, state->imgx+12+1, state->imgy+1+increment, &ink, 1); - ImagingDrawLine(img_i, state->imgx, state->imgy+6+increment, state->imgx+12+1, state->imgy+increment, &ink, 1); + if ((state->y == 0) && (state->up_left_blocks != Py_None)) { + unsigned char side_block = getArrayByte3D(state->up_left_blocks, state->x, 15, state->z); + if (side_block != state->block && is_transparent(side_block)) { + ImagingDrawLine(img_i, state->imgx, state->imgy+6+1+increment, state->imgx+12+1, state->imgy+1+increment, &ink, 1); + ImagingDrawLine(img_i, state->imgx, state->imgy+6+increment, state->imgx+12+1, state->imgy+increment, &ink, 1); + } + } else if (state->y != 0) { + unsigned char side_block = getArrayByte3D(state->blocks, state->x, state->y-1, state->z); + if (side_block != state->block && is_transparent(side_block)) { + // draw.line(((imgx,imgy+6+increment), (imgx+12,imgy+increment)), fill=(0,0,0), width=1) + ImagingDrawLine(img_i, state->imgx, state->imgy+6+1+increment, state->imgx+12+1, state->imgy+1+increment, &ink, 1); + ImagingDrawLine(img_i, state->imgx, state->imgy+6+increment, state->imgx+12+1, state->imgy+increment, &ink, 1); + } } } }