pt_cache_open, pt_image_tile
authorTero Marttila <terom@fixme.fi>
Tue, 29 Dec 2009 00:18:17 +0200
changeset 9 a31048ff76a2
parent 8 400ddf1e7aa9
child 10 6806a90d934f
pt_cache_open, pt_image_tile
src/lib/cache.c
src/lib/cache.h
src/lib/image.c
src/lib/pngtile.h
src/util/main.c
--- a/src/lib/cache.c	Mon Dec 28 23:15:18 2009 +0200
+++ b/src/lib/cache.c	Tue Dec 29 00:18:17 2009 +0200
@@ -12,8 +12,7 @@
 #include <assert.h>
 
 
-
-static int pt_cache_new (struct pt_cache **cache_ptr, const char *path, int mode)
+int pt_cache_new (struct pt_cache **cache_ptr, const char *path, int mode)
 {
     struct pt_cache *cache;
 
@@ -41,20 +40,6 @@
     return -1;
 }
 
-int pt_cache_open (struct pt_cache **cache_ptr, const char *path, int mode)
-{
-    struct pt_cache *cache;
-    
-    // alloc
-    if (pt_cache_new(&cache, path, mode))
-        return -1;
-    
-    // ok
-    *cache_ptr = cache;
-
-    return 0;
-}
-
 int pt_cache_status (struct pt_cache *cache, const char *img_path)
 {
     struct stat st_img, st_cache;
@@ -151,7 +136,7 @@
 /**
  * Mmap the opened cache file using PT_CACHE_HEADER_SIZE plus the calculated size stored in cache->size
  */
-static int pt_cache_open_mmap (struct pt_cache *cache, void **addr_ptr)
+static int pt_cache_open_mmap (struct pt_cache *cache, void **addr_ptr, bool readonly)
 {
     int prot = 0;
     void *addr;
@@ -159,8 +144,11 @@
     // determine prot
     prot |= PROT_READ;
 
-    if (cache->mode & PT_IMG_WRITE)
+    if (!readonly) {
+        assert(cache->mode & PT_IMG_WRITE);
+
         prot |= PROT_WRITE;
+    }
 
     // perform mmap() from second page on
     if ((addr = mmap(NULL, PT_CACHE_HEADER_SIZE + cache->size, prot, MAP_SHARED, cache->fd, 0)) == MAP_FAILED)
@@ -173,6 +161,35 @@
 }
 
 /**
+ * Read in the cache header from the open file
+ */
+static int pt_cache_read_header (struct pt_cache *cache, struct pt_cache_header *header)
+{
+    size_t len = sizeof(*header);
+    char *buf = (char *) header;
+
+    // seek to start
+    if (lseek(cache->fd, 0, SEEK_SET) != 0)
+        return -1;
+
+    // write out full header
+    while (len) {
+        ssize_t ret;
+        
+        // try and write out the header
+        if ((ret = read(cache->fd, buf, len)) < 0)
+            return -1;
+
+        // update offset
+        buf += ret;
+        len -= ret;
+    }
+
+    // done
+    return 0;
+}
+
+/**
  * Write out the cache header into the opened file
  */
 static int pt_cache_write_header (struct pt_cache *cache, const struct pt_cache_header *header)
@@ -226,7 +243,7 @@
         goto error;
 
     // mmap header and data
-    if (pt_cache_open_mmap(cache, &base))
+    if (pt_cache_open_mmap(cache, &base, false))
         goto error;
 
     cache->header = base;
@@ -266,12 +283,46 @@
     return 0;
 }
 
+int pt_cache_open (struct pt_cache *cache)
+{
+    struct pt_cache_header header;
+    void *base;
+
+    // open the .cache
+    if (pt_cache_open_read_fd(cache, &cache->fd))
+        return -1;
+
+    // read in header
+    if (pt_cache_read_header(cache, &header))
+        return -1;
+
+    // calculate data size
+    cache->size = sizeof(header) + header.height * header.row_bytes;
+
+    // mmap header and data
+    if (pt_cache_open_mmap(cache, &base, true))
+        goto error;
+
+    cache->header = base;
+    cache->data = base + PT_CACHE_HEADER_SIZE;
+
+    // done
+    return 0;
+
+error:
+    // cleanup
+    pt_cache_abort(cache);
+
+    return -1;
+}
+
 int pt_cache_update_png (struct pt_cache *cache, png_structp png, png_infop info)
 {
     struct pt_cache_header header;
     
     // XXX: check cache_mode
     // XXX: check image doesn't use any options we don't handle
+    // XXX: close any already-opened cache file
 
     memset(&header, 0, sizeof(header));
 
@@ -285,10 +336,18 @@
             header.width, header.height, header.bit_depth, header.color_type
     );
 
