Merge "change how recovery animation is implemented"
diff --git a/minui/minui.h b/minui/minui.h
index 1b8dd05..5c0defc 100644
--- a/minui/minui.h
+++ b/minui/minui.h
@@ -72,6 +72,7 @@
 
 // Returns 0 if no error, else negative.
 int res_create_surface(const char* name, gr_surface* pSurface);
+int res_create_multi_surface(const char* name, int* frames, gr_surface** pSurface);
 int res_create_localized_surface(const char* name, gr_surface* pSurface);
 void res_free_surface(gr_surface surface);
 
diff --git a/minui/resources.c b/minui/resources.c
index c0a9cca..5ad9b1d 100644
--- a/minui/resources.c
+++ b/minui/resources.c
@@ -181,6 +181,182 @@
     return result;
 }
 
+int res_create_multi_surface(const char* name, int* frames, gr_surface** pSurface) {
+    char resPath[256];
+    int result = 0;
+    unsigned char header[8];
+    png_structp png_ptr = NULL;
+    png_infop info_ptr = NULL;
+    int i;
+    GGLSurface** surface = NULL;
+
+    *pSurface = NULL;
+    *frames = -1;
+
+    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;
+    png_uint_32 width, height;
+    png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth,
+            &color_type, NULL, NULL, NULL);
+
+    int 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;
+    }
+
+    *frames = 1;
+    png_textp text;
+    int num_text;
+    if (png_get_text(png_ptr, info_ptr, &text, &num_text)) {
+        for (i = 0; i < num_text; ++i) {
+            if (text[i].key && strcmp(text[i].key, "Frames") == 0 && text[i].text) {
+                *frames = atoi(text[i].text);
+                break;
+            }
+        }
+        printf("  found frames = %d\n", *frames);
+    }
+
+    if (height % *frames != 0) {
+        printf("bad height (%d) for frame count (%d)\n", height, *frames);
+        result = -9;
+        goto exit;
+    }
+
+    size_t stride = (color_type == PNG_COLOR_TYPE_GRAY ? 1 : 4) * width;
+    size_t pixelSize = stride * height / *frames;
+
+    surface = malloc(*frames * sizeof(GGLSurface*));
+    if (surface == NULL) {
+        result = -8;
+        goto exit;
+    }
+    for (i = 0; i < *frames; ++i) {
+        surface[i] = malloc(sizeof(GGLSurface) + pixelSize);
+        surface[i]->version = sizeof(GGLSurface);
+        surface[i]->width = width;
+        surface[i]->height = height / *frames;
+        surface[i]->stride = width; /* Yes, pixels, not bytes */
+        surface[i]->data = (unsigned char*) (surface[i] + 1);
+
+        if (channels == 3) {
+            surface[i]->format = GGL_PIXEL_FORMAT_RGBX_8888;
+        } else if (color_type == PNG_COLOR_TYPE_PALETTE) {
+            surface[i]->format = GGL_PIXEL_FORMAT_RGBA_8888;
+        } else if (channels == 1) {
+            surface[i]->format = GGL_PIXEL_FORMAT_L_8;
+        } else {
+            surface[i]->format = GGL_PIXEL_FORMAT_RGBA_8888;
+        }
+    }
+
+    int alpha = (channels == 4);
+    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;
+    }
+
+    png_uint_32 y;
+    if (channels == 3 || (channels == 1 && !alpha)) {
+        for (y = 0; y < height; ++y) {
+            int fy = y / *frames;
+            int fr = y % *frames;
+            unsigned char* pRow = surface[fr]->data + fy * 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) {
+            int fy = y / *frames;
+            int fr = y % *frames;
+            unsigned char* pRow = surface[fr]->data + fy * 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) {
+            for (i = 0; i < *frames; ++i) {
+                if (surface[i]) free(surface[i]);
+            }
+            free(surface);
+        }
+    }
+    return result;
+}
+
 static int matches_locale(const char* loc) {
     if (locale == NULL) return 0;
 
@@ -249,7 +425,7 @@
     png_read_info(png_ptr, info_ptr);
 
     int color_type, bit_depth;
-    size_t width, height;
+    png_uint_32 width, height;
     png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth,
             &color_type, NULL, NULL, NULL);
     int channels = png_get_channels(png_ptr, info_ptr);
@@ -261,13 +437,13 @@
     }
 
     unsigned char* row = malloc(width);
