M lib/Graphics/Rendering/Rect/Backgrounds.hs => lib/Graphics/Rendering/Rect/Backgrounds.hs +11 -6
@@ 2,9 2,9 @@ module Graphics.Rendering.Rect.Backgrounds(Backgrounds(..), renderBackgrounds) w
import Graphics.Rendering.Rect.CSS.Backgrounds
import Graphics.Rendering.Rect.Types
-import Graphics.Rendering.Rect.Image (Texture)
+import Graphics.Rendering.Rect.Image (Texture(texSize))
import qualified Data.ByteString.Char8 as B8
-import Linear (M44)
+import Linear (M44, V2(..))
import Control.Monad.IO.Class (MonadIO(..))
import Data.Maybe (fromMaybe, listToMaybe)
@@ 22,19 22,24 @@ imageFragmentShader = B8.pack $ unlines [
"in vec2 coord;",
"out vec4 fcolour;",
"uniform sampler2D image;",
- "void main() { fcolour = texture(image, coord/textureSize(image, 0)); }"
+ "uniform vec2 size;",
+ "void main() { fcolour = texture(image, coord/size); }"
]
renderBackgrounds :: (MonadIO m, MonadIO n) =>
n (Backgrounds Texture -> Rects -> M44 Float -> m ())
renderBackgrounds = do
base <- renderRectWith baseFragmentShader ["colour"]
- layer <- renderRectWith imageFragmentShader []
+ layer <- renderRectWith imageFragmentShader ["size"]
return $ \self a b -> do
base [] [c $ background self] (headDef borderBox $ clip self) a b
let layers = image self `zip` (clip self ++ repeat borderBox)
- forM layers $ \(img0, clip0) ->
- layer [img0] [] clip0 a b
+ `zip` (bgSize self ++ repeat (Size Auto Auto))
+ forM layers $ \((img0, clip0), size0) ->
+ layer [img0] [
+ u $ v2 $ resolveSize (size $ clip0 a) (texSize img0) size0
+ ] clip0 a b
return ()
headDef def = fromMaybe def . listToMaybe
+v2 = uncurry V2
M lib/Graphics/Rendering/Rect/CSS/Backgrounds.hs => lib/Graphics/Rendering/Rect/CSS/Backgrounds.hs +72 -5
@@ 1,10 1,12 @@
{-# LANGUAGE OverloadedStrings, FlexibleInstances #-}
-module Graphics.Rendering.Rect.CSS.Backgrounds (Backgrounds(..)) where
+module Graphics.Rendering.Rect.CSS.Backgrounds (Backgrounds(..),
+ Resize(..), Length(..), resolveSize) where
import Stylist (PropertyParser(..), parseUnorderedShorthand)
-import Data.CSS.Syntax.Tokens (Token(..))
+import Data.CSS.Syntax.Tokens (Token(..), NumericValue(..))
import Data.Maybe (isJust, catMaybes)
import Data.Text (Text)
+import Data.Scientific (scientific, toRealFloat)
import Graphics.Rendering.Rect.CSS.Colour (ColourPallet, parseColour)
import Data.Colour (AlphaColour, transparent)
@@ 14,12 16,18 @@ data Backgrounds img = Backgrounds {
pallet :: ColourPallet,
background :: AlphaColour Float,
clip :: [Rects -> Rect],
- image :: [img]
+ image :: [img],
+ bgSize :: [Resize]
} deriving (Eq, Show, Read)
+-- We need to resolve images before we can compute the actual lengths!
+data Resize = Cover | Contain | Size Length Length deriving (Eq, Show, Read)
+data Length = Absolute Float | Scale Float | Auto deriving (Eq, Show, Read)
+
instance PropertyParser (Backgrounds Text) where
temp = Backgrounds {
- pallet = temp, background = transparent, clip = [borderBox], image = [""]
+ pallet = temp, background = transparent, clip = [borderBox],
+ image = [""], bgSize = [Size Auto Auto]
}
inherit _ = temp
priority _ = []
@@ 42,6 50,20 @@ instance PropertyParser (Backgrounds Text) where
inner [Url ret] = Just ret
inner [Function "url", String ret, RightParen] = Just ret
inner _ = Nothing
+ longhand _ self "background-size" t | val@(_:_) <- parseCSSList inner t =
+ Just self { bgSize = reverse val }
+ where -- TODO: Add shorthand support, after background-position.
+ inner [x, y] | Just a <- length x, Just b <- length y = Just $ Size a b
+ inner [Ident "contain"] = Just Contain
+ inner [Ident "cover"] = Just Cover
+ inner [Ident "auto"] = Just $ Size Auto Auto
+ inner [Ident "initial"] = Just $ Size Auto Auto
+ inner _ = Nothing
+ -- NOTE: Leave lowering other units to CatTrap.
+ length (Ident "auto") = Just Auto
+ length (Dimension _ x "px") = Just $ Absolute $ f x
+ length (Percentage _ x) = Just $ Scale $ p x
+ length _ = Nothing
longhand _ _ _ _ = Nothing
-- The multi-layered shorthand is one source of parsing complexity.
@@ 58,7 80,7 @@ instance PropertyParser (Backgrounds Text) where
-- Shouldn't happen, `inner` expands all props at least to "initial"!
| otherwise = (key, val)
inner toks | ret@(_:_) <- parseUnorderedShorthand self [
- "background-color", "background-clip", "background-color"
+ "background-color", "background-clip", "background-image"
] toks = Just ret
| otherwise = Nothing
shorthand self key val | Just _ <- longhand self self key val = [(key, val)]
@@ 68,6 90,14 @@ parseCSSList cb toks | all isJust ret = catMaybes ret
| otherwise = []
where ret = map cb $ splitList Comma toks
+f :: NumericValue -> Float
+f (NVInteger x) = fromInteger x
+f (NVNumber x) = toRealFloat x
+p :: NumericValue -> Float
+p (NVInteger x) = fromInteger x / 100
+-- Do the division while we're in base-10!
+p (NVNumber x) = toRealFloat (x/scientific 1 2)
+
------
--- Utils taken from HappStack
------
@@ 84,3 114,40 @@ split f s = (left,right)
where
(left,right')=break f s
right = if null right' then [] else tail right'
+
+------
+--- Dynamically-computed properties
+------
+
+resolveSize :: (Float, Float) -> (Float, Float) -> Resize -> (Float, Float)
+resolveSize (owidth, oheight) (width, height) Contain
+ | width > owidth, height*sw > oheight, height > width = (width*sh, height*sh)
+ | width > owidth = (width*sw, height*sw)
+ | height > oheight = (width*sh, height*sh)
+ | height > width = (width*sw, height*sw)
+ | width > height = (width*sh, height*sh)
+ where
+ sh = oheight/height
+ sw = owidth/width
+resolveSize (owidth, oheight) (width, height) Cover
+ | owidth > width, oheight > height*sw = (width*sh, height*sh)
+ | oheight > height, owidth > width*sh = (width*sw, height*sw)
+ | owidth > width = (width*sw, height*sw)
+ | oheight > height = (width*sh, height*sh)
+ | oheight > height*sw = (width*sh, height*sh)
+ | owidth > width*sh = (width*sw, height*sw)
+ | height > width = (width*sw, height*sw)
+ | width > height = (width*sh, height*sh)
+ where
+ sh = oheight/height
+ sw = owidth/width
+resolveSize _ ret (Size Auto Auto) = ret
+resolveSize _ (width, height) (Size x y) = (x', y')
+ where
+ x' | Absolute ret <- x = ret
+ | Scale s <- x = width*s
+ | Auto <- x = y' * width/height
+ y' | Absolute ret <- y = ret
+ | Scale s <- y = height*s
+ -- NOTE: If Auto,Auto case wasn't handled above this'd be an infinite loop.
+ | Auto <- y = x' * height/width
M lib/Graphics/Rendering/Rect/Image.hs => lib/Graphics/Rendering/Rect/Image.hs +10 -5
@@ 23,7 23,7 @@ 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 }
+data Atlas = Atlas { unAtlas :: HM.HashMap Text Texture }
buildAtlas :: MonadIO m => (Text -> IO DynamicImage) -> [Text] -> m Atlas
buildAtlas cb [] = return $ Atlas HM.empty
@@ 35,7 35,7 @@ buildAtlas cb srcs = do
peekArray len ptr
imgs <- liftIO $ forM srcs cb
- forM (zip textures imgs) $ \(texture, dyn) -> do
+ sizes <- 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
@@ 50,11 50,16 @@ buildAtlas cb srcs = do
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
+ return (toEnum $ dynamicMap imageWidth dyn',
+ toEnum $ dynamicMap imageHeight dyn')
-data Texture = Texture { unTexture :: GLuint }
+ let textures' = map (uncurry Texture) $ zip textures sizes
+ return $ Atlas $ HM.fromList $ zip srcs textures'
+
+data Texture = Texture { unTexture :: GLuint, texSize :: (Float, Float) }
+nilTexture = Texture 0 (0, 0)
atlasLookup :: Text -> Atlas -> Texture
-atlasLookup key = Texture . fromMaybe 0 . HM.lookup key . unAtlas
+atlasLookup key = fromMaybe nilTexture . HM.lookup key . unAtlas
-- OpenGL deals in CrCb not CbCr...
-- Convert pixels to some flavour of RGBA
M lib/Graphics/Rendering/Rect/Types.hs => lib/Graphics/Rendering/Rect/Types.hs +2 -1
@@ 1,7 1,7 @@
{-# LANGUAGE RecordWildCards #-}
-- So getters can implement typeclasses
{-# LANGUAGE TypeSynonymInstances, FlexibleInstances #-}
-module Graphics.Rendering.Rect.Types(Rect(..), Rects(..), BoxSelector,
+module Graphics.Rendering.Rect.Types(Rect(..), size, Rects(..), BoxSelector,
Uniform, u, c, renderRectWith, liftGL) where
import Linear (M44, V2(..), V4(..))
@@ 31,6 31,7 @@ rect2geom Rect{..} = UV.fromList [tl, tr, br, tl, br, bl]
where
(tl, tr) = (V2 left top, V2 right top)
(bl, br) = (V2 left bottom, V2 right bottom)
+size Rect {..} = (right - left, bottom - top)
data Rects = Rects {
contentBox :: Rect,