1
votes

The code I am using to convert rgb colors to cmyk and vice versa :

/**
*
*  Javascript color conversion
*  http://www.webtoolkit.info/
*
**/

function HSV(h, s, v) {
    if (h <= 0) { h = 0; }
    if (s <= 0) { s = 0; }
    if (v <= 0) { v = 0; }

    if (h > 360) { h = 360; }
    if (s > 100) { s = 100; }
    if (v > 100) { v = 100; }

    this.h = h;
    this.s = s;
    this.v = v;
}

function RGB(r, g, b) {
    if (r <= 0) { r = 0; }
    if (g <= 0) { g = 0; }
    if (b <= 0) { b = 0; }

    if (r > 255) { r = 255; }
    if (g > 255) { g = 255; }
    if (b > 255) { b = 255; }

    this.r = r;
    this.g = g;
    this.b = b;
}

function CMYK(c, m, y, k) {
    if (c <= 0) { c = 0; }
    if (m <= 0) { m = 0; }
    if (y <= 0) { y = 0; }
    if (k <= 0) { k = 0; }

    if (c > 100) { c = 100; }
    if (m > 100) { m = 100; }
    if (y > 100) { y = 100; }
    if (k > 100) { k = 100; }

    this.c = c;
    this.m = m;
    this.y = y;
    this.k = k;
}

var ColorConverter = {

    _RGBtoHSV : function  (RGB) {
        var result = new HSV(0, 0, 0);

        r = RGB.r / 255;
        g = RGB.g / 255;
        b = RGB.b / 255;

        var minVal = Math.min(r, g, b);
        var maxVal = Math.max(r, g, b);
        var delta = maxVal - minVal;

        result.v = maxVal;

        if (delta == 0) {
            result.h = 0;
            result.s = 0;
        } else {
            result.s = delta / maxVal;
            var del_R = (((maxVal - r) / 6) + (delta / 2)) / delta;
            var del_G = (((maxVal - g) / 6) + (delta / 2)) / delta;
            var del_B = (((maxVal - b) / 6) + (delta / 2)) / delta;

            if (r == maxVal) { result.h = del_B - del_G; }
            else if (g == maxVal) { result.h = (1 / 3) + del_R - del_B; }
            else if (b == maxVal) { result.h = (2 / 3) + del_G - del_R; }

            if (result.h < 0) { result.h += 1; }
            if (result.h > 1) { result.h -= 1; }
        }

        result.h = Math.round(result.h * 360);
        result.s = Math.round(result.s * 100);
        result.v = Math.round(result.v * 100);

        return result;
    },

    _HSVtoRGB : function  (HSV) {
        var result = new RGB(0, 0, 0);

        var h = HSV.h / 360;
        var s = HSV.s / 100;
        var v = HSV.v / 100;

        if (s == 0) {
            result.r = v * 255;
            result.g = v * 255;
            result.v = v * 255;
        } else {
            var_h = h * 6;
            var_i = Math.floor(var_h);
            var_1 = v * (1 - s);
            var_2 = v * (1 - s * (var_h - var_i));
            var_3 = v * (1 - s * (1 - (var_h - var_i)));

            if (var_i == 0) {var_r = v; var_g = var_3; var_b = var_1}
            else if (var_i == 1) {var_r = var_2; var_g = v; var_b = var_1}
            else if (var_i == 2) {var_r = var_1; var_g = v; var_b = var_3}
            else if (var_i == 3) {var_r = var_1; var_g = var_2; var_b = v}
            else if (var_i == 4) {var_r = var_3; var_g = var_1; var_b = v}
            else {var_r = v; var_g = var_1; var_b = var_2};

            result.r = var_r * 255;
            result.g = var_g * 255;
            result.b = var_b * 255;

            result.r = Math.round(result.r);
            result.g = Math.round(result.g);
            result.b = Math.round(result.b);
        }

        return result;
    },

    _CMYKtoRGB : function (CMYK){
        var result = new RGB(0, 0, 0);

        c = CMYK.c / 100;
        m = CMYK.m / 100;
        y = CMYK.y / 100;
        k = CMYK.k / 100;

        result.r = 1 - Math.min( 1, c * ( 1 - k ) + k );
        result.g = 1 - Math.min( 1, m * ( 1 - k ) + k );
        result.b = 1 - Math.min( 1, y * ( 1 - k ) + k );

        result.r = Math.round( result.r * 255 );
        result.g = Math.round( result.g * 255 );
        result.b = Math.round( result.b * 255 );

        return result;
    },

    _RGBtoCMYK : function (RGB){
        var result = new CMYK(0, 0, 0, 0);

        r = RGB.r / 255;
        g = RGB.g / 255;
        b = RGB.b / 255;

        result.k = Math.min( 1 - r, 1 - g, 1 - b );
        result.c = ( 1 - r - result.k ) / ( 1 - result.k );
        result.m = ( 1 - g - result.k ) / ( 1 - result.k );
        result.y = ( 1 - b - result.k ) / ( 1 - result.k );

        result.c = Math.round( result.c * 100 );
        result.m = Math.round( result.m * 100 );
        result.y = Math.round( result.y * 100 );
        result.k = Math.round( result.k * 100 );

        return result;
    },

    toRGB : function (o) {
        if (o instanceof RGB) { return o; }
        if (o instanceof HSV) { return this._HSVtoRGB(o); }
        if (o instanceof CMYK) { return this._CMYKtoRGB(o); }
    },

    toHSV : function (o) {
        if (o instanceof HSV) { return o; }
        if (o instanceof RGB) { return this._RGBtoHSV(o); }
        if (o instanceof CMYK) { return this._RGBtoHSV(this._CMYKtoRGB(o)); }
    },

    toCMYK : function (o) {
        if (o instanceof CMYK) { return o; }
        if (o instanceof RGB) { return this._RGBtoCMYK(o); }
        if (o instanceof HSV) { return this._RGBtoCMYK(this._HSVtoRGB(o)); }
    }

}

