Smudge Tweaks testers needed

Tags: #<Tag:0x00007fc07f101750>


This looks like a good way to do it. I agree, having a pref is the way to go instead of lots of key mappings. I guess the bigger design might need to be re-thought a bit eventually. Right now the brush holds the HSV values no matter what, because HSV is what the brush engine expects. I extended that to include (almost) the full CIECAM spec, regardless if you actually used a CIECAM adjuster. All of this ends up in the stroke map, which I thought was pretty cool in that you can recover the full ciecam spec from the canvas and have parts of the painting using different illuminants, etc.

So, it sounds like it would would make more sense for the stroke map to contain HCY if you used HCY to select or modify the color, and so on. And if you didn’t use CIECAM maybe it shouldn’t actually be in there at all. I guess the basic idea is that the only color model that has useful information is the model you used to make the selection/adjustment.

So, ultimately, the brush should have just two color things, the RGB color and the full UIColor object that you used to select/modify the color. The libmypaint engine should really be modified to just take RGB (I’ve done this on another branch (OCIO) and it works fine). This would free up a few cycles since currently the brush engine is constantly converting HSV->RGB for every dab.

. . . .I was just reviewing all the stuff I had to do to persist CIECAM color dimensions. . . ugh, I did a lot of hacky stuff to try to avoid losing the color data. I think we need to redesign the color manager a bit so that the main managed color does not have to be HSV and/or isn’t reset to HSV all the time. By stuffing the CIECAM stuff into the brush settings I’m basically side-stepping the bigger issue-- the managed color should just be the color in the model that you picked it from. This problem extends to colors stored in the palette, color history, etc. All these are losing information about the color you are using. :frowning:

I’ll have to try to reproduce this better. Does it do this if you set the pref color.average_use_dominant to False? Using the kmeans clustering might slow it down even more.

Good call, that looks a lot better at all sizes. Did you try the slider bar size preference?
So many prefs… … so little gui space :-). I wonder if we could do something like the way browsers do configuration: about:config w/ a list. At least for the obscure prefs. It’s too bad there isn’t anywhere to add a description of each preference that could be listed next to each.


I kind of noticed it :slight_smile: But tbh, I’m not sure if it’s necessary since the rest of the UI (gtk3 elements) doesn’t scale with the window size either.


Upon opening, it often seems to be initialized with a too small value (although maximized). It goes back as soon as I pick a different hue/saturation. Shouldn’t gtk somehow take care of this via high-dpi scalng anyway? To me this feels a bit like reinventing the wheel :slight_smile: But if there is no other option for very-high-resolution devices then I guess it’s fine

An about:config thing would be nice where you can also reset to the default value


Oh wow, I didn’t realize I messed up the vertical sliders. I was only trying to affect the horizontal channel sliders. I guess I shouldn’t have commented out the global constant. For me, the horizontal sliders really need to be quite a bit bigger to be able to really see the color well. I don’t have a “retina” monitor but it is 2560x1440 so it’s kind of annoying.

You’re right though, it seems like if you drag the sliders window to be bigger it should “fill” automatically and make the sliders bigger too. This is probably a band-aid solution :-/


I just checked, Krita seems to do the same thing with color selectors (just the ones in the side panels though)

I think it would work best if you simply add a minimum size for scaling elements: like 16px for sliders and maybe 70px for color picker preview (resp. 140px for the one with the hole :slight_smile: )

Listening to resize-events and redrawing those elements might help too



Subscribing to casually follow the thread and trying to give an answer to that question:

I was aiming for a July release but the reality is that two things happened that are slowing down my initial schedule:

  • I’m co-authoring a paper which absorbs most of my time and have me to update a fair chunk of the plotting API
  • ACES 1.1 has been released so I technically need to generate a new OpenColorIO config for it.

I would like to release v0.3.12 as soon as possible though :slight_smile:

I haven’t read the thread fully but looking at the areas with stripes, I was wondering if you tried to lerp between the inside edge colours and make the outside ones constant? I know the inside interpolated colours would be incorrect but it probably does not really matter from an artist standpoint. This would be ideally available as a toggle to not scorch colour scientists eyes :wink:

Keep up the good job by the way!




