Haskell中的PNG到BMP(用于光泽)

Posted

技术标签:

【中文标题】Haskell中的PNG到BMP(用于光泽)【英文标题】:PNG to BMP in Haskell (for Gloss) 【发布时间】:2012-08-31 21:33:26 【问题描述】:

我有 PNG 文件,the Gloss library 有一个用于 PictureBitmap 构造函数。由于文件类型,我不能使用loadBMP :: FilePath -> IO Picture,所以我正在搜索如何加载PNG文件,将其转换为BMP,并将其提供给bitmapOfBMP :: BMP -> PicturebitmapOfForeignPtr :: Int -> Int -> ForeignPtr Word8 -> Bool -> PicturebitmapOfByteString :: 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

一切都成功了,但图像完全不一样。

【问题讨论】:

另外,作为文体观点,请注意\_ -&gt; idconst id 相同。 为了它的价值,我为此写了gloss-juicy。 太好了,看起来更完整了;我会试试你的包裹!对于大多数严肃的 Gloss 项目来说,这绝对是必须的。 【参考方案1】:

这就是我制作 JuicyPixel-repa 的原因。您将图像作为 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 类型,并从包含的Image 中获取底层imgData

【讨论】:

谢谢,我尝试了第二种解决方案。我没有使用imgData,因为我不知道如何使用,而是转换为 BMP ByteString,它不会显示我的图像,而是散布蓝色和粉红色像素。我将代码添加到问题中。 我设法使用了你的repaToPicture。我把代码放在问题中。 Bool 参数是干什么用的?为什么它返回(Int, Int, Picture) 而不是Picture?生成的Picture 是“错误的”,所以我做了:let (w, h, pic) = repaToPicture True repa; Bitmap _ _ bmp _ = pic in Bitmap w h bmp True。现在,图像显示了,但它逆时针旋转了 180 度。 对不起,我一直很忙,但如果到那时你还需要的话,我会尽力在这个周末帮助你。布尔值确定图片是否会被您的 OpenGL 系统缓存。至于轮换,就用repa的backpermute吧。 感谢您的帮助!该教程非常有帮助:它对我的解释足以让我破解代码并让它工作并提供一个现成的功能。我在表示方面遇到了一些麻烦,但computeS 成功了。还有,我说的是图片旋转了180度,其实是垂直翻转的,所以我把y - j - 1改成j就可以了。【参考方案2】:

尽管我没有单独给出答案,但感谢 Thomas 的回答,我会将其发布在这里而不是问题中。

提醒一下,目标是将 BMP 文件转换为 Gloss 图片,因此我编写了一个名为 bmpToPic 的函数。我把它放在一个模块中,因为它使用了另外两个函数,并且需要很多导入。 此外,Thomas 的回答中的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)

并且图像将显示在窗口中间。

【讨论】:

感谢您的帮助和赞美。我想制作一个更通用的函数,例如 fileFormatToGlossPicture,因为它很简单并且涵盖了所有情况,并将其做成一个包。【参考方案3】:

对于任何在 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

【讨论】:

这是否适用于 Graphics.Gloss.Interface.Pure.Game 中的 Play?我在这里试过,但我得到“无法匹配预期的类型Picture' with actual type Int -> Int -> IO Picture'”。也许我做错了什么......有没有办法将 IO 图片转换为图片? @alitalvez 这是一个IO 类型,因为它需要从文件系统中读取。在haskell 中,您不能只在纯函数中将IO Picture 转换为Picture,因为它与文件系统交互。因此,我为您看到的选项是在您初始化游戏之前 运行此代码(如thePicture &lt;- readPng "/mypath.png"),或者您可以切换到Graphics.Gloss.Interface.IO.Game 以在您的游戏中使用它。我会先从前者开始!

以上是关于Haskell中的PNG到BMP(用于光泽)的主要内容,如果未能解决你的问题,请参考以下文章

haskell 列表中的唯一元素

jQuery FancyBox

BMP 文件到 PNG 文件转换器

JPG、GIF、BMP和PNG格式分别是啥意思?

JPEG、GIF、PNG、BMP哪种图片格式的图片清晰一点?

Heic图片转换精灵无损转换JPG/PNG/BMP方法