通过颜色查找像素坐标(彩色选择器控制)
我目前正在尝试修改彩色选择器控件。一切似乎都按预期工作。但是,我希望有可能在初始化时设置“ selectedcolor”。因此,流如下:
- 选择所需的颜色
- 保存在首选项中
- ,请关闭应用程序
- 打开应用程序再次
- 在先前选择的颜色上初始化了当前
选择器的颜色,仅对Pointer X和Y的坐标进行了考虑。这意味着如果我将提供先前选择的彩色拾取器控制的颜色将无法将指针放在正确的位置,因为它正在等待X,Y坐标而不是颜色。我有一个工作障碍,将所有需要的参数保存到字符串(颜色十六进制代码,以及X和Y坐标)中。它正在工作,但是这增加了与字符串结合,然后在ViewModels中解析的其他复杂性。
我已经熟悉阅读像素,寻找所需颜色并获得其坐标的可能性。以下是一些问题:
- 用于阅读像素的环路迭代冻结了UI,尤其是对于较大的彩色拾取器(大图像)
- 并不总是
- 在初始化期间提供正确的坐标。黑色和白色#00000000和#ffffffff的问题。因此,我将它们添加到If方法中。看起来彩色拾取器实际生成的图像是黑白的吗?在实际情况下,这当然不是一个好的解决方案,因为挑选的颜色可以是白色或黑色:
if(this.pickedcolor.toskcolor()== pixelcolor && this.pickedcolor.toskcolor()!= color.fromhex(“#00000000”)。toskcolor() && this.pickedcolor.toskcolor()!= color.fromhex(“#ffffffff”)。toskcolor()) { //this.selectedpoint =新点(x,y); debug.writeline(string.format(“颜色:{0} |坐标:{1} {2}”,pixelcolor,x,y)); }
这是颜色拾取器控件的OnPaintSurface方法(您可以在底部方法看到此.getPixelCoordinates(bitmap); the;
protected override void OnPaintSurface(SKPaintSurfaceEventArgs e)
{
SKImageInfo skImageInfo = e.Info;
SKSurface skSurface = e.Surface;
this.SKCanvas = skSurface.Canvas;
int skCanvasWidth = skImageInfo.Width;
int skCanvasHeight = skImageInfo.Height;
this.SKCanvas.Clear();
// Draw gradient rainbow Color spectrum
using (SKPaint paint = new SKPaint())
{
paint.IsAntialias = true;
System.Collections.Generic.List<SKColor> colors = new System.Collections.Generic.List<SKColor>();
this.ColorList.ForEach((color) => { colors.Add(Color.FromHex(color).ToSKColor()); });
// create the gradient shader between Colors
using (SKShader shader = SKShader.CreateLinearGradient(
new SKPoint(0, 0),
this.ColorListDirection == ColorListDirection.Horizontal ?
new SKPoint(skCanvasWidth, 0) : new SKPoint(0, skCanvasHeight),
colors.ToArray(),
null,
SKShaderTileMode.Clamp))
{
paint.Shader = shader;
this.SKCanvas.DrawPaint(paint);
}
}
// Draw darker gradient spectrum
using (SKPaint paint = new SKPaint())
{
paint.IsAntialias = true;
// Initiate the darkened primary color list
SKColor[] colors = GetGradientOrder();
// create the gradient shader
using (SKShader shader = SKShader.CreateLinearGradient(
new SKPoint(0, 0),
this.ColorListDirection == ColorListDirection.Horizontal ?
new SKPoint(0, skCanvasHeight) : new SKPoint(skCanvasWidth, 0),
colors,
null,
SKShaderTileMode.Clamp))
{
paint.Shader = shader;
this.SKCanvas.DrawPaint(paint);
}
}
// Picking the Pixel Color values on the Touch Point
// Represent the color of the current Touch point
SKColor touchPointColor;
// Efficient and fast
// https://forums.xamarin.com/discussion/92899/read-a-pixel-info-from-a-canvas
// create the 1x1 bitmap (auto allocates the pixel buffer)
using (SKBitmap bitmap = new SKBitmap(skImageInfo))
{
// get the pixel buffer for the bitmap
IntPtr dstpixels = bitmap.GetPixels();
// read the surface into the bitmap
skSurface.ReadPixels(skImageInfo,
dstpixels,
skImageInfo.RowBytes,
(int)this.SelectedPoint.X,
(int)this.SelectedPoint.Y);
// access the color
touchPointColor = bitmap.GetPixel(0, 0);
//this.GetPixelCoordinates(bitmap);
//bitmap.SetPixel(50, 50, this.PickedColor.ToSKColor());
}
这是getPixelCoordinates方法:
private void GetPixelCoordinates(SKBitmap bitmap)
{
if (bitmap == null)
{
return;
}
for (int x = 0; x < bitmap.Width; x++)
{
for (int y = 0; y < bitmap.Height; y++)
{
SKColor pixelColor = bitmap.GetPixel(x, y);
if (this.PickedColor.ToSKColor() == pixelColor
&& this.PickedColor.ToSKColor() != Color.FromHex("#00000000").ToSKColor()
&& this.PickedColor.ToSKColor() != Color.FromHex("#FFFFFFFF").ToSKColor())
{
//this.SelectedPoint = new Point(x, y);
Debug.WriteLine(String.Format("Color: {0} | Coordinate: {1} {2}", pixelColor, x, y));
}
}
}
}
这里是pickEdColor属性:
public static readonly BindableProperty PickedColorProperty
= BindableProperty.Create(
propertyName: nameof(PickedColor),
returnType: typeof(Color),
declaringType: typeof(ColorPicker),
defaultValue: Color.Green,
defaultBindingMode: BindingMode.TwoWay,
propertyChanged: OnColorChanged);
private static void OnColorChanged(BindableObject bindable, object oldValue, object newValue)
{
ColorPicker control = (ColorPicker)bindable;
control.PickedColor = (Color)newValue;
}
/// <summary>
/// Set the Color Spectrum Gradient Style
/// </summary>
public GradientColorStyle GradientColorStyle
{
get { return (GradientColorStyle)GetValue(GradientColorStyleProperty); }
set { SetValue(GradientColorStyleProperty, value); }
}
public static readonly BindableProperty ColorListProperty
= BindableProperty.Create(
propertyName: nameof(ColorList),
returnType: typeof(string[]),
declaringType: typeof(ColorPicker),
defaultValue: new string[]
{
new Color(255, 0, 0).ToHex(), // Red
new Color(255, 255, 0).ToHex(), // Yellow
new Color(0, 255, 0).ToHex(), // Green (Lime)
new Color(0, 255, 255).ToHex(), // Aqua
new Color(0, 0, 255).ToHex(), // Blue
new Color(255, 0, 255).ToHex(), // Fuchsia
new Color(255, 0, 0).ToHex(), // Red
},
defaultBindingMode: BindingMode.OneTime, null);
我的问题是:我的问题是:唯一生成carameter走路(颜色十六进制代码,以及X和Y坐标)?是否有可能以某种有效的方式将指针放在控制初始化的情况下,而无需持续的循环迭代和冻结UI?
调色板:
I am currently trying to modify color picker control. Everything seems to be working as expected. However I would like to have possibility to set "selectedColor" on initialization. So that flow would be as follows:
- Pick needed color
- Save it in Preferences
- Close application
- Open application again
- Color picker is initialized on previously selected color
Currently picker is taking in account only coordinates of pointer X and Y. This means if I will provide previously selected color for Color picker control it will not be able to place pointer in a right place, because it is waiting for X, Y coordinates and not color. I have got a work-around where I save all needed parameters into string (Color Hex code, as well as X and Y coordinates). It is working, however this is adding additional complexity for combining strings and then parsing them inside ViewModels.
I have been getting familiar with possibility to read pixels, searching for needed color and getting it's coordinates. Here are some problems:
- Loop iteration for reading pixels is freezing UI, especially for larger color pickers (large image)
- Not always providing correct coordinates
- During initialization there is problem with black and white colors #00000000 and #FFFFFFFF. So I have added them into if method. It looks like before color picker is actually generated image is black and white? This is of course not a good solution in real case scenario as picked color can be white or black:
if (this.PickedColor.ToSKColor() == pixelColor && this.PickedColor.ToSKColor() != Color.FromHex("#00000000").ToSKColor() && this.PickedColor.ToSKColor() != Color.FromHex("#FFFFFFFF").ToSKColor()) { //this.SelectedPoint = new Point(x, y); Debug.WriteLine(String.Format("Color: {0} | Coordinate: {1} {2}", pixelColor, x, y)); }
Here is OnPaintSurface method of Color Picker control (you can see at the bottom method this.GetPixelCoordinates(bitmap); that is commented out):
protected override void OnPaintSurface(SKPaintSurfaceEventArgs e)
{
SKImageInfo skImageInfo = e.Info;
SKSurface skSurface = e.Surface;
this.SKCanvas = skSurface.Canvas;
int skCanvasWidth = skImageInfo.Width;
int skCanvasHeight = skImageInfo.Height;
this.SKCanvas.Clear();
// Draw gradient rainbow Color spectrum
using (SKPaint paint = new SKPaint())
{
paint.IsAntialias = true;
System.Collections.Generic.List<SKColor> colors = new System.Collections.Generic.List<SKColor>();
this.ColorList.ForEach((color) => { colors.Add(Color.FromHex(color).ToSKColor()); });
// create the gradient shader between Colors
using (SKShader shader = SKShader.CreateLinearGradient(
new SKPoint(0, 0),
this.ColorListDirection == ColorListDirection.Horizontal ?
new SKPoint(skCanvasWidth, 0) : new SKPoint(0, skCanvasHeight),
colors.ToArray(),
null,
SKShaderTileMode.Clamp))
{
paint.Shader = shader;
this.SKCanvas.DrawPaint(paint);
}
}
// Draw darker gradient spectrum
using (SKPaint paint = new SKPaint())
{
paint.IsAntialias = true;
// Initiate the darkened primary color list
SKColor[] colors = GetGradientOrder();
// create the gradient shader
using (SKShader shader = SKShader.CreateLinearGradient(
new SKPoint(0, 0),
this.ColorListDirection == ColorListDirection.Horizontal ?
new SKPoint(0, skCanvasHeight) : new SKPoint(skCanvasWidth, 0),
colors,
null,
SKShaderTileMode.Clamp))
{
paint.Shader = shader;
this.SKCanvas.DrawPaint(paint);
}
}
// Picking the Pixel Color values on the Touch Point
// Represent the color of the current Touch point
SKColor touchPointColor;
// Efficient and fast
// https://forums.xamarin.com/discussion/92899/read-a-pixel-info-from-a-canvas
// create the 1x1 bitmap (auto allocates the pixel buffer)
using (SKBitmap bitmap = new SKBitmap(skImageInfo))
{
// get the pixel buffer for the bitmap
IntPtr dstpixels = bitmap.GetPixels();
// read the surface into the bitmap
skSurface.ReadPixels(skImageInfo,
dstpixels,
skImageInfo.RowBytes,
(int)this.SelectedPoint.X,
(int)this.SelectedPoint.Y);
// access the color
touchPointColor = bitmap.GetPixel(0, 0);
//this.GetPixelCoordinates(bitmap);
//bitmap.SetPixel(50, 50, this.PickedColor.ToSKColor());
}
Here is GetPixelCoordinates method:
private void GetPixelCoordinates(SKBitmap bitmap)
{
if (bitmap == null)
{
return;
}
for (int x = 0; x < bitmap.Width; x++)
{
for (int y = 0; y < bitmap.Height; y++)
{
SKColor pixelColor = bitmap.GetPixel(x, y);
if (this.PickedColor.ToSKColor() == pixelColor
&& this.PickedColor.ToSKColor() != Color.FromHex("#00000000").ToSKColor()
&& this.PickedColor.ToSKColor() != Color.FromHex("#FFFFFFFF").ToSKColor())
{
//this.SelectedPoint = new Point(x, y);
Debug.WriteLine(String.Format("Color: {0} | Coordinate: {1} {2}", pixelColor, x, y));
}
}
}
}
Here is PickedColor property:
public static readonly BindableProperty PickedColorProperty
= BindableProperty.Create(
propertyName: nameof(PickedColor),
returnType: typeof(Color),
declaringType: typeof(ColorPicker),
defaultValue: Color.Green,
defaultBindingMode: BindingMode.TwoWay,
propertyChanged: OnColorChanged);
private static void OnColorChanged(BindableObject bindable, object oldValue, object newValue)
{
ColorPicker control = (ColorPicker)bindable;
control.PickedColor = (Color)newValue;
}
/// <summary>
/// Set the Color Spectrum Gradient Style
/// </summary>
public GradientColorStyle GradientColorStyle
{
get { return (GradientColorStyle)GetValue(GradientColorStyleProperty); }
set { SetValue(GradientColorStyleProperty, value); }
}
public static readonly BindableProperty ColorListProperty
= BindableProperty.Create(
propertyName: nameof(ColorList),
returnType: typeof(string[]),
declaringType: typeof(ColorPicker),
defaultValue: new string[]
{
new Color(255, 0, 0).ToHex(), // Red
new Color(255, 255, 0).ToHex(), // Yellow
new Color(0, 255, 0).ToHex(), // Green (Lime)
new Color(0, 255, 255).ToHex(), // Aqua
new Color(0, 0, 255).ToHex(), // Blue
new Color(255, 0, 255).ToHex(), // Fuchsia
new Color(255, 0, 0).ToHex(), // Red
},
defaultBindingMode: BindingMode.OneTime, null);
My question is: Is generating string with parameters the only way to go (Color Hex code, as well as X and Y coordinates)? Is there some possibility to place pointer on control initialization by provided color in some efficient way without constant loop iteration and freeze of UI?
Color palette:
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
Stackoverflow专为每个帖子而设计,以询问和回答一个编码问题。
(要问第二个问题,请撰写新帖子。在该帖子中包括理解该问题所需的基本细节。链接回这个问题,因此您不必重复提供其他背景/上下文的信息。)
您的主要问题可以说:
首先,观察。该2D调色板给出3个颜色轴中的2个。您将需要单独的“饱和”滑块,以允许选择任何颜色。
您显示的调色板是“ HSV”颜色模型的近似。
在 wiki hsl-hsv型号,单击右图。 矩形
您的调色板看起来像是标有S(HSV)=1。Hue +饱和 +值的 。
您的配色列表应具有最大值的完全饱和颜色。
从屏幕上行驶,调色板将价值降低到接近零。
这是答案的开始。
需要的是一个数学公式,与绘制的内容相对应。
让我们看看如何生成该矩形图像。
重命名颜色列表,因此更容易使用。
存储为字段,因此可以稍后使用。使用原始的
颜色
,从中生成skcolors
,以便于操作。顶行(y = 0)具有
饱和度
,在x上均匀间隔。底部行(y = maxy)具有
darkenedColors
,均匀跨越x。从顶行到底部的像素颜色是线性插值的。
目标是找到最接近给定颜色的像素,“
颜色goalcolor
”。考虑每个高的,薄的矩形,它们的角落为两个底色和相应的两个底色。目标是查找哪个矩形包含守门员,然后在该矩形中找到最接近守门颜色的矩形的像素。
最棘手的部分是“比较”颜色,以确定两种颜色之间的颜色何时”。在RGB中很难做到这一点。将颜色转换为HSV以匹配您使用的调色板。参见 Greg的答案-Colortohsv 。
如果您进行HSV类:
用法:
这是方法的本质,则更容易。来自
colorIndex
,ncolors
,wgtb
和maxx
,可以计算x
。我建议写几个测试用例,以弄清楚如何做。计算
y
要简单得多。应该使用GOORHSV.V
和Maxy
可以使用。如您所见,这对代码并不微不足道。
最重要的一点:
GOORHSV
在之间。如果以上数学太激烈,则只用其中一个矩形绘制一个盒子。也就是说,在顶部列表中仅具有两种颜色的梯度。试图在该矩形内定位一个颜色像素。
重要的是:可能没有像素正是您要开始的颜色。查找哪个像素最接近该颜色。
如果您不确定自己具有“最佳”像素,请阅读附近的一些像素,请确定哪个是“最接近的”。也就是说,具有最小的
var error =(r2-r1)*(r2-r1) +(g2-g1)*(g2-g1) +(b2-b1)*(b2-b1)*(b2-b1); << /代码>。
StackOverflow is designed for each post to ask and answer one coding question.
(To ask a second question, please make a new post. Include in that post the essential details needed to understand that question. Link back to this question, so you don't have to repeat the information that gives additional background/context.)
Your primary question can be stated as:
First, an observation. That 2D palette gives 2 of 3 color axes. You'll need a separate "saturation" slider, to allow picking of any color.
The palette you show is an approximation to an "HSV" color model.
In wiki HSL-HSV models, click on diagram at right. Your palette looks like the rectangle labeled S(HSV) = 1.
Hue + Saturation + Value.
Your ColorList should have fully Saturated colors at max Value.
Going down the screen, the palette reduces Value to near zero.
This is the beginning of an answer to that.
What is needed, is a mathematical formula that corresponds to what is drawn.
Lets look at how that rectangular image was generated.
Rename the color lists so easier to work with.
Store as fields, so can use them later. Use the original
Colors
from which theSkColors
were generated, for easier manipulation.The top row (y=0) has
saturatedColors
, evenly spaced across x.The bottom row (y=maxY) has
darkenedColors
, evenly spaced across x.The pixel colors are linearly interpolated from top row to bottom row.
Goal is to find pixel closest to a given color, "
Color goalColor
".Consider each tall, thin rectangle whose corners are two topColors and the corresponding two bottomColors. Goal is to find which rectangle contains goalColor, then find the pixel within that rectangle that is closest to goalColor.
The trickiest part is "comparing" colors, to decide when a color is "between" two colors. This is hard to do in RGB; convert colors to HSV to match the palette you are using. See Greg's answer - ColorToHSV.
Its easier if you make an HSV class:
Usage:
This is the essence of the approach. From
colorIndex
,nColors
,wgtB
, andmaxX
, it is possible to calculatex
. I recommend writing several test cases, to figure out how to do so.Calculating
y
is much simpler. Should be possible usinggoalHSV.V
andmaxY
.As you can see, this is not trivial to code.
The most important points:
goalHSV
is between.If the above math is too intense, then draw a box with just one of those rectangles. That is, make the gradient with only TWO colors in the top list. Experiment with trying to locate a color pixel within that rectangle.
IMPORTANT: There might not be a pixel that is EXACTLY the color you are starting with. Find which pixel is closest to that color.
If you aren't sure you have the "best" pixel, then read a few nearby pixels, decide which is "closest". That is, which has the smallest
var error = (r2-r1)*(r2-r1) + (g2-g1)*(g2-g1) + (b2-b1)*(b2-b1);
.