首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >DrawingContext转位图文件

DrawingContext转位图文件
EN

Stack Overflow用户
提问于 2012-07-31 09:58:30
回答 3查看 4.5K关注 0票数 10

我有一个DrawingContext ( Visual或DrawingGroup的一部分),我在其中绘制一堆矩形和/或1位图像。可以把它想象成一个掩蔽的1位图像。我想把它转换成一个位图图像文件。

使用RenderTargetBitmap不是一个选择,因为它只能以32位像素格式呈现,所以如果我必须呈现一个20MB的1位图像,我的堆上最终将有640MB (20*32)的内存。这当然会产生华丽的LOH碎片,并且应用程序在第二次运行时会耗尽内存。

因此,我基本上需要一种方法来高效地从绘图上下文中编写1位位图文件。任何想法/建议/替代方法都将不胜感激。

EN

回答 3

Stack Overflow用户

发布于 2012-09-03 09:02:24

有很多想法,有些有点复杂……

打印到XPS,然后提取位图

您可以将Visual打印到XPS文档。

如果你幸运的话,它会将你在DrawingContext中绘制的1位图像组合在一起,并在XPS文件中生成一个位图。

对于矩形,它可能会将矩形作为基于向量的信息保留在....if中(基于"Shape“的RectangleDrawingContext DrawRectangle可能都会这样做),然后创建一个位图,然后在其中绘制矩形部分,并将该位图绘制到上下文中。

一旦您有了XPS文件,您就可以解析它并提取"image“,如果幸运的话这就是它在内部生成的内容。(XPS只是一个ZIP文件,它使用XAML来描述内容,并使用子目录来存储位图图像数据文件、字体等)。

(我认为可以使用Package类访问XPS文档的原始部分,或者使用XpsDocument进行更高级别的解释)。

或者只在XPS查看器中显示XPS,如果您的目的只是为了提供一种方法,允许在不同的上下文中查看组合的1位图像。

使用提供打印到图像功能的打印机驱动程序

这不是ideal...not,至少因为您必须在machine.....but上安装打印机驱动程序,您可以使用“打印到图像”打印机驱动程序,然后使用它来生成您的位图。以下是一些建议:

  • http://sourceforge.net/projects/imageprinter/
  • http://www.zan1011.com/

创建一个GDI+ HBITMAP并使用GDI+ calls...then完成绘图,将其包装以供WPF...or使用,保存到磁盘。

1首先创建一个足以容纳合成渲染的GDI+位图

(有几种不同的方法来做this...one,方法是使用WriteableBitmap来提供后台缓冲位/存储……如果你想访问memory...in中的位,我认为你不需要这样做)。

代码语言:javascript
复制
var bmp = new System.Drawing.Bitmap( pixelwidth, pixelheight, System.Drawing.Imaging.Format1bppIndexed );

或者,如果您想要访问WriteableBitmap,则使用此方法。

2将任何WPF BitmapSources转换为GDI+位图,以便您可以使用DrawImage将它们绘制到GDI+图形上下文中。

你可以在你的BitmapSources上使用CopyPixels来做这件事。

对于矩形,只需使用GDI+ DrawRectangle渲染命令即可。

3将GDI+位图保存到磁盘

在System.Image.Bitmap上使用.Save方法将其另存为位图。

4使用保存的图像作为图像元素的源

(请注意,这可能会占用大量的内存,当它加载您的图像时,即使它是1bpp...because的,WPF渲染路径都是32bpp)。

4或对GDI+位图进行换行以便在WPF中使用

您可以使用InteropBitmap包装基于GDI+的位图,这样您就可以在BitmapSource中使用它。(请注意,它可能必须是32bpp的one....if,它有to...then,你已经回到了1...but,无论如何都要尝试)。

  • http://arbel.net/2008/10/22/improper-use-of-interopbitmap-can-cause-a-memory-leak/
  • http://arbel.net/2008/10/22/improper-use-of-interopbitmap-can-cause-a-memory-leak/

创建一个位图“服务”

创建另一个作为服务的进程(不必是NT service...could,只需是您启动的子进程)来渲染组合后的1bpp images....there有多种方式与其通信,以向其提供渲染命令。

当内存消耗过高/ LOH变得碎片化时,您可以重新启动此服务。

其他想法

你可以使用OpenGL渲染(例如,使用OpenTK或SharpGL),或者使用DirectX...via渲染带有D3DImage或Direct2D的3D路径(无论这在内存使用方面是否与RenderTargetBitmap相同...这是为了找出答案)。

  • http://www.codeproject.com/Articles/265903/Using-OpenGL-in-a-WPF-Application
  • http://www.codeproject.com/Articles/113991/Using-Direct2D-with-WPF
  • http://www.codeproject.com/Articles/28526/Introduction-to-D3DImage

尝试NET 4.5,因为在LOH Heap中有许多改进:

  • http://blogs.msdn.com/b/dotnet/archive/2011/10/04/large-object-heap-improvements-in-net-4-5.aspx
  • http://blogs.msdn.com/b/dotnet/archive/2012/07/20/the-net-framework-4-5-includes-new-garbage-collector-enhancements-for-client-and-server-apps.aspx
票数 7
EN

Stack Overflow用户

发布于 2012-09-02 15:27:54

我不认为你能做得更好。因为RenderTargetBitmap使用您无法访问的MILcore。我不认为有任何其他方法可以复制Visual。然而,我认为还有一个选择。它不会是一行,但我认为它已经足够好了。

基本上,您将逐块渲染可视块(PGBRA32),并将其动态转换为BlackWhite,然后将其与黑白缓冲区连接。我已经开始了一个小的示例代码,但在半路上决定它不会那么容易,但你可以完成它。

代码语言:javascript
复制
 /// <summary>
/// Renders only part of the visual and returns byte[] array
/// which holds only black/white information.
/// </summary>
/// <param name="oVisual"></param>
/// <param name="x"></param>
/// <param name="y"></param>
/// <param name="width"></param>
/// <param name="height"></param>
/// <returns>black/white pixel information. one pixel=one bit. 1=white, 0=black</returns>
public static byte[] RenderPartially(Visual oVisual, 
    int x, int y, double width, double height)
{
    int nWidth = (int)Math.Ceiling(width);
    int nHeight = (int)Math.Ceiling(height);

    RenderTargetBitmap oTargetBitmap = new RenderTargetBitmap(
        nWidth,
        nHeight,
        96,
        96,
        PixelFormats.Pbgra32
    );

    DrawingVisual oDrawingVisual = new DrawingVisual();
    using (DrawingContext oDrawingContext = oDrawingVisual.RenderOpen())
    {
        VisualBrush oVisualBrush = new VisualBrush(oVisual);

        oDrawingContext.DrawRectangle(
            oVisualBrush,
            null,
            new Rect(
                new Point(x, y),
                new Size(nWidth, nHeight)
            )
        );

        oDrawingContext.Close();
        oTargetBitmap.Render(oDrawingVisual);
    }

    //Pbgra32 == 32 bits ber pixel?!(4bytes)
    // Calculate stride of source and copy it over into new array.
    int bytesPerPixel = oTargetBitmap.Format.BitsPerPixel / 8;
    int stride = oTargetBitmap.PixelWidth * bytesPerPixel;
    byte[] data = new byte[stride * oTargetBitmap.PixelHeight];
    oTargetBitmap.CopyPixels(data, stride, 0);

    //assume pixels in byte[] are stored as PBGRA32 which means that 4 bytes form single PIXEL.
    //so the layout is like:
    // R1, G1, B1, A1,  R2, G2, B2, A2,  R3, G3, B3, A3, and so on.

    byte [] bitBufferBlackWhite = new byte[oTargetBitmap.PixelWidth
        * oTargetBitmap.PixelHeight / bytesPerPixel];

    for(int row = 0; row < oTargetBitmap.PixelHeight; row++)
    {
        for(int col = 0; col < oTargetBitmap.PixelWidth; col++)
        {
            //calculate concrete pixel from PBGRA32
            int index = stride * row + bytesPerPixel * col;
            int r = data[index];
            int g = data[index + 1];
            int b = data[index + 2];
            int transparency = data[index + 3];

            //determine whenever pixel is black or white.
            //note that I dont know the exact process how one converts
            //from PBGRA32 to BlackWhite format. But you should get the idea.
            bool isBlack = r + g + b + transparency == 0;

            //calculate target index and USE bitwise operators in order to
            //set right bits.
        }
    }

    return bitBufferBlackWhite;
}

因此,基本上,使用BlackWhite格式设置新的WriteableBitmap,然后像这样调用此函数:

代码语言:javascript
复制
WriteableBitmap blackWhiteFullBuffer = new WriteableBItmap(....., PIxelFormats.BlackWhite);
for(int x = 0; x < Visual.Width; x += <some uniform amount that divides correctly>)
{
    for(int y = 0; y < VIsual.Height; y+= <some uniform amount that divides correctly>)
    {
        byte[] partialBuffer = PartialRenderer.RenderPartially(Visual, x, y, <uniform amX>,
          <uniform amY>);
        //concat that partial blackWhite buffer with other blackWhite buffer.
        //you do this as long as needed and memory wont grow too much
        //hopefully it will be fast enough too.
        PartialRenderer.ConcateBuffers(blackWhiteFullBuffer, partialBuffer);
    }
}

//如果需要,将blackWhiteFullBuffer保存到普通硬盘。

票数 2
EN

Stack Overflow用户

发布于 2012-08-31 19:20:07

PixelFormats呢?

编辑:(感谢安德斯·古斯塔夫松)

较低的是PixelFormats.BlackWhite,每像素1位。

因此,通过这种方式,您可以将任何BitmapImage转换为FormatConvertedBitmap,从而可以将格式修改为较低的bpp。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/11732113

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档