jpeg pushed a commit to branch master.

http://git.enlightenment.org/core/efl.git/commit/?id=0bba7422cb5a8dd71c864ecb0bebf78767d21d3e

commit 0bba7422cb5a8dd71c864ecb0bebf78767d21d3e
Author: Jean-Philippe Andre <jp.an...@samsung.com>
Date:   Mon Jul 7 19:58:50 2014 +0900

    Evas TGV: Add support for ETC1+Alpha
    
    Save images with alpha in two planes:
    - RGB data as ETC1
    - Alpha as ETC1 (from a greyscale image)
    
    The second plane alpha is located right after the RGB plane.
    
    The RGBA data is not premultiplied, so that RGB can be encoded
    at a better quality in ETC1. This should avoid some blockiness
    artifacts that we can see in the current ETC2 mode (which supports
    alpha natively). Eventually ETC2 should also support non
    premultiplied data for a better encoding quality.
    
    This patch implements the saver and the loader.
    
    @feature
---
 src/modules/evas/loaders/tgv/evas_image_load_tgv.c | 312 ++++++++++--------
 src/modules/evas/savers/tgv/evas_image_save_tgv.c  | 353 +++++++++++++--------
 2 files changed, 398 insertions(+), 267 deletions(-)

diff --git a/src/modules/evas/loaders/tgv/evas_image_load_tgv.c 
b/src/modules/evas/loaders/tgv/evas_image_load_tgv.c
index 7ff0fc3..2ac56c4 100644
--- a/src/modules/evas/loaders/tgv/evas_image_load_tgv.c
+++ b/src/modules/evas/loaders/tgv/evas_image_load_tgv.c
@@ -18,6 +18,20 @@
 #include <arm_neon.h>
 #endif
 
+#ifndef WORDS_BIGENDIAN
+/* x86 */
+#define A_VAL(p) (((uint8_t *)(p))[3])
+#define R_VAL(p) (((uint8_t *)(p))[2])
+#define G_VAL(p) (((uint8_t *)(p))[1])
+#define B_VAL(p) (((uint8_t *)(p))[0])
+#else
+/* ppc */
+#define A_VAL(p) (((uint8_t *)(p))[0])
+#define R_VAL(p) (((uint8_t *)(p))[1])
+#define G_VAL(p) (((uint8_t *)(p))[2])
+#define B_VAL(p) (((uint8_t *)(p))[3])
+#endif
+
 /**************************************************************
  * The TGV file format is oriented around compression mecanism
  * that hardware are good at decompressing. We do still provide
@@ -34,18 +48,21 @@
  * The file format is as follow :
  * - char     magic[4]: "TGV1"
  * - uint8_t  block_size (real block size = (4 << bits[0-3], 4 << bits[4-7])
- * - uint8_t  algorithm (0 -> ETC1)
- * - uint8_t  options[2] (1 -> lz4)
+ * - uint8_t  algorithm (0 -> ETC1, 1 -> ETC2 RGB, 2 -> ETC2 RGBA, 3 -> 
ETC1+Alpha)
+ * - uint8_t  options[2] (bitmask: 1 -> lz4, 2 for block-less, 4 -> 
unpremultiplied)
  * - uint32_t width
  * - uint32_t height
  * - blocks[]
  *   - 0 length encoded compress size (if length == 64 * block_size => no 
compression)
  *   - lzma encoded etc1 block
+ *
+ * If the format is ETC1+Alpha (algo = 3), then a second image is encoded
+ * in ETC1 right after the first one, and it contains grey-scale alpha
+ * values.
  **************************************************************/
 
 // FIXME: wondering if we should support mipmap
 // TODO: support ETC1+ETC2 images (RGB only)
-// TODO: support ETC1 RGB + ETC1 Alpha (2 textures for ETC1 RGBA)
 
 typedef struct _Evas_Loader_Internal Evas_Loader_Internal;
 struct _Evas_Loader_Internal
@@ -67,6 +84,7 @@ struct _Evas_Loader_Internal
 
    Eina_Bool compress : 1;
    Eina_Bool blockless : 1; // Special mode used when copying data directly
