I have a 1 dimensional array of bytes, with separate values for A, R, G, and B, and I know the height and width of the expected image. How can I encode this data and save it as a PNG?
-
6Who is downvoting this question, and why? – user1027167 Aug 25 '15 at 12:35
-
See edit to my answer. Perhaps you can use the array directly using WPF. – ispiro Aug 25 '15 at 13:56
-
?. Because there is a straightforward solution, possibly. Other than in your comments on answers to this quite broad question, you don't mention anything on efficiency or whatever is stopping you from using any native PNG support in .NET, or even libpng. – Jongware Aug 25 '15 at 17:27
-
1@Jongware: Then you should be constructive and add a comment to this question! There are about 7 downvotes and for now only on comment saying why. For me this is a short and clear question. I don't want to read so much unnecessary text. – user1027167 Aug 26 '15 at 07:29
-
2I wasn't aware of any native PNG support options. If I knew of such options to use and could use them, I wouldn't have asked the question. If I had more information to give, I would have given it. – Stealth Rabbi Aug 26 '15 at 11:48
-
@Jongware if there was a straightforward solution to a straightforward question, then please provide the answer. Judging by the variety of answers below, there may not be one. – Stealth Rabbi Aug 26 '15 at 11:58
4 Answers
byte[] data = new byte[] {
255, 255, 000, 000, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 000, 000,
255, 255, 255, 255, 255, 255, 000, 000, 255, 255, 255, 255, 255, 255, 000, 000, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 000, 000, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 000, 000, 255, 255, 255, 255, 255, 255, 000, 000, 255, 255, 255, 255,
255, 255, 000, 000, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 000, 000
};
Bitmap bmp = new Bitmap(5, 5);
for (int y = 0; y < 5; ++y)
for (int x = 0; x < 5; ++x)
{
int offset = y * 5 * 4 + x * 4;
bmp.SetPixel(x, y, Color.FromArgb(data[offset], data[offset + 1], data[offset + 2], data[offset + 3]));
}
bmp.Save(@"c:\tmp.png");
}
If the values in your array are ordered this way: B G R A B G R A B G R A ... you could use the following code, which should be faster:
byte[] data = new byte[] {
// B G R A B G R A B G R A
0, 0, 255, 255, 0, 0, 0, 255, 0, 255, 0, 255,
0, 0, 0, 255, 0, 255, 0, 255, 255, 255, 255, 255,
0, 255, 0, 255, 0, 0, 0, 255, 255, 0, 0, 255
};
int width = 3;
int height = 3;
Bitmap bmp = new Bitmap(width, height, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
var bitmapData = bmp.LockBits(new System.Drawing.Rectangle(0, 0, bmp.Width, bmp.Height), System.Drawing.Imaging.ImageLockMode.WriteOnly, bmp.PixelFormat);
Marshal.Copy(data, 0, bitmapData.Scan0, data.Length);
bmp.UnlockBits(bitmapData);
bmp.Save(@"c:\tmp.png");

- 4,320
- 6
- 33
- 40
-
This is a pretty good solution. I was hoping for something more efficient than setting individual pixels. – Stealth Rabbi Aug 25 '15 at 13:22
-
-
-
-
OK, the second solution basically works. I realized thought that my byte array is R,G,B,A, so the colors are off (what I specified in the question is wrong). Having RGBA is not a supported pixel format, so I guess I have to adjust my code that generates the byte array to put Alpha first. – Stealth Rabbi Aug 26 '15 at 19:14
-
What do you mean with 'off'? I added a 3x3 image as byte array and also the PNG that is created by the code. – user1027167 Aug 27 '15 at 07:16
-
Sorry. I mean that R, G, B, A values were swapped, and the image did not appear to be the right colors. I then went back and read your note above the second example about having to be in the B, G, R, A order. It now shows up just fine. Sorry, a confusing comment. – Stealth Rabbi Aug 27 '15 at 12:00
You can use a Bitmap and Bitmap.SetPixel().
Then save the Bitmap as png using ImageFormat.Png
. Also, you might have to make sure that the Bitmap format maintains transparency .
(See the other answer here for a faster way than SetPixel.)
EDIT
Perhaps WPF can use the array directly. (I don't have much experience with it.)
Here's a faster solution using Unsafe pointers
and LockBits
public unsafe Bitmap Retreive(byte[] values)
{
Bitmap bmp = new Bitmap(Width, Height); //enter your width and height here.
BitmapData bmData = bmp.LockBits(new System.Drawing.Rectangle(0, 0, bmp.Width, bmp.Height), System.Drawing.Imaging.ImageLockMode.ReadOnly, bmp.PixelFormat);
IntPtr scan0 = bmData.Scan0;
int stride = bmData.Stride;
int nWidth = bmp.Width;
int nHeight = bmp.Height;
for (int y = 0; y < nHeight; y++)
{
//define the pointers inside the first loop for parallelizing
byte* p = (byte*)scan0.ToPointer();
p += y * stride;
for (int x = 0; x < nWidth; x++)
{
//fill as the values stored in you byte array;
p[0] = values[0];// R component.
p[1] = values[1];// G component.
p[2] = values[2];// B component.
p[3] = values[3];// Alpha component.
p += 4;
}
}
bmp.UnlockBits(bmData);
return bmp;
}
Just fill the p[]
values like they stored in your array.
Goodluck.
-
I haven't run unsafe code before. What are the impacts of doing this? – Stealth Rabbi Aug 25 '15 at 13:22
-
@StealthRabbi it's not dangerous haha :) it's just acessing the pixels throught pointers which cause the entire process to run significantly faster. you can read more about it here https://msdn.microsoft.com/en-us/library/aa288474(v=vs.71).aspx – Slashy Aug 25 '15 at 13:24
-
-
@user1027167 i dont know the real reason but actually it's because it used unmanaged pointers... you can try my method yourself.. you could see it's running x100 faster :) – Slashy Aug 25 '15 at 13:57
-
@Slashy: I just wanted to point out, that it is dangerous to use unsafe code ;) You should be very careful with that... – user1027167 Aug 25 '15 at 14:02
I suggest it is better to look for a platform-independent solution that doesn't depend on building a desktop image or a Bitmap (neither GDI+ Bitmap nor WPF WriteableBitmap). BigGustave (available on NuGet) did the trick for me. https://github.com/EliotJones/BigGustave It creates the PNGs directly.