diff options
Diffstat (limited to '')
-rw-r--r-- | libraries/evas/src/modules/loaders/psd/evas_image_load_psd.c | 976 |
1 files changed, 976 insertions, 0 deletions
diff --git a/libraries/evas/src/modules/loaders/psd/evas_image_load_psd.c b/libraries/evas/src/modules/loaders/psd/evas_image_load_psd.c new file mode 100644 index 0000000..5a85e17 --- /dev/null +++ b/libraries/evas/src/modules/loaders/psd/evas_image_load_psd.c | |||
@@ -0,0 +1,976 @@ | |||
1 | #define _XOPEN_SOURCE | ||
2 | |||
3 | #ifdef HAVE_CONFIG_H | ||
4 | # include <config.h> | ||
5 | #endif | ||
6 | |||
7 | #include <stdlib.h> | ||
8 | #include <stdio.h> | ||
9 | #include <string.h> | ||
10 | #include <errno.h> | ||
11 | |||
12 | #include <math.h> | ||
13 | #include <sys/types.h> | ||
14 | #include <sys/stat.h> | ||
15 | #include <fcntl.h> | ||
16 | |||
17 | #ifdef HAVE_NETINET_IN_H | ||
18 | # include <netinet/in.h> | ||
19 | #endif | ||
20 | |||
21 | #ifdef HAVE_UNISTD_H | ||
22 | # include <unistd.h> | ||
23 | #endif | ||
24 | |||
25 | #ifdef HAVE_EVIL | ||
26 | # include <Evil.h> | ||
27 | #endif | ||
28 | |||
29 | #include "evas_common.h" | ||
30 | #include "evas_private.h" | ||
31 | |||
32 | typedef struct _PSD_Header PSD_Header; | ||
33 | |||
34 | typedef enum _PSD_Mode | ||
35 | { | ||
36 | PSD_GREYSCALE = 1, | ||
37 | PSD_INDEXED = 2, | ||
38 | PSD_RGB = 3, | ||
39 | PSD_CMYK = 4 | ||
40 | } PSD_Mode; | ||
41 | |||
42 | struct _PSD_Header | ||
43 | { | ||
44 | unsigned char signature[4]; | ||
45 | unsigned short version; | ||
46 | unsigned char reserved[9]; | ||
47 | unsigned short channels; | ||
48 | unsigned int height; | ||
49 | unsigned int width; | ||
50 | unsigned short depth; | ||
51 | |||
52 | unsigned short channel_num; | ||
53 | |||
54 | PSD_Mode mode; | ||
55 | }; | ||
56 | |||
57 | enum { | ||
58 | READ_COMPRESSED_SUCCESS, | ||
59 | READ_COMPRESSED_ERROR_FILE_CORRUPT, | ||
60 | READ_COMPRESSED_ERROR_FILE_READ_ERROR | ||
61 | }; | ||
62 | |||
63 | static Eina_Bool get_compressed_channels_length(PSD_Header *Head, | ||
64 | FILE *file, | ||
65 | unsigned short *rle_table, | ||
66 | unsigned int *chanlen); | ||
67 | |||
68 | static int | ||
69 | read_ushort(FILE *file, unsigned short *ret) | ||
70 | { | ||
71 | unsigned char b[2]; | ||
72 | if (fread(b, sizeof(unsigned char), 2, file) != 2) return 0; | ||
73 | // FIXME: need to check order | ||
74 | *ret = (b[0] << 8) | b[1]; | ||
75 | return 1; | ||
76 | } | ||
77 | |||
78 | static int | ||
79 | read_uint(FILE *file, unsigned int *ret) | ||
80 | { | ||
81 | unsigned char b[4]; | ||
82 | if (fread(b, sizeof(unsigned char), 4, file) != 4) return 0; | ||
83 | // FIXME: need to check order | ||
84 | *ret = ARGB_JOIN(b[0], b[1], b[2], b[3]); | ||
85 | return 1; | ||
86 | } | ||
87 | |||
88 | // Internal function used to get the Psd header from the current file. | ||
89 | Eina_Bool | ||
90 | psd_get_header(PSD_Header *header, FILE * file) | ||
91 | { | ||
92 | unsigned short tmp; | ||
93 | |||
94 | #define CHECK_RET(Call, Value) \ | ||
95 | if (Call != Value) return EINA_FALSE; | ||
96 | |||
97 | CHECK_RET(fread(header->signature, sizeof (unsigned char), 4, file), 4); | ||
98 | CHECK_RET(read_ushort(file, &header->version), 1); | ||
99 | CHECK_RET(fread(header->reserved, sizeof (unsigned char), 6, file), 6); | ||
100 | CHECK_RET(read_ushort(file, &header->channels), 1); | ||
101 | CHECK_RET(read_uint(file, &header->height), 1); | ||
102 | CHECK_RET(read_uint(file, &header->width), 1); | ||
103 | CHECK_RET(read_ushort(file, &header->depth), 1); | ||
104 | |||
105 | CHECK_RET(read_ushort(file, &tmp), 1); | ||
106 | header->mode = tmp; | ||
107 | |||
108 | #undef CHECK_RET | ||
109 | |||
110 | /* fprintf(stderr, "<<<<<<<<<<<\nsignature : %c%c%c%c\n", */ | ||
111 | /* header->signature[0], */ | ||
112 | /* header->signature[1], */ | ||
113 | /* header->signature[2], */ | ||
114 | /* header->signature[3]); */ | ||
115 | /* fprintf(stderr, "version : %i\n", header->version); */ | ||
116 | /* fprintf(stderr, "channels : %i\n", header->channels); */ | ||
117 | /* fprintf(stderr, "width x height : %dx%d\n", header->width, header->height); */ | ||
118 | /* fprintf(stderr, "depth : %i\n", header->depth); */ | ||
119 | /* fprintf(stderr, "mode : %i\n>>>>>>>>>>>>\n", header->mode); */ | ||
120 | |||
121 | return EINA_TRUE; | ||
122 | } | ||
123 | |||
124 | |||
125 | // Internal function used to check if the HEADER is a valid Psd header. | ||
126 | Eina_Bool | ||
127 | is_psd(PSD_Header *header) | ||
128 | { | ||
129 | if (strncmp((char*)header->signature, "8BPS", 4)) | ||
130 | return EINA_FALSE; | ||
131 | if (header->version != 1) | ||
132 | return EINA_FALSE; | ||
133 | if (header->channels < 1 || header->channels > 24) | ||
134 | return EINA_FALSE; | ||
135 | if (header->height < 1 || header->width < 1) | ||
136 | return EINA_FALSE; | ||
137 | if (header->depth != 1 && header->depth != 8 && header->depth != 16) | ||
138 | return EINA_FALSE; | ||
139 | |||
140 | return EINA_TRUE; | ||
141 | } | ||
142 | |||
143 | static Eina_Bool | ||
144 | evas_image_load_file_head_psd(Image_Entry *ie, const char *FileName, | ||
145 | const char *key __UNUSED__, int *error) | ||
146 | { | ||
147 | FILE *f; | ||
148 | PSD_Header header; | ||
149 | Eina_Bool correct; | ||
150 | |||
151 | *error = EVAS_LOAD_ERROR_NONE; | ||
152 | |||
153 | f = fopen(FileName, "rb"); | ||
154 | if (f == NULL) | ||
155 | { | ||
156 | *error = EVAS_LOAD_ERROR_DOES_NOT_EXIST; | ||
157 | return EINA_FALSE; | ||
158 | } | ||
159 | correct = psd_get_header(&header, f); | ||
160 | fclose(f); | ||
161 | |||
162 | if (!correct || !is_psd(&header)) | ||
163 | { | ||
164 | *error = EVAS_LOAD_ERROR_UNKNOWN_FORMAT; | ||
165 | return EINA_FALSE; | ||
166 | } | ||
167 | |||
168 | ie->w = header.width; | ||
169 | ie->h = header.height; | ||
170 | if (header.channels == 3) ie->flags.alpha = 0; | ||
171 | else ie->flags.alpha = 1; | ||
172 | |||
173 | return EINA_TRUE; | ||
174 | } | ||
175 | |||
176 | static unsigned int | ||
177 | read_compressed_channel(FILE* file, | ||
178 | const unsigned int channel_length __UNUSED__, | ||
179 | unsigned int size, | ||
180 | unsigned char* channel) | ||
181 | { | ||
182 | // FIXME: what does channel_length means, and why is it not used | ||
183 | unsigned int i; | ||
184 | char headbyte, c; | ||
185 | |||
186 | #define CHECK_RET(Call, Value) \ | ||
187 | if (Call != Value) return READ_COMPRESSED_ERROR_FILE_READ_ERROR; | ||
188 | |||
189 | for (i = 0; i < size; ) | ||
190 | { | ||
191 | CHECK_RET(fread(&headbyte, 1, 1, file), 1); | ||
192 | |||
193 | if (headbyte >= 0) | ||
194 | { | ||
195 | if (i + headbyte > size) | ||
196 | return READ_COMPRESSED_ERROR_FILE_CORRUPT; | ||
197 | |||
198 | CHECK_RET(fread(channel + i, headbyte + 1, 1, file), 1); | ||
199 | |||
200 | i += headbyte + 1; | ||
201 | } | ||
202 | else if (headbyte >= -127 && headbyte <= -1) | ||
203 | { | ||
204 | int run; | ||
205 | |||
206 | CHECK_RET(fread(&c, 1, 1, file), 1); | ||
207 | |||
208 | run = c; | ||
209 | /* if (run == -1) */ | ||
210 | /* return READ_COMPRESSED_ERROR_FILE_READ_ERROR; */ | ||
211 | |||
212 | if (i + (-headbyte + 1) > size) | ||
213 | return READ_COMPRESSED_ERROR_FILE_CORRUPT; | ||
214 | |||
215 | memset(channel + i, run, -headbyte + 1); | ||
216 | i += -headbyte + 1; | ||
217 | } | ||
218 | } | ||
219 | |||
220 | #undef CHECK_RET | ||
221 | |||
222 | return READ_COMPRESSED_SUCCESS; | ||
223 | } | ||
224 | |||
225 | |||
226 | Eina_Bool | ||
227 | psd_get_data(Image_Entry *ie __UNUSED__, | ||
228 | PSD_Header *head, | ||
229 | FILE *f, | ||
230 | unsigned char *buffer, Eina_Bool compressed, | ||
231 | int *error) | ||
232 | { | ||
233 | unsigned int c, x, y, numchan, bps, bpc, bpp; | ||
234 | unsigned int pixels_count; | ||
235 | unsigned char *channel = NULL; | ||
236 | unsigned char *data = NULL; | ||
237 | |||
238 | // Added 01-07-2009: This is needed to correctly load greyscale and | ||
239 | // paletted images. | ||
240 | switch (head->mode) | ||
241 | { | ||
242 | case PSD_GREYSCALE: | ||
243 | case PSD_INDEXED: | ||
244 | numchan = 1; | ||
245 | break; | ||
246 | default: | ||
247 | numchan = 3; | ||
248 | } | ||
249 | |||
250 | bpp = head->channels; | ||
251 | bpc = head->depth / 8; | ||
252 | pixels_count = head->width * head->height; | ||
253 | |||
254 | data = malloc(sizeof (unsigned char) * pixels_count * bpp); | ||
255 | if (!data) return EINA_FALSE; | ||
256 | |||
257 | channel = malloc(sizeof (unsigned char) * pixels_count * bpc); | ||
258 | if (!channel) | ||
259 | { | ||
260 | free(data); | ||
261 | return EINA_FALSE; | ||
262 | } | ||
263 | |||
264 | bps = head->width * head->channels * bpc; | ||
265 | // @TODO: Add support for this in, though I have yet to run across a .psd | ||
266 | // file that uses this. | ||
267 | if (compressed && bpc == 2) | ||
268 | { | ||
269 | free(data); | ||
270 | free(channel); | ||
271 | fprintf(stderr, "unsupported file format.\n"); | ||
272 | *error = EVAS_LOAD_ERROR_UNKNOWN_FORMAT; | ||
273 | return EINA_FALSE; | ||
274 | } | ||
275 | |||
276 | #define CHECK_RET(Call, Value) \ | ||
277 | if (Call != Value) \ | ||
278 | { \ | ||
279 | free(data); \ | ||
280 | free(channel); \ | ||
281 | return EINA_FALSE; \ | ||
282 | } | ||
283 | |||
284 | if (!compressed) | ||
285 | { | ||
286 | if (bpc == 1) | ||
287 | { | ||
288 | for (c = 0; c < numchan; c++) | ||
289 | { | ||
290 | unsigned char *tmp = channel; | ||
291 | |||
292 | CHECK_RET(fread(tmp, pixels_count, 1, f), 1); | ||
293 | |||
294 | for (y = 0; y < head->height * bps; y += bps) | ||
295 | { | ||
296 | for (x = 0; x < bps; x += bpp, tmp++) | ||
297 | { | ||
298 | data[y + x + c] = *tmp; | ||
299 | } | ||
300 | } | ||
301 | } | ||
302 | |||
303 | // Accumulate any remaining channels into a single alpha channel | ||
304 | //@TODO: This needs to be changed for greyscale images. | ||
305 | for (; c < head->channels; c++) | ||
306 | { | ||
307 | unsigned char *tmp = channel; | ||
308 | |||
309 | CHECK_RET(fread(channel, pixels_count, 1, f), 1); | ||
310 | |||
311 | for (y = 0; y < head->height * bps; y += bps) | ||
312 | { | ||
313 | for (x = 0; x < bps; x += bpp, tmp++) | ||
314 | { | ||
315 | unsigned short newval; | ||
316 | |||
317 | // previous formula was : (old / 255 * new / 255) * 255 | ||
318 | newval = (*tmp) * data[y + x + 3]; | ||
319 | |||
320 | data[y + x + 3] = newval >> 8; | ||
321 | } | ||
322 | } | ||
323 | } | ||
324 | } | ||
325 | else | ||
326 | { | ||
327 | int bps2; | ||
328 | |||
329 | bps2 = bps / 2; | ||
330 | |||
331 | // iCurImage->Bpc == 2 | ||
332 | for (c = 0; c < numchan; c++) | ||
333 | { | ||
334 | unsigned short *shortptr = (unsigned short*) channel; | ||
335 | |||
336 | CHECK_RET(fread(channel, pixels_count * 2, 1, f), 1); | ||
337 | |||
338 | for (y = 0; y < head->height * bps2; y += bps2) | ||
339 | { | ||
340 | for (x = 0; x < (unsigned int)bps2; x += bpp, shortptr++) | ||
341 | { | ||
342 | ((unsigned short*)data)[y + x + c] = *shortptr; | ||
343 | } | ||
344 | } | ||
345 | } | ||
346 | |||
347 | // Accumulate any remaining channels into a single alpha channel | ||
348 | //@TODO: This needs to be changed for greyscale images. | ||
349 | for (; c < head->channels; c++) | ||
350 | { | ||
351 | unsigned short *shortptr = (unsigned short*) channel; | ||
352 | |||
353 | CHECK_RET(fread(channel, pixels_count * 2, 1, f), 1); | ||
354 | |||
355 | for (y = 0; y < head->height * bps2; y += bps2) | ||
356 | { | ||
357 | for (x = 0; x < (unsigned int)bps2; x += bpp, shortptr) | ||
358 | { | ||
359 | unsigned int newval; | ||
360 | |||
361 | newval = *shortptr * ((unsigned short*)data)[y + x + 3]; | ||
362 | |||
363 | ((unsigned short*)data)[y + x + 3] = newval >> 16; | ||
364 | } | ||
365 | } | ||
366 | } | ||
367 | } | ||
368 | } | ||
369 | else | ||
370 | { | ||
371 | unsigned short *rle_table; | ||
372 | unsigned int *chanlen; | ||
373 | |||
374 | rle_table = alloca(head->height * head->channel_num * sizeof (unsigned short)); | ||
375 | chanlen = alloca(head->channel_num * sizeof (unsigned int)); | ||
376 | if (!get_compressed_channels_length(head, f, rle_table, chanlen)) | ||
377 | goto file_read_error; | ||
378 | |||
379 | for (c = 0; c < numchan; c++) | ||
380 | { | ||
381 | unsigned char *tmp = channel; | ||
382 | int err; | ||
383 | |||
384 | err = read_compressed_channel(f, | ||
385 | chanlen[c], | ||
386 | pixels_count, | ||
387 | channel); | ||
388 | if (err == READ_COMPRESSED_ERROR_FILE_CORRUPT) | ||
389 | goto file_corrupt; | ||
390 | else if (err == READ_COMPRESSED_ERROR_FILE_READ_ERROR) | ||
391 | goto file_read_error; | ||
392 | |||
393 | for (y = 0; y < head->height * bps; y += bps) | ||
394 | { | ||
395 | for (x = 0; x < bps; x += bpp, tmp++) | ||
396 | { | ||
397 | data[y + x + c] = *tmp; | ||
398 | } | ||
399 | } | ||
400 | } | ||
401 | |||
402 | // Initialize the alpha channel to solid | ||
403 | //@TODO: This needs to be changed for greyscale images. | ||
404 | if (head->channels >= 4) | ||
405 | { | ||
406 | for (y = 0; y < head->height * bps; y += bps) | ||
407 | { | ||
408 | for (x = 0; x < bps; x += bpp) | ||
409 | { | ||
410 | data[y + x + 3] = 255; | ||
411 | } | ||
412 | } | ||
413 | |||
414 | for (; c < head->channels; c++) | ||
415 | { | ||
416 | unsigned char *tmp = channel; | ||
417 | int err; | ||
418 | |||
419 | err = read_compressed_channel(f, | ||
420 | chanlen[c], | ||
421 | pixels_count, | ||
422 | channel); | ||
423 | if (err == READ_COMPRESSED_ERROR_FILE_CORRUPT) | ||
424 | goto file_corrupt; | ||
425 | else if (err == READ_COMPRESSED_ERROR_FILE_READ_ERROR) | ||
426 | goto file_read_error; | ||
427 | |||
428 | for (y = 0; y < head->height * bps; y += bps) | ||
429 | { | ||
430 | for (x = 0; x < bps; x += bpp, tmp++) | ||
431 | { | ||
432 | unsigned short newval; | ||
433 | |||
434 | newval = *tmp * data[y + x + 3]; | ||
435 | |||
436 | data[y + x + 3] = newval >> 8; | ||
437 | } | ||
438 | } | ||
439 | } | ||
440 | } | ||
441 | } | ||
442 | |||
443 | if (bpp == 3) | ||
444 | { | ||
445 | for (x = 0; x < pixels_count; x++) | ||
446 | { | ||
447 | buffer[x * 4 + 0] = data[x * 3 + 2]; | ||
448 | buffer[x * 4 + 1] = data[x * 3 + 1]; | ||
449 | buffer[x * 4 + 2] = data[x * 3 + 0]; | ||
450 | buffer[x * 4 + 3] = 255; | ||
451 | } | ||
452 | } | ||
453 | else | ||
454 | { | ||
455 | // BRGA to RGBA | ||
456 | for (x= 0; x < pixels_count; x++) | ||
457 | { | ||
458 | buffer[x * 4 + 0] = data[x * 4 + 2]; | ||
459 | buffer[x * 4 + 1] = data[x * 4 + 1]; | ||
460 | buffer[x * 4 + 2] = data[x * 4 + 0]; | ||
461 | buffer[x * 4 + 3] = data[x * 4 + 3]; | ||
462 | } | ||
463 | } | ||
464 | |||
465 | free(channel); | ||
466 | free(data); | ||
467 | return EINA_TRUE; | ||
468 | |||
469 | #undef CHECK_RET | ||
470 | |||
471 | file_corrupt: | ||
472 | *error = EVAS_LOAD_ERROR_CORRUPT_FILE; | ||
473 | |||
474 | file_read_error: | ||
475 | free(channel); | ||
476 | free(data); | ||
477 | |||
478 | return EINA_FALSE; | ||
479 | } | ||
480 | |||
481 | |||
482 | Eina_Bool | ||
483 | get_single_channel(Image_Entry *ie __UNUSED__, | ||
484 | PSD_Header *head, | ||
485 | FILE *f, | ||
486 | unsigned char *buffer, | ||
487 | Eina_Bool compressed) | ||
488 | { | ||
489 | unsigned int i, bpc; | ||
490 | unsigned short *tmp; | ||
491 | char headbyte; | ||
492 | int c; | ||
493 | int pixels_count; | ||
494 | |||
495 | tmp = (unsigned short*)buffer; | ||
496 | bpc = (head->depth / 8); | ||
497 | pixels_count = head->width * head->height; | ||
498 | |||
499 | #define CHECK_RET(Call, Value) \ | ||
500 | if (Call != Value) return EINA_FALSE; | ||
501 | |||
502 | if (!compressed) | ||
503 | { | ||
504 | if (bpc == 1) | ||
505 | { | ||
506 | CHECK_RET(fread(buffer, pixels_count, 1, f), 1); | ||
507 | } | ||
508 | else | ||
509 | { // Bpc == 2 | ||
510 | CHECK_RET(fread(buffer, pixels_count * 2, 1, f), 1); | ||
511 | } | ||
512 | } | ||
513 | else | ||
514 | { | ||
515 | for (i = 0; i < (unsigned int)pixels_count; ) | ||
516 | { | ||
517 | CHECK_RET(fread(&headbyte, 1, 1, f), 1); | ||
518 | |||
519 | if (headbyte >= 0) | ||
520 | { // && HeadByte <= 127 | ||
521 | CHECK_RET(fread(buffer + i, headbyte + 1, 1, f), 1); | ||
522 | |||
523 | i += headbyte + 1; | ||
524 | } | ||
525 | if (headbyte >= -127 && headbyte <= -1) | ||
526 | { | ||
527 | int run; | ||
528 | |||
529 | CHECK_RET(fread(&c, 1, 1, f), 1); | ||
530 | |||
531 | run = c; | ||
532 | if (run == -1) return EINA_FALSE; | ||
533 | |||
534 | memset(buffer + i, run, -headbyte + 1); | ||
535 | i += -headbyte + 1; | ||
536 | } | ||
537 | } | ||
538 | } | ||
539 | |||
540 | #undef CHECK_RET | ||
541 | |||
542 | return EINA_TRUE; | ||
543 | } | ||
544 | |||
545 | Eina_Bool | ||
546 | read_psd_grey(Image_Entry *ie, PSD_Header *head, FILE * f, int *error) | ||
547 | { | ||
548 | unsigned int color_mode, resource_size, misc_info; | ||
549 | unsigned short compressed; | ||
550 | unsigned int type; | ||
551 | void *surface = NULL; | ||
552 | |||
553 | *error = EVAS_LOAD_ERROR_CORRUPT_FILE; | ||
554 | |||
555 | #define CHECK_RET(Call, Value) \ | ||
556 | if (Call != Value) return EINA_FALSE; | ||
557 | |||
558 | CHECK_RET(read_uint(f, &color_mode), 1); | ||
559 | // Skip over the 'color mode data section' | ||
560 | CHECK_RET(fseek(f, color_mode, SEEK_CUR), 0); | ||
561 | |||
562 | CHECK_RET(read_uint(f, &resource_size), 1); | ||
563 | // Read the 'image resources section' | ||
564 | |||
565 | CHECK_RET(fseek(f, resource_size, SEEK_CUR), 0); | ||
566 | |||
567 | CHECK_RET(read_uint(f, &misc_info), 1); | ||
568 | CHECK_RET(fseek(f, misc_info, SEEK_CUR), 0); | ||
569 | |||
570 | CHECK_RET(read_ushort(f, &compressed), 1); | ||
571 | |||
572 | ie->w = head->width; | ||
573 | ie->h = head->height; | ||
574 | if (head->channels == 3) ie->flags.alpha = 0; | ||
575 | else ie->flags.alpha = 1; | ||
576 | |||
577 | head->channel_num = head->channels; | ||
578 | // Temporary to read only one channel...some greyscale .psd files have 2. | ||
579 | head->channels = 1; | ||
580 | |||
581 | switch (head->depth) | ||
582 | { | ||
583 | case 8: | ||
584 | type = 1; | ||
585 | break; | ||
586 | case 16: | ||
587 | type = 2; | ||
588 | break; | ||
589 | default: | ||
590 | *error = EVAS_LOAD_ERROR_UNKNOWN_FORMAT; | ||
591 | return EINA_FALSE; | ||
592 | } | ||
593 | |||
594 | evas_cache_image_surface_alloc(ie, ie->w, ie->h); | ||
595 | surface = evas_cache_image_pixels(ie); | ||
596 | if (!surface) | ||
597 | { | ||
598 | *error = EVAS_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED; | ||
599 | goto cleanup_error; | ||
600 | } | ||
601 | |||
602 | if (!psd_get_data(ie, head, f, surface, compressed, error)) | ||
603 | goto cleanup_error; | ||
604 | |||
605 | return EINA_TRUE; | ||
606 | |||
607 | #undef CHECK_RET | ||
608 | |||
609 | cleanup_error: | ||
610 | return EINA_FALSE; | ||
611 | } | ||
612 | |||
613 | |||
614 | Eina_Bool | ||
615 | read_psd_indexed(Image_Entry *ie, PSD_Header *head, FILE * f, int *error) | ||
616 | { | ||
617 | unsigned int color_mode, resource_size, misc_info; | ||
618 | unsigned short compressed; | ||
619 | void *surface; | ||
620 | |||
621 | *error = EVAS_LOAD_ERROR_CORRUPT_FILE; | ||
622 | |||
623 | #define CHECK_RET(Call, Value) \ | ||
624 | if (Call != Value) return EINA_FALSE; | ||
625 | |||
626 | CHECK_RET(read_uint(f, &color_mode), 1); | ||
627 | CHECK_RET((color_mode % 3), 0); | ||
628 | /* | ||
629 | Palette = (unsigned char*)malloc(Colormode); | ||
630 | if (Palette == NULL) | ||
631 | return EINA_FALSE; | ||
632 | if (fread(&Palette, 1, Colormode, file) != Colormode) | ||
633 | goto cleanup_error; | ||
634 | */ | ||
635 | // Skip over the 'color mode data section' | ||
636 | CHECK_RET(fseek(f, color_mode, SEEK_CUR), 0); | ||
637 | |||
638 | // Read the 'image resources section' | ||
639 | CHECK_RET(read_uint(f, &resource_size), 1); | ||
640 | CHECK_RET(fseek(f, resource_size, SEEK_CUR), 0); | ||
641 | |||
642 | CHECK_RET(read_uint(f, &misc_info), 1); | ||
643 | CHECK_RET(fseek(f, misc_info, SEEK_CUR), 0); | ||
644 | |||
645 | CHECK_RET(read_ushort(f, &compressed), 1); | ||
646 | |||
647 | if (head->channels != 1 || head->depth != 8) | ||
648 | { | ||
649 | *error = EVAS_LOAD_ERROR_UNKNOWN_FORMAT; | ||
650 | return EINA_FALSE; | ||
651 | } | ||
652 | head->channel_num = head->channels; | ||
653 | |||
654 | ie->w = head->width; | ||
655 | ie->h = head->height; | ||
656 | if (head->channels == 3) ie->flags.alpha = 0; | ||
657 | else ie->flags.alpha = 1; | ||
658 | |||
659 | evas_cache_image_surface_alloc(ie, ie->w, ie->h); | ||
660 | surface = evas_cache_image_pixels(ie); | ||
661 | if (!surface) | ||
662 | { | ||
663 | *error = EVAS_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED; | ||
664 | return EINA_FALSE; | ||
665 | } | ||
666 | |||
667 | if (!psd_get_data(ie, head, f, surface, compressed, error)) | ||
668 | return EINA_FALSE; | ||
669 | return EINA_TRUE; | ||
670 | |||
671 | #undef CHECK_RET | ||
672 | } | ||
673 | |||
674 | Eina_Bool | ||
675 | read_psd_rgb(Image_Entry *ie, PSD_Header *head, FILE *f, int *error) | ||
676 | { | ||
677 | unsigned int color_mode, resource_size, misc_info; | ||
678 | unsigned short compressed; | ||
679 | unsigned int type; | ||
680 | void *surface; | ||
681 | |||
682 | #define CHECK_RET(Call, Value) \ | ||
683 | if (Call != Value) return EINA_FALSE; | ||
684 | |||
685 | CHECK_RET(read_uint(f, &color_mode), 1); | ||
686 | // Skip over the 'color mode data section' | ||
687 | CHECK_RET(fseek(f, color_mode, SEEK_CUR), 0); | ||
688 | |||
689 | // Read the 'image resources section' | ||
690 | CHECK_RET(read_uint(f, &resource_size), 1); | ||
691 | CHECK_RET(fseek(f, resource_size, SEEK_CUR), 0); | ||
692 | |||
693 | CHECK_RET(read_uint(f, &misc_info), 1); | ||
694 | CHECK_RET(fseek(f, misc_info, SEEK_CUR), 0); | ||
695 | |||
696 | CHECK_RET(read_ushort(f, &compressed), 1); | ||
697 | |||
698 | head->channel_num = head->channels; | ||
699 | |||
700 | switch (head->depth) | ||
701 | { | ||
702 | case 8: | ||
703 | type = 1; | ||
704 | break; | ||
705 | case 16: | ||
706 | type = 2; | ||
707 | break; | ||
708 | default: | ||
709 | *error = EVAS_LOAD_ERROR_UNKNOWN_FORMAT; | ||
710 | return EINA_FALSE; | ||
711 | } | ||
712 | ie->w = head->width; | ||
713 | ie->h = head->height; | ||
714 | if (head->channels == 3) ie->flags.alpha = 0; | ||
715 | else ie->flags.alpha = 1; | ||
716 | |||
717 | evas_cache_image_surface_alloc(ie, ie->w, ie->h); | ||
718 | surface = evas_cache_image_pixels(ie); | ||
719 | if (!surface) | ||
720 | { | ||
721 | *error = EVAS_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED; | ||
722 | goto cleanup_error; | ||
723 | } | ||
724 | |||
725 | if (!psd_get_data(ie, head, f, surface, compressed, error)) | ||
726 | goto cleanup_error; | ||
727 | |||
728 | evas_common_image_premul(ie); | ||
729 | return EINA_TRUE; | ||
730 | |||
731 | #undef CHECK_RET | ||
732 | |||
733 | cleanup_error: | ||
734 | return EINA_FALSE; | ||
735 | } | ||
736 | |||
737 | Eina_Bool | ||
738 | read_psd_cmyk(Image_Entry *ie, PSD_Header *head, FILE *f, int *error) | ||
739 | { | ||
740 | unsigned int color_mode, resource_size, misc_info, size, i, j, data_size; | ||
741 | unsigned short compressed; | ||
742 | unsigned int format, type; | ||
743 | unsigned char *kchannel = NULL; | ||
744 | void *surface; | ||
745 | |||
746 | *error = EVAS_LOAD_ERROR_CORRUPT_FILE; | ||
747 | |||
748 | #define CHECK_RET(Call, Value) \ | ||
749 | if (Call != Value) return EINA_FALSE; | ||
750 | |||
751 | CHECK_RET(read_uint(f, &color_mode), 1); | ||
752 | // Skip over the 'color mode data section' | ||
753 | CHECK_RET(fseek(f, color_mode, SEEK_CUR), 0); | ||
754 | |||
755 | CHECK_RET(read_uint(f, &resource_size), 1); | ||
756 | // Read the 'image resources section' | ||
757 | CHECK_RET(fseek(f, resource_size, SEEK_CUR), 0); | ||
758 | |||
759 | CHECK_RET(read_uint(f, &misc_info), 1); | ||
760 | CHECK_RET(fseek(f, misc_info, SEEK_CUR), 0); | ||
761 | |||
762 | CHECK_RET(read_ushort(f, &compressed), 1); | ||
763 | |||
764 | switch (head->channels) | ||
765 | { | ||
766 | case 4: | ||
767 | format = 0x1907; | ||
768 | head->channel_num = 4; | ||
769 | head->channels = 3; | ||
770 | break; | ||
771 | case 5: | ||
772 | format = 0x1908; | ||
773 | head->channel_num = 5; | ||
774 | head->channels = 4; | ||
775 | break; | ||
776 | default: | ||
777 | *error = EVAS_LOAD_ERROR_UNKNOWN_FORMAT; | ||
778 | return EINA_FALSE; | ||
779 | } | ||
780 | |||
781 | switch (head->depth) | ||
782 | { | ||
783 | case 8: | ||
784 | type = 1; | ||
785 | break; | ||
786 | case 16: | ||
787 | type = 2; | ||
788 | break; | ||
789 | default: | ||
790 | *error = EVAS_LOAD_ERROR_UNKNOWN_FORMAT; | ||
791 | return EINA_FALSE; | ||
792 | } | ||
793 | |||
794 | ie->w = head->width; | ||
795 | ie->h = head->height; | ||
796 | if (head->channels == 3) ie->flags.alpha = 0; | ||
797 | else ie->flags.alpha = 1; | ||
798 | |||
799 | evas_cache_image_surface_alloc(ie, ie->w, ie->h); | ||
800 | surface = evas_cache_image_pixels(ie); | ||
801 | if (!surface) | ||
802 | { | ||
803 | *error = EVAS_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED; | ||
804 | goto cleanup_error; | ||
805 | } | ||
806 | |||
807 | if (!psd_get_data(ie, head, f, surface, compressed, error)) | ||
808 | goto cleanup_error; | ||
809 | |||
810 | size = type * ie->w * ie->h; | ||
811 | kchannel = malloc(size); | ||
812 | if (kchannel == NULL) | ||
813 | goto cleanup_error; | ||
814 | if (!get_single_channel(ie, head, f, kchannel, compressed)) | ||
815 | goto cleanup_error; | ||
816 | |||
817 | data_size = head->channels * type * ie->w * ie->h; | ||
818 | if (format == 0x1907) | ||
819 | { | ||
820 | unsigned char *tmp = surface; | ||
821 | const unsigned char *limit = tmp + data_size; | ||
822 | |||
823 | for (i = 0, j = 0; tmp < limit; tmp++, j++) | ||
824 | { | ||
825 | int k; | ||
826 | |||
827 | for (k = 0; k < 3; k++) | ||
828 | *tmp = (*tmp * kchannel[j]) >> 8; | ||
829 | |||
830 | // FIXME: tmp[i+3] = 255; | ||
831 | } | ||
832 | } | ||
833 | else | ||
834 | { // RGBA | ||
835 | unsigned char *tmp = surface; | ||
836 | const unsigned char *limit = tmp + data_size; | ||
837 | |||
838 | // The KChannel array really holds the alpha channel on this one. | ||
839 | for (i = 0, j = 0; tmp < limit; tmp += 4, j++) | ||
840 | { | ||
841 | tmp[0] = (tmp[0] * tmp[3]) >> 8; | ||
842 | tmp[1] = (tmp[1] * tmp[3]) >> 8; | ||
843 | tmp[2] = (tmp[2] * tmp[3]) >> 8; | ||
844 | tmp[3] = kchannel[j]; // Swap 'K' with alpha channel. | ||
845 | } | ||
846 | } | ||
847 | |||
848 | free(kchannel); | ||
849 | |||
850 | evas_common_image_premul(ie); | ||
851 | return EINA_TRUE; | ||
852 | |||
853 | cleanup_error: | ||
854 | free(kchannel); | ||
855 | return EINA_FALSE; | ||
856 | } | ||
857 | |||
858 | static Eina_Bool | ||
859 | evas_image_load_file_data_psd(Image_Entry *ie, | ||
860 | const char *file, | ||
861 | const char *key __UNUSED__, | ||
862 | int *error) | ||
863 | { | ||
864 | FILE *f; | ||
865 | PSD_Header header; | ||
866 | Eina_Bool bpsd = EINA_FALSE; | ||
867 | |||
868 | f = fopen(file, "rb"); | ||
869 | if (f == NULL) | ||
870 | { | ||
871 | *error = EVAS_LOAD_ERROR_DOES_NOT_EXIST; | ||
872 | return bpsd; | ||
873 | } | ||
874 | |||
875 | if (!psd_get_header(&header, f) || !is_psd(&header)) | ||
876 | { | ||
877 | fclose(f); | ||
878 | *error = EVAS_LOAD_ERROR_GENERIC; | ||
879 | return EINA_FALSE; | ||
880 | } | ||
881 | |||
882 | ie->w = header.width; | ||
883 | ie->h = header.height; | ||
884 | |||
885 | *error = EVAS_LOAD_ERROR_NONE; | ||
886 | |||
887 | switch (header.mode) | ||
888 | { | ||
889 | case PSD_GREYSCALE: // Greyscale | ||
890 | bpsd = read_psd_grey(ie, &header, f, error); | ||
891 | break; | ||
892 | case PSD_INDEXED: // Indexed | ||
893 | bpsd = read_psd_indexed(ie, &header, f, error); | ||
894 | break; | ||
895 | case PSD_RGB: // RGB | ||
896 | bpsd = read_psd_rgb(ie, &header, f, error); | ||
897 | break; | ||
898 | case PSD_CMYK: // CMYK | ||
899 | bpsd = read_psd_cmyk(ie, &header, f, error); | ||
900 | break; | ||
901 | default : | ||
902 | *error = EVAS_LOAD_ERROR_UNKNOWN_FORMAT; | ||
903 | bpsd = EINA_FALSE; | ||
904 | } | ||
905 | fclose(f); | ||
906 | |||
907 | return bpsd; | ||
908 | } | ||
909 | |||
910 | static Eina_Bool | ||
911 | get_compressed_channels_length(PSD_Header *head, | ||
912 | FILE * file, | ||
913 | unsigned short *rle_table, | ||
914 | unsigned int *chanlen) | ||
915 | { | ||
916 | unsigned int j; | ||
917 | unsigned int c; | ||
918 | |||
919 | if (fread(rle_table, | ||
920 | sizeof(unsigned short), | ||
921 | head->height * head->channel_num, | ||
922 | file) != head->height * head->channel_num) | ||
923 | return EINA_FALSE; | ||
924 | |||
925 | memset(chanlen, 0, head->channel_num * sizeof(unsigned int)); | ||
926 | for (c = 0; c < head->channel_num; c++) | ||
927 | { | ||
928 | unsigned int i; | ||
929 | |||
930 | j = c * head->height; | ||
931 | for (i = 0; i < head->height; i++) | ||
932 | { | ||
933 | chanlen[c] += rle_table[i + j]; | ||
934 | } | ||
935 | } | ||
936 | |||
937 | return EINA_TRUE; | ||
938 | } | ||
939 | |||
940 | static const Evas_Image_Load_Func evas_image_load_psd_func = { | ||
941 | EINA_TRUE, | ||
942 | evas_image_load_file_head_psd, | ||
943 | evas_image_load_file_data_psd, | ||
944 | NULL | ||
945 | }; | ||
946 | |||
947 | static int | ||
948 | module_open(Evas_Module *em) | ||
949 | { | ||
950 | if (!em) return 0; | ||
951 | em->functions = (void *)(&evas_image_load_psd_func); | ||
952 | return 1; | ||
953 | } | ||
954 | |||
955 | static void | ||
956 | module_close(Evas_Module *em __UNUSED__) | ||
957 | { | ||
958 | } | ||
959 | |||
960 | static Evas_Module_Api evas_modapi = | ||
961 | { | ||
962 | EVAS_MODULE_API_VERSION, | ||
963 | "psd", | ||
964 | "none", | ||
965 | { | ||
966 | module_open, | ||
967 | module_close | ||
968 | } | ||
969 | }; | ||
970 | |||
971 | EVAS_MODULE_DEFINE(EVAS_MODULE_TYPE_IMAGE_LOADER, image_loader, psd); | ||
972 | |||
973 | |||
974 | #ifndef EVAS_STATIC_BUILD_PSD | ||
975 | EVAS_EINA_MODULE_DEFINE(image_loader, psd); | ||
976 | #endif | ||