How to WritePixels (array of ushort[]) to WritableBitmap BGR32 - c#

I'm working with WriteableBitmap (PixelFormats.Bgr32).
This is the format I need to save for the rest of the program to work.
Writable = new WriteableBitmap(inImage.XSize, inImage.YSize, dpi, dpi, PixelFormats.Bgr32, null);
The image from the device comes in grayscaled, (ushort[] Gray16).
To use this image in my program I use the following code (inImage - received image, ImageData = ushort[]):
int[] pixels = Array.ConvertAll(inImage.ImageData, val => checked((int)val));
Writable.WritePixels(new Int32Rect(0, 0, width, height), pixels, width * 4, 0);
If I just use
Writable.WritePixels(new Int32Rect(0, 0, width, height), inImage.ImageData, width * 4, 0);
I get a message about insufficient buffer size. (System.ArgumentException: "Buffer size is not sufficient."
)
Code
int[] pixels = Array.ConvertAll(inImage.ImageData, val => checked((int)val))
or
int[] pixels = new int[inImage.XSize * inImage.YSize];
for (int i = 0; i < inImage.ImageData.Length; i++)
{
pixels[i] = inImage.ImageData[i];
}
This seems to me to be a very long and slow and unoptimized process.
Is there any way to optimize the process and immediately write ushort[] array to WriteableBitmap (Bgr32) without converting it to int[] ?
EDIT***
I need this code to be able to work with images with a resolution of 4300x4300 with 45 FPS.
Below is the full code how I load images (this code is called 45 times per second to load a new image)
public class ImageStruct
{
public ushort[] ImageData;
public int XSize;
public int YSize;
public int XDpi;
public int YDpi;
}
WriteableBitmap Writeable;
public void LoadFrame(ImageStruct inImage)
{
var width = inImage.XSize;
var height = inImage.YSize;
var destBpp = PixelFormats.Bgr32.BitsPerPixel / 8;
var uPixels = inImage.ImageData;
fluoroWritable = new WriteableBitmap(inImage.XSize, inImage.YSize, 96, 96, PixelFormats.Bgr32, null);
var iPixels = Array.ConvertAll(uPixels, val => checked((int)val));
fluoroWritable.WritePixels(new Int32Rect(0, 0, width, height), iPixels, width * 4, 0);
}
This code works well when FPS is ~10,
If you increase the FPS, it starts to freeze. Profiling shows that it is the moment when I make from ushort[] => int[] takes the longest time.
I tried to use the code suggested in the answer below:
unsafe public void LoadFrameWithLock(ImageStruct inImage)
{
var width = inImage.XSize;
var height = inImage.YSize;
var destBpp = PixelFormats.Bgr32.BitsPerPixel / 8;
var uPixels = inImage.ImageData;
fluoroWritable = new WriteableBitmap(inImage.XSize, inImage.YSize, 96, 96, PixelFormats.Bgr32, null);
fluoroWritable.Lock();
int* outputIntValues = (int*)fluoroWritable.BackBuffer;
ushort[] inputShortValues = inImage.ImageData;
for (int i = 0; i < inputShortValues.Length; i++)
{
byte as8bpp = (byte)(inputShortValues[i] >> 8);
outputIntValues[i] = /*B*/ as8bpp | /*G*/ (as8bpp << 8) | /*R*/ (as8bpp << 16);
}
fluoroWritable.AddDirtyRect(new Int32Rect(0, 0, fluoroWritable.PixelWidth, fluoroWritable.PixelHeight));
fluoroWritable.Unlock();
}
But the FPS with this code is even lower and in addition the WPF application has a completely hanging interface. and images have no grayscale, but are completely black and white (white or black pixels).
I need the BGR32 format because this WritableBitmap (WPF Image object) is then overlaid with shader effects, and I can also get color images in another place.

