skip contiguous regions of some background color
authorTero Marttila <terom@fixme.fi>
Sun, 24 Jan 2010 23:03:12 +0200
changeset 52 148a120ea7d5
parent 51 866eb1aad566
child 53 8a3165c604f8
skip contiguous regions of some background color
include/pngtile.h
src/lib/cache.c
src/lib/cache.h
src/lib/image.c
src/util/main.c
--- a/include/pngtile.h	Sat Jan 09 16:03:11 2010 +0200
+++ b/include/pngtile.h	Sun Jan 24 23:03:12 2010 +0200
@@ -8,6 +8,7 @@
  */
 #include <stddef.h>
 #include <stdio.h> // for FILE*
+#include <stdint.h>
 
 /**
  * "Global" context shared between images
@@ -52,6 +53,14 @@
 };
 
 /**
+ * Modifyable params for update
+ */
+struct pt_image_params {
+    /** Don't write out any contiguous regions of this color. Left-aligned in whatever format the source image is in */
+    uint8_t background_color[4];
+};
+
+/**
  * Info for image tile 
  *
  * The tile may safely overlap with the edge of the image, but it should not be entirely outside of the image
@@ -109,8 +118,10 @@
 
 /**
  * Update the given image's cache.
+ *
+ * @param params optional parameters to use for the update process
  */
-int pt_image_update (struct pt_image *image);
+int pt_image_update (struct pt_image *image, const struct pt_image_params *params);
 
 /**
  * Load the image's cache in read-only mode without trying to update it.
--- a/src/lib/cache.c	Sat Jan 09 16:03:11 2010 +0200
+++ b/src/lib/cache.c	Sun Jan 24 23:03:12 2010 +0200
@@ -330,7 +330,74 @@
     return err;
 }
 
-int pt_cache_update_png (struct pt_cache *cache, png_structp png, png_infop info)
+#define min(a, b) (((a) < (b)) ? (a) : (b))
+
+/**
+ * Decode the PNG data directly to mmap() - not good for sparse backgrounds
+ */
+static int decode_png_raw (struct pt_cache *cache, png_structp png, png_infop info)
+{
+    // write out raw image data a row at a time
+    for (size_t row = 0; row < cache->header->height; row++) {
+        // read row data, non-interlaced
+        png_read_row(png, cache->data + row * cache->header->row_bytes, NULL);
+    }
+
+    return 0;
+}
+
+static int decode_png_sparse (struct pt_cache *cache, png_structp png, png_infop info)
+{
+    // one row of pixel data
+    uint8_t *row_buf;
+
+    // alloc
+    if ((row_buf = malloc(cache->header->row_bytes)) == NULL)
+        RETURN_ERROR(PT_ERR_MEM);
+
+    // decode each row at a time
+    for (size_t row = 0; row < cache->header->height; row++) {
+        // read row data, non-interlaced
+        png_read_row(png, row_buf, NULL);
+        
+        // skip background-colored regions to keep the cache file sparse
+        // ...in blocks of PT_CACHE_BLOCK_SIZE bytes
+        for (size_t col_base = 0; col_base < cache->header->width; ){
+            // size of this block in bytes
+            size_t block_size = min(PT_CACHE_BLOCK_SIZE * cache->header->col_bytes, cache->header->row_bytes - col_base);
+
+            // ...each pixel
+            for (
+                    size_t col = col_base;
+
+                    // BLOCK_SIZE * col_bytes wide, don't go over the edge
+                    col < col_base + block_size; 
+
+                    col += cache->header->col_bytes
+            ) {
+                // test this pixel
+                if (bcmp(row_buf + col, cache->header->params.background_color, cache->header->col_bytes)) {
+                    // differs
+                    memcpy(
+                            cache->data + row * cache->header->row_bytes + col_base, 
+                            row_buf + col_base,
+                            block_size
+                    );
+                    
+                    // skip to next block
+                    break;
+                }
+            }
+
+            // skip this block
+            continue;
+        }
+    }
+
+    return 0;
+}
+
+int pt_cache_update_png (struct pt_cache *cache, png_structp png, png_infop info, const struct pt_image_params *params)
 {
     struct pt_cache_header header;
     int err;
@@ -383,17 +450,17 @@
         log_debug("num_palette=%u", num_palette);
     }
 
+    // any params
+    if (params)
+        header.params = *params;
+
     // create .tmp and write out header
     if ((err = pt_cache_create(cache, &header)))
         return err;
-
-
-    // write out raw image data a row at a time
-    for (size_t row = 0; row < header.height; row++) {
-        // read row data, non-interlaced
-        png_read_row(png, cache->data + row * header.row_bytes, NULL);
-    }
-
+    
+    // decode
+    if ((err = decode_png_sparse(cache, png, info)))
+        return err;
 
     // move from .tmp to .cache
     if ((err = pt_cache_create_done(cache)))
