WPF двойной градиент для палитры цветов - PullRequest
0 голосов
/ 23 марта 2019

Моя цель заключается в реализации пользовательского палитры цветов.Я не хочу использовать существующие реализации, такие как xceed, среди других причин, потому что я работаю в пространстве RGBA FP32 ^ 4.Я знаю, что WPF работает только в пространстве int8 ^ 4 для отображения, но координаты, с которыми я работаю, находятся в пространстве FP32 ^ 4.Он будет взаимодействовать с активом DirectX12 с 10-битным дисплеем.

Одна вещь, которая мне нужна, это реализовать график с двумя градиентами яркости и насыщенности из пространства {Hue Saturation Luminosity}.

gradients final result

Я не мог понять, как это сделать с двойным градиентом в Rectangle;поэтому я подумал, что один из способов сделать это - иметь один градиент, например,

<Rectangle.Fill>
    <LinearGradientBrush EndPoint="1,0.5" StartPoint="0,0.5">
    <GradientStop Color="{Binding HueColor}" Offset="1"/>
    <GradientStop Color="#00000000" Offset="0"/>
    </LinearGradientBrush>
</Rectangle.Fill>

, а затем добавить фильтр градиента горизонтального насыщения, который будет перекрываться.Использование маски непрозрачности здесь не помогает:

<Rectangle.OpacityMask>
    <LinearGradientBrush EndPoint="0.5,0" StartPoint="0.5,1">
        <GradientStop Color="#00000000" Offset="0"/>
        <GradientStop Color="#FFFFFFFF" Offset="1"/>
    </LinearGradientBrush>
</Rectangle.OpacityMask>

Я не смог найти способ добиться этого в WPF.Я хотел избежать использования cuda или directx12, это было бы излишним, хотя и точным.

Ответы [ 2 ]

0 голосов
/ 26 марта 2019

Попробуйте использовать визуальную кисть. Вы можете связать свой цвет оттенка F32 и использовать конвертер для преобразования между цветовыми пространствами (как вы можете видеть в строке 11). Вы можете даже избавиться от визуальной кисти и использовать маску линейного градиента и непрозрачности прямо в своем прямоугольнике, хотя я сам не пробовал.

<Rectangle Width="200" Height="200">
<Rectangle.Fill>
    <VisualBrush TileMode="None">
        <VisualBrush.Visual>
            <Canvas Background="Black" Width="1" Height="1" SnapsToDevicePixels="True">
                <Rectangle Width="1" Height="1" SnapsToDevicePixels="True">
                    <Rectangle.Fill>
                        <LinearGradientBrush StartPoint="0,0" EndPoint="1,0">
                            <LinearGradientBrush.GradientStops>
                                <GradientStop Color="White" Offset="0" />
                                <GradientStop Color="{Binding HueColor, Converter={StaticResource color4Color}}" Offset="1" />
                            </LinearGradientBrush.GradientStops>
                        </LinearGradientBrush>
                    </Rectangle.Fill>
                    <Rectangle.OpacityMask>
                        <LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
                            <LinearGradientBrush.GradientStops>
                                <GradientStop Color="#FFFFFFFF" Offset="0"/>
                                <GradientStop Color="#00FFFFFF" Offset="1"/>
                            </LinearGradientBrush.GradientStops>
                        </LinearGradientBrush>
                    </Rectangle.OpacityMask>
                </Rectangle>
            </Canvas>
        </VisualBrush.Visual>
    </VisualBrush>
</Rectangle.Fill>
</Rectangle>
0 голосов
/ 24 марта 2019

Мне пришлось вручную реализовать создание растрового изображения с 2 классами.

public class HSLfloat
{
    public float H { get; set; }
    public float S { get; set; }
    public float L { get; set; }

    public HSLfloat(RGBAfloat rgbafloat)
    {
        var tol = 0.000001;
        var rgb = rgbafloat.ToArrayRGB();
        var rgbTuple = rgbafloat.ToArrayTuple();
        var Cmax = rgb.Max();
        var Cmin = rgb.Min();
        var delta = Cmax - Cmin;
        L = delta / 2;
        var s = Math.Abs(delta) < tol ? 0 : delta / (1 - Math.Abs(2 * L - 1));
        var CmaxName = rgbTuple.Where(o => Math.Abs(o.Item1 - Cmax) < tol).Select(o => o.Item2).First();
        if (Math.Abs(delta) < tol)
            H = 0;
        else
        {
            switch (CmaxName)
            {
                case 'R':
                    H = 60 * ((rgbafloat.G - rgbafloat.B) / delta % 6);
                    break;
                case 'G':
                    H = 60 * ((rgbafloat.B - rgbafloat.R) / delta + 2);
                    break;
                case 'B':
                    H = 60 * ((rgbafloat.R - rgbafloat.G) / delta + 4);
                    break;
            }
        }
    }

