#include #include #include #include #include #include #include #include "pcd.h" char pcd_rotor[] = {'-', '\\', '|', '/'}; int pcd_img_start[] = {0 /*dummy */ , 8192, 47104, 196608}; int pcd_def_width[] = {0 /*dummy */ , 192, 384, 768, 1536, 3072, 6144}; int pcd_def_height[] = {0 /*dummy */ , 128, 256, 512, 1024, 2048, 4096}; char pcd_errmsg[512]; int pcd_open(struct PCD_IMAGE *img, char *filename) { int fd; pcd_get_LUT_init(); memset(img, 0, sizeof(struct PCD_IMAGE)); fd = open(filename, O_RDONLY); if (-1 == fd) { sprintf(pcd_errmsg, "open %s: %s", filename, strerror(errno)); return -1; } img->size = lseek(fd, 0, SEEK_END); lseek(fd, 0, SEEK_SET); img->mmap = mmap(NULL, img->size, PROT_READ, MAP_SHARED, fd, 0); if ((void *) -1 == img->mmap) { sprintf(pcd_errmsg, "mmap %s: %s", filename, strerror(errno)); pcd_close(img); return -1; } close(fd); if (0 == strncmp("PCD_OPA", img->mmap, 7)) { /* this is the thumbnails file */ img->thumbnails = (int) img->mmap[10] << 8 | (int) img->mmap[11]; } else { if (img->size < 786432) { sprintf(pcd_errmsg, "%s: probably not a PhotoCD image (too small)", filename); pcd_close(img); return -1; } } return img->thumbnails; } int pcd_get_rot(struct PCD_IMAGE *img, int nr) { if (img->thumbnails) { return img->mmap[12 + nr] & 3; } else { return img->mmap[0x48] & 3; } } int pcd_get_maxres(struct PCD_IMAGE *img) { if (img->thumbnails) { return 1; } else { if (img->size == 786432) return 3; else return 5; } } int pcd_select(struct PCD_IMAGE *img, int res, int nr, int gray, int verbose, int rot, int *left, int *top, int *width, int *height) { int y; unsigned char *ptr; /* free old stuff */ pcd_free(img); /* sanity checks... */ if (0 == img->thumbnails) { if (res < 1 || res > 5) { sprintf(pcd_errmsg, "invalid resolution (%i) specified", res); return -1; } if (img->size == 786432 && res > 3) { sprintf(pcd_errmsg, "PhotoCD file contains only the three lower resolutions"); return -1; } } else { if (nr < 0 || nr >= img->thumbnails) { sprintf(pcd_errmsg, "thumbnail number (%i) out of range", nr); return -1; } } /* width/height == 0: fill in default image size */ if (*left == 0 && *width == 0) *width = PCD_WIDTH(res, rot); if (*top == 0 && *height == 0) *height = PCD_HEIGHT(res, rot); if (5 == res) *left &= ~7, *top &= ~7, *width &= ~7, *height &= ~7; else if (4 == res) *left &= ~3, *top &= ~3, *width &= ~3, *height &= ~3; else *left &= ~1, *top &= ~1, *width &= ~1, *height &= ~1; if (*left < 0 || *top < 0 || *width < 1 || *height < 1 || *left + *width > PCD_WIDTH(res, rot) || *top + *height > PCD_HEIGHT(res, rot)) { sprintf(pcd_errmsg, "specified area (%ix%i+%i+%i) invalid", *width, *height, *left, *top); return -1; } /* recalc coordinates (rotation) */ switch (rot) { case 0: /* none */ img->left = *left; img->top = *top; img->width = *width; img->height = *height; break; case 1: /* 90° ccw */ img->left = PCD_HEIGHT(res, rot) - *top - *height; img->top = *left; img->width = *height; img->height = *width; break; case 2: /* 180° */ img->left = PCD_WIDTH(res, rot) - *left - *width; img->top = PCD_HEIGHT(res, rot) - *top - *height; img->width = *width; img->height = *height; break; case 3: /* 90° cw */ img->left = *top; img->top = PCD_WIDTH(res, rot) - *left - *width; img->width = *height; img->height = *width; break; default: sprintf(pcd_errmsg, "specified orientation (%i) invalid", rot); return -1; } /* prepeare */ img->res = res; img->nr = nr; img->gray = gray; img->verbose = verbose; img->rot = rot; img->luma = malloc(img->height * sizeof(unsigned char *)); img->red = malloc(img->height * sizeof(unsigned char *) >> 1); img->blue = malloc(img->height * sizeof(unsigned char *) >> 1); if (img->luma == NULL || img->red == NULL || img->blue == NULL) { sprintf(pcd_errmsg, "out of memory (malloc failed)"); pcd_free(img); return -1; } if (res <= 3) { /* just fill in pointers */ if (img->thumbnails) { ptr = img->mmap + 10240 + 36864 * nr + (pcd_def_width[res] >> 1) * 3 * img->top; } else { ptr = img->mmap + pcd_img_start[res] + (pcd_def_width[res] >> 1) * 3 * img->top; } for (y = 0; y < img->height; y += 2, ptr += (pcd_def_width[res] >> 1) * 6) { img->luma[y] = ptr + img->left; img->luma[y + 1] = ptr + img->left + (pcd_def_width[res] >> 1) * 2; img->blue[y >> 1] = ptr + (img->left >> 1) + (pcd_def_width[res] >> 1) * 4; img->red[y >> 1] = ptr + (img->left >> 1) + (pcd_def_width[res] >> 1) * 5; } } else { /* high res, have to malloc memory */ img->data = malloc(img->width * img->height * 3 / 2); if (img->data == NULL) { sprintf(pcd_errmsg, "out of memory (malloc failed)"); pcd_free(img); return -1; } ptr = img->data; for (y = 0; y < img->height; y++, ptr += img->width) img->luma[y] = ptr; for (y = 0; y < img->height >> 1; y++, ptr += img->width >> 1) img->blue[y] = ptr; for (y = 0; y < img->height >> 1; y++, ptr += img->width >> 1) img->red[y] = ptr; } return 0; } int pcd_free(struct PCD_IMAGE *img) { img->res = 0; if (img->data) free(img->data); if (img->luma) free(img->luma); if (img->red) free(img->red); if (img->blue) free(img->blue); if (img->seq1) free(img->seq1); if (img->len1) free(img->len1); if (img->seq2) free(img->seq2); if (img->len2) free(img->len2); if (img->seq3) free(img->seq3); if (img->len3) free(img->len3); img->data = NULL; img->luma = img->red = img->blue = NULL; img->seq1 = img->seq2 = img->seq3 = NULL; img->len1 = img->len2 = img->len3 = NULL; return 0; } int pcd_close(struct PCD_IMAGE *img) { pcd_free(img); munmap(img->mmap, img->size); memset(img, 0, sizeof(struct PCD_IMAGE)); return 0; }