# HG changeset patch # User Tero Marttila # Date 1262787815 -7200 # Node ID a387bc77ad5251ff5d2383cbaed4e55367e4d026 # Parent 0ed40e11b0e8b934f24ef46eec34289f97e60b13 implement zoom diff -r 0ed40e11b0e8 -r a387bc77ad52 include/pngtile.h --- a/include/pngtile.h Wed Jan 06 16:05:02 2010 +0200 +++ b/include/pngtile.h Wed Jan 06 16:23:35 2010 +0200 @@ -63,8 +63,8 @@ /** Pixel coordinates of top-left corner */ size_t x, y; - /** Zoom factor (out < zero < in) */ - // TODO: int zoom; + /** Zoom factor of 2^z (out < zero < in) */ + int zoom; }; /** @@ -186,6 +186,8 @@ PT_ERR_PTHREAD_CREATE, PT_ERR_CTX_SHUTDOWN, + PT_ERR_ZOOM, + PT_ERR_MAX, }; diff -r 0ed40e11b0e8 -r a387bc77ad52 pngtile/wsgi.py --- a/pngtile/wsgi.py Wed Jan 06 16:05:02 2010 +0200 +++ b/pngtile/wsgi.py Wed Jan 06 16:23:35 2010 +0200 @@ -66,7 +66,7 @@ @@ -79,8 +79,22 @@ tile_height = TILE_HEIGHT, ) -def render_tile (image, x, y) : - return image.tile_mem(TILE_WIDTH, TILE_HEIGHT, x, y) +def scale_by_zoom (val, zoom) : + if zoom > 0 : + return val << zoom + + elif zoom > 0 : + return val >> -zoom + + else : + return val + +def render_tile (image, x, y, zoom) : + return image.tile_mem( + TILE_WIDTH, TILE_HEIGHT, + scale_by_zoom(x, -zoom), scale_by_zoom(y, -zoom), + zoom + ) def handle_main (req) : # path to image @@ -128,9 +142,10 @@ # tile x = int(req.args['x']) y = int(req.args['y']) + zoom = int(req.args.get('zoom', "0")) # yay render - return Response(render_tile(image, x, y), content_type="image/png") + return Response(render_tile(image, x, y, zoom), content_type="image/png") else : raise exceptions.BadRequest("Unknown args") diff -r 0ed40e11b0e8 -r a387bc77ad52 python/pypngtile.pyx --- a/python/pypngtile.pyx Wed Jan 06 16:05:02 2010 +0200 +++ b/python/pypngtile.pyx Wed Jan 06 16:23:35 2010 +0200 @@ -36,6 +36,7 @@ struct pt_tile_info : size_t width, height size_t x, y + int zoom int pt_image_open (pt_image **image_ptr, pt_ctx *ctx, char *png_path, int cache_mode) int pt_image_info_func "pt_image_info" (pt_image *image, pt_image_info **info_ptr) @@ -90,7 +91,7 @@ pt_image_update(self.image) ) - def tile_file (self, size_t width, size_t height, size_t x, size_t y, object out) : + def tile_file (self, size_t width, size_t height, size_t x, size_t y, int zoom, object out) : cdef stdio.FILE *outf cdef pt_tile_info ti @@ -106,12 +107,13 @@ ti.height = height ti.x = x ti.y = y + ti.zoom = zoom trap_err("pt_image_tile_file", pt_image_tile_file(self.image, &ti, outf) ) - def tile_mem (self, size_t width, size_t height, size_t x, size_t y) : + def tile_mem (self, size_t width, size_t height, size_t x, size_t y, int zoom) : cdef pt_tile_info ti cdef char *buf cdef size_t len @@ -120,6 +122,7 @@ ti.height = height ti.x = x ti.y = y + ti.zoom = zoom # render and return ptr to buffer trap_err("pt_image_tile_mem", diff -r 0ed40e11b0e8 -r a387bc77ad52 src/lib/cache.c --- a/src/lib/cache.c Wed Jan 06 16:05:02 2010 +0200 +++ b/src/lib/cache.c Wed Jan 06 16:23:35 2010 +0200 @@ -493,19 +493,51 @@ return 0; } -int pt_cache_tile_png (struct pt_cache *cache, png_structp png, png_infop info, const struct pt_tile_info *ti) +static size_t scale_by_zoom_factor (size_t value, int z) +{ + if (z > 0) + return value << z; + + else if (z < 0) + return value >> -z; + + else + return value; +} + +#define ADD_AVG(l, r) (l) = ((l) + (r)) / 2 + +static int png_pixel_data (png_color *out, struct pt_cache *cache, size_t row, size_t col) +{ + if (cache->header->color_type == PNG_COLOR_TYPE_PALETTE) { + // palette entry number + int p; + + if (cache->header->bit_depth == 8) + p = *((uint8_t *) tile_row_col(cache, row, col)); + else + return -1; + + if (p >= cache->header->num_palette) + return -1; + + // reference data from palette + *out = cache->header->palette[p]; + + return 0; + + } else { + return -1; + } +} + +/** + * Write unscaled tile data + */ +static int write_png_data_unzoomed (struct pt_cache *cache, png_structp png, png_infop info, const struct pt_tile_info *ti) { int err; - // ensure open - if ((err = pt_cache_open(cache))) - return err; - - // check within bounds - if (ti->x >= cache->header->width || ti->y >= cache->header->height) - // completely outside - RETURN_ERROR(PT_ERR_TILE_CLIP); - // set basic info png_set_IHDR(png, info, ti->width, ti->height, cache->header->bit_depth, cache->header->color_type, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT @@ -529,6 +561,104 @@ else // fill in clipped regions err = write_png_data_clipped(cache, png, info, ti); + + return err; +} + +/** + * Write scaled tile data + */ +static int write_png_data_zoomed (struct pt_cache *cache, png_structp png, png_infop info, const struct pt_tile_info *ti) +{ + // size of the image data in px + size_t data_width = scale_by_zoom_factor(ti->width, -ti->zoom); + size_t data_height = scale_by_zoom_factor(ti->height, -ti->zoom); + + // input pixels per output pixel + size_t pixel_size = scale_by_zoom_factor(1, -ti->zoom); + + // bytes per output pixel + size_t pixel_bytes = 3; + + // size of the output tile in px + size_t row_width = ti->width; + + // size of an output row in bytes (RGB) + size_t row_bytes = row_width * 3; + + // buffer to hold output rows + uint8_t *row_buf; + + // XXX: only supports zooming out... + if (ti->zoom >= 0) + RETURN_ERROR(PT_ERR_ZOOM); + + if ((row_buf = malloc(row_bytes)) == NULL) + RETURN_ERROR(PT_ERR_MEM); + + + // define pixel format: 8bpp RGB + png_set_IHDR(png, info, ti->width, ti->height, 8, PNG_COLOR_TYPE_RGB, + PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT + ); + + // write meta-info + png_write_info(png, info); + + // ...each output row + for (size_t out_row = 0; out_row < ti->height; out_row++) { + memset(row_buf, 0, row_bytes); + + // ...includes pixels starting from this row. + size_t in_row_offset = ti->y + scale_by_zoom_factor(out_row, -ti->zoom); + + // ...each out row includes pixel_size in rows + for (size_t in_row = in_row_offset; in_row < in_row_offset + pixel_size && in_row < cache->header->height; in_row++) { + // and includes each input pixel + for (size_t in_col = ti->x; in_col < ti->x + data_width && in_col < cache->header->width; in_col++) { + png_color c; + + // ...for this output pixel + size_t out_col = scale_by_zoom_factor(in_col - ti->x, ti->zoom); + + // get pixel RGB data + if (png_pixel_data(&c, cache, in_row, in_col)) + return -1; + + // average the RGB data + ADD_AVG(row_buf[out_col * pixel_bytes + 0], c.red); + ADD_AVG(row_buf[out_col * pixel_bytes + 1], c.green); + ADD_AVG(row_buf[out_col * pixel_bytes + 2], c.blue); + } + } + + // output + png_write_row(png, row_buf); + } + + // done + return 0; +} + +int pt_cache_tile_png (struct pt_cache *cache, png_structp png, png_infop info, const struct pt_tile_info *ti) +{ + int err; + + // ensure open + if ((err = pt_cache_open(cache))) + return err; + + // check within bounds + if (ti->x >= cache->header->width || ti->y >= cache->header->height) + // completely outside + RETURN_ERROR(PT_ERR_TILE_CLIP); + + // unscaled or scaled? + if (ti->zoom) + err = write_png_data_zoomed(cache, png, info, ti); + + else + err = write_png_data_unzoomed(cache, png, info, ti); if (err) return err; diff -r 0ed40e11b0e8 -r a387bc77ad52 src/lib/error.c --- a/src/lib/error.c Wed Jan 06 16:05:02 2010 +0200 +++ b/src/lib/error.c Wed Jan 06 16:23:35 2010 +0200 @@ -30,6 +30,7 @@ [PT_ERR_CTX_SHUTDOWN] = "pt_ctx is shutting down", [PT_ERR_TILE_CLIP] = "Tile outside of image", + [PT_ERR_ZOOM] = "Invalid zoom level", }; const char *pt_strerror (int err) diff -r 0ed40e11b0e8 -r a387bc77ad52 src/util/main.c --- a/src/util/main.c Wed Jan 06 16:05:02 2010 +0200 +++ b/src/util/main.c Wed Jan 06 16:23:35 2010 +0200 @@ -19,6 +19,7 @@ { "height", true, NULL, 'H' }, { "x", true, NULL, 'x' }, { "y", true, NULL, 'y' }, + { "zoom", true, NULL, 'z' }, { "threads", true, NULL, 'j' }, { 0, 0, 0, 0 } }; @@ -41,6 +42,7 @@ "\t-H, --height set tile height\n" "\t-x, --x set tile x offset\n" "\t-y, --y set tile z offset\n" + "\t-z, --zoom set zoom factor (<0)\n" "\t-j, --threads number of threads\n" ); } @@ -49,12 +51,12 @@ { int opt; bool force_update = false; - struct pt_tile_info ti = {0, 0, 0, 0}; + struct pt_tile_info ti = {0, 0, 0, 0, 0}; int threads = 2; int tmp, err; // parse arguments - while ((opt = getopt_long(argc, argv, "hqvDUW:H:x:y:j:", options, NULL)) != -1) { + while ((opt = getopt_long(argc, argv, "hqvDUW:H:x:y:z:j:", options, NULL)) != -1) { switch (opt) { case 'h': // display help @@ -93,6 +95,9 @@ case 'y': ti.y = strtol(optarg, NULL, 0); break; + case 'z': + ti.zoom = strtol(optarg, NULL, 0); break; + case 'j': if ((tmp = strtol(optarg, NULL, 0)) < 1) FATAL("Invalid value for -j/--threads"); diff -r 0ed40e11b0e8 -r a387bc77ad52 static/tiles2.js --- a/static/tiles2.js Wed Jan 06 16:05:02 2010 +0200 +++ b/static/tiles2.js Wed Jan 06 16:23:35 2010 +0200 @@ -26,7 +26,7 @@ var x = col * this.tile_width; var y = row * this.tile_height; - var url = this.path + "?x=" + x + "&y=" + y + "&z=" + zl + "&sw=" + sw + "&sh=" + sh; + var url = this.path + "?x=" + x + "&y=" + y + "&zoom=" + zl + "&sw=" + sw + "&sh=" + sh; if (this.refresh) url += "&ts=" + new Date().getTime();