PDA

View Full Version : Code to load PNG



Ñuño Martínez
02-06-2010, 09:58 AM
I want to load PNG's.

I've found I can do it the long way: create a TBitmap, load the picture using "LoadFromFile", copy the image "pixel-by-pixel" to my own Bitmap structure and finally destroy the TBitmap. But this isn't a very "profiled" way.

I'm wondering if it's possible to load a PNG bitmap using the FCL's TDecompressionStream (http://lazarus-ccr.sourceforge.net/fpcdoc/fcl/zstream/tdecompressionstream.html), both use lzw algorithm, don't them? ???

Savage suggested an implementation for Delphi here (http://www.pascalgamedevelopment.com/forum/index.php?topic=3782.msg27127#msg27127). I've "overview" it and seems nice, but it uses it's own deflating code and I was wondering if I can use FCL's one.

Anyway if it's no possible I'll try to "translate" it. I would use Luuk van Venrooij's Image Converter (http://www.pascalgamedevelopment.com/forum/index.php?topic=5966.msg48497#msg48497) and save all them as TGA or something (there are some hundreds of them) but it seems as it doesn't exist. :(

jdarling
02-06-2010, 05:05 PM
Nuno, never found a stand along PNG loader myself :(. The one that is included in FPC/Lazarus requires 3rd party libs, Vampyre will load them without a 3rd party, but bloats the app due to everything else it can do.

So far, Vampyre (http://imaginglib.sourceforge.net/) is the best solution I've found, though I wish I could find a PNG only loader :)

- Jeremy

arthurprs
02-06-2010, 05:19 PM
use Vampyre, there is an .inc file where you can disable some unwanted features.

Stoney
02-06-2010, 06:54 PM
Another vote for Vamypre from me. :)

Brainer
02-06-2010, 08:24 PM
Vampyre is ok, but the latest versions of Delphi have a native support for the PNG format.

jdarling
02-06-2010, 08:32 PM
Vampyre is ok, but the latest versions of Delphi have a native support for the PNG format.


Not quite correct. The latest versions of .NET have support for PNG and thus .NET apps (including Chrome/Delphi) have access. The "native" compiled versions still utilize API to load PNG's from the FS.

If you want cross platform and FPC then your kinda stuck with Vampyre :)

Brainer
02-06-2010, 09:54 PM
Not quite correct. The latest versions of .NET have support for PNG and thus .NET apps (including Chrome/Delphi) have access. The "native" compiled versions still utilize API to load PNG's from the FS.

Not sure if I got it right, but as far as I know, Delphi loads the PNG format on its own using the pngimage unit, so I guess .NET is not needed.

WILL
03-06-2010, 07:28 AM
I believe I tried to add a screenshot feature to Garland's Quest recently amoung the formats I tried PNG was one of them. I had the header down no problems, however it was the compression of the actual pixel data that I was unable to figure out. I eventually settled on TGA, but it would have been nice to at least written raw PNG files. Or use simple RLE, but alas it seems that only LZH is used for PNG, no?

I could post a snippet of my wip?

User137
03-06-2010, 07:51 AM
Nuno, never found a stand along PNG loader myself :(. The one that is included in FPC/Lazarus requires 3rd party libs
What do you mean by this? Lazarus's PNG works without getting anything extra. And after i just opened PNG source files (packages\fcl_image\src) they really don't have anything 3rd party in them.

TPortableNetworkGraphic is found in Graphics unit.

Luuk van Venrooij
03-06-2010, 11:36 AM
Vampyre Image Lib is probebly the best way to go. Supports everything you need. Also If you need my image converter just drop a line and I`ll upload it again.

jdarling
03-06-2010, 12:34 PM
Nuno, never found a stand along PNG loader myself :(. The one that is included in FPC/Lazarus requires 3rd party libs
What do you mean by this? Lazarus's PNG works without getting anything extra. And after i just opened PNG source files (packages\fcl_image\src) they really don't have anything 3rd party in them.

TPortableNetworkGraphic is found in Graphics unit.


Just took another look at the Lazarus version and your right about that :). But, it doesn't always load a PNG properly :( (see attached image below). I never figured out what exactly was wrong with the loader, but I know it has to do with transparency layers generated from some graphics editing software.

For the below image (and similar) I have to load them into PhotoShop and re-save them for them to work properly. Never had such problems with Vampyre.

http://www.eonclash.com/PGD/action_back.png

Course, I could be wrong about the latest versions of Delphi too, I've only read about the details of how they implemented PNG support. I know a while back the concern was in the LZH compression and its licensing.

- Jeremy

User137
03-06-2010, 03:50 PM
The green arrow image loaded up quite nicely, and i have never had any problems with PNG whatever format, alphachanneled or not, i save or get them from. I haven't tested TImage behaviour much but mainly just dig into its data to put in OpenGL :P

Edit: Oh, i have a little ImageViewer tool to test if anyone wants to:
http://www.easy-share.com/1910530660/ImageViewer.zip
(Better no ads-link): http://www.eonclash.com/PGD/User137_ImageViewer.zip

It's made with Lazarus and Next3D, mainly for watching manga and other cartoony folders full of images. Quite a bit of optimization and stuff done to make it finished i hope.

jdarling
04-06-2010, 12:05 AM
Should have mentioned, that one of the other ways I've found to "fix" an image is to right click and save it as from a web browser LOL. seems they tweak the heards when they download an image.

Anyways, did you a favor and here is an alternative download link that doesn't fill the screen with AD's http://www.eonclash.com/PGD/User137_ImageViewer.zip

Interesting little app, I'll post more on my thoughts if you want to startup and link out a thread :)

- Jeremy

Ñuño Martínez
04-06-2010, 08:50 AM
Thank you all for your replies. :)

I was looking few Pascal implementations of PNG and seems to be more complex as I thought. I'm not sure I understand it -- I have this problem with most technical documents written in English. :(

About the suggestion about "Vampyre", I'll give it a chance. I hope it doesn't "overload" or adds a lot of stuff to the executable. Actually I need the PNG loader because I want to use Battle for Wesnoth graphics and I don't want to translate hundreds of pictures to other format.



Also If you need my image converter just drop a line and I`ll upload it again.

Yes please. If I don't use it now but I'll use in the future. And a lot of people will "thanks" it a lot. ;D

Galfar
08-06-2010, 12:18 PM
So far, Vampyre (http://imaginglib.sourceforge.net/) is the best solution I've found, though I wish I could find a PNG only loader :)

I'm planning to make a standalone MiniPNG unit based on Imaging's PNG handler so there is still hope :)
I guess only loading of PNGs is ok (no saving)?



use Vampyre, there is an .inc file where you can disable some unwanted features.

This can remove a lot of bloat from your exe (JPEG2000, TIFF, ...).

jdarling
08-06-2010, 03:11 PM
I'm planning to make a standalone MiniPNG unit based on Imaging's PNG handler so there is still hope :)
I guess only loading of PNGs is ok (no saving)?


Loading would be excellent if absolutely NO (as in 0) 3rd party libraries or etc are required! Creating, Saving, whatever is easy enough. Loading the lil buggers is a real problem.

At least, that's my opinion.

Ñuño Martínez
09-06-2010, 11:15 AM
I'm planning to make a standalone MiniPNG unit based on Imaging's PNG handler so there is still hope :)
I guess only loading of PNGs is ok (no saving)?

Yes: loading only should be enough. :)