--- a/src/lib/cache.h	Sat Jan 09 16:03:11 2010 +0200
+++ b/src/lib/cache.h	Sun Jan 24 23:03:12 2010 +0200
@@ -62,9 +62,17 @@
 
     /** Palette entries, up to 256 entries used */
     png_color palette[PNG_MAX_PALETTE_LENGTH];
+
+    /** Parameters used */
+    struct pt_image_params params;
 };
 
 /**
+ * Handle sparse data at this granularity (pixels)
+ */
+#define PT_CACHE_BLOCK_SIZE 64
+
+/**
  * Construct the image cache info object associated with the given image.
  */
 int pt_cache_new (struct pt_cache **cache_ptr, const char *path, int mode);
@@ -84,7 +92,7 @@
 /**
  * Update the cache data from the given PNG image.
  */
-int pt_cache_update_png (struct pt_cache *cache, png_structp png, png_infop info);
+int pt_cache_update_png (struct pt_cache *cache, png_structp png, png_infop info, const struct pt_image_params *params);
 
 /**
  * Open the existing .cache for use. If already opened, does nothing.
--- a/src/lib/image.c	Sat Jan 09 16:03:11 2010 +0200
+++ b/src/lib/image.c	Sun Jan 24 23:03:12 2010 +0200
@@ -119,7 +119,7 @@
 /**
  * Open the PNG image, and write out to the cache
  */
-static int pt_image_update_cache (struct pt_image *image)
+static int pt_image_update_cache (struct pt_image *image, const struct pt_image_params *params)
 {
     png_structp png;
     png_infop info;
@@ -145,7 +145,7 @@
         JUMP_ERROR(err);
 
     // pass to cache object
-    if ((err = pt_cache_update_png(image->cache, png, info)))
+    if ((err = pt_cache_update_png(image->cache, png, info, params)))
         JUMP_ERROR(err);
 
     // finish off, ignore trailing data
@@ -221,9 +221,9 @@
     return pt_cache_status(image->cache, image->path);
 }
 
-int pt_image_update (struct pt_image *image)
+int pt_image_update (struct pt_image *image, const struct pt_image_params *params)
 {
-    return pt_image_update_cache(image);
+    return pt_image_update_cache(image, params);
 }
 
 int pt_image_load (struct pt_image *image)
--- a/src/util/main.c	Sat Jan 09 16:03:11 2010 +0200
+++ b/src/util/main.c	Sun Jan 24 23:03:12 2010 +0200
@@ -15,6 +15,7 @@
     { "verbose",        false,  NULL,   'v' },
     { "debug",          false,  NULL,   'D' },
     { "force-update",   false,  NULL,   'U' },
+    { "background",     true,   NULL,   'B' },
     { "width",          true,   NULL,   'W' },
     { "height",         true,   NULL,   'H' },
     { "x",              true,   NULL,   'x' },
@@ -38,6 +39,7 @@
         "\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-B, --background     set background pattern for cache update\n"
         "\t-W, --width          set tile width\n"
         "\t-H, --height         set tile height\n"
         "\t-x, --x              set tile x offset\n"
@@ -52,11 +54,12 @@
     int opt;
     bool force_update = false;
     struct pt_tile_info ti = {0, 0, 0, 0, 0};
+    struct pt_image_params update_params = { };
     int threads = 2;
     int tmp, err;
     
     // parse arguments
-    while ((opt = getopt_long(argc, argv, "hqvDUW:H:x:y:z:j:", options, NULL)) != -1) {
+    while ((opt = getopt_long(argc, argv, "hqvDUB:W:H:x:y:z:j:", options, NULL)) != -1) {
         switch (opt) {
             case 'h':
                 // display help
@@ -83,6 +86,23 @@
                 
                 break;
 
+            case 'B':
+                // background pattern
+                {
+                    unsigned int b1, b2, b3, b4;
+                    
+                    // parse 0xXXXXXXXX
+                    if (sscanf(optarg, "0x%02x%02x%02x%02x", &b1, &b2, &b3, &b4) != 4)
+                        FATAL("Invalid hex value for -B/--background: %s", optarg);
+                    
+                    // store
+                    update_params.background_color[0] = b1;
+                    update_params.background_color[1] = b2;
+                    update_params.background_color[2] = b3;
+                    update_params.background_color[3] = b4;
+
+                } break;
+
             case 'W':
                 ti.width = strtol(optarg, NULL, 0); break;
 
@@ -100,7 +120,7 @@
 
             case 'j':
                 if ((tmp = strtol(optarg, NULL, 0)) < 1)
-                    FATAL("Invalid value for -j/--threads");
+                    FATAL("Invalid value for -j/--threads: %s", optarg);
 
                 threads = tmp; break;
 
@@ -168,7 +188,7 @@
 
             log_debug("\tUpdating image cache...");
 
-            if ((err = pt_image_update(image))) {
+            if ((err = pt_image_update(image, &update_params))) {
                 log_warn_errno("pt_image_update: %s: %s", img_path, pt_strerror(err));
             }