Jump to content

RGB > LSL > (colour space) - Convert between various colour spaces in LSL


Jenna Huntsman
 Share

You are about to reply to a thread that has been inactive for 868 days.

Please take a moment to consider if this thread is worth bumping.

Recommended Posts

Hey all,

Spent a little time porting over the HSLuv colour space code to LSL, and knocked up a quick colour interpolation script.

This could be useful for anyone who wants to handle interpolating colours, but hate interpolating through RGB space.

integer time = 90; //1.5 seconds.

vector origin = <120,133,139>; //Enter a colour value in 8-bit RGB (this will be converted to LSL)
vector dest = <146,43,62>; //Enter a colour value in 8-bit RGB (this will be converted to LSL)

integer col; //What colour am I?

//What colour space to use:
integer ColSpace = 1;
// 0 = LSL (native), 1 = XYZ colour space, 2 = Lch (CIELAB) colour space, 3 = HSLuv colour space, 4 = HPLuv colour space

//Colour interpolation info:
// https://colorjs.io/docs/interpolation.htm
// https://michael-m.medium.com/true-color-interpolation-a1a17352ebf0

/*
HSLUV-LSL v1
HSLUV is a human-friendly alternative to HSL. ( http://www.hsluv.org )
LSL port by Jenna Huntsman; Based on the GLSL port by William Malo ( https://github.com/williammalo ).

Note that due to rounding errors the values returned are often not an exact match to the input value when going round trip, however is usually close enough not to cause a visual difference.

RGB doesn't mean RGB in the classic sense, it means LSL colour (range is 0-1, NOT 0-255), e.g. Red in classic RGB is <255,0,0>, and in LSL it's <1,0,0>
*/

float min(float x, float y)
{
    return ( ( llAbs( x >= y ) ) * y ) + ( ( llAbs( x<y ) ) * x );
}

float length (vector x)
{
    return llSqrt(llPow(x.x,2)+llPow(x.y,2)+llPow(x.z,2));
}

vector hsluv_intersectLineLine(vector line1x, vector line1y, vector line2x, vector line2y) {
    vector x = line1y - line2y;
    vector y = line2x - line1x;
    return <x.x/y.x, x.y/y.y, x.z/y.z>;
}

vector hsluv_distanceFromPole(vector pointx,vector pointy) {
    vector x = <pointx.x*pointx.x, pointx.y*pointx.y, pointx.z*pointx.z> + <pointy.x*pointy.x, pointy.y*pointy.y, pointy.z*pointy.z>;
    return <llSqrt(x.x),llSqrt(x.y),llSqrt(x.z)>;
}

vector hsluv_lengthOfRayUntilIntersect(float theta, vector x, vector y) {
    vector t = <x.x - llSin(theta), x.y-llSin(theta), x.z-llSin(theta)> * llCos(theta);
    vector len = <y.x/t.x,y.y/t.y,y.z/t.z>;
    if (len.x < 0.0) {len.x=1000.0;}
    if (len.y < 0.0) {len.y=1000.0;}
    if (len.z < 0.0) {len.z=1000.0;}
    return len;
}