+    // only pack 1 pixel per byte
+    if (header.bit_depth < 8)
+        png_set_packing(png);
+
     // fill in other info
     header.row_bytes = png_get_rowbytes(png, info);
 
-    log_debug("row_bytes=%u", header.row_bytes);
+    // calculate bpp as num_channels * bpc
+    // XXX: this assumes the packed bit depth will be either 8 or 16
+    header.col_bytes = png_get_channels(png, info) * (header.bit_depth == 16 ? 2 : 1);
+
+    log_debug("row_bytes=%u, col_bytes=%u", header.row_bytes, header.col_bytes);
     
     // palette etc.
     if (header.color_type == PNG_COLOR_TYPE_PALETTE) {
@@ -309,7 +368,6 @@
         log_debug("num_palette=%u", num_palette);
     }
 
-
     // create .tmp and write out header
     if (pt_cache_open_create(cache, &header))
         return -1;
@@ -330,6 +388,44 @@
     return 0;
 }
 
+int pt_cache_tile_png (struct pt_cache *cache, png_structp png, png_infop info, const struct pt_tile_info *ti)
+{
+    if (!cache->data) {
+        // not yet open
+        if (pt_cache_open(cache))
+            return -1;
+    }
+
+    // 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
+    );
+
+    // set palette?
+    if (cache->header->color_type == PNG_COLOR_TYPE_PALETTE)
+        png_set_PLTE(png, info, cache->header->palette, cache->header->num_palette);
+
+    // write meta-info
+    png_write_info(png, info);
+
+    // pixel data is packed into 1 pixel per byte
+    png_set_packing(png);
+    
+    // write image data
+    for (size_t row = ti->y; row < ti->y + ti->height; row++) {
+        size_t col = ti->x;
+        
+        // XXX: fill out-of-range regions in some background color
+        png_write_row(png, cache->data + (row * cache->header->row_bytes) + (col * cache->header->col_bytes));
+    }
+
+    // done, flush remaining output
+    png_write_flush(png);
+
+    // ok
+    return 0;
+}
+
 void pt_cache_destroy (struct pt_cache *cache)
 {
     free(cache->path);
--- a/src/lib/cache.h	Mon Dec 28 23:15:18 2009 +0200
+++ b/src/lib/cache.h	Tue Dec 29 00:18:17 2009 +0200
@@ -56,6 +56,9 @@
 
     /** Convenience field for number of bytes per row */
     uint32_t row_bytes;
+    
+    /** Number of bytes per pixel */
+    uint8_t col_bytes;
 
     /** Palette entries, up to 256 entries used */
     png_color palette[PNG_MAX_PALETTE_LENGTH];
@@ -64,7 +67,7 @@
 /**
  * Construct the image cache info object associated with the given image.
  */
-int pt_cache_open (struct pt_cache **cache_ptr, const char *path, int mode);
+int pt_cache_new (struct pt_cache **cache_ptr, const char *path, int mode);
 
 /**
  * Verify if the cached data eixsts, or has become stale compared to the given original file.
@@ -79,6 +82,18 @@
 int pt_cache_update_png (struct pt_cache *cache, png_structp png, png_infop info);
 
 /**
+ * Actually open the existing .cache for use
+ */
+int pt_cache_open (struct pt_cache *cache);
+
+/**
+ * Render out a PNG tile as given, into the established png object, up to (but not including) the png_write_end.
+ *
+ * If the cache is not yet open, this will open it
+ */
+int pt_cache_tile_png (struct pt_cache *cache, png_structp png, png_infop info, const struct pt_tile_info *ti);
+
+/**
  * Release all resources associated with the given cache object without any cleanup.
  */
 void pt_cache_destroy (struct pt_cache *cache);
--- a/src/lib/image.c	Mon Dec 28 23:15:18 2009 +0200
+++ b/src/lib/image.c	Tue Dec 29 00:18:17 2009 +0200
@@ -180,8 +180,8 @@
     if (pt_image_cache_path(image, cache_path, sizeof(cache_path)))
         goto error;
 
-    // open the cache object for this image
-    if (pt_cache_open(&image->cache, cache_path, cache_mode))
+    // create the cache object for this image (doesn't yet open it)
+    if (pt_cache_new(&image->cache, cache_path, cache_mode))
         goto error;
     
     // ok, ready for access
@@ -198,6 +198,7 @@
 int pt_image_info (struct pt_image *image, const struct pt_image_info **info_ptr)
 {
     // XXX: ensure that this was read?
+    // XXX: get this from image->cache?
     *info_ptr = &image->info;
 
     return 0;
@@ -213,6 +214,44 @@
     return pt_image_update_cache(image);
 }
 
+int pt_image_tile (struct pt_image *image, const struct pt_tile_info *tile_info, FILE *out)
+{
+    png_structp png = NULL;
+    png_infop info = NULL;
+        
+    // open PNG writer
+    if ((png = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL)) == NULL)
+        goto error;
+    
+    if ((info = png_create_info_struct(png)) == NULL)
+        goto error;
+
+    // libpng error trap
+    if (setjmp(png_jmpbuf(png)))
+        goto error;
+    
+    // setup IO
+    png_init_io(png, out);
+    
+    // render tile
+    if (pt_cache_tile_png(image->cache, png, info, tile_info))
+        goto error;
+
+    // done
+    png_write_end(png, info);
+
+    // cleanup
+    png_destroy_write_struct(&png, &info);
+
+    return 0;
+
+error:
+    // cleanup
+    png_destroy_write_struct(&png, &info);
+
+    return -1;
+}
+
 void pt_image_destroy (struct pt_image *image)
 {
     free(image->path);
--- a/src/lib/pngtile.h	Mon Dec 28 23:15:18 2009 +0200
+++ b/src/lib/pngtile.h	Tue Dec 29 00:18:17 2009 +0200
@@ -7,6 +7,7 @@
  * Tile-based access to large PNG images.
  */
 #include <stddef.h>
+#include <stdio.h> // for FILE*
 
 /**
  * "Global" context shared between images
@@ -50,6 +51,18 @@
     size_t width, height;
 };
 
+/** Info for image tile */
+struct pt_tile_info {
+    /** Dimensions of output image */
+    size_t width, height;
+
+    /** Pixel coordinates of top-left corner */
+    size_t x, y;
+
+    /** Zoom factor (out < zero < in) */
+    // TODO: int zoom;
+};
+
 
 int pt_ctx_new (struct pt_ctx **ctx_ptr);
 
@@ -81,6 +94,13 @@
 int pt_image_update (struct pt_image *image);
 
 /**
+ * Render a PNG tile to a stream.
+ *
+ * The PNG data will be written to the given stream, which will be flushed, but not closed.
+ */
+int pt_image_tile (struct pt_image *image, const struct pt_tile_info *info, FILE *out);
+
+/**
  * Release the given pt_image without any clean shutdown
  */
 void pt_image_destroy (struct pt_image *image);
--- a/src/util/main.c	Mon Dec 28 23:15:18 2009 +0200
+++ b/src/util/main.c	Tue Dec 29 00:18:17 2009 +0200
@@ -14,6 +14,10 @@
     { "verbose",        false,  NULL,   'v' },
     { "debug",          false,  NULL,   'D' },
     { "force-update",   false,  NULL,   'U' },
+    { "width",          true,   NULL,   'W' },
+    { "height",         true,   NULL,   'H' },
+    { "x",              true,   NULL,   'x' },
+    { "y",              true,   NULL,   'y' },
     { 0,                0,      0,      0   }
 };
 
