mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-25 13:51:41 +00:00
Bug 1533194 - Remove palette dependency and unused BitmapUtils. r=esawin
Differential Revision: https://phabricator.services.mozilla.com/D104576
This commit is contained in:
parent
a7b2fb4104
commit
891c91612b
@ -231,7 +231,6 @@ dependencies {
|
||||
|
||||
implementation "androidx.annotation:annotation:1.1.0"
|
||||
implementation "androidx.legacy:legacy-support-v4:1.0.0"
|
||||
implementation "androidx.palette:palette:1.0.0"
|
||||
|
||||
implementation "com.google.android.gms:play-services-fido:18.1.0"
|
||||
implementation "org.yaml:snakeyaml:1.24:android"
|
||||
|
@ -26,7 +26,6 @@ import java.util.StringTokenizer;
|
||||
import org.mozilla.gecko.annotation.JNITarget;
|
||||
import org.mozilla.gecko.annotation.RobocopTarget;
|
||||
import org.mozilla.gecko.annotation.WrapForJNI;
|
||||
import org.mozilla.gecko.util.BitmapUtils;
|
||||
import org.mozilla.gecko.util.HardwareCodecCapabilityUtils;
|
||||
import org.mozilla.gecko.util.HardwareUtils;
|
||||
import org.mozilla.gecko.util.InputDeviceUtils;
|
||||
@ -49,10 +48,12 @@ import android.content.pm.PackageManager;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import android.content.res.TypedArray;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.ImageFormat;
|
||||
import android.graphics.PixelFormat;
|
||||
import android.graphics.Point;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.drawable.BitmapDrawable;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.hardware.Camera;
|
||||
import android.hardware.Sensor;
|
||||
@ -1391,7 +1392,7 @@ public class GeckoAppShell {
|
||||
icon = ResourcesCompat.getDrawable(getApplicationContext().getResources(), R.drawable.ic_generic_file, getApplicationContext().getTheme());
|
||||
}
|
||||
|
||||
Bitmap bitmap = BitmapUtils.getBitmapFromDrawable(icon);
|
||||
Bitmap bitmap = getBitmapFromDrawable(icon);
|
||||
if (bitmap.getWidth() != resolvedIconSize || bitmap.getHeight() != resolvedIconSize) {
|
||||
bitmap = Bitmap.createScaledBitmap(bitmap, resolvedIconSize, resolvedIconSize, true);
|
||||
}
|
||||
@ -1406,6 +1407,24 @@ public class GeckoAppShell {
|
||||
}
|
||||
}
|
||||
|
||||
private static Bitmap getBitmapFromDrawable(final Drawable drawable) {
|
||||
if (drawable instanceof BitmapDrawable) {
|
||||
return ((BitmapDrawable) drawable).getBitmap();
|
||||
}
|
||||
|
||||
int width = drawable.getIntrinsicWidth();
|
||||
width = width > 0 ? width : 1;
|
||||
int height = drawable.getIntrinsicHeight();
|
||||
height = height > 0 ? height : 1;
|
||||
|
||||
Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
|
||||
Canvas canvas = new Canvas(bitmap);
|
||||
drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
|
||||
drawable.draw(canvas);
|
||||
|
||||
return bitmap;
|
||||
}
|
||||
|
||||
public static String getMimeTypeFromExtension(final String ext) {
|
||||
final MimeTypeMap mtm = MimeTypeMap.getSingleton();
|
||||
return mtm.getMimeTypeFromExtension(ext);
|
||||
@ -1786,7 +1805,7 @@ public class GeckoAppShell {
|
||||
final String pkg = splits[1];
|
||||
final PackageManager pm = getApplicationContext().getPackageManager();
|
||||
final Drawable d = pm.getApplicationIcon(pkg);
|
||||
final Bitmap bitmap = BitmapUtils.getBitmapFromDrawable(d);
|
||||
final Bitmap bitmap = getBitmapFromDrawable(d);
|
||||
return new BitmapConnection(bitmap);
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
|
@ -1,321 +0,0 @@
|
||||
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; 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.util;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.drawable.BitmapDrawable;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.net.Uri;
|
||||
import androidx.annotation.ColorInt;
|
||||
import androidx.palette.graphics.Palette;
|
||||
import android.util.Base64;
|
||||
import android.util.Log;
|
||||
|
||||
public final class BitmapUtils {
|
||||
private static final String LOGTAG = "GeckoBitmapUtils";
|
||||
|
||||
private BitmapUtils() {}
|
||||
|
||||
public static Bitmap decodeByteArray(final byte[] bytes) {
|
||||
return decodeByteArray(bytes, null);
|
||||
}
|
||||
|
||||
public static Bitmap decodeByteArray(final byte[] bytes, final BitmapFactory.Options options) {
|
||||
return decodeByteArray(bytes, 0, bytes.length, options);
|
||||
}
|
||||
|
||||
public static Bitmap decodeByteArray(final byte[] bytes, final int offset, final int length) {
|
||||
return decodeByteArray(bytes, offset, length, null);
|
||||
}
|
||||
|
||||
public static Bitmap decodeByteArray(final byte[] bytes, final int offset, final int length,
|
||||
final BitmapFactory.Options options) {
|
||||
if (bytes.length <= 0) {
|
||||
throw new IllegalArgumentException("bytes.length " + bytes.length
|
||||
+ " must be a positive number");
|
||||
}
|
||||
|
||||
Bitmap bitmap = null;
|
||||
try {
|
||||
bitmap = BitmapFactory.decodeByteArray(bytes, offset, length, options);
|
||||
} catch (OutOfMemoryError e) {
|
||||
Log.e(LOGTAG, "decodeByteArray(bytes.length=" + bytes.length
|
||||
+ ", options= " + options + ") OOM!", e);
|
||||
return null;
|
||||
}
|
||||
|
||||
if (bitmap == null) {
|
||||
Log.w(LOGTAG, "decodeByteArray() returning null because BitmapFactory returned null");
|
||||
return null;
|
||||
}
|
||||
|
||||
if (bitmap.getWidth() <= 0 || bitmap.getHeight() <= 0) {
|
||||
Log.w(LOGTAG, "decodeByteArray() returning null because BitmapFactory returned "
|
||||
+ "a bitmap with dimensions " + bitmap.getWidth()
|
||||
+ "x" + bitmap.getHeight());
|
||||
return null;
|
||||
}
|
||||
|
||||
return bitmap;
|
||||
}
|
||||
|
||||
public static Bitmap decodeStream(final InputStream inputStream) {
|
||||
try {
|
||||
return BitmapFactory.decodeStream(inputStream);
|
||||
} catch (OutOfMemoryError e) {
|
||||
Log.e(LOGTAG, "decodeStream() OOM!", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static Bitmap decodeUrl(final Uri uri) {
|
||||
return decodeUrl(uri.toString());
|
||||
}
|
||||
|
||||
public static Bitmap decodeUrl(final String urlString) {
|
||||
URL url;
|
||||
|
||||
try {
|
||||
url = new URL(urlString);
|
||||
} catch (MalformedURLException e) {
|
||||
Log.w(LOGTAG, "decodeUrl: malformed URL " + urlString);
|
||||
return null;
|
||||
}
|
||||
|
||||
return decodeUrl(url);
|
||||
}
|
||||
|
||||
public static Bitmap decodeUrl(final URL url) {
|
||||
InputStream stream = null;
|
||||
|
||||
try {
|
||||
stream = url.openStream();
|
||||
} catch (IOException e) {
|
||||
Log.w(LOGTAG, "decodeUrl: IOException downloading " + url);
|
||||
return null;
|
||||
}
|
||||
|
||||
if (stream == null) {
|
||||
Log.w(LOGTAG, "decodeUrl: stream not found downloading " + url);
|
||||
return null;
|
||||
}
|
||||
|
||||
Bitmap bitmap = decodeStream(stream);
|
||||
|
||||
try {
|
||||
stream.close();
|
||||
} catch (IOException e) {
|
||||
Log.w(LOGTAG, "decodeUrl: IOException closing stream " + url, e);
|
||||
}
|
||||
|
||||
return bitmap;
|
||||
}
|
||||
|
||||
public static Bitmap decodeResource(final Context context, final int id) {
|
||||
return decodeResource(context, id, null);
|
||||
}
|
||||
|
||||
public static Bitmap decodeResource(final Context context, final int id,
|
||||
final BitmapFactory.Options options) {
|
||||
Resources resources = context.getResources();
|
||||
try {
|
||||
return BitmapFactory.decodeResource(resources, id, options);
|
||||
} catch (OutOfMemoryError e) {
|
||||
Log.e(LOGTAG, "decodeResource() OOM! Resource id=" + id, e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static @ColorInt int getDominantColor(final Bitmap source,
|
||||
final @ColorInt int defaultColor) {
|
||||
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.
|
||||
return getDominantColorCustomImplementation(source, true, defaultColor);
|
||||
} else {
|
||||
try {
|
||||
final Palette palette = Palette.from(source).generate();
|
||||
return palette.getVibrantColor(defaultColor);
|
||||
} catch (ArrayIndexOutOfBoundsException e) {
|
||||
// We saw the palette library fail with an ArrayIndexOutOfBoundsException intermittently
|
||||
// in automation. In this case lets just swallow the exception and move on without a
|
||||
// color. This is a valid condition and callers should handle this gracefully (Bug 1318560).
|
||||
Log.e(LOGTAG, "Palette generation failed with ArrayIndexOutOfBoundsException", e);
|
||||
|
||||
return defaultColor;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static @ColorInt int getDominantColorCustomImplementation(final Bitmap source) {
|
||||
return getDominantColorCustomImplementation(source, true, Color.WHITE);
|
||||
}
|
||||
|
||||
public static @ColorInt int getDominantColorCustomImplementation(
|
||||
final Bitmap source, final boolean applyThreshold, final @ColorInt int defaultColor) {
|
||||
if (source == null) {
|
||||
return defaultColor;
|
||||
}
|
||||
|
||||
// Keep track of how many times a hue in a given bin appears in the image.
|
||||
// Hue values range [0 .. 360), so dividing by 10, we get 36 bins.
|
||||
int[] colorBins = new int[36];
|
||||
|
||||
// The bin with the most colors. Initialize to -1 to prevent accidentally
|
||||
// thinking the first bin holds the dominant color.
|
||||
int maxBin = -1;
|
||||
|
||||
// Keep track of sum hue/saturation/value per hue bin, which we'll use to
|
||||
// compute an average to for the dominant color.
|
||||
float[] sumHue = new float[36];
|
||||
float[] sumSat = new float[36];
|
||||
float[] sumVal = new float[36];
|
||||
float[] hsv = new float[3];
|
||||
|
||||
int height = source.getHeight();
|
||||
int width = source.getWidth();
|
||||
int[] pixels = new int[width * height];
|
||||
source.getPixels(pixels, 0, width, 0, 0, width, height);
|
||||
for (int row = 0; row < height; row++) {
|
||||
for (int col = 0; col < width; col++) {
|
||||
int c = pixels[col + row * width];
|
||||
// Ignore pixels with a certain transparency.
|
||||
if (Color.alpha(c) < 128)
|
||||
continue;
|
||||
|
||||
Color.colorToHSV(c, hsv);
|
||||
|
||||
// If a threshold is applied, ignore arbitrarily chosen values for "white" and "black".
|
||||
if (applyThreshold && (hsv[1] <= 0.35f || hsv[2] <= 0.35f))
|
||||
continue;
|
||||
|
||||
// We compute the dominant color by putting colors in bins based on their hue.
|
||||
int bin = (int) Math.floor(hsv[0] / 10.0f);
|
||||
|
||||
// Update the sum hue/saturation/value for this bin.
|
||||
sumHue[bin] = sumHue[bin] + hsv[0];
|
||||
sumSat[bin] = sumSat[bin] + hsv[1];
|
||||
sumVal[bin] = sumVal[bin] + hsv[2];
|
||||
|
||||
// Increment the number of colors in this bin.
|
||||
colorBins[bin]++;
|
||||
|
||||
// Keep track of the bin that holds the most colors.
|
||||
if (maxBin < 0 || colorBins[bin] > colorBins[maxBin])
|
||||
maxBin = bin;
|
||||
}
|
||||
}
|
||||
|
||||
// maxBin may never get updated if the image holds only transparent and/or black/white pixels.
|
||||
if (maxBin < 0) {
|
||||
return defaultColor;
|
||||
}
|
||||
|
||||
// Return a color with the average hue/saturation/value of the bin with the most colors.
|
||||
hsv[0] = sumHue[maxBin] / colorBins[maxBin];
|
||||
hsv[1] = sumSat[maxBin] / colorBins[maxBin];
|
||||
hsv[2] = sumVal[maxBin] / colorBins[maxBin];
|
||||
return Color.HSVToColor(hsv);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decodes a bitmap from a Base64 data URI.
|
||||
*
|
||||
* @param dataURI a Base64-encoded data URI string
|
||||
* @return the decoded bitmap, or null if the data URI is invalid
|
||||
*/
|
||||
public static Bitmap getBitmapFromDataURI(final String dataURI) {
|
||||
if (dataURI == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
byte[] raw = getBytesFromDataURI(dataURI);
|
||||
if (raw == null || raw.length == 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return decodeByteArray(raw);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a byte[] containing the bytes in a given base64 string, or null if this is not a valid
|
||||
* base64 string.
|
||||
*/
|
||||
public static byte[] getBytesFromBase64(final String base64) {
|
||||
try {
|
||||
return Base64.decode(base64, Base64.DEFAULT);
|
||||
} catch (Exception e) {
|
||||
Log.e(LOGTAG, "exception decoding bitmap from data URI: " + base64, e);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static byte[] getBytesFromDataURI(final String dataURI) {
|
||||
final String base64 = dataURI.substring(dataURI.indexOf(',') + 1);
|
||||
return getBytesFromBase64(base64);
|
||||
}
|
||||
|
||||
public static Bitmap getBitmapFromDrawable(final Drawable drawable) {
|
||||
if (drawable instanceof BitmapDrawable) {
|
||||
return ((BitmapDrawable) drawable).getBitmap();
|
||||
}
|
||||
|
||||
int width = drawable.getIntrinsicWidth();
|
||||
width = width > 0 ? width : 1;
|
||||
int height = drawable.getIntrinsicHeight();
|
||||
height = height > 0 ? height : 1;
|
||||
|
||||
Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
|
||||
Canvas canvas = new Canvas(bitmap);
|
||||
drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
|
||||
drawable.draw(canvas);
|
||||
|
||||
return bitmap;
|
||||
}
|
||||
|
||||
public static int getResource(final Context context, final Uri resourceUrl) {
|
||||
final String scheme = resourceUrl.getScheme();
|
||||
if (!"drawable".equals(scheme)) {
|
||||
// Return a "not found" default icon that's easy to spot.
|
||||
return android.R.drawable.sym_def_app_icon;
|
||||
}
|
||||
|
||||
String resource = resourceUrl.getSchemeSpecificPart();
|
||||
if (resource.startsWith("//")) {
|
||||
resource = resource.substring(2);
|
||||
}
|
||||
|
||||
final Resources res = context.getResources();
|
||||
int id = res.getIdentifier(resource, "drawable", context.getPackageName());
|
||||
if (id != 0) {
|
||||
return id;
|
||||
}
|
||||
|
||||
// For backwards compatibility, we also search in system resources.
|
||||
id = res.getIdentifier(resource, "drawable", "android");
|
||||
if (id != 0) {
|
||||
return id;
|
||||
}
|
||||
|
||||
Log.w(LOGTAG, "Cannot find drawable/" + resource);
|
||||
// Return a "not found" default icon that's easy to spot.
|
||||
return android.R.drawable.sym_def_app_icon;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user