Comfy-Org / Comfy-Org/ComfyUI

Built-in data types: color (+swatch/picker)

Open
#9,531 0 comments 0 reactions 0 assignees View on GitHub
Feature
Dominant language
Python
Stars
133k
Forks
15.7k
Avg merge
1d 6h
Merged PRs (30d)
155

Description

Extracted from: https://github.com/comfyanonymous/ComfyUI/issues/8821

## Feature Idea

Probably, should be a child class of [vector[3/4]](https://github.com/comfyanonymous/ComfyUI/issues/9530), but with one important distinction:

Unlike "just" vectors, colors are expressed in some colorspace. And currently, there's no "default" colorspace across the board. There are two most used ones:
- **sRGB** - for actual distributed files (jpg, png, etc.)
- **Linear** - for "professional" file types (EXR) and for proper (physically correct) color processing _(all the professional CG software recommends a so-called "linear workflow", and some - like Nuke or Houdini - don't even give an option of processing images NOT in linear space)_.

Since comfyUI frontend is in a browser, each color needs to be shown in sRGB, but MANY image operations require it's linear representation. So I suggest a following implementation:
- A `Color` ABC, with three methods/properties: `linear()`, `srgb()` and `raw()`.
- The last one shows/sets the values as they're stored in the class and shown in the fields on the node.
- The other two always return the color in the corresponding space.
- `srgb()` is used for color display in frontend.
- Two concrete classes:
- `ColorLinear`: the actual values are expressed in linear space - so what we perceive as "visually 50% gray" would actually show `[0.214, 0.214, 0.214]` as values on the node. This type's `linear()` method is just an alias for `raw()`, and `srgb()` performs conversion on call.
- `ColorSRGB` - does the opposite: `srgb()` is an alias for `raw()`, `linear()` performs the inverse conversion. The same "visually 50% gray" chosen with this data type would have `[0.5, 0.5, 0.5]`.
- As a special case, there could be a third class - a child of sRGB one, storing colors as ints - to set colors as the 0-255 values, which is well-known to Photoshop users. In this case, the fourth method/property needs to be added to all of the color classes - the one simply multiplying the output of `srgb()` by 255, and converting to int. Its value for "visually 50% gray" would be `[127, 127, 127]` (actually, it should be `127.5`, but this one is int, right?)

This is the 50% gray I'm talking about:
Image

Ideally, there also should be a global option (in user settings) to switch the class used on all the nodes, along with a fourth class (smth like `ColorDefault`) for node authors to use in order to respect this setting.

### Widget

On the frontend, the widget needs to have 3 or 4 fields and a color swatch showing a picker window on click. The picker probably needs to be a custom one, since both Windows and Linux only let you choose an srgb-int color with their system-wide picker (there's simply no way to specify an HDR color: whether you need a "brighter than white" like `[5.0, 1.5, 0.75]` or a very dark (but not black) gray, like `0.0001`).

### Alpha

To save you from ambiguity, Alpha is always linear, regardless of what colorspace the rest of the color is expressed in. Blending one image with an alpha of 0.5 on top of another image does the perfect average of colors even in sRGB, doesn't it?
No gamma for alpha, even when PNG is saved.

## Existing Solutions

For a widget - https://github.com/Amorano/Jovimetrix

For a good example of color picker - here are screenshots from Maya, Nuke and Houdini:

Image Image

Image

Image

## Other

To save you from the headache of researching the right color-transforming function, here's the one used by most of the professional CG software:
```python
def linear_encode_to_srgb(x: float) -> float:
"""
This is the form used in ICC’s sRGB profile.
"""
if x <= 0.003130668442500634: # the most exact cutoff
return (323/25) * x # 12.92 exactly
else:
return (211/200) * (x ** (1/2.4)) - (11/200)

def linear_color_encode_to_srgb(clr: tuple[float, float, float]) -> tuple[float, float, float]: # same transform, component-wise
return tuple(linear_encode_to_srgb(x) for x in clr)
```

If in doubt, here: https://github.com/Lex-DRL/ColorSpace-Estimate/blob/main/conversion.json
- `a` = Linear
- `f_a` = sRGB

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.