aboutsummaryrefslogtreecommitdiffstats
path: root/file.c
diff options
context:
space:
mode:
Diffstat (limited to 'file.c')
-rw-r--r--file.c249
1 files changed, 249 insertions, 0 deletions
diff --git a/file.c b/file.c
new file mode 100644
index 0000000..af9576e
--- /dev/null
+++ b/file.c
@@ -0,0 +1,249 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/mman.h>
+
+#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;
+}