graypegg / chromatism

:rainbow: A simple set of utility functions for colours.
1.78k stars 37 forks source link

Constant time conversion path #24

Closed TehShrike closed 7 years ago

TehShrike commented 7 years ago

Using the getChain function, I generated a map of type strings that let you look up, for any from->to combination, the conversion steps that were between you and that type. I got this map:

{
    "rgb": {
        "cielch": [
            "XYZ",
            "cieluv"
        ],
        "hsluv": [
            "XYZ",
            "cieluv",
            "cielch"
        ]
    },
    "hex": {
        "csshsl": [
            "rgb",
            "hsl"
        ],
        "xyY": [
            "rgb",
            "XYZ"
        ],
        "lms": [
            "rgb",
            "XYZ"
        ],
        "cieluv": [
            "rgb",
            "XYZ"
        ],
        "cielch": [
            "rgb",
            "XYZ",
            "cieluv"
        ],
        "cielab": [
            "rgb",
            "XYZ"
        ],
        "hsluv": [
            "rgb",
            "XYZ",
            "cieluv",
            "cielch"
        ]
    },
    "hsl": {
        "xyY": [
            "rgb",
            "XYZ"
        ],
        "lms": [
            "rgb",
            "XYZ"
        ],
        "cieluv": [
            "rgb",
            "XYZ"
        ],
        "cielch": [
            "rgb",
            "XYZ",
            "cieluv"
        ],
        "cielab": [
            "rgb",
            "XYZ"
        ],
        "hsluv": [
            "rgb",
            "XYZ",
            "cieluv",
            "cielch"
        ]
    },
    "hsv": {
        "csshsl": [
            "rgb",
            "hsl"
        ],
        "xyY": [
            "rgb",
            "XYZ"
        ],
        "lms": [
            "rgb",
            "XYZ"
        ],
        "cieluv": [
            "rgb",
            "XYZ"
        ],
        "cielch": [
            "rgb",
            "XYZ",
            "cieluv"
        ],
        "cielab": [
            "rgb",
            "XYZ"
        ],
        "hsluv": [
            "rgb",
            "XYZ",
            "cieluv",
            "cielch"
        ]
    },
    "csshsl": {
        "hex": [
            "hsl",
            "rgb"
        ],
        "cssrgb": [
            "hsl",
            "rgb"
        ],
        "cmyk": [
            "hsl",
            "rgb"
        ],
        "XYZ": [
            "hsl",
            "rgb"
        ],
        "xyY": [
            "hsl",
            "rgb",
            "XYZ"
        ],
        "lms": [
            "hsl",
            "rgb",
            "XYZ"
        ],
        "cieluv": [
            "hsl",
            "rgb",
            "XYZ"
        ],
        "cielch": [
            "hsl",
            "rgb",
            "XYZ",
            "cieluv"
        ],
        "cielab": [
            "hsl",
            "rgb",
            "XYZ"
        ],
        "yiq": [
            "hsl",
            "rgb"
        ],
        "hsluv": [
            "hsl",
            "rgb",
            "XYZ",
            "cieluv",
            "cielch"
        ]
    },
    "cssrgb": {
        "csshsl": [
            "rgb",
            "hsl"
        ],
        "xyY": [
            "rgb",
            "XYZ"
        ],
        "lms": [
            "rgb",
            "XYZ"
        ],
        "cieluv": [
            "rgb",
            "XYZ"
        ],
        "cielch": [
            "rgb",
            "XYZ",
            "cieluv"
        ],
        "cielab": [
            "rgb",
            "XYZ"
        ],
        "hsluv": [
            "rgb",
            "XYZ",
            "cieluv",
            "cielch"
        ]
    },
    "cmyk": {
        "csshsl": [
            "rgb",
            "hsl"
        ],
        "xyY": [
            "rgb",
            "XYZ"
        ],
        "lms": [
            "rgb",
            "XYZ"
        ],
        "cieluv": [
            "rgb",
            "XYZ"
        ],
        "cielch": [
            "rgb",
            "XYZ",
            "cieluv"
        ],
        "cielab": [
            "rgb",
            "XYZ"
        ],
        "hsluv": [
            "rgb",
            "XYZ",
            "cieluv",
            "cielch"
        ]
    },
    "XYZ": {
        "csshsl": [
            "rgb",
            "hsl"
        ],
        "hsluv": [
            "cieluv",
            "cielch"
        ]
    },
    "xyY": {
        "hex": [
            "XYZ",
            "rgb"
        ],
        "hsl": [
            "XYZ",
            "rgb"
        ],
        "hsv": [
            "XYZ",
            "rgb"
        ],
        "csshsl": [
            "XYZ",
            "rgb",
            "hsl"
        ],
        "cssrgb": [
            "XYZ",
            "rgb"
        ],
        "cmyk": [
            "XYZ",
            "rgb"
        ],
        "cielch": [
            "XYZ",
            "cieluv"
        ],
        "yiq": [
            "XYZ",
            "rgb"
        ],
        "hsluv": [
            "XYZ",
            "cieluv",
            "cielch"
        ]
    },
    "lms": {
        "hex": [
            "XYZ",
            "rgb"
        ],
        "hsl": [
            "XYZ",
            "rgb"
        ],
        "hsv": [
            "XYZ",
            "rgb"
        ],
        "csshsl": [
            "XYZ",
            "rgb",
            "hsl"
        ],
        "cssrgb": [
            "XYZ",
            "rgb"
        ],
        "cmyk": [
            "XYZ",
            "rgb"
        ],
        "cielch": [
            "XYZ",
            "cieluv"
        ],
        "yiq": [
            "XYZ",
            "rgb"
        ],
        "hsluv": [
            "XYZ",
            "cieluv",
            "cielch"
        ]
    },
    "cieluv": {
        "hex": [
            "XYZ",
            "rgb"
        ],
        "hsl": [
            "XYZ",
            "rgb"
        ],
        "hsv": [
            "XYZ",
            "rgb"
        ],
        "csshsl": [
            "XYZ",
            "rgb",
            "hsl"
        ],
        "cssrgb": [
            "XYZ",
            "rgb"
        ],
        "cmyk": [
            "XYZ",
            "rgb"
        ],
        "yiq": [
            "XYZ",
            "rgb"
        ]
    },
    "cielch": {
        "rgb": [
            "cieluv",
            "XYZ"
        ],
        "hex": [
            "cieluv",
            "XYZ",
            "rgb"
        ],
        "hsl": [
            "cieluv",
            "XYZ",
            "rgb"
        ],
        "hsv": [
            "cieluv",
            "XYZ",
            "rgb"
        ],
        "csshsl": [
            "cieluv",
            "XYZ",
            "rgb",
            "hsl"
        ],
        "cssrgb": [
            "cieluv",
            "XYZ",
            "rgb"
        ],
        "cmyk": [
            "cieluv",
            "XYZ",
            "rgb"
        ],
        "xyY": [
            "cieluv",
            "XYZ"
        ],
        "lms": [
            "cieluv",
            "XYZ"
        ],
        "cielab": [
            "cieluv",
            "XYZ"
        ],
        "yiq": [
            "cieluv",
            "XYZ",
            "rgb"
        ]
    },
    "cielab": {
        "hex": [
            "XYZ",
            "rgb"
        ],
        "hsl": [
            "XYZ",
            "rgb"
        ],
        "hsv": [
            "XYZ",
            "rgb"
        ],
        "csshsl": [
            "XYZ",
            "rgb",
            "hsl"
        ],
        "cssrgb": [
            "XYZ",
            "rgb"
        ],
        "cmyk": [
            "XYZ",
            "rgb"
        ],
        "cielch": [
            "XYZ",
            "cieluv"
        ],
        "yiq": [
            "XYZ",
            "rgb"
        ],
        "hsluv": [
            "XYZ",
            "cieluv",
            "cielch"
        ]
    },
    "yiq": {
        "csshsl": [
            "rgb",
            "hsl"
        ],
        "xyY": [
            "rgb",
            "XYZ"
        ],
        "lms": [
            "rgb",
            "XYZ"
        ],
        "cieluv": [
            "rgb",
            "XYZ"
        ],
        "cielch": [
            "rgb",
            "XYZ",
            "cieluv"
        ],
        "cielab": [
            "rgb",
            "XYZ"
        ],
        "hsluv": [
            "rgb",
            "XYZ",
            "cieluv",
            "cielch"
        ]
    },
    "hsluv": {
        "rgb": [
            "cielch",
            "cieluv",
            "XYZ"
        ],
        "hex": [
            "cielch",
            "cieluv",
            "XYZ",
            "rgb"
        ],
        "hsl": [
            "cielch",
            "cieluv",
            "XYZ",
            "rgb"
        ],
        "hsv": [
            "cielch",
            "cieluv",
            "XYZ",
            "rgb"
        ],
        "csshsl": [
            "cielch",
            "cieluv",
            "XYZ",
            "rgb",
            "hsl"
        ],
        "cssrgb": [
            "cielch",
            "cieluv",
            "XYZ",
            "rgb"
        ],
        "cmyk": [
            "cielch",
            "cieluv",
            "XYZ",
            "rgb"
        ],
        "XYZ": [
            "cielch",
            "cieluv"
        ],
        "xyY": [
            "cielch",
            "cieluv",
            "XYZ"
        ],
        "lms": [
            "cielch",
            "cieluv",
            "XYZ"
        ],
        "cielab": [
            "cielch",
            "cieluv",
            "XYZ"
        ],
        "yiq": [
            "cielch",
            "cieluv",
            "XYZ",
            "rgb"
        ]
    }
}

