TeaHS-0.3: Tea/Primitive.hs
{-# LANGUAGE NoMonomorphismRestriction #-}
-- | Includes the Primitive class for drawing primitive concerns, its instances
-- and monadic convenience functions.
module Tea.Primitive ( -- * Primitive Drawing Functions
Primitive ( rect
, setPixel
, getPixel
, clear
, roundedRect
, line
, fadeLine
, bezier
, circle
, arc
, ellipse
)
-- * Drawing options
, PrimitiveOptions (..)
, defaults
-- * Monadic convenience functions
, rectM
, setPixelM
, getPixelM
, clearM
, roundedRectM
, lineM
, fadeLineM
, bezierM
, circleM
, arcM
, ellipseM
) where
import qualified Graphics.UI.SDL as SDL
import qualified Graphics.UI.SDL.Sprig as SPG
import Control.Monad.Trans
import Tea.Tea
import Tea.BlendMode
import Tea.Bitmap
import Tea.Screen
import Tea.Color
-- |A type representing less common options used when drawing Primitives.
-- Typically you would override defaults rather than use this type directly.
data PrimitiveOptions = PrimitiveOptions { mix :: BlendMode -- ^ Which blendmode to use when drawing (default is DestAlpha)
, antialias :: Bool -- ^ Whether or not to antialias lines (default is True)
, filled :: Bool -- ^ Whether or not to fill the shape (if possible) (default is False)
, thickness :: Int -- ^ The line thickness in pixels, if the shape is not filled (default is 1)
} deriving (Show, Eq)
-- | A default set of PrimitiveOptions.
defaults :: PrimitiveOptions
defaults = PrimitiveOptions { mix = DestAlpha, antialias = True, filled = False, thickness = 1 }
colorToPixel surf (Color r g b a) = SDL.mapRGBA (SDL.surfaceGetPixelFormat surf) (fromIntegral r) (fromIntegral g) (fromIntegral b) (fromIntegral a)
withColor s c f = colorToPixel s c >>= flip f (fromIntegral $ alpha c)
alpha' = fromIntegral . alpha
line' s x1 y1 x2 y2 c _ = withColor s c $ SPG.lineBlend s x1 y1 x2 y2
bezier' s x1 y1 x2 y2 x3 y3 x4 y4 q c _ = withColor s c $ SPG.bezierBlend s x1 y1 x2 y2 x3 y3 x4 y4 (fromIntegral q)
rect' s x1 y1 x2 y2 c True = withColor s c $ SPG.rectFilledBlend s x1 y1 x2 y2
rect' s x1 y1 x2 y2 c False = withColor s c $ SPG.rectBlend s x1 y1 x2 y2
roundedRect' s x1 y1 x2 y2 r c True = withColor s c $ SPG.rectRoundFilledBlend s x1 y1 x2 y2 r
roundedRect' s x1 y1 x2 y2 r c False = withColor s c $ SPG.rectRoundBlend s x1 y1 x2 y2 r
circle' s x y r c True = withColor s c $ SPG.circleFilledBlend s x y r
circle' s x y r c False = withColor s c $ SPG.circleBlend s x y r
arc' s x y r a1 a2 c True = withColor s c $ SPG.arcFilledBlend s x y r a1 a2
arc' s x y r a1 a2 c False = withColor s c $ SPG.arcBlend s x y r a1 a2
ellipse' s x y rx ry c True = withColor s c $ SPG.ellipseFilledBlend s x y rx ry
ellipse' s x y rx ry c False = withColor s c $ SPG.ellipseBlend s x y rx ry
fadeLine' s x1 y1 x2 y2 c c2 _ = do c2' <- colorToPixel s c2; c1' <- colorToPixel s c; SPG.lineFadeBlend s x1 y1 x2 y2 c1' (alpha' c) c2' $ alpha' c2
withOptions :: PrimitiveOptions -> (Bool -> IO v) -> IO v
withOptions (PrimitiveOptions m a o t) g = do SPG.pushBlend (blendModeToSPG m)
SPG.pushAA a
SPG.pushThickness t
ret <- g o
--SPG.popThickness caused arbitrary segfaults.. can't be too bad, can it?
SPG.popAA
SPG.popBlend
return ret
pixelToColor s p = SDL.getRGBA p (SDL.surfaceGetPixelFormat s) >>= \(r,g,b,a) -> return $ Color (fromIntegral r) (fromIntegral g) (fromIntegral b) (fromIntegral a)
class Primitive v where
primitive_buffer :: v -> SDL.Surface
-- |Clear the whole buffer with RGBA 0,0,0,0
clear :: v -> Tea s Bool
clear x = liftIO $
SDL.mapRGBA (SDL.surfaceGetPixelFormat surf) 0 0 0 0 >>=
SDL.fillRect surf (Just (SDL.Rect 0 0
(SDL.surfaceGetWidth surf)
(SDL.surfaceGetHeight surf)))
where surf = primitive_buffer x
-- |Get the pixel color value at the specified coordinates
getPixel :: v -> (Int, Int) -> Tea s Color
getPixel s (x, y) = liftIO $ SPG.getPixel (primitive_buffer s) x y >>= pixelToColor (primitive_buffer s)
-- |Set the pixel color value at the specified coordinates
setPixel :: v -> (Int, Int) -> Color -> Tea s ()
setPixel s (x, y) color = liftIO $ SPG.pixel surf x y =<< colorToPixel surf color
where surf = primitive_buffer s
-- |Draw a rectangle
rect :: v -- ^ Buffer to draw to
-> (Int, Int) -- ^ Coordinates of the top-left corner of the
-- rectangle
-> Int -- ^ Width of the rectangle
-> Int -- ^ Height of the rectangle
-> Color -- ^ Color of the rectangle
-> PrimitiveOptions -- ^ Other drawing options
-> Tea s ()
rect s (x,y) w h c opts = liftIO $ withOptions opts $ rect' (primitive_buffer s) x y (x+w) (y+h) c
-- |Same as rectangle, except also takes a Float value for the radius by
-- which to round the corners
roundedRect :: v -> (Int, Int) -> Int -> Int -> Float -> Color -> PrimitiveOptions -> Tea s ()
roundedRect s (x, y) w h r c opts = liftIO $ withOptions opts $ roundedRect' (primitive_buffer s) x y (x+w) (y+h) r c
-- |Draw a line of the specified color between two coordinates.
line :: v -> (Int, Int) -> (Int, Int) -> Color -> PrimitiveOptions -> Tea s ()
line s (x1, y1) (x2, y2) c opts = liftIO $ withOptions opts $ line' (primitive_buffer s) x1 y1 x2 y2 c
-- |Same as line, except takes an extra color for a gradient effect.
fadeLine :: v -> (Int, Int) -> (Int, Int) -> Color -> Color -> PrimitiveOptions -> Tea s ()
fadeLine s (x1, y1) (x2, y2) c1 c2 opts = liftIO $ withOptions opts $ fadeLine' (primitive_buffer s) x1 y1 x2 y2 c1 c2
-- |Draw a bezier curve
bezier :: v -- ^ Buffer to draw to
-> (Int, Int) -- ^ Start coordinate
-> (Int, Int) -- ^ First control point
-> (Int, Int) -- ^ Second control point
-> (Int, Int) -- ^ End coordinate
-> Int -- ^ Quality (number of intermediate points).
-- 4-7 is normal.
-> Color -- ^ Color of the line
-> PrimitiveOptions -- ^ Other drawing options
-> Tea s ()
bezier s (x1, y1) (x2, y2) (x3, y3) (x4, y4) q c opts = liftIO $ withOptions opts $ bezier' (primitive_buffer s) x1 y1 x2 y2 x3 y3 x4 y4 q c
-- |Draw a circle, at the specified point, with the specified radius, in
-- the specified colour.
circle :: v -> (Int, Int) -> Float -> Color -> PrimitiveOptions -> Tea s ()
circle s (x, y) r c opts = liftIO $ withOptions opts $ circle' (primitive_buffer s) x y r c
-- |Draw an arc
arc :: v -- ^ Buffer to draw to
-> (Int, Int) -- ^ Point of the center of the arc's circle
-> Float -- ^ Radius of the arc's circle
-> Float -- ^ Start angle (in degrees)
-> Float -- ^ Stop angle (in degrees)
-> Color -- ^ Color of the arc
-> PrimitiveOptions -- ^ Other drawing options
-> Tea s ()
arc s (x, y) r a1 a2 c opts = liftIO $ withOptions opts $ arc' (primitive_buffer s) x y r a1 a2 c
-- |Draw an ellipse
ellipse :: v -- ^ Buffer to draw to
-> (Int, Int) -- ^ Coordinates of the center
-> Float -- ^ X axis radius
-> Float -- ^ Y axis radius
-> Color -- ^ Color of the ellipse
-> PrimitiveOptions -- ^ Other drawing options
-> Tea s ()
ellipse s (x, y) rx ry c opts = liftIO $ withOptions opts $ ellipse' (primitive_buffer s) x y rx ry c
clearM :: (Primitive a) => Tea s a -> Tea s Bool
clearM = (>>= clear)
-- s/\(.*\)M m\(.*\)/\0 = m >>= \\m' -> \1 m' \2/g
rectM
:: (Primitive a) =>
Tea s a
-> (Int, Int)
-> Int
-> Int
-> Color
-> PrimitiveOptions
-> Tea s ()
rectM m c w h l o = m >>= \m' -> rect m' c w h l o
setPixelM
:: (Primitive a) => Tea s a -> (Int, Int) -> Color -> Tea s ()
setPixelM m c l = m >>= \m' -> setPixel m' c l
getPixelM :: (Primitive a) => Tea s a -> (Int, Int) -> Tea s Color
getPixelM m c = m >>= flip getPixel c
roundedRectM
:: (Primitive a) =>
Tea s a
-> (Int, Int)
-> Int
-> Int
-> Float
-> Color
-> PrimitiveOptions
-> Tea s ()
roundedRectM m c w h r l o = m >>= \m' -> roundedRect m' c w h r l o
lineM
:: (Primitive a) =>
Tea s a
-> (Int, Int)
-> (Int, Int)
-> Color
-> PrimitiveOptions
-> Tea s ()
lineM m c1 c2 l o = m >>= \m' -> line m' c1 c2 l o
fadeLineM
:: (Primitive a) =>
Tea s a
-> (Int, Int)
-> (Int, Int)
-> Color
-> Color
-> PrimitiveOptions
-> Tea s ()
fadeLineM m c1 c2 l1 l2 o = m >>= \m' -> fadeLine m' c1 c2 l1 l2 o
bezierM
:: (Primitive a) =>
Tea s a
-> (Int, Int)
-> (Int, Int)
-> (Int, Int)
-> (Int, Int)
-> Int
-> Color
-> PrimitiveOptions
-> Tea s ()
bezierM m c1 c2 c3 c4 q l o = m >>= \m' -> bezier m' c1 c2 c3 c4 q l o
circleM
:: (Primitive a) =>
Tea s a
-> (Int, Int)
-> Float
-> Color
-> PrimitiveOptions
-> Tea s ()
circleM m c r l o = m >>= \m' -> circle m' c r l o
arcM
:: (Primitive a) =>
Tea s a
-> (Int, Int)
-> Float
-> Float
-> Float
-> Color
-> PrimitiveOptions
-> Tea s ()
arcM m c r s e l o = m >>= \m' -> arc m' c r s e l o
ellipseM
:: (Primitive a) =>
Tea s a
-> (Int, Int)
-> Float
-> Float
-> Color
-> PrimitiveOptions
-> Tea s ()
ellipseM m c rx ry l o = m >>= \m' -> ellipse m' c rx ry l o
instance Primitive Bitmap where
primitive_buffer = buffer
instance Primitive Screen where
primitive_buffer = screenBuffer