--- 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,
};
--- 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 @@
</div>
<script type="text/javascript">
- var tile_source = new Source("%(tile_url)s", %(tile_width)d, %(tile_height)d, 0, 0);
+ var tile_source = new Source("%(tile_url)s", %(tile_width)d, %(tile_height)d, -4, 0);
var main = new Viewport(tile_source, "viewport");
</script>
</body>
@@ -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")
--- 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",
--- 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;
--- 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)
--- 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");
--- 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();