    public HSLfloat(float h, float s, float l)
    {
        H = h;
        S = s;
        L = l;
    }

    public RGBAfloat ToRGBAfloat() => new RGBAfloat(this);
    public (float H, float S, float L) ToTuple() => (H, S, L);
}

и для пространства RGBA:

public class RGBAfloat
{
    public float R { get; set; }
    public float G { get; set; }
    public float B { get; set; }
    public float A { get; set; }

    public RGBAfloat()
    {
    }

    public RGBAfloat(double r, double g, double b, double a = 1d)
    {
        R = (float)r;
        G = (float)g;
        B = (float)b;
        A = (float)a;
    }

    public RGBAfloat(float r, float g, float b, float a= 1f)
    {
        R = r;
        G = g;
        B = b;
        A = a;
    }

    public RGBAfloat(RGBAfloatStruct colorFloatStruct)
    {
        R = colorFloatStruct.R;
        G = colorFloatStruct.G;
        B = colorFloatStruct.B;
        A = colorFloatStruct.A;
    }

    public RGBAfloat(HSLfloat hslfloat)
    {
        var c = (1 - Math.Abs(2 * hslfloat.L - 1)) * hslfloat.S;
        var x = c * (1 - Math.Abs((hslfloat.H / 60) % 2 - 1));
        var m = hslfloat.L - c / 2;
        var quadrant = (int)(hslfloat.H % 360 / 60); // [0-5]
        switch (quadrant)
        {
            default:
            case 0:
                R = c + m;
                G = x + m;
                B = 0f + m;
                break;
            case 1:
                R = x + m;
                G = c + m;
                B = 0f + m;
                break;
            case 2:
                R = 0f + m;
                G = c + m;
                B = x + m;
                break;
            case 3:
                R = 0f + m;
                G = x + m;
                B =  c + m;
                break;
            case 4:
                R = x + m;
                G = 0f + m;
                B = c + m;
                break;
            case 5:
                R = c + m;
                G = 0f + m;
                B = x + m;
                break;
        }
    }

    public RGBAfloatStruct ToRGBAFloatStruct() => new RGBAfloatStruct {A = A, R = R, G = G, B = B};
    public float[] ToArrayRGB() => new[] {R, G, B};
    public float[] ToArray() => new[] {R, G, B, A};
    public (float,char)[] ToArrayTuple() => new[] { (R, 'R'), (G, 'G'), (B, 'B') };
    public (float R, float G, float B) ToTupleRGB() => (R, G, B);
    public (float R, float G, float B, float A) ToTuple() => (R, G, B, A);
    public HSLfloat ToHSLfloat() => new HSLfloat(this);

    public int ToRGBAint()
    {
        var rr = (int)(R * 255);
        var gg = (int)(G * 255);
        var bb = (int)(B * 255);
        int color = rr << 16;
        color |= gg << 8;
        color |= bb << 0;
        return color;
    }

    public WriteableBitmap ToWriteableBitmap(int size = 200)
    {
        var (h, s, l) = ToHSLfloat().ToTuple();
        var writeableBitmap = new WriteableBitmap(size, size, 96, 96, PixelFormats.Bgr32, null);
        for (int y = 0; y < size; y++)
        for (int x = 0; x < size; x++)
        {
            s = (float)x / size;
            l = (float)(size - y) / size;
            var intColor = new HSLfloat(h, s, l).ToRGBAfloat().ToRGBAint();
            unsafe
            {
                var ptr = writeableBitmap.BackBuffer;
                ptr += y * writeableBitmap.BackBufferStride;
                ptr += x * 4;
                *((IntPtr*)ptr) = (IntPtr)intColor;
            }
        }
        return writeableBitmap;
    }
}

Результат, SL карта с (r,g,b)= (0.8, 0.5, 0.3):

SL map with (r,g,b)= (0.8, 0.5, 0.3)

...