evas: add psd file format support.
authorCedric BAIL <cedric.bail@free.fr>
Sun, 24 Apr 2011 19:45:43 +0000 (19:45 +0000)
committerCedric BAIL <cedric.bail@free.fr>
Sun, 24 Apr 2011 19:45:43 +0000 (19:45 +0000)
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
legacy/evas/ChangeLog
legacy/evas/configure.ac
legacy/evas/m4/evas_check_loader.m4
legacy/evas/src/lib/Makefile.am
legacy/evas/src/lib/engines/common/evas_image_load.c
legacy/evas/src/lib/file/evas_module.c
legacy/evas/src/modules/loaders/Makefile.am
legacy/evas/src/modules/loaders/psd/evas_image_load_psd.c [new file with mode: 0644]

index cb75a4a..3a59230 100644 (file)
@@ -23,3 +23,4 @@ Samsung SAIT <tbd>
 Sung W. Park <sungwoo@gmail.com>
 Jiyoun Park <jy0703.park@samsung.com>
 Myoungwoon Roy Kim(roy_kim) <myoungwoon.kim@samsung.com> <myoungwoon@gmail.com>
+Thierry el Borgi <tbd>
index 2515899..8842747 100644 (file)
          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.
+
index 72f5089..2d22dc9 100644 (file)
@@ -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"
index d03ce09..2bff191 100644 (file)
@@ -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)
 
 
index 6e44840..e901a9b 100644 (file)
@@ -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. \
index ba6b38d..2ddbd1b 100644 (file)
@@ -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
index 0c5969f..1098822 100644 (file)
@@ -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);
@@ -239,6 +240,9 @@ static const struct {
 #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 }
 };
 
index 222bef1..f90cfd2 100644 (file)
@@ -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 (file)
index 0000000..f7c3e24
--- /dev/null
@@ -0,0 +1,947 @@
+#define _XOPEN_SOURCE
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+
+#include <math.h>
+#include <netinet/in.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/mman.h>
+
+#ifdef HAVE_EVIL
+# include <Evil.h>
+#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