Evas DDS: Start implementing DDS file loader
authorJean-Philippe Andre <jp.andre@samsung.com>
Thu, 19 Jun 2014 03:34:05 +0000 (12:34 +0900)
committerJean-Philippe Andre <jp.andre@samsung.com>
Thu, 3 Jul 2014 02:37:48 +0000 (11:37 +0900)
This implement the header read.

src/modules/evas/loaders/dds/evas_image_load_dds.c
src/modules/evas/loaders/dds/s3tc.h [new file with mode: 0644]
src/modules/evas/loaders/dds/s3tc_decoder.c [new file with mode: 0644]

index 356fc68..836751d 100644 (file)
 #endif
 
 #include "Evas_Loader.h"
+#include "s3tc.h"
+
+#ifdef _WIN32
+# include <ddraw.h>
+#endif
 
 typedef struct _Evas_Loader_Internal Evas_Loader_Internal;
 struct _Evas_Loader_Internal
 {
    Eina_File *f;
+
+   DxtFormat format;
+   size_t data_size;
+
+   struct {
+      uint32_t flags;
+      uint32_t fourcc;
+      uint32_t rgb_bitcount;
+      uint32_t r_mask;
+      uint32_t g_mask;
+      uint32_t b_mask;
+      uint32_t a_mask;
+
+      Eina_Bool has_alpha : 1;
+      // TODO: check mipmaps to load faster a small image :)
+   } pf; // pixel format
+};
+
+#undef FOURCC
+#ifndef WORDS_BIGENDIAN
+# define FOURCC(a,b,c,d) ((d << 24) | (c << 16) | (b << 8) | a)
+#else
+# define FOURCC(a,b,c,d) ((a << 24) | (b << 16) | (c << 8) | d)
+#endif
+
+#ifndef DIRECTDRAW_VERSION
+// DIRECTDRAW_VERSION is defined in ddraw.h
+// These definitions are from the MSDN reference.
+
+enum DDSFlags {
+   DDSD_CAPS = 0x1,
+   DDSD_HEIGHT = 0x2,
+   DDSD_WIDTH = 0x4,
+   DDSD_PITCH = 0x8,
+   DDSD_PIXELFORMAT = 0x1000,
+   DDSD_MIPMAPCOUNT = 0x20000,
+   DDSD_LINEARSIZE = 0x80000,
+   DDSD_DEPTH = 0x800000
+};
+
+enum DDSPixelFormatFlags {
+   DDPF_ALPHAPIXELS = 0x1,
+   DDPF_ALPHA = 0x2,
+   DDPF_FOURCC = 0x4,
+   DDPF_RGB = 0x40,
+   DDPF_YUV = 0x200,
+   DDPF_LUMINANCE = 0x20000
+};
+
+enum DDSCaps {
+   DDSCAPS_COMPLEX = 0x8,
+   DDSCAPS_MIPMAP = 0x400000,
+   DDSCAPS_TEXTURE = 0x1000
+};
+
+#endif
+
+static const Evas_Colorspace cspaces_s3tc_dxt1_rgb[] = {
+   //EVAS_COLORSPACE_RGB_S3TC_DXT1,
+   EVAS_COLORSPACE_ARGB8888
+};
+
+static const Evas_Colorspace cspaces_s3tc_dxt1_rgba[] = {
+   //EVAS_COLORSPACE_RGBA_S3TC_DXT1,
+   EVAS_COLORSPACE_ARGB8888
+};
+
+static const Evas_Colorspace cspaces_s3tc_dxt2[] = {
+   //EVAS_COLORSPACE_RGBA_S3TC_DXT2, // Not in OpenGL
+   EVAS_COLORSPACE_ARGB8888
+};
+
+static const Evas_Colorspace cspaces_s3tc_dxt3[] = {
+   //EVAS_COLORSPACE_RGB_S3TC_DXT3,
+   EVAS_COLORSPACE_ARGB8888
+};
+
+static const Evas_Colorspace cspaces_s3tc_dxt4[] = {
+   //EVAS_COLORSPACE_RGBA_S3TC_DXT4, // Not in OpenGL
+   EVAS_COLORSPACE_ARGB8888
+};
+
+static const Evas_Colorspace cspaces_s3tc_dxt5[] = {
+   //EVAS_COLORSPACE_RGBA_S3TC_DXT5,
+   EVAS_COLORSPACE_ARGB8888
 };
 
 static void *
 evas_image_load_file_open_dds(Eina_File *f, Eina_Stringshare *key EINA_UNUSED,
-                              Evas_Image_Load_Opts *opts,
+                              Evas_Image_Load_Opts *opts EINA_UNUSED,
                               Evas_Image_Animated *animated EINA_UNUSED,
                               int *error)
 {
    Evas_Loader_Internal *loader;
 
-   // TODO: Check file size
-   if (eina_file_size_get(f) <= 16)
+   if (eina_file_size_get(f) <= 128)
      {
         *error = EVAS_LOAD_ERROR_CORRUPT_FILE;
         return NULL;
@@ -60,12 +149,28 @@ evas_image_load_file_close_dds(void *loader_data)
    free(loader);
 }
 
+static inline uint32_t
+_dword_read(const char **m)
+{
+   uint32_t val = *((uint32_t *) *m);
+   *m += 4;
+   return val;
+}
+
+#define FAIL() do { fprintf(stderr, "DDS: ERROR at %s:%d", __FUNCTION__, __LINE__); goto on_error; } while (0)
+
 static Eina_Bool
 evas_image_load_file_head_dds(void *loader_data,
                               Evas_Image_Property *prop,
                               int *error)
 {
+   static const uint32_t base_flags = /* 0x1007 */
+         DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT | DDSD_PIXELFORMAT;
+
    Evas_Loader_Internal *loader = loader_data;
+   uint32_t flags, height, width, pitchOrLinearSize, block_size = 16, stride,
+         caps, caps2;
+   Eina_Bool has_linearsize, has_mipmapcount;
    const char *m;
 
    m = eina_file_map_all(loader->f, EINA_FILE_SEQUENTIAL);
@@ -75,15 +180,133 @@ evas_image_load_file_head_dds(void *loader_data,
         return EINA_FALSE;
      }
 
+   *error = EVAS_LOAD_ERROR_CORRUPT_FILE;
    if (strncmp(m, "DDS ", 4) != 0)
+     // TODO: Add support for DX10
+     FAIL();
+   m += 4;
+
+   // Read DDS_HEADER
+   if (_dword_read(&m) != 124)
+     FAIL();
+
+   flags = _dword_read(&m);
+   if ((flags & base_flags) != (base_flags))
+     FAIL();
+
+   if ((flags & ~(DDSD_MIPMAPCOUNT | DDSD_LINEARSIZE)) != base_flags)
      {
-        *error = EVAS_LOAD_ERROR_CORRUPT_FILE;
-        return EINA_FALSE;
+        // TODO: A lot of modes are not supported.
+        *error = EVAS_LOAD_ERROR_UNKNOWN_FORMAT;
+        FAIL();
      }
 
-   // TODO
+   has_linearsize = !!(flags & DDSD_LINEARSIZE);
+   if (!has_linearsize)
+     FAIL();
+
+   has_mipmapcount = !!(flags & DDSD_MIPMAPCOUNT);
+   (void) has_mipmapcount; // We don't really care about it.
 
-   return EINA_TRUE;
+   height = _dword_read(&m);
+   width = _dword_read(&m);
+   pitchOrLinearSize = _dword_read(&m);
+   if (!width || !height || (width & 0x3) || (height & 0x3))
+     FAIL();
+
+   // Skip depth & mipmap count + reserved[11]
+   m += 13 * sizeof(uint32_t);
+   // Entering DDS_PIXELFORMAT ddspf
+   if (_dword_read(&m) != 32)
+     FAIL();
+   loader->pf.flags = _dword_read(&m);
+   if (!(loader->pf.flags & DDPF_FOURCC))
+     FAIL(); // Unsupported (uncompressed formats may not have a FOURCC)
+   loader->pf.fourcc = _dword_read(&m);
+   loader->pf.has_alpha = EINA_TRUE;
+   switch (loader->pf.fourcc)
+     {
+      case FOURCC('D', 'X', 'T', '1'):
+        loader->format = DXT1;
+        if ((loader->pf.flags & DDPF_ALPHAPIXELS) == 0)
+          {
+             prop->alpha = EINA_FALSE;
+             prop->cspaces = cspaces_s3tc_dxt1_rgb;
+             loader->pf.has_alpha = EINA_FALSE;
+          }
+        else
+          {
+             prop->alpha = EINA_TRUE;
+             prop->cspaces = cspaces_s3tc_dxt1_rgba;
+          }
+        block_size = 8;
+        break;
+#if 0
+      case FOURCC('D', 'X', 'T', '2'):
+        loader->format = DXT2;
+        prop->alpha = EINA_TRUE;
+        prop->cspaces = cspaces_s3tc_dxt2;
+        break;
+      case FOURCC('D', 'X', 'T', '3'):
+        loader->format = DXT3;
+        prop->alpha = EINA_TRUE;
+        prop->cspaces = cspaces_s3tc_dxt5;
+        break;
+      case FOURCC('D', 'X', 'T', '4'):
+        loader->format = DXT4;
+        prop->alpha = EINA_TRUE;
+        prop->cspaces = cspaces_s3tc_dxt4;
+        break;
+      case FOURCC('D', 'X', 'T', '5'):
+        loader->format = DXT5;
+        prop->alpha = EINA_TRUE;
+        prop->cspaces = cspaces_s3tc_dxt5;
+        break;
+      case FOURCC('D', 'X', '1', '0'):
+        loader->format = DX10;
+        *error = EVAS_LOAD_ERROR_UNKNOWN_FORMAT;
+        FAIL();
+#endif
+      default:
+        // TODO: Implement decoding support for uncompressed formats
+        FAIL();
+     }
+   loader->pf.rgb_bitcount = _dword_read(&m);
+   loader->pf.r_mask = _dword_read(&m);
+   loader->pf.g_mask = _dword_read(&m);
+   loader->pf.b_mask = _dword_read(&m);
+   loader->pf.a_mask = _dword_read(&m);
+   caps = _dword_read(&m);
+   if ((caps & DDSCAPS_TEXTURE) == 0)
+     {
+        *error = EVAS_LOAD_ERROR_UNKNOWN_FORMAT;
+        FAIL();
+     }
+   caps2 = _dword_read(&m);
+   if (caps2 != 0)
+     {
+        // Cube maps not supported
+        *error = EVAS_LOAD_ERROR_UNKNOWN_FORMAT;
+        FAIL();
+     }
+   // Since the rest is unused, just ignore it.
+
+   stride = ((width + 3) >> 2) * block_size;
+   loader->data_size = stride * ((height + 3) >> 2);
+   if (loader->data_size != pitchOrLinearSize)
+     FAIL(); // Invalid size!
+
+   // Check file size
+   if (eina_file_size_get(loader->f) < (128 + loader->data_size))
+     FAIL();
+
+   prop->h = height;
+   prop->w = width;
+   *error = EVAS_LOAD_ERROR_NONE;
+
+on_error:
+   eina_file_map_free(loader->f, (void *) m);
+   return (*error == EVAS_LOAD_ERROR_NONE);
 }
 
 Eina_Bool
@@ -104,7 +327,8 @@ evas_image_load_file_data_dds(void *loader_data,
 
    // TODO
 
-   r = EINA_TRUE;
+   *error = EVAS_LOAD_ERROR_GENERIC;
+   r = EINA_FALSE; // FIXME
 
 on_error:
    eina_file_map_free(loader->f, m);
diff --git a/src/modules/evas/loaders/dds/s3tc.h b/src/modules/evas/loaders/dds/s3tc.h
new file mode 100644 (file)
index 0000000..8380c3a
--- /dev/null
@@ -0,0 +1,13 @@
+#ifndef EFL_S3TC_H
+#define EFL_S3TC_H
+
+typedef enum {
+   DXT1,
+   DXT2,
+   DXT3,
+   DXT4,
+   DXT5,
+   DX10
+} DxtFormat;
+
+#endif // EFL_S3TC_H
diff --git a/src/modules/evas/loaders/dds/s3tc_decoder.c b/src/modules/evas/loaders/dds/s3tc_decoder.c
new file mode 100644 (file)
index 0000000..44492dc
--- /dev/null
@@ -0,0 +1,2 @@
+#include "s3tc.h"
+