/* compare two or more jpegs; produce striped image. _ (_)_ __ __ _ ___ _ __ ___ _ __ ___ | | '_ \ / _` |/ __| '_ ` _ \| '_ \ / __| | | |_) | (_| | (__| | | | | | |_) | _ | (__ _/ | .__/ \__, |\___|_| |_| |_| .__/ (_) \___| |__/|_| |___/ |_| ian macky jul-13-2003 */ #include #include #include #include #define DEST_DIR "../tmp/" #define IMAGE_LIST "../cmplist" #define CD_LIST "../cdlist" #define JPGCMP_HEAD "jpgcmp.head" #define JPGCMP_TAIL "jpgcmp.tail" #define MAX_LINE 1024 /* max line for catting */ #define MAX_IMAGE 16 /* max # images can compare */ #define MAX_IMAGE_WIDTH 1024 /* max size of image scanline */ #define MAX_PATH 256 /* max length of file path */ #define MAX_CMPLIST (1024 * 4) #define MAX_OUTLINE (MAX_IMAGE * MAX_IMAGE_WIDTH) #define MAX_FILENAME 256 boolean cd_compare; /* TRUE if comparing CDs, not images */ char images[MAX_CMPLIST][MAX_PATH]; int n_images; struct jpeg_compress_struct jc; JSAMPLE joutrow[MAX_OUTLINE * 3]; /* R G B for each pixel */ JSAMPROW joutarray[1] = { joutrow }; JSAMPLE jinrow[MAX_IMAGE_WIDTH * 3]; JSAMPROW jinarray[1] = { jinrow }; int n_imgnums; /* # of image#s specified */ int imgnums[MAX_IMAGE]; /* the image#s */ int n_cdnums; /* # of CD#s specified */ float cdnums[MAX_IMAGE]; /* the CD#s */ void cat_file(char *path); boolean check_numbers(void); boolean load_images(void); boolean parse_args(int argc, char **argv); boolean parse_query(char *qs); void sub_smaller(char *oldsub, char *newsub, char *buf); int main(int argc, char *argv[]) { FILE *f; char *qs, *what; char numlist[MAX_IMAGE * 6]; /* "NNNNN," for each */ char numbuf[32]; /* image#, "301" etc */ char dest_file[MAX_IMAGE * 6 + sizeof(DEST_DIR)]; char path[MAX_PATH]; int i, iw3, r, max_height, total_width, imgnum; /* per input image */ FILE *file[MAX_IMAGE]; int image_height[MAX_IMAGE]; int image_padding[MAX_IMAGE]; int image_width[MAX_IMAGE]; int image_offset[MAX_IMAGE]; /* jpeg controls */ struct jpeg_decompress_struct ijd[MAX_IMAGE], *jd; struct jpeg_error_mgr jerr; JSAMPLE *jp; if ((qs = getenv("QUERY_STRING"))) /* we acting as cgi? */ puts("Content-type: text/html\n"); /* begin expected response */ if (qs) /* CGI? */ { cat_file(JPGCMP_HEAD); if (!parse_query(qs)) { puts("Malformed Query"); cat_file(JPGCMP_TAIL); return 2; } } else { if (!parse_args(argc, argv)) return 2; } /* load master image list */ if (!load_images() || !n_images) { puts("Failed to load images"); return 1; } /* Check to make sure numbers are in range */ if (!check_numbers()) return 3; /* loop over image#s. turn the arguments to ints, range check, and look up that image# in the image list. form the destination file as 100,200,300.jpg etc. */ *numlist = 0; max_height = 0; total_width = 0; for (i = 0; i < n_imgnums; i++) { if (i) strcat(numlist, ","); imgnum = imgnums[i]; if (cd_compare) sprintf(numbuf, "%g", cdnums[i]); else sprintf(numbuf, "%d", imgnum); strcat(numlist, numbuf); strcpy(path, "../"); strcat(path, images[imgnum - 1]); if (!(file[i] = fopen(path, "rb"))) { printf("Couldn't open image #%d, %s\n", imgnum, images[imgnum - 1]); return 7; } /* set up jpeg decompression */ jd = &ijd[i]; jd->err = jpeg_std_error(&jerr); jpeg_create_decompress(jd); jpeg_stdio_src(jd, file[i]); jpeg_read_header(jd, TRUE); if (jd->image_height > max_height) max_height = jd->image_height; image_height[i] = jd->image_height; if (!i) /* first image, take the left half */ { image_offset[i] = 0; image_width[i] = jd->image_width / 2; } else if (i == n_imgnums - 1) /* last image, take right half */ image_offset[i] = image_width[i] = jd->image_width / 2; else /* middle images take middle quarter */ { image_width[i] = jd->image_width / 4; image_offset[i] = image_width[i] + (image_width[i] / 2); } total_width += image_width[i]; jpeg_start_decompress(jd); } strcpy(dest_file, DEST_DIR); if (cd_compare) strcat(dest_file, "cd_"); else strcat(dest_file, "img_"); strcat(dest_file, numlist); strcat(dest_file, ".jpg"); if (qs) { what = cd_compare ? "CD" : "Image"; printf("
", dest_file, what, numlist, what, numlist); cat_file(JPGCMP_TAIL); } for (i = 0; i < n_imgnums; i++) image_padding[i] = max_height - image_height[i]; /* create compression structure */ jc.err = jpeg_std_error(&jerr); jpeg_create_compress(&jc); if (!(f = fopen(dest_file, "wb"))) { printf("couldn't open output file %s\n", dest_file); return 8; } jpeg_stdio_dest(&jc, f); jc.image_width = total_width; jc.image_height = max_height; jc.in_color_space = JCS_RGB; jc.input_components = 3; /* R G & B per pixel */ jpeg_set_defaults(&jc); jpeg_start_compress(&jc, TRUE); /* form output image */ for (r = 0; r < max_height; r++) /* for each scanline */ { jp = joutrow; /* dest ptr in output row */ for (i = 0; i < n_imgnums; i++) /* for each input image */ { iw3 = image_width[i] * 3; /* total bytes (RGB each pixel) */ /* for short images, pad top with white */ if (r < image_padding[i]) /* needs padding? */ memset(jp, 0xFF, iw3); /* (0xFF, 0xFF, 0xFF) RGB */ else { jpeg_read_scanlines(&ijd[i], jinarray, 1); memcpy(jp, jinrow + image_offset[i] * 3, iw3); } jp += iw3; } jpeg_write_scanlines(&jc, joutarray, 1); /* write one scanline */ } jpeg_finish_compress(&jc); fclose(f); /* close shop */ for (i = 0; i < n_imgnums; i++) fclose(file[i]); return 0; } boolean load_images(void) { FILE *f; char *file, *sp, line[MAX_LINE]; file = cd_compare ? CD_LIST : IMAGE_LIST; if (!((f = fopen(file, "r")))) { printf("couldn't open images list '%s'\n", file); return FALSE; } while (fgets(line, sizeof(line), f)) { if (n_images == MAX_CMPLIST) { puts("Too many images!"); return FALSE; } if (!((sp = strchr(line, ' ')))) continue; *sp = 0; /* cut off color and color name */ strcpy(images[n_images++], line); } return TRUE; } boolean parse_query(char *qs) { char *p, *eq, *numbers, *comma, *from, *what; int imgnum, n; float cdnum; if (!(eq = strchr(qs, '='))) { puts("

