1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
{-# LANGUAGE OverloadedStrings #-}
module Graphics.Rendering.Rect.Image(
Atlas, buildAtlas, Texture(..), atlasLookup) where
import qualified Data.HashMap.Lazy as HM
import Data.Text (Text)
import Codec.Picture (DynamicImage(..), Image(..), PixelRGBA8(..), PixelF,
pixelMap, generateImage)
import Codec.Picture.Types (promoteImage, dynamicMap, convertImage)
import Control.Monad.IO.Class (MonadIO(..))
import Control.Monad (forM)
import Data.Maybe (fromMaybe)
import Typograffiti.GL
import Graphics.GL.Core32
import Graphics.GL.Types
import Graphics.GL.Ext.EXT.Cmyka
import Graphics.GL.Ext.SGIX.Ycrcb
import Graphics.GL.Compatibility32
import Data.Vector.Storable (unsafeWith, unsafeCast, Vector)
import Foreign.Ptr (castPtr)
import Foreign.Marshal.Array (allocaArray, peekArray)
data Atlas = Atlas { unAtlas :: HM.HashMap Text GLuint }
buildAtlas :: MonadIO m => (Text -> IO DynamicImage) -> [Text] -> m Atlas
buildAtlas cb [] = return $ Atlas HM.empty
buildAtlas cb srcs = do
-- TODO Merge textures into an actual atlas.
let len = length srcs
textures <- liftIO $ allocaArray (toEnum len) $ \ptr -> do
glGenTextures (toEnum len) ptr
peekArray len ptr
imgs <- liftIO $ forM srcs cb
forM (zip textures imgs) $ \(texture, dyn) -> do
-- NOTE: `unsafe` crashes with a divide-by-zero given a `Vector ()`
let dyn' = convertDyn dyn
let img = dynamicMap (unsafeCast . imageData) dyn' :: Vector Word
let (format, word) = glFormat dyn'
liftIO $ glBindTexture GL_TEXTURE_2D texture
liftIO $ unsafeWith img $ glTexImage2D GL_TEXTURE_2D 0 GL_RGBA
(toEnum $ dynamicMap imageWidth dyn')
(toEnum $ dynamicMap imageHeight dyn')
0 format word . castPtr
liftIO $ glTexParameteri GL_TEXTURE_2D GL_TEXTURE_MIN_FILTER GL_LINEAR
liftIO $ glTexParameteri GL_TEXTURE_2D GL_TEXTURE_MAG_FILTER GL_LINEAR
liftIO $ glTexParameteri GL_TEXTURE_2D GL_TEXTURE_WRAP_S GL_REPEAT
liftIO $ glTexParameteri GL_TEXTURE_2D GL_TEXTURE_WRAP_T GL_REPEAT
return $ Atlas $ HM.fromList $ zip srcs textures
data Texture = Texture { unTexture :: GLuint }
atlasLookup :: Text -> Atlas -> Texture
atlasLookup key = Texture . fromMaybe 0 . HM.lookup key . unAtlas
-- OpenGL deals in CrCb not CbCr...
-- Convert pixels to some flavour of RGBA
convertDyn :: DynamicImage -> DynamicImage
convertDyn (ImageY8 img) = ImageRGBA8 $ promoteImage img
convertDyn (ImageY16 img) = ImageRGBA16 $ promoteImage img
convertDyn (ImageY32 img) =
ImageRGBF $ promoteImage (pixelMap fromIntegral img :: Image PixelF)
convertDyn (ImageYF img) = ImageRGBF $ promoteImage img
convertDyn (ImageYA8 img) = ImageRGBA8 $ promoteImage img
convertDyn (ImageYA16 img) = ImageRGBA16 $ promoteImage img
convertDyn (ImageRGB8 img) = ImageRGB8 img
convertDyn (ImageRGB16 img) = ImageRGB16 img
convertDyn (ImageRGBF img) = ImageRGBF img
convertDyn (ImageRGBA8 img) = ImageRGBA8 img
convertDyn (ImageRGBA16 img) = ImageRGBA16 img
convertDyn (ImageYCbCr8 img) = ImageRGB8 $ convertImage img
convertDyn (ImageCMYK8 img) = ImageRGB8 $ convertImage img
convertDyn (ImageCMYK16 img) = ImageRGB16 $ convertImage img
glFormat (ImageY8 _) = (GL_LUMINANCE, GL_UNSIGNED_BYTE)
glFormat (ImageY16 _) = (GL_LUMINANCE, GL_UNSIGNED_SHORT)
glFormat (ImageY32 _) = (GL_LUMINANCE, GL_UNSIGNED_INT)
glFormat (ImageYF _) = (GL_LUMINANCE, GL_FLOAT)
glFormat (ImageYA8 _) = (GL_LUMINANCE_ALPHA, GL_BYTE)
glFormat (ImageYA16 _) = (GL_LUMINANCE_ALPHA, GL_UNSIGNED_SHORT)
glFormat (ImageRGB8 _) = (GL_RGB, GL_UNSIGNED_BYTE)
glFormat (ImageRGB16 _) = (GL_RGB, GL_UNSIGNED_SHORT)
glFormat (ImageRGBF _) = (GL_RGB, GL_FLOAT)
glFormat (ImageRGBA8 _) = (GL_RGBA, GL_UNSIGNED_BYTE)
glFormat (ImageRGBA16 _) = (GL_RGBA, GL_UNSIGNED_BYTE)
glFormat (ImageYCbCr8 _) = (GL_YCRCB_444_SGIX, GL_UNSIGNED_BYTE)
glFormat (ImageCMYK8 _) = (GL_CMYK_EXT, GL_UNSIGNED_BYTE)
glFormat (ImageCMYK16 _) = (GL_CMYK_EXT, GL_UNSIGNED_SHORT)