Merge pull request #76545 from JoNax97/gradient_color_spaces
Add Linear SRGB and OKLab color spaces to Gradient.
This commit is contained in:
commit
c97201babc
5 changed files with 142 additions and 20 deletions
|
@ -80,8 +80,12 @@
|
|||
<member name="colors" type="PackedColorArray" setter="set_colors" getter="get_colors" default="PackedColorArray(0, 0, 0, 1, 1, 1, 1, 1)">
|
||||
Gradient's colors returned as a [PackedColorArray].
|
||||
</member>
|
||||
<member name="interpolation_color_space" type="int" setter="set_interpolation_color_space" getter="get_interpolation_color_space" enum="Gradient.ColorSpace" default="0">
|
||||
The color space used to interpolate between points of the gradient. It does not affect the returned colors, which will always be in sRGB space. See [enum ColorSpace] for available modes.
|
||||
[b]Note:[/b] This setting has no effect when [member interpolation_mode] is set to [constant GRADIENT_INTERPOLATE_CONSTANT].
|
||||
</member>
|
||||
<member name="interpolation_mode" type="int" setter="set_interpolation_mode" getter="get_interpolation_mode" enum="Gradient.InterpolationMode" default="0">
|
||||
Defines how the colors between points of the gradient are interpolated. See [enum InterpolationMode] for available modes.
|
||||
The algorithm used to interpolate between points of the gradient. See [enum InterpolationMode] for available modes.
|
||||
</member>
|
||||
<member name="offsets" type="PackedFloat32Array" setter="set_offsets" getter="get_offsets" default="PackedFloat32Array(0, 1)">
|
||||
Gradient's offsets returned as a [PackedFloat32Array].
|
||||
|
@ -97,5 +101,14 @@
|
|||
<constant name="GRADIENT_INTERPOLATE_CUBIC" value="2" enum="InterpolationMode">
|
||||
Cubic interpolation.
|
||||
</constant>
|
||||
<constant name="GRADIENT_COLOR_SPACE_SRGB" value="0" enum="ColorSpace">
|
||||
sRGB color space.
|
||||
</constant>
|
||||
<constant name="GRADIENT_COLOR_SPACE_LINEAR_SRGB" value="1" enum="ColorSpace">
|
||||
Linear sRGB color space.
|
||||
</constant>
|
||||
<constant name="GRADIENT_COLOR_SPACE_OKLAB" value="2" enum="ColorSpace">
|
||||
[url=https://bottosson.github.io/posts/oklab/]Oklab[/url] color space. This color space provides a smooth and uniform-looking transition between colors.
|
||||
</constant>
|
||||
</constants>
|
||||
</class>
|
||||
|
|
|
@ -41,6 +41,7 @@ void GradientEditor::set_gradient(const Ref<Gradient> &p_gradient) {
|
|||
gradient->connect("changed", callable_mp(this, &GradientEditor::_gradient_changed));
|
||||
set_points(gradient->get_points());
|
||||
set_interpolation_mode(gradient->get_interpolation_mode());
|
||||
set_interpolation_color_space(gradient->get_interpolation_color_space());
|
||||
}
|
||||
|
||||
void GradientEditor::reverse_gradient() {
|
||||
|
@ -93,6 +94,7 @@ void GradientEditor::_gradient_changed() {
|
|||
Vector<Gradient::Point> grad_points = gradient->get_points();
|
||||
set_points(grad_points);
|
||||
set_interpolation_mode(gradient->get_interpolation_mode());
|
||||
set_interpolation_color_space(gradient->get_interpolation_color_space());
|
||||
queue_redraw();
|
||||
editing = false;
|
||||
}
|
||||
|
@ -104,9 +106,11 @@ void GradientEditor::_ramp_changed() {
|
|||
undo_redo->add_do_method(gradient.ptr(), "set_offsets", get_offsets());
|
||||
undo_redo->add_do_method(gradient.ptr(), "set_colors", get_colors());
|
||||
undo_redo->add_do_method(gradient.ptr(), "set_interpolation_mode", get_interpolation_mode());
|
||||
undo_redo->add_do_method(gradient.ptr(), "set_interpolation_color_space", get_interpolation_color_space());
|
||||
undo_redo->add_undo_method(gradient.ptr(), "set_offsets", gradient->get_offsets());
|
||||
undo_redo->add_undo_method(gradient.ptr(), "set_colors", gradient->get_colors());
|
||||
undo_redo->add_undo_method(gradient.ptr(), "set_interpolation_mode", gradient->get_interpolation_mode());
|
||||
undo_redo->add_undo_method(gradient.ptr(), "set_interpolation_color_space", gradient->get_interpolation_color_space());
|
||||
undo_redo->commit_action();
|
||||
editing = false;
|
||||
}
|
||||
|
@ -171,6 +175,14 @@ Gradient::InterpolationMode GradientEditor::get_interpolation_mode() {
|
|||
return interpolation_mode;
|
||||
}
|
||||
|
||||
void GradientEditor::set_interpolation_color_space(Gradient::ColorSpace p_color_space) {
|
||||
interpolation_color_space = p_color_space;
|
||||
}
|
||||
|
||||
Gradient::ColorSpace GradientEditor::get_interpolation_color_space() {
|
||||
return interpolation_color_space;
|
||||
}
|
||||
|
||||
ColorPicker *GradientEditor::get_picker() {
|
||||
return picker;
|
||||
}
|
||||
|
@ -385,6 +397,7 @@ void GradientEditor::_notification(int p_what) {
|
|||
// Draw color ramp.
|
||||
gradient_cache->set_points(points);
|
||||
gradient_cache->set_interpolation_mode(interpolation_mode);
|
||||
gradient_cache->set_interpolation_color_space(interpolation_color_space);
|
||||
preview_texture->set_gradient(gradient_cache);
|
||||
draw_texture_rect(preview_texture, Rect2(handle_width / 2, 0, total_w, h));
|
||||
|
||||
|
|
|
@ -45,6 +45,7 @@ class GradientEditor : public Control {
|
|||
int grabbed = -1;
|
||||
Vector<Gradient::Point> points;
|
||||
Gradient::InterpolationMode interpolation_mode = Gradient::GRADIENT_INTERPOLATE_LINEAR;
|
||||
Gradient::ColorSpace interpolation_color_space = Gradient::GRADIENT_COLOR_SPACE_SRGB;
|
||||
|
||||
bool editing = false;
|
||||
Ref<Gradient> gradient;
|
||||
|
@ -84,6 +85,9 @@ public:
|
|||
void set_interpolation_mode(Gradient::InterpolationMode p_interp_mode);
|
||||
Gradient::InterpolationMode get_interpolation_mode();
|
||||
|
||||
void set_interpolation_color_space(Gradient::ColorSpace p_color_space);
|
||||
Gradient::ColorSpace get_interpolation_color_space();
|
||||
|
||||
ColorPicker *get_picker();
|
||||
PopupPanel *get_popup();
|
||||
|
||||
|
|
|
@ -69,7 +69,12 @@ void Gradient::_bind_methods() {
|
|||
ClassDB::bind_method(D_METHOD("set_interpolation_mode", "interpolation_mode"), &Gradient::set_interpolation_mode);
|
||||
ClassDB::bind_method(D_METHOD("get_interpolation_mode"), &Gradient::get_interpolation_mode);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_interpolation_color_space", "interpolation_color_space"), &Gradient::set_interpolation_color_space);
|
||||
ClassDB::bind_method(D_METHOD("get_interpolation_color_space"), &Gradient::get_interpolation_color_space);
|
||||
|
||||
ADD_GROUP("Interpolation", "interpolation_");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "interpolation_mode", PROPERTY_HINT_ENUM, "Linear,Constant,Cubic"), "set_interpolation_mode", "get_interpolation_mode");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "interpolation_color_space", PROPERTY_HINT_ENUM, "sRGB,Linear sRGB,Oklab"), "set_interpolation_color_space", "get_interpolation_color_space");
|
||||
|
||||
ADD_GROUP("Raw Data", "");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::PACKED_FLOAT32_ARRAY, "offsets"), "set_offsets", "get_offsets");
|
||||
|
@ -78,6 +83,16 @@ void Gradient::_bind_methods() {
|
|||
BIND_ENUM_CONSTANT(GRADIENT_INTERPOLATE_LINEAR);
|
||||
BIND_ENUM_CONSTANT(GRADIENT_INTERPOLATE_CONSTANT);
|
||||
BIND_ENUM_CONSTANT(GRADIENT_INTERPOLATE_CUBIC);
|
||||
|
||||
BIND_ENUM_CONSTANT(GRADIENT_COLOR_SPACE_SRGB);
|
||||
BIND_ENUM_CONSTANT(GRADIENT_COLOR_SPACE_LINEAR_SRGB);
|
||||
BIND_ENUM_CONSTANT(GRADIENT_COLOR_SPACE_OKLAB);
|
||||
}
|
||||
|
||||
void Gradient::_validate_property(PropertyInfo &p_property) const {
|
||||
if (p_property.name == "interpolation_color_space" && interpolation_mode == GRADIENT_INTERPOLATE_CONSTANT) {
|
||||
p_property.usage = PROPERTY_USAGE_NO_EDITOR;
|
||||
}
|
||||
}
|
||||
|
||||
Vector<float> Gradient::get_offsets() const {
|
||||
|
@ -101,12 +116,22 @@ Vector<Color> Gradient::get_colors() const {
|
|||
void Gradient::set_interpolation_mode(Gradient::InterpolationMode p_interp_mode) {
|
||||
interpolation_mode = p_interp_mode;
|
||||
emit_signal(CoreStringNames::get_singleton()->changed);
|
||||
notify_property_list_changed();
|
||||
}
|
||||
|
||||
Gradient::InterpolationMode Gradient::get_interpolation_mode() {
|
||||
return interpolation_mode;
|
||||
}
|
||||
|
||||
void Gradient::set_interpolation_color_space(Gradient::ColorSpace p_color_space) {
|
||||
interpolation_color_space = p_color_space;
|
||||
emit_signal(CoreStringNames::get_singleton()->changed);
|
||||
}
|
||||
|
||||
Gradient::ColorSpace Gradient::get_interpolation_color_space() {
|
||||
return interpolation_color_space;
|
||||
}
|
||||
|
||||
void Gradient::set_offsets(const Vector<float> &p_offsets) {
|
||||
points.resize(p_offsets.size());
|
||||
for (int i = 0; i < points.size(); i++) {
|
||||
|
|
|
@ -33,6 +33,8 @@
|
|||
|
||||
#include "core/io/resource.h"
|
||||
|
||||
#include "thirdparty/misc/ok_color.h"
|
||||
|
||||
class Gradient : public Resource {
|
||||
GDCLASS(Gradient, Resource);
|
||||
OBJ_SAVE_TYPE(Gradient);
|
||||
|
@ -44,6 +46,12 @@ public:
|
|||
GRADIENT_INTERPOLATE_CUBIC,
|
||||
};
|
||||
|
||||
enum ColorSpace {
|
||||
GRADIENT_COLOR_SPACE_SRGB,
|
||||
GRADIENT_COLOR_SPACE_LINEAR_SRGB,
|
||||
GRADIENT_COLOR_SPACE_OKLAB,
|
||||
};
|
||||
|
||||
struct Point {
|
||||
float offset = 0.0;
|
||||
Color color;
|
||||
|
@ -56,6 +64,7 @@ private:
|
|||
Vector<Point> points;
|
||||
bool is_sorted = true;
|
||||
InterpolationMode interpolation_mode = GRADIENT_INTERPOLATE_LINEAR;
|
||||
ColorSpace interpolation_color_space = GRADIENT_COLOR_SPACE_SRGB;
|
||||
|
||||
_FORCE_INLINE_ void _update_sorting() {
|
||||
if (!is_sorted) {
|
||||
|
@ -64,8 +73,55 @@ private:
|
|||
}
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ Color transform_color_space(const Color p_color) const {
|
||||
switch (interpolation_color_space) {
|
||||
case GRADIENT_COLOR_SPACE_SRGB:
|
||||
default:
|
||||
return p_color;
|
||||
|
||||
case GRADIENT_COLOR_SPACE_LINEAR_SRGB:
|
||||
return p_color.srgb_to_linear();
|
||||
|
||||
case GRADIENT_COLOR_SPACE_OKLAB:
|
||||
Color linear_color = p_color.srgb_to_linear();
|
||||
ok_color::RGB rgb{};
|
||||
rgb.r = linear_color.r;
|
||||
rgb.g = linear_color.g;
|
||||
rgb.b = linear_color.b;
|
||||
|
||||
ok_color ok_color;
|
||||
ok_color::Lab lab_color = ok_color.linear_srgb_to_oklab(rgb);
|
||||
|
||||
// Constructs an RGB color using the Lab values directly. This allows reusing the interpolation code.
|
||||
return { lab_color.L, lab_color.a, lab_color.b, linear_color.a };
|
||||
}
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ Color inv_transform_color_space(const Color p_color) const {
|
||||
switch (interpolation_color_space) {
|
||||
case GRADIENT_COLOR_SPACE_SRGB:
|
||||
default:
|
||||
return p_color;
|
||||
|
||||
case GRADIENT_COLOR_SPACE_LINEAR_SRGB:
|
||||
return p_color.linear_to_srgb();
|
||||
|
||||
case GRADIENT_COLOR_SPACE_OKLAB:
|
||||
ok_color::Lab lab{};
|
||||
lab.L = p_color.r;
|
||||
lab.a = p_color.g;
|
||||
lab.b = p_color.b;
|
||||
|
||||
ok_color new_ok_color;
|
||||
ok_color::RGB ok_rgb = new_ok_color.oklab_to_linear_srgb(lab);
|
||||
Color linear{ ok_rgb.r, ok_rgb.g, ok_rgb.b, p_color.a };
|
||||
return linear.linear_to_srgb();
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
static void _bind_methods();
|
||||
void _validate_property(PropertyInfo &p_property) const;
|
||||
|
||||
public:
|
||||
Gradient();
|
||||
|
@ -92,6 +148,9 @@ public:
|
|||
void set_interpolation_mode(InterpolationMode p_interp_mode);
|
||||
InterpolationMode get_interpolation_mode();
|
||||
|
||||
void set_interpolation_color_space(Gradient::ColorSpace p_color_space);
|
||||
ColorSpace get_interpolation_color_space();
|
||||
|
||||
_FORCE_INLINE_ Color get_color_at_offset(float p_offset) {
|
||||
if (points.is_empty()) {
|
||||
return Color(0, 0, 0, 1);
|
||||
|
@ -134,16 +193,22 @@ public:
|
|||
if (first < 0) {
|
||||
return points[0].color;
|
||||
}
|
||||
const Point &pointFirst = points[first];
|
||||
const Point &pointSecond = points[second];
|
||||
const Point &point1 = points[first];
|
||||
const Point &point2 = points[second];
|
||||
float weight = (p_offset - point1.offset) / (point2.offset - point1.offset);
|
||||
|
||||
switch (interpolation_mode) {
|
||||
case GRADIENT_INTERPOLATE_LINEAR: {
|
||||
return pointFirst.color.lerp(pointSecond.color, (p_offset - pointFirst.offset) / (pointSecond.offset - pointFirst.offset));
|
||||
} break;
|
||||
case GRADIENT_INTERPOLATE_CONSTANT: {
|
||||
return pointFirst.color;
|
||||
} break;
|
||||
return point1.color;
|
||||
}
|
||||
case GRADIENT_INTERPOLATE_LINEAR:
|
||||
default: { // Fallback to linear interpolation.
|
||||
Color color1 = transform_color_space(point1.color);
|
||||
Color color2 = transform_color_space(point2.color);
|
||||
|
||||
Color interpolated = color1.lerp(color2, weight);
|
||||
return inv_transform_color_space(interpolated);
|
||||
}
|
||||
case GRADIENT_INTERPOLATE_CUBIC: {
|
||||
int p0 = first - 1;
|
||||
int p3 = second + 1;
|
||||
|
@ -153,20 +218,21 @@ public:
|
|||
if (p0 < 0) {
|
||||
p0 = first;
|
||||
}
|
||||
const Point &pointP0 = points[p0];
|
||||
const Point &pointP3 = points[p3];
|
||||
const Point &point0 = points[p0];
|
||||
const Point &point3 = points[p3];
|
||||
|
||||
float x = (p_offset - pointFirst.offset) / (pointSecond.offset - pointFirst.offset);
|
||||
float r = Math::cubic_interpolate(pointFirst.color.r, pointSecond.color.r, pointP0.color.r, pointP3.color.r, x);
|
||||
float g = Math::cubic_interpolate(pointFirst.color.g, pointSecond.color.g, pointP0.color.g, pointP3.color.g, x);
|
||||
float b = Math::cubic_interpolate(pointFirst.color.b, pointSecond.color.b, pointP0.color.b, pointP3.color.b, x);
|
||||
float a = Math::cubic_interpolate(pointFirst.color.a, pointSecond.color.a, pointP0.color.a, pointP3.color.a, x);
|
||||
Color color0 = transform_color_space(point0.color);
|
||||
Color color1 = transform_color_space(point1.color);
|
||||
Color color2 = transform_color_space(point2.color);
|
||||
Color color3 = transform_color_space(point3.color);
|
||||
|
||||
return Color(r, g, b, a);
|
||||
} break;
|
||||
default: {
|
||||
// Fallback to linear interpolation.
|
||||
return pointFirst.color.lerp(pointSecond.color, (p_offset - pointFirst.offset) / (pointSecond.offset - pointFirst.offset));
|
||||
Color interpolated;
|
||||
interpolated[0] = Math::cubic_interpolate(color1[0], color2[0], color0[0], color3[0], weight);
|
||||
interpolated[1] = Math::cubic_interpolate(color1[1], color2[1], color0[1], color3[1], weight);
|
||||
interpolated[2] = Math::cubic_interpolate(color1[2], color2[2], color0[2], color3[2], weight);
|
||||
interpolated[3] = Math::cubic_interpolate(color1[3], color2[3], color0[3], color3[3], weight);
|
||||
|
||||
return inv_transform_color_space(interpolated);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -175,5 +241,6 @@ public:
|
|||
};
|
||||
|
||||
VARIANT_ENUM_CAST(Gradient::InterpolationMode);
|
||||
VARIANT_ENUM_CAST(Gradient::ColorSpace);
|
||||
|
||||
#endif // GRADIENT_H
|
||||
|
|
Loading…
Reference in a new issue