Ozt*_*aco 8 c# wpf pixel writeablebitmap
是否可以直接读/写WriteableBitmap的像素数据?我目前正在使用WriteableBitmapEx,SetPixel()但速度很慢,我希望直接访问像素而不会产生任何开销.
我有一段时间没有使用过HTML5的画布,但是如果我没记错的话,你可以将它的图像数据作为单个数字数组获得,这就是我正在寻找的东西
提前致谢
Mit*_*tch 29
要回答您的问题,您可以通过使用Lock,写,Unlock模式更直接地访问可写位图的数据,如下所示,但除非您基于图像的内容绘图,否则通常没有必要.更典型的是,您可以创建一个新缓冲区并使其成为位图,而不是相反.
话虽如此,WPF中有许多可扩展点来执行创新绘图而无需借助像素操作.对于大多数控件,现有的WPF基元(Border,Line,Rectangle,Image等)绰绰有余 - 不要担心使用它们中的许多,它们使用起来相当便宜.对于复杂控件,可以使用DrawingContext绘制D3D基元.对于图像效果,您可以使用类实现GPU辅助着色器或使用内置效果(模糊和阴影).Effect
但是,如果您的情况需要直接像素访问,请选择像素格式并开始写入.我建议BGRA32,因为它很容易理解,可能是最常见的讨论.
BGRA32表示像素数据以4个字节的形式存储在存储器中,表示图像的蓝色,绿色,红色和alpha通道.这很方便,因为每个像素最终都在4字节边界上,使其以32位整数存储.处理32位整数时,请记住在大多数平台上顺序都是相反的(BitConverter.IsLittleEndian如果需要支持多个平台,请检查以确定运行时的正确字节顺序,x86和x86_64都是小端)
图像数据存储在一个stride宽的水平条带中,这些条带构成图像宽度的单行.的stride宽度总是大于或等于所述图像乘以在选择的格式每像素的字节数的像素宽度.某些情况会导致步幅长于width * bytesPerPixel某些建筑物特定的步幅,因此您必须使用步幅宽度来计算行的开始,而不是乘以宽度.由于我们使用的是4字节宽的像素格式,我们的步伐确实如此width * 4,但您不应该依赖它.
如上所述,我建议使用WritableBitmap的唯一情况是,如果您正在访问现有图像,那么这是以下示例:
之前/之后:

// must be compiled with /UNSAFE
// get an image to draw on and convert it to our chosen format
BitmapSource srcImage = JpegBitmapDecoder.Create(File.Open("img13.jpg", FileMode.Open),
BitmapCreateOptions.None, BitmapCacheOption.OnLoad).Frames[0];
if (srcImage.Format != PixelFormats.Bgra32)
srcImage = new FormatConvertedBitmap(srcImage, PixelFormats.Bgra32, null, 0);
// get a writable bitmap of that image
var wbitmap = new WriteableBitmap(srcImage);
int width = wbitmap.PixelWidth;
int height = wbitmap.PixelHeight;
int stride = wbitmap.BackBufferStride;
int bytesPerPixel = (wbitmap.Format.BitsPerPixel + 7) / 8;
wbitmap.Lock();
byte* pImgData = (byte*)wbitmap.BackBuffer;
// set alpha to transparent for any pixel with red < 0x88 and invert others
int cRowStart = 0;
int cColStart = 0;
for (int row = 0; row < height; row++)
{
cColStart = cRowStart;
for (int col = 0; col < width; col++)
{
byte* bPixel = pImgData + cColStart;
UInt32* iPixel = (UInt32*)bPixel;
if (bPixel[2 /* bgRa */] < 0x44)
{
// set to 50% transparent
bPixel[3 /* bgrA */] = 0x7f;
}
else
{
// invert but maintain alpha
*iPixel = *iPixel ^ 0x00ffffff;
}
cColStart += bytesPerPixel;
}
cRowStart += stride;
}
wbitmap.Unlock();
// if you are going across threads, you will need to additionally freeze the source
wbitmap.Freeze();
Run Code Online (Sandbox Code Playgroud)
但是,如果您不修改现有图像,则没有必要.例如,您可以使用所有安全代码绘制棋盘图案:
输出:

