From 1511ebe5ed9943e31a66214b720544cdbf515eaa Mon Sep 17 00:00:00 2001 From: giraffedata Date: Sat, 25 Sep 2010 20:41:29 +0000 Subject: Get pamtopdbimg, pdbimgtopam working git-svn-id: http://svn.code.sf.net/p/netpbm/code/trunk@1302 9d0c8265-081b-0410-96cb-a4ca84ce46f8 --- converter/other/Makefile | 4 +- converter/other/ipdb.c | 1248 ++++------------------------------------- converter/other/ipdb.h | 68 ++- converter/other/pamtopdbimg.c | 613 ++++++++++++++++++-- converter/other/pdbimgtopam.c | 774 +++++++++++++++++++++++++ doc/HISTORY | 2 +- 6 files changed, 1518 insertions(+), 1191 deletions(-) create mode 100644 converter/other/pdbimgtopam.c diff --git a/converter/other/Makefile b/converter/other/Makefile index 64c69b1c..cded8f33 100644 --- a/converter/other/Makefile +++ b/converter/other/Makefile @@ -93,7 +93,7 @@ ifneq ($(DONT_HAVE_PROCESS_MGMT),Y) PORTBINARIES += pstopnm endif -BINARIES = $(PORTBINARIES) pnmtorast rasttopnm pamtopdbimg +BINARIES = $(PORTBINARIES) pnmtorast rasttopnm pamtopdbimg pdbimgtopam ifeq ($(HAVE_PNGLIB),Y) BINARIES += pnmtopng pngtopam pamrgbatopng @@ -210,7 +210,7 @@ pnmtorast rasttopnm: %: %.o rast.o $(NETPBMLIB) $(LIBOPT) $(shell $(LIBOPT) $(NETPBMLIB)) \ $(MATHLIB) $(LDFLAGS) $(LDLIBS) $(RPATH) $(LADD) -pamtopdbimg: %: %.o ipdb.o $(NETPBMLIB) $(LIBOPT) +pdbimgtopam pamtopdbimg: %: %.o ipdb.o $(NETPBMLIB) $(LIBOPT) $(LD) -o $@ $@.o ipdb.o \ $(shell $(LIBOPT) $(NETPBMLIB)) \ $(LDFLAGS) $(LDLIBS) $(RPATH) $(LADD) diff --git a/converter/other/ipdb.c b/converter/other/ipdb.c index 39e65b35..5c1fc314 100644 --- a/converter/other/ipdb.c +++ b/converter/other/ipdb.c @@ -19,6 +19,7 @@ * Authors: Eric A. Howe (mu@trends.net) * Bryan Henderson, 2010 */ +#include #include #include @@ -28,143 +29,153 @@ typedef uint32_t pilot_time_t; -/* - * Pixel setting macros. - */ -#define setg16pixel(b,v,o) ((b) |= ((v) << (4 - 4*(o)))) -#define getg16pixel(b,o) (((b) >> (4 - 4*(o))) & 0x0f) -#define setgpixel(b,v,o) ((b) |= ((v) << (6 - 2*(o)))) -#define getgpixel(b,o) (((b) >> (6 - 2*(o))) & 0x03) -#define setmpixel(b,v,o) ((b) |= ((v) << (7 - (o)))) -#define getmpixel(b,o) (((b) >> (7 - (o))) & 0x01) -/* - * Pixels/byte. - */ -#define img_ppb(i) ( \ - (i)->type == IMG_GRAY ? 4 : \ - (i)->type == IMG_GRAY16 ? 2 : \ - 8 \ - ) -/* - * Size (in bytes) of an image's data. - */ -#define img_size(i) (size_t)((i)->width/img_ppb(i)*(i)->height) + +static unsigned int +imgPpb(IMAGE * const imgP) { +/*---------------------------------------------------------------------------- + Pixels per byte +-----------------------------------------------------------------------------*/ + return + imgP->type == IMG_GRAY ? 4 : + imgP->type == IMG_GRAY16 ? 2 : + 8; +} + + + +unsigned int +ipdb_img_ppb(IMAGE * const imgP) { +/*---------------------------------------------------------------------------- + Pixels per byte +-----------------------------------------------------------------------------*/ + return imgPpb(imgP); +} + + + +size_t +ipdb_img_size(IMAGE * const imgP) { +/*---------------------------------------------------------------------------- + Size (in bytes) of an image's data. +-----------------------------------------------------------------------------*/ + return (size_t)(imgP->width / imgPpb(imgP) * imgP->height); +} + + /* * Return the start of row `r'. */ -#define img_row(i, r) (&(i)->data[(r)*(i)->width/img_ppb(i)]) + uint8_t * + ipdb_img_row(IMAGE * const imgP, + unsigned int const row) { -/* - * Only use four bytes of these. - */ -#define IPDB_vIMG "vIMG" -#define IPDB_View "View" + return &imgP->data[(row) * imgP->width / imgPpb(imgP)]; + } -/* - * Only use three bytes of this. - */ -#define IPDB_MYST "\x40\x6f\x80" -static pilot_time_t const unixepoch = (66*365+17)*24*3600; - /* The unix epoch in Mac time (the Mac epoch is 00:00 UTC 1904.01.01). - The 17 is the number of leap years. - */ -static const char * const errorDesc[] = { - /* E_BADCOLORS */ - "Invalid palette, only {0x00, 0x55, 0xAA, 0xFF} allowed.", + #define img_row(i, r) - /* E_NOTIMAGE */ - "Not an image file.", + static pilot_time_t const unixepoch = (66*365+17)*24*3600; + /* The unix epoch in Mac time (the Mac epoch is 00:00 UTC 1904.01.01). + The 17 is the number of leap years. + */ - /* E_IMAGETHERE */ - "Image record already present, logic error.", + static const char * const errorDesc[] = { + /* E_BADCOLORS */ + "Invalid palette, only {0x00, 0x55, 0xAA, 0xFF} allowed.", - /* E_IMAGENOTTHERE */ - "Image record required before text record, logic error.", + /* E_NOTIMAGE */ + "Not an image file.", - /* E_TEXTTHERE */ - "Text record already present, logic error.", + /* E_IMAGETHERE */ + "Image record already present, logic error.", - /* E_NOTRECHDR */ - "Invalid record header encountered.", + /* E_IMAGENOTTHERE */ + "Image record required before text record, logic error.", - /* E_UNKNOWNRECHDR */ - "Unknown record header.", + /* E_TEXTTHERE */ + "Text record already present, logic error.", - /* E_TOOBIGG */ - "Image too big, maximum size approx. 640*400 gray pixels.", + /* E_NOTRECHDR */ + "Invalid record header encountered.", - /* E_TOOBIGM */ - "Image too big, maximum size approx. 640*800 monochrome pixels.", -}; + /* E_UNKNOWNRECHDR */ + "Unknown record header.", + /* E_TOOBIGG */ + "Image too big, maximum size approx. 640*400 gray pixels.", + /* E_TOOBIGM */ + "Image too big, maximum size approx. 640*800 monochrome pixels.", + }; -const char * -ipdb_err(int const e) { - if (e < 0) - return e >= E_LAST ? errorDesc[-e - 1] : "unknown error"; - else - return strerror(e); -} + const char * + ipdb_err(int const e) { + if (e < 0) + return e >= E_LAST ? errorDesc[-e - 1] : "unknown error"; + else + return strerror(e); + } -static void -rechdr_free(RECHDR * const recP) { - if (recP) { - free(recP->extra); - free(recP); - } -} + static void + rechdr_free(RECHDR * const recP) { + if (recP) { + free(recP->extra); + free(recP); + } + } -static void -image_free(IMAGE * const imgP) { - if (imgP) { - rechdr_free(imgP->r); - free(imgP->data); - free(imgP); - } -} + void + ipdb_image_free(IMAGE * const imgP) { + if (imgP) { + rechdr_free(imgP->r); + free(imgP->data); + free(imgP); + } + } -static void -text_free(TEXT * const textP) { - if (textP) { - rechdr_free(textP->r); - free(textP->data); - free(textP); - } -} + void + ipdb_text_free(TEXT * const textP) { + if (textP) { + rechdr_free(textP->r); + free(textP->data); + free(textP); + } + } -static void -pdbhead_free(PDBHEAD * const headP) { - free(headP); -} + void + ipdb_pdbhead_free(PDBHEAD * const headP) { + free(headP); + } -static void -ipdb_clear(IPDB * const pdbP) { - if (pdbP) { - image_free(pdbP->i); - text_free(pdbP->t); - pdbhead_free(pdbP->p); + + void + ipdb_clear(IPDB * const pdbP) { + + if (pdbP) { + ipdb_image_free(pdbP->i); + ipdb_text_free(pdbP->t); + ipdb_pdbhead_free(pdbP->p); } } @@ -179,8 +190,8 @@ ipdb_free(IPDB * const pdbP) { -static PDBHEAD * -pdbhead_alloc(const char * const name) { +PDBHEAD * +ipdb_pdbhead_alloc(const char * const name) { PDBHEAD * pdbHeadP; @@ -240,8 +251,8 @@ rechdr_alloc(int const type, -static IMAGE * -image_alloc(const char * const name, +IMAGE * +ipdb_image_alloc(const char * const name, int const type, int const w, int const h) { @@ -280,7 +291,7 @@ image_alloc(const char * const name, failed = true; if (failed) - image_free(imgP); + ipdb_image_free(imgP); } else failed = true; @@ -289,8 +300,8 @@ image_alloc(const char * const name, -static TEXT * -text_alloc(const char * const content) { +TEXT * +ipdb_text_alloc(const char * const content) { TEXT * textP; bool failed; @@ -321,7 +332,7 @@ text_alloc(const char * const content) { failed = true; if (failed) - text_free(textP); + ipdb_text_free(textP); } else failed = true; @@ -344,7 +355,7 @@ ipdb_alloc(const char * const name) { MEMSZERO(pdbP); if (name) { - pdbP->p = pdbhead_alloc(name); + pdbP->p = ipdb_pdbhead_alloc(name); if (!pdbP->p) failed = true; @@ -359,1028 +370,13 @@ ipdb_alloc(const char * const name) { -static uint8_t * -decompress(const uint8_t * const buffer, - int const byteCount) { -/* - * This will *always* free `buffer'. - * - * The compression scheme used is a simple RLE; the control codes, - * CODE, are one byte and have the following meanings: - * - * CODE > 0x80 Insert (CODE + 1 - 0x80) copies of the next byte. - * CODE <= 0x80 Insert the next (CODE + 1) literal bytes. - * - * Compressed pieces can (and do) cross row boundaries. - */ - uint8_t * data; - - MALLOCARRAY(data, byteCount); - - if (data) { - const uint8_t * inP; - uint8_t * outP; - int bytesLeft; - - for (bytesLeft = byteCount, inP = &buffer[0], outP = &data[0]; - bytesLeft > 0; - ) { - - int got, put; - - if (*inP > 0x80) { - put = *inP++ + 1 - 0x80; - memset(outP, *inP, put); - got = 1; - } else { - put = *inP++ + 1; - memcpy(outP, inP, put); - got = put; - } - inP += got; - outP += put; - bytesLeft -= put; - } - } - free((void *)buffer); - return data; -} - - - -#define UNKNOWN_OFFSET (uint32_t)-1 - -static int -image_read_data(IMAGE * const imgP, - uint32_t const end_offset, - FILE * const fP) { - - int retval; - size_t data_size; - uint8_t * buffer; - - data_size = 0; /* initial value */ - - if (end_offset == UNKNOWN_OFFSET) { - /* - * Read until EOF. Some of them have an extra zero byte - * dangling off the end. I originally thought this was - * an empty note record (even though there was no record - * header for it); however, the release notes for Image - * Compression Manager 1.1 on http://www.pilotgear.com - * note this extra byte as a bug in Image Compression - * Manager 1.0 which 1.1 fixes. We'll just blindly read - * this extra byte and ignore it by paying attention to - * the image dimensions. - */ - MALLOCARRAY(buffer, img_size(imgP)); - - if (buffer == NULL) - retval = ENOMEM; - else { - data_size = fread(buffer, 1, img_size(imgP), fP); - if (data_size <= 0) - retval = EIO; - else - retval = 0; - - if (retval != 0) - free(buffer); - } - } else { - /* - * Read to the indicated offset. - */ - data_size = end_offset - ftell(fP) + 1; - - MALLOCARRAY(buffer, data_size); - - if (buffer == NULL) - retval = ENOMEM; - else { - ssize_t rc; - rc = fread(buffer, 1, data_size, fP); - if (rc != data_size) - retval = EIO; - else - retval = 0; - - if (retval != 0) - free(buffer); - } - } - if (retval == 0) { - /* - * Compressed data can cross row boundaries so we decompress - * the data here to avoid messiness in the row access functions. - */ - if (data_size != img_size(imgP)) { - imgP->data = decompress(buffer, img_size(imgP)); - if (imgP->data == NULL) - retval = ENOMEM; - else - imgP->compressed = true; - } else { - imgP->compressed = false; - imgP->data = buffer; - } - - if (retval != 0) - free(buffer); - } - return retval; -} - - - -static int -image_read(IMAGE * const imgP, - uint32_t const end_offset, - FILE * const fP) { - - if (imgP) { - imgP->r->offset = (uint32_t)ftell(fP); - - fread(&imgP->name, 1, 32, fP); - pm_readcharu(fP, &imgP->version); - pm_readcharu(fP, &imgP->type); - fread(&imgP->reserved1, 1, 4, fP); - fread(&imgP->note, 1, 4, fP); - pm_readbigshortu(fP, &imgP->x_last); - pm_readbigshortu(fP, &imgP->y_last); - fread(&imgP->reserved2, 1, 4, fP); - pm_readbigshortu(fP, &imgP->x_anchor); - pm_readbigshortu(fP, &imgP->y_anchor); - pm_readbigshortu(fP, &imgP->width); - pm_readbigshortu(fP, &imgP->height); - - image_read_data(imgP, end_offset, fP); - } - return 0; -} - - - -static int -text_read(TEXT * const textP, - FILE * const fP) { - - int retval; - char * s; - char buf[128]; - int used, alloced, len; - - if (textP == NULL) - return 0; - - textP->r->offset = (uint32_t)ftell(fP); - - /* - * What a pain in the ass! Why the hell isn't there a length - * attached to the text record? I suppose the designer wasn't - * concerned about non-seekable (i.e. pipes) input streams. - * Perhaps I'm being a little harsh, the lack of a length probably - * isn't much of an issue on the Pilot. - */ - used = 0; - alloced = 0; - s = NULL; - retval = 0; /* initial value */ - while ((len = fread(buf, 1, sizeof(buf), fP)) != 0 && retval == 0) { - if (buf[len - 1] == '\0') - --len; - if (used + len > alloced) { - alloced += 2 * sizeof(buf); - REALLOCARRAY(s, alloced); - - if (s == NULL) - retval = ENOMEM; - } - if (retval == 0) { - memcpy(s + used, buf, len); - used += len; - } - } - if (retval == 0) { - textP->data = calloc(1, used + 1); - if (textP->data == NULL) - retval = ENOMEM; - else - memcpy(textP->data, s, used); - } - if (s) - free(s); - - return retval; -} - - - -static int -pdbhead_read(PDBHEAD * const pdbHeadP, - FILE * const fP) { - - int retval; - - fread(pdbHeadP->name, 1, 32, fP); - pm_readbigshortu(fP, &pdbHeadP->flags); - pm_readbigshortu(fP, &pdbHeadP->version); - pm_readbiglongu2(fP, &pdbHeadP->ctime); - pm_readbiglongu2(fP, &pdbHeadP->mtime); - pm_readbiglongu2(fP, &pdbHeadP->btime); - pm_readbiglongu2(fP, &pdbHeadP->mod_num); - pm_readbiglongu2(fP, &pdbHeadP->app_info); - pm_readbiglongu2(fP, &pdbHeadP->sort_info); - fread(pdbHeadP->type, 1, 4, fP); - fread(pdbHeadP->id, 1, 4, fP); - pm_readbiglongu2(fP, &pdbHeadP->uniq_seed); - pm_readbiglongu2(fP, &pdbHeadP->next_rec); - pm_readbigshortu(fP, &pdbHeadP->num_recs); - - if (!memeq(pdbHeadP->type, IPDB_vIMG, 4) - || !memeq(pdbHeadP->id, IPDB_View, 4)) - retval = E_NOTIMAGE; - else - retval = 0; - - return retval; -} - - - -static int -rechdr_read(RECHDR * const rechdrP, - FILE * const fP) { - - int retval; - off_t len; - - pm_readbiglongu2(fP, &rechdrP->offset); - - len = (off_t)rechdrP->offset - ftell(fP); - switch(len) { - case 4: - case 12: - /* - * Version zero (eight bytes of record header) or version - * two with a note (two chunks of eight record header bytes). - */ - fread(&rechdrP->unknown[0], 1, 3, fP); - fread(&rechdrP->rec_type, 1, 1, fP); - rechdrP->n_extra = 0; - rechdrP->extra = NULL; - retval = 0; - break; - case 6: - /* - * Version one (ten bytes of record header). - */ - fread(&rechdrP->unknown[0], 1, 3, fP); - fread(&rechdrP->rec_type, 1, 1, fP); - rechdrP->n_extra = 2; - MALLOCARRAY(rechdrP->extra, rechdrP->n_extra); - if (rechdrP->extra == NULL) - retval = ENOMEM; - else { - fread(rechdrP->extra, 1, rechdrP->n_extra, fP); - retval = 0; - } - break; - default: - /* - * hmmm.... I'll assume this is the record header - * for a text record. - */ - fread(&rechdrP->unknown[0], 1, 3, fP); - fread(&rechdrP->rec_type, 1, 1, fP); - rechdrP->n_extra = 0; - rechdrP->extra = NULL; - retval = 0; - break; - } - if (retval == 0) { - if ((rechdrP->rec_type != IMG_REC && rechdrP->rec_type != TEXT_REC) - || !memeq(rechdrP->unknown, IPDB_MYST, 3)) - retval = E_NOTRECHDR; - } - return retval; -} - - - -int -ipdb_read(IPDB * const pdbP, - FILE * const fP) { - - int retval; - - ipdb_clear(pdbP); - - pdbP->p = pdbhead_alloc(NULL); - - if (pdbP->p == NULL) - retval = ENOMEM; - else { - int status; - - status = pdbhead_read(pdbP->p, fP); - - if (status != 0) - retval = status; - else { - pdbP->i = image_alloc(pdbP->p->name, IMG_GRAY, 0, 0); - if (pdbP->i == NULL) - retval = ENOMEM; - else { - int status; - status = rechdr_read(pdbP->i->r, fP); - if (status != 0) - retval = status; - else { - if (pdbP->p->num_recs > 1) { - pdbP->t = text_alloc(NULL); - if (pdbP->t == NULL) - retval = ENOMEM; - else { - int status; - status = rechdr_read(pdbP->t->r, fP); - if (status != 0) - retval = status; - else - retval = 0; - } - } else - retval = 0; - - if (retval == 0) { - uint32_t const offset = - pdbP->t == NULL ? - UNKNOWN_OFFSET : pdbP->t->r->offset - 1; - - int status; - - status = image_read(pdbP->i, offset, fP); - if (status != 0) - retval = status; - else { - if (pdbP->t != NULL) { - int status; - - status = text_read(pdbP->t, fP); - if (status != 0) - retval = status; - } - } - } - } - } - } - } - return retval; -} - - - -static const uint8_t * -g16unpack(const uint8_t * const p, - uint8_t * const g, - int const w) { - - static const uint8_t pal[] = - {0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88, - 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11, 0x00}; - const uint8_t * seg; - unsigned int i; - - for (i = 0, seg = p; i < w; i += 2, ++seg) { - g[i + 0] = pal[getg16pixel(*seg, 0)]; - g[i + 1] = pal[getg16pixel(*seg, 1)]; - } - return g; -} - - - -static const uint8_t * -gunpack(const uint8_t * const p, - uint8_t * const g, - int const w) { - - static const uint8_t pal[] = {0xff, 0xaa, 0x55, 0x00}; - const uint8_t * seg; - unsigned int i; - - for (i = 0, seg = p; i < w; i += 4, ++seg) { - g[i + 0] = pal[getgpixel(*seg, 0)]; - g[i + 1] = pal[getgpixel(*seg, 1)]; - g[i + 2] = pal[getgpixel(*seg, 2)]; - g[i + 3] = pal[getgpixel(*seg, 3)]; - } - return g; -} - - - -static const uint8_t * -munpack(const uint8_t * const p, - uint8_t * const b, - int const w) { - - static const uint8_t pal[] = {0x00, 0x01}; - const uint8_t * seg; - unsigned int i; - - for (i = 0, seg = p; i < w; i += 8, ++seg) { - b[i + 0] = pal[getmpixel(*seg, 0)]; - b[i + 1] = pal[getmpixel(*seg, 1)]; - b[i + 2] = pal[getmpixel(*seg, 2)]; - b[i + 3] = pal[getmpixel(*seg, 3)]; - b[i + 4] = pal[getmpixel(*seg, 4)]; - b[i + 5] = pal[getmpixel(*seg, 5)]; - b[i + 6] = pal[getmpixel(*seg, 6)]; - b[i + 7] = pal[getmpixel(*seg, 7)]; - } - return b; -} - - - -const uint8_t * -ipdb_g16row(IPDB * const pdbP, - unsigned int const row, - uint8_t * const buffer) { - - return g16unpack(img_row(pdbP->i, row), buffer, ipdb_width(pdbP)); -} - - - -const uint8_t * -ipdb_grow(IPDB * const pdbP, - unsigned int const row, - uint8_t * const buffer) { - - return gunpack(img_row(pdbP->i, row), buffer, ipdb_width(pdbP)); -} - - - -const uint8_t * -ipdb_mrow(IPDB * const pdbP, - unsigned int const row, - uint8_t * const buffer) { - - return munpack(img_row(pdbP->i, row), buffer, ipdb_width(pdbP)); -} - - - -int -ipdb_remove_image(IPDB * const pdbP) { - - int retval; - /* - * There's no point in fiddling with pdbP->t->r->offset here since we - * never know what it really should be until write-time anyway. - */ - - if (pdbP->i == NULL) - retval = 0; - else { - image_free(pdbP->i); - --pdbP->p->num_recs; - retval = 0; - } - return retval; -} - - - -int -ipdb_remove_text(IPDB * const pdbP) { - - int retval; - - if (pdbP->t == NULL) - retval = 0; - else { - text_free(pdbP->t); - if (pdbP->i) - pdbP->i->r->offset -= 8; - --pdbP->p->num_recs; - retval = 0; - } - return retval; -} - - - -static int -pdbhead_write(PDBHEAD * const pdbheadP, - FILE * const fP) { - - fwrite(pdbheadP->name, 1, 32, fP); - pm_writebigshort(fP, pdbheadP->flags); - pm_writebigshort(fP, pdbheadP->version); - pm_writebiglong(fP, pdbheadP->ctime); - pm_writebiglong(fP, pdbheadP->mtime); - pm_writebiglong(fP, pdbheadP->btime); - pm_writebiglong(fP, pdbheadP->mod_num); - pm_writebiglong(fP, pdbheadP->app_info); - pm_writebiglong(fP, pdbheadP->sort_info); - fwrite(pdbheadP->type, 1, 4, fP); - fwrite(pdbheadP->id, 1, 4, fP); - pm_writebiglong(fP, pdbheadP->uniq_seed); - pm_writebiglong(fP, pdbheadP->next_rec); - pm_writebigshort(fP, pdbheadP->num_recs); - - return 0; -} - - - -static int -rechdr_write(RECHDR * const rechdrP, - FILE * const fP) { - - if (rechdrP) { - pm_writebiglong(fP, rechdrP->offset); - fwrite(rechdrP->unknown, 1, 3, fP); - fwrite(&rechdrP->rec_type, 1, 1, fP); - - if (rechdrP->n_extra != 0) - fwrite(rechdrP->extra, 1, rechdrP->n_extra, fP); - } - return 0; -} - - - -static int -image_write(IMAGE * const imgP, - uint8_t * const data, - size_t const n, - FILE * const fP) { - - fwrite(imgP->name, 1, 32, fP); - fwrite(&imgP->version, 1, 1, fP); - fwrite(&imgP->type, 1, 1, fP); - fwrite(imgP->reserved1, 1, 4, fP); - fwrite(imgP->note, 1, 4, fP); - pm_writebigshort(fP, imgP->x_last); - pm_writebigshort(fP, imgP->y_last); - fwrite(imgP->reserved2, 1, 2, fP); - pm_writebigshort(fP, imgP->x_anchor); - pm_writebigshort(fP, imgP->y_anchor); - pm_writebigshort(fP, imgP->width); - pm_writebigshort(fP, imgP->height); - fwrite(data, 1, n, fP); - - return 0; -} - - - -static int -text_write(TEXT * const textP, - FILE * const fP) { - - if (textP) - fwrite(textP->data, 1, strlen(textP->data), fP); - - return 0; -} - - - -typedef struct { - unsigned int match; - uint8_t buf[128]; - int mode; - size_t len; - size_t used; - uint8_t * p; -} RLE; -#define MODE_MATCH 0 -#define MODE_LIT 1 -#define MODE_NONE 2 - -#define reset(r) { \ - (r)->match = 0xffff; \ - (r)->mode = MODE_NONE; \ - (r)->len = 0; \ - } - - - -static void -put_match(RLE * const rleP, - size_t const n) { - - *rleP->p++ = 0x80 + n - 1; - *rleP->p++ = rleP->match; - rleP->used += 2; - reset(rleP); -} - - - -static void -put_lit(RLE * const rleP, - size_t const n) { - - *rleP->p++ = n - 1; - rleP->p = (uint8_t *)memcpy(rleP->p, rleP->buf, n) + n; - rleP->used += n + 1; - reset(rleP); -} - - - -static size_t -compress(const uint8_t * const inData, - size_t const n_in, - uint8_t * const out) { - - static void (*put[])(RLE *, size_t) = {put_match, put_lit}; - RLE rle; - size_t i; - const uint8_t * p; - - MEMSZERO(&rle); - rle.p = out; - reset(&rle); - - for (i = 0, p = &inData[0]; i < n_in; ++i, ++p) { - if (*p == rle.match) { - if (rle.mode == MODE_LIT && rle.len > 1) { - put_lit(&rle, rle.len - 1); - ++rle.len; - rle.match = *p; - } - rle.mode = MODE_MATCH; - ++rle.len; - } else { - if (rle.mode == MODE_MATCH) - put_match(&rle, rle.len); - rle.mode = MODE_LIT; - rle.match = *p; - rle.buf[rle.len++] = *p; - } - if (rle.len == 128) - put[rle.mode](&rle, rle.len); - } - if (rle.len != 0) - put[rle.mode](&rle, rle.len); - - return rle.used; -} - - - -int -ipdb_write(IPDB * const pdbP, - int const comp, - FILE * const fP) { - - int retval; - - if (pdbP->i == NULL) - retval = E_IMAGENOTTHERE; - else { - RECHDR * const trP = pdbP->t == NULL ? NULL : pdbP->t->r; - RECHDR * const irP= pdbP->i->r; - - int n; - uint8_t * data; - - n = img_size(pdbP->i); /* initial value */ - data = pdbP->i->data; /* initial value */ - - if (comp == IPDB_NOCOMPRESS) - retval = 0; - else { - /* Allocate for the worst case. */ - int const allocSz = (3*n + 2)/2; - - MALLOCARRAY(data, allocSz); - - if (data == NULL) - retval = ENOMEM; - else { - int sz; - sz = compress(pdbP->i->data, n, data); - if (comp == IPDB_COMPMAYBE && sz >= n) { - free(data); - data = pdbP->i->data; - } else { - pdbP->i->compressed = TRUE; - if (pdbP->i->type == IMG_GRAY16) - pdbP->i->version = 9; - else - pdbP->i->version = 1; - if (pdbP->t != NULL) - pdbP->t->r->offset -= n - sz; - n = sz; - } - retval = 0; - } - - if (retval == 0) { - retval = pdbhead_write(pdbP->p, fP); - if (retval == 0) { - retval = rechdr_write(irP, fP); - if (retval == 0) { - retval = rechdr_write(trP, fP); - if (retval == 0) { - retval = image_write(pdbP->i, data, n, fP); - if (retval == 0) { - retval = text_write(pdbP->t, fP); - } - } - } - } - if (data != pdbP->i->data) - free(data); - } - } - } - return retval; -} - - - -static int -g16pack(const uint8_t * const inData, - uint8_t * const p, - int const w) { - - int off, i; - uint8_t * seg; - const uint8_t * g; - - for(i = off = 0, seg = p, g=inData; i < w; ++i, ++g) { - switch(*g) { - case 0xff: setg16pixel(*seg, 0x00, off); break; - case 0xee: setg16pixel(*seg, 0x01, off); break; - case 0xdd: setg16pixel(*seg, 0x02, off); break; - case 0xcc: setg16pixel(*seg, 0x03, off); break; - case 0xbb: setg16pixel(*seg, 0x04, off); break; - case 0xaa: setg16pixel(*seg, 0x05, off); break; - case 0x99: setg16pixel(*seg, 0x06, off); break; - case 0x88: setg16pixel(*seg, 0x07, off); break; - case 0x77: setg16pixel(*seg, 0x08, off); break; - case 0x66: setg16pixel(*seg, 0x09, off); break; - case 0x55: setg16pixel(*seg, 0x0a, off); break; - case 0x44: setg16pixel(*seg, 0x0b, off); break; - case 0x33: setg16pixel(*seg, 0x0c, off); break; - case 0x22: setg16pixel(*seg, 0x0d, off); break; - case 0x11: setg16pixel(*seg, 0x0e, off); break; - case 0x00: setg16pixel(*seg, 0x0f, off); break; - default: return E_BADCOLORS; - } - if (++off == 2) { - ++seg; - off = 0; - } - } - return w/2; -} - - - -static int -gpack(const uint8_t * const inData, - uint8_t * const p, - int const w) { - - int off, i; - uint8_t * seg; - const uint8_t * g; - - for (i = off = 0, seg = p, g = inData; i < w; ++i, ++g) { - switch(*g) { - case 0xff: setgpixel(*seg, 0x00, off); break; - case 0xaa: setgpixel(*seg, 0x01, off); break; - case 0x55: setgpixel(*seg, 0x02, off); break; - case 0x00: setgpixel(*seg, 0x03, off); break; - default: return E_BADCOLORS; - } - if (++off == 4) { - ++seg; - off = 0; - } - } - return w/4; -} - - - -static int -mpack(const uint8_t * const inData, - uint8_t * const p, - int const w) { - - int off, i; - uint8_t * seg; - const uint8_t * b; - - for (i = off = 0, seg = p, b = inData; i < w; ++i, ++b) { - setmpixel(*seg, *b == 0, off); - if (++off == 8) { - ++seg; - off = 0; - } - } - return w/8; -} - - - -static int -adjust_dims(unsigned int const w, - unsigned int const h, - unsigned int * const awP, - unsigned int * const ahP) { - - unsigned int provW, provH; - - provW = w; - provH = h; - if (provW % 16 != 0) - provW += 16 - (provW % 16); - if (provW < 160) - provW = 160; - if (provH < 160) - provH = 160; - - *awP = provW; - *ahP = provH; - - return w == provW && h == provH; -} - - - -/* - * You can allocate only 64k chunks of memory on the pilot and that - * supplies an image size limit. - */ -#define MAX_SIZE(t) ((1 << 16)*((t) == IMG_GRAY ? 4 : 8)) -#define MAX_ERROR(t) ((t) == IMG_GRAY ? E_TOOBIGG : E_TOOBIGM) - -static int -image_insert_init(IPDB * const pdbP, - int const uw, - int const uh, - int const type) { - - char * const name = pdbP->p->name; - unsigned int w, h; - int retval; - - if (pdbP->p->num_recs != 0) - retval = E_IMAGETHERE; - else { - adjust_dims(uw, uh, &w, &h); - if (w*h > MAX_SIZE(type)) - retval = MAX_ERROR(type); - else { - pdbP->i = image_alloc(name, type, w, h); - if(pdbP->i == NULL) - retval = ENOMEM; - else { - pdbP->p->num_recs = 1; - - retval =0; - } - } - } - return retval; -} - - - -int -ipdb_insert_g16image(IPDB * const pdbP, - int const w, - int const h, - const uint8_t * const gArg) { - - int retval; - int i; - - i = image_insert_init(pdbP, w, h, IMG_GRAY16); - if (i != 0) - retval = i; - else { - int const incr = ipdb_width(pdbP)/2; - unsigned int i; - uint8_t * p; - const uint8_t * g; - - for (i = 0, p = pdbP->i->data, g = gArg, retval = 0; - i < h && retval == 0; - ++i, p += incr, g += w) { - - int const len = g16pack(g, p, w); - if (len < 0) - retval = len; - } - } - return retval; -} - - - -int -ipdb_insert_gimage(IPDB * const pdbP, - int const w, - int const h, - const uint8_t * const gArg) { - - int i; - int retval; - - i = image_insert_init(pdbP, w, h, IMG_GRAY); - - if (i != 0) - retval = i; - else { - int const incr = ipdb_width(pdbP)/4; - unsigned int i; - uint8_t * p; - const uint8_t * g; - - for (i = 0, p = pdbP->i->data, g = gArg, retval = 0; - i < h && retval == 0; - ++i, p += incr, g += w) { - - int const len = gpack(g, p, w); - if (len < 0) - retval = len; - } - } - return retval; -} - - - -int -ipdb_insert_mimage(IPDB * const pdbP, - int const w, - int const h, - const uint8_t * const bArg) { - - int retval; - int i; - - i = image_insert_init(pdbP, w, h, IMG_MONO); - if (i != 0) - retval = i; - else { - int const incr = ipdb_width(pdbP)/8; - unsigned int i; - uint8_t * p; - const uint8_t * b; - - for (i = 0, p = pdbP->i->data, b = bArg, retval = 0; - i < h && retval == 0; - ++i, p += incr, b += w) { - - int const len = mpack(b, p, w); - if (len < 0) - retval = len; - } - } - return retval; -} - - - -int -ipdb_insert_text(IPDB * const pdbP, - const char * const s) { - - int retval; - - if (pdbP->i == NULL) - retval = E_IMAGENOTTHERE; - else if (pdbP->p->num_recs == 2) - retval = E_TEXTTHERE; - else { - pdbP->t = text_alloc(s); - if (pdbP->t == NULL) - retval = ENOMEM; - else { - pdbP->p->num_recs = 2; - - pdbP->i->r->offset += 8; - pdbP->t->r->offset = - pdbP->i->r->offset + IMAGESIZE + img_size(pdbP->i); +const char * +ipdb_typeName(uint8_t const type) { - retval = 0; - } + switch (type) { + case IMG_GRAY16: return "16 Bit Grayscale"; break; + case IMG_GRAY: return "Grayscale"; break; + case IMG_MONO: return "Monochrome"; break; + default: return "???"; } - return retval; } diff --git a/converter/other/ipdb.h b/converter/other/ipdb.h index d00fde1a..6af5fc44 100644 --- a/converter/other/ipdb.h +++ b/converter/other/ipdb.h @@ -127,6 +127,7 @@ typedef struct { */ uint8_t * data; } IMAGE; + #define IMAGESIZE (32 + 1 + 1 + 4 + 4 + 2*2 + 4 + 2*2 + 2*2) /* @@ -136,6 +137,10 @@ typedef struct { #define IMG_GRAY ((uint8_t)0) #define IMG_MONO ((uint8_t)0xff) +const char * +ipdb_typeName(uint8_t const type); + + /* * Compression constants for IMAGE.version. */ @@ -159,6 +164,16 @@ typedef struct { TEXT * t; } IPDB; +/* + * Only use four bytes of these. + */ +#define IPDB_vIMG "vIMG" +#define IPDB_View "View" +/* + * Only use three bytes of this. + */ +#define IPDB_MYST "\x40\x6f\x80" + /* * Flags for ipdb_write(). */ @@ -185,43 +200,44 @@ typedef struct { const char * ipdb_err(int error); -int -ipdb_read(IPDB *, FILE *); - -int -ipdb_write(IPDB *, int, FILE *); +size_t +ipdb_img_size(IMAGE * const imgP); -int -ipdb_insert_g16image(IPDB *, int, int, const uint8_t *); +unsigned int +ipdb_img_ppb(IMAGE * const imgP); -int -ipdb_insert_gimage(IPDB *, int, int, const uint8_t*); +uint8_t * +ipdb_img_row(IMAGE * const imgP, + unsigned int const row); -int -ipdb_insert_mimage(IPDB *, int, int, const uint8_t *); +void +ipdb_free(IPDB *); -int -ipdb_insert_text(IPDB *, const char *); +IPDB * +ipdb_alloc(const char *); -int -ipdb_remove_image(IPDB *); +void +ipdb_clear(IPDB * const pdbP); -int -ipdb_remove_text(IPDB *); +PDBHEAD * +ipdb_pdbhead_alloc(const char * const name); -const uint8_t * -ipdb_g16row(IPDB *, unsigned int, uint8_t *); +void +ipdb_pdbhead_free(PDBHEAD * const headP); -const uint8_t * -ipdb_grow(IPDB *, unsigned int, uint8_t *); +IMAGE * +ipdb_image_alloc(const char * const name, + int const type, + int const w, + int const h); -const uint8_t * -ipdb_mrow(IPDB *, unsigned int, uint8_t *); +void +ipdb_image_free(IMAGE * const imgP); void -ipdb_free(IPDB *); +ipdb_text_free(TEXT * const textP); -IPDB * -ipdb_alloc(const char *); +TEXT * +ipdb_text_alloc(const char * const content); #endif diff --git a/converter/other/pamtopdbimg.c b/converter/other/pamtopdbimg.c index dcd03450..53868aa8 100644 --- a/converter/other/pamtopdbimg.c +++ b/converter/other/pamtopdbimg.c @@ -30,6 +30,7 @@ */ #include +#include #include #include @@ -131,51 +132,595 @@ parseCommandLine(int argc, const char ** argv, +/* + * Pixel setting macros. + */ +#define setg16pixel(b,v,o) ((b) |= ((v) << (4 - 4*(o)))) +#define setgpixel(b,v,o) ((b) |= ((v) << (6 - 2*(o)))) +#define setmpixelblack(b,o) ((b) |= (1 << (7 - (o)))) + + + +static int +pdbheadWrite(PDBHEAD * const pdbheadP, + FILE * const fileP) { + + fwrite(pdbheadP->name, 1, 32, fileP); + pm_writebigshort(fileP, pdbheadP->flags); + pm_writebigshort(fileP, pdbheadP->version); + pm_writebiglong(fileP, pdbheadP->ctime); + pm_writebiglong(fileP, pdbheadP->mtime); + pm_writebiglong(fileP, pdbheadP->btime); + pm_writebiglong(fileP, pdbheadP->mod_num); + pm_writebiglong(fileP, pdbheadP->app_info); + pm_writebiglong(fileP, pdbheadP->sort_info); + fwrite(pdbheadP->type, 1, 4, fileP); + fwrite(pdbheadP->id, 1, 4, fileP); + pm_writebiglong(fileP, pdbheadP->uniq_seed); + pm_writebiglong(fileP, pdbheadP->next_rec); + pm_writebigshort(fileP, pdbheadP->num_recs); + + return 0; +} + + + +static int +rechdrWrite(RECHDR * const rechdrP, + FILE * const fileP) { + + if (rechdrP) { + pm_writebiglong(fileP, rechdrP->offset); + fwrite(rechdrP->unknown, 1, 3, fileP); + fwrite(&rechdrP->rec_type, 1, 1, fileP); + + if (rechdrP->n_extra != 0) + fwrite(rechdrP->extra, 1, rechdrP->n_extra, fileP); + } + return 0; +} + + + static void -readimg(IPDB * const pdbP, - FILE * const ifP, - bool const depth4) { +imageWriteHeader(IMAGE * const imgP, + FILE * const fileP) { + + fwrite(imgP->name, 1, 32, fileP); + fwrite(&imgP->version, 1, 1, fileP); + fwrite(&imgP->type, 1, 1, fileP); + fwrite(imgP->reserved1, 1, 4, fileP); + fwrite(imgP->note, 1, 4, fileP); + pm_writebigshort(fileP, imgP->x_last); + pm_writebigshort(fileP, imgP->y_last); + fwrite(imgP->reserved2, 1, 4, fileP); + pm_writebigshort(fileP, imgP->x_anchor); + pm_writebigshort(fileP, imgP->y_anchor); + pm_writebigshort(fileP, imgP->width); + pm_writebigshort(fileP, imgP->height); +} - struct pam inpam; - tuple * tuplerow; - int status; - uint8_t * imgRaster; - unsigned int row; - pnm_readpaminit(ifP, &inpam, PAM_STRUCT_SIZE(tuple_type)); - if (strneq(inpam.tuple_type, "RGB", 3)) - pm_error("Input image is color. Cannot make a Palm color image."); +static void +imageWriteData(IMAGE * const imgP, + const uint8_t * const data, + size_t const dataSize, + FILE * const fileP) { - MALLOCARRAY(imgRaster, inpam.width * inpam.height); + fwrite(data, 1, dataSize, fileP); +} - tuplerow = pnm_allocpamrow(&inpam); - for (row = 0; row < inpam.height; ++row) { - unsigned int col; - pnm_readpamrow(&inpam, tuplerow); +static void +imageWrite(IMAGE * const imgP, + uint8_t * const data, + size_t const dataSize, + FILE * const fileP) { - for (col = 0; col < inpam.width; ++col) - imgRaster[row * inpam.width + col] = tuplerow[col][0]; + imageWriteHeader(imgP, fileP); + + imageWriteData(imgP, data, dataSize, fileP); +} + + + +static int +textWrite(TEXT * const textP, + FILE * const fileP) { + + if (textP) + fwrite(textP->data, 1, strlen(textP->data), fileP); + + return 0; +} + + + +typedef struct { + unsigned int match; + uint8_t buf[128]; + int mode; + size_t len; + size_t used; + uint8_t * p; +} RLE; +#define MODE_MATCH 0 +#define MODE_LIT 1 +#define MODE_NONE 2 + +#define reset(r) { \ + (r)->match = 0xffff; \ + (r)->mode = MODE_NONE; \ + (r)->len = 0; \ } + + +static void +putMatch(RLE * const rleP, + size_t const n) { + + *rleP->p++ = 0x80 + n - 1; + *rleP->p++ = rleP->match; + rleP->used += 2; + reset(rleP); +} + + + +static void +putLit(RLE * const rleP, + size_t const n) { + + *rleP->p++ = n - 1; + rleP->p = (uint8_t *)memcpy(rleP->p, rleP->buf, n) + n; + rleP->used += n + 1; + reset(rleP); +} + + + +static size_t +compress(const uint8_t * const inData, + size_t const n_in, + uint8_t * const out) { + + static void (*put[])(RLE *, size_t) = {putMatch, putLit}; + RLE rle; + size_t i; + const uint8_t * p; + + MEMSZERO(&rle); + rle.p = out; + reset(&rle); + + for (i = 0, p = &inData[0]; i < n_in; ++i, ++p) { + if (*p == rle.match) { + if (rle.mode == MODE_LIT && rle.len > 1) { + putLit(&rle, rle.len - 1); + ++rle.len; + rle.match = *p; + } + rle.mode = MODE_MATCH; + ++rle.len; + } else { + if (rle.mode == MODE_MATCH) + putMatch(&rle, rle.len); + rle.mode = MODE_LIT; + rle.match = *p; + rle.buf[rle.len++] = *p; + } + if (rle.len == 128) + put[rle.mode](&rle, rle.len); + } + if (rle.len != 0) + put[rle.mode](&rle, rle.len); + + return rle.used; +} + + + +static void +compressIfRequired(IPDB * const pdbP, + int const comp, + uint8_t ** const compressedDataP, + size_t * const compressedSizeP) { + + if (comp == IPDB_NOCOMPRESS) { + *compressedDataP = pdbP->i->data; + *compressedSizeP = ipdb_img_size(pdbP->i); + } else { + int const uncompressedSz = ipdb_img_size(pdbP->i); + + /* Allocate for the worst case. */ + size_t const allocSz = (3 * uncompressedSz + 2)/2; + + uint8_t * data; + + data = pdbP->i->data; + + MALLOCARRAY(data, allocSz); + + if (data == NULL) + pm_error("Could not get %lu bytes of memory to decompress", + (unsigned long)allocSz); + else { + size_t compressedSz; + compressedSz = compress(pdbP->i->data, uncompressedSz, data); + if (comp == IPDB_COMPMAYBE && compressedSz >= uncompressedSz) { + /* Return the uncompressed data */ + free(data); + *compressedDataP = pdbP->i->data; + *compressedSizeP = uncompressedSz; + } else { + pdbP->i->compressed = TRUE; + if (pdbP->i->type == IMG_GRAY16) + pdbP->i->version = 9; + else + pdbP->i->version = 1; + if (pdbP->t != NULL) + pdbP->t->r->offset -= uncompressedSz - compressedSz; + *compressedDataP = data; + *compressedSizeP = compressedSz; + } + } + } +} + + + +static void +ipdbWrite(IPDB * const pdbP, + int const comp, + FILE * const fileP) { + + RECHDR * const trP = pdbP->t == NULL ? NULL : pdbP->t->r; + RECHDR * const irP = pdbP->i->r; + + int rc; + uint8_t * compressedData; + /* This is the image raster, compressed as required. + (I.e. if it doesn't have to be compressed, it isn't). + */ + size_t compressedSize; + + assert(pdbP->i); + + compressIfRequired(pdbP, comp, &compressedData, &compressedSize); + + rc = pdbheadWrite(pdbP->p, fileP); + if (rc != 0) + pm_error("Failed to write PDB header. %s", ipdb_err(rc)); + + rc = rechdrWrite(irP, fileP); + if (rc != 0) + pm_error("Failed to write image record header. %s", ipdb_err(rc)); + + rc = rechdrWrite(trP, fileP); + if (rc != 0) + pm_error("Failed to write text record header. %s", ipdb_err(rc)); + + imageWrite(pdbP->i, compressedData, compressedSize, fileP); + + rc = textWrite(pdbP->t, fileP); + if (rc != 0) + pm_error("Failed to write text. %s", ipdb_err(rc)); + + /* Oh, gross. compressIfRequired() might have returned a pointer to + storage that was already allocated, or it might have returned a + pointer to newly malloc'ed storage. In the latter case, we have + to free the storage. + */ + if (compressedData != pdbP->i->data) + free(compressedData); +} + + + +static void +g16pack(tuple * const tupleRow, + struct pam * const pamP, + uint8_t * const outData, + unsigned int const paddedWidth) { +/*---------------------------------------------------------------------------- + Pack a row of 16-level graysacle pixels 'tupleRow', described by *pamP into + 'outData', padding it to 'paddedWidth' with white. + + We pack 2 input pixels into one output byte. +-----------------------------------------------------------------------------*/ + unsigned int col; + unsigned int off; + uint8_t * seg; + + for (col = 0, off = 0, seg = &outData[0]; col < paddedWidth; ++col) { + if (col < pamP->width) + setg16pixel(*seg, 16 - tupleRow[col][0] * 16 / pamP->maxval, off); + else + /* Pad on the right with white */ + setgpixel(*seg, 0, off); + + if (++off == 2) { + ++seg; + off = 0; + } + } +} + + + +static void +gpack(tuple * const tupleRow, + struct pam * const pamP, + uint8_t * const outData, + unsigned int const paddedWidth) { +/*---------------------------------------------------------------------------- + Pack a row of 4-level graysacle pixels 'tupleRow', described by *pamP into + 'outData', padding it to 'paddedWidth' with white. + + We pack 4 input pixels into one output byte. +-----------------------------------------------------------------------------*/ + unsigned int col; + unsigned int off; + uint8_t * seg; + + for (col = 0, off = 0, seg = &outData[0]; col < paddedWidth; ++col) { + if (col < pamP->width) + setgpixel(*seg, 4 - tupleRow[col][0] * 4 / pamP->maxval, off); + else + /* Pad on the right with white */ + setgpixel(*seg, 0, off); + + if (++off == 4) { + ++seg; + off = 0; + } + } +} + + + +static void +mpack(tuple * const tupleRow, + struct pam * const pamP, + uint8_t * const outData, + unsigned int const paddedWidth) { +/*---------------------------------------------------------------------------- + Pack a row of monochrome pixels 'tupleRow', described by *pamP into + 'outData', padding it to 'paddedWidth' with white. + + We pack 8 input pixels into one output byte. +-----------------------------------------------------------------------------*/ + unsigned int col; + unsigned int off; + uint8_t * seg; + + assert(paddedWidth % 8 == 0); + + /* Initialize row to white, then set necessary pixels black */ + memset(outData, 0, paddedWidth/8); + + for (col = 0, off = 0, seg = &outData[0]; col < paddedWidth; ++col) { + if (col < pamP->width && tupleRow[col][0] == PAM_BLACK) + setmpixelblack(*seg, off); + if (++off == 8) { + ++seg; + off = 0; + } + } +} + + + +static int +adjustDimensions(unsigned int const w, + unsigned int const h, + unsigned int * const awP, + unsigned int * const ahP) { + + unsigned int provW, provH; + + provW = w; + provH = h; + if (provW % 16 != 0) + provW += 16 - (provW % 16); + if (provW < 160) + provW = 160; + if (provH < 160) + provH = 160; + + *awP = provW; + *ahP = provH; + + return w == provW && h == provH; +} + + + +/* + * You can allocate only 64k chunks of memory on the pilot and that + * supplies an image size limit. + */ +#define MAX_SIZE(t) ((1 << 16)*((t) == IMG_GRAY ? 4 : 8)) + +static void +imageInsertInit(IPDB * const pdbP, + int const uw, + int const uh, + int const type) { + + char * const name = pdbP->p->name; + unsigned int adjustedWidth, adjustedHeight; + + if (pdbP->p->num_recs != 0) + pm_error("Image record already present, logic error."); + else { + adjustDimensions(uw, uh, &adjustedWidth, &adjustedHeight); + pm_message("Output dimensions: %uw x %uh", + adjustedWidth, adjustedHeight); + if (adjustedWidth * adjustedHeight > MAX_SIZE(type)) + pm_error("Image too large. Maximum number of pixels allowed " + "for a %s image is %u", + ipdb_typeName(type), MAX_SIZE(type)); + else { + pdbP->i = + ipdb_image_alloc(name, type, adjustedWidth, adjustedHeight); + if (pdbP->i == NULL) + pm_message("Could not get memory for %u x %u image", + adjustedWidth, adjustedHeight); + else + pdbP->p->num_recs = 1; + } + } +} + + + +static void +insertG16image(IPDB * const pdbP, + struct pam * const pamP, + tuple ** const tuples) { +/*---------------------------------------------------------------------------- + Insert into the PDB an image in 16-level grayscale format. + + The pixels of the image to insert are 'tuples', described by *pamP. + Note that the image inserted may be padded up to larger dimensions. +-----------------------------------------------------------------------------*/ + imageInsertInit(pdbP, pamP->width, pamP->height, IMG_GRAY16); + { + int const rowSize = ipdb_width(pdbP)/2; + /* The size in bytes of a packed, padded row */ + + uint8_t * outP; + unsigned int row; + + for (row = 0, outP = &pdbP->i->data[0]; + row < pamP->height; + ++row, outP += rowSize) + g16pack(tuples[row], pamP, outP, ipdb_width(pdbP)); + + /* Pad with white on the bottom */ + for (; row < ipdb_height(pdbP); ++row) + memset(outP, 0, rowSize); + } +} + + + +static void +insertGimage(IPDB * const pdbP, + struct pam * const pamP, + tuple ** const tuples) { +/*---------------------------------------------------------------------------- + Insert into the PDB an image in 4-level grayscale format. + + The pixels of the image to insert are 'tuples', described by *pamP. + Note that the image inserted may be padded up to larger dimensions. +-----------------------------------------------------------------------------*/ + imageInsertInit(pdbP, pamP->width, pamP->height, IMG_GRAY); + { + int const rowSize = ipdb_width(pdbP)/4; + /* The size in bytes of a packed, padded row */ + + uint8_t * outP; + unsigned int row; + + for (row = 0, outP = &pdbP->i->data[0]; + row < pamP->height; + ++row, outP += rowSize) + gpack(tuples[row], pamP, outP, ipdb_width(pdbP)); + + /* Pad with white on the bottom */ + for (; row < ipdb_height(pdbP); ++row) + memset(outP, 0, rowSize); + } +} + + + +static void +insertMimage(IPDB * const pdbP, + struct pam * const pamP, + tuple ** const tuples) { +/*---------------------------------------------------------------------------- + Insert into the PDB an image in monochrome format. + + The pixels of the image to insert are 'tuples', described by *pamP. + Note that the image inserted may be padded up to larger dimensions. +-----------------------------------------------------------------------------*/ + imageInsertInit(pdbP, pamP->width, pamP->height, IMG_MONO); + { + int const rowSize = ipdb_width(pdbP)/8; + /* The size in bytes of a packed, padded row */ + + uint8_t * outP; + unsigned int row; + + for (row = 0, outP = &pdbP->i->data[0]; + row < pamP->height; + ++row, outP += rowSize) + mpack(tuples[row], pamP, outP, ipdb_width(pdbP)); + + /* Pad with white on the bottom */ + for (; row < ipdb_height(pdbP); ++row) + memset(outP, 0, rowSize); + } +} + + + +static int +insertText(IPDB * const pdbP, + const char * const s) { + + int retval; + + if (pdbP->i == NULL) + retval = E_IMAGENOTTHERE; + else if (pdbP->p->num_recs == 2) + retval = E_TEXTTHERE; + else { + pdbP->t = ipdb_text_alloc(s); + if (pdbP->t == NULL) + retval = ENOMEM; + else { + pdbP->p->num_recs = 2; + + pdbP->i->r->offset += 8; + pdbP->t->r->offset = + pdbP->i->r->offset + IMAGESIZE + ipdb_img_size(pdbP->i); + + retval = 0; + } + } + return retval; +} + + + +static void +readimg(IPDB * const pdbP, + FILE * const ifP, + bool const depth4) { + + struct pam inpam; + tuple ** tuples; + + tuples = pnm_readpam(ifP, &inpam, PAM_STRUCT_SIZE(tuple_type)); + + if (strneq(inpam.tuple_type, "RGB", 3)) + pm_error("Input image is color. Cannot make a Palm color image."); + if (inpam.maxval == 1) - status = ipdb_insert_mimage(pdbP, inpam.width, inpam.height, - imgRaster); + insertMimage(pdbP, &inpam, tuples); else if (depth4) - status = ipdb_insert_g16image(pdbP, inpam.width, inpam.height, - imgRaster); + insertG16image(pdbP, &inpam, tuples); else - status = ipdb_insert_gimage(pdbP, inpam.width, inpam.height, - imgRaster); + insertGimage(pdbP, &inpam, tuples); - if (status != 0) - pm_error("ipdb_insert failed. Error %d (%s)", - status, ipdb_err(status)); - - pnm_freepamrow(tuplerow); - free(imgRaster); + pnm_freepamarray(tuples, &inpam); } @@ -217,7 +762,7 @@ readtxt(IPDB * const pdbP, for (n = strlen(fileContent) - 1; n >= 0 && fileContent[n] == '\n'; --n) fileContent[n] = '\0'; - ipdb_insert_text(pdbP, fileContent); + insertText(pdbP, fileContent); } @@ -229,7 +774,6 @@ main(int argc, const char **argv) { IPDB * pdbP; FILE * ifP; int comp; - int status; pm_proginit(&argc, argv); @@ -253,10 +797,7 @@ main(int argc, const char **argv) { if (cmdline.notefile) readtxt(pdbP, cmdline.notefile); - status = ipdb_write(pdbP, comp, stdout); - - if (status != 0) - pm_error("Failed to write PDB. %s.", ipdb_err(status)); + ipdbWrite(pdbP, comp, stdout); if (comp == IPDB_COMPMAYBE && !ipdb_compressed(pdbP)) pm_message("Image too complex to be compressed."); diff --git a/converter/other/pdbimgtopam.c b/converter/other/pdbimgtopam.c new file mode 100644 index 00000000..6e33be0d --- /dev/null +++ b/converter/other/pdbimgtopam.c @@ -0,0 +1,774 @@ +/*============================================================================= + pamtopdbimg +=============================================================================== + + Convert Palm Pilot PDB Image format (for viewing by + Pilot Image Viewer) to Netpbm image. + + Bryan Henderson derived this from Eric Howe's program named + 'imgvtopnm', in September 2010. +=============================================================================*/ +/* + * Copyright (C) 1997 Eric A. Howe + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Authors: Eric A. Howe (mu@trends.net) + * Bryan Henderson + */ +#include +#include + +#include "pm_c_util.h" +#include "mallocvar.h" +#include "nstring.h" +#include "shhopt.h" +#include "pam.h" + +#include "ipdb.h" + + +struct cmdlineInfo { + /* All the information the user supplied in the command line, + in a form easy for the program to use. + */ + const char * inputFileName; /* '-' if stdin */ + const char * notefile; /* NULL if not specified */ + unsigned int verbose; +}; + + + +static void +parseCommandLine(int argc, const char ** argv, + struct cmdlineInfo * const cmdlineP) { +/*---------------------------------------------------------------------------- + parse program command line described in Unix standard form by argc + and argv. Return the information in the options as *cmdlineP. + + If command line is internally inconsistent (invalid options, etc.), + issue error message to stderr and abort program. + + Note that the strings we return are stored in the storage that + was passed to us as the argv array. We also trash *argv. +-----------------------------------------------------------------------------*/ + optEntry *option_def; + /* Instructions to optParseOptions3 on how to parse our options. + */ + optStruct3 opt; + + unsigned int option_def_index; + + unsigned int notefileSpec; + + MALLOCARRAY_NOFAIL(option_def, 100); + + option_def_index = 0; /* incremented by OPTENT3 */ + OPTENT3(0, "notefile", OPT_STRING, &cmdlineP->notefile, + ¬efileSpec, 0); + OPTENT3(0, "verbose", OPT_FLAG, NULL, + &cmdlineP->verbose, 0); + + opt.opt_table = option_def; + opt.short_allowed = false; /* We have no short (old-fashioned) options */ + opt.allowNegNum = false; /* We have no parms that are negative numbers */ + + optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0); + /* Uses and sets argc, argv, and some of *cmdlineP and others. */ + + if (!notefileSpec) + cmdlineP->notefile = NULL; + + if (argc-1 < 1) + cmdlineP->inputFileName = "-"; + else if (argc-1 == 1) + cmdlineP->inputFileName = argv[1]; + else + pm_error("Program takes at most one argument: input file name"); +} + + + +#define getg16pixel(b,o) (((b) >> (4 - 4*(o))) & 0x0f) +#define getgpixel(b,o) (((b) >> (6 - 2*(o))) & 0x03) +#define getmpixel(b,o) (((b) >> (7 - (o))) & 0x01) + + +static void +abortShort() { + pm_error("Invalid image. Compression algorithm runs out of " + "compressed data before generating the expected " + "amount of image data"); +} + + + +static void +abortOverrun() { + pm_error("Invalid image. Compression algorithm finds the end of " + "the image in the middle of a run"); +} + + + +static void +decompress(const uint8_t * const compressed, + size_t const compressedSize, + size_t const imageSize, + uint8_t ** const uncompressedP) { +/*---------------------------------------------------------------------------- + Decompress the data 'compressed', which is 'compressedSize' bytes long. + Return the decompressed data in newly malloced storage as + *decompressedP. Decompression should yield exactly 'imageSize' bytes. +-----------------------------------------------------------------------------*/ + /* + * The compression scheme used is a simple RLE; the control codes, + * CODE, are one byte and have the following meanings: + * + * CODE > 0x80 Insert (CODE + 1 - 0x80) copies of the next byte. + * CODE <= 0x80 Insert the next (CODE + 1) literal bytes. + * + * Compressed pieces can (and do) cross row boundaries. + */ + uint8_t * uncompressed; + + MALLOCARRAY(uncompressed, imageSize); + + if (uncompressed) { + const uint8_t * inP; + uint8_t * outP; + size_t bytesLeft; + + for (bytesLeft = imageSize, + inP = &compressed[0], outP = &uncompressed[0]; + bytesLeft > 0; + ) { + + int got, put; + + if (inP > compressed + compressedSize) + abortShort(); + + if (*inP > 0x80) { + put = *inP++ + 1 - 0x80; + if (outP + put > uncompressed + imageSize) + abortOverrun(); + memset(outP, *inP, put); + got = 1; + } else { + put = *inP++ + 1; + if (inP + put > compressed + compressedSize) + abortShort(); + if (outP + put > uncompressed + imageSize) + abortOverrun(); + memcpy(outP, inP, put); + got = put; + } + inP += got; + outP += put; + assert(bytesLeft >= put); + bytesLeft -= put; + } + } + *uncompressedP = uncompressed; +} + + + +#define UNKNOWN_OFFSET (uint32_t)-1 + +static void +readCompressed(IMAGE * const imgP, + uint32_t const end_offset, + FILE * const fP, + size_t * const dataSizeP, + uint8_t ** const dataP, + int * const retvalP) { +/*---------------------------------------------------------------------------- + Read the compressed data from file *fP (actually, if the image isn't + compressed, then it's just the regular data). + + Return the data in newly malloced storage as *dataP, which is + *dataSizeP bytes long. +-----------------------------------------------------------------------------*/ + int retval; + uint8_t * buffer; + size_t dataSize; + + dataSize = 0; /* initial value */ + + if (end_offset == UNKNOWN_OFFSET) { + /* + * Read until EOF. Some of them have an extra zero byte + * dangling off the end. I originally thought this was + * an empty note record (even though there was no record + * header for it); however, the release notes for Image + * Compression Manager 1.1 on http://www.pilotgear.com + * note this extra byte as a bug in Image Compression + * Manager 1.0 which 1.1 fixes. We'll just blindly read + * this extra byte and ignore it by paying attention to + * the image dimensions. + */ + MALLOCARRAY(buffer, ipdb_img_size(imgP)); + + if (buffer == NULL) + retval = ENOMEM; + else { + dataSize = fread(buffer, 1, ipdb_img_size(imgP), fP); + if (dataSize <= 0) + retval = EIO; + else + retval = 0; + + if (retval != 0) + free(buffer); + } + } else { + /* + * Read to the indicated offset. + */ + dataSize = end_offset - ftell(fP) + 1; + + MALLOCARRAY(buffer, dataSize); + + if (buffer == NULL) + retval = ENOMEM; + else { + ssize_t rc; + rc = fread(buffer, 1, dataSize, fP); + if (rc != dataSize) + retval = EIO; + else + retval = 0; + + if (retval != 0) + free(buffer); + } + } + *dataSizeP = dataSize; + *dataP = buffer; + *retvalP = retval; +} + + + +static void +imageReadHeader(FILE * const fileP, + IMAGE * const imgP, + bool const dump) { + + fread(&imgP->name, 1, 32, fileP); + pm_readcharu(fileP, &imgP->version); + pm_readcharu(fileP, &imgP->type); + fread(&imgP->reserved1, 1, 4, fileP); + fread(&imgP->note, 1, 4, fileP); + pm_readbigshortu(fileP, &imgP->x_last); + pm_readbigshortu(fileP, &imgP->y_last); + fread(&imgP->reserved2, 1, 4, fileP); + pm_readbigshortu(fileP, &imgP->x_anchor); + pm_readbigshortu(fileP, &imgP->y_anchor); + pm_readbigshortu(fileP, &imgP->width); + pm_readbigshortu(fileP, &imgP->height); + + if (dump) { + pm_message("PDB IMAGE header:"); + pm_message(" Name: '%.*s'", (int)sizeof(imgP->name), imgP->name); + pm_message(" Version: %02x", imgP->version); + pm_message(" Type: %s", ipdb_typeName(imgP->type)); + pm_message(" Note: %02x %02x %02x %02x", + imgP->note[0], imgP->note[1], imgP->note[2], imgP->note[3]); + pm_message(" X_last: %u", imgP->x_last); + pm_message(" Y_last: %u", imgP->y_last); + pm_message(" X_anchor: %u", imgP->x_anchor); + pm_message(" Y_anchor: %u", imgP->y_anchor); + pm_message(" Width: %u", imgP->width); + pm_message(" Height: %u", imgP->height); + pm_message("Pixels per byte: %u", ipdb_img_ppb(imgP)); + pm_message("Image size: %lu bytes", + (unsigned long)ipdb_img_size(imgP)); + } +} + + +static int +imageReadData(FILE * const fileP, + IMAGE * const imgP, + uint32_t const end_offset) { + + int retval; + size_t dataSize; + uint8_t * buffer; + + readCompressed(imgP, end_offset, fileP, &dataSize, &buffer, &retval); + + if (retval == 0) { + /* + * Compressed data can cross row boundaries so we decompress + * the data here to avoid messiness in the row access functions. + */ + if (dataSize != ipdb_img_size(imgP)) { + decompress(buffer, dataSize, ipdb_img_size(imgP), &imgP->data); + if (imgP->data == NULL) + retval = ENOMEM; + else + imgP->compressed = true; + free(buffer); + } else { + imgP->compressed = false; + imgP->data = buffer; + /* Storage at 'buffer' now belongs to *imgP */ + } + } + return retval; +} + + + +static int +imageRead(IMAGE * const imgP, + uint32_t const end_offset, + FILE * const fileP, + bool const verbose) { + + if (imgP) { + imgP->r->offset = (uint32_t)ftell(fileP); + + imageReadHeader(fileP, imgP, verbose); + + imageReadData(fileP, imgP, end_offset); + } + return 0; +} + + + +static int +textRead(TEXT * const textP, + FILE * const fileP) { + + int retval; + char * s; + char buf[128]; + int used, alloced, len; + + if (textP == NULL) + return 0; + + textP->r->offset = (uint32_t)ftell(fileP); + + /* + * What a pain in the ass! Why the hell isn't there a length + * attached to the text record? I suppose the designer wasn't + * concerned about non-seekable (i.e. pipes) input streams. + * Perhaps I'm being a little harsh, the lack of a length probably + * isn't much of an issue on the Pilot. + */ + used = 0; + alloced = 0; + s = NULL; + retval = 0; /* initial value */ + while ((len = fread(buf, 1, sizeof(buf), fileP)) != 0 && retval == 0) { + if (buf[len - 1] == '\0') + --len; + if (used + len > alloced) { + alloced += 2 * sizeof(buf); + REALLOCARRAY(s, alloced); + + if (s == NULL) + retval = ENOMEM; + } + if (retval == 0) { + memcpy(s + used, buf, len); + used += len; + } + } + if (retval == 0) { + textP->data = calloc(1, used + 1); + if (textP->data == NULL) + retval = ENOMEM; + else + memcpy(textP->data, s, used); + } + if (s) + free(s); + + return retval; +} + + + +static int +pdbheadRead(PDBHEAD * const pdbHeadP, + FILE * const fileP) { + + int retval; + + fread(pdbHeadP->name, 1, 32, fileP); + pm_readbigshortu(fileP, &pdbHeadP->flags); + pm_readbigshortu(fileP, &pdbHeadP->version); + pm_readbiglongu2(fileP, &pdbHeadP->ctime); + pm_readbiglongu2(fileP, &pdbHeadP->mtime); + pm_readbiglongu2(fileP, &pdbHeadP->btime); + pm_readbiglongu2(fileP, &pdbHeadP->mod_num); + pm_readbiglongu2(fileP, &pdbHeadP->app_info); + pm_readbiglongu2(fileP, &pdbHeadP->sort_info); + fread(pdbHeadP->type, 1, 4, fileP); + fread(pdbHeadP->id, 1, 4, fileP); + pm_readbiglongu2(fileP, &pdbHeadP->uniq_seed); + pm_readbiglongu2(fileP, &pdbHeadP->next_rec); + pm_readbigshortu(fileP, &pdbHeadP->num_recs); + + if (!memeq(pdbHeadP->type, IPDB_vIMG, 4) + || !memeq(pdbHeadP->id, IPDB_View, 4)) + retval = E_NOTIMAGE; + else + retval = 0; + + return retval; +} + + + +static int +rechdrRead(RECHDR * const rechdrP, + FILE * const fileP) { + + int retval; + off_t len; + + pm_readbiglongu2(fileP, &rechdrP->offset); + + len = (off_t)rechdrP->offset - ftell(fileP); + switch(len) { + case 4: + case 12: + /* + * Version zero (eight bytes of record header) or version + * two with a note (two chunks of eight record header bytes). + */ + fread(&rechdrP->unknown[0], 1, 3, fileP); + fread(&rechdrP->rec_type, 1, 1, fileP); + rechdrP->n_extra = 0; + rechdrP->extra = NULL; + retval = 0; + break; + case 6: + /* + * Version one (ten bytes of record header). + */ + fread(&rechdrP->unknown[0], 1, 3, fileP); + fread(&rechdrP->rec_type, 1, 1, fileP); + rechdrP->n_extra = 2; + MALLOCARRAY(rechdrP->extra, rechdrP->n_extra); + if (rechdrP->extra == NULL) + retval = ENOMEM; + else { + fread(rechdrP->extra, 1, rechdrP->n_extra, fileP); + retval = 0; + } + break; + default: + /* + * hmmm.... I'll assume this is the record header + * for a text record. + */ + fread(&rechdrP->unknown[0], 1, 3, fileP); + fread(&rechdrP->rec_type, 1, 1, fileP); + rechdrP->n_extra = 0; + rechdrP->extra = NULL; + retval = 0; + break; + } + if (retval == 0) { + if ((rechdrP->rec_type != IMG_REC && rechdrP->rec_type != TEXT_REC) + || !memeq(rechdrP->unknown, IPDB_MYST, 3)) + retval = E_NOTRECHDR; + } + return retval; +} + + + +static int +ipdbRead(IPDB * const pdbP, + FILE * const fileP, + bool const verbose) { + + int retval; + + ipdb_clear(pdbP); + + pdbP->p = ipdb_pdbhead_alloc(NULL); + + if (pdbP->p == NULL) + retval = ENOMEM; + else { + int status; + + status = pdbheadRead(pdbP->p, fileP); + + if (status != 0) + retval = status; + else { + pdbP->i = ipdb_image_alloc(pdbP->p->name, IMG_GRAY, 0, 0); + if (pdbP->i == NULL) + retval = ENOMEM; + else { + int status; + status = rechdrRead(pdbP->i->r, fileP); + if (status != 0) + retval = status; + else { + if (pdbP->p->num_recs > 1) { + pdbP->t = ipdb_text_alloc(NULL); + if (pdbP->t == NULL) + retval = ENOMEM; + else { + int status; + status = rechdrRead(pdbP->t->r, fileP); + if (status != 0) + retval = status; + else + retval = 0; + } + } else + retval = 0; + + if (retval == 0) { + uint32_t const offset = + pdbP->t == NULL ? + UNKNOWN_OFFSET : pdbP->t->r->offset - 1; + + int status; + + status = imageRead(pdbP->i, offset, fileP, verbose); + if (status != 0) + retval = status; + else { + if (pdbP->t != NULL) { + int status; + + status = textRead(pdbP->t, fileP); + if (status != 0) + retval = status; + } + } + } + } + } + } + } + return retval; +} + + + +static void +g16unpack(const uint8_t * const p, + uint8_t * const g, + int const w) { + + static const uint8_t pal[] = + {0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88, + 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11, 0x00}; + const uint8_t * seg; + unsigned int i; + + for (i = 0, seg = p; i < w; i += 2, ++seg) { + g[i + 0] = pal[getg16pixel(*seg, 0)]; + g[i + 1] = pal[getg16pixel(*seg, 1)]; + } +} + + + +static void +gunpack(const uint8_t * const p, + uint8_t * const g, + int const w) { + + static const uint8_t pal[] = {0xff, 0xaa, 0x55, 0x00}; + const uint8_t * seg; + unsigned int i; + + for (i = 0, seg = p; i < w; i += 4, ++seg) { + g[i + 0] = pal[getgpixel(*seg, 0)]; + g[i + 1] = pal[getgpixel(*seg, 1)]; + g[i + 2] = pal[getgpixel(*seg, 2)]; + g[i + 3] = pal[getgpixel(*seg, 3)]; + } +} + + + +static void +munpack(const uint8_t * const p, + uint8_t * const b, + int const w) { + + static const uint8_t pal[] = {0x00, 0x01}; + const uint8_t * seg; + unsigned int i; + + for (i = 0, seg = p; i < w; i += 8, ++seg) { + b[i + 0] = pal[getmpixel(*seg, 0)]; + b[i + 1] = pal[getmpixel(*seg, 1)]; + b[i + 2] = pal[getmpixel(*seg, 2)]; + b[i + 3] = pal[getmpixel(*seg, 3)]; + b[i + 4] = pal[getmpixel(*seg, 4)]; + b[i + 5] = pal[getmpixel(*seg, 5)]; + b[i + 6] = pal[getmpixel(*seg, 6)]; + b[i + 7] = pal[getmpixel(*seg, 7)]; + } +} + + + +static void +ipdb_g16row(IPDB * const pdbP, + unsigned int const row, + uint8_t * const buffer) { + + g16unpack(ipdb_img_row(pdbP->i, row), buffer, ipdb_width(pdbP)); +} + + + +static void +ipdb_grow(IPDB * const pdbP, + unsigned int const row, + uint8_t * const buffer) { + + gunpack(ipdb_img_row(pdbP->i, row), buffer, ipdb_width(pdbP)); +} + + + +static void +ipdb_mrow(IPDB * const pdbP, + unsigned int const row, + uint8_t * const buffer) { + + munpack(ipdb_img_row(pdbP->i, row), buffer, ipdb_width(pdbP)); +} + + + +static void +writePdbImg(IPDB * const pdbP, + FILE * const ofP) { + + struct pam pam; + tuple * tupleRow; + unsigned int row; + uint8_t * imgRow; + + MALLOCARRAY(imgRow, ipdb_width(pdbP)); + + pam.size = sizeof(pam); + pam.len = PAM_STRUCT_SIZE(tuple_type); + pam.file = ofP; + pam.plainformat = 0; + pam.width = ipdb_width(pdbP); + pam.height = ipdb_height(pdbP); + pam.depth = 1; + pam.maxval = ipdb_type(pdbP) == IMG_MONO ? 1 : 255; + pam.bytes_per_sample = pnm_bytespersample(pam.maxval); + pam.format = PAM_FORMAT; + strcpy(pam.tuple_type, + ipdb_type(pdbP) == IMG_MONO ? + PAM_PBM_TUPLETYPE : PAM_PGM_TUPLETYPE); + + pnm_writepaminit(&pam); + + tupleRow = pnm_allocpamrow(&pam); + + for (row = 0; row < pam.height; ++row) { + unsigned int col; + + + if (ipdb_type(pdbP) == IMG_MONO) + ipdb_mrow(pdbP, row, imgRow); + else if (ipdb_type(pdbP) == IMG_GRAY) + ipdb_grow(pdbP, row, imgRow); + else + ipdb_g16row(pdbP, row, imgRow); + + for (col = 0; col < pam.width; ++col) + tupleRow[col][0] = imgRow[col]; + + pnm_writepamrow(&pam, tupleRow); + } + pnm_freepamrow(tupleRow); + + free(imgRow); +} + + + +static void +writetxt(IPDB * const pdbP, + const char * const name) { + + const char * const note = ipdb_text(pdbP); + + FILE * fP; + + if (name == NULL || note == NULL) { + } else { + fP = pm_openw(name); + if (fP == NULL) + pm_error("Could not open note file '%s' for output", name); + + fprintf(fP, "%s\n", note); + + pm_close(fP); + } +} + + + +int +main(int argc, const char ** argv) { + + struct cmdlineInfo cmdline; + FILE * ifP; + IPDB * pdbP; + int status; + + pm_proginit(&argc, argv); + + parseCommandLine(argc, argv, &cmdline); + + ifP = pm_openr(cmdline.inputFileName); + + pdbP = ipdb_alloc(NULL); + if (pdbP == NULL) + pm_error("Could not allocate IPDB structure."); + + status = ipdbRead(pdbP, ifP, cmdline.verbose); + if (status != 0) + pm_error("Image header read error: %s.", ipdb_err(status)); + + writePdbImg(pdbP, stdout); + + writetxt(pdbP, cmdline.notefile); + + ipdb_free(pdbP); + + pm_close(ifP); + + return EXIT_SUCCESS; +} diff --git a/doc/HISTORY b/doc/HISTORY index 72d41a12..05980ce5 100644 --- a/doc/HISTORY +++ b/doc/HISTORY @@ -6,7 +6,7 @@ CHANGE HISTORY not yet BJH Release 10.52.00 - Add pamtopdbimg. + Add pamtopdbimg, pdbimgtopam. Add pamrecolor. Thanks Scott Pakin. -- cgit 1.4.1