for example , when I convert color rgb(8,56,213) which is outside the print gamut on the printer to cmyk:

console.log( ColorConverter.toCMYK(new RGB(8, 56, 213)) );

gives me this code: cmyk(96%,74%,0,16%)

now can a printer that prints with cmyk colors, prints all of cmyk colors? does it also prints cmyk(96%,74%,0,16%) ?? If so what is exact usage of ICC profiles ??

1

1 Answers

0
votes

What you need to understand is that color is perceptive, not objective. Imagine a red car, now imagine that same red car parked in a parking lot at night under a streetlight. That same car now looks orange. The color of the car has not changed, but the illumination has changed your perception of the color. This is why color viewed under controlled lighting.

The next bit of perception is reflectivity. Ink can never print true gold, because gold is more than just a color, it is reflective. Gold printed in CMYK looks like mustard.

The most important element of color matching is ink coverage. Print a band of color from 0%-100% and the strip should be a continuous blend from no color to full color. If the color becomes solid 80% of the way up the stipe, then the ink has 20% gain. if the color is white (doesn't print) for the first 20% that is 20% negative press gain. The stripe can do both, start 20% from zero and max out at 80% from full. This changes based on paper and dpi resolution.

Next print out cyan yellow and magenta stripes out on 2 different printers. They will look different. This is because dye, pigment and toner needs to be made out of stuff and stuff isn't perfect, even if the color stripes make a smooth transition from 0%-100%.

The icc color profile attempts to make sense of this confusion, where the monitor is matched to an expected standard, then the displayed color is matched to what should be expected to be seen on printed output. The camera and scanner also need their own profiles.

This gets especially tricky for flesh tones, where a little more red of a magenta ink will turn to a sun burn or a little too green of a cyan will make the person look sick. So printers have resorted to using light magenta and light cyan to have ink hues closer to flesh colors.

Even with good cyan, magenta and yellow ink, when mixed together they tend to make brown and not gray, so when equal amounts of CMY are observed, the equal amount is subtracted from the CMY and moved to the black (K) channel. The going back to the discussion of gold, 100% K isn't a rich black, so to make the black a rich black, some amount of CMY is printed, even with 100% black, which makes the black look blacker or perhaps a slight bit of a blue/black.

Clear as mud? A similar analogy is temperature. the RGB to CMYK conversion is like centigrade to Fahrenheit conversion. But the real answer to the question also includes if it is sunny or cloudy, how low in the sky is the sun, what is the humidity, what is the wind speed, are you standing on blacktop, grass or standing in water, how cold is the water... The icc profile gives the "realfeel" temperature, which attempts to take into account the other variable, but even so, stand under a tree on a sunny day and the real feel temperature will change quickly. So to get the real "realfeel", a profile needs to be created for each variable. The sun would need to be calibrated to a standard, just as the LCD monitor. The tree your standing under needs to be calibrated just like the camera used to take the original photo...

How accurate color needs to match is similar to how accurate temperature needs to be reported. For many things, close is good enough, but for flesh tones or body temperature, a 10% mistake can be fatal.