天天看點

WPF 簡單實作顔色選擇器

作者:opendotnet

WPF 簡單實作顔色選擇器

控件名:ColorPicker

作 者:WPFDevelopersOrg - 驚鏵

原文連結[1]:https://github.com/yanjinhuagood/ColorPickerSample

  • 架構使用

    .NET4

  • Visual Studio 2022

    ;
WPF 簡單實作顔色選擇器

1)新增

xaml

代碼如下:

  • 定義一個

    WriteableBitmap

    用于記錄顔色緩沖值。
  • XAML

    中定義

    Canvas

    設定背景為一張圖像。
  • Canvas

    中添加

    Thumb

    是一個可拖動的控件,用于實作互動中的拖動後擷取顔色。
<Canvas x:Name="canvas" MouseLeftButtonDown="canvas_MouseLeftButtonDown">
<Canvas.Background>
<ImageBrush ImageSource="{Binding Bitmap}" />
</Canvas.Background>
<Thumb
x:Name="thumb"
Canvas.Left="0"
Canvas.Top="0"
Width="20"
Height="20"
Background="Transparent"
BorderBrush="Black"
BorderThickness="2"
DragDelta="Thumb_DragDelta">
<Thumb.Template>
<ControlTemplate TargetType="Thumb">
<Border
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
CornerRadius="10"
SnapsToDevicePixels="True" />
</ControlTemplate>
</Thumb.Template>
</Thumb>
</Canvas>
           

2)新增

Loaded邏輯

處理代碼如下:

  • 首先嵌套循環,用于在圖像的每個像素位置上進行操作色值。
  • height

    width

    是圖像的高度和寬度。
  • 在外層循環中,變量

    y

    從 0 開始遞增,直到小于

    height

  • 在内層循環中,變量

    x

    從 0 開始遞增,直到小于

    width

  • 在每個像素位置上,通過計算

    normalizedX

    normalizedY

    ,将

    x

    y

    的值歸一化到 [0, 1] 範圍内。
  • 使用

    HSVToRGB

    函數将歸一化後的

    normalizedX

    normalizedY

    和 1(表示最大亮度)轉換為 RGB 值,并将結果存儲在

    r

    g

    b

    變量中。
  • 計算像素在圖像資料緩沖區中的偏移量

    pixelOffset

    ,其中

    stride

    是每行像素占用的位元組數。
  • 使用

    Marshal.WriteByte

    方法将 RGB 值寫入圖像資料緩沖區中的相應位置,同時設定 Alpha 通道為 0xFF(完全不透明)。
IntPtr backBuffer = Bitmap.BackBuffer;
int stride = Bitmap.BackBufferStride;
for (int y = 0; y < height; y++)
 {
for (int x = 0; x < width; x++)
 {
byte r, g, b;

double normalizedX = (double)x / (width - 1);
double normalizedY = (double)y / (height - 1);

 HSVToRGB(normalizedX, normalizedY, 1, out r, out g, out b);

int pixelOffset = y * stride + x * 4;
 Marshal.WriteByte(backBuffer, pixelOffset + 0, b);
 Marshal.WriteByte(backBuffer, pixelOffset + 1, g);
 Marshal.WriteByte(backBuffer, pixelOffset + 2, r);
 Marshal.WriteByte(backBuffer, pixelOffset + 3, 0xFF);
 }
 }
           

3)新增

HSVToRGB

