packages feed

simple-cairo-0.1.0.5: src/Graphics/Cairo/Utilities/CairoMatrixT/Internal.hsc

{-# LANGUAGE BlockArguments #-}
{-# LANGUAGE TypeApplications, ViewPatterns #-}
{-# OPTIONS_GHC -Wall -fno-warn-tabs #-}

module Graphics.Cairo.Utilities.CairoMatrixT.Internal (
	Matrix(..), IsCairoMatrixT(..),
	CairoMatrixT(..), CairoMatrixRegularT, cairoMatrixCopyFromRegular,
	cairoMatrixAlloc, cairoMatrixGet,
	cairoMatrixNew, cairoMatrixRegularNew,
	cairoMatrixNewIdentity,
	cairoMatrixNewTranslate,
	cairoMatrixNewScale, cairoMatrixRegularNewScale, cairoMatrixNewRotate,
	cairoMatrixTranslate, cairoMatrixScale, cairoMatrixRotate, cairoMatrixInvert,
	cairoMatrixMultiply,
	Distance(..), cairoMatrixTransformDistance,
	Point(..), cairoMatrixTransformPoint ) where

import Foreign.Ptr
import Foreign.ForeignPtr hiding (newForeignPtr)
import Foreign.Concurrent
import Foreign.Marshal
import Foreign.Storable
import Foreign.C.Types
import Control.Monad
import Control.Monad.Primitive
import Data.Word

import Graphics.Cairo.Exception

#include <cairo.h>

class IsCairoMatrixT mtx where
	toCairoMatrixT :: mtx s -> CairoMatrixT s
	fromCairoMatrixT :: CairoMatrixT s -> mtx s

withCairoMatrixT :: (PrimMonad m, IsCairoMatrixT mtx) =>
	mtx (PrimState m) -> (Ptr (CairoMatrixT (PrimState m)) -> IO a) -> m a
withCairoMatrixT (toCairoMatrixT -> CairoMatrixT fmtx) = unsafeIOToPrim . withForeignPtr fmtx

newtype CairoMatrixT s = CairoMatrixT (ForeignPtr (CairoMatrixT s)) deriving Show

instance IsCairoMatrixT CairoMatrixT where
	toCairoMatrixT = id
	fromCairoMatrixT = id

newtype CairoMatrixRegularT s = CairoMatrixRegularT (ForeignPtr (CairoMatrixT s)) deriving Show

instance IsCairoMatrixT CairoMatrixRegularT where
	toCairoMatrixT (CairoMatrixRegularT f) = CairoMatrixT f
	fromCairoMatrixT (CairoMatrixT f) = CairoMatrixRegularT f

cairoMatrixAlloc :: PrimMonad m =>
	(Ptr (CairoMatrixT (PrimState m)) -> IO a) ->
	m (ForeignPtr (CairoMatrixT (PrimState m)))
cairoMatrixAlloc f = unsafeIOToPrim do
	p <- mallocBytes #{size cairo_matrix_t}
	(($) <$> newForeignPtr <*> free $ p) <* f p

data Matrix = Matrix CDouble CDouble CDouble CDouble CDouble CDouble deriving Show

cairoMatrixGet :: (PrimMonad m, IsCairoMatrixT mtx) => mtx (PrimState m) -> m Matrix
cairoMatrixGet mtx = withCairoMatrixT mtx \p -> Matrix
	<$> #{peek cairo_matrix_t, xx} p <*> #{peek cairo_matrix_t, yx} p
	<*> #{peek cairo_matrix_t, xy} p <*> #{peek cairo_matrix_t, yy} p
	<*> #{peek cairo_matrix_t, x0} p <*> #{peek cairo_matrix_t, y0} p

cairoMatrixNew :: PrimMonad m =>
	CDouble -> CDouble -> CDouble -> CDouble -> CDouble -> CDouble -> 
	m (CairoMatrixT (PrimState m))
cairoMatrixNew xx yx xy yy x0 y0 = CairoMatrixT
	<$> cairoMatrixAlloc \p -> c_cairo_matrix_init p xx yx xy yy x0 y0

cairoMatrixRegularNew :: PrimMonad m =>
	CDouble -> CDouble -> CDouble -> CDouble -> CDouble -> CDouble -> 
	m (Either (CairoMatrixT (PrimState m)) (CairoMatrixRegularT (PrimState m)))
cairoMatrixRegularNew xx yx xy yy x0 y0 = mk
	<$> cairoMatrixAlloc \p -> c_cairo_matrix_init p xx yx xy yy x0 y0
	where mk = case xx * yy - yx * xy of
		0 -> Left . CairoMatrixT; _ -> Right . CairoMatrixRegularT

foreign import ccall "cairo_matrix_init" c_cairo_matrix_init ::
	Ptr (CairoMatrixT s) -> CDouble -> CDouble -> CDouble -> CDouble -> CDouble -> CDouble -> IO ()

cairoMatrixNewIdentity :: (PrimMonad m, IsCairoMatrixT mtx) => m (mtx (PrimState m))
cairoMatrixNewIdentity = fromCairoMatrixT . CairoMatrixT
	<$> cairoMatrixAlloc c_cairo_matrix_init_identity

foreign import ccall "cairo_matrix_init_identity" c_cairo_matrix_init_identity ::
	Ptr (CairoMatrixT s) -> IO ()

cairoMatrixNewTranslate :: (PrimMonad m, IsCairoMatrixT mtx) =>
	CDouble -> CDouble -> m (mtx (PrimState m))
cairoMatrixNewTranslate tx ty = fromCairoMatrixT . CairoMatrixT
	<$> cairoMatrixAlloc \p -> c_cairo_matrix_init_translate p tx ty

foreign import ccall "cairo_matrix_init_translate" c_cairo_matrix_init_translate ::
	Ptr (CairoMatrixT s) -> CDouble -> CDouble -> IO ()

cairoMatrixNewScale :: PrimMonad m =>
	CDouble -> CDouble -> m (CairoMatrixT (PrimState m))
cairoMatrixNewScale sx sy = CairoMatrixT
	<$> cairoMatrixAlloc \p -> c_cairo_matrix_init_scale p sx sy

cairoMatrixRegularNewScale :: PrimMonad m =>
	CDouble -> CDouble ->
	m (Either (CairoMatrixT (PrimState m)) (CairoMatrixRegularT (PrimState m)))
cairoMatrixRegularNewScale sx sy = mk
	<$> cairoMatrixAlloc \p -> c_cairo_matrix_init_scale p sx sy
	where mk = case (sx, sy) of
		(_, 0) -> Left . CairoMatrixT
		(0, _) -> Left . CairoMatrixT
		_ -> Right . CairoMatrixRegularT

foreign import ccall "cairo_matrix_init_scale" c_cairo_matrix_init_scale ::
	Ptr (CairoMatrixT s) -> CDouble -> CDouble -> IO ()

cairoMatrixNewRotate :: (PrimMonad m, IsCairoMatrixT mtx) => CDouble -> m (mtx (PrimState m))
cairoMatrixNewRotate rad = fromCairoMatrixT . CairoMatrixT
	<$> cairoMatrixAlloc \p -> c_cairo_matrix_init_rotate p rad

foreign import ccall "cairo_matrix_init_rotate" c_cairo_matrix_init_rotate ::
	Ptr (CairoMatrixT s) -> CDouble -> IO ()

cairoMatrixTranslate :: (PrimMonad m, IsCairoMatrixT mtx) =>
	mtx (PrimState m) -> CDouble -> CDouble -> m ()
cairoMatrixTranslate mtx tx ty =
	withCairoMatrixT mtx \p -> c_cairo_matrix_translate p tx ty

foreign import ccall "cairo_matrix_translate" c_cairo_matrix_translate ::
	Ptr (CairoMatrixT s) -> CDouble -> CDouble -> IO ()

cairoMatrixScale :: PrimMonad m =>
	CairoMatrixT (PrimState m) -> CDouble -> CDouble -> m ()
cairoMatrixScale mtx sx sy =
	withCairoMatrixT mtx \p -> c_cairo_matrix_scale p sx sy

foreign import ccall "cairo_matrix_scale" c_cairo_matrix_scale ::
	Ptr (CairoMatrixT s) -> CDouble -> CDouble -> IO ()

cairoMatrixRotate :: (PrimMonad m, IsCairoMatrixT mtx) =>
	mtx (PrimState m) -> CDouble -> m ()
cairoMatrixRotate mtx rad =
	withCairoMatrixT mtx \p -> c_cairo_matrix_rotate p rad

foreign import ccall "cairo_matrix_rotate" c_cairo_matrix_rotate ::
	Ptr (CairoMatrixT s) -> CDouble -> IO ()

cairoMatrixInvert :: PrimMonad m =>
	CairoMatrixRegularT (PrimState m) -> m ()
cairoMatrixInvert (CairoMatrixRegularT fmtx) =
	unsafeIOToPrim $ withForeignPtr fmtx \pmtx ->
		cairoStatusToThrowError =<< c_cairo_matrix_invert pmtx

foreign import ccall "cairo_matrix_invert" c_cairo_matrix_invert ::
	Ptr (CairoMatrixT s) -> IO #{type cairo_status_t}

cairoMatrixMultiply :: (PrimMonad m, IsCairoMatrixT mtx) =>
	mtx (PrimState m) -> mtx (PrimState m) -> mtx (PrimState m) -> m ()
cairoMatrixMultiply
	(toCairoMatrixT -> CairoMatrixT fr)
	(toCairoMatrixT -> CairoMatrixT fa)
	(toCairoMatrixT -> CairoMatrixT fb) = unsafeIOToPrim
	$ withForeignPtr fr \pr -> withForeignPtr fa \pa -> withForeignPtr fb \pb ->
		c_cairo_matrix_multiply pr pa pb

foreign import ccall "cairo_matrix_multiply" c_cairo_matrix_multiply ::
	Ptr (CairoMatrixT s) -> Ptr (CairoMatrixT s) -> Ptr (CairoMatrixT s) -> IO ()

data Distance = Distance CDouble CDouble deriving Show

cairoMatrixTransformDistance :: (PrimMonad m, IsCairoMatrixT mtx) =>
	mtx (PrimState m) -> Distance -> m Distance
cairoMatrixTransformDistance mtx (Distance dx dy) =
	withCairoMatrixT mtx \pmtx -> alloca \pdx -> alloca \pdy -> do
		zipWithM_ poke [pdx, pdy] [dx, dy]
		c_cairo_matrix_transform_distance pmtx pdx pdy
		Distance <$> peek pdx <*> peek pdy

foreign import ccall "cairo_matrix_transform_distance" c_cairo_matrix_transform_distance ::
	Ptr (CairoMatrixT s) -> Ptr CDouble -> Ptr CDouble -> IO ()

data Point = Point CDouble CDouble deriving Show

cairoMatrixTransformPoint :: (PrimMonad m, IsCairoMatrixT mtx) =>
	mtx (PrimState m) -> Point -> m Point
cairoMatrixTransformPoint mtx (Point x y) =
	withCairoMatrixT mtx \pmtx -> alloca \px -> alloca \py -> do
		zipWithM_ poke [px, py] [x, y]
		c_cairo_matrix_transform_point pmtx px py
		Point <$> peek px <*> peek py

foreign import ccall "cairo_matrix_transform_point" c_cairo_matrix_transform_point ::
	Ptr (CairoMatrixT s) -> Ptr CDouble -> Ptr CDouble -> IO ()

cairoMatrixCopyFromRegular :: PrimMonad m =>
	CairoMatrixRegularT (PrimState m) -> m (CairoMatrixT (PrimState m))
cairoMatrixCopyFromRegular (CairoMatrixRegularT fmtx) = unsafeIOToPrim
	$ CairoMatrixT <$> withForeignPtr fmtx \pmtx -> do
		p <- cairoMatrixCopy pmtx
		newForeignPtr p (free p)

cairoMatrixCopy :: Ptr (CairoMatrixT s) -> IO (Ptr (CairoMatrixT s))
cairoMatrixCopy p0 = do
	p <- mallocBytes #{size cairo_matrix_t}
	#{poke cairo_matrix_t, xx} p =<< (#{peek cairo_matrix_t, xx} p0 :: IO CDouble)
	#{poke cairo_matrix_t, yx} p =<< (#{peek cairo_matrix_t, yx} p0 :: IO CDouble)
	#{poke cairo_matrix_t, xy} p =<< (#{peek cairo_matrix_t, xy} p0 :: IO CDouble)
	#{poke cairo_matrix_t, yy} p =<< (#{peek cairo_matrix_t, yy} p0 :: IO CDouble)
	#{poke cairo_matrix_t, x0} p =<< (#{peek cairo_matrix_t, x0} p0 :: IO CDouble)
	#{poke cairo_matrix_t, y0} p =<< (#{peek cairo_matrix_t, y0} p0 :: IO CDouble)
	pure p