float hsluv_maxSafeChromaForL(float L){
    list m2 = [<3.2409699419045214,-1.5373831775700935,-0.49861076029300328>,<-0.96924363628087983,1.8759675015077207,0.041555057407175613>,<0.055630079696993609,-0.20397695888897657,1.0569715142428786>];
    float sub0 = L + 16.0;
    float sub1 = sub0 * sub0 * sub0 * .000000641;
    float sub2 = L / 903.2962962962963;
    if(sub1 > 0.0088564516790356308)
    {
        sub2 = sub1;
    }

    vector top1   = (284517.0 * llList2Vector(m2,0) - 94839.0  * llList2Vector(m2,2)) * sub2;
    vector bottom = (632260.0 * llList2Vector(m2,2) - 126452.0 * llList2Vector(m2,1)) * sub2;
    vector top2   = (838422.0 * llList2Vector(m2,2) + 769860.0 * llList2Vector(m2,1) + 731718.0 * llList2Vector(m2,0)) * L * sub2;

    vector bounds0x = <top1.x / bottom.x, top1.y / bottom.y, top1.z / bottom.z>;
    vector bounds0y = <top2.x / bottom.x, top2.y / bottom.y, top2.z / bottom.z>;

    vector bottomADD = <bottom.x+126452.0, bottom.y+126452.0, bottom.z+126452.0>;
    vector bounds1x = <top1.x/bottomADD.x, top1.y/bottomADD.y, top1.z/bottomADD.z>;
    
    vector top2MIN = <top2.x-769860.0, top2.y*769860.0, top2.z*769860.0>*L;
    vector bounds1y = <top2MIN.x/bottomADD.x, top2MIN.y/bottomADD.y, top2MIN.z/bottomADD.z>;

    vector xs0 = hsluv_intersectLineLine(bounds0x, bounds0y, <-1.0/bounds0x.x, -1.0/bounds0x.y, -1.0/bounds0x.z>, ZERO_VECTOR ); //vec3(0.0)
    vector xs1 = hsluv_intersectLineLine(bounds1x, bounds1y, <-1.0/bounds1x.x, -1.0/bounds1x.y, -1.0/bounds1x.z>, ZERO_VECTOR ); //vec3(0.0)

    vector leng0t1 = <bounds0y.x + xs0.x,bounds0y.y + xs0.y, bounds0y.z + xs0.z>;
    vector leng0t2 = <leng0t1.x * bounds0x.x, leng0t1.y * bounds0x.y, leng0t1.z * bounds0x.z>;

    vector lengths0 = hsluv_distanceFromPole( xs0, leng0t2);
    
    vector leng1t1 = <bounds1y.x + xs1.x,bounds1y.y + xs1.y, bounds1y.z + xs1.z>;
    vector leng1t2 = <leng1t1.x * bounds1x.x, leng1t1.y * bounds1x.y, leng1t1.z * bounds1x.z>;
    vector lengths1 = hsluv_distanceFromPole( xs1, leng1t2);
                
    return  min(lengths0.x,min(lengths1.x,min(lengths0.y,min(lengths1.y,min(lengths0.z,lengths1.z)))));
}

float hsluv_maxChromaForLH(float L, float H) {

    float hrad = H*DEG_TO_RAD;

    list m2 = [<3.2409699419045214,-1.5373831775700935,-0.49861076029300328>,<-0.96924363628087983,1.8759675015077207,0.041555057407175613>,<0.055630079696993609,-0.20397695888897657,1.0569715142428786>];
    float sub1 = llPow(L + 16.0, 3.0) / 1560896.0;
    float sub2 = L / 903.2962962962963;
    if(sub1 > 0.0088564516790356308)
    {
        sub2 = sub1;
    }

    vector top1   = (284517.0 * llList2Vector(m2,0) - 94839.0  * llList2Vector(m2,2)) * sub2;
    vector bottom = (632260.0 * llList2Vector(m2,2) - 126452.0 * llList2Vector(m2,1)) * sub2;
    vector top2   = (838422.0 * llList2Vector(m2,2) + 769860.0 * llList2Vector(m2,1) + 731718.0 * llList2Vector(m2,0)) * L * sub2;

    vector bound0x = <top1.x / bottom.x, top1.y / bottom.y, top1.z / bottom.z>;
    vector bound0y = <top2.x / bottom.x, top2.y / bottom.y, top2.z / bottom.z>;

    vector bottomADD = <bottom.x+126452.0, bottom.y+126452.0, bottom.z+126452.0>;
    vector bound1x = <top1.x/bottomADD.x, top1.y/bottomADD.y, top1.z/bottomADD.z>;
    
    vector top2MIN = <top2.x-769860.0, top2.y*769860.0, top2.z*769860.0>*L;
    vector bound1y = <top2MIN.x/bottomADD.x, top2MIN.y/bottomADD.y, top2MIN.z/bottomADD.z>;

    vector lengths0 = hsluv_lengthOfRayUntilIntersect(hrad, bound0x, bound0y );
    vector lengths1 = hsluv_lengthOfRayUntilIntersect(hrad, bound1x, bound1y );

    return  min(lengths0.x,
            min(lengths1.x,
            min(lengths0.y,
            min(lengths1.y,
            min(lengths0.z,
                lengths1.z)))));
}