Internal error, malformed QUERY_STRING

"); printf("

"%s"

", qs); cat_file(JPGCMP_TAIL); } cd_compare = !strncmp(qs, "cd", 2); what = cd_compare ? "CD" : "image"; numbers = eq + 1; /* list of image#s sep by commas */ sub_smaller("%2C", ",", numbers); /* decode %2C into comma */ for (p = numbers; *p; p++) /* decode +'s into comma */ if ((*p == '+')) *p = ','; for (from = numbers; from; from = comma) { if ((comma = strchr(from, ','))) *comma++ = 0; if (cd_compare) { if (!(cdnum = atof(from))) { printf("Invalid CD# '%s'\n", from); return FALSE; } if (n_imgnums == MAX_IMAGE) { printf("Too many CD#s, max %d\n", MAX_IMAGE); return FALSE; } cdnums[n_cdnums++] = cdnum; } else { if (!(imgnum = atoi(from))) { printf("Invalid image# '%s'\n", from); return FALSE; } if (n_imgnums == MAX_IMAGE) { printf("Too many image#s, max %d\n", MAX_IMAGE); return FALSE; } imgnums[n_imgnums++] = imgnum; } } n = cd_compare ? n_cdnums : n_imgnums; if (n < 2) { printf("

Two or more %s numbers (separated " "by spaces or commas) are required

", what); printf("

"%s"

", numbers); return FALSE; } return TRUE; } boolean parse_args(int argc, char *argv[]) { char *a; int i, imgnum; if (argc < 3) { puts("usage: jpgcmp [...]"); return FALSE; } for (i = 1; i < argc; i++) { a = argv[i]; if (!(imgnum = atoi(a))) { printf("Invalid image# '%s'\n", a); return FALSE; } if (n_imgnums == MAX_IMAGE) { printf("Too many image#s, max %d\n", MAX_IMAGE); return FALSE; } imgnums[n_imgnums++] = imgnum; } if (n_imgnums < 2) { puts("Two or more image numbers are required"); return FALSE; } return TRUE; } void sub_smaller(char *oldsub, char *newsub, char *buf) { int newlen = strlen(newsub), oldlen = strlen(oldsub); char *to, *from, *p, *old; to = from = buf; while ((p = strstr(from, oldsub))) { if (newlen) memcpy(p, newsub, newlen); to = p + newlen; from = old = p + oldlen; while ((*to++ = *old++)) ; } } boolean check_numbers() { char cd[32]; int i, j; if (cd_compare) { for (i = 0; i < n_cdnums; i++) { sprintf(cd, "/%g/", cdnums[i]); for (j = 0; j < n_images; j++) { if (strstr(images[j], cd)) break; } if (j == n_images) { printf("No image is available for CD# %g\n", cdnums[i]); return FALSE; } imgnums[i] = j + 1; } n_imgnums = n_cdnums; } else { for (i = 0; i < n_imgnums; i++) { if (imgnums[i] > n_images) { printf("Image# %d is out of range 1 - %d\n", imgnums[i], n_images); return FALSE; } } } 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); } /* $Id: jpgcmp.c,v 1.4 2005/03/13 19:13:55 ian Exp $ */