@@ -31,6 +35,10 @@
         "\t-v, --verbose        display more informational output\n"
         "\t-D, --debug          equivalent to -v\n"
         "\t-U, --force-update   unconditionally update image caches\n"
+        "\t-W, --width          set tile width\n"
+        "\t-H, --height         set tile height\n"
+        "\t-x, --x              set tile x offset\n"
+        "\t-y, --y              set tile z offset\n"
     );
 }
 
@@ -38,9 +46,10 @@
 {
     int opt;
     bool force_update = false;
+    struct pt_tile_info ti = {0, 0, 0, 0};
     
     // parse arguments
-    while ((opt = getopt_long(argc, argv, "hqvDU", options, NULL)) != -1) {
+    while ((opt = getopt_long(argc, argv, "hqvDUW:H:x:y:", options, NULL)) != -1) {
         switch (opt) {
             case 'h':
                 // display help
@@ -67,6 +76,18 @@
                 
                 break;
 
+            case 'W':
+                ti.width = strtol(optarg, NULL, 0); break;
+
+            case 'H':
+                ti.height = strtol(optarg, NULL, 0); break;
+
+            case 'x':
+                ti.x = strtol(optarg, NULL, 0); break;
+
+            case 'y':
+                ti.y = strtol(optarg, NULL, 0); break;
+
             case '?':
                 // useage error
                 help(argv[0]);
@@ -139,7 +160,13 @@
         else
             log_info("\tImage dimensions: %zux%zu", img_info->width, img_info->height);
 
-        // done
+        // render tile?
+        if (ti.width && ti.height) {
+            log_debug("Render tile %zux%zu@(%zu,%zu) -> stdout", ti.width, ti.height, ti.x, ti.y);
+
+            if (pt_image_tile(image, &ti, stdout))
+                log_errno("pt_image_tile: %s", img_path);
+        }
 
 error:
         // cleanup