From 358d22b61dd62e523b750017c7755facf4b92b9f Mon Sep 17 00:00:00 2001 From: Terry Jia Date: Mon, 30 Mar 2026 08:27:23 -0400 Subject: [PATCH] update color curve blueprint using image histogram node --- blueprints/Color Curves.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/blueprints/Color Curves.json b/blueprints/Color Curves.json index 933c53978..f1a383942 100644 --- a/blueprints/Color Curves.json +++ b/blueprints/Color Curves.json @@ -1 +1 @@ -{"revision": 0,"last_node_id": 10,"last_link_id": 0,"nodes": [{"id": 10,"type": "d5c462c8-1372-4af8-84f2-547c83470d04","pos": [3610,-2630],"size": [270,420],"flags": {},"order": 0,"mode": 0,"inputs": [{"label": "image","localized_name": "images.image0","name": "images.image0","type": "IMAGE","link": null}],"outputs": [{"label": "IMAGE","localized_name": "IMAGE0","name": "IMAGE0","type": "IMAGE","links": []}],"properties": {"proxyWidgets": [["4","curve"],["5","curve"],["6","curve"],["7","curve"]]},"widgets_values": [],"title": "Color Curves"}],"links": [],"version": 0.4,"definitions": {"subgraphs": [{"id": "d5c462c8-1372-4af8-84f2-547c83470d04","version": 1,"state": {"lastGroupId": 0,"lastNodeId": 8,"lastLinkId": 33,"lastRerouteId": 0},"revision": 0,"config": {},"name": "Color Curves","inputNode": {"id": -10,"bounding": [2660,-4500,120,60]},"outputNode": {"id": -20,"bounding": [4270,-4500,120,60]},"inputs": [{"id": "abc345b7-f55e-4f32-a11d-3aa4c2b0936b","name": "images.image0","type": "IMAGE","linkIds": [29],"localized_name": "images.image0","label": "image","pos": [2760,-4480]}],"outputs": [{"id": "eb0ec079-46da-4408-8263-9ef85569d33d","name": "IMAGE0","type": "IMAGE","linkIds": [28],"localized_name": "IMAGE0","label": "IMAGE","pos": [4290,-4480]}],"widgets": [],"nodes": [{"id": 4,"type": "CurveEditor","pos": [3060,-4500],"size": [270,200],"flags": {},"order": 0,"mode": 0,"inputs": [{"label": "curve","localized_name": "curve","name": "curve","type": "CURVE","widget": {"name": "curve"},"link": null},{"label": "histogram","localized_name": "histogram","name": "histogram","type": "HISTOGRAM","shape": 7,"link": null}],"outputs": [{"localized_name": "CURVE","name": "CURVE","type": "CURVE","links": [30]}],"title": "RGB Master","properties": {"Node name for S&R": "CurveEditor"},"widgets_values": []},{"id": 5,"type": "CurveEditor","pos": [3060,-4250],"size": [270,200],"flags": {},"order": 1,"mode": 0,"inputs": [{"label": "curve","localized_name": "curve","name": "curve","type": "CURVE","widget": {"name": "curve"},"link": null},{"label": "histogram","localized_name": "histogram","name": "histogram","type": "HISTOGRAM","shape": 7,"link": null}],"outputs": [{"localized_name": "CURVE","name": "CURVE","type": "CURVE","links": [31]}],"title": "Red","properties": {"Node name for S&R": "CurveEditor"},"widgets_values": []},{"id": 6,"type": "CurveEditor","pos": [3060,-4000],"size": [270,200],"flags": {},"order": 2,"mode": 0,"inputs": [{"label": "curve","localized_name": "curve","name": "curve","type": "CURVE","widget": {"name": "curve"},"link": null},{"label": "histogram","localized_name": "histogram","name": "histogram","type": "HISTOGRAM","shape": 7,"link": null}],"outputs": [{"localized_name": "CURVE","name": "CURVE","type": "CURVE","links": [32]}],"title": "Green","properties": {"Node name for S&R": "CurveEditor"},"widgets_values": []},{"id": 7,"type": "CurveEditor","pos": [3060,-3750],"size": [270,200],"flags": {},"order": 3,"mode": 0,"inputs": [{"label": "curve","localized_name": "curve","name": "curve","type": "CURVE","widget": {"name": "curve"},"link": null},{"label": "histogram","localized_name": "histogram","name": "histogram","type": "HISTOGRAM","shape": 7,"link": null}],"outputs": [{"localized_name": "CURVE","name": "CURVE","type": "CURVE","links": [33]}],"title": "Blue","properties": {"Node name for S&R": "CurveEditor"},"widgets_values": []},{"id": 8,"type": "GLSLShader","pos": [3590,-4500],"size": [420,500],"flags": {},"order": 4,"mode": 0,"inputs": [{"label": "image0","localized_name": "images.image0","name": "images.image0","type": "IMAGE","link": 29},{"label": "image1","localized_name": "images.image1","name": "images.image1","shape": 7,"type": "IMAGE","link": null},{"label": "u_curve0","localized_name": "curves.u_curve0","name": "curves.u_curve0","shape": 7,"type": "CURVE","link": 30},{"label": "u_curve1","localized_name": "curves.u_curve1","name": "curves.u_curve1","shape": 7,"type": "CURVE","link": 31},{"label": "u_curve2","localized_name": "curves.u_curve2","name": "curves.u_curve2","shape": 7,"type": "CURVE","link": 32},{"label": "u_curve3","localized_name": "curves.u_curve3","name": "curves.u_curve3","shape": 7,"type": "CURVE","link": 33},{"localized_name": "fragment_shader","name": "fragment_shader","type": "STRING","widget": {"name": "fragment_shader"},"link": null},{"localized_name": "size_mode","name": "size_mode","type": "COMFY_DYNAMICCOMBO_V3","widget": {"name": "size_mode"},"link": null}],"outputs": [{"localized_name": "IMAGE0","name": "IMAGE0","type": "IMAGE","links": [28]},{"localized_name": "IMAGE1","name": "IMAGE1","type": "IMAGE","links": null},{"localized_name": "IMAGE2","name": "IMAGE2","type": "IMAGE","links": null},{"localized_name": "IMAGE3","name": "IMAGE3","type": "IMAGE","links": null}],"properties": {"Node name for S&R": "GLSLShader"},"widgets_values": ["#version 300 es\nprecision highp float;\n\nuniform sampler2D u_image0;\nuniform sampler2D u_curve0; // RGB master curve (256x1 LUT)\nuniform sampler2D u_curve1; // Red channel curve\nuniform sampler2D u_curve2; // Green channel curve\nuniform sampler2D u_curve3; // Blue channel curve\n\nin vec2 v_texCoord;\nlayout(location = 0) out vec4 fragColor0;\n\n// GIMP-compatible curve lookup with manual linear interpolation.\n// Matches gimp_curve_map_value_inline() from gimpcurve-map.c:\n// index = value * (n_samples - 1)\n// f = fract(index)\n// result = (1-f) * samples[floor] + f * samples[ceil]\n//\n// Uses texelFetch (NEAREST) to avoid GPU half-texel offset issues\n// that occur with texture() + GL_LINEAR on small 256x1 LUTs.\nfloat applyCurve(sampler2D curve, float value) {\n value = clamp(value, 0.0, 1.0);\n\n float pos = value * 255.0;\n int lo = int(floor(pos));\n int hi = min(lo + 1, 255);\n float f = pos - float(lo);\n\n float a = texelFetch(curve, ivec2(lo, 0), 0).r;\n float b = texelFetch(curve, ivec2(hi, 0), 0).r;\n\n return a + f * (b - a);\n}\n\nvoid main() {\n vec4 color = texture(u_image0, v_texCoord);\n\n // GIMP order: per-channel curves first, then RGB master curve.\n // See gimp_curve_map_pixels() default case in gimpcurve-map.c:\n // dest = colors_curve( channel_curve( src ) )\n float tmp_r = applyCurve(u_curve1, color.r);\n float tmp_g = applyCurve(u_curve2, color.g);\n float tmp_b = applyCurve(u_curve3, color.b);\n color.r = applyCurve(u_curve0, tmp_r);\n color.g = applyCurve(u_curve0, tmp_g);\n color.b = applyCurve(u_curve0, tmp_b);\n\n fragColor0 = vec4(color.rgb, color.a);\n}\n","from_input"]}],"groups": [],"links": [{"id": 29,"origin_id": -10,"origin_slot": 0,"target_id": 8,"target_slot": 0,"type": "IMAGE"},{"id": 28,"origin_id": 8,"origin_slot": 0,"target_id": -20,"target_slot": 0,"type": "IMAGE"},{"id": 30,"origin_id": 4,"origin_slot": 0,"target_id": 8,"target_slot": 2,"type": "CURVE"},{"id": 31,"origin_id": 5,"origin_slot": 0,"target_id": 8,"target_slot": 3,"type": "CURVE"},{"id": 32,"origin_id": 6,"origin_slot": 0,"target_id": 8,"target_slot": 4,"type": "CURVE"},{"id": 33,"origin_id": 7,"origin_slot": 0,"target_id": 8,"target_slot": 5,"type": "CURVE"}],"extra": {"workflowRendererVersion": "LG"},"category": "Image Tools/Color adjust"}]}} \ No newline at end of file +{"revision": 0, "last_node_id": 10, "last_link_id": 0, "nodes": [{"id": 10, "type": "d5c462c8-1372-4af8-84f2-547c83470d04", "pos": [3610, -2630], "size": [270, 420], "flags": {}, "order": 0, "mode": 0, "inputs": [{"label": "image", "localized_name": "images.image0", "name": "images.image0", "type": "IMAGE", "link": null}], "outputs": [{"label": "IMAGE", "localized_name": "IMAGE0", "name": "IMAGE0", "type": "IMAGE", "links": []}], "properties": {"proxyWidgets": [["4", "curve"], ["5", "curve"], ["6", "curve"], ["7", "curve"]]}, "widgets_values": [], "title": "Color Curves"}], "links": [], "version": 0.4, "definitions": {"subgraphs": [{"id": "d5c462c8-1372-4af8-84f2-547c83470d04", "version": 1, "state": {"lastGroupId": 0, "lastNodeId": 9, "lastLinkId": 38, "lastRerouteId": 0}, "revision": 0, "config": {}, "name": "Color Curves", "inputNode": {"id": -10, "bounding": [2660, -4500, 120, 60]}, "outputNode": {"id": -20, "bounding": [4270, -4500, 120, 60]}, "inputs": [{"id": "abc345b7-f55e-4f32-a11d-3aa4c2b0936b", "name": "images.image0", "type": "IMAGE", "linkIds": [29, 34], "localized_name": "images.image0", "label": "image", "pos": [2760, -4480]}], "outputs": [{"id": "eb0ec079-46da-4408-8263-9ef85569d33d", "name": "IMAGE0", "type": "IMAGE", "linkIds": [28], "localized_name": "IMAGE0", "label": "IMAGE", "pos": [4290, -4480]}], "widgets": [], "nodes": [{"id": 4, "type": "CurveEditor", "pos": [3060, -4500], "size": [270, 200], "flags": {}, "order": 0, "mode": 0, "inputs": [{"label": "curve", "localized_name": "curve", "name": "curve", "type": "CURVE", "widget": {"name": "curve"}, "link": null}, {"label": "histogram", "localized_name": "histogram", "name": "histogram", "type": "HISTOGRAM", "shape": 7, "link": 35}], "outputs": [{"localized_name": "CURVE", "name": "CURVE", "type": "CURVE", "links": [30]}], "title": "RGB Master", "properties": {"Node name for S&R": "CurveEditor"}, "widgets_values": []}, {"id": 5, "type": "CurveEditor", "pos": [3060, -4250], "size": [270, 200], "flags": {}, "order": 1, "mode": 0, "inputs": [{"label": "curve", "localized_name": "curve", "name": "curve", "type": "CURVE", "widget": {"name": "curve"}, "link": null}, {"label": "histogram", "localized_name": "histogram", "name": "histogram", "type": "HISTOGRAM", "shape": 7, "link": 36}], "outputs": [{"localized_name": "CURVE", "name": "CURVE", "type": "CURVE", "links": [31]}], "title": "Red", "properties": {"Node name for S&R": "CurveEditor"}, "widgets_values": []}, {"id": 6, "type": "CurveEditor", "pos": [3060, -4000], "size": [270, 200], "flags": {}, "order": 2, "mode": 0, "inputs": [{"label": "curve", "localized_name": "curve", "name": "curve", "type": "CURVE", "widget": {"name": "curve"}, "link": null}, {"label": "histogram", "localized_name": "histogram", "name": "histogram", "type": "HISTOGRAM", "shape": 7, "link": 37}], "outputs": [{"localized_name": "CURVE", "name": "CURVE", "type": "CURVE", "links": [32]}], "title": "Green", "properties": {"Node name for S&R": "CurveEditor"}, "widgets_values": []}, {"id": 7, "type": "CurveEditor", "pos": [3060, -3750], "size": [270, 200], "flags": {}, "order": 3, "mode": 0, "inputs": [{"label": "curve", "localized_name": "curve", "name": "curve", "type": "CURVE", "widget": {"name": "curve"}, "link": null}, {"label": "histogram", "localized_name": "histogram", "name": "histogram", "type": "HISTOGRAM", "shape": 7, "link": 38}], "outputs": [{"localized_name": "CURVE", "name": "CURVE", "type": "CURVE", "links": [33]}], "title": "Blue", "properties": {"Node name for S&R": "CurveEditor"}, "widgets_values": []}, {"id": 8, "type": "GLSLShader", "pos": [3590, -4500], "size": [420, 500], "flags": {}, "order": 4, "mode": 0, "inputs": [{"label": "image0", "localized_name": "images.image0", "name": "images.image0", "type": "IMAGE", "link": 29}, {"label": "image1", "localized_name": "images.image1", "name": "images.image1", "shape": 7, "type": "IMAGE", "link": null}, {"label": "u_curve0", "localized_name": "curves.u_curve0", "name": "curves.u_curve0", "shape": 7, "type": "CURVE", "link": 30}, {"label": "u_curve1", "localized_name": "curves.u_curve1", "name": "curves.u_curve1", "shape": 7, "type": "CURVE", "link": 31}, {"label": "u_curve2", "localized_name": "curves.u_curve2", "name": "curves.u_curve2", "shape": 7, "type": "CURVE", "link": 32}, {"label": "u_curve3", "localized_name": "curves.u_curve3", "name": "curves.u_curve3", "shape": 7, "type": "CURVE", "link": 33}, {"localized_name": "fragment_shader", "name": "fragment_shader", "type": "STRING", "widget": {"name": "fragment_shader"}, "link": null}, {"localized_name": "size_mode", "name": "size_mode", "type": "COMFY_DYNAMICCOMBO_V3", "widget": {"name": "size_mode"}, "link": null}], "outputs": [{"localized_name": "IMAGE0", "name": "IMAGE0", "type": "IMAGE", "links": [28]}, {"localized_name": "IMAGE1", "name": "IMAGE1", "type": "IMAGE", "links": null}, {"localized_name": "IMAGE2", "name": "IMAGE2", "type": "IMAGE", "links": null}, {"localized_name": "IMAGE3", "name": "IMAGE3", "type": "IMAGE", "links": null}], "properties": {"Node name for S&R": "GLSLShader"}, "widgets_values": ["#version 300 es\nprecision highp float;\n\nuniform sampler2D u_image0;\nuniform sampler2D u_curve0; // RGB master curve (256x1 LUT)\nuniform sampler2D u_curve1; // Red channel curve\nuniform sampler2D u_curve2; // Green channel curve\nuniform sampler2D u_curve3; // Blue channel curve\n\nin vec2 v_texCoord;\nlayout(location = 0) out vec4 fragColor0;\n\n// GIMP-compatible curve lookup with manual linear interpolation.\n// Matches gimp_curve_map_value_inline() from gimpcurve-map.c:\n// index = value * (n_samples - 1)\n// f = fract(index)\n// result = (1-f) * samples[floor] + f * samples[ceil]\n//\n// Uses texelFetch (NEAREST) to avoid GPU half-texel offset issues\n// that occur with texture() + GL_LINEAR on small 256x1 LUTs.\nfloat applyCurve(sampler2D curve, float value) {\n value = clamp(value, 0.0, 1.0);\n\n float pos = value * 255.0;\n int lo = int(floor(pos));\n int hi = min(lo + 1, 255);\n float f = pos - float(lo);\n\n float a = texelFetch(curve, ivec2(lo, 0), 0).r;\n float b = texelFetch(curve, ivec2(hi, 0), 0).r;\n\n return a + f * (b - a);\n}\n\nvoid main() {\n vec4 color = texture(u_image0, v_texCoord);\n\n // GIMP order: per-channel curves first, then RGB master curve.\n // See gimp_curve_map_pixels() default case in gimpcurve-map.c:\n // dest = colors_curve( channel_curve( src ) )\n float tmp_r = applyCurve(u_curve1, color.r);\n float tmp_g = applyCurve(u_curve2, color.g);\n float tmp_b = applyCurve(u_curve3, color.b);\n color.r = applyCurve(u_curve0, tmp_r);\n color.g = applyCurve(u_curve0, tmp_g);\n color.b = applyCurve(u_curve0, tmp_b);\n\n fragColor0 = vec4(color.rgb, color.a);\n}\n", "from_input"]}, {"id": 9, "type": "ImageHistogram", "pos": [2800, -4300], "size": [210, 150], "flags": {}, "order": 5, "mode": 0, "inputs": [{"label": "image", "localized_name": "image", "name": "image", "type": "IMAGE", "link": 34}], "outputs": [{"localized_name": "HISTOGRAM", "name": "rgb", "type": "HISTOGRAM", "links": [35]}, {"localized_name": "HISTOGRAM", "name": "luminance", "type": "HISTOGRAM", "links": []}, {"localized_name": "HISTOGRAM", "name": "red", "type": "HISTOGRAM", "links": [36]}, {"localized_name": "HISTOGRAM", "name": "green", "type": "HISTOGRAM", "links": [37]}, {"localized_name": "HISTOGRAM", "name": "blue", "type": "HISTOGRAM", "links": [38]}], "properties": {"Node name for S&R": "ImageHistogram"}, "widgets_values": []}], "groups": [], "links": [{"id": 29, "origin_id": -10, "origin_slot": 0, "target_id": 8, "target_slot": 0, "type": "IMAGE"}, {"id": 28, "origin_id": 8, "origin_slot": 0, "target_id": -20, "target_slot": 0, "type": "IMAGE"}, {"id": 30, "origin_id": 4, "origin_slot": 0, "target_id": 8, "target_slot": 2, "type": "CURVE"}, {"id": 31, "origin_id": 5, "origin_slot": 0, "target_id": 8, "target_slot": 3, "type": "CURVE"}, {"id": 32, "origin_id": 6, "origin_slot": 0, "target_id": 8, "target_slot": 4, "type": "CURVE"}, {"id": 33, "origin_id": 7, "origin_slot": 0, "target_id": 8, "target_slot": 5, "type": "CURVE"}, {"id": 34, "origin_id": -10, "origin_slot": 0, "target_id": 9, "target_slot": 0, "type": "IMAGE"}, {"id": 35, "origin_id": 9, "origin_slot": 0, "target_id": 4, "target_slot": 1, "type": "HISTOGRAM"}, {"id": 36, "origin_id": 9, "origin_slot": 2, "target_id": 5, "target_slot": 1, "type": "HISTOGRAM"}, {"id": 37, "origin_id": 9, "origin_slot": 3, "target_id": 6, "target_slot": 1, "type": "HISTOGRAM"}, {"id": 38, "origin_id": 9, "origin_slot": 4, "target_id": 7, "target_slot": 1, "type": "HISTOGRAM"}], "extra": {"workflowRendererVersion": "LG"}, "category": "Image Tools/Color adjust"}]}} \ No newline at end of file