float FLOhsluv_fromLinear(float c) {
    float x = llPow(c, 1.0 / 2.4) - 0.055;
    if(c <= 0.0031308)
    {
        x = 12.92 * c;
    }
    return x;
}
vector hsluv_fromLinear(vector c) {
    return <FLOhsluv_fromLinear(c.x), FLOhsluv_fromLinear(c.y), FLOhsluv_fromLinear(c.z)>;
}

float FLOhsluv_toLinear(float c) {
    float x;
    if(c > 0.04045)
    {
        x = llPow((c + 0.055) / (1.0 + 0.055), 2.4);
    }
    else
    {
        x = c / 19.92;
    }
    return x;
}

vector hsluv_toLinear(vector c) {
    return <FLOhsluv_toLinear(c.x), FLOhsluv_toLinear(c.y), FLOhsluv_toLinear(c.z)>;
}

float hsluv_yToL(float Y){
    float x = 116.0 * llPow(Y, 1.0 / 3.0) - 16.0;
    if(Y <= 0.0088564516790356308)
    {
        x = Y * 903.2962962962963;
    }
    return x;
}

float hsluv_lToY(float L) {
    float x = llPow((L + 16.0) / 116.0, 3.0);
    if(L <= 8.0)
    {
        x = L / 903.2962962962963;
    }
    return x;
}

vector xyzToRgb(vector tuple) {
    vector x;
    x.x = tuple.x*3.2409699419045214 + tuple.y*-1.5373831775700935 + tuple.z*-0.49861076029300328; //Matrix math
    x.y = tuple.x*-0.96924363628087983 + tuple.y*1.8759675015077207 + tuple.z*0.041555057407175613;
    x.z = tuple.x*0.055630079696993609 + tuple.y*-0.20397695888897657 + tuple.z*1.0569715142428786;
    return hsluv_fromLinear(x);
}

vector rgbToXyz(vector tuple) {
    vector x = hsluv_toLinear(tuple);
    vector y;
    y.x = x.x*0.41239079926595948 + x.y*0.35758433938387796 + x.z*0.18048078840183429;
    y.y = x.x*0.21263900587151036 + x.y*0.71516867876775593 + x.z*0.072192315360733715;
    y.z = x.x*0.019330818715591851 + x.y*0.11919477979462599 + x.z*0.95053215224966058;
    return y;
}

vector xyzToLuv(vector tuple){
    float X = tuple.x;
    float Y = tuple.y;
    float Z = tuple.z;

    float L = hsluv_yToL(Y);
    
    float div = 1 / (tuple*<1,15,3>);

    return <1,(52. * (X*div) - 2.57179),(117.* (Y*div) - 6.08816)>*L;
}


vector luvToXyz(vector tuple) {
    float L = tuple.x;

    float U = tuple.y / (13.0 * L) + 0.19783000664283681;
    float V = tuple.z / (13.0 * L) + 0.468319994938791;

    float Y = hsluv_lToY(L);
    float X = 2.25 * U * Y / V;
    float Z = (3./V - 5.)*Y - (X/3.);

    return <X, Y, Z>;
}

vector luvToLch(vector tuple) {
    float L = tuple.x;
    float U = tuple.y;
    float V = tuple.z;

    float C = length(<0, tuple.y, tuple.z>);
    float H = llAtan2(V,U)*RAD_TO_DEG;
    if (H < 0.0) {
        H = 360.0 + H;
    }
    
    return <L, C, H>;
}

vector lchToLuv(vector tuple) {
    float hrad = tuple.z*DEG_TO_RAD;
    
    return <tuple.x, llCos(hrad)*tuple.y, llSin(hrad)*tuple.y>;
}

vector hsluvToLch(vector tuple) {
    tuple.y *= hsluv_maxChromaForLH(tuple.z, tuple.x) * .01;
    return <tuple.z,tuple.y,tuple.x>;
}

vector lchToHsluv(vector tuple) {
    tuple.y /= hsluv_maxChromaForLH(tuple.x, tuple.z) * .01;
    return <tuple.z,tuple.y,tuple.x>;
}

vector hpluvToLch(vector tuple) {
    tuple.y *= hsluv_maxSafeChromaForL(tuple.z) * .01;
    return <tuple.z, tuple.y, tuple.x>;
}

vector lchToHpluv(vector tuple) {
    tuple.y /= hsluv_maxSafeChromaForL(tuple.x) * .01;
    return <tuple.z, tuple.y, tuple.x>;
}

