如何在WritableBitmap + WritablebitmapEx上绘制弧线
本文关键字:绘制 WritablebitmapEx WritableBitmap | 更新日期: 2023-09-27 18:18:55
我在WPF应用程序中使用WritableBitmapEx在WritableBitmap上绘制了很多形状。不幸的是,没有现成的函数来在它上面画一个弧。
1。在WritableBitmap上画一个弧?
2。在WritableBitmap上绘制一个可变厚度的抗锯齿弧?
我只需要画圆弧。
有可能画一个漂亮的,抗锯齿弧与可变厚度(System.Windows.Media.ArcSegment)在画布上-但是成千上万的形状的画布的性能很差-这就是为什么我使用WritableBitmap
如果某些算法需要,我已经计算过弧参数,如:中心点、半径、起始点、端点、起始角、结束角、arlength、IsLarge或Direction
我试图用类似的代码手动绘制它:
int number_of_points = 1000;
for(int i=0; i<=number_of_points; i++){
double progress=(double)i/number_of_points;
double theta = (StartAngle + ArcLength * progress) * Math.PI / 180.0;
draw_pixel(
Center.X + Radius * Math.Cos(theta),
Center.Y + Radius * Math.Sin(theta)
);
}
但是随着图像分辨率的变化,弧的大小变化(如何计算最佳的number_of_points?),弧的厚度变化以及抗锯齿,它开始变得有点棘手。
在WritableBitmap上画一个弧?
在分析了github上的mono libgdiplus源代码后,我发现他们正在使用Bezier曲线绘制弧。
我已经将它们的一些函数移植到了c#中。可以使用drawwarc扩展函数(在WritableBitmapEx的DrawBezier的帮助下)来绘制简单的弧线。在WritableBitmapEx中没有抗锯齿版本的DrawBezier所以这个解决方案回答(仅)我的第一个问题:namespace System.Windows.Media.Imaging
{
public static partial class WriteableBitmapArcExtensions
{
//port of mono libgdiplus function
//append_arcs (GpPath *path, float x, float y, float width, float height, float startAngle, float sweepAngle)
//from: https://github.com/mono/libgdiplus/blob/master/src/graphics-path.c
public static void DrawArc(this WriteableBitmap bmp, float x, float y, float width, float height, float startAngle, float sweepAngle, Color color)
{
int i;
float drawn = 0;
int increment;
float endAngle;
bool enough = false;
if (Math.Abs(sweepAngle) >= 360)
{
bmp.DrawEllipse((int)x, (int)y, (int)width, (int)height, color);
return;
}
endAngle = startAngle + sweepAngle;
increment = (endAngle < startAngle) ? -90 : 90;
/* i is the number of sub-arcs drawn, each sub-arc can be at most 90 degrees.*/
/* there can be no more then 4 subarcs, ie. 90 + 90 + 90 + (something less than 90) */
for (i = 0; i < 4; i++)
{
float current = startAngle + drawn;
float additional;
if (enough)
return;
additional = endAngle - current; /* otherwise, add the remainder */
if (Math.Abs(additional) > 90)
{
additional = increment;
}
else
{
/* a near zero value will introduce bad artefact in the drawing */
if ((additional >= -0.0001f) && (additional <= 0.0001f))
return;
enough = true;
}
bmp._DrawArc(
x, y,
width, height, /* bounding rectangle */
current, current + additional, color);
drawn += additional;
}
}
//port of mono libgdiplus function
//append_arc (GpPath *path, BOOL start, float x, float y, float width, float height, float startAngle, float endAngle)
//from: https://github.com/mono/libgdiplus/blob/master/src/graphics-path.c
private static void _DrawArc(this WriteableBitmap bmp, float x, float y, float width, float height, float startAngle, float endAngle, Color color)
{
double sin_alpha, sin_beta, cos_alpha, cos_beta;
var rx = width / 2;
var ry = height / 2;
/* center */
var cx = x + rx;
var cy = y + ry;
/* angles in radians */
var alpha = startAngle * Math.PI / 180;
var beta = endAngle * Math.PI / 180;
/* adjust angles for ellipses */
alpha = Math.Atan2(rx * Math.Sin(alpha), ry * Math.Cos(alpha));
beta = Math.Atan2(rx * Math.Sin(beta), ry * Math.Cos(beta));
if (Math.Abs(beta - alpha) > Math.PI)
{
if (beta > alpha)
beta -= 2 * Math.PI;
else
alpha -= 2 * Math.PI;
}
var delta = beta - alpha;
// http://www.stillhq.com/ctpfaq/2001/comp.text.pdf-faq-2001-04.txt (section 2.13)
var bcp = 4.0 / 3 * (1 - Math.Cos(delta / 2)) / Math.Sin(delta / 2);
sin_alpha = Math.Sin(alpha);
sin_beta = Math.Sin(beta);
cos_alpha = Math.Cos(alpha);
cos_beta = Math.Cos(beta);
/* starting point */
double sx = cx + rx * cos_alpha;
double sy = cy + ry * sin_alpha;
//DrawBezier comes from WritableBitmapEx library
bmp.DrawBezier(
(int)(sx),
(int)(sy),
(int)(cx + rx * (cos_alpha - bcp * sin_alpha)),
(int)(cy + ry * (sin_alpha + bcp * cos_alpha)),
(int)(cx + rx * (cos_beta + bcp * sin_beta)),
(int)(cy + ry * (sin_beta - bcp * cos_beta)),
(int)(cx + rx * cos_beta),
(int)(cy + ry * sin_beta),
color
);
}
}
}
我在WritableBitmapEx网站上评论了一个问题:我想画弧线——所以也许这部分代码会包含在WritableBitmapEx库中。
2。在WritableBitmap上绘制一个可变厚度的抗锯齿弧?
在阅读了来自ForeverZer0的评论后,我用System.Drawing.Graphics和WritableBitmap做了一些实验。在获得wpf WriteableBitmap的DrawingContext的帮助下,我用这样的代码完成了它:
WritableBitmap ret = BitmapFactory.New(img_width, img_height);
ret.Lock();
var bmp = new System.Drawing.Bitmap(
ret.PixelWidth,
ret.PixelHeight,
ret.BackBufferStride,
System.Drawing.Imaging.PixelFormat.Format32bppPArgb,
ret.BackBuffer
);
System.Drawing.Graphics g = System.Drawing.Graphics.FromImage(bmp);
g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
g.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.HighQuality;
g.DrawArc(...); //<-- draws an antialiased arc with variable thickness
g.Dispose();
bmp.Dispose();
ret.AddDirtyRect(new Int32Rect(0, 0, ret.PixelWidth, ret.PixelHeight));
ret.Unlock();
return ret; //<-- WritableBitmap with beautifull arc on it;