--- 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