I'm generating new blending modes for Krita

I tend to agree that the conventions are so burned into people’s routines that adding the configuration parameter will rock boats or just confuse people. On the other hand, after getting used to using it, it’s an extra degree of flexibility that’s hard to live without.

I tried to implement some fixed modes in GIMP once. I know enough to know that adding configurable modes probably wouldn’t be simple, and it’s all over my head.

Regarding ‘superlight’:
Keep in mind the way I’m thinking about using the contour maps to guide what I desire from the math. I can look at the contour map of ‘pnorm’, and I can just say, “I want two copies of that, squished vertically, and one is flipped”, then out comes the algebra hammer. So ‘lo’ just defines where those two halves of the contour map meet.

In the code, ‘lo’ is a logical mask which is implicitly cast as numeric. I don’t know how this would be handled in a real programming language, but in Matlab, overcalculating and using multiplicative masking is faster than logical indexing:

case 'superlight' % use piecewise-pnorm to create a superelliptic contrast mode
	amount=max(1,amount);
	if amount==1;
		R=2*M+I-1;
	else				
		lo=M<0.5; hi=~lo;
		R=zeros(size(M));
		R(hi)=(I(hi).^amount + (2*M(hi)-1).^amount).^(1/amount);
		R(lo)=1-((1-I(lo)).^amount + (1-2*M(lo)).^amount).^(1/amount);
	end

EDIT:
I don’t know if it’s apparent, but everything in Matlab is going to be processed as whole arrays instead of pixel-wise. I don’t recall how things are handled in Krita.

I don’t know if it’s apparent, but everything in Matlab is going to be processed as whole arrays instead of pixel-wise. I don’t recall how things are handled in Krita.

That’s not a problem. The equation I have observed seem translate-able to Krita format.

(X ? Y : Z) means if X is true, then Y, else Z. That’s what I do for piecewise functions. amount is a variable that I wish to assign. I (matlab) = src (krita) ; M. = dst (krita) . I managed to recreate svg softlight myself, and that is difficult to code in, but certainly was very possible for me.

I’ll trust real programmers to know what’s fast and appropriate in real languages. I kind of assumed that my gross misapplication of Matlab was bound to produce methods that weren’t optimized for other, more practical environments.

Regarding adapting ‘superlight’ to fixed-parameter use:
The flexibility of the mode aside, the initial motivation was to make an eased version of ‘pinlight’. The loci of right angles in the contour map are a ridge and valley in the surface – a region where the directional derivatives suddenly change. When the input images are smooth, this change in the dominance between FG and BG tends to produce a thresholding or hard masking behavior, adding rough edges to what were smooth images. Easing this transition was the goal. That said, an appropriate fixed parameter might be something in the neighborhood of 4-10 instead of 1.

Also, in case you can’t tell, I work in a vacuum and until now have had no evidence that anyone looked at my code. I tend to change my conventions over time. IMBLEND was one of the first things I made, so the M/I convention was something that only it retains.

M & I
mask & image
src & dst
fg & bg

I get what you mean now. It’s a blending mode which uses a randomised transfer function with levels selected by the values in the images - bomb interpolates between the levels, hardbomb doesn’t. I’ve just written an implementation in G’MIC for both bomb and hardbomb:

#@gui Bomb blend : fx_blend_bomb, fx_blend_bomb_preview()
#@gui : Recompute = button(0)
#@gui : X = int(16,1,256)
#@gui : Y = int(16,1,256)
#@gui : Interpolation = choice(1,"Nearest","Linear")
#@gui : Reverse = bool(0)
fx_blend_bomb :
if {$4==0}
inter=1
else
inter=3
fi
$2,$3,1,3 noise. 255 r. 256,256,1,3,{$inter} to_rgb. 
if $5 rv[0,1] fi
f... "i(#2,i(#0),i(#1))" to_rgb
rm[1,2]
fx_blend_bomb_preview :
fx_blend_bomb $*

Example: image 1, image 2, result after fx_blend_bomb 0,16,16,1,1 :

image

1 Like