Thank you for stopping by! I like the idea of a toggle to turn that on/off and fill-in the gaps. That would also disable the automatic gamut mapping I perform when you click in a stripe-zone (using two-stages of goofy scipy minimizations). I like the idea of a toggle to avoid accidentally kicking in the gamut mapping.

I was actually just testing OCIO to do all of this and get rid of the stripes and minimization steps; just flll in the whole bar with the output from OCIO. I just feed linear RGB output from CAM16 directly through OCIO Reference-to-GUI transform. The bars and adjusters were much more performant and I was pretty excited. But I hit a snag-- when lightness was increased to the point where desaturation was required, the color was always desaturated with D65 and not the illuminant I specified with CAM16. So I was trying a Bradford transform but I don’t think that will work either because ultimately I need to feed it back through OCIO. So, I think I would need an OCIO config for each illuminant? Maybe I can generate them on-the-fly since I’m allowing custom XYZ illuminants. I’d hate to abandon illuminants since it seems to be artistically useful.

Thanks again for all your work on colour-science. We’ve got plenty to work on without a new release :slight_smile: I’m continually amazed by the generosity and patience of folks like you and @troy_s in helping us mortals understand colour. :slight_smile:


Some minor refactoring on the slider-stripes code …that are already merged to your repo :slight_smile:

Stripes background is not transparent anymore (above GTK theme color). Before, some ugly outlines showed through, so instead it is now a 50% grey. I also made the gradient fade into that neutral color instead of black.

Not sure if optimization/caching is needed here. When testing with a slider length across 2 monitors (about 2000 black/white stripes), it took about 10ms to calculate and draw one slider on my PC (measured with time.time()).

I also noticed that, when you have pure black selected and use the ciecam sliders cH, cS, or cT (by clicking) mypaint hangs for quite some time:


I noticed that hang too on startup (black color really), good catch it is totally the optimization step trying to find a solution to something. Must really not like pure darkness when trying to do something with that color temperature I guess (I think it started when I added that)
Looking a lot nicer!
Thoughts on temperature slider? I think it’s a lot more intuitive to use than going into prefs and selecting an illuminant. … but you can still ctrl-shift-R to pick an illuminant from the canvas, too.


It looks nice. I would also add some snapping function for the “neutral” temperature to basically find D65 more easily (or maybe having all those steps in general).

I also don’t seem to be able to select the outermost areas of the slider.


Yeah, some kind of reset button (besides going into prefs and selecting D65), or maybe radio buttons to toggle between some common ones. We really should add type-in boxes to all the sliders, too.

So, as we move away from D65 color temp the numbers skew when doing a round-trip from CCT to RGB back to CCT. No idea why at the moment :slight_smile:

array([ 0.86889696, 0.99450373, 1.33161882])
on the other end 1667K is outside the sRGB gamut, but that’s another issue. Probably should make the temperature slider also use the stripes

edit-- colour.temperature.xy_to_CCT_Hernandez1999 seems to work much better. Actually-- now the other direction has problems, as you move towards lower temps. Hmm this might actually be due to the negative rgb values… For now I’ll probably just limit the lower end to a higher value.

edit… it wasn’t the negative rgb. It’s like Hernandez method works better for >=6500K and the default Kang 2002 is better for <6500K. This is embarrassing but I added that logic into the slider.


Did some tweaks with the modifier keys and added the option for HSV, HCY and CIECAM ('color.tune_model' with current default value 'CIECAM')

No matter what model is selected, at the end both hsv and ciecam get applied in the brush settings.

It bugged me a bit that HSV/HCY uses values from 0…1 and CIECAM 0…100 or 0…360. Therefore the step size value had to be handled differently for the old models. (not a real problem though)

I also tweaked the step sizes a little bit since I though rotating the hue was relatively slow compared to changing the value or saturation. There is also some refactoring, see how you like it, and maybe test all 6 functions with all 3 options :slight_smile:


Very cool! I’ll try this out today. CIECAM is looking very nice when compared to the other models. :slight_smile: I did a bunch of cleanup and (mostly) fixed some problems with the lightness being ramped down unexpectedly when using illuminants != D65. So, I re-introduced the problem of not being able to hit 100% white easily due to larger step size overshooting the “correct” maximum lightness. I will work on that soon though.

