Search code examples
c#wpfxamlcanvas

WPF Drawing Wheel Color Picker?


I'm have try drawing color picker by Ellipse LinearGradientBrush.

<Ellipse Width="200" Height="200">
    <Ellipse.Fill>
        <VisualBrush TileMode="None">
            <VisualBrush.Visual>
                <Canvas 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="#FF0000" Offset="0" />
                                    <GradientStop Color="#FFFF00" Offset="0.167" />
                                    <GradientStop Color="#00FF00" Offset="0.333" />
                                    <GradientStop Color="#00FFFF" Offset="0.5" />
                                    <GradientStop Color="#0000FF" Offset="0.667" />
                                    <GradientStop Color="#FF00FF" Offset="0.833" />
                                    <GradientStop Color="#FF0000" 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>
    </Ellipse.Fill>
</Ellipse>

It's just like this:

enter image description here

But How can drawing a colorpicker have multiple color and like the scattered out color like a this.

enter image description here

I have not idea. please any one help.


Solution

  • Personally, I'd just use a background texture. @Jim Foye is right though, it's very easy to do with a pixel shader:

    1. make sure you've got the fxc shader compiler installed (this tutorial should help).

    2. write a shader to convert the primitive UV coordinates to HSV, and then from HSV to RGB (math is on the HSL and HSV Wikipedia page). Here's a simple one I whipped up just now that does the job nicely:

    // ColorWheelEffect.hlsl
    #define PI 3.141592653f
    #define value 1.0f
    
    float4 main(float2 uv : TEXCOORD) : COLOR {
        uv = 2 * uv - 1;
        float saturation = length(uv);
        float hue = 3 * (PI - atan2(uv.y, -uv.x)) / PI;
        float chroma = value * saturation;
        float second = chroma * (1 - abs(hue % 2.0 - 1));
        float m = value - chroma;
        float3 rgb;
        if (hue < 1)
            rgb = float3(chroma, second, 0);
        else if (hue < 2)
            rgb = float3(second, chroma, 0);
        else if (hue < 3)
            rgb = float3(0, chroma, second);
        else if (hue < 4)
            rgb = float3(0, second, chroma);
        else if (hue < 5)
            rgb = float3(second, 0, chroma);
        else
            rgb = float3(chroma, 0, second);
        return float4(rgb + m, saturation < 1);
    }
    
    1. Compile your shader into a ps file with something like this:
        fxc.exe /T ps_3_0 /E main /Fo ColorWheelEffect.ps ColorWheelEffect.hlsl
    
    1. Add the ps file as a resource to your WPF project and provide an effect class for it:
        public class ColorWheelEffect : ShaderEffect
        {
            public ColorWheelEffect()
            {
                this.PixelShader = new PixelShader { UriSource = new Uri("pack://application:,,,/YourApplicationName;component/ColorWheelEffect.ps", UriKind.Absolute) };
            }
        }
    
    1. Create an ellipse in your XAML and set the effect to an instance of your effect class:
        <!-- Must fill with transparent or you won't see anything -->
        <Ellipse Width="200" Height="200" Fill="Transparent">
            <Ellipse.Effect>
                <local:ColorWheelEffect />
            </Ellipse.Effect>
        </Ellipse>
    

    Result:

    enter image description here