diff options
author | Deepanshu Gupta <deepanshu@google.com> | 2015-02-26 14:34:57 -0800 |
---|---|---|
committer | Deepanshu Gupta <deepanshu@google.com> | 2015-02-26 15:02:16 -0800 |
commit | 1a10ca7e526736b4fd143f7c9f3b29643c0062a4 (patch) | |
tree | af4ab4d14bc3e1a31d1c6ac1bf27ef0cd2a153a5 | |
parent | d68448a601687e6f1fbd44b8f8471ad06686a7fa (diff) | |
download | frameworks_base-1a10ca7e526736b4fd143f7c9f3b29643c0062a4.zip frameworks_base-1a10ca7e526736b4fd143f7c9f3b29643c0062a4.tar.gz frameworks_base-1a10ca7e526736b4fd143f7c9f3b29643c0062a4.tar.bz2 |
Correct PorterDuff filters.
1. Remove unused modes - makes the class more manageable, and missing
modes can always be readded from the git history.
2. Reuse the existing BlendComposite instances where possible.
3. Fix incorrect alpha computation for multiply mode.
4. Change the alpha computation for all blend modes to compenstate for
the fact that the color filter image that we create extends beyond the
image it is inteded to be applied to.
Change-Id: Iedebf289a23325ee4c6d406dcad46a9edb1855c7
-rw-r--r-- | tools/layoutlib/bridge/src/android/graphics/BlendComposite.java | 552 | ||||
-rw-r--r-- | tools/layoutlib/bridge/src/android/graphics/PorterDuffColorFilter_Delegate.java | 2 |
2 files changed, 37 insertions, 517 deletions
diff --git a/tools/layoutlib/bridge/src/android/graphics/BlendComposite.java b/tools/layoutlib/bridge/src/android/graphics/BlendComposite.java index a3ec2cc..b9928fc 100644 --- a/tools/layoutlib/bridge/src/android/graphics/BlendComposite.java +++ b/tools/layoutlib/bridge/src/android/graphics/BlendComposite.java @@ -29,75 +29,38 @@ import java.awt.image.WritableRaster; * The class is adapted from a demo tool for Blending Modes written by * Romain Guy (romainguy@android.com). The tool is available at * http://www.curious-creature.org/2006/09/20/new-blendings-modes-for-java2d/ + * + * This class has been adapted for applying color filters. When applying color filters, the src + * image should not extend beyond the dest image, but in our implementation of the filters, it does. + * To compensate for the effect, we recompute the alpha value of the src image before applying + * the color filter as it should have been applied. */ public final class BlendComposite implements Composite { public enum BlendingMode { - NORMAL, - AVERAGE, - MULTIPLY, - SCREEN, - DARKEN, - LIGHTEN, - OVERLAY, - HARD_LIGHT, - SOFT_LIGHT, - DIFFERENCE, - NEGATION, - EXCLUSION, - COLOR_DODGE, - INVERSE_COLOR_DODGE, - SOFT_DODGE, - COLOR_BURN, - INVERSE_COLOR_BURN, - SOFT_BURN, - REFLECT, - GLOW, - FREEZE, - HEAT, - ADD, - SUBTRACT, - STAMP, - RED, - GREEN, - BLUE, - HUE, - SATURATION, - COLOR, - LUMINOSITY + MULTIPLY(Multiply), + SCREEN(Screen), + DARKEN(Darken), + LIGHTEN(Lighten), + OVERLAY(Overlay), + ADD(Add); + + private BlendComposite mComposite; + + BlendingMode(BlendComposite composite) { + mComposite = composite; + } + + BlendComposite getBlendComposite() { + return mComposite; + } } - public static final BlendComposite Normal = new BlendComposite(BlendingMode.NORMAL); - public static final BlendComposite Average = new BlendComposite(BlendingMode.AVERAGE); public static final BlendComposite Multiply = new BlendComposite(BlendingMode.MULTIPLY); public static final BlendComposite Screen = new BlendComposite(BlendingMode.SCREEN); public static final BlendComposite Darken = new BlendComposite(BlendingMode.DARKEN); public static final BlendComposite Lighten = new BlendComposite(BlendingMode.LIGHTEN); public static final BlendComposite Overlay = new BlendComposite(BlendingMode.OVERLAY); - public static final BlendComposite HardLight = new BlendComposite(BlendingMode.HARD_LIGHT); - public static final BlendComposite SoftLight = new BlendComposite(BlendingMode.SOFT_LIGHT); - public static final BlendComposite Difference = new BlendComposite(BlendingMode.DIFFERENCE); - public static final BlendComposite Negation = new BlendComposite(BlendingMode.NEGATION); - public static final BlendComposite Exclusion = new BlendComposite(BlendingMode.EXCLUSION); - public static final BlendComposite ColorDodge = new BlendComposite(BlendingMode.COLOR_DODGE); - public static final BlendComposite InverseColorDodge = new BlendComposite(BlendingMode.INVERSE_COLOR_DODGE); - public static final BlendComposite SoftDodge = new BlendComposite(BlendingMode.SOFT_DODGE); - public static final BlendComposite ColorBurn = new BlendComposite(BlendingMode.COLOR_BURN); - public static final BlendComposite InverseColorBurn = new BlendComposite(BlendingMode.INVERSE_COLOR_BURN); - public static final BlendComposite SoftBurn = new BlendComposite(BlendingMode.SOFT_BURN); - public static final BlendComposite Reflect = new BlendComposite(BlendingMode.REFLECT); - public static final BlendComposite Glow = new BlendComposite(BlendingMode.GLOW); - public static final BlendComposite Freeze = new BlendComposite(BlendingMode.FREEZE); - public static final BlendComposite Heat = new BlendComposite(BlendingMode.HEAT); public static final BlendComposite Add = new BlendComposite(BlendingMode.ADD); - public static final BlendComposite Subtract = new BlendComposite(BlendingMode.SUBTRACT); - public static final BlendComposite Stamp = new BlendComposite(BlendingMode.STAMP); - public static final BlendComposite Red = new BlendComposite(BlendingMode.RED); - public static final BlendComposite Green = new BlendComposite(BlendingMode.GREEN); - public static final BlendComposite Blue = new BlendComposite(BlendingMode.BLUE); - public static final BlendComposite Hue = new BlendComposite(BlendingMode.HUE); - public static final BlendComposite Saturation = new BlendComposite(BlendingMode.SATURATION); - public static final BlendComposite Color = new BlendComposite(BlendingMode.COLOR); - public static final BlendComposite Luminosity = new BlendComposite(BlendingMode.LUMINOSITY); private float alpha; private BlendingMode mode; @@ -112,21 +75,16 @@ public final class BlendComposite implements Composite { } public static BlendComposite getInstance(BlendingMode mode) { - return new BlendComposite(mode); + return mode.getBlendComposite(); } public static BlendComposite getInstance(BlendingMode mode, float alpha) { + if (alpha > 0.9999f) { + return getInstance(mode); + } return new BlendComposite(mode, alpha); } - public BlendComposite derive(BlendingMode mode) { - return this.mode == mode ? this : new BlendComposite(mode, getAlpha()); - } - - public BlendComposite derive(float alpha) { - return this.alpha == alpha ? this : new BlendComposite(getMode(), alpha); - } - public float getAlpha() { return alpha; } @@ -157,11 +115,7 @@ public final class BlendComposite implements Composite { BlendComposite bc = (BlendComposite) obj; - if (mode != bc.mode) { - return false; - } - - return alpha == bc.alpha; + return mode == bc.mode && alpha == bc.alpha; } public CompositeContext createContext(ColorModel srcColorModel, @@ -220,6 +174,11 @@ public final class BlendComposite implements Composite { dstPixel[2] = (pixel ) & 0xFF; dstPixel[3] = (pixel >> 24) & 0xFF; + // ---- Modified from original ---- + // recompute src pixel for transparency. + srcPixel[3] *= dstPixel[3] / 0xFF; + // ---- Modification ends ---- + result = blender.blend(srcPixel, dstPixel, result); // mixes the result with the opacity @@ -246,123 +205,8 @@ public final class BlendComposite implements Composite { private static abstract class Blender { public abstract int[] blend(int[] src, int[] dst, int[] result); - private static void RGBtoHSL(int r, int g, int b, float[] hsl) { - float var_R = (r / 255f); - float var_G = (g / 255f); - float var_B = (b / 255f); - - float var_Min; - float var_Max; - float del_Max; - - if (var_R > var_G) { - var_Min = var_G; - var_Max = var_R; - } else { - var_Min = var_R; - var_Max = var_G; - } - if (var_B > var_Max) { - var_Max = var_B; - } - if (var_B < var_Min) { - var_Min = var_B; - } - - del_Max = var_Max - var_Min; - - float H, S, L; - L = (var_Max + var_Min) / 2f; - - if (del_Max - 0.01f <= 0.0f) { - H = 0; - S = 0; - } else { - if (L < 0.5f) { - S = del_Max / (var_Max + var_Min); - } else { - S = del_Max / (2 - var_Max - var_Min); - } - - float del_R = (((var_Max - var_R) / 6f) + (del_Max / 2f)) / del_Max; - float del_G = (((var_Max - var_G) / 6f) + (del_Max / 2f)) / del_Max; - float del_B = (((var_Max - var_B) / 6f) + (del_Max / 2f)) / del_Max; - - if (var_R == var_Max) { - H = del_B - del_G; - } else if (var_G == var_Max) { - H = (1 / 3f) + del_R - del_B; - } else { - H = (2 / 3f) + del_G - del_R; - } - if (H < 0) { - H += 1; - } - if (H > 1) { - H -= 1; - } - } - - hsl[0] = H; - hsl[1] = S; - hsl[2] = L; - } - - private static void HSLtoRGB(float h, float s, float l, int[] rgb) { - int R, G, B; - - if (s - 0.01f <= 0.0f) { - R = (int) (l * 255.0f); - G = (int) (l * 255.0f); - B = (int) (l * 255.0f); - } else { - float var_1, var_2; - if (l < 0.5f) { - var_2 = l * (1 + s); - } else { - var_2 = (l + s) - (s * l); - } - var_1 = 2 * l - var_2; - - R = (int) (255.0f * hue2RGB(var_1, var_2, h + (1.0f / 3.0f))); - G = (int) (255.0f * hue2RGB(var_1, var_2, h)); - B = (int) (255.0f * hue2RGB(var_1, var_2, h - (1.0f / 3.0f))); - } - - rgb[0] = R; - rgb[1] = G; - rgb[2] = B; - } - - private static float hue2RGB(float v1, float v2, float vH) { - if (vH < 0.0f) { - vH += 1.0f; - } - if (vH > 1.0f) { - vH -= 1.0f; - } - if ((6.0f * vH) < 1.0f) { - return (v1 + (v2 - v1) * 6.0f * vH); - } - if ((2.0f * vH) < 1.0f) { - return (v2); - } - if ((3.0f * vH) < 2.0f) { - return (v1 + (v2 - v1) * ((2.0f / 3.0f) - vH) * 6.0f); - } - return (v1); - } - public static Blender getBlenderFor(BlendComposite composite) { switch (composite.getMode()) { - case NORMAL: - return new Blender() { - @Override - public int[] blend(int[] src, int[] dst, int[] result) { - System.arraycopy(src, 0, result, 0, 4); - return result; - } - }; case ADD: return new Blender() { @Override @@ -373,65 +217,6 @@ public final class BlendComposite implements Composite { return result; } }; - case AVERAGE: - return new Blender() { - @Override - public int[] blend(int[] src, int[] dst, int[] result) { - for (int i = 0; i < 3; i++) { - result[i] = (src[i] + dst[i]) >> 1; - } - result[3] = Math.min(255, src[3] + dst[3]); - return result; - } - }; - case BLUE: - return new Blender() { - @Override - public int[] blend(int[] src, int[] dst, int[] result) { - System.arraycopy(dst, 0, result, 0, 3); - result[3] = Math.min(255, src[3] + dst[3]); - return result; - } - }; - case COLOR: - return new Blender() { - @Override - public int[] blend(int[] src, int[] dst, int[] result) { - float[] srcHSL = new float[3]; - RGBtoHSL(src[0], src[1], src[2], srcHSL); - float[] dstHSL = new float[3]; - RGBtoHSL(dst[0], dst[1], dst[2], dstHSL); - - HSLtoRGB(srcHSL[0], srcHSL[1], dstHSL[2], result); - result[3] = Math.min(255, src[3] + dst[3]); - - return result; - } - }; - case COLOR_BURN: - return new Blender() { - @Override - public int[] blend(int[] src, int[] dst, int[] result) { - for (int i = 0; i < 3; i++) { - result[i] = src[i] == 0 ? 0 : - Math.max(0, 255 - (((255 - dst[i]) << 8) / src[i])); - } - result[3] = Math.min(255, src[3] + dst[3]); - return result; - } - }; - case COLOR_DODGE: - return new Blender() { - @Override - public int[] blend(int[] src, int[] dst, int[] result) { - for (int i = 0; i < 3; i++) { - result[i] = src[i] == 255 ? 255 : - Math.min((dst[i] << 8) / (255 - src[i]), 255); - } - result[3] = Math.min(255, src[3] + dst[3]); - return result; - } - }; case DARKEN: return new Blender() { @Override @@ -443,136 +228,6 @@ public final class BlendComposite implements Composite { return result; } }; - case DIFFERENCE: - return new Blender() { - @Override - public int[] blend(int[] src, int[] dst, int[] result) { - for (int i = 0; i < 3; i++) { - result[i] = dst[i] + src[i] - (dst[i] * src[i] >> 7); - } - result[3] = Math.min(255, src[3] + dst[3]); - return result; - } - }; - case EXCLUSION: - return new Blender() { - @Override - public int[] blend(int[] src, int[] dst, int[] result) { - for (int i = 0; i < 3; i++) { - result[i] = dst[i] + src[i] - (dst[i] * src[i] >> 7); - } - result[3] = Math.min(255, src[3] + dst[3]); - return result; - } - }; - case FREEZE: - return new Blender() { - @Override - public int[] blend(int[] src, int[] dst, int[] result) { - for (int i = 0; i < 3; i++) { - result[i] = src[i] == 0 ? 0 : - Math.max(0, 255 - (255 - dst[i]) * (255 - dst[i]) / src[i]); - } - result[3] = Math.min(255, src[3] + dst[3]); - return result; - } - }; - case GLOW: - return new Blender() { - @Override - public int[] blend(int[] src, int[] dst, int[] result) { - for (int i = 0; i < 3; i++) { - result[i] = dst[i] == 255 ? 255 : - Math.min(255, src[i] * src[i] / (255 - dst[i])); - } - result[3] = Math.min(255, src[3] + dst[3]); - return result; - } - }; - case GREEN: - return new Blender() { - @Override - public int[] blend(int[] src, int[] dst, int[] result) { - return new int[] { - dst[0], - dst[1], - src[2], - Math.min(255, src[3] + dst[3]) - }; - } - }; - case HARD_LIGHT: - return new Blender() { - @Override - public int[] blend(int[] src, int[] dst, int[] result) { - return new int[] { - src[0] < 128 ? dst[0] * src[0] >> 7 : - 255 - ((255 - src[0]) * (255 - dst[0]) >> 7), - src[1] < 128 ? dst[1] * src[1] >> 7 : - 255 - ((255 - src[1]) * (255 - dst[1]) >> 7), - src[2] < 128 ? dst[2] * src[2] >> 7 : - 255 - ((255 - src[2]) * (255 - dst[2]) >> 7), - Math.min(255, src[3] + dst[3]) - }; - } - }; - case HEAT: - return new Blender() { - @Override - public int[] blend(int[] src, int[] dst, int[] result) { - return new int[] { - dst[0] == 0 ? 0 : Math.max(0, 255 - (255 - src[0]) * (255 - src[0]) / dst[0]), - dst[1] == 0 ? 0 : Math.max(0, 255 - (255 - src[1]) * (255 - src[1]) / dst[1]), - dst[2] == 0 ? 0 : Math.max(0, 255 - (255 - src[2]) * (255 - src[2]) / dst[2]), - Math.min(255, src[3] + dst[3]) - }; - } - }; - case HUE: - return new Blender() { - @Override - public int[] blend(int[] src, int[] dst, int[] result) { - float[] srcHSL = new float[3]; - RGBtoHSL(src[0], src[1], src[2], srcHSL); - float[] dstHSL = new float[3]; - RGBtoHSL(dst[0], dst[1], dst[2], dstHSL); - - HSLtoRGB(srcHSL[0], dstHSL[1], dstHSL[2], result); - result[3] = Math.min(255, src[3] + dst[3]); - - return result; - } - }; - case INVERSE_COLOR_BURN: - return new Blender() { - @Override - public int[] blend(int[] src, int[] dst, int[] result) { - return new int[] { - dst[0] == 0 ? 0 : - Math.max(0, 255 - (((255 - src[0]) << 8) / dst[0])), - dst[1] == 0 ? 0 : - Math.max(0, 255 - (((255 - src[1]) << 8) / dst[1])), - dst[2] == 0 ? 0 : - Math.max(0, 255 - (((255 - src[2]) << 8) / dst[2])), - Math.min(255, src[3] + dst[3]) - }; - } - }; - case INVERSE_COLOR_DODGE: - return new Blender() { - @Override - public int[] blend(int[] src, int[] dst, int[] result) { - return new int[] { - dst[0] == 255 ? 255 : - Math.min((src[0] << 8) / (255 - dst[0]), 255), - dst[1] == 255 ? 255 : - Math.min((src[1] << 8) / (255 - dst[1]), 255), - dst[2] == 255 ? 255 : - Math.min((src[2] << 8) / (255 - dst[2]), 255), - Math.min(255, src[3] + dst[3]) - }; - } - }; case LIGHTEN: return new Blender() { @Override @@ -584,21 +239,6 @@ public final class BlendComposite implements Composite { return result; } }; - case LUMINOSITY: - return new Blender() { - @Override - public int[] blend(int[] src, int[] dst, int[] result) { - float[] srcHSL = new float[3]; - RGBtoHSL(src[0], src[1], src[2], srcHSL); - float[] dstHSL = new float[3]; - RGBtoHSL(dst[0], dst[1], dst[2], dstHSL); - - HSLtoRGB(dstHSL[0], dstHSL[1], srcHSL[2], result); - result[3] = Math.min(255, src[3] + dst[3]); - - return result; - } - }; case MULTIPLY: return new Blender() { @Override @@ -606,22 +246,10 @@ public final class BlendComposite implements Composite { for (int i = 0; i < 3; i++) { result[i] = (src[i] * dst[i]) >> 8; } - result[3] = Math.min(255, src[3] + dst[3]); + result[3] = Math.min(255, src[3] + dst[3] - (src[3] * dst[3]) / 255); return result; } }; - case NEGATION: - return new Blender() { - @Override - public int[] blend(int[] src, int[] dst, int[] result) { - return new int[] { - 255 - Math.abs(255 - dst[0] - src[0]), - 255 - Math.abs(255 - dst[1] - src[1]), - 255 - Math.abs(255 - dst[2] - src[2]), - Math.min(255, src[3] + dst[3]) - }; - } - }; case OVERLAY: return new Blender() { @Override @@ -634,125 +262,17 @@ public final class BlendComposite implements Composite { return result; } }; - case RED: - return new Blender() { - @Override - public int[] blend(int[] src, int[] dst, int[] result) { - return new int[] { - src[0], - dst[1], - dst[2], - Math.min(255, src[3] + dst[3]) - }; - } - }; - case REFLECT: - return new Blender() { - @Override - public int[] blend(int[] src, int[] dst, int[] result) { - return new int[] { - src[0] == 255 ? 255 : Math.min(255, dst[0] * dst[0] / (255 - src[0])), - src[1] == 255 ? 255 : Math.min(255, dst[1] * dst[1] / (255 - src[1])), - src[2] == 255 ? 255 : Math.min(255, dst[2] * dst[2] / (255 - src[2])), - Math.min(255, src[3] + dst[3]) - }; - } - }; - case SATURATION: + case SCREEN: return new Blender() { @Override public int[] blend(int[] src, int[] dst, int[] result) { - float[] srcHSL = new float[3]; - RGBtoHSL(src[0], src[1], src[2], srcHSL); - float[] dstHSL = new float[3]; - RGBtoHSL(dst[0], dst[1], dst[2], dstHSL); - - HSLtoRGB(dstHSL[0], srcHSL[1], dstHSL[2], result); + result[0] = 255 - ((255 - src[0]) * (255 - dst[0]) >> 8); + result[1] = 255 - ((255 - src[1]) * (255 - dst[1]) >> 8); + result[2] = 255 - ((255 - src[2]) * (255 - dst[2]) >> 8); result[3] = Math.min(255, src[3] + dst[3]); - return result; } }; - case SCREEN: - return new Blender() { - @Override - public int[] blend(int[] src, int[] dst, int[] result) { - return new int[] { - 255 - ((255 - src[0]) * (255 - dst[0]) >> 8), - 255 - ((255 - src[1]) * (255 - dst[1]) >> 8), - 255 - ((255 - src[2]) * (255 - dst[2]) >> 8), - Math.min(255, src[3] + dst[3]) - }; - } - }; - case SOFT_BURN: - return new Blender() { - @Override - public int[] blend(int[] src, int[] dst, int[] result) { - return new int[] { - dst[0] + src[0] < 256 ? - (dst[0] == 255 ? 255 : - Math.min(255, (src[0] << 7) / (255 - dst[0]))) : - Math.max(0, 255 - (((255 - dst[0]) << 7) / src[0])), - dst[1] + src[1] < 256 ? - (dst[1] == 255 ? 255 : - Math.min(255, (src[1] << 7) / (255 - dst[1]))) : - Math.max(0, 255 - (((255 - dst[1]) << 7) / src[1])), - dst[2] + src[2] < 256 ? - (dst[2] == 255 ? 255 : - Math.min(255, (src[2] << 7) / (255 - dst[2]))) : - Math.max(0, 255 - (((255 - dst[2]) << 7) / src[2])), - Math.min(255, src[3] + dst[3]) - }; - } - }; - case SOFT_DODGE: - return new Blender() { - @Override - public int[] blend(int[] src, int[] dst, int[] result) { - return new int[] { - dst[0] + src[0] < 256 ? - (src[0] == 255 ? 255 : - Math.min(255, (dst[0] << 7) / (255 - src[0]))) : - Math.max(0, 255 - (((255 - src[0]) << 7) / dst[0])), - dst[1] + src[1] < 256 ? - (src[1] == 255 ? 255 : - Math.min(255, (dst[1] << 7) / (255 - src[1]))) : - Math.max(0, 255 - (((255 - src[1]) << 7) / dst[1])), - dst[2] + src[2] < 256 ? - (src[2] == 255 ? 255 : - Math.min(255, (dst[2] << 7) / (255 - src[2]))) : - Math.max(0, 255 - (((255 - src[2]) << 7) / dst[2])), - Math.min(255, src[3] + dst[3]) - }; - } - }; - case SOFT_LIGHT: - break; - case STAMP: - return new Blender() { - @Override - public int[] blend(int[] src, int[] dst, int[] result) { - return new int[] { - Math.max(0, Math.min(255, dst[0] + 2 * src[0] - 256)), - Math.max(0, Math.min(255, dst[1] + 2 * src[1] - 256)), - Math.max(0, Math.min(255, dst[2] + 2 * src[2] - 256)), - Math.min(255, src[3] + dst[3]) - }; - } - }; - case SUBTRACT: - return new Blender() { - @Override - public int[] blend(int[] src, int[] dst, int[] result) { - return new int[] { - Math.max(0, src[0] + dst[0] - 256), - Math.max(0, src[1] + dst[1] - 256), - Math.max(0, src[2] + dst[2] - 256), - Math.min(255, src[3] + dst[3]) - }; - } - }; } throw new IllegalArgumentException("Blender not implement for " + composite.getMode().name()); diff --git a/tools/layoutlib/bridge/src/android/graphics/PorterDuffColorFilter_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/PorterDuffColorFilter_Delegate.java index 4ac376c..1ca94dc 100644 --- a/tools/layoutlib/bridge/src/android/graphics/PorterDuffColorFilter_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/PorterDuffColorFilter_Delegate.java @@ -103,7 +103,7 @@ public class PorterDuffColorFilter_Delegate extends ColorFilter_Delegate { // For filtering the colors, the src image should contain the "color" only for pixel values // which are not transparent in the target image. But, we are using a simple rectangular image // completely filled with color. Hence some Composite rules do not apply as intended. However, - // in such cases, they can usually be mapped to some other mode, which produces an + // in such cases, they can usually be mapped to some other mode, which produces an approximately // equivalent result. private Mode getCompatibleMode(Mode mode) { Mode m = mode; |