/* compare two insulator CDs _ ___ __| | ___ _ __ ___ _ __ ___ / __/ _` |/ __| '_ ` _ \| '_ \ / __| | (_| (_| | (__| | | | | | |_) | _ | (__ \___\__,_|\___|_| |_| |_| .__/ (_) \___| |_| ian macky jan-25-2003 */ #include #include #include #include #include #define CDCMP_HEAD "cdcmp.head" #define CDCMP_TAIL "cdcmp.tail" #define MAX_LINE 128 /* max line for catting */ #define MAX_PATH 64 /* max length of file path */ #define MAX_FIELD 32 /* max field in CD data file */ #ifndef TRUE # define TRUE 1 # define FALSE 0 #endif #ifndef MAX # define MAX(a, b) ((a > b) ? (a) : (b)) #endif typedef int boolean; typedef struct { FILE *f; char image_path[MAX_PATH]; int image_width, image_height; char width[MAX_FIELD]; char height[MAX_FIELD]; char weight[MAX_FIELD]; char voltage[MAX_FIELD]; char leakage[MAX_FIELD]; char pinhole[MAX_FIELD]; char styleno[MAX_FIELD]; } cd_data; char *qs; /* HTML Query String */ float cd1, cd2; /* the two CD#s to compare */ cd_data data1, data2; /* data for each CD */ int width, height; /* size of comparison GIF */ void cat_file(char *path); int gif_block(FILE *f, int bit_p); boolean gif_compare(char *path); boolean gif_size(cd_data *data); boolean load_data(float cd, cd_data *data); int lzw_clear(FILE *f); int lzw_encode(FILE *f); boolean parse_args(int argc, char **argv); boolean parse_query(char *qs); int main(int argc, char *argv[]) { char path[MAX_PATH]; struct stat statbuf; if ((qs = getenv("QUERY_STRING"))) /* we acting as cgi? */ puts("Content-type: text/html\n"); /* begin expected response */ if (qs) /* CGI? */ { cat_file(CDCMP_HEAD); if (!parse_query(qs)) { puts("

Malformed Query

"); cat_file(CDCMP_TAIL); return 1; } } else { if (!parse_args(argc, argv)) return 2; } if (!load_data(cd1, &data1)) return 3; if (!load_data(cd2, &data2)) return 4; /* see if there's a comparison gif already. if not, make it. */ if (qs) sprintf(path, "../tmp/%g,%g.gif", cd1, cd2); else sprintf(path, "/tmp/%g,%g.gif", cd1, cd2); if (stat(path, &statbuf)) gif_compare(path); /* if not doing web result, just show output file and exit */ if (!qs) { printf("Comparison gif: %s\n", path); return 0; } /* comparison gif will be encompassing box */ width = MAX(data1.image_width, data2.image_width); height = MAX(data1.image_height, data2.image_height); /* now start the body */ puts(""); printf("\n", data1.image_path, data1.image_width, data1.image_height, cd1, cd1); printf("\n", path, width, height, cd1, cd2, cd1, cd2); printf("\n", data2.image_path, data2.image_width, data2.image_height, cd2, cd2); puts("

"); puts(""); puts(""); printf("\n", cd1); puts(""); printf("\n", cd2); puts(""); #define PRINT_DATA(label, field) \ printf("" \ "" \ "" \ "\n", label, data1.field, data2.field) PRINT_DATA("Width", width); PRINT_DATA("Height", height); PRINT_DATA("Weight", weight); PRINT_DATA("Voltage", voltage); PRINT_DATA("Leakage", leakage); PRINT_DATA("Pinhole", pinhole); PRINT_DATA("Style#", styleno); puts("
" "CD %g" "CD %g
" \ "%s%s·%s

"); puts("
"); puts("Data is available for CDs 102, 104, 106, 110, 112, 113, 115, 116, 117,"); puts("119, 121, 122.4, 124, 125, 126, 133, 134, 137, 139, 143, 145, 147, 150,"); puts("151, 152, 154, 160, 162.1, 162, 164.4, 164, 168, 174, 178, 185, 187, 188,"); puts("190-191, 196, 200, 201, 202, 203.2, 205, 213, 225, 226, 228, 232, 233,"); puts("235, 240, 251, 252, 254, 257, 259, 262, 263, 267, 268, 269, 272, 273, 275,"); puts("281, 282, 283, 284, 287, 288, 289, 296, 297, 299, 300, 302, 308, 316, 318,"); puts("319, 320, 322, 323, 325, 327, 330 and 331"); puts("

