How to write png file from scratch?

Nov 24, 2022 at 3:35pm
Hi,
I want to write a.png file from scratch.
For example a red 8 x 8 pixel square.
Do I need a special library?

Thanks for your input.
Nov 24, 2022 at 4:35pm
You could of course read up on the PNG format and output the file in the correct format yourself using std::ofstream but using a library such as libpng is probably much easier.
Last edited on Nov 24, 2022 at 4:37pm
Nov 24, 2022 at 5:03pm
https://docs.fileformat.com/image/png/
which tells you that

PNG compression method 0 (the only compression method presently defined for PNG) specifies deflate/inflate compression with a sliding window of at most 32768 bytes. Deflate compression is an LZ77 derivative used in zip, gzip, pkzip, and related programs.

which means that doing it from scratch is a pain in the backside. If you allow yourself a zip library, its not too bad, but if you are going to do that, why not just use a png library and be done with it?

Conclusion: use the library. There are simpler image formats that support little more than an RGB array with a header, but PNG apparently isn't one of them.

There is no uncompressed variant of PNG.
Last edited on Nov 24, 2022 at 5:05pm
Nov 24, 2022 at 6:07pm
If you are using Windows you can use GDI+ to create the image, and save as PNG.

https://stackoverflow.com/questions/39551863/creating-gdi-bitmaps-in-memory-and-then-saving-as-png

GDI+ can read and write a number of supported image formats.

https://learn.microsoft.com/en-us/windows/win32/gdiplus/-gdiplus-using-image-encoders-and-decoders-use
Nov 24, 2022 at 6:35pm
jonnin: Keep reading.

The compressed data within the zlib datastream is stored as a series of blocks, each of which can represent raw (uncompressed) data, LZ77-compressed data encoded with fixed Huffman codes, or LZ77-compressed data encoded with custom Huffman codes.

Not only is it possible to write uncompressed PNGs, it's quite easy. It's not much harder than writing a Windows bitmap (BMP).

https://www.w3.org/TR/2003/REC-PNG-20031110/
You just need to write the header and then lay out the data in chunks. It can be done in 100-200 lines.
Last edited on Nov 24, 2022 at 8:24pm
Nov 24, 2022 at 7:55pm
Ah, I saw and misread that. I thought it was saying it was in uncompressed LZW type format (eg zip with compression set to 'store' or 0) not pure raw. That makes it easier...
Nov 25, 2022 at 3:10am
I found this talking about image magic?
https://stackoverflow.com/questions/36288421/c-create-png-bitmap-from-array-of-numbers

Isn’t there something easier? Then png?
Format doesn’t matter. As long as it is uncompressed

Edit... check this out.
https://stackoverflow.com/questions/32203347/create-a-png-from-an-array-of-bytes

This is exactly what I'm trying to do.

How do I convert the following into C++ code?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
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");
Last edited on Nov 25, 2022 at 3:20am
Nov 25, 2022 at 4:55am
How do I convert the following into C++ code?
That code is using a library built into .NET to write the PNG, so since C++ doesn't have that capability built-in, to translate that to C++ you'd still need to link to a PNG implementation.

If you don't care about format, what I usually do when I need to output bitmaps is just dump the pixels to a file named whatever.raw and open the file in IrfanView. That's the quickest way to do it, for me.
Nov 25, 2022 at 5:14am
Thanks for your reply.
Sorry I wasn’t clear. I am using c++ cli and .net
So that above example should do what I want.
Just don’t know how to convert the syntax.
Nov 25, 2022 at 9:05am
OK. I have something like this

1
2
3
	 Image^ bmp = gcnew Bitmap(8, 8, System::Drawing::Imaging::PixelFormat::Format32bppArgb);

	 bmp.SetPixel(NULL, 0, 0, RGB(255, 255, 255));


but SetPixel doesn't exist...
Nov 25, 2022 at 9:10am
void SetPixel(int x, int y, COLORREF color)

Are you sure that you need that first NULL?
Nov 25, 2022 at 9:28am
I don't know. I got that from the link above.

I removed the NULL but that didn't fix it.

I looked on google for it but no one has the problem of SetPixel not showing up.

