Using ImagePluginPNG to (un)compress a memorybuffer

Share your advanced PureBasic knowledge/code with the community.
wilbert
PureBasic Expert
PureBasic Expert
Posts: 3942
Joined: Sun Aug 08, 2004 5:21 am
Location: Netherlands

Using ImagePluginPNG to (un)compress a memorybuffer

Post by wilbert »

After some experimenting I was able to use the PNG plugin to (un)compress a memory buffer using zlib compression.

Code: Select all

; purebasic 4.0 windows source

UsePNGImageEncoder()

Structure zstream
 next_in.l
 avail_in.l
 total_in.l
 next_out.l
 avail_out.l
 total_out.l
 msg.l
 state.l
 zalloc.l
 zfree.l
 opaque.l
 data_type.l
 adler.l
 reserved.l
EndStructure

Procedure.l Z_Compress(dest,destLen,source,sourceLen)
 
 Static strm.zstream
 strm\next_in = source
 strm\avail_in = sourceLen
 strm\next_out = dest
 strm\avail_out = destLen
 strm\zalloc = #Null
 strm\zfree = #Null
 strm\opaque = #Null

 Static zf.l
 Static err.l
 !extrn _deflateInit_
 !pushd _deflateInit_
 !popd [s_Z_Compress.v_zf]
 err = CallCFunctionFast(zf,@strm,-1,"1.2.3",SizeOf(zstream))
 If err <> 0
  ProcedureReturn zerr
 EndIf
 !extrn _deflate
 !pushd _deflate
 !popd [s_Z_Compress.v_zf]
 err = CallCFunctionFast(zf,@strm,4)
 !extrn _deflateEnd
 !pushd _deflateEnd
 !popd [s_Z_Compress.v_zf]
 If err <> 1
  CallCFunctionFast(zf,@strm)
  If err = 0
   ProcedureReturn -5
  Else
   ProcedureReturn err
  EndIf
 EndIf 
 PokeL(destLen,strm\total_out)
 ProcedureReturn CallCFunctionFast(zf,@strm)
 
EndProcedure

Procedure.l Z_Uncompress(dest,destLen,source,sourceLen)

 Static strm.zstream
 strm\next_in = source
 strm\avail_in = sourceLen
 strm\next_out = dest
 strm\avail_out = destLen
 strm\zalloc = #Null
 strm\zfree = #Null
 strm\opaque = #Null

 Static zf.l
 Static err.l
 !extrn _inflateInit_
 !pushd _inflateInit_
 !popd [s_Z_Uncompress.v_zf]
 err = CallCFunctionFast(zf,@strm,"1.2.3",SizeOf(zstream))
 If err <> 0
  ProcedureReturn zerr
 EndIf
 !extrn _inflate
 !pushd _inflate
 !popd [s_Z_Uncompress.v_zf]
 err = CallCFunctionFast(zf,@strm,4)
 !extrn _inflateEnd
 !pushd _inflateEnd
 !popd [s_Z_Uncompress.v_zf]
 If err <> 1
  CallCFunctionFast(zf,@strm)
  If err = 0
   ProcedureReturn -5
  Else
   ProcedureReturn err
  EndIf
 EndIf 
 PokeL(destLen,strm\total_out)
 ProcedureReturn CallCFunctionFast(zf,@strm)

EndProcedure


; see if it works

in = AllocateMemory(100)
out = AllocateMemory(100)
s.s = "Hello hello hello hello hello hello hello hello hello hello hello"
PokeS(in,s)
Debug Len(s)

insize.l = Len(s)
outsize.l = MemorySize(out)
Debug Z_Compress(out,@outsize,in,insize)
Debug outsize

Swap in, out

insize.l = outsize
outsize = MemorySize(out)
Debug Z_Uncompress(out,@outsize,in,insize)
Debug outsize
Last edited by wilbert on Sat Sep 02, 2006 9:57 am, edited 1 time in total.
wilbert
PureBasic Expert
PureBasic Expert
Posts: 3942
Joined: Sun Aug 08, 2004 5:21 am
Location: Netherlands

Post by wilbert »

Linux version

Code: Select all

; purebasic 3.94 linux source

UsePNGImageEncoder()

Global zf.l
 