// draw rectangles
int width = 640, height = 480, bytesperpixel = 4;
int stride = width * bytesperpixel;
byte[] imgdata = new byte[width * height * bytesperpixel];
int rectDim = 40;
UInt32 darkcolorPixel = 0xffaaaaaa;
UInt32 lightColorPixel = 0xffeeeeee;
UInt32[] intPixelData = new UInt32[width * height];
for (int row = 0; row < height; row++)
{
for (int col = 0; col < width; col++)
{
intPixelData[row * width + col] = ((col / rectDim) % 2) != ((row / rectDim) % 2) ?
lightColorPixel : darkcolorPixel;
}
}
Buffer.BlockCopy(intPixelData, 0, imgdata, 0, imgdata.Length);
// compose the BitmapImage
var bsCheckerboard = BitmapSource.Create(width, height, 96, 96, PixelFormats.Bgra32, null, imgdata, stride);
Run Code Online (Sandbox Code Playgroud)
如果你直接写入字节数组,你甚至不需要Int32中间体.
输出:

// draw using byte array
int width = 640, height = 480, bytesperpixel = 4;
int stride = width * bytesperpixel;
byte[] imgdata = new byte[width * height * bytesperpixel];
// draw a gradient from red to green from top to bottom (R00 -> ff; Gff -> 00)
// draw a gradient of alpha from left to right
// Blue constant at 00
for (int row = 0; row < height; row++)
{
for (int col = 0; col < width; col++)
{
// BGRA
imgdata[row * stride + col * 4 + 0] = 0;
imgdata[row * stride + col * 4 + 1] = Convert.ToByte((1 - (col / (float)width)) * 0xff);
imgdata[row * stride + col * 4 + 2] = Convert.ToByte((col / (float)width) * 0xff);
imgdata[row * stride + col * 4 + 3] = Convert.ToByte((row / (float)height) * 0xff);
}
}
var gradient = BitmapSource.Create(width, height, 96, 96, PixelFormats.Bgra32, null, imgdata, stride);
Run Code Online (Sandbox Code Playgroud)
编辑:显然,您正在尝试使用WPF来制作某种图像编辑器.我仍然会将WPF基元用于形状和源位图,然后将转换,缩放,旋转实现为RenderTransform's,位图效果为Effect',并将所有内容保留在WPF模型中.但是,如果这对您不起作用,我们还有许多其他选择.
您可以使用WPF基元渲染到RenderTargetBitmap已选择PixelFormat使用的基元,WritableBitmap如下所示:
Canvas cvRoot = new Canvas();
// position primitives on canvas
var rtb = new RenderTargetBitmap(width, height, dpix, dpiy, PixelFormats.Bgra32);
var wb = new WritableBitmap(rtb);
Run Code Online (Sandbox Code Playgroud)
您可以使用WPF DrawingVisual发出GDI样式命令,然后渲染到位图,如RenderTargetBitmap页面上的示例所示.
你可以使用GDI使用InteropBitmap使用创建System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap从一个HBITMAP从检索Bitmap.GetHBitmap方法.但是请确保你没有泄漏HBITMAP.
经过长时间的头痛,我发现这篇文章解释了一种不使用位算术的方法,并允许我将其视为数组:
unsafe
{
IntPtr pBackBuffer = bitmap.BackBuffer;
byte* pBuff = (byte*)pBackBuffer.ToPointer();
pBuff[4 * x + (y * bitmap.BackBufferStride)] = 255;
pBuff[4 * x + (y * bitmap.BackBufferStride) + 1] = 255;
pBuff[4 * x + (y * bitmap.BackBufferStride) + 2] = 255;
pBuff[4 * x + (y * bitmap.BackBufferStride) + 3] = 255;
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
17525 次 |
| 最近记录: |