コードを載せる
import SDL.Video (Renderer)
import SDL.Video.Renderer (Surface,Texture,SurfacePixelFormat(..),Rectangle(..),PixelFormat(..)
,rendererDrawColor,clear,destroyTexture
,createTextureFromSurface,copyEx,present
,lockSurface,unlockSurface,surfacePixels,surfaceFormat,createRGBSurfaceFrom)
import SDL.Vect (Point(P),V2(..),V4(..))
import SDL.Internal.Numbered (fromNumber)
import qualified SDL.Raw.Types as SDLT
import Foreign.Ptr (castPtr)
import Foreign.ForeignPtr (newForeignPtr_)
import Foreign.Storable (peek)
import qualified Data.Vector.Storable.Mutable as VM
import System.Random.Shuffle (shuffleM)
import Data.Word (Word8)
draw :: Renderer -> [Surface] -> IO ()
draw re imageS = do
imageTO <- mapM (createTextureFromSurface re) imageS
let imageS0 = head imageS
lockSurface imageS0
SurfacePixelFormat pointerPixFormat <- surfaceFormat imageS0
surPixFormat <- peek pointerPixFormat
let sPixFormat = fromNumber (SDLT.pixelFormatFormat surPixFormat) :: PixelFormat
pointer0 <- surfacePixels imageS0
frPointer <- newForeignPtr_ (castPtr pointer0)
let mvector = VM.unsafeFromForeignPtr0 frPointer (4*64*64) :: (VM.MVector (VM.PrimState IO) Word8)
unlockSurface imageS0
mvector2 <- VM.clone mvector
mapM_ (\y -> mapM_ (\x -> sfl4x4 mvector2 (V2 x y)) [0..15]) [0..15]
newImageS0 <- createRGBSurfaceFrom mvector2 (V2 64 64) (4*64) sPixFormat
newImageT <- createTextureFromSurface re newImageS0
let imageTextures = imageTO ++ [newImageT]
initDraw re
imageDraw re imageTextures
mapM_ destroyTexture imageTextures
present re
きっかけは ブロックを使った操画(ゲーム)を考へてゐて そのブロックのイメージを 「もうちょっと柔らかいものにしたいな〜」 といふことだった
BABA IS YOU といふ 超名作ゲームのブロックの ほわほわする感じが すごく良くて そんなやうなイメージが創れたらいいな〜 といふ 安直な思ひがあった
しかし おそらく それをやるためには イメージを大量に用意して アニメーションさせるのではなく イメージそれ自體にアクセスして その情報を變へるようなことが必要だ と考へた
イメージにアクセスするためには SDL2の場合 surfaceの情報を取得する必要がある
SDL.Video.Renderer のドキュメントを見ると
surfacePixels :: MonadIO m => Surface -> m (Ptr ())
とあり
Obtain the pointer to the underlying pixels in a surface. You should bracket this call with lockSurface and unlockSurface, respectively.
と説明されてゐる
要は 「ほら 情報にアクセスできるポインタだよ これで後はよろしく」
みたいな感じだ
何の例もない
どうすればいいか どこにも書いてゐない
ちなみに 仮にデータが得られたとして それを どう使ふのかといへば
createRGBSurfaceFrom
:: (Functor m, MonadIO m)
=> IOVector Word8
-> V2 CInt
-> CInt
-> PixelFormat
-> m Surface
最終的に IOVector Word8 といふ型でデータが供給できればよい
んで Data.Vector.Storable.Mutable のドキュメントを見ると
unsafeFromForeignPtr0
:: Storable a
=> ForeignPtr a
-> Int
-> MVector s a
これが 私の見たところ このモジュール内で ポインタからMVector をつくる唯一(offsetなしなら)の方法だった
ちなみに つくりたいのは IOVector Word8 である
ここで
type IOVector = MVector RealWorld
と書いてある
また
MVector s a の s は (PrimMonad m => PrimState m) であり
PrimState IO が RealWordl である(らしい)
https://stackoverflow.com/questions/8959226/constructing-iovector-from-storable-mvector
この邊の理屈は 正直よく分からんかった
が 先程の
unsafeFromForeignPtr0 :: Storable a => ForeignPtr a -> Int -> MVector s a
の a を Word8 として s を PrimState IO とすれば
返り値の型は MVector RealWorld Word8
すなはち IOVector Wrod8 となる
つまり ForeignPtr Word8 型のポインタと データサイズ(Int)を與へることで
目的の型をもつデータがゲットできる といふわけだ
そこで Ptr () を ForeignPtr Word8 にしなければならない
Foreign.Ptr モジュールのドキュメントによると
castPtr :: Ptr a -> Ptr b
といふのがある
これで () を Word8にできるのでは?
といふことで castPtr を適用し
Foreign.ForeginPtr モジュールにある
newForeignPtr_ :: Ptr a -> IO (ForeignPtr a)
を適用して めでたく ForeginPtr Word8 をつくりあげることができた
さて 一番説明がやっかいさうなのは PixelFormat だ
SDL.Video.Renderer に PixelFormatは定義されてゐて
RGB888 や RGBA8888 ABGR8888 BGRA8888 など いくつものフォーマットがある
最終目的を思ひ出して欲しい
createRGBSurfaceFrom に必要なデータを揃えることだった
IOVector Word8 はゲットしたし イメージサイズも分かってゐる
ピッチといふのは ピクセル一行分が 何バイトに相當するかを示すもので
1ピクセルにつき4バイト必要な RGBA8888 や ABGR8888などは
(横の幅(px)) × 4 で求まる
殘るは PixelFormat だけなのだが
色々と適當に フォーマットを試してみて できたイメージの結果を確認していくと
これは ABGR8888 だな といふのが分かる
それ以外のものは 明らかに色が もとのイメージと異なったからだ
(といふことは ある意味 PixelFormatだけ變へてやれば 瞬時に画像の色合いを變へることができるわけだ)
けれども これから色々なイメージを讀み込まうと思ってゐる場合
常に ABGR8888とは限らないだらう
だから イメージの情報を讀み取ることで PixelFormatの情報を得たいわけだ
これに關して
surfaceFormat :: MonadIO m => Surface -> m SurfacePixelFormat
といふ函數が SDL.Video.Renderer で用意されてゐる
しかし 返ってくるのは なぜか PixelFormat ではなく SurfacePixelFormat といふ謎なやつだ
そこの部分のドキュメントを見ると
newtype SurfacePixelFormat
Constructors:
SurfacePixelFormat (Ptr PixelFormat)
となってゐる
Ptrだとおおお?!?
すなはち surface の PixelFormat を知りたくて尋ねたら 「ハイ! アドレスだよ! 見といてね?」
なんて言はれ ポカーン とするしかない状況が生じたのだ
ここで メモリのアドレスにあるデータを見る方法が必要になってくる
これは 色々検索して
https://naohaq.github.io/
を見たときに分かったのだが
「peek」といふものだ
Foreign.Storable モジュールに書かれてゐる
やった〜!
といふことで peekを使ひ その内容をPixelFormatとして createRGBSurfaceFrom に入れてみた
が・・・エラー
なんやねん といふことで peekした内容を表示してみたら
PixelFormat {pixelFormatFormat = 376840196, pixelFormatPalette = 0x0000000000000000, pixelFormatBitsPerPixel = 32, pixelFormatBytesPerPixel = 4, pixelFormatRMask = 255, pixelFormatGMask = 65280, pixelFormatBMask = 16711680, pixelFormatAMask = 4278190080}
な〜〜?!
SDL.Video.Renderer のドキュメントで
SurfacePixelFormat (Ptr PixelFormat) とあるうち PixelFormatのリンクをクリックすると
全然別のドキュメントに飛ばされる
それが
SDL.Raw.Types だ
ここで定義される PixelFormatは
data PixelFormat
Constructors:
PixelFormat
pixelFormatFormat :: !Word32
pixelFormatPalette :: !(Ptr Palette)
pixelFormatBitsPerPixel :: !Word8
pixelFormatBytesPerPixel :: !Word8
pixelFormatRMask :: !Word32
pixelFormatGMask :: !Word32
pixelFormatBMask :: !Word32
pixelFormatAMask :: !Word32
この内容は さきほど プリントさせたものと合致してゐる
んで どないしろっちゅーねん!
まう一度 SDL.Video.Renderer にある PixelFormat のドキュメントとにらめっこした
何か このふたつの PixelFormatには 關聯性がある筈なんだ・・・
ん?
FromNumber PixelFormat Word32
ToNumber PixelFormat Word32
Word32?
さういへば SDL.Raw.Types のPixelFormat の最初に Word32の型があったな・・・
それは 意味不明な數字だった・・・
そして fromNumber といふ函數があるらしい・・・
まさか あの數字が ABGR8888 を表はしてるとか???
fromNumber函數は モジュール SDL.Internal.Numbered にあった
これを SDL.Raw.Types の PixelFormat 内にある PixelFormatFormat の數字に適用すると・・・
動いた!!!
Youtube のストリーム配信で 説明しやうとやってみた
www.youtube.com
コードは githubに載せてゐる
github.com
一言いはせてほしい
Haskell は 冒険だ!