Using color and color.darker in Android?

后端 未结 4 1751
傲寒
傲寒 2020-12-02 09:07

Okay, so I have an integer variable in my application. It\'s the value of a color, being set by a color picker in my preferences. Now, I need to use both that color and a da

相关标签:
4条回答
  • 2020-12-02 09:33

    The easiest, I think, would be to convert to HSV, do the darkening there, and convert back:

    float[] hsv = new float[3];
    int color = getColor();
    Color.colorToHSV(color, hsv);
    hsv[2] *= 0.8f; // value component
    color = Color.HSVToColor(hsv);
    

    To lighten, a simple approach may be to multiply the value component by something > 1.0. However, you'll have to clamp the result to the range [0.0, 1.0]. Also, simply multiplying isn't going to lighten black.

    Therefore a better solution is: Reduce the difference from 1.0 of the value component to lighten:

    hsv[2] = 1.0f - 0.8f * (1.0f - hsv[2]);
    

    This is entirely parallel to the approach for darkening, just using 1 as the origin instead of 0. It works to lighten any color (even black) and doesn't need any clamping. It could be simplified to:

    hsv[2] = 0.2f + 0.8f * hsv[2];
    

    However, because of possible rounding effects of floating point arithmetic, I'd be concerned that the result might exceed 1.0f (by perhaps one bit). Better to stick to the slightly more complicated formula.

    0 讨论(0)
  • 2020-12-02 09:42

    The Java Color routine for darken and brighten doesn't require anything special. In fact, it's simply the unrolled understanding of what brightness is being applied to relevant colors. Which is to say you can simply take the red, green, blue values. Multiply them by any factor, ensure they fall correctly into gamut.

    The following is the code found in the Color class.

    private static final double FACTOR = 0.7;
    
    //...
    
    public Color darker() {
        return new Color(Math.max((int)(getRed()  *FACTOR), 0),
                         Math.max((int)(getGreen()*FACTOR), 0),
                         Math.max((int)(getBlue() *FACTOR), 0),
                         getAlpha());
    }
    

    Obviously, from this we can see how to do this process in android. Take the RGB values, multiply them by a factor, and crimp them into gamut. (Recoded from scratch for licensing reasons).

    public int crimp(int c) {
            return Math.min(Math.max(c, 0), 255);
        }
    
    public int darken(int color) {
            double factor = 0.7;
            return (color & 0xFF000000) | 
                    (crimp((int) (((color >> 16) & 0xFF) * factor)) << 16) |
                    (crimp((int) (((color >> 8) & 0xFF) * factor)) << 8) |
                    (crimp((int) (((color) & 0xFF) * factor)));
        }
    

    Do note this is the same thing as just increasing the brightness in HSB, the B is simply the brightest factor, the hue is the ratio between the various colors, and S is how far apart they are. So if we just grab all the colors and multiply them by a factor, we end up with the same colors in the same mix with a bit more white/black in them.

    A lot of modern colorspaces also do this with calculating the Y value through the various color components that best approximate brightness. So you could if you wanted convert to a better form of Y or L through any of the modern colorspaces and unconvert them, other colorspaces have a better form of gamma with regard to how much each color contributes to the actual brightness, lightness, value, whiteness, blackness, or whatever that colorspace calls it. This would do a better job, but for most purposes this is solid.

    So at the extreme-most you can do this by converting to Lab, decreasing the L component and converting it back.

    Here's code to do that:

    static int darken(int color) {
        double factor = 0.7;
        double[] returnarray = new double[3];
        convertRGBsRGB(returnarray, ((color >> 16) & 0xFF), ((color >> 8) & 0xFF), (color & 0xFF));
        convertRGBXYZ(returnarray,returnarray[0], returnarray[1], returnarray[2]);
        convertXYZLab(returnarray,returnarray[0], returnarray[1], returnarray[2]);
        returnarray[0] *= factor;
        convertLabXYZ(returnarray,returnarray[0], returnarray[1], returnarray[2]);
        convertXYZRGB(returnarray,returnarray[0], returnarray[1], returnarray[2]);
        return (color & 0xFF000000) | convertsRGBRGB(returnarray);
    }
    static void convertRGBsRGB(double[] returnarray, int R, int G, int B) {
        double var_R = (((double) R) / 255.0d);                     //RGB from 0 to 255
        double var_G = (((double) G) / 255.0d);
        double var_B = (((double) B) / 255.0d);
        returnarray[0] = var_R;
        returnarray[1] = var_G;
        returnarray[2] = var_B;
    }
    static int convertsRGBRGB(double[] sRGB) {
        int red = (int) (sRGB[0] * 255);
        int green = (int) (sRGB[1] * 255);
        int blue = (int) (sRGB[2] * 255);
        red = crimp(red);
        green = crimp(green);
        blue = crimp(blue);
        return (red << 16) | (green << 8) | blue;
    }
    public static int crimp(int v) {
        if (v > 0xff) {
            v = 0xff;
        }
        if (v < 0) {
            v = 0;
        }
        return v;
    }
    
    public static final double ref_X = 95.047; //ref_X =  95.047   Observer= 2°, Illuminant= D65
    public static final double ref_Y = 100.000; //ref_Y = 100.000
    public static final double ref_Z = 108.883;//ref_Z = 108.883
    static void convertRGBXYZ(double[] returnarray, double var_R, double var_G, double var_B) {
    
        if (var_R > 0.04045) {
            var_R = Math.pow(((var_R + 0.055) / 1.055), 2.4);
    
        } else {
            var_R = var_R / 12.92;
    
        }
        if (var_G > 0.04045) {
            var_G = Math.pow(((var_G + 0.055) / 1.055), 2.4);
    
        } else {
            var_G = var_G / 12.92;
    
        }
        if (var_B > 0.04045) {
            var_B = Math.pow(((var_B + 0.055) / 1.055), 2.4);
    
        } else {
            var_B = var_B / 12.92;
        }
        var_R = var_R * 100;
        var_G = var_G * 100;
        var_B = var_B * 100; //Observer. = 2°, Illuminant = D65
        double X = var_R * 0.4124 + var_G * 0.3576 + var_B * 0.1805;
        double Y = var_R * 0.2126 + var_G * 0.7152 + var_B * 0.0722;
        double Z = var_R * 0.0193 + var_G * 0.1192 + var_B * 0.9505;
    
        returnarray[0] = X;
        returnarray[1] = Y;
        returnarray[2] = Z;
    }
    
    static void convertXYZLab(double[] returnarray, double X, double Y, double Z) {
        double var_X = X / ref_X;
        double var_Y = Y / ref_Y;
        double var_Z = Z / ref_Z;
    
        if (var_X > 0.008856) {
            var_X = Math.cbrt(var_X);
    
        } else {
            var_X = (7.787 * var_X) + (16.0d / 116.0d);
    
        }
        if (var_Y > 0.008856) {
            var_Y = Math.cbrt(var_Y);
    
        } else {
            var_Y = (7.787 * var_Y) + (16.0d / 116.0d);
    
        }
        if (var_Z > 0.008856) {
            var_Z = Math.cbrt(var_Z);
    
        } else {
            var_Z = (7.787 * var_Z) + (16.0d / 116.0d);
        }
        double CIE_L = (116 * var_Y) - 16;
        double CIE_a = 500 * (var_X - var_Y);
        double CIE_b = 200 * (var_Y - var_Z);
        returnarray[0] = CIE_L;
        returnarray[1] = CIE_a;
        returnarray[2] = CIE_b;
    }
    
    static void convertLabXYZ(double[] returnarray, double CIE_L, double CIE_a, double CIE_b) {
        double var_Y = (CIE_L + 16) / 116;
        double var_X = CIE_a / 500 + var_Y;
        double var_Z = var_Y - CIE_b / 200;
    
        if ((var_Y * var_Y * var_Y) > 0.008856) {
            var_Y = (var_Y * var_Y * var_Y);
    
        } else {
            var_Y = (((var_Y - 16) / 116)) / 7.787;
        }
        if ((var_X * var_X * var_X) > 0.008856) {
            var_X = (var_X * var_X * var_X);
        } else {
            var_X = ((var_X - 16) / 116) / 7.787;
    
        }
        if ((var_Z * var_Z * var_Z) > 0.008856) {
            var_Z = (var_Z * var_Z * var_Z);
        } else {
            var_Z = ((var_Z - 16) / 116) / 7.787;
        }
    
        double X = ref_X * var_X; //ref_X =  95.047     Observer= 2°, Illuminant= D65
        double Y = ref_Y * var_Y; //ref_Y = 100.000
        double Z = ref_Z * var_Z; //ref_Z = 108.883
        returnarray[0] = X;
        returnarray[1] = Y;
        returnarray[2] = Z;
    }
    
    static void convertXYZRGB(double[] returnarray, double X, double Y, double Z) {
        double var_X = X / 100; //X from 0 to  95.047      (Observer = 2°, Illuminant = D65)
        double var_Y = Y / 100; //Y from 0 to 100.000
        double var_Z = Z / 100; //Z from 0 to 108.883
    
        double var_R = (var_X * 3.2406) + (var_Y * -1.5372) + (var_Z * -0.4986);
        double var_G = (var_X * -0.9689) + (var_Y * 1.8758) + (var_Z * 0.0415);
        double var_B = (var_X * 0.0557) + (var_Y * -0.2040) + (var_Z * 1.0570);
    
        if (var_R > 0.0031308) {
            var_R = 1.055 * (Math.pow(var_R, (1f / 2.4f))) - 0.055;
        } else {
            var_R = 12.92 * var_R;
        }
        if (var_G > 0.0031308) {
            var_G = 1.055 * (Math.pow(var_G, (1f / 2.4f))) - 0.055;
    
        } else {
            var_G = 12.92 * var_G;
    
        }
        if (var_B > 0.0031308) {
            var_B = 1.055 * (Math.pow(var_B, (1f / 2.4f))) - 0.055;
    
        } else {
            var_B = 12.92 * var_B;
    
        }
        returnarray[0] = var_R;
        returnarray[1] = var_G;
        returnarray[2] = var_B;
    }
    

    My couple lines there do the same thing as Color.darken() here's an image of a sample color set (these colors are max distance from all previous colors through CIE-LabD2000, just using them as a robust color samples set.)

    Index Color, Color.darker(), and my basic darken(), all at a FACTOR of 0.7. (these should be identical)

    Next for those who suggested using Lab to darken,

    Index Color, Color.darker(), and Lab Darker(), all at a FACTOR of 0.7. (is this an improvement worth the time suck?)

    0 讨论(0)
  • 2020-12-02 09:47

    Ted's answer to lighten a color wasn't working for me so here is a solution that might help someone else:

    /**
     * Lightens a color by a given factor.
     * 
     * @param color
     *            The color to lighten
     * @param factor
     *            The factor to lighten the color. 0 will make the color unchanged. 1 will make the
     *            color white.
     * @return lighter version of the specified color.
     */
    public static int lighter(int color, float factor) {
        int red = (int) ((Color.red(color) * (1 - factor) / 255 + factor) * 255);
        int green = (int) ((Color.green(color) * (1 - factor) / 255 + factor) * 255);
        int blue = (int) ((Color.blue(color) * (1 - factor) / 255 + factor) * 255);
        return Color.argb(Color.alpha(color), red, green, blue);
    }
    
    0 讨论(0)
  • 2020-12-02 09:51

    Here is what I created:

    /**
     * Returns darker version of specified <code>color</code>.
     */
    public static int darker (int color, float factor) {
        int a = Color.alpha( color );
        int r = Color.red( color );
        int g = Color.green( color );
        int b = Color.blue( color );
    
        return Color.argb( a,
                Math.max( (int)(r * factor), 0 ),
                Math.max( (int)(g * factor), 0 ),
                Math.max( (int)(b * factor), 0 ) );
    }
    
    0 讨论(0)
提交回复
热议问题