Weird behavior of HSV Value blending mode

EDIT: I forgot to mention my OS and Krita Version, I am working on Win10 and Krita 5.0.6

According to Krita Official Documentation, Layer on “Value” blending mode
“Takes the Hue and Saturation of the lower layer and outputs them with the Value of the upper layer.”

But that is not happening at all when I try it

Instead of changing just the value of the color, it messes with the saturation too, it’s supposed to change only the value, Is this a bug? or some misunderstanding on my part about how HSV Value blending mode works ?

I’ve isolated this to a very specific isolated example, I am trying to change some full saturated dark red (#800000) to full valued using a Value layer on top with just white (to set the maximum value), I was expecting to get fully saturated red (saturation is supposed to keep the same), but instead of getting the expected #ff0000 , I’ve got #ffbbbb

Sample file:

It includes one layer on normal mode with color #800000
And another layer on top with HSV Value blending mode with color #ffffff, I expect to get #ff0000 but instead I got #ffbbbb

https://drive.google.com/file/d/1a4abq_YuAoKtec-S2G2kBHp5JVqq3oyG/view?usp=sharing

I have also raised similar questions:
Questions of HSX blending mode - Develop / Artists Feedback & Testing - Krita Artists (krita-artists.org)

I get the feeling blend modes need to be double checked on their output :sweat:

I did some calculations and the points of HSV value is moving HSL lightness instead. it should be a single letter index on the wrong spot. This is a plain bug and should be reported.

I think it’s a bug too

In previous versions of Krita (and I mean 5.0.X) that blending mode behave like I expected to be

Maybe I can fix this bug. I want to add the copy and over modes of HSX series to krita so that HSX mixing can be used in the smudge brush engine. However, the biggest difficulty at present is that I don’t know how to solve the problem of color sampling, that is, let color sampling also use HSX for mixing instead of RGB

color sampling?

Yes, the smudge brush engine will sample all colors within the radius according to the smear radius, and then get a new color according to the specific gravity and mix it with the colors below. I don’t know how to change the color sampling algorithm

in RGB? goshhh. that explains alot.
YUV might be the best alternative there it does not have much math as HSX.
But the code itself, you would probably have to get the sampled RGB colors convert them all to whatever do the average color there and then convert it back to RGB?

Yes, I found the relevant code in krita, but I don’t know how to change it. I changed it in mypaint brush engine before, so it should be feasible
The algorithm logic is consistent with the blending mode, but this part of the code is still too difficult for me

    float dh, dc, dl, da, sw;
    dh = dc = dl = da = sw = 0.0f;

    while(it.nextPixel()) {

        QPointF pt(it.x(), it.y());

        float rr = 0.0f;
        if(outer.fadeSq(pt) <= 1.0f) {
            float yy = (it.y() + 0.5f - y);
            float xx = (it.x() + 0.5f - x);
            rr = qMax((yy * yy + xx * xx) * one_over_radius2, 0.0f);
        }

        float weight = 1.0f - rr;

        channelType* src = reinterpret_cast<channelType*>(it.rawData());
        float sh, sc, sl, sa;
        
        if (unitValue == 1.0f) {
            sh = src[0];
            sc = src[1];
            sl = src[2];
            sa = src[3];
        } else {
            sl = src[0] / maxValue;
            sc = src[1] / maxValue;
            sh = src[2] / maxValue;
            sa = src[3] / maxValue;
            sh = CLAMP(sh, 0.0f, 1.0f);
            sc = CLAMP(sc, 0.0f, 1.0f);
            sl = CLAMP(sl, 0.0f, 1.0f);
            sa = CLAMP(sa, 0.0f, 1.0f);
        }

        if (sa > 0.0f) {
            using namespace Arithmetic;
            sa = sa * weight;
            da += sa;
            sa = sa / da;

            RGBToHCL(sh, sc, sl, &sh, &sc, &sl);

            if (dc < 0.00001f) dh = sh;
            if (sc < 0.00001f) sh = dh;

            if (sh < dh)
                dh = ((dh - sh) < 0.5f) ? dh : (dh - 1.0f);
            else
                sh = ((sh - dh) < 0.5f) ? sh : (sh - 1.0f);

            dh = lerp(dh, sh, sa);;
            dh = (dh < 0.0f) ? (dh + 1.0f) : ((1.0f < dh) ? (dh - 1.0f) : dh);

            dc = lerp(dc, sc, sa);
            dl = lerp(dl, sl, sa);
        }

        sw += weight;
    }

    if (sw > 0.0f) {
        HCLToRGB(dh, dc, dl, color_r, color_g, color_b);
        *color_a = da / sw;
    }

This is the part I modified in mypaint engine

This is part of krita’s internal color blending

template<class HSXType, class TReal>
inline void cfMixHcl(TReal sr, TReal sg, TReal sb, TReal sw, TReal& dr, TReal& dg, TReal& db) {
    using namespace Arithmetic;
    TReal sh, sc, sl, dh, dc, dl;
    RGB_HCL(sr, sg, sb, &sh, &sc, &sl);
    RGB_HCL(dr, dg, db, &dh, &dc, &dl);

    if (sc < 0.00001f) sh = dh;
    if (dc < 0.00001f) dh = sh;

    if (sh <= dh)
        dh = ((dh - sh) < 0.5f) ? dh : (dh - 1.0f);
    else
        sh = ((sh - dh) < 0.5f) ? sh : (sh - 1.0f);

    dh = lerp(dh, sh, sw);
    dh = (dh < 0.f) ? (dh + 1.f) : ((1.f < dh) ? (dh - 1.f) : dh);

    dc = lerp(dc, sc, sw);
    dl = lerp(dl, sl, sw);

    HCL_RGB(dh, dc, dl, &dr, &dg, &db);
}

This is the HCL blending mode code I added, which is almost the same as the mypiant engine

The pigment-painter @Joe_Eagar made has various models such as HSV and YUV, which can be used as a reference.But this digress.
image

OK, thanks. If anyone can add the option of blending mode in the smudge brush engine, I will add these blending modes and fix this bug,maybe it will be easier to ask the official developers to add other color sampling methods after that