PNG Sucker

Post your FreeBASIC source, examples, tips and tricks here. Please don’t post code without including an explanation.
Post Reply
nimdays
Posts: 236
Joined: May 29, 2014 22:01
Location: West Java, Indonesia

PNG Sucker

Post by nimdays »

This will create a backup of your png file and reduce the size.

Code: Select all

function rev(v as uinteger)as uinteger
    dim as ubyte ptr p = cast(ubyte ptr,@v)
    return p[0] shr 24 or p[1] shr 16 or p[2] shr 8 or p[3]
end function

type chunk
    as integer csize,cname
end type

dim as integer l,c
dim as ubyte d() 'buffer
dim as chunk ck  

dim as string fl '= "_untitled.png"
input "Enter PNG file : ",fl

if open (fl for binary access read as #1) <> 0 then 
    ?"Unable to open " & fl : sleep : end
end if

l = lof(1)
print fl & " : " & l & " bytes"
print
redim d(l)
get #1,,d(0),8

if *cast(uinteger ptr,@(d(0))) <> &h474E5089 or _
   *cast(uinteger ptr,@(d(4))) <> &hA1A0A0D then
   ?"Invalid PNG Header " : close #1 : end
end if

c += 8 'inc counter
print "Adding Header : 8 bytes"

while not eof(1)
    get #1,,ck 'get chunk size and name
    dim s as string *4 = *cast(zstring ptr,@ck.cname)
    dim as integer lc = rev(ck.csize)
    if s = "IDAT" or s = "IHDR" or s = "IEND" then 'you may add more
        seek 1,seek(1) - 8 'go back
        get #1,,d(c),lc + 12
        c += lc + 12
        print "Adding " & s & " : " & lc + 12 & " bytes"
    else
        seek #1,seek(1) + lc + 4 'next chunk
        print "Ignoring " & s & " : " & lc + 12 & " bytes"
    end if
wend

close #1

redim preserve d(c - 1)

print
print "writing _" & fl

open "_" & fl for binary access write as #1
   put #1,,d()
close #1

open "_" & fl for input as #1
   print "_" & fl & " : " & lof(1) & " bytes"
close #

print
print "press any key to quit"
sleep
''
jj2007
Posts: 2326
Joined: Oct 23, 2016 15:28
Location: Roma, Italia
Contact:

Re: PNG Sucker

Post by jj2007 »

The program writes lots of lines to the console but stops with

Code: Select all

writing _C:\path\PeView2Png.png
_C:\path\PeView2Png.png : 0 bytes
and no files modified or created. With a different file, it ends with

Code: Select all

c* : 214 bytes
Ignoring ÙÉ«V : 239 bytes
Adding IEND : 12 bytes
writing _sucker.png
_sucker.png : 302 bytes
which is huge success in terms of size reduction, but the image quality suffers a little bit ;-)
MrSwiss
Posts: 3910
Joined: Jun 02, 2013 9:27
Location: Switzerland

Re: PNG Sucker

Post by MrSwiss »

@nimdays,

don't ever use U/Integer for anything, that depends on a fixed size, as below:
(If the code is supposed to be *portable*, aka: useful for both: FBC 32/64 bit!)

Code: Select all

' FBC 32: OK, FBC 64: not OK!
if *cast(uinteger ptr,@(d(0))) <> &h474E5089 or _	' clearly a 32-bit value
   *cast(uinteger ptr,@(d(4))) <> &hA1A0A0D then
   ?"Invalid PNG Header " : close #1 : end
end if
In this particular instance (ULong), or "one shot" (ULongInt):

Code: Select all

if *cptr(uLong ptr,@(d(0))) <> &h474E5089 or _	' corrected
   *cptr(uLong ptr,@(d(4))) <> &hA1A0A0D then
   ?"Invalid PNG Header " : close #1 : end
end if
using ULongInt (preferred method; no manual index math., speed, readability, ...):

Code: Select all

If *CPtr(ULongInt Ptr, @(d(0))) <> &h474E5089A1A0A0D Then	' clearly a 64-bit value
   Print "Invalid PNG Header! Press a key, to END ";
   Close #1 : Sleep : End 1	' indicate ERROR
End If
counting_pine
Site Admin
Posts: 6323
Joined: Jul 05, 2005 17:32
Location: Manchester, Lancs

Re: PNG Sucker

Post by counting_pine »

The header check is also currently little-endian dependent. It could be made to work in big-endian by using the rev() function (which should work correctly in either endian, but will only actually reverse the byte order in little-endian).

I seem to recall that PNG chunk names use one of the bits to determine whether a chunk is "critical" or not. (Any critical chunks should not be removed without good reason.)
I think in effect all critical chunks have the first letter of their name set to uppercase, so you could check for that.
http://www.libpng.org/pub/png/book/chapter08.html

(I'd say it's also usually worth keeping tRNS chunks.)

Note: PNGOUT (http://www.advsys.net/ken/utils.htm) will remove extra chunks, although it also insists on processing the image.
nimdays
Posts: 236
Joined: May 29, 2014 22:01
Location: West Java, Indonesia

Re: PNG Sucker

Post by nimdays »

@jj2007, Thanks for testing, I'll fix the path.
@MrSwiss, Thanks for the correction, I'll try on Linux 64 first.
@counting_pine, Thanks for the link and input about some critical chunks.

I will fix the bug and test on 64 bit OS.
Browsing on Win32 at the moment.
Post Reply