PNG в BMP в Haskell (для глянца)

У меня есть файлы PNG и библиотека Gloss имеет конструктор Bitmap для Picture. Я не могу использовать loadBMP :: FilePath -> IO Picture из-за типа файла, поэтому я ищу, как загрузить файл PNG, преобразовать его в BMP и передать в bitmapOfBMP :: BMP -> Picture, bitmapOfForeignPtr :: Int -> Int -> ForeignPtr Word8 -> Bool -> Picture или bitmapOfByteString :: Int -> Int -> ByteString -> Bool -> Picture.


Тест с 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

Все получается, но изображение совсем не то.


person L01man    schedule 31.08.2012    source источник
comment
Кроме того, как стилистический момент, обратите внимание, что \_ -> id совпадает с const id.   -  person huon    schedule 01.09.2012
comment
Для этого я написал gloss-juicy.   -  person Alp Mestanogullari    schedule 26.10.2013
comment
Отлично, он выглядит более полным; Ваш пакет попробую! Это определенно необходимо для большинства серьезных проектов Gloss.   -  person L01man    schedule 27.12.2013


Ответы (3)


Именно поэтому я сделал JuicyPixel-репа. Вы читаете изображение как массив Repa и конвертируете его, как я сделал в gloss-osm на 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

В качестве альтернативы вы можете просто использовать JuicyPixels напрямую, указать регистр в типе DynamicImage и получить базовый imgData из содержащегося Image.

person Thomas M. DuBuisson    schedule 31.08.2012
comment
Спасибо, попробовал второе решение. Я не использовал imgData, потому что не знаю, как это сделать, а преобразовал в BMP ByteString, который не показывает мне мое изображение, а расплывается в синих и розовых пикселях. Я добавил код к вопросу. - person L01man; 01.09.2012
comment
Мне удалось воспользоваться вашим repaToPicture. Ввожу код в вопрос. Для чего нужен параметр Bool? Почему он возвращает (Int, Int, Picture), а не Picture? Сгенерированный Picture неверен, поэтому я сделал: let (w, h, pic) = repaToPicture True repa; Bitmap _ _ bmp _ = pic in Bitmap w h bmp True. Теперь изображение отображается, но оно повернуто на 180 градусов против часовой стрелки. - person L01man; 05.09.2012
comment
Извините, я был занят, но я постараюсь помочь вам в эти выходные, если вам это все еще нужно к тому времени. Логическое значение определяет, будет ли изображение кэшироваться вашей системой OpenGL. Что касается ротации, просто используйте backpermute от repa. - person Thomas M. DuBuisson; 05.09.2012
comment
Спасибо за помощь! Учебник был очень полезен: он объяснил мне достаточно, чтобы взломать код и заставить его работать, и дал готовую к использованию функцию. У меня возникли некоторые проблемы с представлением, но computeS сделал свое дело. Кроме того, я сказал, что изображение было повернуто на 180 градусов, но на самом деле оно было перевернуто по вертикали, поэтому я просто изменил y - j - 1 на j, и это сработало. - person L01man; 05.09.2012

Хотя я пришел не только с ответом, но благодаря ответу Томаса я опубликую его здесь, а не внутри вопроса.

Напоминаю, что цель состоит в том, чтобы преобразовать файл BMP в изображение Gloss, поэтому я написал функцию под названием bmpToPic. Я поместил его в модуль, потому что он использует две другие функции и требует много импорта. Кроме того, repaToPicture из ответа Томаса здесь немного отличается.

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

Вы используете это так:

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)

и изображение появится в середине окна.

person L01man    schedule 05.09.2012
comment
Спасибо за вашу помощь и комплимент. Я думаю сделать более общую функцию, например, fileFormatToGlossPicture, потому что она проста и охватывает все случаи, и сделать ее пакетом. - person L01man; 26.09.2012

Для тех, кто наткнулся на это в 2017 году, это стало значительно проще! Мне потребовалось некоторое время, чтобы понять, так как приведенный выше пример кода больше не работает. Вот моя функция для чтения PNG в изображение:

import Codec.Picture.Repa (readImageRGBA, toByteString, reverseColorChannel)
import Graphics.Gloss

readPng :: FilePath -> Int -> Int -> IO Picture
readPng path w h = do
  (Right img) <- readImageRGBA path
  let bs = toByteString $ reverseColorChannel img
  return $ bitmapOfByteString w h (BitmapFormat TopToBottom PxRGBA) bs True
person David Ackerman    schedule 26.11.2017
comment
Будет ли это работать с Play от Graphics.Gloss.Interface.Pure.Game? Я пробовал здесь, но получаю сообщение «Не удалось сопоставить ожидаемый тип Picture' with actual type Int -> Int -> IO Picture». Может быть, я делаю что-то не так... Есть ли способ перевести IO Picture в Picture? - person chr0m1ng; 29.11.2017
comment
@alitalvez Это тип IO, потому что ему нужно читать из файловой системы. В Haskell вы не можете просто превратить IO Picture в Picture в чистой функции, поскольку она взаимодействует с файловой системой. Таким образом, я вижу для вас варианты: либо запустить этот код до инициализации игры (например, thePicture <- readPng "/mypath.png"), либо вы можете переключиться на Graphics.Gloss.Interface.IO.Game, чтобы использовать его в своей игре. Я бы начал с первого! - person David Ackerman; 03.12.2017