Wow, I love that result. I think I’m going to recreate MATLAB blending modes soon. Krita will probably be the very first software to support all of the existing known rgb-compatible blending mode. It will also be easier to read from source as Krita uses if, then ; step-by-step lines ; src and dst; Scale() to calculate using float… . The source material if accepted can be converted to GIMP code.

If you like that, wait until you see the upgrade:

#@gui Bomb blend : fx_blend_bomb, fx_blend_bomb_preview()
#@gui : Recompute = button(0)
#@gui : Mesh X = int(16,1,256)
#@gui : Mesh Y = int(16,1,256)
#@gui : Mesh smoothness = float(0.5,0,10)
#@gui : Mesh contrast = float(50,0,100)
#@gui : Reverse = bool(0)
#@gui : Alpha = bool(0)
fx_blend_bomb :
l[0,1] to_rgba 
$2,$3,1,4 noise. 255
if $7 ac. "noise 255",rgba_a fi
r. 256,256 n. 0,255 blur. {$4^2}%
c. {($5-1/255)/2}%,{100-($5-1/255)/2}% n. 0,255
if {!$7} to_rgb. fi
if $6 rv[0,1] fi 
f... "i(#2,i(#0),i(#1))" if {!$7} to_rgb fi
rm[1,2]
endl
fx_blend_bomb_preview :
fx_blend_bomb $*

It can now mess things up properly in the alpha channel as well.

Here are some examples of what this thing can now do. Simple gradients:
image

Alpha channel:

image

Really abstract material (which will most likely end up on those ‘aesthetic’ blogs and similar places):

image

image

…and of course, a lot of fun with horribly-compressed JIFFs.

image

I’m really not able to follow the code here, but it looks like the same sort of idea. I’m assuming that’s a 16x16 tf?

The output seems to be more strongly dominated by primaries and secondaries than I’d expect, so I’m not sure if there’s a difference in our approaches. Then again, it’s hard to tell when things are random…

I hate to ask, but for sake of making sure we’re on the same page regarding the 2D interpolator, could you do me a favor and if possible, run a test pair with a fixed tf for normal and hard variants of your setup? I’m really lost in g’mic.

% 4x4x3 tf.  i assume you'd need to convert to int
tf_r=[0.46793 5.1692e-06 0.87901 0.61863 0.40424;0.26789 0.19705 0.13195 0.83972 0.35911;0.07158 0.14952 0.64716 0.76896 0.21964;0.94989 0.76023 1 0.22937 0.98994;0.8272 0.13955 0.13179 0.08721 0.35122]
tf_g=[0.79758 0.38628 0.080147 0.87035 0.053319;0.74359 0.22975 0.026048 0.56244 0.79968;1 0.57999 0.82849 0.031294 0.44134;0.49606 0.82131 0.35239 0.0477 0.65466;0.46792 0 0.4475 0.87378 0.52436]
tf_b=[0.48131 0.1731 0 0.47259 0.53728;0.54483 0.44578 0.95829 0.80938 0.16761;1 0.23373 0.96989 0.78254 0.70184;0.47504 0.2867 0.85657 0.36321 0.0074168;0.27969 0.76006 0.20659 0.019315 0.60487]
tf=cat(3,tf_r,tf_g,tf_b);

% normal and hard modes
ashes1=imblend(fg,bg,1,'mesh',tf);
ashes2=imblend(fg,bg,1,'hardmesh',tf);

…and of course, a lot of fun with horribly-compressed JIFFs.

Exploiting processing error sources to produce remotely-referential derived images? Now we’re talking!

@Joan_Rake1 Could you write the full command names or a least annotate for the benefit of the non-G’MIC script writers?

