Update minui to work properly

minui is not used by TWRP but it is included in AOSP recovery
source and healthd (formerly charger) uses minui for displaying
the battery charging animation during off-mode charging.

This patch fixes / updates minui to use updated code to load png
files into memory.

Change-Id: I706d10d66de95886396d866e80615b1fb905d201
diff --git a/minui/resources.c b/minui/resources.c
index fe1e999..ed25e45 100644
--- a/minui/resources.c
+++ b/minui/resources.c
@@ -47,143 +47,15 @@
     return x * y;
 }
 
-int res_create_surface(const char* name, gr_surface* pSurface) {
-    char resPath[256];
-    GGLSurface* surface = NULL;
-    int result = 0;
-    unsigned char header[8];
-    png_structp png_ptr = NULL;
-    png_infop info_ptr = NULL;
+#define SURFACE_DATA_ALIGNMENT 8
 
-    *pSurface = NULL;
-
-    snprintf(resPath, sizeof(resPath)-1, "/res/images/%s.png", name);
-    resPath[sizeof(resPath)-1] = '\0';
-    FILE* fp = fopen(resPath, "rb");
-    if (fp == NULL) {
-        result = -1;
-        goto exit;
-    }
-
-    size_t bytesRead = fread(header, 1, sizeof(header), fp);
-    if (bytesRead != sizeof(header)) {
-        result = -2;
-        goto exit;
-    }
-
-    if (png_sig_cmp(header, 0, sizeof(header))) {
-        result = -3;
-        goto exit;
-    }
-
-    png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
-    if (!png_ptr) {
-        result = -4;
-        goto exit;
-    }
-
-    info_ptr = png_create_info_struct(png_ptr);
-    if (!info_ptr) {
-        result = -5;
-        goto exit;
-    }
-
-    if (setjmp(png_jmpbuf(png_ptr))) {
-        result = -6;
-        goto exit;
-    }
-
-    png_init_io(png_ptr, fp);
-    png_set_sig_bytes(png_ptr, sizeof(header));
-    png_read_info(png_ptr, info_ptr);
-
-    int color_type, bit_depth;
-    size_t width, height;
-
-    png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth,
-            &color_type, NULL, NULL, NULL);
-
-    png_byte channels = png_get_channels(png_ptr, info_ptr);
-
-    if (!(bit_depth == 8 &&
-          ((channels == 3 && color_type == PNG_COLOR_TYPE_RGB) ||
-           (channels == 4 && color_type == PNG_COLOR_TYPE_RGBA) ||
-           (channels == 1 && (color_type == PNG_COLOR_TYPE_PALETTE ||
-                              color_type == PNG_COLOR_TYPE_GRAY))))) {
-        return -7;
-        goto exit;
-    }
-
-    size_t stride = (color_type == PNG_COLOR_TYPE_GRAY ? 1 : 4) * width;
-    size_t pixelSize = stride * height;
-
-    surface = malloc(sizeof(GGLSurface) + pixelSize);
-    if (surface == NULL) {
-        result = -8;
-        goto exit;
-    }
-    unsigned char* pData = (unsigned char*) (surface + 1);
-    surface->version = sizeof(GGLSurface);
-    surface->width = width;
-    surface->height = height;
-    surface->stride = width; /* Yes, pixels, not bytes */
-    surface->data = pData;
-    surface->format = (channels == 3) ? GGL_PIXEL_FORMAT_RGBX_8888 :
-        ((color_type == PNG_COLOR_TYPE_PALETTE ? GGL_PIXEL_FORMAT_RGBA_8888 : GGL_PIXEL_FORMAT_L_8));
-
-    int alpha = 0;
-    if (color_type == PNG_COLOR_TYPE_PALETTE) {
-        png_set_palette_to_rgb(png_ptr);
-    }
-    if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
-        png_set_tRNS_to_alpha(png_ptr);
-        alpha = 1;
-    }
-    if (color_type == PNG_COLOR_TYPE_GRAY) {
-        alpha = 1;
-    }
-
-    unsigned int y;
-    if (channels == 3 || (channels == 1 && !alpha)) {
-        for (y = 0; y < height; ++y) {
-            unsigned char* pRow = pData + y * stride;
-            png_read_row(png_ptr, pRow, NULL);
-
-            int x;
-            for(x = width - 1; x >= 0; x--) {
-                int sx = x * 3;
-                int dx = x * 4;
-                unsigned char r = pRow[sx];
-                unsigned char g = pRow[sx + 1];
-                unsigned char b = pRow[sx + 2];
-                unsigned char a = 0xff;
-                pRow[dx    ] = r; // r
-                pRow[dx + 1] = g; // g
-                pRow[dx + 2] = b; // b
-                pRow[dx + 3] = a;
-            }
-        }
-    } else {
-        for (y = 0; y < height; ++y) {
-            unsigned char* pRow = pData + y * stride;
-            png_read_row(png_ptr, pRow, NULL);
-        }
-    }
-
-    *pSurface = (gr_surface) surface;
-
-exit:
-    png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
-
-    if (fp != NULL) {
-        fclose(fp);
-    }
-    if (result < 0) {
-        if (surface) {
-            free(surface);
-        }
-    }
-    return result;
+static GGLSurface* malloc_surface(size_t data_size) {
+    unsigned char* temp = malloc(sizeof(GGLSurface) + data_size + SURFACE_DATA_ALIGNMENT);
+    if (temp == NULL) return NULL;
+    GGLSurface* surface = (GGLSurface*) temp;
+    surface->data = temp + sizeof(GGLSurface) +
+        (SURFACE_DATA_ALIGNMENT - (sizeof(GGLSurface) % SURFACE_DATA_ALIGNMENT));
+    return surface;
 }
 
 static int open_png(const char* name, png_structp* png_ptr, png_infop* info_ptr,
@@ -270,6 +142,114 @@
     return result;
 }
 
