From 0cef5e41fefbd7975f5c4d9477041502d8049c7a Mon Sep 17 00:00:00 2001 From: Cedric BAIL Date: Sun, 24 Apr 2011 19:45:43 +0000 Subject: [PATCH] evas: add psd file format support. Patch from Thierry el Borgi with some rework of myself. NOTE: I don't have much file to test, so if some don't contact us with those file and we will fix the loader if needed. SVN revision: 58873 --- legacy/evas/AUTHORS | 1 + legacy/evas/ChangeLog | 5 + legacy/evas/configure.ac | 5 + legacy/evas/m4/evas_check_loader.m4 | 20 + legacy/evas/src/lib/Makefile.am | 5 + .../src/lib/engines/common/evas_image_load.c | 5 +- legacy/evas/src/lib/file/evas_module.c | 4 + legacy/evas/src/modules/loaders/Makefile.am | 6 + .../modules/loaders/psd/evas_image_load_psd.c | 947 ++++++++++++++++++ 9 files changed, 996 insertions(+), 2 deletions(-) create mode 100644 legacy/evas/src/modules/loaders/psd/evas_image_load_psd.c diff --git a/legacy/evas/AUTHORS b/legacy/evas/AUTHORS index cb75a4a773..3a59230dbe 100644 --- a/legacy/evas/AUTHORS +++ b/legacy/evas/AUTHORS @@ -23,3 +23,4 @@ Samsung SAIT Sung W. Park Jiyoun Park Myoungwoon Roy Kim(roy_kim) +Thierry el Borgi diff --git a/legacy/evas/ChangeLog b/legacy/evas/ChangeLog index 251589995d..884274731b 100644 --- a/legacy/evas/ChangeLog +++ b/legacy/evas/ChangeLog @@ -262,3 +262,8 @@ Supports a filter object or filter under (the area where the object is filtered). Various parameters to tweak, and potential for additional filters (but you get to write the shader ;-) + +2011-04-24 Thierry el Borgi + + * Add PSD file format support. + diff --git a/legacy/evas/configure.ac b/legacy/evas/configure.ac index 72f50898b9..2d22dc9606 100644 --- a/legacy/evas/configure.ac +++ b/legacy/evas/configure.ac @@ -122,6 +122,7 @@ want_evas_image_loader_bmp="yes" want_evas_image_loader_tga="yes" want_evas_image_loader_wbmp="yes" want_evas_image_loader_ico="yes" +want_evas_image_loader_psd="yes" want_evas_font_loader_eet="yes" @@ -835,6 +836,8 @@ EVAS_CHECK_IMAGE_LOADER([WBMP], [${want_evas_image_loader_wbmp}]) EVAS_CHECK_IMAGE_LOADER([ICO], [${want_evas_image_loader_ico}]) +EVAS_CHECK_IMAGE_LOADER([PSD], [${want_evas_image_loader_psd}]) + ##################################################################### ## Cpu based optimizations @@ -1607,6 +1610,7 @@ src/modules/loaders/tga/Makefile src/modules/loaders/svg/Makefile src/modules/loaders/pmaps/Makefile src/modules/loaders/wbmp/Makefile +src/modules/loaders/psd/Makefile src/modules/savers/Makefile src/modules/savers/edb/Makefile src/modules/savers/eet/Makefile @@ -1698,6 +1702,7 @@ echo " TGA.....................: $have_evas_image_loader_tga" echo " TIFF....................: $have_evas_image_loader_tiff" echo " WBMP....................: $have_evas_image_loader_wbmp" echo " XPM.....................: $have_evas_image_loader_xpm" +echo " PSD.....................: $have_evas_image_loader_psd" echo echo "Font Sourcing Systems:" echo " EET.....................: $have_evas_font_loader_eet" diff --git a/legacy/evas/m4/evas_check_loader.m4 b/legacy/evas/m4/evas_check_loader.m4 index d03ce091f0..2bff191559 100644 --- a/legacy/evas/m4/evas_check_loader.m4 +++ b/legacy/evas/m4/evas_check_loader.m4 @@ -390,6 +390,26 @@ fi ]) +dnl use: EVAS_CHECK_LOADER_DEP_PSD(loader, want_static[, ACTION-IF-FOUND[, ACTION-IF-NOT-FOUND]]) + +AC_DEFUN([EVAS_CHECK_LOADER_DEP_PSD], +[ + +have_dep="yes" +evas_image_loader_[]$1[]_cflags="" +evas_image_loader_[]$1[]_libs="" + +AC_SUBST([evas_image_loader_$1_cflags]) +AC_SUBST([evas_image_loader_$1_libs]) + +if test "x${have_dep}" = "xyes" ; then + m4_default([$3], [:]) +else + m4_default([$4], [:]) +fi + +]) + dnl use: EVAS_CHECK_IMAGE_LOADER(loader, want_loader, macro) diff --git a/legacy/evas/src/lib/Makefile.am b/legacy/evas/src/lib/Makefile.am index 6e44840190..e901a9b24d 100644 --- a/legacy/evas/src/lib/Makefile.am +++ b/legacy/evas/src/lib/Makefile.am @@ -164,6 +164,11 @@ SUBDIRS += ../modules/loaders/xpm EVAS_STATIC_MODULE += ../modules/loaders/xpm/libevas_loader_xpm.la EVAS_STATIC_LIBADD += @evas_image_loader_xpm_libs@ endif +if EVAS_STATIC_BUILD_PSD +SUBDIRS += ../modules/loaders/psd +EVAS_STATIC_MODULE += ../modules/loaders/psd/libevas_loader_psd.la +EVAS_STATIC_LIBADD += @evas_image_loader_psd_libs@ +endif AM_CPPFLAGS = \ -I. \ diff --git a/legacy/evas/src/lib/engines/common/evas_image_load.c b/legacy/evas/src/lib/engines/common/evas_image_load.c index ba6b38df78..2ddbd1bcab 100644 --- a/legacy/evas/src/lib/engines/common/evas_image_load.c +++ b/legacy/evas/src/lib/engines/common/evas_image_load.c @@ -32,12 +32,13 @@ static const struct ext_loader_s loaders[] = { "tga", "tga" }, { "wbmp", "wbmp" }, { "ico", "ico" }, - { "cur", "ico" } + { "cur", "ico" }, + { "psd", "psd" } }; static const char *loaders_name[] = { - "png", "jpeg", "eet", "xpm", "tiff", "gif", "svg", "pmaps", "edb", "bmp", "tga", "wbmp", "ico" + "png", "jpeg", "eet", "xpm", "tiff", "gif", "svg", "pmaps", "edb", "bmp", "tga", "wbmp", "ico", "psd" }; struct evas_image_foreach_loader_data diff --git a/legacy/evas/src/lib/file/evas_module.c b/legacy/evas/src/lib/file/evas_module.c index 0c5969f764..1098822b6e 100644 --- a/legacy/evas/src/lib/file/evas_module.c +++ b/legacy/evas/src/lib/file/evas_module.c @@ -121,6 +121,7 @@ EVAS_EINA_STATIC_MODULE_DEFINE(image_loader, edb); EVAS_EINA_STATIC_MODULE_DEFINE(image_loader, tga); EVAS_EINA_STATIC_MODULE_DEFINE(image_loader, wbmp); EVAS_EINA_STATIC_MODULE_DEFINE(image_loader, ico); +EVAS_EINA_STATIC_MODULE_DEFINE(image_loader, psd); EVAS_EINA_STATIC_MODULE_DEFINE(image_saver, edb); EVAS_EINA_STATIC_MODULE_DEFINE(image_saver, eet); EVAS_EINA_STATIC_MODULE_DEFINE(image_saver, jpeg); @@ -238,6 +239,9 @@ static const struct { #endif #ifdef EVAS_STATIC_BUILD_TIFF EVAS_EINA_STATIC_MODULE_USE(image_saver, tiff), +#endif +#ifdef EVAS_STATIC_BUILD_PSD + EVAS_EINA_STATIC_MODULE_USE(image_saver, psd), #endif { NULL, NULL } }; diff --git a/legacy/evas/src/modules/loaders/Makefile.am b/legacy/evas/src/modules/loaders/Makefile.am index 222bef1e12..f90cfd21a4 100644 --- a/legacy/evas/src/modules/loaders/Makefile.am +++ b/legacy/evas/src/modules/loaders/Makefile.am @@ -80,3 +80,9 @@ SUBDIRS += xpm endif endif +if BUILD_LOADER_PSD +if !EVAS_STATIC_BUILD_PSD +SUBDIRS += psd +endif +endif + diff --git a/legacy/evas/src/modules/loaders/psd/evas_image_load_psd.c b/legacy/evas/src/modules/loaders/psd/evas_image_load_psd.c new file mode 100644 index 0000000000..f7c3e2428e --- /dev/null +++ b/legacy/evas/src/modules/loaders/psd/evas_image_load_psd.c @@ -0,0 +1,947 @@ +#define _XOPEN_SOURCE + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_EVIL +# include +#endif + +#include "evas_common.h" +#include "evas_private.h" + +typedef struct _PSD_Header PSD_Header; + +typedef enum _PSD_Mode + { + PSD_GREYSCALE = 1, + PSD_INDEXED = 2, + PSD_RGB = 3, + PSD_CMYK = 4 + } PSD_Mode; + +struct _PSD_Header +{ + unsigned char signature[4]; + unsigned short version; + unsigned char reserved[9]; + unsigned short channels; + unsigned int height; + unsigned int width; + unsigned short depth; + + unsigned short channel_num; + + PSD_Mode mode; +}; + +enum { + READ_COMPRESSED_SUCCESS, + READ_COMPRESSED_ERROR_FILE_CORRUPT, + READ_COMPRESSED_ERROR_FILE_READ_ERROR +}; + +static Eina_Bool get_compressed_channels_length(PSD_Header *Head, + FILE *file, + unsigned short *rle_table, + unsigned int *chanlen); + +static int +read_ushort(FILE *file, unsigned short *ret) +{ + unsigned char b[2]; + if (fread(b, sizeof(unsigned char), 2, file) != 2) return 0; + // FIXME: need to check order + *ret = (b[0] << 8) | b[1]; + return 1; +} + +static int +read_uint(FILE *file, unsigned int *ret) +{ + unsigned char b[4]; + if (fread(b, sizeof(unsigned char), 4, file) != 4) return 0; + // FIXME: need to check order + *ret = ARGB_JOIN(b[0], b[1], b[2], b[3]); + return 1; +} + +// Internal function used to get the Psd header from the current file. +Eina_Bool +psd_get_header(PSD_Header *header, FILE * file) +{ + unsigned short tmp; + +#define CHECK_RET(Call, Value) \ + if (Call != Value) return EINA_FALSE; + + CHECK_RET(fread(header->signature, sizeof (unsigned char), 4, file), 4); + CHECK_RET(read_ushort(file, &header->version), 1); + CHECK_RET(fread(header->reserved, sizeof (unsigned char), 6, file), 6); + CHECK_RET(read_ushort(file, &header->channels), 1); + CHECK_RET(read_uint(file, &header->height), 1); + CHECK_RET(read_uint(file, &header->width), 1); + CHECK_RET(read_ushort(file, &header->depth), 1); + + CHECK_RET(read_ushort(file, &tmp), 1); + header->mode = tmp; + +#undef CHECK_RET + + /* fprintf(stderr, "<<<<<<<<<<<\nsignature : %c%c%c%c\n", */ + /* header->signature[0], */ + /* header->signature[1], */ + /* header->signature[2], */ + /* header->signature[3]); */ + /* fprintf(stderr, "version : %i\n", header->version); */ + /* fprintf(stderr, "channels : %i\n", header->channels); */ + /* fprintf(stderr, "width x height : %dx%d\n", header->width, header->height); */ + /* fprintf(stderr, "depth : %i\n", header->depth); */ + /* fprintf(stderr, "mode : %i\n>>>>>>>>>>>>\n", header->mode); */ + + return EINA_TRUE; +} + + +// Internal function used to check if the HEADER is a valid Psd header. +Eina_Bool +is_psd(PSD_Header *header) +{ + if (strncmp((char*)header->signature, "8BPS", 4)) + return EINA_FALSE; + if (header->version != 1) + return EINA_FALSE; + if (header->channels < 1 || header->channels > 24) + return EINA_FALSE; + if (header->height < 1 || header->width < 1) + return EINA_FALSE; + if (header->depth != 1 && header->depth != 8 && header->depth != 16) + return EINA_FALSE; + + return EINA_TRUE; +} + +static Eina_Bool +evas_image_load_file_head_psd(Image_Entry *ie, const char *FileName, const char *key, int *error) +{ + FILE *f; + PSD_Header header; + + *error = EVAS_LOAD_ERROR_NONE; + + f = fopen(FileName, "rb"); + if (f == NULL) + { + *error = EVAS_LOAD_ERROR_DOES_NOT_EXIST; + return EINA_FALSE; + } + psd_get_header(&header, f); + fclose(f); + + if (!is_psd(&header)) + { + *error = EVAS_LOAD_ERROR_UNKNOWN_FORMAT; + return EINA_FALSE; + } + + ie->w = header.width; + ie->h = header.height; + if (header.channels == 3) ie->flags.alpha = 0; + else ie->flags.alpha = 1; + + return EINA_TRUE; +} + +static unsigned int +read_compressed_channel(FILE* file, + const unsigned int channel_length, unsigned int size, + unsigned char* channel) +{ + // FIXME: what does channel_length means, and why is it not used + unsigned int i; + char headbyte, c; + +#define CHECK_RET(Call, Value) \ + if (Call != Value) return READ_COMPRESSED_ERROR_FILE_READ_ERROR; + + for (i = 0; i < size; ) + { + CHECK_RET(fread(&headbyte, 1, 1, file), 1); + + if (headbyte >= 0) + { + if (i + headbyte > size) + return READ_COMPRESSED_ERROR_FILE_CORRUPT; + + CHECK_RET(fread(channel + i, headbyte + 1, 1, file), 1); + + i += headbyte + 1; + } + else if (headbyte >= -127 && headbyte <= -1) + { + int run; + + CHECK_RET(fread(&c, 1, 1, file), 1); + + run = c; + /* if (run == -1) */ + /* return READ_COMPRESSED_ERROR_FILE_READ_ERROR; */ + + if (i + (-headbyte + 1) > size) + return READ_COMPRESSED_ERROR_FILE_CORRUPT; + + memset(channel + i, run, -headbyte + 1); + i += -headbyte + 1; + } + } + +#undef CHECK_RET + + return READ_COMPRESSED_SUCCESS; +} + + +Eina_Bool +psd_get_data(Image_Entry *ie, + PSD_Header *head, + FILE *f, + unsigned char *buffer, Eina_Bool compressed, + int *error) +{ + unsigned int c, x, y, numchan, bps, bpc, bpp; + unsigned int pixels_count; + unsigned char *channel = NULL; + unsigned char *data = NULL; + + // Added 01-07-2009: This is needed to correctly load greyscale and + // paletted images. + switch (head->mode) + { + case PSD_GREYSCALE: + case PSD_INDEXED: + numchan = 1; + break; + default: + numchan = 3; + } + + bpp = head->channels; + bpc = head->depth / 8; + pixels_count = head->width * head->height; + + data = malloc(sizeof (unsigned char) * pixels_count * bpp); + if (!data) return EINA_FALSE; + + channel = malloc(sizeof (unsigned char) * pixels_count * bpc); + if (!channel) + { + free(data); + return EINA_FALSE; + } + + bps = head->width * head->channels * bpc; + // @TODO: Add support for this in, though I have yet to run across a .psd + // file that uses this. + if (compressed && bpc == 2) + { + free(data); + free(channel); + fprintf(stderr, "unsupported file format.\n"); + *error = EVAS_LOAD_ERROR_UNKNOWN_FORMAT; + return EINA_FALSE; + } + +#define CHECK_RET(Call, Value) \ + if (Call != Value) \ + { \ + free(data); \ + free(channel); \ + return EINA_FALSE; \ + } + + if (!compressed) + { + if (bpc == 1) + { + for (c = 0; c < numchan; c++) + { + unsigned char *tmp = channel; + + CHECK_RET(fread(tmp, pixels_count, 1, f), 1); + + for (y = 0; y < head->height * bps; y += bps) + { + for (x = 0; x < bps; x += bpp, tmp++) + { + data[y + x + c] = *tmp; + } + } + } + + // Accumulate any remaining channels into a single alpha channel + //@TODO: This needs to be changed for greyscale images. + for (; c < head->channels; c++) + { + unsigned char *tmp = channel; + + CHECK_RET(fread(channel, pixels_count, 1, f), 1); + + for (y = 0; y < head->height * bps; y += bps) + { + for (x = 0; x < bps; x += bpp, tmp++) + { + unsigned short newval; + + // previous formula was : (old / 255 * new / 255) * 255 + newval = (*tmp) * data[y + x + 3]; + + data[y + x + 3] = newval >> 8; + } + } + } + } + else + { + int bps2; + + bps2 = bps / 2; + + // iCurImage->Bpc == 2 + for (c = 0; c < numchan; c++) + { + unsigned short *shortptr = (unsigned short*) channel; + + CHECK_RET(fread(channel, pixels_count * 2, 1, f), 1); + + for (y = 0; y < head->height * bps2; y += bps2) + { + for (x = 0; x < bps2; x += bpp, shortptr++) + { + ((unsigned short*)data)[y + x + c] = *shortptr; + } + } + } + + // Accumulate any remaining channels into a single alpha channel + //@TODO: This needs to be changed for greyscale images. + for (; c < head->channels; c++) + { + unsigned short *shortptr = (unsigned short*) channel; + + CHECK_RET(fread(channel, pixels_count * 2, 1, f), 1); + + for (y = 0; y < head->height * bps2; y += bps2) + { + for (x = 0; x < bps2; x += bpp, shortptr) + { + unsigned int newval; + + newval = *shortptr * ((unsigned short*)data)[y + x + 3]; + + ((unsigned short*)data)[y + x + 3] = newval >> 16; + } + } + } + } + } + else + { + unsigned short *rle_table; + unsigned int *chanlen; + + rle_table = alloca(head->height * head->channel_num * sizeof (unsigned short)); + chanlen = alloca(head->channel_num * sizeof (unsigned int)); + if (!get_compressed_channels_length(head, f, rle_table, chanlen)) + goto file_read_error; + + for (c = 0; c < numchan; c++) + { + unsigned char *tmp = channel; + int err; + + err = read_compressed_channel(f, + chanlen[c], + pixels_count, + channel); + if (err == READ_COMPRESSED_ERROR_FILE_CORRUPT) + goto file_corrupt; + else if (err == READ_COMPRESSED_ERROR_FILE_READ_ERROR) + goto file_read_error; + + for (y = 0; y < head->height * bps; y += bps) + { + for (x = 0; x < bps; x += bpp, tmp++) + { + data[y + x + c] = *tmp; + } + } + } + + // Initialize the alpha channel to solid + //@TODO: This needs to be changed for greyscale images. + if (head->channels >= 4) + { + for (y = 0; y < head->height * bps; y += bps) + { + for (x = 0; x < bps; x += bpp) + { + data[y + x + 3] = 255; + } + } + + for (; c < head->channels; c++) + { + unsigned char *tmp = channel; + int err; + + err = read_compressed_channel(f, + chanlen[c], + pixels_count, + channel); + if (err == READ_COMPRESSED_ERROR_FILE_CORRUPT) + goto file_corrupt; + else if (err == READ_COMPRESSED_ERROR_FILE_READ_ERROR) + goto file_read_error; + + for (y = 0; y < head->height * bps; y += bps) + { + for (x = 0; x < bps; x += bpp, tmp++) + { + unsigned short newval; + + newval = *tmp * data[y + x + 3]; + + data[y + x + 3] = newval >> 8; + } + } + } + } + } + + if (bpp == 3) + { + for (x = 0; x < pixels_count; x++) + { + buffer[x * 4 + 0] = data[x * 3 + 2]; + buffer[x * 4 + 1] = data[x * 3 + 1]; + buffer[x * 4 + 2] = data[x * 3 + 0]; + buffer[x * 4 + 3] = 255; + } + } + else + { + // BRGA to RGBA + for (x= 0; x < pixels_count; x++) + { + buffer[x * 4 + 0] = data[x * 4 + 2]; + buffer[x * 4 + 1] = data[x * 4 + 1]; + buffer[x * 4 + 2] = data[x * 4 + 0]; + buffer[x * 4 + 3] = data[x * 4 + 3]; + } + } + + free(channel); + free(data); + return EINA_TRUE; + +#undef CHECK_RET + + file_corrupt: + *error = EVAS_LOAD_ERROR_CORRUPT_FILE; + + file_read_error: + free(channel); + free(data); + + return EINA_FALSE; +} + + +Eina_Bool +get_single_channel(Image_Entry *ie, + PSD_Header *head, + FILE *f, + unsigned char *buffer, + Eina_Bool compressed) +{ + unsigned int i, bpc; + unsigned short *tmp; + char headbyte; + int c; + int pixels_count; + + tmp = (unsigned short*)buffer; + bpc = (head->depth / 8); + pixels_count = head->width * head->height; + +#define CHECK_RET(Call, Value) \ + if (Call != Value) return EINA_FALSE; + + if (!compressed) + { + if (bpc == 1) + { + CHECK_RET(fread(buffer, pixels_count, 1, f), 1); + } + else + { // Bpc == 2 + CHECK_RET(fread(buffer, pixels_count * 2, 1, f), 1); + } + } + else + { + for (i = 0; i < pixels_count; ) + { + CHECK_RET(fread(&headbyte, 1, 1, f), 1); + + if (headbyte >= 0) + { // && HeadByte <= 127 + CHECK_RET(fread(buffer + i, headbyte + 1, 1, f), 1); + + i += headbyte + 1; + } + if (headbyte >= -127 && headbyte <= -1) + { + int run; + + CHECK_RET(fread(&c, 1, 1, f), 1); + + run = c; + if (run == -1) return EINA_FALSE; + + memset(buffer + i, run, -headbyte + 1); + i += -headbyte + 1; + } + } + } + +#undef CHECK_RET + + return EINA_TRUE; +} + +Eina_Bool +read_psd_grey(Image_Entry *ie, PSD_Header *head, FILE * f, int *error) +{ + unsigned int color_mode, resource_size, misc_info; + unsigned short compressed; + unsigned int type; + void *surface = NULL; + + *error = EVAS_LOAD_ERROR_CORRUPT_FILE; + +#define CHECK_RET(Call, Value) \ + if (Call != Value) return EINA_FALSE; + + CHECK_RET(read_uint(f, &color_mode), 1); + // Skip over the 'color mode data section' + CHECK_RET(fseek(f, color_mode, SEEK_CUR), 0); + + CHECK_RET(read_uint(f, &resource_size), 1); + // Read the 'image resources section' + + CHECK_RET(fseek(f, resource_size, SEEK_CUR), 0); + + CHECK_RET(read_uint(f, &misc_info), 1); + CHECK_RET(fseek(f, misc_info, SEEK_CUR), 0); + + CHECK_RET(read_ushort(f, &compressed), 1); + + ie->w = head->width; + ie->h = head->height; + if (head->channels == 3) ie->flags.alpha = 0; + else ie->flags.alpha = 1; + + head->channel_num = head->channels; + // Temporary to read only one channel...some greyscale .psd files have 2. + head->channels = 1; + + switch (head->depth) + { + case 8: + type = 1; + break; + case 16: + type = 2; + break; + default: + *error = EVAS_LOAD_ERROR_UNKNOWN_FORMAT; + return EINA_FALSE; + } + + evas_cache_image_surface_alloc(ie, ie->w, ie->h); + surface = evas_cache_image_pixels(ie); + + if (!psd_get_data(ie, head, f, surface, compressed, error)) + goto cleanup_error; + + return EINA_TRUE; + +#undef CHECK_RET + + cleanup_error: + return EINA_FALSE; +} + + +Eina_Bool +read_psd_indexed(Image_Entry *ie, PSD_Header *head, FILE * f, int *error) +{ + unsigned int color_mode, resource_size, misc_info; + unsigned short compressed; + void *surface; + + *error = EVAS_LOAD_ERROR_CORRUPT_FILE; + +#define CHECK_RET(Call, Value) \ + if (Call != Value) return EINA_FALSE; + + CHECK_RET(read_uint(f, &color_mode), 1); + CHECK_RET((color_mode % 3), 0); + /* + Palette = (unsigned char*)malloc(Colormode); + if (Palette == NULL) + return EINA_FALSE; + if (fread(&Palette, 1, Colormode, file) != Colormode) + goto cleanup_error; + */ + // Skip over the 'color mode data section' + CHECK_RET(fseek(f, color_mode, SEEK_CUR), 0); + + // Read the 'image resources section' + CHECK_RET(read_uint(f, &resource_size), 1); + CHECK_RET(fseek(f, resource_size, SEEK_CUR), 0); + + CHECK_RET(read_uint(f, &misc_info), 1); + CHECK_RET(fseek(f, misc_info, SEEK_CUR), 0); + + CHECK_RET(read_ushort(f, &compressed), 1); + + if (head->channels != 1 || head->depth != 8) + { + *error = EVAS_LOAD_ERROR_UNKNOWN_FORMAT; + return EINA_FALSE; + } + head->channel_num = head->channels; + + ie->w = head->width; + ie->h = head->height; + if (head->channels == 3) ie->flags.alpha = 0; + else ie->flags.alpha = 1; + + evas_cache_image_surface_alloc(ie, ie->w, ie->h); + surface = evas_cache_image_pixels(ie); + + if (!psd_get_data(ie, head, f, surface, compressed, error)) + return EINA_FALSE; + return EINA_TRUE; + +#undef CHECK_RET +} + +Eina_Bool +read_psd_rgb(Image_Entry *ie, PSD_Header *head, FILE *f, int *error) +{ + unsigned int color_mode, resource_size, misc_info; + unsigned short compressed; + unsigned int format, type; + void *surface; + +#define CHECK_RET(Call, Value) \ + if (Call != Value) return EINA_FALSE; + + CHECK_RET(read_uint(f, &color_mode), 1); + // Skip over the 'color mode data section' + CHECK_RET(fseek(f, color_mode, SEEK_CUR), 0); + + // Read the 'image resources section' + CHECK_RET(read_uint(f, &resource_size), 1); + CHECK_RET(fseek(f, resource_size, SEEK_CUR), 0); + + CHECK_RET(read_uint(f, &misc_info), 1); + CHECK_RET(fseek(f, misc_info, SEEK_CUR), 0); + + CHECK_RET(read_ushort(f, &compressed), 1); + + head->channel_num = head->channels; + + switch (head->depth) + { + case 8: + type = 1; + break; + case 16: + type = 2; + break; + default: + *error = EVAS_LOAD_ERROR_UNKNOWN_FORMAT; + return EINA_FALSE; + } + ie->w = head->width; + ie->h = head->height; + if (head->channels == 3) ie->flags.alpha = 0; + else ie->flags.alpha = 1; + + evas_cache_image_surface_alloc(ie, ie->w, ie->h); + surface = evas_cache_image_pixels(ie); + + if (!psd_get_data(ie, head, f, surface, compressed, error)) + goto cleanup_error; + + evas_common_image_premul(ie); + return EINA_TRUE; + +#undef CHECK_RET + + cleanup_error: + return EINA_FALSE; +} + +Eina_Bool +read_psd_cmyk(Image_Entry *ie, PSD_Header *head, FILE *f, int *error) +{ + unsigned int color_mode, resource_size, misc_info, size, i, j, data_size; + unsigned short compressed; + unsigned int format, type; + unsigned char *kchannel = NULL; + void *surface; + + *error = EVAS_LOAD_ERROR_CORRUPT_FILE; + +#define CHECK_RET(Call, Value) \ + if (Call != Value) return EINA_FALSE; + + CHECK_RET(read_uint(f, &color_mode), 1); + // Skip over the 'color mode data section' + CHECK_RET(fseek(f, color_mode, SEEK_CUR), 0); + + CHECK_RET(read_uint(f, &resource_size), 1); + // Read the 'image resources section' + CHECK_RET(fseek(f, resource_size, SEEK_CUR), 0); + + CHECK_RET(read_uint(f, &misc_info), 1); + CHECK_RET(fseek(f, misc_info, SEEK_CUR), 0); + + CHECK_RET(read_ushort(f, &compressed), 1); + + switch (head->channels) + { + case 4: + format = 0x1907; + head->channel_num = 4; + head->channels = 3; + break; + case 5: + format = 0x1908; + head->channel_num = 5; + head->channels = 4; + break; + default: + *error = EVAS_LOAD_ERROR_UNKNOWN_FORMAT; + return EINA_FALSE; + } + + switch (head->depth) + { + case 8: + type = 1; + break; + case 16: + type = 2; + break; + default: + *error = EVAS_LOAD_ERROR_UNKNOWN_FORMAT; + return EINA_FALSE; + } + + ie->w = head->width; + ie->h = head->height; + if (head->channels == 3) ie->flags.alpha = 0; + else ie->flags.alpha = 1; + + evas_cache_image_surface_alloc(ie, ie->w, ie->h); + surface = evas_cache_image_pixels(ie); + + if (!psd_get_data(ie, head, f, surface, compressed, error)) + goto cleanup_error; + + size = type * ie->w * ie->h; + kchannel = malloc(size); + if (kchannel == NULL) + goto cleanup_error; + if (!get_single_channel(ie, head, f, kchannel, compressed)) + goto cleanup_error; + + data_size = head->channels * type * ie->w * ie->h; + if (format == 0x1907) + { + unsigned char *tmp = surface; + const unsigned char *limit = tmp + data_size; + + for (i = 0, j = 0; tmp < limit; tmp++, j++) + { + int k; + + for (k = 0; k < 3; k++) + *tmp = (*tmp * kchannel[j]) >> 8; + + // FIXME: tmp[i+3] = 255; + } + } + else + { // RGBA + unsigned char *tmp = surface; + const unsigned char *limit = tmp + data_size; + + // The KChannel array really holds the alpha channel on this one. + for (i = 0, j = 0; tmp < limit; tmp += 4, j++) + { + tmp[0] = (tmp[0] * tmp[3]) >> 8; + tmp[1] = (tmp[1] * tmp[3]) >> 8; + tmp[2] = (tmp[2] * tmp[3]) >> 8; + tmp[3] = kchannel[j]; // Swap 'K' with alpha channel. + } + } + + free(kchannel); + + evas_common_image_premul(ie); + return EINA_TRUE; + + cleanup_error: + free(kchannel); + return EINA_FALSE; +} + +static Eina_Bool +evas_image_load_file_data_psd(Image_Entry *ie, + const char *file, + const char *key, + int *error) +{ + FILE *f; + PSD_Header header; + Eina_Bool bpsd = EINA_FALSE; + + f = fopen(file, "rb"); + if (f == NULL) + { + *error = EVAS_LOAD_ERROR_DOES_NOT_EXIST; + return bpsd; + } + + psd_get_header(&header, f); + if (!is_psd(&header)) + { + *error = EVAS_LOAD_ERROR_GENERIC; + return EINA_FALSE; + } + + ie->w = header.width; + ie->h = header.height; + + *error = EVAS_LOAD_ERROR_NONE; + + switch (header.mode) + { + case PSD_GREYSCALE: // Greyscale + bpsd = read_psd_grey(ie, &header, f, error); + break; + case PSD_INDEXED: // Indexed + bpsd = read_psd_indexed(ie, &header, f, error); + break; + case PSD_RGB: // RGB + bpsd = read_psd_rgb(ie, &header, f, error); + break; + case PSD_CMYK: // CMYK + bpsd = read_psd_cmyk(ie, &header, f, error); + break; + default : + *error = EVAS_LOAD_ERROR_UNKNOWN_FORMAT; + bpsd = EINA_FALSE; + } + fclose(f); + + return bpsd; +} + +static Eina_Bool +get_compressed_channels_length(PSD_Header *head, + FILE * file, + unsigned short *rle_table, + unsigned int *chanlen) +{ + unsigned int j; + unsigned int c; + + if (fread(rle_table, + sizeof(unsigned short), + head->height * head->channel_num, + file) != head->height * head->channel_num) + return EINA_FALSE; + + memset(chanlen, 0, head->channel_num * sizeof(unsigned int)); + for (c = 0; c < head->channel_num; c++) + { + unsigned int i; + + j = c * head->height; + for (i = 0; i < head->height; i++) + { + chanlen[c] += rle_table[i + j]; + } + } + + return EINA_TRUE; +} + +static const Evas_Image_Load_Func evas_image_load_psd_func = { + EINA_TRUE, + evas_image_load_file_head_psd, + evas_image_load_file_data_psd +}; + +static int +module_open(Evas_Module *em) +{ + if (!em) return 0; + em->functions = (void *)(&evas_image_load_psd_func); + return 1; +} + +static void +module_close(Evas_Module *em __UNUSED__) +{ +} + +static Evas_Module_Api evas_modapi = + { + EVAS_MODULE_API_VERSION, + "psd", + "none", + { + module_open, + module_close + } + }; + +EVAS_MODULE_DEFINE(EVAS_MODULE_TYPE_IMAGE_LOADER, image_loader, psd); + + +#ifndef EVAS_STATIC_BUILD_PSD +EVAS_EINA_MODULE_DEFINE(image_loader, psd); +#endif