Also, what does i(#2,i(#0),i(#1)) mean? Could you comment in on its mathematical form or at least provide an explanation?

I’m really not able to follow the code here, but it looks like the same sort of idea. I’m assuming that’s a 16x16 tf?

That’s the default but users can specify a size up to 256x256 and down to 1x1.

The output seems to be more strongly dominated by primaries and secondaries than I’d expect, so I’m not sure if there’s a difference in our approaches. Then again, it’s hard to tell when things are random…

That would probably be due to the contrast setting being turned up high (>50). It’s a cut-and-normalise clipping combo which chops off higher and lower values before scaling the remainder back to the 0-255 range.

I hate to ask, but for sake of making sure we’re on the same page regarding the 2D interpolator, could you do me a favor and if possible, run a test pair with a fixed tf for normal and hard variants of your setup? I’m really lost in g’mic.

You’re better off using the GIMP plugin so you can see what’s going on if you’re using the command line version for now, but I can give you the command that you need:

fx_blend_bomb 0,16,16,0,73.1,0,0

Keep the two 16s constant; the fourth option is the smoothness and it should be 0 for hardbomb and 2 for bomb. Insert ‘display’ into the command’s code to see what’s happening, if you try: …1] fi display f... "i(… then you’ll see that the TF matrix is an RGB image if the last option is 0 and an RGBA image if it’s 1.

Exploiting processing error sources to produce remotely-referential derived images? Now we’re talking!

I love such broken textures myself. I can go a bit further:

image

Just caught this.

Could you write the full command names or a least annotate for the benefit of the non-G’MIC script writers?

#@gui Bomb blend : fx_blend_bomb, fx_blend_bomb_preview()
#@gui : note = note("Creates a random transfer function 'mesh' and then blends images accordingly. Based on method shown <a href="https://discuss.pixls.us/t/im-generating-new-blending-modes-for-krita/8104/16">on discuss.pixls.us</a>.")
#@gui : Recompute = button(0)
#@gui : Mesh X = int(16,1,256)
#@gui : Mesh Y = int(16,1,256)
#@gui : Mesh smoothness = float(0.5,0,10)
#@gui : Mesh contrast = float(50,0,100)
#@gui : Reverse = bool(0)
#@gui : Alpha = bool(0)
#@gui : Normalise = bool(0)
fx_blend_bomb :
# select the first two images in the list and convert them to rgba
local[0,1] to_rgba 
# create transfer function mesh image and apply noise
# if we're using the alpha channel, apply noise in alpha channel too
$2,$3,1,4 noise[-1] 255
if $7 apply_channels[-1] "noise 255",rgba_a endif
# resize mesh using nearest-neighbour interpolation and blend to smooth it out
resize[-1] 256,256 normalize[-1] 0,255 blur[-1] {$4^2}%
# clip values to add contrast to mesh
cut[-1] {($5-1/255)/2}%,{100-($5-1/255)/2}% normalize[-1] 0,255
# delete the alpha channels of all images if we're not using them
if {!$7} to_rgb endif
# reverse blending images if we choose to
if $6 reverse[0,1] endif
# select colour to fill each pixel with from transfer function mesh image to fill result image with
# use x-coordinate from value of bottom image, y-coordinate from value of top image, channels are independent
fill[-3] "i(#2,i(#0),i(#1))"
# remove matrix and second image
remove[1,2]
# a final normalisation
if $8 apply_channels "normalize 0,255",rgba endif
# deselect
endlocal
fx_blend_bomb_preview :
fx_blend_bomb $*

It’s still got some way to go (you’ll notice that I’ve already added another normalize). I have to sort out multiple layer inputs like G’MIC’s standard blend filter already has.

I wish that G’MIC filter worked on Krita. Well, your CubeHelix filter works just well.

It’s probably a segfault so it’s not something that I can fix. Unfortunately David won’t be back to fix things for weeks if my memory’s serving me well so Krita users will be in limbo for now. I don’t know who else can help but it probably won’t be anything to do with G’MIC.

Confirmed, I can add the missing compatible blending modes from matlab to krita -

This is P-Norm blending Mode. Next up is some modes from IFS Illusions, and Superlight.

Here’s part of the code for P-Norm -

template<class T>
    inline T cfPNorm(T src, T dst) {
    using namespace Arithmetic;

return clamp<T>(pow(pow(dst,2)+pow(src,2),.5)); 
}

Wow, superlight is tough to crack.