方法代碼如下:

  • HSVToRGB 方法傳回 r 、g、b
  • h

    是色相值,取值範圍為 [0, 1]。
  • s

    是飽和度值,取值範圍為 [0, 1]。
  • v

    是亮度值,取值範圍為 [0, 1]。
  • r

    g

    b

    是輸出參數,用于存儲轉換後的 RGB 值。
  • 在函數内部,根據 HSV 轉換公式進行計算。如果飽和度

    s

    為 0,則表示灰階色調,此時将 RGB 的三個分量都設定為亮度

    v

    的值,并乘以 255 轉換為位元組表示。
  • 如果飽和度

    s

    不為 0,則根據色相

    h

    的值确定所處的色相區間,并根據公式計算出對應的 RGB 值。具體步驟如下:
  • 将色相

    h

    乘以 6,得到一個擴充的色相值

    hue

  • hue

    的整數部分作為索引

    i

    ,表示所處的色相區間。
  • 計算

    hue

    的小數部分

    f

  • 根據公式計算出對應的 RGB 值,其中

    p

    是亮度

    v

    與飽和度

    s

    的乘積,

    q

    是亮度

    v

    與飽和度

    s

    以及

    f

    的乘積,

    t

    是亮度

    v

    與飽和度

    s

    以及

    (1.0 - f)

    的乘積。
  • 根據索引

    i

    的值,将計算得到的 RGB 值賦給輸出參數

    r

    g

    b

private static void HSVToRGB(double h, double s, double v, out byte r, out byte g, out byte b)
 {
if (s == 0)
 {
 r = g = b = (byte)(v * 255);
 }
else
 {
double hue = h * 6.0;
int i = (int)Math.Floor(hue);
double f = hue - i;
double p = v * (1.0 - s);
double q = v * (1.0 - (s * f));
double t = v * (1.0 - (s * (1.0 - f)));

switch (i)
 {
case 0:
 r = (byte)(v * 255);
 g = (byte)(t * 255);
 b = (byte)(p * 255);
break;
case 1:
 r = (byte)(q * 255);
 g = (byte)(v * 255);
 b = (byte)(p * 255);
break;
case 2:
 r = (byte)(p * 255);
 g = (byte)(v * 255);
 b = (byte)(t * 255);
break;
case 3:
 r = (byte)(p * 255);
 g = (byte)(q * 255);
 b = (byte)(v * 255);
break;
case 4:
 r = (byte)(t * 255);
 g = (byte)(p * 255);
 b = (byte)(v * 255);
break;
default:
 r = (byte)(v * 255);
 g = (byte)(p * 255);
 b = (byte)(q * 255);
break;
 }
 }
 }
           

4)新增

Thumb_DragDelta

代碼如下:

  • 在事件處理程式中,首先擷取拖動的

    Thumb

    控件,并計算出新的左側和頂部位置。通過

    Canvas.GetLeft(thumb)

    Canvas.GetTop(thumb)

    方法擷取目前

    Thumb

    控件在

    Canvas

    中的左側和頂部位置,然後将其與拖動的變化量

    e.HorizontalChange

    e.VerticalChange

    相加,得到新的位置。
  • 計算

    Canvas

    的右側和底部邊界。通過

    canvas.ActualWidth - thumb.ActualWidth

    canvas.ActualHeight - thumb.ActualHeight

    計算出

    Canvas

    的右側和底部邊界位置。
  • 對新的左側和頂部位置進行邊界檢查。如果新的左側位置小于 0,則将其設定為 0,以保證

    Thumb

    控件不會超出

    Canvas

    的左側邊界。如果新的左側位置大于

    Canvas

    的右側邊界位置

    canvasRight

    ,則将其設定為

    canvasRight

    ,以確定

    Thumb

    控件不會超出

    Canvas

    的右側邊界。類似地,對新的頂部位置進行邊界檢查。
  • 通過

    Canvas.SetLeft(thumb, newLeft)

    Canvas.SetTop(thumb, newTop)

    Thumb

    控件的位置更新為新的左側和頂部位置。
  • 調用

    GetAreaColor()

    方法來擷取更新後的區域顔色。
private void Thumb_DragDelta(object sender, DragDeltaEventArgs e)
 {
var thumb = (Thumb)sender;
double newLeft = Canvas.GetLeft(thumb) + e.HorizontalChange;
double newTop = Canvas.GetTop(thumb) + e.VerticalChange;
double canvasRight = canvas.ActualWidth - thumb.ActualWidth;
double canvasBottom = canvas.ActualHeight - thumb.ActualHeight;

if (newLeft < 0)
 newLeft = 0;
else if (newLeft > canvasRight)
 newLeft = canvasRight;

if (newTop < 0)
 newTop = 0;
else if (newTop > canvasBottom)
 newTop = canvasBottom;

 Canvas.SetLeft(thumb, newLeft);
 Canvas.SetTop(thumb, newTop);

 GetAreaColor();
 }
           