"); puts("
"); puts(""); puts(""); puts(""); puts("
"); cat_file(CDCMP_TAIL); return 0; /* success */ } boolean parse_query(char *qs) { char *p; if (!(p = strstr(qs, "cd1="))) { puts("

Internal error, malformed QUERY_STRING

"); printf("

"%s"

", qs); cat_file(CDCMP_TAIL); } cd1 = atof(p + 4); if (!(p = strstr(qs, "cd2="))) { puts("

Internal error, malformed QUERY_STRING

"); printf("

"%s"

", qs); cat_file(CDCMP_TAIL); } cd2 = atof(p + 4); return TRUE; } boolean parse_args(int argc, char *argv[]) { if (argc != 3) { puts("usage: cdcmp "); return FALSE; } cd1 = atof(argv[1]); cd2 = atof(argv[2]); return TRUE; } boolean load_data(float cd, cd_data *data) { char *e, *p, *bar, path[MAX_PATH], line[MAX_LINE]; FILE *f; sprintf(path, "../CD/%g/%g.dat", cd, cd); if (!(f = fopen(path, "r")) || !fgets(line, sizeof(line), f)) { if (qs) fputs("

", stdout); printf("No data for CD %g", cd); if (qs) fputs("

", stdout); putchar('\n'); return FALSE; } e = line + strlen(line) - 1; *e = 0; /* 3"|4"|20 oz|2ndary|-|1"| */ bar = strchr(line, '|'); *bar++ = 0; strcpy(data->width, line); p = bar; bar = strchr(bar, '|'); *bar++ = 0; strcpy(data->height, p); p = bar; bar = strchr(bar, '|'); *bar++ = 0; strcpy(data->weight, p); p = bar; bar = strchr(bar, '|'); *bar++ = 0; strcpy(data->voltage, p); p = bar; bar = strchr(bar, '|'); *bar++ = 0; strcpy(data->leakage, p); p = bar; bar = strchr(bar, '|'); *bar++ = 0; strcpy(data->pinhole, p); strcpy(data->styleno, bar); fclose(f); sprintf(data->image_path, "../CD/%g/%gt.gif", cd, cd); gif_size(data); if ((data->image_width > 200) || (data->image_height > 200)) { sprintf(data->image_path, "../CD/%g/%gtt.gif", cd, cd); gif_size(data); } return TRUE; } void cat_file(char *path) { char line[MAX_LINE]; FILE *f; if ((f = fopen(path, "r"))) { while (fgets(line, sizeof(line), f)) fputs(line, stdout); fclose(f); } else printf("(Couldn't open '%s')\n", path); } /* ____ ___ _____ _ _ _______ __ / ___|_ _| ___| __ _ _ __ __| | | | |__ /\ \ / / | | _ | || |_ / _` | '_ \ / _` | | | / / \ \ /\ / / | |_| || || _| | (_| | | | | (_| | | |___ / /_ \ V V / \____|___|_| \__,_|_| |_|\__,_| |_____/____| \_/\_/ */ #define GIFCMP_MAX_ROW 512 #define GIFCMP_MAX_COL 512 GifByteType xmap[GIFCMP_MAX_ROW][GIFCMP_MAX_COL]; #define GIFCMP_CMAP_SIZE 256 GifColorType cmap[GIFCMP_CMAP_SIZE] = { /* first 8 is map for gif #1 (blues) */ { 0xFF, 0xFF, 0xFF }, { 0xDB, 0xDB, 0xFF }, { 0xB7, 0xB7, 0xFF }, { 0x93, 0x93, 0xFF }, { 0x6F, 0x6F, 0xFF }, { 0x4B, 0x4B, 0xFF }, { 0x27, 0x27, 0xFF }, { 0x00, 0x00, 0xFF }, /* second 8 is map for gif #2 (reds) */ { 0xFF, 0xFF, 0xFF }, { 0xFF, 0xDB, 0xDB }, { 0xFF, 0xB7, 0xB7 }, { 0xFF, 0x6F, 0x6F }, { 0xFF, 0x93, 0x93 }, { 0xFF, 0x4B, 0x4B }, { 0xFF, 0x27, 0x27 }, { 0xFF, 0x00, 0x00 }, /* third 8 is the sum (purples) */ { 0xFF, 0xFF, 0xFF }, { 0xFF, 0xDB, 0xFF }, { 0xFF, 0xB7, 0xFF }, { 0xFF, 0x6F, 0xFF }, { 0xFF, 0x93, 0xFF }, { 0xFF, 0x4B, 0xFF }, { 0xFF, 0x27, 0xFF }, { 0xFF, 0x00, 0xFF } /* rest is zeros */ }; /* --- GIF data ------------------------------------------------------- */ #define GIF_ID "GIF87a" /* magic if of GIF files */ #define GIF_EXTENSION "gif" /* default extension */ #define GIF_RGB_DEPTH 8 /* 8 bits each for RG&B */ #define GIF_RGB_COLORS (1 << GIF_RGB_DEPTH) #define GIF_RGB_MAX (GIF_RGB_COLORS - 1) #define GIF_MAX_IMG_BLK 254 /* max bytes in image block */ #define GIF_BLOCK_BITS 8 /* 8 bits per block byte */ #define GIF_F_GLOB_CM 0x80 /* flag: global colormap */ #define GIF_IMAGE_SEP ',' /* image separator */ #define GIF_FILE_TERM ';' /* file terminator */ /* --- LZW data ------------------------------------------------------- */ #define LZW_MIN_INPUT_SIZE 2 /* can't take single bits */ #define LZW_MAX_INPUT_SIZE 8 /* max input datum is byte */ #define LZW_MIN_CODE_SIZE 3 /* smallest possible code */ #define LZW_ODATA_SIZE 8 /* 8-bit output bytes */ #define LZW_MAX_CODE_SIZE 12 /* max code we'll use. */ #define LZW_HASH_SLOTS 5099 /* prime > 2^MAX_CODE_SIZE */ #define LZW_MAX_CODE ((1 << LZW_MAX_CODE_SIZE) - 1) #define LZW_NOT ((LZW_MAX_CODE << 1) - 1) #define LZW_HASH(pred, suff) ((((pred) << 4) ^ (suff)) % LZW_HASH_SLOTS) typedef unsigned char lzw_idata; /* input datum max 8 bits */ typedef short lzw_ocode; /* output code (max 12 bits); -1 = free */ int width; /* raster width in bytes */ int height; /* raster height in bytes */ int h1; /* height - 1 */ int depth; /* raster depth in bits (1, 2, 4, 8) */ int in_size; /* # of bits in input data */ lzw_ocode pred[LZW_HASH_SLOTS]; /* predecessor code table */ lzw_idata suff[LZW_HASH_SLOTS]; /* + suffix data table */ lzw_ocode code[LZW_HASH_SLOTS]; /* = compression code table */ lzw_ocode free_code; /* next free output code */ lzw_ocode clear_code; /* sent to clear tables */ lzw_ocode eoi; /* End-of-Information code */ lzw_ocode first_code; /* first code for strings */ int init_size; /* initial code size */ int code_size; /* working code size */ int max_code; /* largest code */ int n_out; /* # output bytes written */ int n_bit; /* # output bits accumulated */ unsigned long accum; /* accumulated codes: must hold 23 bits!! */ unsigned char codebuf[GIF_MAX_IMG_BLK + 1]; #define put1(c, f) putc((c), f) /* write 1-byte quantity */ void put2(unsigned u, FILE *f); /* | Write GIF header */ void gif_header(FILE *f) { fputs(GIF_ID, f); /* usually "GIF" + "87a" */ put2(width, f); put2(height, f); put1(GIF_F_GLOB_CM | (GIF_RGB_DEPTH - 1) << 4 | (depth - 1), f); put1(0, f); /* background color index always 0 */ put1(0, f); /* pixel aspect ratio (0 = ideal = 1:1) */ } /* | Write colormap */ void gif_cm(FILE *f) { int i; for (i = 0; i < GIFCMP_CMAP_SIZE; i++) { put1((int) cmap[i].Red, f); put1((int) cmap[i].Green, f); put1((int) cmap[i].Blue, f); } } /* | Write image descriptor */ int gif_image(FILE *f) { put1(GIF_IMAGE_SEP, f); /* image separator */ put2(0, f); /* left position */ put2(0, f); /* right position */ put2(width, f); /* image width */ put2(height, f); /* image height */ put1(0, f); /* no local cm, etc */ put1(in_size, f); /* initial code size */ if (!lzw_encode(f)) /* do the encoding */ return FALSE; /* probly disk full */ if (!gif_block(f, TRUE)) /* write last block */ return FALSE; /* disk full? */ put1(0, f); /* block terminator */ return TRUE; } /* | Write both bytes of a 16-bit quantity to f as */ void put2(unsigned u, FILE *f) { put1(u & 0xFF, f); /* mask off high, keep low 8 */ put1((u >> 8) & 0xFF, f); /* shift high 8 down to low */ } /* | Accumulate next LZW output code */ int gif_out(FILE *f, unsigned c) { accum |= (c << n_bit); n_bit += code_size; while (n_bit > 7) { if (n_out == GIF_MAX_IMG_BLK) /* this block full? */ gif_block(f, FALSE); /* spit out; start next. */ codebuf[n_out++] = (unsigned char) accum; accum >>= 8; n_bit -= 8; } return TRUE; } /* | Write out current block */ int gif_block(FILE *f, int bit_p) { int m, n = n_out; if (bit_p && n_bit) /* partial byte? */ { codebuf[n++] = (unsigned char) accum; n_bit = 0; accum = 0; } if (n > 0) { put1(n, f); /* write block count */ m = fwrite(codebuf, 1, n, f); if (m != n) return FALSE; n_out = 0; } return TRUE; } boolean gif_size(cd_data *data) { FILE *f; if (!(f = fopen(data->image_path, "rb"))) return FALSE; data->f = f; if ((getc(f) != 'G') || (getc(f) != 'I') || (getc(f) != 'F')) return FALSE; getc(f); getc(f); getc(f); /* ignore version */ data->image_width = (short) getc(f) + getc(f) * 256; data->image_height = (short) getc(f) + getc(f) * 256; return TRUE; } /* | Set the output code size */ void lzw_codesize(int size) { code_size = size; /* initial code is 3-9 bits */ max_code = (1 << size) - 1; /* largest possible code */ } /* | Update LZW table */ void lzw_new(FILE *f, lzw_ocode p, lzw_idata s) { lzw_ocode slot; int step; if (free_code > max_code) { /* need extra code bit? */ if (max_code == LZW_MAX_CODE) { /* hits abs max? */ lzw_clear(f); /* yes, clear table. */ return; } lzw_codesize(++code_size); /* increase code size. */ } slot = LZW_HASH(p, s); /* initial hash code */ step = slot ? (LZW_HASH_SLOTS - slot) : 1; /* search displacement */ while (code[slot] >= 0) /* while slots full */ if ((slot -= step) < 0) /* step back; too far? */ slot += LZW_HASH_SLOTS; /* back to the living. */ pred[slot] = p; /* predecessor */ suff[slot] = s; /* + suffix */ code[slot] = free_code++; /* = compression code */ } /* | LZW search function. Uses the 'next' field to go down the collision | tree to find the entry corresponding to the passed key. */ int lzw_find(lzw_ocode p, lzw_idata s, lzw_ocode *uc) { lzw_ocode c; int slot, step; slot = LZW_HASH(p, s); /* get initial hash code */ step = slot ? (LZW_HASH_SLOTS - slot) : 1; /* search displacement */ while ((c = code[slot]) >= 0) { /* slot in use? */ if ((pred[slot] == p) && (suff[slot] == s)) { *uc = c; /* store output code */ return TRUE; /* and return success. */ } if ((slot -= step) < 0) /* step back. too far? */ slot += LZW_HASH_SLOTS; /* back to the living. */ } return FALSE; /* not found */ } /* | name: lzw_encode | function: LZW-encode input data | args: none | | Variable-length-code LZW compressor. Given an input function | and the size in bits of the input data, compresses the data into | chars and outputs them with the given user output function (as to | a stream or collected into a buffer, etc). */ int lzw_encode(FILE *f) { lzw_ocode o, n; lzw_idata i; int size; int x, y; init_size = size = in_size + 1; /* initial code-size */ clear_code = 1 << (size - 1); /* send to clear table */ eoi = clear_code + 1; /* End Of Information code */ first_code = clear_code + 2; /* first useable string code */ lzw_codesize(size); /* start at initial size */ lzw_clear(f); /* initialize hash table */ /* --- Start encoding ----------------------------------------------------- */ for (o = LZW_NOT, y = height - 1; y >= 0; y--) { for (x = 0; x < width; x++) { i = (lzw_idata) xmap[height - y][x]; if (lzw_find(o, i, &n)) /* look for pred+suff o+i */ o = n; /* existing pattern so far */ else /* else new pattern: */ { if (!gif_out(f, o)) /* spit out known part */ return FALSE; /* disk full? */ lzw_new(f, o, i); /* hash new string */ lzw_find(LZW_NOT, i, &o); /* start over with input */ } } } /* send final byte & End Of Information code */ if (!gif_out(f, o) || !gif_out(f, eoi)) return FALSE; return TRUE; } /* | Initialize LZW tables */ int lzw_clear(FILE *f) { int i; gif_out(f, clear_code); /* output reset code */ lzw_codesize(init_size); /* start over at initial size */ for (i = 0; i < LZW_HASH_SLOTS; i++) /* reset all hash slots. */ code[i] = -1; /* slot not in use */ free_code = 0; /* first avail code is 0 */ for (i = 0; i < clear_code; i++) /* for all single values */ lzw_new(f, LZW_NOT, i); /* add them to table. */ free_code = first_code; /* first avail pattern code */ return TRUE; } boolean gif_compare(char *path) { FILE *f; GifFileType *g1, *g2; char *r1, *r2; GifPixelType v1, v2, v3; int i, j, ow1, ow2, oh1, oh2; /* load the two source gifs */ sprintf(data1.image_path, "../CD/%g/%gt.gif", cd1, cd1); if (!(g1 = DGifOpenFileName(data1.image_path))) { printf("Couldn't open first GIF %s\n", data1.image_path); return FALSE; } if (DGifSlurp(g1) != GIF_OK) { printf("Failed to slurp first GIF %s", data1.image_path); return FALSE; } sprintf(data2.image_path, "../CD/%g/%gt.gif", cd2, cd2); if (!(g2 = DGifOpenFileName(data2.image_path))) { printf("Couldn't open second GIF %s\n", data2.image_path); return FALSE; } if (DGifSlurp(g2) != GIF_OK) { printf("Failed to slurp second GIF %s", data2.image_path); return FALSE; } /* figure the union size */ width = g1->SWidth; if (g2->SWidth > width) width = g2->SWidth; height = g1->SHeight; if (g2->SHeight > height) height = g2->SHeight; depth = 8; in_size = (depth == 1) ? 2 : depth; /* output header info */ sprintf(path, "../tmp/%g,%g.gif", cd1, cd2); if (!(f = fopen(path, "wb"))) { printf("Failed to open output file %s\n", path); return FALSE; } gif_header(f); gif_cm(f); /* make the XOR and output out GIF line at a time */ r1 = g1->SavedImages->RasterBits; r2 = g2->SavedImages->RasterBits; ow1 = (width - g1->SWidth) / 2; ow2 = (width - g2->SWidth) / 2; oh1 = height - g1->SHeight; oh2 = height - g2->SHeight; for (i = 0; i < height; i++) { for (j = 0; j < width; j++) { if (i < oh1) v1 = 0; else if ((j < ow1) || (j >= (g1->SWidth + ow1))) v1 = 0; else v1 = r1[(i - oh1) * g1->SWidth + (j - ow1)]; if (i < oh2) v2 = 0; else if ((j < ow2) || (j >= g2->SWidth + ow2)) v2 = 0; else v2 = r2[(i - oh2) * g2->SWidth + (j - ow2)]; if (v1) { if (v2) { v3 = v1 + v2; /* black or purples */ xmap[i][j] = (v3 > 7) ? 24 : (char) 16 + v3; } else xmap[i][j] = v1; /* blues */ } else { if (v2) xmap[i][j] = v2 + 8; /* greens */ else xmap[i][j] = 0; /* neither, white */ } } } DGifCloseFile(g1); DGifCloseFile(g2); /* write result */ gif_image(f); fclose(f); return TRUE; } /* $Id: cdcmp.c,v 1.1 2005/03/17 03:52:45 ian Exp $ */