我想创建一个GraphicsPath和一个点列表,以形成位图非透明区域的轮廓。如果需要,我可以保证每个图像只有一个不透明像素的实心集合。例如,我应该能够沿着像素边缘顺时针或逆时针记录点并执行完整的闭环。
这个算法的速度不重要。然而,如果我可以跳过一些点来减少较小且不太复杂的GraphicsPath,则结果点的效率是半重要的。
我将列出当前的代码,它在大多数图像上都能正常工作。然而,一些更复杂的图像最终会产生连接顺序错误的路径。我知道为什么会发生这种情况,但我想不出解决方案。
这里有一个示例展示了我的需求。红色轮廓应该是一组点的数组,可以制作成GraphicsPath来进行命中检测、绘制轮廓笔和用刷子填充它。
这个算法的速度不重要。然而,如果我可以跳过一些点来减少较小且不太复杂的GraphicsPath,则结果点的效率是半重要的。
我将列出当前的代码,它在大多数图像上都能正常工作。然而,一些更复杂的图像最终会产生连接顺序错误的路径。我知道为什么会发生这种情况,但我想不出解决方案。
public static Point[] GetOutlinePoints(Bitmap image)
{
List<Point> outlinePoints = new List<Point>();
BitmapData bitmapData = image.LockBits(new Rectangle(0, 0, image.Width, image.Height), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
byte[] originalBytes = new byte[image.Width * image.Height * 4];
Marshal.Copy(bitmapData.Scan0, originalBytes, 0, originalBytes.Length);
for (int x = 0; x < bitmapData.Width; x++)
{
for (int y = 0; y < bitmapData.Height; y++)
{
byte alpha = originalBytes[y * bitmapData.Stride + 4 * x + 3];
if (alpha != 0)
{
Point p = new Point(x, y);
if (!ContainsPoint(outlinePoints, p))
outlinePoints.Add(p);
break;
}
}
}
for (int y = 0; y < bitmapData.Height; y++)
{
for (int x = bitmapData.Width - 1; x >= 0; x--)
{
byte alpha = originalBytes[y * bitmapData.Stride + 4 * x + 3];
if (alpha != 0)
{
Point p = new Point(x, y);
if (!ContainsPoint(outlinePoints, p))
outlinePoints.Add(p);
break;
}
}
}
for (int x = bitmapData.Width - 1; x >= 0; x--)
{
for (int y = bitmapData.Height - 1; y >= 0; y--)
{
byte alpha = originalBytes[y * bitmapData.Stride + 4 * x + 3];
if (alpha != 0)
{
Point p = new Point(x, y);
if (!ContainsPoint(outlinePoints, p))
outlinePoints.Add(p);
break;
}
}
}
for (int y = bitmapData.Height - 1; y >= 0; y--)
{
for (int x = 0; x < bitmapData.Width; x++)
{
byte alpha = originalBytes[y * bitmapData.Stride + 4 * x + 3];
if (alpha != 0)
{
Point p = new Point(x, y);
if (!ContainsPoint(outlinePoints, p))
outlinePoints.Add(p);
break;
}
}
}
// Added to close the loop
outlinePoints.Add(outlinePoints[0]);
image.UnlockBits(bitmapData);
return outlinePoints.ToArray();
}
public static bool ContainsPoint(IEnumerable<Point> points, Point value)
{
foreach (Point p in points)
{
if (p == value)
return true;
}
return false;
}
当我将点转换为路径时:
GraphicsPath outlinePath = new GraphicsPath();
outlinePath.AddLines(_outlinePoints);
这里有一个示例展示了我的需求。红色轮廓应该是一组点的数组,可以制作成GraphicsPath来进行命中检测、绘制轮廓笔和用刷子填充它。