1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <unistd.h>
5 #include <fcntl.h>
6 #include <errno.h>
7 #include <sys/mman.h>
8
9 #include "pcd.h"
10
11 char pcd_rotor[] =
12 {'-', '\\', '|', '/'};
13 int pcd_img_start[] =
14 {0 /*dummy */ , 8192, 47104, 196608};
15 int pcd_def_width[] =
16 {0 /*dummy */ , 192, 384, 768, 1536, 3072, 6144};
17 int pcd_def_height[] =
18 {0 /*dummy */ , 128, 256, 512, 1024, 2048, 4096};
19 char pcd_errmsg[512];
20
21 int
pcd_open(struct PCD_IMAGE * img,char * filename)22 pcd_open(struct PCD_IMAGE *img, char *filename)
23 {
24 int fd;
25
26 pcd_get_LUT_init();
27 memset(img, 0, sizeof(struct PCD_IMAGE));
28
29 fd = open(filename, O_RDONLY);
30 if (-1 == fd) {
31 sprintf(pcd_errmsg, "open %s: %s", filename, strerror(errno));
32 return -1;
33 }
34 img->size = lseek(fd, 0, SEEK_END);
35 lseek(fd, 0, SEEK_SET);
36 img->mmap = mmap(NULL, img->size, PROT_READ, MAP_SHARED, fd, 0);
37 if ((void *) -1 == img->mmap) {
38 sprintf(pcd_errmsg, "mmap %s: %s", filename, strerror(errno));
39 pcd_close(img);
40 return -1;
41 }
42 close(fd);
43 if (0 == strncmp("PCD_OPA", img->mmap, 7)) {
44 /* this is the thumbnails file */
45 img->thumbnails = (int) img->mmap[10] << 8 | (int) img->mmap[11];
46 } else {
47 if (img->size < 786432) {
48 sprintf(pcd_errmsg, "%s: probably not a PhotoCD image (too small)",
49 filename);
50 pcd_close(img);
51 return -1;
52 }
53 }
54 return img->thumbnails;
55 }
56
57 int
pcd_get_rot(struct PCD_IMAGE * img,int nr)58 pcd_get_rot(struct PCD_IMAGE *img, int nr)
59 {
60 if (img->thumbnails) {
61 return img->mmap[12 + nr] & 3;
62 } else {
63 return img->mmap[0x48] & 3;
64 }
65 }
66
67 int
pcd_get_maxres(struct PCD_IMAGE * img)68 pcd_get_maxres(struct PCD_IMAGE *img)
69 {
70 if (img->thumbnails) {
71 return 1;
72 } else {
73 if (img->size == 786432)
74 return 3;
75 else
76 return 5;
77 }
78 }
79
80 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)81 pcd_select(struct PCD_IMAGE *img, int res, int nr, int gray, int verbose,
82 int rot, int *left, int *top, int *width, int *height)
83 {
84 int y;
85 unsigned char *ptr;
86
87 /* free old stuff */
88 pcd_free(img);
89
90 /* sanity checks... */
91 if (0 == img->thumbnails) {
92 if (res < 1 || res > 5) {
93 sprintf(pcd_errmsg, "invalid resolution (%i) specified", res);
94 return -1;
95 }
96 if (img->size == 786432 && res > 3) {
97 sprintf(pcd_errmsg,
98 "PhotoCD file contains only the three lower resolutions");
99 return -1;
100 }
101 } else {
102 if (nr < 0 || nr >= img->thumbnails) {
103 sprintf(pcd_errmsg,
104 "thumbnail number (%i) out of range", nr);
105 return -1;
106 }
107 }
108
109 /* width/height == 0: fill in default image size */
110 if (*left == 0 && *width == 0)
111 *width = PCD_WIDTH(res, rot);
112 if (*top == 0 && *height == 0)
113 *height = PCD_HEIGHT(res, rot);
114
115 if (5 == res)
116 *left &= ~7, *top &= ~7, *width &= ~7, *height &= ~7;
117 else if (4 == res)
118 *left &= ~3, *top &= ~3, *width &= ~3, *height &= ~3;
119 else
120 *left &= ~1, *top &= ~1, *width &= ~1, *height &= ~1;
121 if (*left < 0 || *top < 0 ||
122 *width < 1 || *height < 1 ||
123 *left + *width > PCD_WIDTH(res, rot) ||
124 *top + *height > PCD_HEIGHT(res, rot)) {
125 sprintf(pcd_errmsg, "specified area (%ix%i+%i+%i) invalid",
126 *width, *height, *left, *top);
127 return -1;
128 }
129 /* recalc coordinates (rotation) */
130 switch (rot) {
131 case 0: /* none */
132 img->left = *left;
133 img->top = *top;
134 img->width = *width;
135 img->height = *height;
136 break;
137 case 1: /* 90� ccw */
138 img->left = PCD_HEIGHT(res, rot) - *top - *height;
139 img->top = *left;
140 img->width = *height;
141 img->height = *width;
142 break;
143 case 2: /* 180� */
144 img->left = PCD_WIDTH(res, rot) - *left - *width;
145 img->top = PCD_HEIGHT(res, rot) - *top - *height;
146 img->width = *width;
147 img->height = *height;
148 break;
149 case 3: /* 90� cw */
150 img->left = *top;
151 img->top = PCD_WIDTH(res, rot) - *left - *width;
152 img->width = *height;
153 img->height = *width;
154 break;
155 default:
156 sprintf(pcd_errmsg, "specified orientation (%i) invalid", rot);
157 return -1;
158 }
159 /* prepeare */
160 img->res = res;
161 img->nr = nr;
162 img->gray = gray;
163 img->verbose = verbose;
164 img->rot = rot;
165 img->luma = malloc(img->height * sizeof(unsigned char *));
166 img->red = malloc(img->height * sizeof(unsigned char *) >> 1);
167 img->blue = malloc(img->height * sizeof(unsigned char *) >> 1);
168
169 if (img->luma == NULL ||
170 img->red == NULL ||
171 img->blue == NULL) {
172 sprintf(pcd_errmsg, "out of memory (malloc failed)");
173 pcd_free(img);
174 return -1;
175 }
176 if (res <= 3) {
177 /* just fill in pointers */
178 if (img->thumbnails) {
179 ptr = img->mmap + 10240 + 36864 * nr +
180 (pcd_def_width[res] >> 1) * 3 * img->top;
181 } else {
182 ptr = img->mmap + pcd_img_start[res] +
183 (pcd_def_width[res] >> 1) * 3 * img->top;
184 }
185 for (y = 0; y < img->height; y += 2, ptr += (pcd_def_width[res] >> 1) * 6) {
186 img->luma[y] = ptr + img->left;
187 img->luma[y + 1] = ptr + img->left + (pcd_def_width[res] >> 1) * 2;
188 img->blue[y >> 1] = ptr + (img->left >> 1) + (pcd_def_width[res] >> 1) * 4;
189 img->red[y >> 1] = ptr + (img->left >> 1) + (pcd_def_width[res] >> 1) * 5;
190 }
191 } else {
192 /* high res, have to malloc memory */
193 img->data = malloc(img->width * img->height * 3 / 2);
194 if (img->data == NULL) {
195 sprintf(pcd_errmsg, "out of memory (malloc failed)");
196 pcd_free(img);
197 return -1;
198 }
199 ptr = img->data;
200 for (y = 0; y < img->height; y++, ptr += img->width)
201 img->luma[y] = ptr;
202 for (y = 0; y < img->height >> 1; y++, ptr += img->width >> 1)
203 img->blue[y] = ptr;
204 for (y = 0; y < img->height >> 1; y++, ptr += img->width >> 1)
205 img->red[y] = ptr;
206 }
207 return 0;
208 }
209
210 int
pcd_free(struct PCD_IMAGE * img)211 pcd_free(struct PCD_IMAGE *img)
212 {
213 img->res = 0;
214 if (img->data)
215 free(img->data);
216 if (img->luma)
217 free(img->luma);
218 if (img->red)
219 free(img->red);
220 if (img->blue)
221 free(img->blue);
222 if (img->seq1)
223 free(img->seq1);
224 if (img->len1)
225 free(img->len1);
226 if (img->seq2)
227 free(img->seq2);
228 if (img->len2)
229 free(img->len2);
230 if (img->seq3)
231 free(img->seq3);
232 if (img->len3)
233 free(img->len3);
234 img->data = NULL;
235 img->luma = img->red = img->blue = NULL;
236 img->seq1 = img->seq2 = img->seq3 = NULL;
237 img->len1 = img->len2 = img->len3 = NULL;
238 return 0;
239 }
240
241 int
pcd_close(struct PCD_IMAGE * img)242 pcd_close(struct PCD_IMAGE *img)
243 {
244 pcd_free(img);
245 munmap(img->mmap, img->size);
246 memset(img, 0, sizeof(struct PCD_IMAGE));
247
248 return 0;
249 }
250