Structure zstream
 next_in.l
 avail_in.l
 total_in.l
 next_out.l
 avail_out.l
 total_out.l
 msg.l
 state.l
 zalloc.l
 zfree.l
 opaque.l
 data_type.l
 adler.l
 reserved.l
EndStructure

Procedure.l Z_Compress(dest,destLen,source,sourceLen)
 
 Static strm.zstream
 strm\next_in = source
 strm\avail_in = sourceLen
 strm\next_out = dest
 strm\avail_out = destLen
 strm\zalloc = #Null
 strm\zfree = #Null
 strm\opaque = #Null

 Static err.l
 !extrn deflateInit_
 !pushd deflateInit_
 !popd [v_zf]
 err = CallCFunctionFast(zf,@strm,-1,"1.2.3",SizeOf(zstream))
 If err <> 0
  ProcedureReturn zerr
 EndIf
 !extrn deflate
 !pushd deflate
 !popd [v_zf]
 err = CallCFunctionFast(zf,@strm,4)
 !extrn deflateEnd
 !pushd deflateEnd
 !popd [v_zf]
 If err <> 1
  CallCFunctionFast(zf,@strm)
  If err = 0
   ProcedureReturn -5
  Else
   ProcedureReturn err
  EndIf
 EndIf 
 PokeL(destLen,strm\total_out)
 ProcedureReturn CallCFunctionFast(zf,@strm)
 
EndProcedure

Procedure.l Z_Uncompress(dest,destLen,source,sourceLen)

 Static strm.zstream
 strm\next_in = source
 strm\avail_in = sourceLen
 strm\next_out = dest
 strm\avail_out = destLen
 strm\zalloc = #Null
 strm\zfree = #Null
 strm\opaque = #Null

 Static err.l
 !extrn inflateInit_
 !pushd inflateInit_
 !popd [v_zf]
 err = CallCFunctionFast(zf,@strm,"1.2.3",SizeOf(zstream))
 If err <> 0
  ProcedureReturn zerr
 EndIf
 !extrn inflate
 !pushd inflate
 !popd [v_zf]
 err = CallCFunctionFast(zf,@strm,4)
 !extrn inflateEnd
 !pushd inflateEnd
 !popd [v_zf]
 If err <> 1
  CallCFunctionFast(zf,@strm)
  If err = 0
   ProcedureReturn -5
  Else
   ProcedureReturn err
  EndIf
 EndIf 
 PokeL(destLen,strm\total_out)
 ProcedureReturn CallCFunctionFast(zf,@strm)

EndProcedure


; see if it works

in = AllocateMemory(100)
out = AllocateMemory(100)
s.s = "Hello hello hello hello hello hello hello hello hello hello hello"
PokeS(in,s)
Debug Len(s)

insize.l = Len(s)
outsize.l = 100
Debug Z_Compress(out,@outsize,in,insize)
Debug outsize

tmp = in
in = out
out = tmp

insize.l = outsize
outsize = 100
Debug Z_Uncompress(out,@outsize,in,insize)
Debug outsize
Trond
Always Here
Always Here
Posts: 7446
Joined: Mon Sep 22, 2003 6:45 pm
Location: Norway

Post by Trond »

This looks cool, I will see if I can get it to work.
Edit: How do I know how large output buffer I will need?
wilbert
PureBasic Expert
PureBasic Expert
Posts: 3942
Joined: Sun Aug 08, 2004 5:21 am
Location: Netherlands

Post by wilbert »

It's the more or less the same as the PureZIP_UnpackMemory function from the PureZIP library. In other words ... you don't know and just have to make sure the destination buffer is large enough.

When I wrote this code I needed to decompress a compressed swf file in memory and still be able to use png files. For that purpose it worked fine.
Trond
Always Here
Always Here
Posts: 7446
Joined: Mon Sep 22, 2003 6:45 pm
Location: Norway

Post by Trond »

Code: Select all

PokeL(destLen, strm\total_out)
Is that a bug? It's poking into the memory at location destLen.
wilbert
PureBasic Expert
PureBasic Expert
Posts: 3942
Joined: Sun Aug 08, 2004 5:21 am
Location: Netherlands

Post by wilbert »

It's supposed to be that way. As a parameter you pass a memory address as you can see in the debug function used to test it. The function itself returns a status code. You pass the address of a variable that after the function is executed should contain the uncompressed size.

