EXPERIMENTAL/WIP: Bug 1324028 - Use edge colour as dominant favicon color if consistent draft
authorAndrzej Hunt <ahunt@mozilla.com>
Sat, 10 Dec 2016 20:33:34 -0800
changeset 479564 d0c4091dce4d175cd8fe78da39cee9306b0679eb
parent 479563 0fde1cea6a21d01b1651f8516ea14e1ffc59af5b
child 544722 41da0cf720568ec09d994e07efcd6aa3161fb4dd
push id44293
push userahunt@mozilla.com
push dateMon, 06 Feb 2017 22:26:28 +0000
bugs1324028
milestone54.0a1
EXPERIMENTAL/WIP: Bug 1324028 - Use edge colour as dominant favicon color if consistent If all edges of a favicon are the same colour, we should use that as the background colour too - that way the favicon no longer appears distinct from the background. We still fall back to the dominant colour in most cases, however this improves favicon appearance for some websites. MozReview-Commit-ID: 6xkgXxHHla2
mobile/android/base/java/org/mozilla/gecko/icons/processing/ColorProcessor.java
--- a/mobile/android/base/java/org/mozilla/gecko/icons/processing/ColorProcessor.java
+++ b/mobile/android/base/java/org/mozilla/gecko/icons/processing/ColorProcessor.java
@@ -1,15 +1,17 @@
 /* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*-
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 package org.mozilla.gecko.icons.processing;
 
+import android.graphics.Bitmap;
+import android.support.annotation.ColorInt;
 import android.support.v7.graphics.Palette;
 import android.util.Log;
 
 import org.mozilla.gecko.gfx.BitmapUtils;
 import org.mozilla.gecko.icons.IconRequest;
 import org.mozilla.gecko.icons.IconResponse;
 import org.mozilla.gecko.util.HardwareUtils;
 
@@ -22,16 +24,24 @@ public class ColorProcessor implements P
     private static final int DEFAULT_COLOR = 0; // 0 == No color
 
     @Override
     public void process(IconRequest request, IconResponse response) {
         if (response.hasColor()) {
             return;
         }
 
+        final Bitmap bitmap = response.getBitmap();
+
+        final @ColorInt Integer edgeColor = getEdgeColor(bitmap);
+        if (edgeColor != null) {
+            response.updateColor(edgeColor);
+            return;
+        }
+
         if (HardwareUtils.isX86System()) {
             // (Bug 1318667) We are running into crashes when using the palette library with
             // specific icons on x86 devices. They take down the whole VM and are not recoverable.
             // Unfortunately our release icon is triggering this crash. Until we can switch to a
             // newer version of the support library where this does not happen, we are using our
             // own slower implementation.
             extractColorUsingCustomImplementation(response);
         } else {
@@ -53,9 +63,71 @@ public class ColorProcessor implements P
         }
     }
 
     private void extractColorUsingCustomImplementation(final IconResponse response) {
         final int dominantColor = BitmapUtils.getDominantColor(response.getBitmap());
 
         response.updateColor(dominantColor);
     }
+
+    /**
+     * If a bitmap has a consistent edge colour (i.e. if all the border pixels have the same colour),
+     * return that colour.
+     * @param bitmap
+     * @return The edge colour. null if there is no consistent edge color.
+     */
+    private @ColorInt Integer getEdgeColor(final Bitmap bitmap) {
+        final int width = bitmap.getWidth();
+        final int height = bitmap.getHeight();
+
+        // Only allocate an array once, with the max width we need once, to minimise the number
+        // of allocations.
+        @ColorInt int[] edge = new int[Math.max(width, height)];
+
+        // Top:
+        bitmap.getPixels(edge, 0, width, 0, 0, width, 1);
+        final @ColorInt Integer edgecolor = getEdgeColor(edge, width);
+        if (edgecolor == null) {
+            return null;
+        }
+
+        // Bottom:
+        bitmap.getPixels(edge, 0, width, 0, height - 1, width, 1);
+        if (!edgecolor.equals(getEdgeColor(edge, width))) {
+            return null;
+        }
+
+        // Left:
+        bitmap.getPixels(edge, 0, 1, 0, 0, 1, height);
+        if (!edgecolor.equals(getEdgeColor(edge, height))) {
+            return null;
+        }
+
+        // Right:
+        bitmap.getPixels(edge, 0, 1, width - 1, 0, 1, height);
+        if (!edgecolor.equals(getEdgeColor(edge, height))) {
+            return null;
+        }
+
+        return edgecolor;
+    }
+
+    /**
+     * Obtain the colour for a given edge.
+     *
+     * @param edge An array containing the color values of the pixels constituting the edge of a bitmap.
+     * @param length The length of the array to be traversed. Must be smaller than, or equal to
+     * the total length of the array.
+     * @return The colour contained within the array, or -1 if colours vary.
+     */
+    private @ColorInt Integer getEdgeColor(@ColorInt int[] edge, int length) {
+        @ColorInt int color = edge[0];
+
+        for (int i = 1; i < length; i++) {
+            if (edge[i] != color) {
+                return null;
+            }
+        }
+
+        return color;
+    }
 }