Edit...
To be clear I need to set a pixel of a bitmap image.

Thanks.
Last edited on Nov 25, 2022 at 9:37am
Nov 25, 2022 at 12:30pm
IIRC, the syntax in C++/CLI to access members of managed pointers (T ^) is ->, not ..
Nov 25, 2022 at 7:09pm
Yes I tried that but it doesn't exist.
Nov 26, 2022 at 12:14am
This code is from the link that George P posted.
However it won't compile. Identifier GdiplusInit undifined.

The first line of the error Says Music_FormB.h(1218,7): warning C4101: 'location': unreferenced local variable

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

#include <windows.h>
#include <objidl.h>
#include <gdiplus.h>
using namespace Gdiplus;
#pragma comment (lib,"Gdiplus.lib")


void drawImage( int width, int height ) {
    GdiplusInit gdiplusinit;

    //Create a bitmap
    Bitmap myBitmap( width, height, PixelFormat32bppARGB );
    Graphics g( &myBitmap );
    Pen blackpen( Color( 255, 0, 0, 0 ), 3 );

    //draw on bitmap
    g.DrawLine( &blackpen, 1, 1, 200, 200 );

    // Save bitmap (as a png)
    CLSID pngClsid;
    int result = GetEncoderClsid( L"image/png", &pngClsid );
    if ( result == -1 )
        throw runtime_error( "GetEncoderClsid" );
    if ( Ok != myBitmap.Save( L"C:\\test\\test.png", &pngClsid, NULL ) )
        throw runtime_error( "Bitmap::Save" );
}
Last edited on Nov 26, 2022 at 12:17am
Nov 26, 2022 at 5:58am
I turned the above into a complete sample program. This is my first time using GDI+, so be sceptical.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
#include <windows.h>
#include <objidl.h>
#include <gdiplus.h>
#pragma comment (lib,"Gdiplus.lib")
using namespace Gdiplus;

#include <algorithm>
#include <vector>

int constexpr width = 400;
int constexpr height = 300;

static constexpr bool not_ok(Gdiplus::Status r) { return r != Gdiplus::Status::Ok; }

int main()
{
  ULONG_PTR token;
  GdiplusStartupInput si;
  GdiplusStartupOutput so;
  // TODO(mbozzi): Ensure (using RAII) that GDIPlus is shut down after all GDI+ objects are dead
  Status r = GdiplusStartup(&token, &si, &so);
  if (not_ok(r)) return 1;

  //Create a bitmap
  Bitmap myBitmap(width, height, PixelFormat32bppARGB);
  Graphics g(&myBitmap);
  SolidBrush b(Color(255, 255, 0, 255));
  g.FillRectangle(&b, 0, 0, width, height);

  // Save bitmap (as a png)
  CLSID pngClsid; 

  { // find the CLSID for PNG encoder
    UINT codec_info_array_size = 0;
    UINT codec_info_array_size_bytes = 0;
    r = GetImageEncodersSize(&codec_info_array_size, &codec_info_array_size_bytes);
    if (not_ok(r)) return 1;

    ImageCodecInfo* codec_info_array = (ImageCodecInfo*)malloc(codec_info_array_size_bytes);
    // NOTE(mbozzi): std::vector<ImageCodecInfo> codec_info_array(codec_info_array_size);
    // is wrong since sizeof(ImageCodecInfo) * codec_info_array_size != codec_info_array_size_bytes
    // GetImageEncoders is (kind of) allocating a FAM; inspect memory if you're curious

    r = GetImageEncoders(codec_info_array_size, codec_info_array_size_bytes, codec_info_array);
    if (not_ok(r)) return 1;

    auto const b = codec_info_array;
    auto const e = codec_info_array + codec_info_array_size;
    auto const i = std::find_if(b, e, [](ImageCodecInfo const& i) { return i.FormatID == ImageFormatPNG; });

    if (i == e) return 1; else pngClsid = i->Clsid;
  }

  r == myBitmap.Save(L".\\test.png", &pngClsid, NULL);
  if (not_ok(r)) return 1;
}
Topic archived. No new replies allowed.