Maybe I should have used another variable name to indicate it's the address of a variable that is expected.
Trond
Always Here
Always Here
Posts: 7446
Joined: Mon Sep 22, 2003 6:45 pm
Location: Norway

Post by Trond »

Code: Select all

strm\avail_out = destLen
But here it wants the value? Or is it still supposed to be the pointer?
wilbert
PureBasic Expert
PureBasic Expert
Posts: 3942
Joined: Sun Aug 08, 2004 5:21 am
Location: Netherlands

Post by wilbert »

As far as I know the pointer.
I used uncompr.c from the zlib library as an example but c is not my specialty ;) You might want to look at that file but for me it has worked so far.
Num3
PureBasic Expert
PureBasic Expert
Posts: 2812
Joined: Fri Apr 25, 2003 4:51 pm
Location: Portugal, Lisbon
Contact:

Post by Num3 »

Genial!

One of the Best Tips'&'Tricks...
Trond
Always Here
Always Here
Posts: 7446
Joined: Mon Sep 22, 2003 6:45 pm
Location: Norway

Post by Trond »

It's a great trick that works very well, but this time I think I found a real bug. Shouldn't zerr be err?

Code: Select all

 If err <> 0 
  ProcedureReturn zerr 
 EndIf
Num3
PureBasic Expert
PureBasic Expert
Posts: 2812
Joined: Fri Apr 25, 2003 4:51 pm
Location: Portugal, Lisbon
Contact:

Post by Num3 »

Trond wrote:It's a great trick that works very well, but this time I think I found a real bug. Shouldn't zerr be err?

Code: Select all

 If err <> 0 
  ProcedureReturn zerr 
 EndIf
Did you read VISTA SDK ???

That's the new fail error checking routine ;)
wilbert
PureBasic Expert
PureBasic Expert
Posts: 3942
Joined: Sun Aug 08, 2004 5:21 am
Location: Netherlands

Post by wilbert »

Trond wrote:Shouldn't zerr be err?
You are right of course :)
I just never got an error so I didn't notice the problem.
Trond
Always Here
Always Here
Posts: 7446
Joined: Mon Sep 22, 2003 6:45 pm
Location: Norway

Post by Trond »

I didn't get an error either, but I turned on EnableExplicit.
javabean
User
User
Posts: 60
Joined: Sat Nov 08, 2003 10:29 am
Location: Austria

Re: Using ImagePluginPNG to (un)compress a memorybuffer

Post by javabean »

Just a little modification of wilberts code: manual import of the deflate and inflate functions from a lib without making use of ImagePluginPNG.

Code: Select all

ImportC "zlib.lib"
  inflateInit(*strm.l,*version.l,stream_size.l) As "_inflateInit_@12"
  inflate(*strm.l,flush.l) As "_inflate@8"
  inflateEnd(*strm.l) As "_inflateEnd@4"
  deflateInit(*strm.l,level.l,*version.l,stream_size.l) As "_deflateInit_@16"
  deflate(*strm.l,flush.l) As "_deflate@8"
  deflateEnd(*strm.l) As "_deflateEnd@4"
EndImport

Structure zstream
  next_in.l
  avail_in.l
  total_in.l
  next_out.l
  avail_out.l
  total_out.l
  msg.l
  state.l
  zalloc.l
  zfree.l
  opaque.l
  data_type.l
  adler.l
  reserved.l
EndStructure


#Z_OK            =  0
#Z_STREAM_END    =  1
#Z_NEED_DICT     =  2
#Z_ERRNO         = -1
#Z_STREAM_ERROR  = -2
#Z_DATA_ERROR    = -3
#Z_MEM_ERROR     = -4
#Z_BUF_ERROR     = -5
#Z_VERSION_ERROR = -6

#Z_NO_FLUSH      =  0
#Z_PARTIAL_FLUSH =  1
#Z_SYNC_FLUSH    =  2
#Z_FULL_FLUSH    =  3
#Z_FINISH        =  4
#Z_BLOCK         =  5

Global version$ ="1.2.3"

