I have PNG files and the Gloss library has a Bitmap constructor for Picture. I can't use loadBMP :: FilePath -> IO Picture because of the filetype, so I'm searching how to load a PNG file, convert it to BMP, and feed it to either bitmapOfBMP :: BMP -> Picture, bitmapOfForeignPtr :: Int -> Int -> ForeignPtr Word8 -> Bool -> Picture or bitmapOfByteString :: Int -> Int -> ByteString -> Bool -> Picture.


Test with JuicyPixels

import Data.ByteString as B
import System.IO as A

import Codec.Picture.Png
import Graphics.Gloss.Interface.Pure.Game


main = do
    png <- B.readFile "samus.png"
    let img = decodePng png
    case img of
        Left x -> A.putStrLn x
        Right x -> do
            let bmp = encodeDynamicPng x
            case bmp of
                Left x -> A.putStrLn x
                Right x -> do
                    let pic = bitmapOfByteString 29 52 x True
                    game pic

game pic
    =  play
        (InWindow "Test" (700, 500) (10, 10))
        white
        30
        pic
        draw
        (const id)
        (const id)

draw bmp
    = bmp

Everything succeeds but the image is not the same at all.

share|improve this question
    
Also, just as a stylistic point, note that \_ -> id is the same as const id. – huon Aug 31 '12 at 23:11
    
I'll remember this function. – L01man Sep 1 '12 at 8:45
    
Forwhat it's worth, I have written gloss-juicy for this. – Alp Mestanogullari Oct 26 '13 at 1:16
    
Great, it looks more complete; I'll try your package! It definitely is a must for most serious Gloss projects. – L01man Dec 27 '13 at 13:48

That's why I made JuicyPixel-repa. You read in the image as a Repa array and convert it, like I did in gloss-osm, to a Picture:

repaToPicture :: Bool -> Array F.F DIM3 Word8 -> (Int, Int, Picture)
repaToPicture b arr =
let fptr = F.toForeignPtr arr
bs = BI.fromForeignPtr fptr 0 len
in (col, row, bitmapOfByteString row col bs b)
 where
  len = row * col * depth
  (Z :. row :. col :. depth) = extent arr

Alternatively, you could just use JuicyPixels directly, case over the DynamicImage type and get the underlying imgData from the contained Image.

share|improve this answer
    
Thanks, I tried the second solution. I didn't use the imgData because I don't know how to, but converted to a BMP ByteString, which doesn't show me my image but spreaded blue and pink pixels. I added the code to the question. – L01man Aug 31 '12 at 23:06
    
I managed to use your repaToPicture. I put the code in the question. What is the Bool parameter for? Why does it return (Int, Int, Picture) and not Picture? The generated Picture is "wrong", so I did: let (w, h, pic) = repaToPicture True repa; Bitmap _ _ bmp _ = pic in Bitmap w h bmp True. Now, the image shows, but it's rotated by 180 degrees counter-clockwise. – L01man Sep 5 '12 at 8:45
    
Sorry, I've been busy but I'll try to get to helping you some this weekend if you still need it by then. The boolean determines if the picture will be cached by your OpenGL system. As for the rotation, just use repa's backpermute. – Thomas M. DuBuisson Sep 5 '12 at 15:59
    
Thank you for your help! The tutorial was very helpful: it explained enough for me to hack the code and have it working and gave a ready-to-use function. I ran into some trouble with the representation stuff but computeS did the trick. Also, I said that the image was rotated by 180 degrees, but it was in fact flipped vertically, so I just changed y - j - 1 to j and it worked. – L01man Sep 5 '12 at 18:32
up vote 1 down vote accepted

Even though I didn't come with the answer alone, but thanks to Thomas' answer, I will post it here instead of inside the question.

As a reminder, the goal is to convert a BMP file into a Gloss' Picture, so I wrote a function called bmpToPic. I put it in a module, because it uses two others functions, and needs many imports. Also, repaToPicture from Thomas' answer is slighty different here.

module PngToPic
    (pngToPic)
    where

import Data.ByteString as B
import Data.Word

import Codec.Picture.Png
import Codec.Picture.Repa
import qualified Data.ByteString.Internal as BI
import Data.Array.Repa ((:.)(..), Z, Z(..), extent, DIM3, Array)
import qualified Data.Array.Repa as R
import qualified Data.Array.Repa.Repr.ForeignPtr as F
import Graphics.Gloss.Data.Picture


pngToPic :: ByteString -> Picture
pngToPic png
    = let
        Right img -- unsafe
            = decodePng png
        repa
            = imgData (convertImage img :: Img RGBA)
    in repaToPicture True repa

repaToPicture :: Bool -> Array F.F DIM3 Word8 -> Picture
repaToPicture b arr
    = bitmapOfByteString row col bs b
    where
        bs
            = BI.fromForeignPtr fptr 0 (R.size sh)
        fptr
            = F.toForeignPtr arr'
        sh@(Z :. col :. row :. depth)
            = extent arr'
        arr'
            = flipVert arr

flipVert :: Array F.F DIM3 Word8 -> Array F.F DIM3 Word8
flipVert g
    = R.computeS $ R.backpermute e flop g
    where
        e@(Z :. x :. y :. _)
            = extent g
        flop (Z :. i         :. j         :. k)
            = Z :. x - i - 1 :. j :. k

You use it like that:

import Data.ByteString as B

import Graphics.Gloss.Interface.Pure.Game

import PngToPic

main = do
    png <- B.readFile "someImage.png"
    game $ pngToPic png

game pic
    = play
        (InWindow "Test" (700, 500) (10, 10))
        white
        30
        pic
        id
        (const id)
        (const id)

and the image will show up in the middle of the window.

share|improve this answer
    
Good work finishing this up and closing the loop. – Thomas M. DuBuisson Sep 26 '12 at 4:04
    
Thank you for your help and compliment. I think of making a more general function, fileFormatToGlossPicture, for example, because it's easy and covers all the cases, and making it a package. – L01man Sep 26 '12 at 16:21

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.