Is there any way to optimize the process and immediately write ushort[] array to WriteableBitmap (Bgr32) without converting it to int[] ?
Yes, instead of calling WritePixels use the Lock method so the BackBuffer will be available as a naked pointer (do not forget to call Unlock when you are finished).
Please note though that as your source and target pixel formats are different (16bpp grayscale vs. 32bpp BGRx) simple casting of short values to int will not be correct. All 16 bit grayscale values must be converted to 8 bit RGB values:
// the result as you defined in OP
Writable = new WriteableBitmap(inImage.XSize, inImage.YSize, dpi, dpi, PixelFormats.Bgr32, null);
// you must be in an unsafe scope to use pointers
int* outputIntValues = (int*)Writable.BackBuffer;
short[] inputShortValues = inImage.ImageData;
// converting pixels to BGR32
for (int i = 0; i < inputShortValues.Length; i++)
{
// taking the most significant bits from the 16bpp gray values
byte as8bpp = (byte)(inputShortValues[i] >> 8);
outputIntValues[i] = /*B*/ as8bpp | /*G*/ (as8bpp << 8) | /*R*/ (as8bpp << 16);
}
// notifying the consumers that we edited the raw content
Writable.AddDirtyRect(new Int32Rect(0, 0, Writable.PixelWidth, Writable.PixelHeight));
// releasing the buffer
Writable.Unlock();
The sample above needs to be inside of an unsafe scope because of the pointer and you must enable unsafe blocks for your project in your .csproj file.
To make things simpler you can use my Drawing Libraries, which now has dedicated WPF support so the conversion will just be literally two lines. It does not need unsafe context and is actually faster than the example above because it uses parallel processing:
// Interpret your short[] as a grayscale bitmap
using var bmpGrayscale = BitmapDataFactory.CreateBitmapData(buffer: inImage.ImageData,
size: new Size(inImage.XSize, inImage.XSize),
stride: inImage.XSize * 2, // if input pixels are contiguous
KnownPixelFormat.Format16bppGrayScale));
// Convert it to a BGR32 WriteableBitmap (async overloads are available, too)
WriteableBitmap bmpResult = bmpGrayscale.ToWriteableBitmap(PixelFormats.Bgr32);

Related

Process Bitmap in C# using underlying buffer accessed with Bitmap.LockBits function