+   Eina_Bool unpremul : 1;
 };
 
 static const Evas_Colorspace cspaces_etc1[2] = {
@@ -84,6 +102,11 @@ static const Evas_Colorspace cspaces_rgba8_etc2_eac[2] = {
   EVAS_COLORSPACE_ARGB8888
 };
 
+static const Evas_Colorspace cspaces_etc1_alpha[2] = {
+  EVAS_COLORSPACE_ETC1_ALPHA,
+  EVAS_COLORSPACE_ARGB8888
+};
+
 static void *
 evas_image_load_file_open_tgv(Eina_File *f, Eina_Stringshare *key EINA_UNUSED,
                               Evas_Image_Load_Opts *opts,
@@ -195,6 +218,13 @@ evas_image_load_file_head_tgv(void *loader_data,
         loader->cspace = EVAS_COLORSPACE_RGBA8_ETC2_EAC;
         prop->alpha = EINA_TRUE;
         break;
+      case 3:
+        prop->cspaces = cspaces_etc1_alpha;
+        loader->cspace = EVAS_COLORSPACE_ETC1_ALPHA;
+        loader->unpremul = !!(m[OFFSET_OPTIONS] & 0x4);
+        prop->alpha = EINA_TRUE;
+        prop->premul = loader->unpremul;
+        break;
       default:
         *error = EVAS_LOAD_ERROR_CORRUPT_FILE;
         goto on_error;
@@ -241,8 +271,8 @@ evas_image_load_file_head_tgv(void *loader_data,
    prop->h = loader->size.height;
    prop->borders.l = 1;
    prop->borders.t = 1;
-   prop->borders.r = roundup(loader->size.width + 2, 4) - prop->w - 1;
-   prop->borders.b = roundup(loader->size.height + 2, 4) - prop->h - 1;
+   prop->borders.r = roundup(prop->w + 2, 4) - prop->w - 1;
+   prop->borders.b = roundup(prop->h + 2, 4) - prop->h - 1;
 
    ret = EINA_TRUE;
 
@@ -292,6 +322,7 @@ evas_image_load_file_data_tgv(void *loader_data,
    unsigned int etc_width = 0;
    unsigned int etc_block_size;
    Eina_Bool r = EINA_FALSE;
+   int num_planes = 1, plane, alpha_offset = 0;
 
    length = eina_file_size_get(loader->f);
    offset = OFFSET_BLOCKS;
@@ -309,25 +340,27 @@ evas_image_load_file_data_tgv(void *loader_data,
    switch (loader->cspace)
      {
       case EVAS_COLORSPACE_ETC1:
-        etc_block_size = 8;
-        etc_width = ((prop->w + 2) / 4 + ((prop->w + 2) % 4 ? 1 : 0)) * 8;
-        break;
       case EVAS_COLORSPACE_RGB8_ETC2:
         etc_block_size = 8;
-        etc_width = ((prop->w + 2) / 4 + ((prop->w + 2) % 4 ? 1 : 0)) * 8;
         break;
       case EVAS_COLORSPACE_RGBA8_ETC2_EAC:
         etc_block_size = 16;
-        etc_width = ((prop->w + 2) / 4 + ((prop->w + 2) % 4 ? 1 : 0)) * 16;
+        break;
+      case EVAS_COLORSPACE_ETC1_ALPHA:
+        etc_block_size = 8;
+        num_planes = 2;
+        alpha_offset = ((prop->w + 2 + 3) / 4) * ((prop->h + 2 + 3) / 4) * 8 / 
sizeof(*p_etc);
         break;
       default: abort();
      }
+   etc_width = ((prop->w + 2 + 3) / 4) * etc_block_size;
 
    switch (prop->cspace)
      {
       case EVAS_COLORSPACE_ETC1:
       case EVAS_COLORSPACE_RGB8_ETC2:
       case EVAS_COLORSPACE_RGBA8_ETC2_EAC:
+      case EVAS_COLORSPACE_ETC1_ALPHA:
         if (master.x % 4 || master.y % 4)
           abort();
         break;
@@ -352,125 +385,152 @@ evas_image_load_file_data_tgv(void *loader_data,
    if (loader->compress)
      buffer = alloca(etc_block_size * block_count);
 
-   for (y = 0; y < loader->size.height + 2; y += loader->block.height)
-     for (x = 0; x < loader->size.width + 2; x += loader->block.width)
-       {
-          Eina_Rectangle current;
-          const char *data_start;
-          const char *it;
-          unsigned int expand_length;
-          unsigned int i, j;
-
-          block_length = _tgv_length_get(m + offset, length, &offset);
-
-          if (block_length == 0) goto on_error;
-
-          data_start = m + offset;
-          offset += block_length;
-
-          EINA_RECTANGLE_SET(&current, x, y,
-                             loader->block.width, loader->block.height);
-
-          if (!eina_rectangle_intersection(&current, &master))
-            continue;
-
-          if (loader->compress)
-            {
-               expand_length = LZ4_uncompress(data_start, buffer,
-                                              block_count * etc_block_size);
-               // That's an overhead for now, need to be fixed
-               if (expand_length != block_length)
-                 goto on_error;
-            }
-          else
-            {
-               buffer = (void*) data_start;
-               if (block_count * etc_block_size != block_length)
-                 goto on_error;
-            }
-          it = buffer;
-
-          for (i = 0; i < loader->block.height; i += 4)
-            for (j = 0; j < loader->block.width; j += 4, it += etc_block_size)
+   for (plane = 0; plane < num_planes; plane++)
+     for (y = 0; y < loader->size.height + 2; y += loader->block.height)
+       for (x = 0; x < loader->size.width + 2; x += loader->block.width)
+         {
+            Eina_Rectangle current;
+            const char *data_start;
+            const char *it;
+            unsigned int expand_length;
+            unsigned int i, j;
+
+            block_length = _tgv_length_get(m + offset, length, &offset);
+
+            if (block_length == 0) goto on_error;
+
+            data_start = m + offset;
+            offset += block_length;
+
+            EINA_RECTANGLE_SET(&current, x, y,
+                               loader->block.width, loader->block.height);
+
+            if (!eina_rectangle_intersection(&current, &master))
+              continue;
+
+            if (loader->compress)
+              {
+                 expand_length = LZ4_uncompress(data_start, buffer,
+                                                block_count * etc_block_size);
+                 // That's an overhead for now, need to be fixed
+                 if (expand_length != block_length)
+                   goto on_error;
+              }
+            else
               {
-                 Eina_Rectangle current_etc;
-                 unsigned int temporary[4 * 4];
-                 unsigned int offset_x, offset_y;
-                 int k;
-
-                 EINA_RECTANGLE_SET(&current_etc, x + j, y + i, 4, 4);
-
-                 if (!eina_rectangle_intersection(&current_etc, &current))
-                   continue;
-
-                 switch (prop->cspace)
-                   {
-                    case EVAS_COLORSPACE_ARGB8888:
-                      switch (loader->cspace)
-                        {
-                         case EVAS_COLORSPACE_ETC1:
-                           if (!rg_etc1_unpack_block(it, temporary, 0))
-                             {
-                                // TODO: Should we decode as RGB8_ETC2?
-                                fprintf(stderr, "ETC1: Block starting at {%i, 
%i} is corrupted!\n", x + j, y + i);
-                                continue;
-                             }
-                           break;
-                         case EVAS_COLORSPACE_RGB8_ETC2:
-                           rg_etc2_rgb8_decode_block((uint8_t *) it, 
temporary);
-                           break;
-                         case EVAS_COLORSPACE_RGBA8_ETC2_EAC:
-                           rg_etc2_rgba8_decode_block((uint8_t *) it, 
temporary);
-                           break;
-                         default: abort();
-                        }
-
-                       offset_x = current_etc.x - x - j;
-                       offset_y = current_etc.y - y - i;
+                 buffer = (void*) data_start;
+                 if (block_count * etc_block_size != block_length)
+                   goto on_error;
+              }
+            it = buffer;
+
+            for (i = 0; i < loader->block.height; i += 4)
+              for (j = 0; j < loader->block.width; j += 4, it += 
etc_block_size)
+                {
+                   Eina_Rectangle current_etc;
+                   unsigned int temporary[4 * 4];
+                   unsigned int offset_x, offset_y;
+                   int k, l;
+
+                   EINA_RECTANGLE_SET(&current_etc, x + j, y + i, 4, 4);
+
+                   if (!eina_rectangle_intersection(&current_etc, &current))
+                     continue;
+
+                   switch (prop->cspace)
+                     {
+                      case EVAS_COLORSPACE_ARGB8888:
+                        switch (loader->cspace)
+                          {
+                           case EVAS_COLORSPACE_ETC1:
+                           case EVAS_COLORSPACE_ETC1_ALPHA:
+                             if (!rg_etc1_unpack_block(it, temporary, 0))
+                               {
+                                  // TODO: Should we decode as RGB8_ETC2?
+                                  fprintf(stderr, "ETC1: Block starting at 
{%i, %i} is corrupted!\n", x + j, y + i);
+                                  continue;
+                               }
+                             break;
+                           case EVAS_COLORSPACE_RGB8_ETC2:
+                             rg_etc2_rgb8_decode_block((uint8_t *) it, 
temporary);
+                             break;
+                           case EVAS_COLORSPACE_RGBA8_ETC2_EAC:
+                             rg_etc2_rgba8_decode_block((uint8_t *) it, 
temporary);
+                             break;
+                           default: abort();
+                          }
+
+                        offset_x = current_etc.x - x - j;
+                        offset_y = current_etc.y - y - i;
+
+                        if (!plane)
+                          {
 #ifdef BUILD_NEON
-                       if (evas_common_cpu_has_feature(CPU_FEATURE_NEON))
-                         {
-                            uint32_t *dst = &p[current_etc.x - 1 + 
(current_etc.y - 1) * master.w];
-                            uint32_t *src = &temporary[offset_x + offset_y * 
4];
-                            for (k = 0; k < current_etc.h; k++)
-                              {
-                                 if (current_etc.w == 4)
-                                   vst1q_u32(dst, vld1q_u32(src));
-                                 else if (current_etc.w == 3)
-                                   {
-                                      vst1_u32(dst, vld1_u32(src));
-                                      *(dst + 2) = *(src + 2);
-                                   }
-                                 else if (current_etc.w == 2)
-                                   vst1_u32(dst, vld1_u32(src));
-                                 else
-                                   *dst = *src;
-                                 dst += master.w;
-                                 src += 4;
-                              }
-                         }
-                       else
+                             if (evas_common_cpu_has_feature(CPU_FEATURE_NEON))
+                               {
+                                  uint32_t *dst = &p[current_etc.x - 1 + 
(current_etc.y - 1) * master.w];
+                                  uint32_t *src = &temporary[offset_x + 
offset_y * 4];
+                                  for (k = 0; k < current_etc.h; k++)
+                                    {
+                                       if (current_etc.w == 4)
+                                         vst1q_u32(dst, vld1q_u32(src));
+                                       else if (current_etc.w == 3)
+                                         {
+                                            vst1_u32(dst, vld1_u32(src));
+                                            *(dst + 2) = *(src + 2);
+                                         }
+                                       else if (current_etc.w == 2)
+                                         vst1_u32(dst, vld1_u32(src));
+                                       else
+                                          *dst = *src;
+                                       dst += master.w;
+                                       src += 4;
+                                    }
+                               }
+                             else
 #endif
-                       for (k = 0; k < current_etc.h; k++)
-                         {
-                            memcpy(&p[current_etc.x - 1 +
-                                      (current_etc.y - 1 + k) * master.w],
-                                   &temporary[offset_x + (offset_y + k) * 4],
-                                   current_etc.w * sizeof (unsigned int));
-                         }
-                       break;
-                    case EVAS_COLORSPACE_ETC1:
-                    case EVAS_COLORSPACE_RGB8_ETC2:
-                    case EVAS_COLORSPACE_RGBA8_ETC2_EAC:
-                       memcpy(&p_etc[(current_etc.x / 4) * etc_block_size +
-                                     (current_etc.y / 4) * etc_width],
-                              it, etc_block_size);
-                       break;
-                    default:
-                       abort();
-                   }
-              }
-       }
+                             for (k = 0; k < current_etc.h; k++)
+                               {
+                                  memcpy(&p[current_etc.x - 1 + (current_etc.y 
- 1 + k) * master.w],
+                                         &temporary[offset_x + (offset_y + k) 
* 4],
+                                         current_etc.w * sizeof (unsigned 
int));
+                               }
+                          }
+                        else
+                          {
+                             for (k = 0; k < current_etc.h; k++)
+                               for (l = 0; l < current_etc.w; l++)
+                                 {
+                                    unsigned int *rgbdata =
+                                      &p[current_etc.x - 1 + (current_etc.y - 
1 + k) * master.w + l];
+                                    unsigned int *adata =
+                                      &temporary[offset_x + (offset_y + k) * 4 
+ l];
+                                    A_VAL(rgbdata) = G_VAL(adata);
+                                 }
+                          }
+                        break;
+                      case EVAS_COLORSPACE_ETC1:
+                      case EVAS_COLORSPACE_RGB8_ETC2:
+                      case EVAS_COLORSPACE_RGBA8_ETC2_EAC:
+                        memcpy(&p_etc[(current_etc.x / 4) * etc_block_size +
+                                      (current_etc.y / 4) * etc_width],
+                               it, etc_block_size);
+                        break;
+                      case EVAS_COLORSPACE_ETC1_ALPHA:
+                        memcpy(&p_etc[(current_etc.x / 4) * etc_block_size +
+                                      (current_etc.y / 4) * etc_width +
+                                      plane * alpha_offset],
+                               it, etc_block_size);
+                        break;
+                      default:
+                        abort();
+                     }
+                } // bx,by inside blocks
+         } // x,y macroblocks
+
+   // TODO: Add support for more unpremultiplied modes (ETC2)
+   if (prop->cspace == EVAS_COLORSPACE_ARGB8888)
+     prop->premul = loader->unpremul; // call premul if unpremul data
 
    r = EINA_TRUE;
    *error = EVAS_LOAD_ERROR_NONE;
diff --git a/src/modules/evas/savers/tgv/evas_image_save_tgv.c 
b/src/modules/evas/savers/tgv/evas_image_save_tgv.c
index 2c1d031..b1ed426 100644
--- a/src/modules/evas/savers/tgv/evas_image_save_tgv.c
+++ b/src/modules/evas/savers/tgv/evas_image_save_tgv.c
@@ -32,16 +32,28 @@ _block_size_get(int size)
    return MIN(k, MAX_BLOCK);
 }
 
+static inline void
+_alpha_to_greyscale_convert(uint32_t *data, int len)
+{
+   for (int k = 0; k < len; k++)
+     {
+        int alpha = A_VAL(data);
+        *data++ = ARGB_JOIN(alpha, alpha, alpha, alpha);
+     }
+}
+
 static int
 _save_direct_tgv(RGBA_Image *im, const char *file, int compress)
 {
+   // FIXME: Now we have border information, this comment isn't valid anymore:
+
    // In case we are directly copying ETC1/2 data, we can't properly
    // duplicate the 1 pixel borders. So we just assume the image contains
    // them already.
 
    // TODO: Add block by block compression.
 
-   int image_width, image_height;
+   int image_width, image_height, planes = 1;
    uint32_t width, height;
    uint8_t header[8] = "TGV1";
    int etc_block_size, etc_data_size, buffer_size, data_size, remain;
@@ -77,6 +89,12 @@ _save_direct_tgv(RGBA_Image *im, const char *file, int 
compress)
         etc_block_size = 16;
         header[5] = 2;
         break;
+      case EVAS_COLORSPACE_ETC1_ALPHA:
+        // FIXME: Properly handle premul vs. unpremul data
+        etc_block_size = 8;
+        header[5] = 3;
+        planes = 2;
+        break;
       default:
         return 0;
      }
@@ -95,7 +113,7 @@ _save_direct_tgv(RGBA_Image *im, const char *file, int 
compress)
    if (fwrite(&width, sizeof (uint32_t), 1, f) != 1) goto on_error;
    if (fwrite(&height, sizeof (uint32_t), 1, f) != 1) goto on_error;
 
-   etc_data_size = image_width * image_height * etc_block_size / 16;
+   etc_data_size = image_width * image_height * etc_block_size * planes / 16;
    if (compress)
      {
         buffer_size = LZ4_compressBound(etc_data_size);
@@ -156,13 +174,14 @@ evas_image_save_file_tgv(RGBA_Image *im,
    FILE *f;
    uint8_t *comp = NULL;
    uint8_t *buffer;
-   uint32_t *data;
-   uint32_t width, height;
+   uint32_t *data = NULL;
+   uint32_t nl_width, nl_height;
    uint8_t header[8] = "TGV1";
    int block_width, block_height, macro_block_width, macro_block_height;
    int block_count, image_stride, image_height, etc_block_size;
    Evas_Colorspace cspace;
-   Eina_Bool alpha;
+   Eina_Bool alpha, alpha_texture = EINA_FALSE, unpremul = EINA_FALSE;
+   int num_planes = 1;
 
 #ifdef DEBUG_STATS
    struct timespec ts1, ts2;
@@ -181,6 +200,8 @@ evas_image_save_file_tgv(RGBA_Image *im,
       case EVAS_COLORSPACE_ETC1:
       case EVAS_COLORSPACE_RGB8_ETC2:
       case EVAS_COLORSPACE_RGBA8_ETC2_EAC:
+      case EVAS_COLORSPACE_ETC1_ALPHA:
+        // Note: This case should probably never happen
         if (encoding)
           WRN("Ignoring 'encoding' argument the data is already ETC1/2");
         return _save_direct_tgv(im, file, compress);
@@ -190,9 +211,8 @@ evas_image_save_file_tgv(RGBA_Image *im,
 
    image_stride = im->cache_entry.w;
    image_height = im->cache_entry.h;
-   data = im->image.data;
-   width = htonl(image_stride);
-   height = htonl(image_height);
+   nl_width = htonl(image_stride);
+   nl_height = htonl(image_height);
    compress = !!compress;
 
    // Disable dithering, as it will deteriorate the quality of flat surfaces
@@ -210,13 +230,14 @@ evas_image_save_file_tgv(RGBA_Image *im,
    block_height = _block_size_get(image_height + 2);
    header[4] = (block_height << 4) | block_width;
 
-   // header[5]: 0 for ETC1, 1 for RGB8_ETC2, 2 for RGBA8_ETC2_EAC
+   // header[5]: 0 for ETC1, 1 for RGB8_ETC2, 2 for RGBA8_ETC2_EAC, 3 for 
ETC1+Alpha
    if (!encoding) encoding = "etc2";
    if (!strcasecmp(encoding, "etc1"))
      {
         if (alpha)
           {
-             ERR("ETC1 does not support alpha encoding. Abort.");
+             ERR("ETC1 does not support alpha encoding. Abort. "
+                 "Please use 'encoding=etc1+alpha' for ETC1+Alpha encoding.");
              return 0;
           }
         cspace = EVAS_COLORSPACE_ETC1;
@@ -238,16 +259,38 @@ evas_image_save_file_tgv(RGBA_Image *im,
              header[5] = 2;
           }
      }
+   else if (!strcasecmp(encoding, "etc1+alpha"))
+     {
+        if (!alpha)
+          {
+             INF("Selected etc1+alpha but image has no alpha, reverting to 
etc1.");
+             cspace = EVAS_COLORSPACE_ETC1;
+             etc_block_size = 8;
+             header[5] = 0;
+          }
+        else
+          {
+             // Save as two textures, and unpremul the data
+             alpha_texture = EINA_TRUE;
+             unpremul = EINA_TRUE;
+             num_planes = 2; // RGB and Alpha
+             cspace = EVAS_COLORSPACE_ETC1_ALPHA;
+             etc_block_size = 8;
+             header[5] = 3;
+          }
+     }
    else
      {
         ERR("Unknown encoding '%.8s' selected. Abort.", encoding);
         return 0;
      }
 
-   // header[6]: 0 for raw, 1, for LZ4 compressed
-   header[6] = compress;
+   // header[6]: 0 for raw, 1, for LZ4 compressed, 4 for unpremultiplied RGBA
+   // blockless mode (0x2) is never used here
+   header[6] = (compress ? 0x1 : 0x0) | (unpremul ? 0x4 : 0x0);
 
-   // header[7]: options (unused)
+   // header[7]: unused options
+   // Note: consider extending the header instead of filling all the bits here
    header[7] = 0;
 
    f = fopen(file, "w");
@@ -255,8 +298,8 @@ evas_image_save_file_tgv(RGBA_Image *im,
 
    // Write header
    if (fwrite(header, sizeof (uint8_t), 8, f) != 8) goto on_error;
-   if (fwrite(&width, sizeof (uint32_t), 1, f) != 1) goto on_error;
-   if (fwrite(&height, sizeof (uint32_t), 1, f) != 1) goto on_error;
+   if (fwrite(&nl_width, sizeof (uint32_t), 1, f) != 1) goto on_error;
+   if (fwrite(&nl_height, sizeof (uint32_t), 1, f) != 1) goto on_error;
 
    // Real block size in pixels, obviously a multiple of 4
    macro_block_width = 4 << block_width;
@@ -269,164 +312,190 @@ evas_image_save_file_tgv(RGBA_Image *im,
    if (compress)
      comp = alloca(LZ4_compressBound(block_count * etc_block_size));
 
-   // Write macro block
-   for (int y = 0; y < image_height + 2; y += macro_block_height)
+   // Write a whole plane (RGB or Alpha)
+   for (int plane = 0; plane < num_planes; plane++)
      {
-        uint32_t *input, *last_col, *last_row, *last_pix;
-        int real_y;
-        int wlen;
-
-        if (y == 0) real_y = 0;
-        else if (y < image_height + 1) real_y = y - 1;
-        else real_y = image_height - 1;
-
-        for (int x = 0; x < image_stride + 2; x += macro_block_width)
+        if (!alpha_texture)
+          {
+             // Normal mode
+             data = im->image.data;
+          }
+        else if (!plane)
+          {
+             int len = image_stride * image_height;
+             // RGB plane for ETC1+Alpha
+             data = malloc(len * 4);
+             if (!data) goto on_error;
+             memcpy(data, im->image.data, len * 4);
+             if (unpremul) evas_data_argb_unpremul(data, len);
+          }
+        else
           {
-             uint8_t *offset = buffer;
-             int real_x = x;
+             // Alpha plane for ETC1+Alpha
+             _alpha_to_greyscale_convert(data, image_stride * image_height);
+          }
 
-             if (x == 0) real_x = 0;
-             else if (x < image_stride + 1) real_x = x - 1;
-             else real_x = image_stride - 1;
+        // Write macro block
+        for (int y = 0; y < image_height + 2; y += macro_block_height)
+          {
+             uint32_t *input, *last_col, *last_row, *last_pix;
+             int real_y;
+             int wlen;
 
-             input = data + real_y * image_stride + real_x;
-             last_row = data + image_stride * (image_height - 1) + real_x;
-             last_col = data + (real_y + 1) * image_stride - 1;
-             last_pix = data + image_height * image_stride - 1;
+             if (y == 0) real_y = 0;
+             else if (y < image_height + 1) real_y = y - 1;
+             else real_y = image_height - 1;
 
-             for (int by = 0; by < macro_block_height; by += 4)
+             for (int x = 0; x < image_stride + 2; x += macro_block_width)
                {
-                  int dup_top = ((y + by) == 0) ? 1 : 0;
-                  int max_row = MAX(0, MIN(4, image_height - real_y - by));
-                  int oy = (y == 0) ? 1 : 0;
+                  uint8_t *offset = buffer;
+                  int real_x = x;
+
+                  if (x == 0) real_x = 0;
+                  else if (x < image_stride + 1) real_x = x - 1;
+                  else real_x = image_stride - 1;
 
-                  for (int bx = 0; bx < macro_block_width; bx += 4)
+                  input = data + real_y * image_stride + real_x;
+                  last_row = data + image_stride * (image_height - 1) + real_x;
+                  last_col = data + (real_y + 1) * image_stride - 1;
+                  last_pix = data + image_height * image_stride - 1;
+
+                  for (int by = 0; by < macro_block_height; by += 4)
                     {
-                       int dup_left = ((x + bx) == 0) ? 1 : 0;
-                       int max_col = MAX(0, MIN(4, image_stride - real_x - 
bx));
-                       uint32_t todo[16] = { 0 };
-                       int row, col;
-                       int ox = (x == 0) ? 1 : 0;
+                       int dup_top = ((y + by) == 0) ? 1 : 0;
+                       int max_row = MAX(0, MIN(4, image_height - real_y - 
by));
+                       int oy = (y == 0) ? 1 : 0;
 
-                       if (dup_left)
+                       for (int bx = 0; bx < macro_block_width; bx += 4)
                          {
-                            // Duplicate left column
-                            for (row = 0; row < max_row; row++)
-                              todo[row * 4] = input[row * image_stride];
-                            for (row = max_row; row < 4; row++)
-                              todo[row * 4] = last_row[0];
-                         }
+                            int dup_left = ((x + bx) == 0) ? 1 : 0;
+                            int max_col = MAX(0, MIN(4, image_stride - real_x 
- bx));
+                            uint32_t todo[16] = { 0 };
+                            int row, col;
+                            int ox = (x == 0) ? 1 : 0;
 
-                       if (dup_top)
-                         {
-                            // Duplicate top row
-                            for (col = 0; col < max_col; col++)
-                              todo[col] = input[MAX(col + bx - ox, 0)];
-                            for (col = max_col; col < 4; col++)
-                              todo[col] = last_col[0];
-                         }
+                            if (dup_left)
+                              {
+                                 // Duplicate left column
+                                 for (row = 0; row < max_row; row++)
+                                   todo[row * 4] = input[row * image_stride];
+                                 for (row = max_row; row < 4; row++)
+                                   todo[row * 4] = last_row[0];
+                              }
 
-                       for (row = dup_top; row < 4; row++)
-                         {
-                            for (col = dup_left; col < max_col; col++)
+                            if (dup_top)
                               {
-                                 if (row < max_row)
-                                   {
-                                      // Normal copy
-                                      todo[row * 4 + col] = input[(row + by - 
oy) * image_stride + bx + col - ox];
-                                   }
-                                 else
-                                   {
-                                      // Copy last line
-                                      todo[row * 4 + col] = last_row[col + bx 
- ox];
-                                   }
+                                 // Duplicate top row
+                                 for (col = 0; col < max_col; col++)
+                                   todo[col] = input[MAX(col + bx - ox, 0)];
+                                 for (col = max_col; col < 4; col++)
+                                   todo[col] = last_col[0];
                               }
-                            for (col = max_col; col < 4; col++)
+
+                            for (row = dup_top; row < 4; row++)
                               {
-                                 // Right edge
-                                 if (row < max_row)
+                                 for (col = dup_left; col < max_col; col++)
                                    {
-                                      // Duplicate last column
-                                      todo[row * 4 + col] = last_col[MAX(row + 
by - oy, 0) * image_stride];
+                                      if (row < max_row)
+                                        {
+                                           // Normal copy
+                                           todo[row * 4 + col] = input[(row + 
by - oy) * image_stride + bx + col - ox];
+                                        }
+                                      else
+                                        {
+                                           // Copy last line
+                                           todo[row * 4 + col] = last_row[col 
+ bx - ox];
+                                        }
                                    }
-                                 else
+                                 for (col = max_col; col < 4; col++)
                                    {
-                                      // Duplicate very last pixel again and 
again
-                                      todo[row * 4 + col] = *last_pix;
+                                      // Right edge
+                                      if (row < max_row)
+                                        {
+                                           // Duplicate last column
+                                           todo[row * 4 + col] = 
last_col[MAX(row + by - oy, 0) * image_stride];
+                                        }
+                                      else
+                                        {
+                                           // Duplicate very last pixel again 
and again
+                                           todo[row * 4 + col] = *last_pix;
+                                        }
                                    }
                               }
-                         }
 
-                       switch (cspace)
-                         {
-                          case EVAS_COLORSPACE_ETC1:
-                            rg_etc1_pack_block(offset, (uint32_t *) todo, 
&param);
-                            break;
-                          case EVAS_COLORSPACE_RGB8_ETC2:
-                            etc2_rgb8_block_pack(offset, (uint32_t *) todo, 
&param);
-                            break;
-                          case EVAS_COLORSPACE_RGBA8_ETC2_EAC:
-                            etc2_rgba8_block_pack(offset, (uint32_t *) todo, 
&param);
-                            break;
-                          default: return 0;
-                         }
+                            switch (cspace)
+                              {
+                               case EVAS_COLORSPACE_ETC1:
+                               case EVAS_COLORSPACE_ETC1_ALPHA:
+                                 rg_etc1_pack_block(offset, (uint32_t *) todo, 
&param);
+                                 break;
+                               case EVAS_COLORSPACE_RGB8_ETC2:
+                                 etc2_rgb8_block_pack(offset, (uint32_t *) 
todo, &param);
+                                 break;
+                               case EVAS_COLORSPACE_RGBA8_ETC2_EAC:
+                                 etc2_rgba8_block_pack(offset, (uint32_t *) 
todo, &param);
+                                 break;
+                               default: return 0;
+                              }
 
 #ifdef DEBUG_STATS
-                       {
-                          // Decode to compute PSNR, this is slow.
-                          uint32_t done[16];
-
-                          if (alpha)
-                            rg_etc2_rgba8_decode_block(offset, done);
-                          else
-                            rg_etc2_rgb8_decode_block(offset, done);
-
-                          for (int k = 0; k < 16; k++)
-                            {
-                               const int r = (R_VAL(&(todo[k])) - 
R_VAL(&(done[k])));
-                               const int g = (G_VAL(&(todo[k])) - 
G_VAL(&(done[k])));
-                               const int b = (B_VAL(&(todo[k])) - 
B_VAL(&(done[k])));
-                               const int a = (A_VAL(&(todo[k])) - 
A_VAL(&(done[k])));
-                               mse += r*r + g*g + b*b;
-                               if (alpha) mse_alpha += a*a;
-                               mse_div++;
-                            }
-                       }
+                            if (plane == 0)
+                              {
+                                 // Decode to compute PSNR, this is slow.
+                                 uint32_t done[16];
+
+                                 if (alpha)
+                                   rg_etc2_rgba8_decode_block(offset, done);
+                                 else
+                                    rg_etc2_rgb8_decode_block(offset, done);
+
+                                 for (int k = 0; k < 16; k++)
+                                   {
+                                      const int r = (R_VAL(&(todo[k])) - 
R_VAL(&(done[k])));
+                                      const int g = (G_VAL(&(todo[k])) - 
G_VAL(&(done[k])));
+                                      const int b = (B_VAL(&(todo[k])) - 
B_VAL(&(done[k])));
+                                      const int a = (A_VAL(&(todo[k])) - 
A_VAL(&(done[k])));
+                                      mse += r*r + g*g + b*b;
+                                      if (alpha) mse_alpha += a*a;
+                                      mse_div++;
+                                   }
+                              }
 #endif
 
-                       offset += etc_block_size;
+                            offset += etc_block_size;
+                         }
                     }
-               }
 
-             if (compress)
-               {
-                  wlen = LZ4_compressHC((char *) buffer, (char *) comp,
-                                        block_count * etc_block_size);
-               }
-             else
-               {
-                  comp = buffer;
-                  wlen = block_count * etc_block_size;
-               }
-
-             if (wlen > 0)
-               {
-                  unsigned int blen = wlen;
+                  if (compress)
+                    {
+                       wlen = LZ4_compressHC((char *) buffer, (char *) comp,
+                                             block_count * etc_block_size);
+                    }
+                  else
+                    {
+                       comp = buffer;
+                       wlen = block_count * etc_block_size;
+                    }
 
-                  while (blen)
+                  if (wlen > 0)
                     {
-                       unsigned char plen;
+                       unsigned int blen = wlen;
 
-                       plen = blen & 0x7F;
-                       blen = blen >> 7;
+                       while (blen)
+                         {
+                            unsigned char plen;
 
-                       if (blen) plen = 0x80 | plen;
-                       if (fwrite(&plen, 1, 1, f) != 1) goto on_error;
+                            plen = blen & 0x7F;
+                            blen = blen >> 7;
+
+                            if (blen) plen = 0x80 | plen;
+                            if (fwrite(&plen, 1, 1, f) != 1) goto on_error;
+                         }
+                       if (fwrite(comp, wlen, 1, f) != 1) goto on_error;
                     }
-                  if (fwrite(comp, wlen, 1, f) != 1) goto on_error;
-               }
-          }
-     }
+               } // 4 rows
+          } // macroblocks
+     } // planes
    fclose(f);
 
 #ifdef DEBUG_STATS
@@ -452,9 +521,11 @@ evas_image_save_file_tgv(RGBA_Image *im,
      }
 #endif
 
+   if (alpha_texture) free(data);
    return 1;
 
 on_error:
+   if (alpha_texture) free(data);
    fclose(f);
    return 0;
 }

-- 


Reply via email to