Defold PNG - Inspect, load and save PNG files

Not really sure how useful this is but I thought I’d share it anyway. Defold PNG is a Defold native extension that allows you to inspect, load and save PNG files. Usage:

-- load my.png from disk
local f = io.open("my.png", "rb")
local bytes = f:read("*a")

-- read the PNG header
local info = png.info(bytes)
print(info.width)
print(info.height)
print(info.colortype) -- png.RGB, png.RGBA, png.GREY, png.GREY_ALPHA or png.PALETTE
print(info.bitdepth)

-- decode the PNG to a Defold buffer in RGB(A) format
local buf, w, h = png.decode_rgb(bytes)
local buf, w, h = png.decode_rgba(bytes)

-- use buffer as a sprite texture
local resource_path = go.get("#sprite", "texture0")
local header = { width = w, height = h, type = resource.TEXTURE_TYPE_2D, format = resource.TEXTURE_FORMAT_RGBA, num_mip_maps = 1 }
resource.set_texture(resource_path, header, buf)

-- create 100x100 raw pixels of random colors
local w, h = 100, 100
local pixels = ""
for i=1,w*h do
	pixels = pixels .. string.char(math.random(1,255), math.random(1,255), math.random(1,255), 255)
end

--- encode raw RGB(A) pixels into a PNG
local bytes = png.encode_rgb(pixels, w, h)
local bytes = png.encode_rgba(pixels, w, h)

-- write bytes to foo.png
local f = io.open("foo.png", "wb")
f:write(bytes)
f:flush()
f:close()
17 Likes

Very useful, thanks! How difficult would it be to do something similar for GIFs? Because my game is 100% deterministic a super-stretch goal of mine is to record player input and when they solve a puzzle play back the key inputs to create a GIF of their solution that they can then tweet/whatever. Would be happy to look into extensions and do it myself if/when the time comes, just wondering if it’s even feasible.

4 Likes

You would probably want to save the game state and only make the GIF after with a custom render script. Use a render target to render everything to a texture, downscale each frame, then batch process them to produce the GIF. There are still mystery steps which I am not clear on doing still but with buffers and native extensions I think it should all be possible right now. Could also save every x frame textures downscaled to memory too and then produce the GIF at game over based on last few frames with a few seconds/msecs inbetween or based on when moves are made.

2 Likes

a game that generates replay gifs may go quite viral, from where I am.
Love the idea

6 Likes

Well this is pretty awesome, thanks a lot for making it! Just a little bit faster than decoding PNGs with pure Lua, haha. As a test I loaded a 17MB PNG (about 5000x3000px) and it decoded in just a tad over 1 second. This definitely opens up some possibilities.

One question though: is there some way to get a loaded PNG onto a GUI node? gui.new_texture and all the related functions require a string for the texture buffer . . .

Yes, but not using my extension. You can use:

local file = io.open("my.png", "rb")
local bytes = file:read("*a")
local png = image.load(bytes)
gui.new_texture("my.png", png.width, png.height, "rgba", png.buffer)

Complete example: https://github.com/britzl/publicexamples/blob/master/examples/loadtexture/load_texture/loadtexture.gui_script#L8-L19

Now, I could ofc add support for returning decoded image as a string instead of a buffer, but I reasoned that since image.load() exists there was no need for it.

5 Likes

Ohhh, bingo. Boy do I feel sheepish. I was trying to use resource.load or something when I tried this before. Thanks!

3 Likes

Amazing… I did the search “save to png” without any hope and found this!

How I can do the reverse: Get a render target and save as PNG? Respecting alpha channel.

1 Like

https://www.defold.com/community/projects/56489/ might help.

1 Like

Also here is a similar extension, but works with JPG too.

5 Likes