Then I shortened each list of steps to only give you the next step (since you can look back at the map to figure out what to do after that). That gets you:

{
    "rgb": {
        "cielch": "XYZ",
        "hsluv": "XYZ"
    },
    "hex": {
        "csshsl": "rgb",
        "xyY": "rgb",
        "lms": "rgb",
        "cieluv": "rgb",
        "cielch": "rgb",
        "cielab": "rgb",
        "hsluv": "rgb"
    },
    "hsl": {
        "xyY": "rgb",
        "lms": "rgb",
        "cieluv": "rgb",
        "cielch": "rgb",
        "cielab": "rgb",
        "hsluv": "rgb"
    },
    "hsv": {
        "csshsl": "rgb",
        "xyY": "rgb",
        "lms": "rgb",
        "cieluv": "rgb",
        "cielch": "rgb",
        "cielab": "rgb",
        "hsluv": "rgb"
    },
    "csshsl": {
        "hex": "hsl",
        "cssrgb": "hsl",
        "cmyk": "hsl",
        "XYZ": "hsl",
        "xyY": "hsl",
        "lms": "hsl",
        "cieluv": "hsl",
        "cielch": "hsl",
        "cielab": "hsl",
        "yiq": "hsl",
        "hsluv": "hsl"
    },
    "cssrgb": {
        "csshsl": "rgb",
        "xyY": "rgb",
        "lms": "rgb",
        "cieluv": "rgb",
        "cielch": "rgb",
        "cielab": "rgb",
        "hsluv": "rgb"
    },
    "cmyk": {
        "csshsl": "rgb",
        "xyY": "rgb",
        "lms": "rgb",
        "cieluv": "rgb",
        "cielch": "rgb",
        "cielab": "rgb",
        "hsluv": "rgb"
    },
    "XYZ": {
        "csshsl": "rgb",
        "hsluv": "cieluv"
    },
    "xyY": {
        "hex": "XYZ",
        "hsl": "XYZ",
        "hsv": "XYZ",
        "csshsl": "XYZ",
        "cssrgb": "XYZ",
        "cmyk": "XYZ",
        "cielch": "XYZ",
        "yiq": "XYZ",
        "hsluv": "XYZ"
    },
    "lms": {
        "hex": "XYZ",
        "hsl": "XYZ",
        "hsv": "XYZ",
        "csshsl": "XYZ",
        "cssrgb": "XYZ",
        "cmyk": "XYZ",
        "cielch": "XYZ",
        "yiq": "XYZ",
        "hsluv": "XYZ"
    },
    "cieluv": {
        "hex": "XYZ",
        "hsl": "XYZ",
        "hsv": "XYZ",
        "csshsl": "XYZ",
        "cssrgb": "XYZ",
        "cmyk": "XYZ",
        "yiq": "XYZ"
    },
    "cielch": {
        "rgb": "cieluv",
        "hex": "cieluv",
        "hsl": "cieluv",
        "hsv": "cieluv",
        "csshsl": "cieluv",
        "cssrgb": "cieluv",
        "cmyk": "cieluv",
        "xyY": "cieluv",
        "lms": "cieluv",
        "cielab": "cieluv",
        "yiq": "cieluv"
    },
    "cielab": {
        "hex": "XYZ",
        "hsl": "XYZ",
        "hsv": "XYZ",
        "csshsl": "XYZ",
        "cssrgb": "XYZ",
        "cmyk": "XYZ",
        "cielch": "XYZ",
        "yiq": "XYZ",
        "hsluv": "XYZ"
    },
    "yiq": {
        "csshsl": "rgb",
        "xyY": "rgb",
        "lms": "rgb",
        "cieluv": "rgb",
        "cielch": "rgb",
        "cielab": "rgb",
        "hsluv": "rgb"
    },
    "hsluv": {
        "rgb": "cielch",
        "hex": "cielch",
        "hsl": "cielch",
        "hsv": "cielch",
        "csshsl": "cielch",
        "cssrgb": "cielch",
        "cmyk": "cielch",
        "XYZ": "cielch",
        "xyY": "cielch",
        "lms": "cielch",
        "cielab": "cielch",
        "yiq": "cielch"
    }
}

