| { |
| "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", |
| "description": "Fine-tunes tone and color with per-channel curve adjustments using a real-time GPU fragment shader." |
| } |
| ] |
| } |
| } |