WILL
11-06-2010, 09:10 PM
I'd love to be able to make all my screenshots in PNG format (which would mean writing to PNG) I've gone as far as to

Here is my incomplete implimentation of a PNG screenshot generator: (taken from Garland's Quest)

type
TPNGStart = packed Array[0 .. 8] of Byte;

TPNG_IHDR = packed record
Width : DWord;
Height : DWord;
BitDepth : Byte;
ColorType : Byte;
CompressionMethod : Byte;
FilterMethod : Byte;
InterlaceMethod : Byte;
end;
TPNG_Chunk = packed record
Length : DWord;
ChunkType : Array[0 .. 3] of Byte;
ChunkCRC : DWord;
end;


procedure ScreenShot_PNG(Filename: String);
var
i: Integer;
pngStartBytes: TPNGStart;
pngIHDR: TPNG_IHDR;
pngChunk: TPNG_Chunk;

vport: array[0 .. 3] of Integer;
Buffer: PChar;
BufferLength: Integer;
f: TMemoryStream;
crc: Cardinal;
begin
// First 8 bytes //
pngStartBytes[0] := 137;
pngStartBytes[1] := 80;
pngStartBytes[2] := 78;
pngStartBytes[3] := 71;
pngStartBytes[4] := 13;
pngStartBytes[5] := 10;
pngStartBytes[6] := 26;
pngStartBytes[7] := 10;

// Get OpenGL ViewPort Data
glGetIntegerv(GL_VIEWPORT, @vport);
glPixelStorei(GL_PACK_ALIGNMENT, 1);

// Chunk Information //
FillChar(pngChunk, SizeOf(pngChunk), 0);
pngChunk.Length := SizeOf(pngIHDR);
pngChunk.ChunkType[0] := ord('I');
pngChunk.ChunkType[1] := ord('H');
pngChunk.ChunkType[2] := ord('D');
pngChunk.ChunkType[3] := ord('R');

// IHDR Chunk Data //
FillChar(pngIHDR, SizeOf(pngIHDR), 0);
pngIHDR.Width := vport[2];
pngIHDR.Height := vport[3];
pngIHDR.BitDepth := 8;
pngIHDR.ColorType := 2; // 2 = Each pixel is an R,G,B triple. 6 = Each pixel is an R,G,B triple, followed by an alpha sample.
pngIHDR.CompressionMethod := 0; // deflate/inflate compression with a sliding window of at most 32768 bytes
pngIHDR.FilterMethod := 0; // adaptive filtering with five basic filter types
pngIHDR.InterlaceMethod := 0; // 0 (no interlace) or 1 (Adam7 interlace)

// Temp Store Chunk Type & Data into Buffer for processing
BufferLength := (4 + SizeOf(pngIHDR)); // ChunkType + ChunkData
GetMem(Buffer, BufferLength);
for i := 0 to 3 do // Chunk Type to Buffer
Write(Buffer^, pngChunk.ChunkType[i]);
// Write(Buffer^, pngIHDR); // Chunk Data to Buffer

// CRC for Chunk Data //
crc := crc32(0, nil, 0);
// pngChunk.ChunkCRC := crc32(crc, Buffer, BufferLength);

// Start Writing PNG File
f := TMemoryStream.Create;
for i := 0 to 7 do
f.Write(pngStartBytes[0], 1); // First 8 bytes
f.Write(pngChunk.Length, SizeOf(pngChunk.Length)); // IHDR Chunk Length
f.Write(buffer, BufferLength); // IHDR Chunk Type & Data
f.Write(pngChunk.ChunkCRC, SizeOf(pngChunk.ChunkCRC)); // IHDR Chunk CRC

FreeMem(Buffer);

// --- IDAT Chunk --- //

// Chunk Information //
FillChar(pngChunk, SizeOf(pngChunk), 0);
pngChunk.Length := SizeOf(pngIHDR);
pngChunk.ChunkType[0] := ord('I');
pngChunk.ChunkType[1] := ord('H');
pngChunk.ChunkType[2] := ord('D');
pngChunk.ChunkType[3] := ord('R');

// Temp Store Chunk Type & Data into Buffer for processing
BufferLength := (4 + (pngIHDR.Width * pngIHDR.Height * 3)); // rgb
GetMem(Buffer, BufferLength);
glReadPixels(0, 0, vport[2], vport[3], GL_RGB8, GL_UNSIGNED_BYTE, Buffer);

FreeMem(Buffer);


// crc32(crc : cardinal; buf : Pbyte; len : cardinal)
// f.Write(Buffer^, BufferLength);

// IEND Chunk

f.SaveToFile(Filename);
f.Free;
end;

it should work if you can modify it to generate the proper LZH compression for whatever pixel format you will be using. Oh and it is OpenGL dependent.