-
Notifications
You must be signed in to change notification settings - Fork 2.7k
feat(ui): Raster Layer Color Adjusters #8420
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(ui): Raster Layer Color Adjusters #8420
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Super nice to have this in the app, thanks for working on it.
- As you noted, perf is not great as layer size increases. It seems easy to accidentally end up with a super big layer and stack of adjustments that bogs the system down. I think this is OK for a v1 implementation but we should do our best to make it faster ASAP. I imagine the only real way to do that is native code. Maybe https://wasm-vips.kleisauke.nl/ or port to a diff lang and compile to wasm.
- I ended up with a full UI crash at some point. Somethign was trying to access a property of
undefined
. IIRC I was adjusting sharpness. I refreshed the page and it started working again. Sorry but I didn't capture the error; I am unable to reproduce it now.
Some UI/UX feedback:
- It's not clear if you have selected simple or curves. Suggest setting the colorScheme on the button to
invokeBlue
to indicate active mode. Added code suggestion - When disabled, you can still fiddle w/ the sliders and curves editor but they don't do anything. Suggest disabling the knobs n dials when disabled
- There's no way to dismiss the adjustment without applying/finishing it. And finishing always rasterizes the layer. Suggest comparing selected adjust mode and its settings and if unchagned from defaults, when finishing, do not rasterize
- Layout is a bit wonky when panel is at min width - also needs some padding:
- Curves editor canvas does not scale when resizing panel, resulting in stretched appearance:
- The font size for channel labels in curve editor is too small
- Curves editor needs padding around it
- Some wonkiness when dragging a point across the x coord of another point:
Screen.Recording.2025-08-15.at.1.09.56.pm.mov
- Some awkward behaviour w/ doubleclicking, I see we are calculating the nearest point and remvoing it on double click but it's awkward when double clicking away from an existing point:
Screen.Recording.2025-08-15.at.1.12.21.pm.mov
- Some wonkiness when clicking on the top-right point:
Screen.Recording.2025-08-15.at.1.13.41.pm.mov
...ontend/web/src/features/controlLayers/components/RasterLayer/RasterLayerAdjustmentsPanel.tsx
Outdated
Show resolved
Hide resolved
...i/frontend/web/src/features/controlLayers/components/RasterLayer/RasterLayerCurvesEditor.tsx
Outdated
Show resolved
Hide resolved
...i/frontend/web/src/features/controlLayers/components/RasterLayer/RasterLayerCurvesEditor.tsx
Outdated
Show resolved
Hide resolved
...i/frontend/web/src/features/controlLayers/components/RasterLayer/RasterLayerCurvesEditor.tsx
Outdated
Show resolved
Hide resolved
...i/frontend/web/src/features/controlLayers/components/RasterLayer/RasterLayerCurvesEditor.tsx
Outdated
Show resolved
Hide resolved
...ontend/web/src/features/controlLayers/components/RasterLayer/RasterLayerAdjustmentsPanel.tsx
Outdated
Show resolved
Hide resolved
...ontend/web/src/features/controlLayers/components/RasterLayer/RasterLayerAdjustmentsPanel.tsx
Show resolved
Hide resolved
...ontend/web/src/features/controlLayers/components/RasterLayer/RasterLayerAdjustmentsPanel.tsx
Outdated
Show resolved
Hide resolved
...ontend/web/src/features/controlLayers/components/RasterLayer/RasterLayerAdjustmentsPanel.tsx
Outdated
Show resolved
Hide resolved
...i/frontend/web/src/features/controlLayers/components/RasterLayer/RasterLayerCurvesEditor.tsx
Outdated
Show resolved
Hide resolved
Could use Nitpick - I think "apply" is a better word than "finish" for Invoke - we use "apply" all over the place for this kind of action. |
I teamed up with Claude to implement the filters (and the LightnessToAlpha filter, used by Control layers) in a few different non-JS ways: Notes:
C++ compiled to WASM:
WebGL:
The code for each implementation was super messy and hacky and I think I discarded it all last week. But just wanted to throw these ideas out. |
f5d5f69
to
450a486
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you! I have cleaned up some stuff to simplify/conventionalize React-y stuff and improve UI rendering performance. As a high-level metric, this improved FPS from ~11 to ~19.
There are some more gains to be had in the filter calculations themselves without moving to a native code implementation.
I teamed up with an LLM to investigate optimizing the algorithms. We got pretty good results. While the improvements were highly variable, they averaged to ~30% faster than the current implementation for simple and more like ~50% faster for curves.
I added a check to the benchmarks to compare the outputs of the original and optimized implementations and found they were not identical. One or two channel values were often +/-1 different from the current implementation.
I suppose this difference was related to precision, and while it is a very small diff and not noticeable to the user, I decided to not change the algorithms.
I also fixed a small bug in the curves editor points sorting that could incorrectly sorted points where x=255. The bug essentially converted the real "last" point into a "user" point. So you could move it away from 255,255.
… konva node attrs
dramatically improves the feel of the sliders
Do not use whole layer as trigger for histo recalc; use the canvas cache of the layer - it more reliably indicates when the layer pixel data has changed, and fixes an issue where we can miss the first histo calc due to race conditiong with async layer bbox calculation.
d532106
to
e9c39f7
Compare
Summary
Client-side support for making image adjustments in the canvas raster layers. These effects are applied using konva filters. They can be kept on to automatically apply to any new edits of the layer or finalized to bake the results in with the Finish button. Can be collapsed to take up less space or temporarily disabled to compare against the original.
Generating, creating from bounding box, and saving the canvas will use the current visible state of the layer (no need to finalize). If a saved canvas is recalled with the Remix option, the original layer with any pending adjustments will appear again. Duplicating a layer creates a duplicate with identical pending adjustments if they exist. Select Object will persist any pending adjustments if the user selects Apply, but not Save As (select object uses the underlying layer data directly instead of the view).
Has two modes:

Simple: Brightness, Contrast, Saturation, Temperature, Tint, Sharpness
"Brightness" is an absolute shift applied to all pixels and causes clipping on the ends. Some users might expect that to be called "Exposure" depending on the software they are coming from. Open to suggestions if we want to swap terms.
Curves: Master, Red, Green, Blue

Right click menu to access:

Related Issues / Discussions
Similar to the filters exposed in #7594 but in the frontend. Backend filters can still be useful for specific colorspace actions, but most of the common use cases should be solved with these settings.
A note on performance: For large layers, the filters can take noticeable time to complete. Konva filters re-apply any time the settings are changed or when the layer has its data updated. If a layer has pending adjustments, they will recalculate when:
Moving layers, generating new layers, or making edits to other layers does not trigger recalculation.
QA Instructions
Demo video of interactions (uses old right-click menu)
Screencast From 2025-08-13 02-22-58.webm
Merge Plan
Ready to merge
Checklist
What's New
copy (if doing a release after this PR)