【问题标题】:Optimize drawing for paint application c#优化绘图应用程序 c#
【发布时间】:2012-04-24 23:50:04
【问题描述】:

我正在使用 c# 制作像 photoshop 这样的绘画项目。 我使用 GDI+ 进行绘图。遗憾的是,我无法发布所需声誉点的屏幕截图。 编辑:好的,我有足够的代表上传图片。

当画笔大小增加时,我使用鼠标进行的绘图会滞后。 我有一个画布缓冲区,它被绘制到画布面板

protected override void OnPaint(PaintEventArgs e)
    {
        if (canvasBuffer != null)
        {
            using (Graphics g = e.Graphics)
            {
                g.DrawImage(canvasBuffer, e.ClipRectangle, e.ClipRectangle, GraphicsUnit.Pixel);
            }
        }
    }

当任何绘画完成时都会发生这种情况。

  1. 我存储了鼠标拖动事件的点列表。
  2. 我从a点到b点画了一系列圆,以记录点为中心画一条平滑线
  3. 此绘图是在存储在图层类中的笔划列表中的位图上完成的。 这个绘图也是用 CompositingMode.SourceCopy 来实现 alpha 值绘图
  4. 我有一个 layerBuffer 来存储图层的最终图像。我通过使用 SourceCopy 兼容模式使用透明颜色的绘图清除它来将受笔触影响的更改绘制到此 layerBuffer,然后使用 SourceOver 在笔触列表中绘制位图
  5. 由于我正在实施的分层系统,我将所有图层缓冲区绘制到一个图片缓冲区。 这张图片Buffer最终通过缩放变换绘制到canvasBuffer中。

注意:画布缓冲区的受影响区域以与图层缓冲区相同的方式完成,即清除受影响的部分并重新绘制图片缓冲区的整个受影响部分。 如果我不清除之前的绘图,则使用 alpha 值绘图将无法按预期工作。

请帮助我优化此代码或提出一些新方法来提高性能并减少使用鼠标绘制时的延迟。 另外,将绘图代码与使用线程的缓冲区的点计算和绘图分开会有帮助吗?

这里是代码。

public void PSF_Painted(PSF_PaintEvent e)
    {
        Layer SelectedLayer = psf.Layers[0];//Get selected layer here
        if ((BrushTool)getActiveTool() != null)
        {
            //getting the pen attributes from the brush tool
            BrushTool brushTool = (BrushTool)getActiveTool();
            Pen pen = brushTool.getPen();
            Brush brush = pen.Brush;
            int brushSize = (int)pen.Width;
            //loading points data
            List<Point> recordedPoints = null;
            Point currentPoint = new Point(0, 0);
            Point previousPoint = new Point(0, 0);
            if (e.RecordedPoints != null)
            {
                recordedPoints = e.RecordedPoints;
                if (recordedPoints.Count > 1)
                {
                    currentPoint = recordedPoints[recordedPoints.Count - 1];
                    previousPoint = recordedPoints[recordedPoints.Count - 2];
                }
                else if (recordedPoints.Count == 1)
                {
                    currentPoint = recordedPoints[0];
                    previousPoint = currentPoint;
                }
            }
            if (e.PaintEventType == PSF_PaintEvent.StrokeStarted)
            {
                //Console.WriteLine("StrokeStarted");
                SelectedLayer.Strokes.Add(new Bitmap(SelectedLayer.Width, SelectedLayer.Height));
            }
            else if (e.PaintEventType == PSF_PaintEvent.Painting)
            {
                //Draw the drawing in the bitmap of the layer's stroke data
                using (Graphics g = Graphics.FromImage(SelectedLayer.Strokes[SelectedLayer.Strokes.Count - 1]))
                {
                    List<Point> points = Global.GetPointsOnLine(previousPoint.X, previousPoint.Y, currentPoint.X, currentPoint.Y);
                    for (int i = 0; i < points.Count; i++)
                    {
                        g.CompositingMode = System.Drawing.Drawing2D.CompositingMode.SourceCopy;
                        if (pen.Width == 1)
                        {
                            g.FillRectangle(brush, new Rectangle(points[i].X, points[i].Y, brushSize, brushSize));
                        }
                        else
                        {
                            g.FillEllipse(brush, new Rectangle(points[i].X, points[i].Y, brushSize, brushSize));
                        }
                        int xt, xb, yt, yb;
                        xt = points[i].X;
                        xb = points[i].X + brushSize;
                        yt = points[i].Y;
                        yb = points[i].Y + brushSize;

                        if (xt < 0) xt = 0;
                        if (xb > psf.Width) xb = SelectedLayer.Width;
                        if (yt < 0) yt = 0;
                        if (yb > psf.Height) yb = SelectedLayer.Height;

                        //Refresh changes to the affected part of the canvas buffer
                        Rectangle affectedRect = new Rectangle(xt, yt, xb - xt, yb - yt);
                        float zf = psf.ZoomFactor;
                        Rectangle canvasAffectedRect = new Rectangle((int)(affectedRect.X * zf), (int)(affectedRect.Y * zf), (int)(affectedRect.Size.Width * zf), (int)(affectedRect.Size.Height * zf));
                        SelectedLayer.invalidateLayerBuffer(affectedRect);
                        invalidateCanvasBuffer(canvasAffectedRect);
                    }
                }
            }
            else if (e.PaintEventType == PSF_PaintEvent.StrokeCompleted)
            {
                //Console.WriteLine("StrokeCompleted");
            }
        }
        this.Invalidate();
    }

【问题讨论】:

  • 您是如何触发刷新的?您是否在 MouseMove 处理程序中调用 Refresh?您如何以及何时调用 PSF_Painted?
  • @Brannon:我在 mouseclick、mouseUp、mouseDown 和 mouseMove 之后触发了 PSF_Painted。此外,在每个 PSF_Painted 被触发后,我也会使作为面板控件的画布无效。
  • 与其试图猜测您在哪里遇到了性能问题,我将向您介绍一个免费的 .NET 分析器,我听说过它的好消息:eqatec.com/tools/profiler。探查器将分析您的代码的执行情况,并告诉您运行时间最长的是什么。然后,您应该将优化工作重点放在耗时最长的事情上(因为修复这些事情会带来最大的好处)
  • 好的,谢谢你的链接..但我也想知道这是否是正确的做法。

标签: c# optimization drawing gdi+ double-buffering


【解决方案1】:

好的,我找到了这个优化的解决方案。 我只记录了距离大于画笔大小 1/5 的点。 我意识到我不需要一条非常完美的线条。因此,可以在生产线质量上进行折衷以换取性能。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-01-24
    • 2012-04-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-02-14
    • 2013-02-08
    相关资源
    最近更新 更多