我有一个像心脏或任何随机形状的不规则形状的图片。 我可以在视觉上使它透明,但我需要让它只在形状区域上可点击。我听说我应该使用“Region”,但我无法弄清楚如何。
我尝试搜索所有非空,透明或空的像素并使用它们创建点阵列,但我无法创建/重塑当前控制区域。我正在尝试制作一个自定义控件,你可以选择一个按钮或图片,它们是不规则形状并且彼此靠近。
这是我正在处理的事情:
如图所示,有8个不同的部分(假设右侧和左侧组合)。正如你所看到的,它们彼此接近,其中一些甚至适合其他人之间的空白区域。
我的目标是,例如,如果我点击了Pectorals(图中的红色区域),它将变为彩色版本,并且会运行一堆其他代码。
问题是,默认情况下,当我们使用PictureBox
添加任何图片时,它会从该边框开始围绕该图片创建Rectangle
。因此,如果我将两张图片(如图所示)放在一起,则一个空区域会阻止我点击另一张图片。
由于这个问题,它也会引发错误的对象ClickEvent
。
我正在尝试设置“提升事件区域”,我假设我们在图片存在的地方称它为Graphic Region
。我可以用一个循环来收集像素的位置,该循环确定该图片的哪些坐标具有“颜色”(意味着它是图片的一部分,我想要点击的区域)但我不能用该数据限制该区域。
我正在努力实现的一个例子:https://www.youtube.com/watch?v=K_JzL4kzCoE
这样做的最佳方式是什么?
答案 0 :(得分:5)
这是解决此问题的两种方法:
使用Regions
。
使用透明Images
。
第一种方法涉及创建控件,例如PictureBoxes
或Panels
,它们具有图像的形状,并且只能在该形状内单击。
这很好,只要您可以访问构成形状的矢量轮廓。
以下示例将Region
的可见和可点击的Panel
限制为从跟踪点列表创建的不规则形状的blob:
List<Point> points = new List<Point>();
points.Add(new Point(50,50));points.Add(new Point(60,65));points.Add(new Point(40,70));
points.Add(new Point(50,90));points.Add(new Point(30,95));points.Add(new Point(20,60));
points.Add(new Point(40,55));
using (GraphicsPath gp = new GraphicsPath())
{
gp.AddClosedCurve(points.ToArray());
panel1.Region = new Region(gp);
}
不幸的是,从其中包含的点开始Region
将无效;想象一个Region
作为矢量形状的列表,它们由点组成,但仅用于创建包含向量,而不是像素..
你可以追踪形状,但这是很多工作,而且不值得。
因此,如果您没有矢量形状:请转到第二种方法:
这将假设您有图像(可能是PNG),这些图像在所有不应接受任何点击的位置都是透明的。
最简单和最有效的方法是将它们与它们所在的点一起列入一个列表;然后,每当他们改变时,将它们全部绘制成一个图像,您可以将其分配给PictureBox.Image
。
这是一个Mouseclick
事件,它会搜索图像列表中最顶层的Image
以查找被点击的事件。要将它们与位置组合,我使用元组列表:
List<Tuple<Image, Point>> imgList = new List<Tuple<Image, Point>>();
我们在每个MouseClick
中搜索此列表:
private void pictureBox1_MouseClick(object sender, MouseEventArgs e)
{
int found = -1;
// I search backward because I drew forward:
for (int i = imageList1.Images.Count - 1; i >= 0; i--)
{
Bitmap bmp = (Bitmap) imgList[i].Item1;
Point pt = (Point) imgList[i].Item2;
Point pc = new Point(e.X - pt.X, e.Y - pt.Y);
Rectangle bmpRect = new Rectangle(pt.X, pt.Y, bmp.Width, bmp.Height);
// I give a little slack (11) but you could also check for > 0!
if (bmpRect.Contains(e.Location) && bmp.GetPixel(pc.X, pc.Y).A > 11)
{ found = i; break; }
}
// do what you want with the found image..
// I show the image in a 2nd picBox and its name in the form text:
if (found >= 0) {
pictureBox2.Image = imageList1.Images[found];
Text = imageList1.Images.Keys[found];
}
}
以下是我将这些图像合二为一的方法。请注意,对于测试,我已将它们添加到ImageList
对象中。这具有严重的缺点,因为所有图像都缩放到通用尺寸。你可能想要创建一个适合自己的列表!
Bitmap patchImages()
{
Bitmap bmp = new Bitmap(pictureBox1.ClientSize.Width, pictureBox1.ClientSize.Height);
imgList.Clear();
Random R = new Random(1);
using (Graphics G = Graphics.FromImage(bmp) )
{
foreach (Image img in imageList1.Images)
{
// for testing: put each at a random spot
Point pt = new Point(R.Next(333), R.Next(222));
G.DrawImage(img, pt);
// also add to the searchable list:
imgList.Add(new Tuple<Image, Point>(img, pt));
}
}
return bmp;
}
我在启动时打电话给它:
private void Form1_Load(object sender, EventArgs e)
{
pictureBox1.Image = patchImages();
}
除此之外:这种将所有图像合成为一种图像的方式也是唯一可以让您自由重叠图像的方法。 Winforms
不支持重叠控件的真实透明度。并且为每个形状测试一个Pixel
(最多)也非常快。
答案 1 :(得分:1)
这是处理图像蒙版的Winforms示例。当用户点击掩码图像时,它会弹出一个消息框。这种基本技术显然可以修改以适应。
public partial class Form1 : Form {
readonly Color mask = Color.Black;
public Form1() {
InitializeComponent();
}
private void pictureBox1_Click(object sender, EventArgs e) {
var me = e as MouseEventArgs;
using (var bmp = new Bitmap(pictureBox1.Image)) {
if (me.X < pictureBox1.Image.Width && me.Y < pictureBox1.Image.Height) {
var colorAtMouse = bmp.GetPixel(me.X, me.Y);
if (colorAtMouse.ToArgb() == mask.ToArgb()) {
MessageBox.Show("Mask clicked!");
}
}
}
}
}
pictureBox1
从心形资源中加载了Image
,我是自由的。
答案 2 :(得分:0)