I’m practicing a workflow where you pick a color off the canvas, and then adjust the temperature slider before you manipulate the lightness or saturation. For instance, for a surface facing a low sun, I pick the color and drag the temperature to warm, then increase lightness or desaturate the color. Likewise, for a surface in the shadows but illuminated by the sky, I pick the color and then drag the temperature slider to the cooler end and then adjust lightness and saturation. Work in progress:

Oh as a byproduct of fixing stuff, if you pick a hue from the norm slider it will reset the temperature to D65 since that is what the slider is… Seems to be rational…


Is there are fixed number or you might need many of them?


@KelSolaar, infinite, I suppose. I’m allowing pick-from-canvas illuminant as well as CCT slider and a few named illuminants like D50, etc. I’ll probably remove the named illuminants since CCT seems much more useful. Gamut mapping is painfully slow still although I can avoid it somewhat by just using dull colors :stuck_out_tongue:

Most confusing interface ever? Six sliders now. Here’s an even more confusing demo :slight_smile:

@toniw thank you for refactoring a bunch of that adjuster code! Looks much better. I added a “limiter” slider to constrain the colorfulness. Combined with an illuminant this sort of shifts the palette towards the illuminant side of the colorwheel. Might be useful and easier than going into prefs to adjust the limiter. So, I think I will remove the pref gui for illuminant and color purity limiter, and add the gui pref for the adjuster model pref that you added.


I have some idea on the issues with gamut mapping when the illuminant doesn’t match the display reference (D65). I wrote some things here:

I’m hoping this will allow a nice smooth transition from a chromatic color to an achromatic lighter color while only consciously increasing lightness, regardless of the illuminant. The goal being to have an intuitive way to create warm or cool whites/grays from any color.


Ok I think I have warm/cool and the gamut mapping working in general. Probably lots of room for optimization, but you can paint rainbows of different temperature now, really quickly:


I’ve added some OpenColorIO stuff to this branch here:
and I’ve also merged this into my Master branch so you’ll need OpenColorIO to compile it now.
If you have an OCIO environment variable pointing to a config that has the filmic blender roles, default, reference, and color_picking, it should work. Otherwise without $OCIO it will behave normally w/o color management. Maybe a bit slower since I’m still converting int to float regardless.

I still have a transform to figure out; when selecting a GUI color (color_picking role) needs to be transformed to reference. So right now if you use the same color space for reference and color_picking roles it should be consistent, but if you set the color_picking to sRGB OETF and the reference to linear, there will be a mismatch until I add that transform. With the limitations of Cairo we’re kinda stuck with sRGB for the gui unless we can tolerate banding issues.

The nice thing is I’ve managed to get SWIG figured out, so this is using OCIO C++ for the tile transforms, and the “cache” only stores “display tiles”. That is, when sampling the canvas for color, it will sample the reference tiles instead of the tiles that have been transformed for display (default) role.

The other nice thing is that if you set your reference to Linear, you do not need to set the Smudge Pow setting to ~2.4. This should be a small boost to performance for the spectral brushes. You can still use Smudge Pow for artist effects, of course.

Guide to generate a LUT for your monitor for color correction:
and the filmic config for OCIO:
both courtesy of @troy_s :smiley:

Hmm I might be misusing roles… I’m setting my “default” role to “sRGB Corrected” This isn’t the flexible way to do it… but at least you can set your image space, GUI space, and Display space separately now.


I’ve got the Reference to GUI transforms almost sorted out, and I’ve added a preference to allow you to increase the rendered tile cache size. The default is 2048 tiles, which, at 64x64 is only about 8 megapixels of caching. I bumped mine up by 100X and performance greatly improved (at the expense of memory usage).


And on that note, I’ve removed the OCIO stuff from master. Bit off way more than I can chew at the moment. I’ll continue the color management saga on the OCIO_int branch. Probably revisit making the core tile storage float. Need to handle load/save better, probably use OpenImageIO for that. But for now, we’re back to non-managed on my master branch. Sigh. I copied the progress I made to master_plus_OCIO branch, joining the wreckage of a few other OCIO branches :slight_smile:


Might have cracked the code for the illuminant shifting. Added shortcut keys shift z and shift x to shift the illuminant warmer or cooler in real time while painting:

I’m NOT scaling XYZ so that Y=100, I think it looks much nicer without that normalization… but maybe I ought to… not sure