Procedure.l Z_Compress(*dest.l,*destLen.l,*source.l,sourceLen.l)

  Static strm.zstream
    strm\next_in    = *source
    strm\avail_in   = sourceLen
    strm\next_out   = *dest
    strm\avail_out  = *destLen
    strm\zalloc     = #Null
    strm\zfree      = #Null
    strm\opaque     = #Null
  
  Static err.l

  err.l = deflateInit(@strm,-1,@version$,SizeOf(zstream))
  
  If err <> 0
    ProcedureReturn err
  EndIf

  err = deflate(@strm,#Z_FINISH)

  If err <> 1
    deflateEnd(@strm)
    If err = 0
     ProcedureReturn #Z_BUF_ERROR
    Else
     ProcedureReturn err
    EndIf
  EndIf
  
  PokeL(*destLen,strm\total_out)
  ProcedureReturn deflateEnd(@strm)

EndProcedure


Procedure.l Z_Uncompress(*dest.l,*destLen.l,*source.l,sourceLen.l)

  Static strm.zstream
    strm\next_in    = *source
    strm\avail_in   = sourceLen
    strm\next_out   = *dest
    strm\avail_out  = *destLen
    strm\zalloc     = #Null
    strm\zfree      = #Null
    strm\opaque     = #Null
  
  Static err.l

  err.l = inflateInit(@strm,@version$,SizeOf(zstream))
  If err <> 0
    ProcedureReturn err
  EndIf

  err = inflate(@strm,#Z_FINISH)

  If err <> 1
    inflateEnd(@strm)
    If err = 0
     ProcedureReturn #Z_BUF_ERROR
    Else
     ProcedureReturn err
    EndIf
  EndIf
  PokeL(*destLen,strm\total_out)
  ProcedureReturn inflateEnd(@strm)

EndProcedure


; see if it works

in = AllocateMemory(100)
out = AllocateMemory(100)
s.s = "Hello hello hello hello hello hello hello hello hello hello hello"
PokeS(in,s)
Debug "Uncompressed: " + PeekS(@s)

insize.l = Len(s)
outsize.l = MemorySize(out)
Debug Z_Compress(out,@outsize,in,insize)
Debug "Compressed: "+ PeekS(out,outsize)

Swap in, out

insize.l = outsize
outsize = MemorySize(out)
Debug Z_Uncompress(out,@outsize,in,insize)
Debug "Uncompressed: "+ PeekS(out,outsize)
User avatar
ts-soft
Always Here
Always Here
Posts: 5756
Joined: Thu Jun 24, 2004 2:44 pm
Location: Berlin - Germany

Re: Using ImagePluginPNG to (un)compress a memorybuffer

Post by ts-soft »

a bit shorter and crossplattform:

Code: Select all

; Autor: Thomas (ts-soft) Schulz
; PB-Version: 4.xx
; OS: windows/linux/MacOS

CompilerSelect #PB_Compiler_OS
  CompilerCase #PB_OS_Linux
    ImportC #PB_Compiler_Home + "purelibraries/linux/libraries/zlib.a"
CompilerCase #PB_OS_MacOS
  ImportC "/usr/lib/libz.dylib"
  CompilerCase #PB_OS_Windows
    ImportC "zlib.lib"
CompilerEndSelect
  compress2(*dest, *destLen, *source, sourceLen, level)
  uncompress(*dest, *destLen, *source, sourceLen)
EndImport

Procedure zipPackMemory(*source, sourceLen = #PB_Default, level = #PB_Default)
  Protected *dest, destLen
  
  If level < #PB_Default Or level > 9 : level = #PB_Default : EndIf
  If *source
    If sourceLen = #PB_Default : sourceLen = MemorySize(*source) : EndIf
    destLen = sourceLen + 13 + (Int(sourceLen / 100))
    *dest = AllocateMemory(destLen)
    If *dest
      If Not compress2(*dest, @destLen, *source, sourceLen, level)
        *dest = ReAllocateMemory(*dest, destLen)
        ProcedureReturn *dest
      EndIf
    EndIf
  EndIf
EndProcedure

Procedure zipUnpackMemory(*source, *dest)
  Protected sourceLen = MemorySize(*source)
  Protected destLen = MemorySize(*dest)

  If Not uncompress(*dest, @destLen, *source, sourceLen)
    ProcedureReturn destLen
  EndIf
EndProcedure
Greetings
Thomas
Post Reply