5)新增

canvas_MouseLeftButtonDown

代碼如下:

  • 實作滑鼠左鍵按下時,将 Thumb 控件移動到滑鼠點選位置,并進行邊界限制。同時,還擷取了滑鼠點選位置的顔色資訊
private void canvas_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
 {
var canvasPosition = e.GetPosition(canvas);
double newLeft = canvasPosition.X - thumb.ActualWidth / 2;
double newTop = canvasPosition.Y - thumb.ActualHeight / 2;

double canvasRight = canvas.ActualWidth - thumb.ActualWidth;
double canvasBottom = canvas.ActualHeight - thumb.ActualHeight;

if (newLeft < 0)
 newLeft = 0;
else if (newLeft > canvasRight)
 newLeft = canvasRight;

if (newTop < 0)
 newTop = 0;
else if (newTop > canvasBottom)
 newTop = canvasBottom;

 Canvas.SetLeft(thumb, newLeft);
 Canvas.SetTop(thumb, newTop);
var thumbPosition = e.GetPosition(canvas);
 GetAreaColor(thumbPosition);
 }
           

5)新增

GetAreaColor

代碼如下:

  • Thumb

    控件的中心點坐标轉換為相對于

    Canvas

    的坐标。
  • 計算每行像素資料所占的位元組數。
  • 建立一個位元組數組,用于存儲位圖的像素資料。
  • 将位圖的像素資料複制到位元組數組中。
  • 計算要通路的像素在位元組數組中的索引位置。
  • Color.FromArgb

    取其

    Alpha

    、紅色、綠色和藍色通道的值。在這段代碼中,它被用于構造一個

    Color

    對象,表示位圖中特定像素的顔色。
    • pixels[pixelIndex + 3]

      表示位元組數組中的第

      pixelIndex + 3

      個元素,即 Alpha 通道的值。
    • pixels[pixelIndex + 2]

      表示位元組數組中的第

      pixelIndex + 2

      個元素,即紅色通道的值。
    • pixels[pixelIndex + 1]

      表示位元組數組中的第

      pixelIndex + 1

      個元素,即綠色通道的值。
    • pixels[pixelIndex]

      表示位元組數組中的第

      pixelIndex

      個元素,即藍色通道的值。
void GetAreaColor(Point? thumbPosition = )
 {
 thumbPosition = thumbPosition ==  ? thumbPosition = thumb.TranslatePoint(new Point(thumb.ActualWidth / 2, thumb.ActualHeight / 2), canvas) : thumbPosition;
int xCoordinate = (int)thumbPosition?.X;
int yCoordinate = (int)thumbPosition?.Y;

if (xCoordinate >= 0 && xCoordinate < Bitmap.PixelWidth && yCoordinate >= 0 && yCoordinate < Bitmap.PixelHeight)
 {
int stride = Bitmap.PixelWidth * (Bitmap.Format.BitsPerPixel / 8);
byte[] pixels = new byte[Bitmap.PixelHeight * stride];
 Bitmap.CopyPixels(new Int32Rect(0, 0, Bitmap.PixelWidth, Bitmap.PixelHeight), pixels, stride, 0);
int pixelIndex = (yCoordinate * stride) + (xCoordinate * (Bitmap.Format.BitsPerPixel / 8));
 Color color = Color.FromArgb(pixels[pixelIndex + 3], pixels[pixelIndex + 2], pixels[pixelIndex + 1], pixels[pixelIndex]);
 MyBtn.Background = new SolidColorBrush(color);
 }
 }
           
WPF 簡單實作顔色選擇器

碼雲[2]

參考資料

[1]

原文連結: https://github.com/yanjinhuagood/ColorPickerSample

[2]

碼雲: https://gitee.com/yanjinhua/ColorPickerSample

繼續閱讀