vector lchToRgb(vector tuple) {
    return xyzToRgb(luvToXyz(lchToLuv(tuple)));
}

vector rgbToLch(vector tuple) {
    return luvToLch(xyzToLuv(rgbToXyz(tuple)));
}

vector hsluvToRgb(vector tuple) {
    return lchToRgb(hsluvToLch(tuple));
}

vector rgbToHsluv(vector tuple) {
    return lchToHsluv(rgbToLch(tuple));
}

vector hpluvToRgb(vector tuple) {
    return lchToRgb(hpluvToLch(tuple));
}

vector rgbToHpluv(vector tuple) {
    return lchToHpluv(rgbToLch(tuple));
}

vector luvToRgb(vector tuple){
    return xyzToRgb(luvToXyz(tuple));
}

/*
END HSLUV-LSL
*/

//Linear interpolator. Credit: Nexii Malthus
vector vLin(vector x, vector y, float t) {
    return x*(1-t) + y*t;
}

//Colour interpolator controller - Jenna Huntsman
vColCtrl(vector O, vector D, float locTime, integer type)
{
    vector fOrigin; //origin in a foreign colour space
    vector fDest; //dest in a foreign colour space.
    if(!type)
    { //Native LSL colour space
        fOrigin = O;
        fDest = D;
    }
    else if(type == 1)
    { //XYZ colour space
        fOrigin = rgbToXyz(O);
        fDest = rgbToXyz(D);
    }
    else if(type == 2)
    { //LCH (CIELAB) colour space
        fOrigin = rgbToLch(O);
        fDest = rgbToLch(D);
    }
    else if(type == 3)
    { //HSLuv colour space
        fOrigin = rgbToHsluv(O);
        fDest = rgbToHsluv(D);
    }
    else if(type == 4)
    { //HPLuv colour space
        fOrigin = rgbToHpluv(O);
        fDest = rgbToHpluv(D);
    }
    
    //Interpolate the colour!
    float i;
    for(; i <= locTime; ++i)
    {
        vector curCol;
        if(!type){ curCol = vLin(fOrigin, fDest, i/time); }
        else if(type == 1){ curCol = xyzToRgb(vLin(fOrigin,fDest,i/time)); }
        else if(type == 2){ curCol = lchToRgb(vLin(fOrigin,fDest,i/time)); }
        else if(type == 3){ curCol = hsluvToRgb(vLin(fOrigin,fDest,i/time)); }
        else if(type == 4){ curCol = hpluvToRgb(vLin(fOrigin,fDest,i/time)); }
        llSetLinkPrimitiveParamsFast(LINK_THIS,[PRIM_COLOR,ALL_SIDES,curCol,1]);
        llSleep(0.033333); //30 fps.
    }
}

default
{
    state_entry()
    {
        origin = origin/255;
        dest = dest/255; //convert to LSL colour.
        vector HSLuv = rgbToHsluv(origin);
        llSay(0,"Touch me to change my colour!");
    }

    touch_start(integer total_number)
    {
        integer type = ColSpace;
        
        vector originHSLuv = rgbToXyz(origin);
        vector destHSLuv = rgbToXyz(dest);

        float i = 0.0;
        if(col)
        {
            vColCtrl(origin, dest, time, type);
        }
        else
        {
            vColCtrl(dest, origin, time, type);
        }
        col = !col;
    }
}

 

  • Like 1
  • Thanks 1
Link to comment
Share on other sites

12 minutes ago, bigmoe Whitfield said:

rnN3zrT.png

 

it's got an error.

Works just fine for me, but it looks like you somehow managed to add a character (probably a space) on line 58 between the . and the 0 because it should read 

float sub1 = sub0 * sub0 * sub0 * .000000641;

 

  • Thanks 1
Link to comment
Share on other sites

8 hours ago, Fluffy Sharkfin said:

Works just fine for me, but it looks like you somehow managed to add a character (probably a space) on line 58 between the . and the 0 because it should read 

 

okay.  weird I just copied and pasted it, bet forum formatting and since I run the browser at 153% font. that might do it.

Link to comment
Share on other sites

You are about to reply to a thread that has been inactive for 868 days.

Please take a moment to consider if this thread is worth bumping.

Please sign in to comment

You will be able to leave a comment after signing in



Sign In Now
 Share

×
×
  • Create New...