src/lib/cache.c
changeset 9 a31048ff76a2
parent 8 400ddf1e7aa9
child 10 6806a90d934f
--- 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);