I am trying to load a PNG file, get the uncompressed RGBA bytes, then send them to the gzip or zlib packages.
The pngload package returns image data as a (StorableArray (Int, Int) Word8), and the compression packages take lazy ByteStrings. Therefore, I am attempting to build a (StorableArray (Int, Int) Word8 -> ByteString) function.
So far, I have tried the following:
import qualified Codec.Image.PNG as PNG
import Control.Monad (mapM)
import Data.Array.Storable (withStorableArray)
import qualified Data.ByteString.Lazy as LB (ByteString, pack, take)
import Data.Word (Word8)
import Foreign (Ptr, peekByteOff)
main = do
-- Load PNG into "image"...
bytes <- withStorableArray
(PNG.imageData image)
(bytesFromPointer lengthOfImageData)
bytesFromPointer :: Int -> Ptr Word8 -> IO LB.ByteString
bytesFromPointer count pointer = LB.pack $
mapM (peekByteOff pointer) [0..(count-1)]
This causes the stack to run out of memory, so clearly I am doing something very wrong. There are more things I could try with Ptr's and ForeignPtr's, but there are a lot of "unsafe" functions in there.
Any help here would be appreciated; I'm fairly stumped.
Generally, pack and unpack are a bad idea for performance. If you have a Ptr, and a length in bytes, you can generate a strict bytestring in two different ways:
Like this:
import qualified Codec.Image.PNG as PNG
import Control.Monad
import Data.Array.Storable (withStorableArray)
import Codec.Compression.GZip
import qualified Data.ByteString.Lazy as L
import qualified Data.ByteString.Unsafe as S
import Data.Word
import Foreign
-- Pack a Ptr Word8 as a strict bytestring, then box it to a lazy one, very
-- efficiently
bytesFromPointer :: Int -> Ptr Word8 -> IO L.ByteString
bytesFromPointer n ptr = do
s <- S.unsafePackCStringLen (castPtr ptr, n)
return $! L.fromChunks [s]
-- Dummies, since they were not provided
image = undefined
lengthOfImageData = 10^3
-- Load a PNG, and compress it, writing it back to disk
main = do
bytes <- withStorableArray
(PNG.imageData image)
(bytesFromPointer lengthOfImageData)
L.writeFile "foo" . compress $ bytes
I'm using the O(1) version, that just repackages the Ptr from the StorableArray. You might wish to copy it first, via "packCStringLen".