-    int y;
+    png_uint_32 y;
     for (y = 0; y < height; ++y) {
         png_read_row(png_ptr, row, NULL);
         int w = (row[1] << 8) | row[0];
         int h = (row[3] << 8) | row[2];
         int len = row[4];
-        char* loc = row+5;
+        char* loc = (char*)row+5;
 
         if (y+1+h >= height || matches_locale(loc)) {
             printf("  %20s: %s (%d x %d @ %d)\n", name, loc, w, h, y);
diff --git a/res/images/icon_installing.png b/res/images/icon_installing.png
index 571eb8b..c2c0201 100644
--- a/res/images/icon_installing.png
+++ b/res/images/icon_installing.png
Binary files differ
diff --git a/res/images/icon_installing_overlay01.png b/res/images/icon_installing_overlay01.png
deleted file mode 100644
index e762d6c..0000000
--- a/res/images/icon_installing_overlay01.png
+++ /dev/null
Binary files differ
diff --git a/res/images/icon_installing_overlay02.png b/res/images/icon_installing_overlay02.png
deleted file mode 100644
index f7a8530..0000000
--- a/res/images/icon_installing_overlay02.png
+++ /dev/null
Binary files differ
diff --git a/res/images/icon_installing_overlay03.png b/res/images/icon_installing_overlay03.png
deleted file mode 100644
index 1a1d738..0000000
--- a/res/images/icon_installing_overlay03.png
+++ /dev/null
Binary files differ
diff --git a/res/images/icon_installing_overlay04.png b/res/images/icon_installing_overlay04.png
deleted file mode 100644
index a74903d..0000000
--- a/res/images/icon_installing_overlay04.png
+++ /dev/null
Binary files differ
diff --git a/res/images/icon_installing_overlay05.png b/res/images/icon_installing_overlay05.png
deleted file mode 100644
index d17bdc0..0000000
--- a/res/images/icon_installing_overlay05.png
+++ /dev/null
Binary files differ
diff --git a/res/images/icon_installing_overlay06.png b/res/images/icon_installing_overlay06.png
deleted file mode 100644
index 1200b75..0000000
--- a/res/images/icon_installing_overlay06.png
+++ /dev/null
Binary files differ
diff --git a/res/images/icon_installing_overlay07.png b/res/images/icon_installing_overlay07.png
deleted file mode 100644
index 3838a85..0000000
--- a/res/images/icon_installing_overlay07.png
+++ /dev/null
Binary files differ
diff --git a/res/images/indeterminate01.png b/res/images/indeterminate01.png
deleted file mode 100644
index 933528d..0000000
--- a/res/images/indeterminate01.png
+++ /dev/null
Binary files differ
diff --git a/res/images/indeterminate02.png b/res/images/indeterminate02.png
deleted file mode 100644
index d760e2b..0000000
--- a/res/images/indeterminate02.png
+++ /dev/null
Binary files differ
diff --git a/res/images/indeterminate03.png b/res/images/indeterminate03.png
deleted file mode 100644
index 0e97399..0000000
--- a/res/images/indeterminate03.png
+++ /dev/null
Binary files differ
diff --git a/res/images/indeterminate04.png b/res/images/indeterminate04.png
deleted file mode 100644
index c7d5b4e..0000000
--- a/res/images/indeterminate04.png
+++ /dev/null
Binary files differ
diff --git a/res/images/indeterminate05.png b/res/images/indeterminate05.png
deleted file mode 100644
index d6fb2a0..0000000
--- a/res/images/indeterminate05.png
+++ /dev/null
Binary files differ
diff --git a/res/images/indeterminate06.png b/res/images/indeterminate06.png
deleted file mode 100644
index 4486761..0000000
--- a/res/images/indeterminate06.png
+++ /dev/null
Binary files differ
diff --git a/screen_ui.cpp b/screen_ui.cpp
index 8376341..38b21f8 100644
--- a/screen_ui.cpp
+++ b/screen_ui.cpp
@@ -69,20 +69,8 @@
     menu_top(0),
     menu_items(0),
     menu_sel(0),
-
-    // These values are correct for the default image resources
-    // provided with the android platform.  Devices which use
-    // different resources should have a subclass of ScreenRecoveryUI
-    // that overrides Init() to set these values appropriately and
-    // then call the superclass Init().
     animation_fps(20),
-    indeterminate_frames(6),
-    installing_frames(7),
-    install_overlay_offset_x(13),
-    install_overlay_offset_y(190),
-    overlay_offset_x(-1),
-    overlay_offset_y(-1) {
-
+    installing_frames(-1) {
     for (int i = 0; i < 5; i++)
         backgroundIcon[i] = NULL;
 
@@ -90,20 +78,6 @@
     self = this;
 }
 
-// Draw the given frame over the installation overlay animation.  The
-// background is not cleared or draw with the base icon first; we
-// assume that the frame already contains some other frame of the
-// animation.  Does nothing if no overlay animation is defined.
-// Should only be called with updateMutex locked.
-void ScreenRecoveryUI::draw_install_overlay_locked(int frame) {
-    if (installationOverlay == NULL || overlay_offset_x < 0) return;
-    gr_surface surface = installationOverlay[frame];
-    int iconWidth = gr_get_width(surface);
-    int iconHeight = gr_get_height(surface);
-    gr_blit(surface, 0, 0, iconWidth, iconHeight,
-            overlay_offset_x, overlay_offset_y);
-}
-
 // Clear the screen and draw the currently selected background icon (if any).
 // Should only be called with updateMutex locked.
 void ScreenRecoveryUI::draw_background_locked(Icon icon)
@@ -114,6 +88,9 @@
 
     if (icon) {
         gr_surface surface = backgroundIcon[icon];
+        if (icon == INSTALLING_UPDATE || icon == ERASING) {
+            surface = installation[installingFrame];
+        }
         gr_surface text_surface = backgroundText[icon];
 
         int iconWidth = gr_get_width(surface);
@@ -121,16 +98,13 @@
         int textWidth = gr_get_width(text_surface);
         int textHeight = gr_get_height(text_surface);
 
-        int iconX = (gr_fb_width() - iconWidth) / 2;
-        int iconY = (gr_fb_height() - (iconHeight+textHeight+40)) / 2;
+        iconX = (gr_fb_width() - iconWidth) / 2;
+        iconY = (gr_fb_height() - (iconHeight+textHeight+40)) / 2;
 
         int textX = (gr_fb_width() - textWidth) / 2;
         int textY = ((gr_fb_height() - (iconHeight+textHeight+40)) / 2) + iconHeight + 40;
 
         gr_blit(surface, 0, 0, iconWidth, iconHeight, iconX, iconY);
-        if (icon == INSTALLING_UPDATE || icon == ERASING) {
-            draw_install_overlay_locked(installingFrame);
-        }
 
         gr_color(255, 255, 255, 255);
         gr_texticon(textX, textY, text_surface);
@@ -144,7 +118,8 @@
     if (currentIcon == ERROR) return;
 
     if (currentIcon == INSTALLING_UPDATE || currentIcon == ERASING) {
-        draw_install_overlay_locked(installingFrame);
+        gr_surface icon = installation[installingFrame];
+        gr_blit(icon, 0, 0, gr_get_width(icon), gr_get_height(icon), iconX, iconY);
     }
 
     if (progressBarType != EMPTY) {
@@ -181,18 +156,6 @@
                 }
             }
         }
-
-        if (progressBarType == INDETERMINATE) {
-            static int frame = 0;
-            gr_blit(progressBarIndeterminate[frame], 0, 0, width, height, dx, dy);
-            // in RTL locales, we run the animation backwards, which
-            // makes the spinner spin the other way.
-            if (rtl_locale) {
-                frame = (frame + indeterminate_frames - 1) % indeterminate_frames;
-            } else {
-                frame = (frame + 1) % indeterminate_frames;
-            }
-        }
     }
 }
 
