diff --git a/overviewer_core/src/composite.c b/overviewer_core/src/composite.c index 6d9884780..4989e2127 100644 --- a/overviewer_core/src/composite.c +++ b/overviewer_core/src/composite.c @@ -499,7 +499,7 @@ resize_half(PyObject *dest, PyObject *src) { /* alpha properties */ int src_has_alpha, dest_has_alpha; /* iteration variables */ - unsigned int x, y; + int x, y; /* temp color variables */ unsigned int r, g, b, a; /* size values for source and destination */ @@ -508,8 +508,9 @@ resize_half(PyObject *dest, PyObject *src) { imDest = imaging_python_to_c(dest); imSrc = imaging_python_to_c(src); - if (!imDest || !imSrc) + if (!imDest || !imSrc) { return NULL; + } /* check the various image modes, make sure they make sense */ if (strcmp(imDest->mode, "RGBA") != 0) { @@ -558,51 +559,35 @@ resize_half(PyObject *dest, PyObject *src) { for (x = 0; x < dest_width; x++) { - // read first column - r = *in_row1; - r += *in_row2; - in_row1++; - in_row2++; - g = *in_row1; - g += *in_row2; - in_row1++; - in_row2++; - b = *in_row1; - b += *in_row2; - in_row1++; - in_row2++; + /* read first column */ + r = *in_row1++; + g = *in_row1++; + b = *in_row1++; + r += *in_row2++; + g += *in_row2++; + b += *in_row2++; - if (src_has_alpha) - { - a = *in_row1; - a += *in_row2; - in_row1++; - in_row2++; + if (src_has_alpha) { + + a = *in_row1++; + a += *in_row2++; } - // read second column - r += *in_row1; - r += *in_row2; - in_row1++; - in_row2++; - g += *in_row1; - g += *in_row2; - in_row1++; - in_row2++; - b += *in_row1; - b += *in_row2; - in_row1++; - in_row2++; + /* read second column */ + r += *in_row1++; + g += *in_row1++; + b += *in_row1++; + r += *in_row2++; + g += *in_row2++; + b += *in_row2++; - if (src_has_alpha) - { - a += *in_row1; - a += *in_row2; - in_row1++; - in_row2++; + if (src_has_alpha) { + + a += *in_row1++; + a += *in_row2++; } - // write blended color + /* write blended color */ *out = (UINT8)(r >> 2); out++; *out = (UINT8)(g >> 2); @@ -621,22 +606,230 @@ resize_half(PyObject *dest, PyObject *src) { return dest; } -/* wraps resize_half so it can be called directly from python */ +/* sharpens the image + */ +inline PyObject * +sharpen(PyObject *src, double sharpness) { + /* libImaging handles */ + Imaging imSrc; + /* iteration variables */ + int x, y, i; + /* temp color variables */ + int r, g, b, a; + /* size values for source and destination */ + int width, height; + /* kernel values for middle, edge and corner*/ + int k_mid, k_edg, k_crn; + + imSrc = imaging_python_to_c(src); + + if (!imSrc) { + return NULL; + } + + /* check if sharpness value is out of range */ + if (sharpness < 0 || sharpness > 1) { + PyErr_SetString(PyExc_ValueError, + "sharpness must be in the range [0, 1]"); + return NULL; + } + + /* check image mode, make sure it makes sense */ + if (strcmp(imSrc->mode, "RGBA") != 0 && strcmp(imSrc->mode, "RGB") != 0) { + PyErr_SetString(PyExc_ValueError, + "given source image does not have mode \"RGBA\" or \"RGB\""); + return NULL; + } + + width = imSrc->xsize; + height = imSrc->ysize; + + /* check that there exists anything to sharpen */ + if (width <= 2 || height <= 2) { + /* nothing to do, return */ + return src; + } + + /* kernel normalized to 1024 */ + k_edg = -(int)(sharpness * 1024) / 6; + k_crn = k_edg / 2; + k_mid = 1024 - 4 * (k_edg + k_crn); + + { + const int kernel[9] = { + k_crn, k_edg, k_crn, + k_edg, k_mid, k_edg, + k_crn, k_edg, k_crn + }; + + int alpha_kernel[9]; + int sum = 0; + + const int has_alpha = (imSrc->pixelsize == 4 ? 1 : 0); + const int pixel_size = imSrc->pixelsize; + const int row_width = pixel_size * width; + const int temp_width = row_width + pixel_size * 2; + + UINT8 *temp1 = calloc(temp_width, sizeof(UINT8)); + UINT8 *temp2 = calloc(temp_width, sizeof(UINT8)); + UINT8 *temp3 = calloc(temp_width, sizeof(UINT8)); + + UINT8 *temp_above = temp1 + pixel_size; + UINT8 *temp_middle = temp2 + pixel_size; + UINT8 *temp_below = temp3 + pixel_size; + UINT8 *temp; + + memcpy(alpha_kernel, kernel, 9); + for (i = 0; i < 9; i++) + sum += alpha_kernel[i]; + + memcpy(temp_above, (UINT8 *)imSrc->image[0], row_width); + memcpy(temp_middle, (UINT8 *)imSrc->image[0], row_width); + memcpy(temp_below, (UINT8 *)imSrc->image[1], row_width); + + memcpy(temp_above - pixel_size, temp_above, pixel_size); + memcpy(temp_middle - pixel_size, temp_middle, pixel_size); + memcpy(temp_below - pixel_size, temp_below, pixel_size); + + memcpy(temp_above + row_width, temp_above + row_width - pixel_size, pixel_size); + memcpy(temp_middle + row_width, temp_middle + row_width - pixel_size, pixel_size); + memcpy(temp_below + row_width, temp_below + row_width - pixel_size, pixel_size); + + for (y = 0; y < height; y++) { + + UINT8 *row = (UINT8 *)imSrc->image[y]; + + for (x = 0; x < row_width; x += pixel_size) { + + int rx = x; + int gx = x + 1; + int bx = x + 2; + int ax = x + 3; + + if (has_alpha) { + + row[ax] = temp_middle[ax]; + sum = 0; + + /* pre-multiply alpha */ + alpha_kernel[0] = kernel[0] * temp_above[ax - pixel_size]; + alpha_kernel[1] = kernel[1] * temp_above[ax]; + alpha_kernel[2] = kernel[2] * temp_above[ax + pixel_size]; + alpha_kernel[3] = kernel[3] * temp_middle[ax - pixel_size]; + alpha_kernel[4] = kernel[4] * temp_middle[ax]; + alpha_kernel[5] = kernel[5] * temp_middle[ax + pixel_size]; + alpha_kernel[6] = kernel[6] * temp_below[ax - pixel_size]; + alpha_kernel[7] = kernel[7] * temp_below[ax]; + alpha_kernel[8] = kernel[8] * temp_below[ax + pixel_size]; + + for (i = 0; i < 9; i++) + sum += alpha_kernel[i]; + + if (sum == 0) { + row[rx] = temp_middle[rx]; + row[gx] = temp_middle[gx]; + row[bx] = temp_middle[bx]; + row[ax] = temp_middle[ax]; + continue; + } + } + + r = alpha_kernel[0] * temp_above[rx - pixel_size]; + r += alpha_kernel[1] * temp_above[rx]; + r += alpha_kernel[2] * temp_above[rx + pixel_size]; + r += alpha_kernel[3] * temp_middle[rx - pixel_size]; + r += alpha_kernel[4] * temp_middle[rx]; + r += alpha_kernel[5] * temp_middle[rx + pixel_size]; + r += alpha_kernel[6] * temp_below[rx - pixel_size]; + r += alpha_kernel[7] * temp_below[rx]; + r += alpha_kernel[8] * temp_below[rx + pixel_size]; + + g = alpha_kernel[0] * temp_above[gx - pixel_size]; + g += alpha_kernel[1] * temp_above[gx]; + g += alpha_kernel[2] * temp_above[gx + pixel_size]; + g += alpha_kernel[3] * temp_middle[gx - pixel_size]; + g += alpha_kernel[4] * temp_middle[gx]; + g += alpha_kernel[5] * temp_middle[gx + pixel_size]; + g += alpha_kernel[6] * temp_below[gx - pixel_size]; + g += alpha_kernel[7] * temp_below[gx]; + g += alpha_kernel[8] * temp_below[gx + pixel_size]; + + b = alpha_kernel[0] * temp_above[bx - pixel_size]; + b += alpha_kernel[1] * temp_above[bx]; + b += alpha_kernel[2] * temp_above[bx + pixel_size]; + b += alpha_kernel[3] * temp_middle[bx - pixel_size]; + b += alpha_kernel[4] * temp_middle[bx]; + b += alpha_kernel[5] * temp_middle[bx + pixel_size]; + b += alpha_kernel[6] * temp_below[bx - pixel_size]; + b += alpha_kernel[7] * temp_below[bx]; + b += alpha_kernel[8] * temp_below[bx + pixel_size]; + + r /= sum; + g /= sum; + b /= sum; + + if(r > 0xFF) r = 0xFF; + else if(r < 0) r = 0; + + if(g > 0xFF) g = 0xFF; + else if(g < 0) g = 0; + + if(b > 0xFF) b = 0xFF; + else if(b < 0) b = 0; + + row[rx] = r; + row[gx] = g; + row[bx] = b; + } + + temp = temp_above; + temp_above = temp_middle; + temp_middle = temp_below; + temp_below = temp; + + if(y < height - 2) { + memcpy(temp_below, (UINT8 *)imSrc->image[y + 2], row_width); + memcpy(temp_below - pixel_size, temp_below, pixel_size); + memcpy(temp_below + row_width, temp_below + row_width - pixel_size, pixel_size); + } else { + memcpy(temp_below - pixel_size, temp_middle - pixel_size, temp_width); + } + } + + free(temp1); + free(temp2); + free(temp3); + } + + return src; +} + +/* wraps resize_half and sharpen so they can be called from python */ PyObject * resize_half_wrap(PyObject *self, PyObject *args) { /* raw input python variables */ PyObject *dest, *src; + /* sharpness parameter in range [0, 1] */ + double sharpness; /* return value: dest image on success */ PyObject *ret; - if (!PyArg_ParseTuple(args, "OO", &dest, &src)) + if (!PyArg_ParseTuple(args, "OOd", &dest, &src, &sharpness)) return NULL; ret = resize_half(dest, src); + if (ret == dest) { + + if(sharpness > 0) { + /* sharpen resized texture */ + ret = sharpen(ret, sharpness); + } + /* Python needs us to own our return value */ Py_INCREF(dest); } + return ret; } diff --git a/overviewer_core/src/overviewer.h b/overviewer_core/src/overviewer.h index e20c15f60..1e3ca7177 100644 --- a/overviewer_core/src/overviewer.h +++ b/overviewer_core/src/overviewer.h @@ -70,6 +70,7 @@ PyObject *draw_triangle(PyObject *dest, int inclusive, unsigned char r2, unsigned char g2, unsigned char b2, int tux, int tuy, int *touchups, unsigned int num_touchups); PyObject *resize_half(PyObject *dest, PyObject *src); +PyObject *sharpen(PyObject *src, double sharpness); PyObject *resize_half_wrap(PyObject *self, PyObject *args); /* forward declaration of RenderMode object */ diff --git a/overviewer_core/src/primitives/base.c b/overviewer_core/src/primitives/base.c index e6e95156a..ff41425d1 100644 --- a/overviewer_core/src/primitives/base.c +++ b/overviewer_core/src/primitives/base.c @@ -57,19 +57,19 @@ base_finish(void *data, RenderState *state) { static int base_occluded(void *data, RenderState *state, int x, int y, int z) { - if ( (x != 0) && (y != 15) && (z != 15) && - !render_mode_hidden(state->rendermode, x-1, y, z) && + if ( !render_mode_hidden(state->rendermode, x-1, y, z) && !render_mode_hidden(state->rendermode, x, y, z+1) && !render_mode_hidden(state->rendermode, x, y+1, z) && - !is_transparent(getArrayByte3D(state->blocks, x-1, y, z)) && - !is_transparent(getArrayByte3D(state->blocks, x, y, z+1)) && - !is_transparent(getArrayByte3D(state->blocks, x, y+1, z))) { + !is_transparent(get_data(state, BLOCKS, x-1, y, z)) && + !is_transparent(get_data(state, BLOCKS, x, y, z+1)) && + !is_transparent(get_data(state, BLOCKS, x, y+1, z))) { return 1; } return 0; } + static void base_draw(void *data, RenderState *state, PyObject *src, PyObject *mask, PyObject *mask_light) { PrimitiveBase *self = (PrimitiveBase *)data; diff --git a/overviewer_core/tileset.py b/overviewer_core/tileset.py index 825816acb..21b84c7fd 100644 --- a/overviewer_core/tileset.py +++ b/overviewer_core/tileset.py @@ -24,6 +24,7 @@ import time import errno import stat +import math from collections import namedtuple from itertools import product, izip @@ -490,7 +491,17 @@ def do_work(self, tilepath): # All others dest = os.path.join(self.outputdir, *(str(x) for x in tilepath[:-1])) name = str(tilepath[-1]) - self._render_compositetile(dest, name) + + # Intensity of the sharpness filter when zoomed all the way in, range = [0, 1] + initial_sharpness = 0.4 + # Reduction in the filter's intensity for each level zoomed out, range = [0, 1] + decay_rate = 0.5 + + sharpness = initial_sharpness * math.pow(decay_rate, self.treedepth - len(tilepath) - 1) + if sharpness < 0.01: + sharpness = 0 + + self._render_compositetile(dest, name, sharpness) def get_initial_data(self): """This is called similarly to get_persistent_data, but is called after @@ -803,7 +814,7 @@ def _chunk_scan(self): def __str__(self): return "" % os.path.basename(self.outputdir) - def _render_compositetile(self, dest, name): + def _render_compositetile(self, dest, name, sharpness): """ Renders a tile at os.path.join(dest, name)+".ext" by taking tiles from os.path.join(dest, name, "{0,1,2,3}.png") @@ -869,11 +880,10 @@ def _render_compositetile(self, dest, name): for path in quadPath_filtered: try: - #quad = Image.open(path[1]).resize((192,192), Image.ANTIALIAS) src = Image.open(path[1]) src.load() quad = Image.new("RGBA", (192, 192), self.options['bgcolor']) - resize_half(quad, src) + resize_half(quad, src, sharpness) img.paste(quad, path[0]) except Exception, e: logging.warning("Couldn't open %s. It may be corrupt. Error was '%s'", path[1], e)