diff options
Diffstat (limited to 'converter/other/pamtopdbimg.c')
-rw-r--r-- | converter/other/pamtopdbimg.c | 613 |
1 files changed, 577 insertions, 36 deletions
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 <stdlib.h> +#include <assert.h> #include <string.h> #include <sys/stat.h> @@ -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."); |