Loading packages/SystemUI/src/com/android/systemui/glwallpaper/ImageProcessHelper.java +114 −24 Original line number Diff line number Diff line Loading @@ -30,13 +30,15 @@ import android.os.Message; import android.util.Log; /** * A helper class that computes histogram and percentile 85 from a bitmap. * Percentile 85 will be computed each time the user picks a new image wallpaper. * A helper class that computes threshold from a bitmap. * Threshold will be computed each time the user picks a new image wallpaper. */ class ImageProcessHelper { private static final String TAG = ImageProcessHelper.class.getSimpleName(); private static final float DEFAULT_PER85 = 0.8f; private static final int MSG_UPDATE_PER85 = 1; private static final float DEFAULT_THRESHOLD = 0.8f; private static final float DEFAULT_OTSU_THRESHOLD = 0f; private static final float MAX_THRESHOLD = 0.89f; private static final int MSG_UPDATE_THRESHOLD = 1; /** * This color matrix will be applied to each pixel to get luminance from rgb by below formula: Loading @@ -53,8 +55,8 @@ class ImageProcessHelper { @Override public boolean handleMessage(Message msg) { switch (msg.what) { case MSG_UPDATE_PER85: mPer85 = (float) msg.obj; case MSG_UPDATE_THRESHOLD: mThreshold = (float) msg.obj; return true; default: return false; Loading @@ -62,20 +64,20 @@ class ImageProcessHelper { } }); private float mPer85 = DEFAULT_PER85; private float mThreshold = DEFAULT_THRESHOLD; void startComputingPercentile85(Bitmap bitmap) { new Per85ComputeTask(mHandler).execute(bitmap); void start(Bitmap bitmap) { new ThresholdComputeTask(mHandler).execute(bitmap); } float getPercentile85() { return mPer85; float getThreshold() { return Math.min(mThreshold, MAX_THRESHOLD); } private static class Per85ComputeTask extends AsyncTask<Bitmap, Void, Float> { private static class ThresholdComputeTask extends AsyncTask<Bitmap, Void, Float> { private Handler mUpdateHandler; Per85ComputeTask(Handler handler) { ThresholdComputeTask(Handler handler) { super(handler); mUpdateHandler = handler; } Loading @@ -84,35 +86,55 @@ class ImageProcessHelper { protected Float doInBackground(Bitmap... bitmaps) { Bitmap bitmap = bitmaps[0]; if (bitmap != null) { int[] histogram = processHistogram(bitmap); return computePercentile85(bitmap, histogram); return new Threshold().compute(bitmap); } Log.e(TAG, "Per85ComputeTask: Can't get bitmap"); return DEFAULT_PER85; Log.e(TAG, "ThresholdComputeTask: Can't get bitmap"); return DEFAULT_THRESHOLD; } @Override protected void onPostExecute(Float result) { Message msg = mUpdateHandler.obtainMessage(MSG_UPDATE_PER85, result); Message msg = mUpdateHandler.obtainMessage(MSG_UPDATE_THRESHOLD, result); mUpdateHandler.sendMessage(msg); } } private static class Threshold { public float compute(Bitmap bitmap) { Bitmap grayscale = toGrayscale(bitmap); int[] histogram = getHistogram(grayscale); boolean isSolidColor = isSolidColor(grayscale, histogram); // We will see gray wallpaper during the transition if solid color wallpaper is set, // please refer to b/130360362#comment16. // As a result, we use Percentile85 rather than Otsus if a solid color wallpaper is set. ThresholdAlgorithm algorithm = isSolidColor ? new Percentile85() : new Otsus(); return algorithm.compute(grayscale, histogram); } private int[] processHistogram(Bitmap bitmap) { private Bitmap toGrayscale(Bitmap bitmap) { int width = bitmap.getWidth(); int height = bitmap.getHeight(); Bitmap target = Bitmap.createBitmap(width, height, bitmap.getConfig()); Canvas canvas = new Canvas(target); Bitmap grayscale = Bitmap.createBitmap(width, height, bitmap.getConfig()); Canvas canvas = new Canvas(grayscale); ColorMatrix cm = new ColorMatrix(LUMINOSITY_MATRIX); Paint paint = new Paint(); paint.setColorFilter(new ColorMatrixColorFilter(cm)); canvas.drawBitmap(bitmap, new Matrix(), paint); return grayscale; } private int[] getHistogram(Bitmap grayscale) { int width = grayscale.getWidth(); int height = grayscale.getHeight(); // TODO: Fine tune the performance here, tracking on b/123615079. int[] histogram = new int[256]; for (int row = 0; row < height; row++) { for (int col = 0; col < width; col++) { int pixel = target.getPixel(col, row); int pixel = grayscale.getPixel(col, row); int y = Color.red(pixel) + Color.green(pixel) + Color.blue(pixel); histogram[y]++; } Loading @@ -121,8 +143,29 @@ class ImageProcessHelper { return histogram; } private float computePercentile85(Bitmap bitmap, int[] histogram) { float per85 = DEFAULT_PER85; private boolean isSolidColor(Bitmap bitmap, int[] histogram) { boolean solidColor = false; int pixels = bitmap.getWidth() * bitmap.getHeight(); // In solid color case, only one element of histogram has value, // which is pixel counts and the value of other elements should be 0. for (int value : histogram) { if (value != 0 && value != pixels) { break; } if (value == pixels) { solidColor = true; break; } } return solidColor; } } private static class Percentile85 implements ThresholdAlgorithm { @Override public float compute(Bitmap bitmap, int[] histogram) { float per85 = DEFAULT_THRESHOLD; int pixelCount = bitmap.getWidth() * bitmap.getHeight(); float[] acc = new float[256]; for (int i = 0; i < acc.length; i++) { Loading @@ -141,4 +184,51 @@ class ImageProcessHelper { return per85; } } private static class Otsus implements ThresholdAlgorithm { @Override public float compute(Bitmap bitmap, int[] histogram) { float threshold = DEFAULT_OTSU_THRESHOLD; float maxVariance = 0; float pixelCount = bitmap.getWidth() * bitmap.getHeight(); float[] w = new float[2]; float[] m = new float[2]; float[] u = new float[2]; for (int i = 0; i < histogram.length; i++) { m[1] += i * histogram[i]; } w[1] = pixelCount; for (int tonalValue = 0; tonalValue < histogram.length; tonalValue++) { float dU; float variance; float numPixels = histogram[tonalValue]; float tmp = numPixels * tonalValue; w[0] += numPixels; w[1] -= numPixels; if (w[0] == 0 || w[1] == 0) { continue; } m[0] += tmp; m[1] -= tmp; u[0] = m[0] / w[0]; u[1] = m[1] / w[1]; dU = u[0] - u[1]; variance = w[0] * w[1] * dU * dU; if (variance > maxVariance) { threshold = (tonalValue + 1f) / histogram.length; maxVariance = variance; } } return threshold; } } private interface ThresholdAlgorithm { float compute(Bitmap bitmap, int[] histogram); } } packages/SystemUI/src/com/android/systemui/glwallpaper/ImageWallpaperRenderer.java +4 −4 Original line number Diff line number Diff line Loading @@ -78,8 +78,8 @@ public class ImageWallpaperRenderer implements GLSurfaceView.Renderer, mBitmap = mWallpaperManager.getBitmap(); mBitmapWidth = mBitmap.getWidth(); mBitmapHeight = mBitmap.getHeight(); // Compute per85 as transition threshold, this is an async work. mImageProcessHelper.startComputingPercentile85(mBitmap); // Compute threshold of the image, this is an async work. mImageProcessHelper.start(mBitmap); mWallpaperManager.forgetLoadedWallpaper(); } } Loading Loading @@ -108,13 +108,13 @@ public class ImageWallpaperRenderer implements GLSurfaceView.Renderer, @Override public void onDrawFrame(GL10 gl) { float per85 = mImageProcessHelper.getPercentile85(); float threshold = mImageProcessHelper.getThreshold(); float reveal = mImageRevealHelper.getReveal(); glClear(GL_COLOR_BUFFER_BIT); glUniform1f(mWallpaper.getHandle(ImageGLWallpaper.U_AOD2OPACITY), 1); glUniform1f(mWallpaper.getHandle(ImageGLWallpaper.U_PER85), per85); glUniform1f(mWallpaper.getHandle(ImageGLWallpaper.U_PER85), threshold); glUniform1f(mWallpaper.getHandle(ImageGLWallpaper.U_REVEAL), reveal); scaleViewport(reveal); Loading Loading
packages/SystemUI/src/com/android/systemui/glwallpaper/ImageProcessHelper.java +114 −24 Original line number Diff line number Diff line Loading @@ -30,13 +30,15 @@ import android.os.Message; import android.util.Log; /** * A helper class that computes histogram and percentile 85 from a bitmap. * Percentile 85 will be computed each time the user picks a new image wallpaper. * A helper class that computes threshold from a bitmap. * Threshold will be computed each time the user picks a new image wallpaper. */ class ImageProcessHelper { private static final String TAG = ImageProcessHelper.class.getSimpleName(); private static final float DEFAULT_PER85 = 0.8f; private static final int MSG_UPDATE_PER85 = 1; private static final float DEFAULT_THRESHOLD = 0.8f; private static final float DEFAULT_OTSU_THRESHOLD = 0f; private static final float MAX_THRESHOLD = 0.89f; private static final int MSG_UPDATE_THRESHOLD = 1; /** * This color matrix will be applied to each pixel to get luminance from rgb by below formula: Loading @@ -53,8 +55,8 @@ class ImageProcessHelper { @Override public boolean handleMessage(Message msg) { switch (msg.what) { case MSG_UPDATE_PER85: mPer85 = (float) msg.obj; case MSG_UPDATE_THRESHOLD: mThreshold = (float) msg.obj; return true; default: return false; Loading @@ -62,20 +64,20 @@ class ImageProcessHelper { } }); private float mPer85 = DEFAULT_PER85; private float mThreshold = DEFAULT_THRESHOLD; void startComputingPercentile85(Bitmap bitmap) { new Per85ComputeTask(mHandler).execute(bitmap); void start(Bitmap bitmap) { new ThresholdComputeTask(mHandler).execute(bitmap); } float getPercentile85() { return mPer85; float getThreshold() { return Math.min(mThreshold, MAX_THRESHOLD); } private static class Per85ComputeTask extends AsyncTask<Bitmap, Void, Float> { private static class ThresholdComputeTask extends AsyncTask<Bitmap, Void, Float> { private Handler mUpdateHandler; Per85ComputeTask(Handler handler) { ThresholdComputeTask(Handler handler) { super(handler); mUpdateHandler = handler; } Loading @@ -84,35 +86,55 @@ class ImageProcessHelper { protected Float doInBackground(Bitmap... bitmaps) { Bitmap bitmap = bitmaps[0]; if (bitmap != null) { int[] histogram = processHistogram(bitmap); return computePercentile85(bitmap, histogram); return new Threshold().compute(bitmap); } Log.e(TAG, "Per85ComputeTask: Can't get bitmap"); return DEFAULT_PER85; Log.e(TAG, "ThresholdComputeTask: Can't get bitmap"); return DEFAULT_THRESHOLD; } @Override protected void onPostExecute(Float result) { Message msg = mUpdateHandler.obtainMessage(MSG_UPDATE_PER85, result); Message msg = mUpdateHandler.obtainMessage(MSG_UPDATE_THRESHOLD, result); mUpdateHandler.sendMessage(msg); } } private static class Threshold { public float compute(Bitmap bitmap) { Bitmap grayscale = toGrayscale(bitmap); int[] histogram = getHistogram(grayscale); boolean isSolidColor = isSolidColor(grayscale, histogram); // We will see gray wallpaper during the transition if solid color wallpaper is set, // please refer to b/130360362#comment16. // As a result, we use Percentile85 rather than Otsus if a solid color wallpaper is set. ThresholdAlgorithm algorithm = isSolidColor ? new Percentile85() : new Otsus(); return algorithm.compute(grayscale, histogram); } private int[] processHistogram(Bitmap bitmap) { private Bitmap toGrayscale(Bitmap bitmap) { int width = bitmap.getWidth(); int height = bitmap.getHeight(); Bitmap target = Bitmap.createBitmap(width, height, bitmap.getConfig()); Canvas canvas = new Canvas(target); Bitmap grayscale = Bitmap.createBitmap(width, height, bitmap.getConfig()); Canvas canvas = new Canvas(grayscale); ColorMatrix cm = new ColorMatrix(LUMINOSITY_MATRIX); Paint paint = new Paint(); paint.setColorFilter(new ColorMatrixColorFilter(cm)); canvas.drawBitmap(bitmap, new Matrix(), paint); return grayscale; } private int[] getHistogram(Bitmap grayscale) { int width = grayscale.getWidth(); int height = grayscale.getHeight(); // TODO: Fine tune the performance here, tracking on b/123615079. int[] histogram = new int[256]; for (int row = 0; row < height; row++) { for (int col = 0; col < width; col++) { int pixel = target.getPixel(col, row); int pixel = grayscale.getPixel(col, row); int y = Color.red(pixel) + Color.green(pixel) + Color.blue(pixel); histogram[y]++; } Loading @@ -121,8 +143,29 @@ class ImageProcessHelper { return histogram; } private float computePercentile85(Bitmap bitmap, int[] histogram) { float per85 = DEFAULT_PER85; private boolean isSolidColor(Bitmap bitmap, int[] histogram) { boolean solidColor = false; int pixels = bitmap.getWidth() * bitmap.getHeight(); // In solid color case, only one element of histogram has value, // which is pixel counts and the value of other elements should be 0. for (int value : histogram) { if (value != 0 && value != pixels) { break; } if (value == pixels) { solidColor = true; break; } } return solidColor; } } private static class Percentile85 implements ThresholdAlgorithm { @Override public float compute(Bitmap bitmap, int[] histogram) { float per85 = DEFAULT_THRESHOLD; int pixelCount = bitmap.getWidth() * bitmap.getHeight(); float[] acc = new float[256]; for (int i = 0; i < acc.length; i++) { Loading @@ -141,4 +184,51 @@ class ImageProcessHelper { return per85; } } private static class Otsus implements ThresholdAlgorithm { @Override public float compute(Bitmap bitmap, int[] histogram) { float threshold = DEFAULT_OTSU_THRESHOLD; float maxVariance = 0; float pixelCount = bitmap.getWidth() * bitmap.getHeight(); float[] w = new float[2]; float[] m = new float[2]; float[] u = new float[2]; for (int i = 0; i < histogram.length; i++) { m[1] += i * histogram[i]; } w[1] = pixelCount; for (int tonalValue = 0; tonalValue < histogram.length; tonalValue++) { float dU; float variance; float numPixels = histogram[tonalValue]; float tmp = numPixels * tonalValue; w[0] += numPixels; w[1] -= numPixels; if (w[0] == 0 || w[1] == 0) { continue; } m[0] += tmp; m[1] -= tmp; u[0] = m[0] / w[0]; u[1] = m[1] / w[1]; dU = u[0] - u[1]; variance = w[0] * w[1] * dU * dU; if (variance > maxVariance) { threshold = (tonalValue + 1f) / histogram.length; maxVariance = variance; } } return threshold; } } private interface ThresholdAlgorithm { float compute(Bitmap bitmap, int[] histogram); } }
packages/SystemUI/src/com/android/systemui/glwallpaper/ImageWallpaperRenderer.java +4 −4 Original line number Diff line number Diff line Loading @@ -78,8 +78,8 @@ public class ImageWallpaperRenderer implements GLSurfaceView.Renderer, mBitmap = mWallpaperManager.getBitmap(); mBitmapWidth = mBitmap.getWidth(); mBitmapHeight = mBitmap.getHeight(); // Compute per85 as transition threshold, this is an async work. mImageProcessHelper.startComputingPercentile85(mBitmap); // Compute threshold of the image, this is an async work. mImageProcessHelper.start(mBitmap); mWallpaperManager.forgetLoadedWallpaper(); } } Loading Loading @@ -108,13 +108,13 @@ public class ImageWallpaperRenderer implements GLSurfaceView.Renderer, @Override public void onDrawFrame(GL10 gl) { float per85 = mImageProcessHelper.getPercentile85(); float threshold = mImageProcessHelper.getThreshold(); float reveal = mImageRevealHelper.getReveal(); glClear(GL_COLOR_BUFFER_BIT); glUniform1f(mWallpaper.getHandle(ImageGLWallpaper.U_AOD2OPACITY), 1); glUniform1f(mWallpaper.getHandle(ImageGLWallpaper.U_PER85), per85); glUniform1f(mWallpaper.getHandle(ImageGLWallpaper.U_PER85), threshold); glUniform1f(mWallpaper.getHandle(ImageGLWallpaper.U_REVEAL), reveal); scaleViewport(reveal); Loading