@@ -319,12 +282,6 @@
             redraw = 1;
         }
 
-        // update the progress bar animation, if active
-        // skip this if we have a text overlay (too expensive to update)
-        if (progressBarType == INDETERMINATE && !show_text) {
-            redraw = 1;
-        }
-
         // move the progress bar forward on timed intervals, if configured
         int duration = progressScopeDuration;
         if (progressBarType == DETERMINATE && duration > 0) {
@@ -355,6 +312,13 @@
     }
 }
 
+void ScreenRecoveryUI::LoadBitmapArray(const char* filename, int* frames, gr_surface** surface) {
+    int result = res_create_multi_surface(filename, frames, surface);
+    if (result < 0) {
+        LOGE("missing bitmap %s\n(Code %d)\n", filename, result);
+    }
+}
+
 void ScreenRecoveryUI::LoadLocalizedBitmap(const char* filename, gr_surface* surface) {
     int result = res_create_localized_surface(filename, surface);
     if (result < 0) {
@@ -376,7 +340,9 @@
     text_cols = gr_fb_width() / char_width;
     if (text_cols > kMaxCols - 1) text_cols = kMaxCols - 1;
 
-    LoadBitmap("icon_installing", &backgroundIcon[INSTALLING_UPDATE]);
+    backgroundIcon[NONE] = NULL;
+    LoadBitmapArray("icon_installing", &installing_frames, &installation);
+    backgroundIcon[INSTALLING_UPDATE] = installing_frames ? installation[0] : NULL;
     backgroundIcon[ERASING] = backgroundIcon[INSTALLING_UPDATE];
     LoadBitmap("icon_error", &backgroundIcon[ERROR]);
     backgroundIcon[NO_COMMAND] = backgroundIcon[ERROR];
@@ -389,31 +355,6 @@
     LoadLocalizedBitmap("no_command_text", &backgroundText[NO_COMMAND]);
     LoadLocalizedBitmap("error_text", &backgroundText[ERROR]);
 
-    int i;
-
-    progressBarIndeterminate = (gr_surface*)malloc(indeterminate_frames *
-                                                    sizeof(gr_surface));
-    for (i = 0; i < indeterminate_frames; ++i) {
-        char filename[40];
-        // "indeterminate01.png", "indeterminate02.png", ...
-        sprintf(filename, "indeterminate%02d", i+1);
-        LoadBitmap(filename, progressBarIndeterminate+i);
-    }
-
-    if (installing_frames > 0) {
-        installationOverlay = (gr_surface*)malloc(installing_frames *
-                                                   sizeof(gr_surface));
-        for (i = 0; i < installing_frames; ++i) {
-            char filename[40];
-            // "icon_installing_overlay01.png",
-            // "icon_installing_overlay02.png", ...
-            sprintf(filename, "icon_installing_overlay%02d", i+1);
-            LoadBitmap(filename, installationOverlay+i);
-        }
-    } else {
-        installationOverlay = NULL;
-    }
-
     pthread_create(&progress_t, NULL, progress_thread, NULL);
 
     RecoveryUI::Init();
@@ -446,16 +387,6 @@
 {
     pthread_mutex_lock(&updateMutex);
 
-    // Adjust the offset to account for the positioning of the
-    // base image on the screen.
-    if (backgroundIcon[icon] != NULL) {
-        gr_surface bg = backgroundIcon[icon];
-        gr_surface text = backgroundText[icon];
-        overlay_offset_x = install_overlay_offset_x + (gr_fb_width() - gr_get_width(bg)) / 2;
-        overlay_offset_y = install_overlay_offset_y +
-            (gr_fb_height() - (gr_get_height(bg) + gr_get_height(text) + 40)) / 2;
-    }
-
     currentIcon = icon;
     update_screen_locked();
 
@@ -495,7 +426,7 @@
     if (fraction > 1.0) fraction = 1.0;
     if (progressBarType == DETERMINATE && fraction > progress) {
         // Skip updates that aren't visibly different.
-        int width = gr_get_width(progressBarIndeterminate[0]);
+        int width = gr_get_width(progressBarEmpty);
         float scale = width * progressScopeSize;
         if ((int) (progress * scale) != (int) (fraction * scale)) {
             progress = fraction;
diff --git a/screen_ui.h b/screen_ui.h
index fc35d95..ada006d 100644
--- a/screen_ui.h
+++ b/screen_ui.h
@@ -69,8 +69,7 @@
     pthread_mutex_t updateMutex;
     gr_surface backgroundIcon[5];
     gr_surface backgroundText[5];
-    gr_surface *installationOverlay;
-    gr_surface *progressBarIndeterminate;
+    gr_surface *installation;
     gr_surface progressBarEmpty;
     gr_surface progressBarFill;
 
@@ -100,9 +99,9 @@
     pthread_t progress_t;
 
     int animation_fps;
-    int indeterminate_frames;
     int installing_frames;
-    int overlay_offset_x, overlay_offset_y;
+
+    int iconX, iconY;
 
     void draw_install_overlay_locked(int frame);
     void draw_background_locked(Icon icon);
@@ -114,6 +113,7 @@
     void progress_loop();
 
     void LoadBitmap(const char* filename, gr_surface* surface);
+    void LoadBitmapArray(const char* filename, int* frames, gr_surface** surface);
     void LoadLocalizedBitmap(const char* filename, gr_surface* surface);
 };