如何优化像素艺术编辑器中的绘制区域

2024-03-23

我有像素艺术创作程序,并且画布上有矩形,它们是一个字段(像素?)。对于数据量不大(例如 128x128)来说,这是一个很好的解决方案。如果我想在画布上创建 1024x1024 矩形,这个过程会很长,RAM 使用量约为 1-2 GB,之后程序运行速度非常慢。如何优化这个,或者创建更好的解决方案?


Using a Rectangle表示每个像素的是wrong方法来做到这一点。作为一个FrameworkElement,每个矩形都参与布局和输入命中测试。这种方法重量太重,无法扩展。放弃它now.

我建议直接绘图WriteableBitmap并使用自定义表面在用户绘制时渲染位图。

以下是允许使用单色进行简单绘图的最小概念证明。它需要可写位图Ex库,可从 NuGet 获取。

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);
        }
    }
}

只需将其导入到您的 Xaml 中,最好是在ScrollViewer:

<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>

显然,这与功能齐全的像素艺术编辑器相去甚远,但它很实用,并且足以让您走上正轨。编辑 128x128 图像与编辑 1024x1024 图像之间的内存使用差异约为 30mb。启动它并查看它的运行情况:

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

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何优化像素艺术编辑器中的绘制区域 的相关文章

  • 如何在 C# 中将 IEnumerable 转换为 Enum?

    我已将多个字符串解析为枚举标志 但看不到将它们合并为单个枚举位字段的巧妙方法 我使用的方法循环遍历字符串值 然后 将值转换为 Enum 对象 如下所示 Flags public enum MyEnum None 0 First 1 Seco
  • 将 Azure Blob 与 Azure 网站连接

    我正在尝试将 Azure 网站连接到 Azure blob 我打算在容器中托管一些文件 然后从我的网站获取它们 我从本教程开始 http azure microsoft com en us documentation articles we
  • 如何显示图片目录中的图像?

    我想显示图片库中的图片 我获取图片并绑定数据 StorageFolder picturesFolder KnownFolders PicturesLibrary IReadOnlyList
  • (简单)boost thread_group 问题

    我正在尝试编写一个相当简单的线程应用程序 但我对 boost 的线程库很陌生 我正在开发的一个简单的测试程序是 include
  • 如何将 Boost 库添加到 XCode 6.0 中的 C++ 程序?

    我在用着XCode6 0并且需 要boost程序库 我已经下载了boost 1 57 0 tar gz from http sourceforge net projects boost files boost 1 57 0 http sou
  • C++变量声明和初始化规则

    考虑以下声明和初始化类型变量的方法C C c1 C c2 c2 C C c3 C C c4 C 所有这些是否完全等同 或者其中一些可以根据确切的定义而有所不同C 假设它有公共默认值和复制构造函数 这些意味着 C c1 default con
  • .net 日历 - 使整个单元执行回发(可点击)

    我已经启动并运行了一个 net 日历 并从数据库中获取信息 默认情况下 天数会应用回发操作 我想做的是将该操作应用于整个单元格 这样用户就不需要仅单击文本链接 我是 dayRenderer 操作 我有以下行来尝试复制该操作 但第二个参数我不
  • 如何强制操作系统收回内存? (C++)

    在我的 C 代码中 我分配了大量内存来创建树 然后在每个节点中使用 删除 来释放内存 删除所有内容后 我检查操作系统使用的内存量 发现内存未释放 这是预期的 因为该进程不会立即将内存返回给操作系统 因为它仍然可能会再次使用它 问题是 我在删
  • MaxLength 数据注释是否可以与 List 一起使用?

    一个可以用 MaxLength 具有字符串和简单数组的属性 i e MaxLength 500 public string ProductName get set Or MaxLength 50 public string Products
  • C++ 获取两个分隔符字符串之间的字符串

    C C 中是否有任何内置函数可以在两个分隔符字符串之间获取字符串 我的输入看起来像 STARTDELIMITER 0 192 168 1 18 STOPDELIMITER 我的输出应该是 0 192 168 1 18 提前致谢 你可以这样做
  • C# 从视频文件的一部分中提取帧

    使用 AForge ffmpeg 包装器 您可以使用 VideoFileReader 类从视频中提取帧并将其保存为位图 请参阅以下示例 提取 avi 文件的帧 https stackoverflow com questions 178256
  • Azure 函数 - 如何读取表单数据

    如何阅读表单数据 in Azure 函数 我尝试了多种方法 但总是出现错误 例如 using System Net public static async Task
  • HttpCookie 和 Cookie 的区别?

    所以我很困惑 因为 msdn 和其他教程告诉我使用 HttpCookies 通过 Response Cookies Add cookie 添加 cookie 但这就是问题所在 Response Cookies Add 只接受 Cookie
  • 有丰富的领域模型示例吗? [关闭]

    Closed 此问题正在寻求书籍 工具 软件库等的推荐 不满足堆栈溢出指南 help closed questions 目前不接受答案 我正在寻找一个简单的示例来说明使用富域模型的好处 理想情况下 我想要一个之前和之后的代码列表 应该尽可能
  • C++ OpenMP:嵌套循环,其中内部迭代器依赖于外部迭代器

    考虑以下代码 include
  • 如何将格式化的电子邮件地址解析为显示名称和电子邮件地址?

    给定电子邮件地址 Jim 电子邮件受保护 gt 如果我尝试将其传递给 MailAddress 我会得到异常 指定的字符串不符合电子邮件地址所需的格式 如何将此地址解析为显示名称 Jim 和电子邮件地址 电子邮件受保护 cdn cgi l e
  • 检查字符串是否多次包含子字符串[重复]

    这个问题在这里已经有答案了 要搜索字符串内的子字符串 我可以使用contains 功能 但是如何检查一个字符串是否多次包含子字符串呢 优化这一点 对我来说 知道有多个结果而不是有多少就足够了 尝试利用快速IndexOf and LastIn
  • 让 clang-tidy 修复头文件

    我正在将当前使用 gcc 编译的项目移至 clang 并有一堆 gcc 没有生成的警告 Winconsistent missing override clang tidy致力于修复这些错误 cpp文件 但是它不触及hpp文件 因为在数据库中
  • 是否可以在 Visual Studio 2010 项目中使用多个“字符集”?

    如您所知 在 Visual Studio 2010 c 中 我们有 noset unicode 和 MBCS 字符集 我们可以通过菜单或预处理器指令 如 define UNICODE 来设置它 我正在开发一个项目 它有一个使用 MBCS 字
  • WPF 重复按钮 MouseUp

    有没有办法让重复按钮上的 MouseUpevent 在不再按下按钮时触发 我试图使用 MouseMove 事件来跟踪按下按钮时鼠标的位置 但 MouseDown 和 MouseUp 都不会触发鼠标左键的事件 关于可以做什么的任何想法或建议

随机推荐