diff options
author | giraffedata <giraffedata@9d0c8265-081b-0410-96cb-a4ca84ce46f8> | 2006-08-19 03:12:28 +0000 |
---|---|---|
committer | giraffedata <giraffedata@9d0c8265-081b-0410-96cb-a4ca84ce46f8> | 2006-08-19 03:12:28 +0000 |
commit | 1fd361a1ea06e44286c213ca1f814f49306fdc43 (patch) | |
tree | 64c8c96cf54d8718847339a403e5e67b922e8c3f /converter/pbm/pbmtomrf.c | |
download | netpbm-mirror-1fd361a1ea06e44286c213ca1f814f49306fdc43.tar.gz netpbm-mirror-1fd361a1ea06e44286c213ca1f814f49306fdc43.tar.xz netpbm-mirror-1fd361a1ea06e44286c213ca1f814f49306fdc43.zip |
Create Subversion repository
git-svn-id: http://svn.code.sf.net/p/netpbm/code/trunk@1 9d0c8265-081b-0410-96cb-a4ca84ce46f8
Diffstat (limited to 'converter/pbm/pbmtomrf.c')
-rw-r--r-- | converter/pbm/pbmtomrf.c | 338 |
1 files changed, 338 insertions, 0 deletions
diff --git a/converter/pbm/pbmtomrf.c b/converter/pbm/pbmtomrf.c new file mode 100644 index 00000000..186e95f5 --- /dev/null +++ b/converter/pbm/pbmtomrf.c @@ -0,0 +1,338 @@ +/* pbmtomrf - convert pbm to mrf + * public domain by RJM + * + * Adapted to Netpbm by Bryan Henderson 2003.08.09. Bryan got his copy from + * ftp://ibiblio.org/pub/linux/apps/convert, dated 1998.03.03. + */ + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <limits.h> + +#include "pm_c_util.h" +#include "pbm.h" + +static int bitbox; +static int bitsleft; + +static FILE *bit_out; + + +static void +bit_init(FILE * const out) { + bitbox = 0; + bitsleft = 8; + bit_out = out; +} + + + +static void +bit_output(int const bit) { + --bitsleft; + bitbox |= (bit << bitsleft); + if (bitsleft == 0) { + fputc(bitbox, bit_out); + bitbox = 0; + bitsleft = 8; + } +} + + + +static void +bit_flush(void) { + /* there are never 0 bits left outside of bit_output, but + * if 8 bits are left here there's nothing to flush, so + * only do it if bitsleft!=8. + */ + if (bitsleft != 8) { + bitsleft = 1; + bit_output(0); /* yes, really. This will always work. */ + } +} + + + +static void +doSquare(unsigned char * const image, + int const ox, + int const oy, + int const w, + int const size) { + + unsigned int y; + unsigned int t; + + /* check square to see if it's all black or all white. */ + + t = 0; + for (y = 0; y < size; ++y) { + unsigned int x; + for (x = 0; x < size; ++x) + t += image[(oy+y)*w + ox + x]; + } + /* if the total's 0, it's black. if it's size*size, it's white. */ + if (t == 0 || t == size*size) { + if (size != 1) /* (it's implicit when size is 1, of course) */ + bit_output(1); /* all same color */ + bit_output(t?1:0); + return; + } + + /* otherwise, if our square is greater than 1x1, we need to recurse. */ + if(size > 1) { + bit_output(0); /* not all same */ + doSquare(image, ox, oy, w, size>>1); + doSquare(image, ox+size, oy, w, size>>1); + doSquare(image, ox, oy+size, w, size>>1); + doSquare(image, ox+size, oy+size, w, size>>1); + } +} + + + +static void +fiddleRightEdge(unsigned char * const image, + unsigned int const w, + unsigned int const h, + unsigned int const pw, + bool * const flippedP) { + + unsigned int row; + unsigned int t; + + for (row = t = 0; row < h; ++row) + t += image[row*pw + w - 1]; + + if (t*2 > h) { + unsigned int row; + + *flippedP = TRUE; + for (row = 0; row < h; ++row) { + unsigned int col; + for (col = w; col < pw; ++col) + image[row*pw + col] = 1; + } + } else + *flippedP = FALSE; +} + + + +static void +fiddleBottomEdge(unsigned char * const image, + unsigned int const w, + unsigned int const h, + unsigned int const pw, + unsigned int const ph, + bool * const flippedP) { + + unsigned int col; + unsigned int t; + + for (col = t = 0; col < w; ++col) + t += image[(h-1)*pw + col]; + + if (t*2 > w) { + unsigned int row; + *flippedP = TRUE; + for (row = h; row < ph; ++row) { + unsigned int col; + for (col = 0; col < w; ++col) + image[row*pw + col] = 1; + } + } else + *flippedP = FALSE; +} + + + + +static void +fiddleBottomRightCorner(unsigned char * const image, + unsigned int const w, + unsigned int const h, + unsigned int const pw, + unsigned int const ph) { + unsigned int row; + + for (row = h; row < ph; ++row) { + unsigned int col; + + for (col = w; col < pw; ++col) + image[row*pw + col] = 1; + } +} + + + +static void +fiddleEdges(unsigned char * const image, + int const cols, + int const rows) { +/* the aim of this routine is play around with the edges which + * are compressed into the mrf but thrown away when it's decompressed, + * such that we get the best compression possible. + * If you don't see why this is a good idea, consider the simple case + * of a 1x1 white pixel. Placed on a black 64x64 this takes several bytes + * to compress. On a white 64x64, it takes two bits. + * (Clearly most cases will be more complicated, but you should get the + * basic idea from that.) + */ + + /* there are many possible approaches to this problem, and this one's + * certainly not the best, but at least it's quick and easy, and it's + * better than nothing. :-) + * + * So, all we do is flip the runoff area of an edge to white + * if more than half of the pixels on that edge are + * white. Then for the bottom-right runoff square (if there is + * one), we flip it if we flipped both edges. + */ + + /* w64 is units-of-64-bits width, h64 same for height */ + unsigned int const w64 = (cols + 63) / 64; + unsigned int const h64 = (rows + 63) / 64; + + int const pw=w64*64; + int const ph=h64*64; + + bool flippedRight, flippedBottom; + + if (cols % 64 != 0) + fiddleRightEdge(image, cols, rows, pw, &flippedRight); + else + flippedRight = FALSE; + + if (rows % 64 != 0) + fiddleBottomEdge(image, cols, rows, pw, ph, &flippedBottom); + else + flippedBottom = FALSE; + + if (flippedRight && flippedBottom) + fiddleBottomRightCorner(image, cols, rows, pw, ph); +} + + + +static void +readPbmImage(FILE * const ifP, + unsigned char ** const imageP, + int * const colsP, + int * const rowsP) { + + + /* w64 is units-of-64-bits width, h64 same for height */ + unsigned int w64, h64; + + unsigned char * image; + int cols, rows, format; + unsigned int row; + bit * bitrow; + + pbm_readpbminit(ifP, &cols, &rows, &format); + + w64 = (cols + 63) / 64; + h64 = (rows + 63) / 64; + + if (UINT_MAX/w64/64/h64/64 == 0) + pm_error("Ridiculously large, unprocessable image: %u cols x %u rows", + cols, rows); + + image = calloc(w64*h64*64*64,1); + if (image == NULL) + pm_error("Unable to get memory for raster"); + + /* get bytemap image rounded up into mod 64x64 squares */ + + bitrow = pbm_allocrow(cols); + + for (row = 0; row < rows; ++row) { + unsigned int col; + + pbm_readpbmrow(ifP, bitrow, cols, format); + + for (col =0; col < cols; ++col) + image[row*(w64*64) + col] = (bitrow[col] == PBM_WHITE ? 1 : 0); + } + pbm_freerow(bitrow); + *imageP = image; + *colsP = cols; + *rowsP = rows; +} + + + +static void +outputMrf(FILE * const ofP, + unsigned char * const image, + unsigned int const cols, + unsigned int const rows) { + + unsigned int const w64 = (cols + 63) / 64; + unsigned int const h64 = (rows + 63) / 64; + + unsigned int row; + + fprintf(ofP, "MRF1"); + fprintf(ofP, "%c%c%c%c", cols >> 24, cols >> 16, cols >> 8, cols >> 0); + fprintf(ofP, "%c%c%c%c", rows >> 24, rows >> 16, rows >> 8, rows >> 0); + fputc(0, ofP); /* option byte, unused for now */ + + /* now recursively check squares. */ + + bit_init(ofP); + + for (row = 0; row < h64; ++row) { + unsigned int col; + for (col = 0; col < w64; ++col) + doSquare(image, col*64, row*64, w64*64, 64); + } + bit_flush(); +} + + + +int +main(int argc,char *argv[]) { + + FILE * ifP; + FILE * ofP; + unsigned char *image; + int rows, cols; + + pbm_init(&argc, argv); + + if (argc-1 > 1) + pm_error("Too many arguments: %d. Only argument is input file", + argc-1); + + if (argc-1 == 1) + ifP = pm_openr(argv[1]); + else + ifP = stdin; + + ofP = stdout; + + readPbmImage(ifP, &image, &cols, &rows); + + pm_close(ifP); + + /* if necessary, alter the unused outside area to aid compression of + * edges of image. + */ + + fiddleEdges(image, cols, rows); + + outputMrf(ofP, image, cols, rows); + + free(image); + + return 0; +} + + + + |