I shortened that data structure down to this slim version by hand.

Unlike the currently-committed conversion function, or my original breadth-first search implementation, It doesn't involve iterating over any arrays.

I merged #23 into this branch to pre-emptively fix the merge conflicts.

TehShrike commented 7 years ago

hmm. I'm not sure why this pull request passes on node 4, but #23 and https://github.com/toish/chromatism/commit/71f2c1c882b81cd70e8c585eeee259e00dc5d32d didn't pass when they ran.

graypegg commented 7 years ago

I've since made some tweaks to the current conversion chain, I'll make the tweaks! This should ease out the performance hit from my search a bit.

chain4

graypegg commented 7 years ago

Actually, just noticed you have RGB>HSV, would prefer RGB>HSL>HSV, as HSV is just a value-shifted version of HSL, so a lot of the code is the same. RGB>HSL>HSV would cut down on repeated code

TehShrike commented 7 years ago

I believe that the current implementation exactly matches the conversion steps that would have been taken before. That said, I have no problem implementing improvements in this pull request.

TehShrike commented 7 years ago

Made that change, saves a nice little bit of code. While making that change, I wondered: do we need both HSV->RGB and HSL->RGB conversion functions? Could we eliminate one of them?

graypegg commented 7 years ago

@TehShrike Nope, don't need those either! Other then the over head to switch modes, should be about the same speed to back the same way

TehShrike commented 7 years ago

hmm, if I try to eliminate either of those conversions, the output values change enough that the tests don't pass. I'll open up an issue for that, and leave that out of this pull request.

graypegg commented 7 years ago

Sorry for the absence on this! Working on the HSL/V => RGB conversion fix at the moment, merging this in to keep things organized