EDIT: @DGM - I think I cracked it, but just for float images for now. I can fix that though.

Well, it does look better than Pinlight.

I guess if it’s going to be fixed-parameter, in this case it’s technically the 2-norm, or i guess you could call it a euclidian norm, or ‘hypotenuse’

… also, yeah. I’ve got it easy doing everything in floating point.

I guess if it’s going to be fixed-parameter, in this case it’s technically the 2-norm, or i guess you could call it a euclidian norm, or ‘hypotenuse’

Duly noted.

… also, yeah. I’ve got it easy doing everything in floating point.

Yeah, I can see why. Krita has floating point support, and integer point support. Meaning I have to support both modes. Binary modes are the exception to that rule, and it’s not worth implement workaround or another 16 blending modes to support binary float modes. In the case of superlight, well, it’s not going to be very easy to implement for both, but it is worth supporting for both, and it is much better than Pinlight.

EDIT: Fixed Superlight for Integer Color Space Mode

Now it works on float, and integer.

I guess it helps once I figured out that this looks like a pipeline… Let me know if I’m getting this straight.

So you create a random-valued array of range according to the working image datatype and of user-defined size:

$2,$3,1,4 noise[-1] 255

you resize the array to suit using it as a LUT for uint8 inputs:

resize[-1] 256,256

you renormalize and do a blur of user-defined kernel size

normalize[-1] 0,255 blur[-1] {$4^2}%

which I guess is more flexible than just having nearest/linear interpolation options…
i guess this is the point at which our methods differ as you mention:

cut[-1] {($5-1/255)/2},{100-($5-1/255)/2} normalize[-1] 0,255

clip upper and lower 25% (default) of the tf and renormalize.

I’m not sure what this reverses. Is this just transposition (swapping input images)?

if $6 reverse[0,1] endif

this syntax baffles me, but I take it this is essentially a LUT reading task.

fill[-3] “i(#2,i(#0),i(#1))”

so disregarding the differences in the smoothing/interpolation approach, the default contrast parameter is adding more regions where the tf is saturated. Like you said, things would tend to get pushed into the corners more than I’d expect. It’s nice to have the extra degree of freedom.

Would there be a way to hold an unmodified version of the random tf and re-use it between invocation? Having these extra parameters has me kind of imagining something where if a particular desirable random tf is found, the user could tick a box to hold the random tf, so that the smoothing and thresholding parameters could be tuned without losing the base tf. That would be pretty dang convenient.

I love such broken textures myself. I can go a bit further:

Now all you need to do is [dives off on a tangent about favorite ways to shred a painting into total abstraction and then coax it back into resembling some sort of fantasy landscape]…

Yeah, I kind of figured there’d be a duality of paths for different image types. I was straight up lazy defaulting to double, and you can imagine I pay for it in memory whether It’s needed or not.

The results are looking good. What parameter value did you settle on?

Hmm, I’m looking at the pdf document you sent me, and I picked the one that is not too extreme or too soft, but just right. 2.3333 is just right for P-Norm. However, for superlight, I think I might create 3 varients with 3 different parameters. I’ll continue exploring IMBLEND blending modes with fixed parameters.

Having a few variants would easily work. Subtle control over the parameter really isn’t that necessary. Regarding my first response in the thread, is the addition of a bunch of extra modes going to cause objection or problems? Is this something that is going to be either optional or user-configurable? I haven’t had a chance to install 4.2, and I really don’t remember how Krita was configured.

That, and I hate to say this, but I hope you don’t think MIMT/IMBLEND are part of Matlab. I’m pretty sure if you referred to them as Matlab modes, you’d probably confuse the Matlab users most of all – since they’d be wondering when Matlab could do that. The FEX is all user-contributed files. I’m not associated with Mathworks or anything.

On the topic of sources, I have a question. I’ve found two different formulae for the ‘softlight’ variant attributed to EffectBank in the following sources, but I’ve not found anything more definitive. The website no longer exists.

This looks ideal
This is almost identical, but faster.

I was wondering if you’d seen any other sources regarding this mode.

1 Like