+// "display" surfaces are transformed into the framebuffer's required
+// pixel format (currently only RGBX is supported) at load time, so
+// gr_blit() can be nothing more than a memcpy() for each row.  The
+// next two functions are the only ones that know anything about the
+// framebuffer pixel format; they need to be modified if the
+// framebuffer format changes (but nothing else should).
+
+// Allocate and return a gr_surface sufficient for storing an image of
+// the indicated size in the framebuffer pixel format.
+static GGLSurface* init_display_surface(png_uint_32 width, png_uint_32 height) {
+    GGLSurface* surface;
+
+    surface = (GGLSurface*) malloc_surface(width * height * 4);
+    if (surface == NULL) return NULL;
+
+    surface->version = sizeof(GGLSurface);
+    surface->width = width;
+    surface->height = height;
+    surface->stride = width;
+
+    return surface;
+}
+
+// Copy 'input_row' to 'output_row', transforming it to the
+// framebuffer pixel format.  The input format depends on the value of
+// 'channels':
+//
+//   1 - input is 8-bit grayscale
+//   3 - input is 24-bit RGB
+//   4 - input is 32-bit RGBA/RGBX
+//
+// 'width' is the number of pixels in the row.
+static void transform_rgb_to_draw(unsigned char* input_row,
+                                  unsigned char* output_row,
+                                  int channels, int width) {
+    int x;
+    unsigned char* ip = input_row;
+    unsigned char* op = output_row;
+
+    switch (channels) {
+        case 1:
+            // expand gray level to RGBX
+            for (x = 0; x < width; ++x) {
+                *op++ = *ip;
+                *op++ = *ip;
+                *op++ = *ip;
+                *op++ = 0xff;
+                ip++;
+            }
+            break;
+
+        case 3:
+            // expand RGBA to RGBX
+            for (x = 0; x < width; ++x) {
+                *op++ = *ip++;
+                *op++ = *ip++;
+                *op++ = *ip++;
+                *op++ = 0xff;
+            }
+            break;
+
+        case 4:
+            // copy RGBA to RGBX
+            memcpy(output_row, input_row, width*4);
+            break;
+    }
+}
+
+int res_create_surface(const char* name, gr_surface* pSurface) {
+    GGLSurface* surface = NULL;
+    int result = 0;
+    png_structp png_ptr = NULL;
+    png_infop info_ptr = NULL;
+    png_uint_32 width, height;
+    png_byte channels;
+
+    *pSurface = NULL;
+
+    result = open_png(name, &png_ptr, &info_ptr, &width, &height, &channels);
+    if (result < 0) return result;
+
+    surface = init_display_surface(width, height);
+    if (surface == NULL) {
+        result = -8;
+        goto exit;
+    }
+
+    unsigned char* p_row = malloc(width * 4);
+    unsigned int y;
+    for (y = 0; y < height; ++y) {
+        png_read_row(png_ptr, p_row, NULL);
+        transform_rgb_to_draw(p_row, surface->data + y * width * 4, channels, width);
+    }
+    free(p_row);
+
+    if (channels == 3)
+        surface->format = GGL_PIXEL_FORMAT_RGBX_8888;
+    else
+        surface->format = GGL_PIXEL_FORMAT_RGBA_8888;
+
+    *pSurface = (gr_surface) surface;
+
+  exit:
+    png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
+    if (result < 0 && surface != NULL) free(surface);
+    return result;
+}
+
 int res_create_multi_display_surface(const char* name, int* frames, gr_surface** pSurface) {
     gr_surface* surface = NULL;
     int result = 0;
@@ -304,29 +284,38 @@
         goto exit;
     }
 
-    surface = malloc(*frames * sizeof(gr_surface));
+    surface = malloc(*frames * sizeof(GGLSurface));
     if (surface == NULL) {
         result = -8;
         goto exit;
     }
     for (i = 0; i < *frames; ++i) {
-        surface[i] = NULL;//init_display_surface(width, height / *frames);
+        surface[i] = init_display_surface(width, height / *frames);
         if (surface[i] == NULL) {
             result = -8;
             goto exit;
         }
     }
 
-    /*unsigned char* p_row = malloc(width * 4);
+    unsigned char* p_row = malloc(width * 4);
     unsigned int y;
     for (y = 0; y < height; ++y) {
         png_read_row(png_ptr, p_row, NULL);
         int frame = y % *frames;
-        unsigned char* out_row = surface[frame]->data +
-            (y / *frames) * surface[frame]->row_bytes;
+        GGLSurface* p = (GGLSurface*) surface[frame];
+        unsigned char* out_row = p->data +
+            (y / *frames) * width * 4;
         transform_rgb_to_draw(p_row, out_row, channels, width);
     }
-    free(p_row); this will need to be brought in line with the older resources and graphics code */
+    free(p_row);
+
+    for (i = 0; i < *frames; ++i) {
+        GGLSurface* p = (GGLSurface*) surface[i];
+        if (channels == 3)
+            p->format = GGL_PIXEL_FORMAT_RGBX_8888;
+        else
+            p->format = GGL_PIXEL_FORMAT_RGBA_8888;
+    }
 
     *pSurface = (gr_surface*) surface;