Merge "Encode paletted PNGs more efficiently"
diff --git a/tools/aapt/Images.cpp b/tools/aapt/Images.cpp
index 5ad3379..867a6ff 100644
--- a/tools/aapt/Images.cpp
+++ b/tools/aapt/Images.cpp
@@ -873,14 +873,15 @@
 
 static void analyze_image(const char *imageName, image_info &imageInfo, int grayscaleTolerance,
                           png_colorp rgbPalette, png_bytep alphaPalette,
-                          int *paletteEntries, bool *hasTransparency, int *colorType,
-                          png_bytepp outRows)
+                          int *paletteEntries, int *alphaPaletteEntries, bool *hasTransparency,
+                          int *colorType, png_bytepp outRows)
 {
     int w = imageInfo.width;
     int h = imageInfo.height;
-    int i, j, rr, gg, bb, aa, idx;
-    uint32_t colors[256], col;
-    int num_colors = 0;
+    int i, j, rr, gg, bb, aa, idx;;
+    uint32_t opaqueColors[256], alphaColors[256];
+    uint32_t col;
+    int numOpaqueColors = 0, numAlphaColors = 0;
     int maxGrayDeviation = 0;
 
     bool isOpaque = true;
@@ -891,6 +892,10 @@
     // 1. Every pixel has R == G == B (grayscale)
     // 2. Every pixel has A == 255 (opaque)
     // 3. There are no more than 256 distinct RGBA colors
+    //        We will track opaque colors separately from colors with
+    //        alpha.  This allows us to reencode the color table more
+    //        efficiently (color tables entries without a corresponding
+    //        alpha value are assumed to be opaque).
 
     if (kIsDebug) {
         printf("Initial image data:\n");
@@ -943,26 +948,53 @@
             if (isPalette) {
                 col = (uint32_t) ((rr << 24) | (gg << 16) | (bb << 8) | aa);
                 bool match = false;
-                for (idx = 0; idx < num_colors; idx++) {
-                    if (colors[idx] == col) {
-                        match = true;
-                        break;
+
+                if (aa == 0xff) {
+                    for (idx = 0; idx < numOpaqueColors; idx++) {
+                        if (opaqueColors[idx] == col) {
+                            match = true;
+                            break;
+                        }
                     }
+
+                    if (!match) {
+                        if (numOpaqueColors < 256) {
+                            opaqueColors[numOpaqueColors] = col;
+                        }
+                        numOpaqueColors++;
+                    }
+
+                    // Write the palette index for the pixel to outRows optimistically.
+                    // We might overwrite it later if we decide to encode as gray or
+                    // gray + alpha.  We may also need to overwrite it when we combine
+                    // into a single palette.
+                    *out++ = idx;
+                } else {
+                    for (idx = 0; idx < numAlphaColors; idx++) {
+                        if (alphaColors[idx] == col) {
+                            match = true;
+                            break;
+                        }
+                    }
+
+                    if (!match) {
+                        if (numAlphaColors < 256) {
+                            alphaColors[numAlphaColors] = col;
+                        }
+                        numAlphaColors++;
+                    }
+
+                    // Write the palette index for the pixel to outRows optimistically.
+                    // We might overwrite it later if we decide to encode as gray or
+                    // gray + alpha.
+                    *out++ = idx;
                 }
 
-                // Write the palette index for the pixel to outRows optimistically
-                // We might overwrite it later if we decide to encode as gray or
-                // gray + alpha
-                *out++ = idx;
-                if (!match) {
-                    if (num_colors == 256) {
-                        if (kIsDebug) {
-                            printf("Found 257th color at %d, %d\n", i, j);
-                        }
-                        isPalette = false;
-                    } else {
-                        colors[num_colors++] = col;
+                if (numOpaqueColors + numAlphaColors > 256) {
+                    if (kIsDebug) {
+                        printf("Found 257th color at %d, %d\n", i, j);
                     }
+                    isPalette = false;
                 }
             }
         }
@@ -970,9 +1002,9 @@
 
     *paletteEntries = 0;
     *hasTransparency = !isOpaque;
-    int bpp = isOpaque ? 3 : 4;
-    int paletteSize = w * h + bpp * num_colors;
+    int paletteSize = w * h + 3 * numOpaqueColors + 4 * numAlphaColors;
 
+    int bpp = isOpaque ? 3 : 4;
     if (kIsDebug) {
         printf("isGrayscale = %s\n", isGrayscale ? "true" : "false");
         printf("isOpaque = %s\n", isOpaque ? "true" : "false");
@@ -1017,16 +1049,37 @@
     // color type chosen
 
     if (*colorType == PNG_COLOR_TYPE_PALETTE) {
+        // Combine the alphaColors and the opaqueColors into a single palette.
+        // The alphaColors must be at the start of the palette.
+        uint32_t* colors = alphaColors;
+        memcpy(colors + numAlphaColors, opaqueColors, 4 * numOpaqueColors);
+
+        // Fix the indices of the opaque colors in the image.
+        for (j = 0; j < h; j++) {
+            png_bytep row = imageInfo.rows[j];
+            png_bytep out = outRows[j];
+            for (i = 0; i < w; i++) {
+                uint32_t pixel = ((uint32_t*) row)[i];
+                if (pixel >> 24 == 0xFF) {
+                    out[i] += numAlphaColors;
+                }
+            }
+        }
+
         // Create separate RGB and Alpha palettes and set the number of colors
-        *paletteEntries = num_colors;
+        int numColors = numOpaqueColors + numAlphaColors;
+        *paletteEntries = numColors;
+        *alphaPaletteEntries = numAlphaColors;
 
         // Create the RGB and alpha palettes
-        for (int idx = 0; idx < num_colors; idx++) {
+        for (int idx = 0; idx < numColors; idx++) {
             col = colors[idx];
             rgbPalette[idx].red   = (png_byte) ((col >> 24) & 0xff);
             rgbPalette[idx].green = (png_byte) ((col >> 16) & 0xff);
             rgbPalette[idx].blue  = (png_byte) ((col >>  8) & 0xff);
-            alphaPalette[idx]     = (png_byte)  (col        & 0xff);
+            if (idx < numAlphaColors) {
+                alphaPalette[idx] = (png_byte)  (col        & 0xff);
+            }
         }
     } else if (*colorType == PNG_COLOR_TYPE_GRAY || *colorType == PNG_COLOR_TYPE_GRAY_ALPHA) {
         // If the image is gray or gray + alpha, compact the pixels into outRows
@@ -1090,10 +1143,10 @@
     png_color rgbPalette[256];
     png_byte alphaPalette[256];
     bool hasTransparency;
-    int paletteEntries;
+    int paletteEntries, alphaPaletteEntries;
 
     analyze_image(imageName, imageInfo, grayscaleTolerance, rgbPalette, alphaPalette,
-                  &paletteEntries, &hasTransparency, &color_type, outRows);
+                  &paletteEntries, &alphaPaletteEntries, &hasTransparency, &color_type, outRows);
 
     if (kIsDebug) {
         switch (color_type) {
@@ -1124,7 +1177,8 @@
     if (color_type == PNG_COLOR_TYPE_PALETTE) {
         png_set_PLTE(write_ptr, write_info, rgbPalette, paletteEntries);
         if (hasTransparency) {
-            png_set_tRNS(write_ptr, write_info, alphaPalette, paletteEntries, (png_color_16p) 0);
+            png_set_tRNS(write_ptr, write_info, alphaPalette, alphaPaletteEntries,
+                    (png_color_16p) 0);
         }
        png_set_filter(write_ptr, 0, PNG_NO_FILTERS);
     } else {