How to optimize draw area in pixel art editor

时间:2018-01-25 16:19:49

标签: c# wpf canvas draw

I have pixel art creator program, and I have rectangles on canvas that are one field (pixel?). And this is good solution on not huge amount of it (for example 128x128). if i want to create 1024x1024 rectangles on canvas this process is very long, ram usage is about 1-2 gb and after that program runs very slowly. How to optimize this, or create better solution?

2 个答案:

答案 0 :(得分:5)

使用FrameworkElement代表每个像素是执行此操作的错误方式。作为WriteableBitmap,每个矩形都参与布局和输入命中测试。这种方法太重,无法扩展。放弃现在

我建议直接绘制到public class PixelEditor : FrameworkElement { private readonly Surface _surface; private readonly Visual _gridLines; public int PixelWidth { get; } = 128; public int PixelHeight { get; } = 128; public int Magnification { get; } = 10; public PixelEditor() { _surface = new Surface(this); _gridLines = CreateGridLines(); Cursor = Cursors.Pen; AddVisualChild(_surface); AddVisualChild(_gridLines); } protected override int VisualChildrenCount => 2; protected override Visual GetVisualChild(int index) { return index == 0 ? _surface : _gridLines; } private void Draw() { var p = Mouse.GetPosition(_surface); var magnification = Magnification; var surfaceWidth = PixelWidth * magnification; var surfaceHeight = PixelHeight * magnification; if (p.X < 0 || p.X >= surfaceWidth || p.Y < 0 || p.Y >= surfaceHeight) return; _surface.SetColor( (int)(p.X / magnification), (int)(p.Y / magnification), Colors.DodgerBlue); _surface.InvalidateVisual(); } protected override void OnMouseMove(MouseEventArgs e) { base.OnMouseMove(e); if (e.LeftButton == MouseButtonState.Pressed && IsMouseCaptured) Draw(); } protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e) { base.OnMouseLeftButtonDown(e); CaptureMouse(); Draw(); } protected override void OnMouseLeftButtonUp(MouseButtonEventArgs e) { base.OnMouseLeftButtonUp(e); ReleaseMouseCapture(); } protected override Size MeasureOverride(Size availableSize) { var magnification = Magnification; var size = new Size(PixelWidth* magnification, PixelHeight * magnification); _surface.Measure(size); return size; } protected override Size ArrangeOverride(Size finalSize) { _surface.Arrange(new Rect(finalSize)); return finalSize; } private Visual CreateGridLines() { var dv = new DrawingVisual(); var dc = dv.RenderOpen(); var w = PixelWidth; var h = PixelHeight; var m = Magnification; var d = -0.5d; // snap gridlines to device pixels var pen = new Pen(new SolidColorBrush(Color.FromArgb(63, 63, 63, 63)), 1d); pen.Freeze(); for (var x = 1; x < w; x++) dc.DrawLine(pen, new Point(x * m + d, 0), new Point(x * m + d, h * m)); for (var y = 1; y < h; y++) dc.DrawLine(pen, new Point(0, y * m + d), new Point(w * m, y * m + d)); dc.Close(); return dv; } private sealed class Surface : FrameworkElement { private readonly PixelEditor _owner; private readonly WriteableBitmap _bitmap; public Surface(PixelEditor owner) { _owner = owner; _bitmap = BitmapFactory.New(owner.PixelWidth, owner.PixelHeight); _bitmap.Clear(Colors.White); RenderOptions.SetBitmapScalingMode(this, BitmapScalingMode.NearestNeighbor); } protected override void OnRender(DrawingContext dc) { base.OnRender(dc); var magnification = _owner.Magnification; var width = _bitmap.PixelWidth * magnification; var height = _bitmap.PixelHeight * magnification; dc.DrawImage(_bitmap, new Rect(0, 0, width, height)); } internal void SetColor(int x, int y, Color color) { _bitmap.SetPixel(x, y, color); } } } 并使用自定义曲面在用户绘制时渲染位图。

以下是允许以单一颜色进行简单绘制的最低概念证明。它需要 WriteableBitmapEx 库,可以从NuGet获得。

ScrollViewer

只需将其导入您的Xaml,最好是在<Window x:Class="WpfTest.PixelArtEditor" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:l="clr-namespace:WpfTest" Title="PixelArtEditor" Width="640" Height="480"> <ScrollViewer HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto"> <l:PixelEditor /> </ScrollViewer> </Window>

{{1}}

显然,这与功能齐全的像素艺术编辑器相差甚远,但它功能齐全,足以让你走上正轨。编辑128x128图像与1024x1024之间的内存使用量差异约为30mb。把它点起来看看它的实际效果:

Screenshot of Pixel Art Editor

嘿,这很有趣!谢谢你的转移。

答案 1 :(得分:0)

只需改进Mike Strobel解决方案即可将网格线捕捉到设备像素。

var d = -0.5d; // snap gridlines to device pixels

using (DrawingContext dc = _dv.RenderOpen())
{
    GuidelineSet guidelineSet = new GuidelineSet();
    guidelineSet.GuidelinesX.Add(0.5);
    guidelineSet.GuidelinesY.Add(0.5);
    dc.PushGuidelineSet(guidelineSet);

    // Draw grid
}