I need to do an analysis with an arbitrary image. I would like to start with the easiest example - just copy a image to picturebox.
Bitmap foreImg = new Bitmap("input.jpg");
//output image
Bitmap resImg = new Bitmap(foreImg.Width, foreImg.Height);
unsafe
{
BitmapData oneBits = foreImg.LockBits(new Rectangle(0, 0, foreImg.Width, foreImg.Height), ImageLockMode.ReadOnly, foreImg.PixelFormat);
BitmapData thrBits = resImg.LockBits(new Rectangle(0, 0, resImg.Width, resImg.Height), ImageLockMode.WriteOnly, resImg.PixelFormat);
System.Threading.Tasks.Parallel.For(0, foreImg.Width * foreImg.Height, j =>
{
Pixel* pxOne = (Pixel*)((byte*)oneBits.Scan0 + j * sizeof(Pixel));
Pixel* pxRes = (Pixel*)((byte*)thrBits.Scan0 + j * sizeof(Pixel));
pxRes->Green = pxOne->Green;
pxRes->Red = pxOne->Red;
pxRes->Blue = pxOne->Blue;
});
foreImg.UnlockBits(oneBits);
resImg.UnlockBits(thrBits);
}
In the result of my program the image is distorted
Original: original_image
After: after_image. What am I doing wrong?
Thanks! The problem was is that PixelFormat of input images does not match with my struct Pixel. Indeed, I wasn't add alpha byte, and in this case I was suppose to use Format24bppRgb.
Your code for image copy has couple errors due to assumptions which turn not true for particular image which is copied. First assumption us that when you create new target image for copy operation it will have exactly the same pixel representation as the source image what may be sometimes true but in many cases will not:
Bitmap resImg = new Bitmap(foreImg.Width, foreImg.Height);
should be instead:
Bitmap resImg = new Bitmap(foreImg.Width, foreImg.Height, foreImg.PixelFormat);
The next assumption which may or may not turn wrong depending on image is an implicit assumption that the source image PixelFormat is exactly 3 bytes in size and corresponds to PixelFormat.Format24bppRgb format (or multiple of 3 bytes as I do not know what is the size of Red, Green or Blue channel in your Pixel structure and it could be PixelFormat.Format48bppRgb format) and consequently the bytes are copied from the source image to the destination image based on this assumption.
To perform exact copy it is necessary to copy exactly the same number of bytes from source image to destination image and it does not require using an underlying Pixel structure but instead it can be based on integer copy. Last but not least if the goal is to copy image instead of analyzing it's content Pixel by Pixel the fastest method is to use specialized memory copy function:
System.Buffer.MemoryCopy((void*)oneBits.Scan0, (void*)thrBits.Scan0, byteLength, byteLength);
Below there is a code listing with code which copies an image using ulong as a carrier. I have added function which returns Pixel size in bytes which is used to calculate image size in bytes and perform exact copy. However it can be used to select matching Pixel structure which than can be used to analyze image data. For instance if an image has PixelFormat.Format24bppRgb format one can use Pixel structure of 3 byte size and RGB colors. For other formats it would be necessary to define other Pixel structures which would directly replicate image Pixel format.
using System;
using System.Drawing;
using System.Drawing.Imaging;
namespace DrawingImagingOperations
{
class Program
{
static void Main(string[] args)
{
Bitmap foreImg = new Bitmap(#"..\..\YaHI9.jpg");
//output image
Bitmap resImg = new Bitmap(foreImg.Width, foreImg.Height, foreImg.PixelFormat);
unsafe
{
BitmapData oneBits = foreImg.LockBits(new Rectangle(0, 0, foreImg.Width, foreImg.Height), ImageLockMode.ReadOnly, foreImg.PixelFormat);
BitmapData thrBits = resImg.LockBits(new Rectangle(0, 0, resImg.Width, resImg.Height), ImageLockMode.WriteOnly, resImg.PixelFormat);
int pixelSize = GetPixelSize(foreImg.PixelFormat);
var byteLength = foreImg.Width * foreImg.Height * pixelSize;
var length = byteLength / sizeof(UInt64);
var reminder = byteLength % sizeof(UInt64);
System.Threading.Tasks.Parallel.For(0, length, j =>
{
ulong* pxOne = (ulong*)((byte*)oneBits.Scan0 + j * sizeof(UInt64));
ulong* pxRes = (ulong*)((byte*)thrBits.Scan0 + j * sizeof(UInt64));
*pxRes = *pxOne;
});
if (reminder > 0)
{
byte* pSrc = (byte*)oneBits.Scan0 + (pixelSize * length);
byte* pDst = (byte*)thrBits.Scan0 + (pixelSize * length);
for (int j = length; j < byteLength; j++)
*pDst++ = *pSrc++;
}
foreImg.UnlockBits(oneBits);
resImg.UnlockBits(thrBits);
}
resImg.Save(#"..\..\imgCopy.jpg");
}
internal static int GetPixelSize(PixelFormat data)
{
switch (data)
{
case PixelFormat.Format8bppIndexed:
return 1;
case PixelFormat.Format16bppGrayScale:
case PixelFormat.Format16bppRgb555:
case PixelFormat.Format16bppRgb565:
case PixelFormat.Format16bppArgb1555:
return 2;
case PixelFormat.Format24bppRgb:
return 3;
case PixelFormat.Canonical:
case PixelFormat.Format32bppArgb:
case PixelFormat.Format32bppPArgb:
case PixelFormat.Format32bppRgb:
return 4;
case PixelFormat.Format48bppRgb:
return 6;
case PixelFormat.Format64bppArgb:
case PixelFormat.Format64bppPArgb:
return 8;
}
throw new FormatException("Unsupported image format: " + data);
}
}
}

What is the fastest way for Image Processing in C# [duplicate]

I am trying to teach myself C# and have heard from a variety of sources that the functions get and setpixel can be horribly slow. What are some of the alternatives and is the performance improvement really that significant?
A chunk of my code for reference:
public static Bitmap Paint(Bitmap _b, Color f)
{
Bitmap b = new Bitmap(_b);
for (int x = 0; x < b.Width; x++)
{
for (int y = 0; y < b.Height; y++)
{
Color c = b.GetPixel(x, y);
b.SetPixel(x, y, Color.FromArgb(c.A, f.R, f.G, f.B));
}
}
return b;
}
The immediately usable code
public class DirectBitmap : IDisposable
{
public Bitmap Bitmap { get; private set; }
public Int32[] Bits { get; private set; }
public bool Disposed { get; private set; }
public int Height { get; private set; }
public int Width { get; private set; }
protected GCHandle BitsHandle { get; private set; }
public DirectBitmap(int width, int height)
{
Width = width;
Height = height;
Bits = new Int32[width * height];
BitsHandle = GCHandle.Alloc(Bits, GCHandleType.Pinned);
Bitmap = new Bitmap(width, height, width * 4, PixelFormat.Format32bppPArgb, BitsHandle.AddrOfPinnedObject());
}
public void SetPixel(int x, int y, Color colour)
{
int index = x + (y * Width);
int col = colour.ToArgb();
Bits[index] = col;
}
public Color GetPixel(int x, int y)
{
int index = x + (y * Width);
int col = Bits[index];
Color result = Color.FromArgb(col);
return result;
}
public void Dispose()
{
if (Disposed) return;
Disposed = true;
Bitmap.Dispose();
BitsHandle.Free();
}
}
There's no need for LockBits or SetPixel. Use the above class for direct access to bitmap data.
With this class, it is possible to set raw bitmap data as 32-bit data. Notice that it is PARGB, which is premultiplied alpha. See Alpha Compositing on Wikipedia for more information on how this works and examples on the MSDN article for BLENDFUNCTION to find out how to calculate the alpha properly.
If premultiplication might overcomplicate things, use PixelFormat.Format32bppArgb instead. A performance hit occurs when it's drawn, because it's internally being converted to PixelFormat.Format32bppPArgb. If the image doesn't have to change prior to being drawn, the work can be done before premultiplication, drawn to a PixelFormat.Format32bppArgb buffer, and further used from there.
Access to standard Bitmap members is exposed via the Bitmap property. Bitmap data is directly accessed using the Bits property.
Using byte instead of int for raw pixel data
Change both instances of Int32 to byte, and then change this line:
Bits = new Int32[width * height];
To this:
Bits = new byte[width * height * 4];
When bytes are used, the format is Alpha/Red/Green/Blue in that order. Each pixel takes 4 bytes of data, one for each channel. The GetPixel and SetPixel functions will need to be reworked accordingly or removed.
Benefits to using the above class
Memory allocation for merely manipulating the data is unnecessary; changes made to the raw data are immediately applied to the bitmap.
There are no additional objects to manage. This implements IDisposable just like Bitmap.
It does not require an unsafe block.
Considerations
Pinned memory cannot be moved. It's a required side effect in order for this kind of memory access to work. This reduces the efficiency of the garbage collector (MSDN Article). Do it only with bitmaps where performance is required, and be sure to Dispose them when you're done so the memory can be unpinned.
Access via the Graphics object
Because the Bitmap property is actually a .NET Bitmap object, it's straightforward to perform operations using the Graphics class.
var dbm = new DirectBitmap(200, 200);
using (var g = Graphics.FromImage(dbm.Bitmap))
{
g.DrawRectangle(Pens.Black, new Rectangle(50, 50, 100, 100));
}
Performance comparison
The question asks about performance, so here's a table that should show the relative performance between the three different methods proposed in the answers. This was done using a .NET Standard 2 based application and NUnit.
* Time to fill the entire bitmap with red pixels *
- Not including the time to create and dispose the bitmap
- Best out of 100 runs taken
- Lower is better
- Time is measured in Stopwatch ticks to emphasize magnitude rather than actual time elapsed
- Tests were performed on an Intel Core i7-4790 based workstation
Bitmap size
Method 4x4 16x16 64x64 256x256 1024x1024 4096x4096
DirectBitmap <1 2 28 668 8219 178639
LockBits 2 3 33 670 9612 197115
SetPixel 45 371 5920 97477 1563171 25811013
* Test details *
- LockBits test: Bitmap.LockBits is only called once and the benchmark
includes Bitmap.UnlockBits. It is expected that this
is the absolute best case, adding more lock/unlock calls
will increase the time required to complete the operation.
The reason bitmap operations are so slow in C# is due to locking and unlocking. Every operation will perform a lock on the required bits, manipulate the bits, and then unlock the bits.
You can vastly improve the speed by handling the operations yourself. See the following example.
using (var tile = new Bitmap(tilePart.Width, tilePart.Height))
{
try
{
BitmapData srcData = sourceImage.LockBits(tilePart, ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
BitmapData dstData = tile.LockBits(new Rectangle(0, 0, tile.Width, tile.Height), ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb);
unsafe
{
byte* dstPointer = (byte*)dstData.Scan0;
byte* srcPointer = (byte*)srcData.Scan0;
for (int i = 0; i < tilePart.Height; i++)
{
for (int j = 0; j < tilePart.Width; j++)
{
dstPointer[0] = srcPointer[0]; // Blue
dstPointer[1] = srcPointer[1]; // Green
dstPointer[2] = srcPointer[2]; // Red
dstPointer[3] = srcPointer[3]; // Alpha
srcPointer += BytesPerPixel;
dstPointer += BytesPerPixel;
}
srcPointer += srcStrideOffset + srcTileOffset;
dstPointer += dstStrideOffset;
}
}
tile.UnlockBits(dstData);
aSourceImage.UnlockBits(srcData);
tile.Save(path);
}
catch (InvalidOperationException e)
{
}
}
It's been some time, but I found an example that might be useful.
var btm = new Bitmap("image.png");
BitmapData btmDt = btm.LockBits(
new Rectangle(0, 0, btm.Width, btm.Height),
ImageLockMode.ReadWrite,
btm.PixelFormat
);
IntPtr pointer = btmDt.Scan0;
int size = Math.Abs(btmDt.Stride) * btm.Height;
byte[] pixels = new byte[size];
Marshal.Copy(pointer, pixels, 0, size);
for (int b = 0; b < pixels.Length; b++)
{
pixels[b] = 255; //Do something here
}
Marshal.Copy(pixels, 0, pointer, size);
btm.UnlockBits(btmDt);
You can use Bitmap.LockBits method. Also if you want to use parallel task execution, you can use the Parallel class in System.Threading.Tasks namespace. Following links have some samples and explanations.
http://csharpexamples.com/fast-image-processing-c/
http://msdn.microsoft.com/en-us/library/dd460713%28v=vs.110%29.aspx
http://msdn.microsoft.com/tr-tr/library/system.drawing.imaging.bitmapdata%28v=vs.110%29.aspx
This code should be parallelized, there is a massive performance gain being missed by running this synchronously. Almost no modern Microchip will have less than 4 threads available and some chips will have 40 threads available.
There is absolutely no reason to run that first loop synchronously. You can go through either the width or the length using many, many threads.
private void TakeApart_Fast(Bitmap processedBitmap)
{
BitmapData bitmapData = processedBitmap.LockBits(new Rectangle(0, 0, processedBitmap.Width, processedBitmap.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
ConcurrentBag<byte> points = new ConcurrentBag<byte>();
unsafe
{
int bytesPerPixel = System.Drawing.Bitmap.GetPixelFormatSize(processedBitmap.PixelFormat) / 8;
int heightInPixels = bitmapData.Height;
int widthInBytes = bitmapData.Width * bytesPerPixel;
_RedMin = byte.MaxValue;
_RedMax = byte.MinValue;
byte* PtrFirstPixel = (byte*)bitmapData.Scan0;
Parallel.For(0, heightInPixels, y =>
{
byte* currentLine = PtrFirstPixel + (y * bitmapData.Stride);
for (int x = 0; x < widthInBytes; x = x + bytesPerPixel)
{
// red
byte redPixel = currentLine[x + 2];
//save information with the concurrentbag
}
});
processedBitmap.UnlockBits(bitmapData);
}
}`
a benchmark wouldn't mean much because the answer to how much this will speed up the proccess depends 100% on what hardware you are using, and what else is running in the background, it all depends on how many free threads are available. If your running this on a 4000 series graphics card with thousands of streaming proccessors you may be able to do iterate through every column of the image at the same time.
if your running it with and old quad core you may only have 5 or 6 threads which is still incredibly significant.

Image Manipulation c#

I have the following code which takes an array of bytes which i generated and writes them out to this bitmap. If i set the pixel format to Format4bppIndexed, then i get a readable image repeating width wise 4 times, if i set it to Format1bppIndexed(which is the correct setting) then i get one big unreadable image.
The image was a decoded Jbig2 image , i know the bytes are correct i can't seem to figure out how to get it into a 1bpp readable format.
Does anyone have any advice on that matter
Bitmap bitmap = new Bitmap(width, height, PixelFormat.Format1bppIndexed);
//Create a BitmapData and Lock all pixels to be written
BitmapData bmpData = bitmap.LockBits(
new Rectangle(0, 0, bitmap.Width, bitmap.Height),
ImageLockMode.WriteOnly, bitmap.PixelFormat);
//Copy the data from the byte array into BitmapData.Scan0
Marshal.Copy(newarray, 0, bmpData.Scan0, newarray.Length);
//Unlock the pixels
bitmap.UnlockBits(bmpData);
The following may work although, if I remember correctly, Stride sometimes has an effect and a simple block-copy won't suffice (line by line must be used instead).
Bitmap bitmap = new Bitmap(
width,
height,
System.Drawing.PixelFormat.Format16bppGrayScale
);
To handle the Stride you'd want:
BitmapData^ data = bitmap->LockBits(oSize,
ImageLockMode::ReadOnly, bitmap->PixelFormat);
try {
unsigned char *pData = (unsigned char *)data->Scan0.ToPointer();
for( int x = 0; x < bmpImage->Width; ++x )
{
for( int y = 0; y < bmpImage->Height; ++y )
{
// Note: Stride is data width of scan line rounded up
// to 4 byte boundary.
// Requires use of Stride, not (width * pixelWidth)
int ps = y*bmpImage->Width*(nBitsPerPixel / 8)
+ x * (nBitsPerPixel / 8);
int p = y * data->Stride + x * (nBitsPerPixel / 8);
Byte lo = newarray[ps + 1];
Byte hi = newarray[ps + 0];
pData[p + 1] = lo;
pData[p + 0] = hi;
}
}
} finally {
bmpImage->UnlockBits(data);
}
Note: This was written in C++/CLI. Let me know if you need C# equivalents for any of the operations here. (Also, I pulled it from a read from bitmap rather than a write to bitmap so it may yet be a bit rough, but should hopefully give you the idea...)
I figured this out Although i'm still not sure why it should matter.
Based on this stackoverflow posting How can I load the raw data of a 48bpp image into a Bitmap?
I used the WPF classes instead of the GDI and wrote the code like this
var bitmap = new WriteableBitmap(width, height, 96, 96, System.Windows.Media.PixelFormats.BlackWhite, null);
bitmap.WritePixels(new System.Windows.Int32Rect(0, 0, width, height), newarray, stride, 0);
MemoryStream stream3 = new MemoryStream();
var encoder = new TiffBitmapEncoder ();
encoder.Frames.Add(BitmapFrame.Create(bitmap));
encoder.Save(stream3);
This correctly creates the image.
If anyone has any insight into why this might be the case please comment below
The port which now mostly works(lots of cleanup code) was based on a java implementation of JPedal Big2 Decoder to .NET. If anyone knows anyone interested send them here
https://github.com/devteamexpress/JBig2Decoder.NET

Pixel Manipulation in CGBitmapContext depend on the view's superview?

I came across something really strange with while manipulating pixels using CGBitmapContext. Basically I'm changing the Alpha values of PNGs. I have an example where I can successfully Zero the Alpha value of a PNGs pixel EXCEPT when my ImageView's superview is an ImageView with an imageā€¦ Why this is? I'm not sure if it's a bug or if something in the Context is set up incorrectly.
Below is an example. It zeros' alpha values successfully, but when you give the superview's UIImageView an Image, it doesn't work right. The pixels that should have an Alpha of zero are slightly visible. But when you remove the image from the superview's UIImageView the Alpha values are zeroed correctly, you see the white background behind it perfectly. I've tried using several different CGBlendModes but that doesn't seem to do it.
Here is a dropbox link to an example project demonstrating this strange event:
https://www.dropbox.com/s/e93hzxl5ru5wnss/TestAlpha.zip
And here is the code copy/pasted:
// Create a Background ImageView
UIImageView Background = new UIImageView(this.View.Bounds);
// Comment out the below line to see the Pixels alpha values change correctly, UnComment the below line to see the pixel's alpha values change incorrectly. Why is this?
// When Commented out, The pixels whose alpha value I set to zero become transparent and I can see the white background through the image.
// When Commented in, I SHOULD see the "GrayPattern.png" Image through the transparent pixels. I can kind of see it, but for some reason I still see the dirt pixels who's alpha is set to zero! Is this a bug? Is the CGBitmapContext set up incorrectly?
Background.Image = UIImage.FromFile("GrayPattern.png");
this.View.AddSubview(Background);
UIImageView MyImageView = new UIImageView(UIScreen.MainScreen.Bounds);
UIImage sourceImage = UIImage.FromFile("Dirt.png");
MyImageView.Image = sourceImage;
Background.AddSubview(MyImageView);
CGImage image = MyImageView.Image.CGImage;
Console.WriteLine(image.AlphaInfo);
int width = image.Width;
int height = image.Height;
CGColorSpace colorSpace = CGColorSpace.CreateDeviceRGB();
int bytesPerRow = image.BytesPerRow;
int bytesPerPixel = bytesPerRow / width;
int bitmapByteCount = bytesPerRow * height;
int bitsPerComponent = image.BitsPerComponent;
CGImageAlphaInfo alphaInfo = CGImageAlphaInfo.PremultipliedLast;
// Allocate memory because the BitmapData is unmanaged
IntPtr BitmapData = Marshal.AllocHGlobal(bitmapByteCount);
CGBitmapContext context = new CGBitmapContext(BitmapData, width, height, bitsPerComponent, bytesPerRow, colorSpace, alphaInfo);
context.SetBlendMode(CGBlendMode.Copy);
context.DrawImage(new RectangleF(0, 0, width, height), image);
UIImageView MyImageView_brush = new UIImageView(new RectangleF(0, 0, 100, 100));
UIImage sourceImage_brush = UIImage.FromFile("BB_Brush_Rect_MoreFeather.png");
MyImageView_brush.Image = sourceImage_brush;
MyImageView_brush.Hidden = true;
Background.AddSubview(MyImageView_brush);
CGImage image_brush = sourceImage_brush.CGImage;
Console.WriteLine(image_brush.AlphaInfo);
int width_brush = image_brush.Width;
int height_brush = image_brush.Height;
CGColorSpace colorSpace_brush = CGColorSpace.CreateDeviceRGB();
int bytesPerRow_brush = image_brush.BytesPerRow;
int bytesPerPixel_brush = bytesPerRow_brush / width_brush;
int bitmapByteCount_brush = bytesPerRow_brush * height_brush;
int bitsPerComponent_brush = image_brush.BitsPerComponent;
CGImageAlphaInfo alphaInfo_brush = CGImageAlphaInfo.PremultipliedLast;
// Allocate memory because the BitmapData is unmanaged
IntPtr BitmapData_brush = Marshal.AllocHGlobal(bitmapByteCount_brush);
CGBitmapContext context_brush = new CGBitmapContext(BitmapData_brush, width_brush, height_brush, bitsPerComponent_brush, bytesPerRow_brush, colorSpace_brush, alphaInfo_brush);
context_brush.SetBlendMode(CGBlendMode.Copy);
context_brush.DrawImage(new RectangleF(0, 0, width_brush, height_brush), image_brush);
for ( int x = 0; x < width_brush; x++ )
{
for ( int y = 0; y < height_brush; y++ )
{
int byteIndex_brush = (bytesPerRow_brush * y) + x * bytesPerPixel_brush;
byte alpha_brush = GetByte(byteIndex_brush+3, BitmapData_brush);
// Console.WriteLine("alpha_brush = " + alpha_brush);
byte setValue = (byte)(255 - alpha_brush);
// Console.WriteLine("setValue = " + setValue);
int byteIndex = (bytesPerRow * y) + x * bytesPerPixel;
SetByte(byteIndex+3, BitmapData, setValue);
}
}
// Set the MyImageView Image equal to the context image, but I still see all top image, I dont' see any bottom image showing through.
MyImageView.Image = UIImage.FromImage (context.ToImage());
context.Dispose();
context_brush.Dispose();
// Free memory used by the BitmapData now that we're finished
Marshal.FreeHGlobal(BitmapData);
Marshal.FreeHGlobal(BitmapData_brush);
public unsafe byte GetByte(int offset, IntPtr buffer)
{
byte* bufferAsBytes = (byte*) buffer;
return bufferAsBytes[offset];
}
public unsafe void SetByte(int offset, IntPtr buffer, byte setValue)
{
byte* bufferAsBytes = (byte*) buffer;
bufferAsBytes[offset] = setValue;
}
Update I've found a workaround (though its performance is pretty unacceptable for what I"m doing) The workaround is simply taking the result image of from my above code, convert it to NSData, then convert the NSData back to a UIIMage... Magically the image looks correct! This workaround; however, is not acceptable for what I'm doing because it literally takes 2 seconds to do this with my Image and that is way too long for my App. A couple hundred ms would be fun, but 2 seconds is way to long for me. If this is a bug hopefully it gets resolved!
Here is the workaround (poor performance) code. Just add this code to the end of the code above (or download my dropbox project and add this code to the end of it and run):
NSData test = new NSData();
test = MyImageView.Image.AsPNG();
UIImage workAroundImage = UIImage.LoadFromData(test);
MyImageView.Image = workAroundImage;
Here are before and after pictures. Before is without the workaround, After includes the workaround.
Notice the Gray doens't show up completely. It's covered in some light brown as if the alpha value isn't full zeroed.
Notice with the workaround you can see the gray perfectly and clearly, no light brown over it at all. The only thing I did was Convert the image to NSData, then back to UIImage again! Very strange...

Calculating the required buffer size for the WriteableBitmap.WritePixels method

How do I calculate the required buffer size for the WriteableBitmap.WritePixels method?
I am using the overload taking four parameters, the first is an Int32Rect, the next is a byte array containing the RGBA numbers for the colour, the third is the stride (which is the width of my writeable bitmap multiplied by the bits per pixel divided by 8), and the last is the buffer (referred to as the offset in Intellisense).
I am getting the Buffer size is not sufficient runtime error in the below code:
byte[] colourData = { 0, 0, 0, 0 };
var xCoordinate = 1;
var yCoordinate = 1;
var width = 2;
var height = 2;
var rect = new Int32Rect(xCoordinate, yCoordinate, width, height);
var writeableBitmap = new WriteableBitmap(MyImage.Source as BitmapSource);
var stride = width*writeableBitmap.Format.BitsPerPixel/8;
writeableBitmap.WritePixels(rect, colourData, stride,0);
What is the formula I need to use to calculate the buffer value needed in the above code?
The stride value is calculated as the number of bytes per "pixel line" in the write rectangle:
var stride = (rect.Width * bitmap.Format.BitsPerPixel + 7) / 8;
The required buffer size is the number of bytes per line multiplied by the number of lines:
var bufferSize = rect.Height * stride;
Provided that you have a 2x2 write rectangle and a 32-bits-per-pixel format, e.g. PixelFormats.Pbgra32, you get stride as 8 and bufferSize as 16.
The stride is simply the width in bytes of your input buffer. It is called stride, because sometimes there is extra memory behind each line of an image, which makes it impossible to use the width of the image to read each line of an image.
So in your example, this is 2. You do not need to calculate anything with the bits per pixel of the bitmap, the WritePixels method knows all this information. You need to provide the information about how your input data is structured.
However, as mentioned in the other answer, your example won't work if the bitmap is also 2x2. Then the starting coordinate would be 0,0.
EDIT:
When I look closer at your example, I see the mistake. You say the colourData is the input color. But this is input per pixel. So if you want to change a rect of 2x2, you need the following inputdata:
byte[] colourData = { 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0 };
And then the bytes per pixel is equal to that of the bitmap, so that is 4, times the width of each line (2), makes total 8.
Here's Microsoft's reflected code that performs the check within CopyPixels
int num = ((sourceRect.Width * this.Format.BitsPerPixel) + 7) / 8;
if (stride < num)
{
throw new ArgumentOutOfRangeException("stride", MS.Internal.PresentationCore.SR.Get("ParameterCannotBeLessThan", new object[] { num }));
}
int num2 = (stride * (sourceRect.Height - 1)) + num;
if (bufferSize < num2)
{
throw new ArgumentOutOfRangeException("buffer", MS.Internal.PresentationCore.SR.Get("ParameterCannotBeLessThan", new object[] { num2 }));
}
Although user Clemens answered the question concerning the buffer size, the questioner was not aware that he calculated the buffer size already correct and the problem was somewhere else.
While details are given and discussed in comments there is lacking one comprehensive snippet (and complete usage example of .WritePixels (without .CopyPixels) as well).
Here it is (I scanned similar questions, but this has been the best place):
var dpiX = 96;
var writeableBitmap = new WriteableBitmap(width, height, dpiX, dpiX, PixelFormats.Bgra32, null); // Pixelformat of Bgra32 results always in 4 bytes per pixel
int bytesPerPixel = (writeableBitmap.Format.BitsPerPixel + 7) / 8; // general formula
int stride = bytesPerPixel * width; // general formula valid for all PixelFormats
byte[] pixelByteArrayOfColors = new byte[stride * height]; // General calculation of buffer size
// The numbers in the array are indices to the used BitmapPalette,
// since we initialized it with null in the writeableBitmap init, they refer directly to RGBA, but only in this case.
// Choose a light green color for whole bitmap (for not easy to find commented MSDN example with random colors, see https://msdn.microsoft.com/en-us/library/system.windows.media.imaging.writeablebitmap(VS.85).aspx
for (int pixel = 0; pixel < pixelByteArrayOfColors.Length; pixel += bytesPerPixel)
{
pixelByteArrayOfColors[pixel] = 0; // blue (depends normally on BitmapPalette)
pixelByteArrayOfColors[pixel + 1] = 255; // green (depends normally on BitmapPalette)
pixelByteArrayOfColors[pixel + 2] = 0; // red (depends normally on BitmapPalette)
pixelByteArrayOfColors[pixel + 3] = 50; // alpha (depends normally on BitmapPalette)
}
writeableBitmap.WritePixels(new Int32Rect(0, 0, width, height), pixelByteArrayOfColors, stride, 0);
I am work with this.
60z fs
this.playerOpacityMaskImage.WritePixels(
new Int32Rect(0, 0, this.depthWidth, this.depthHeight),
this.greenScreenPixelData,
this.depthWidth * ((this.playerOpacityMaskImage.Format.BitsPerPixel + 7) / 8),
0);
I am not sure but try this works for 24 bit rgb
{
//your code
var stride = width * 3;
WriteableBitmap bmp = new WriteableBitmap(width, height, 96, 96, PixelFormats.Bgr24, null);
bmp.WritePixels(new System.Windows.Int32Rect(0, 0, width , height),byte[],stride,0));
}

Categories