packages feed

simple-pango-0.1.0.1: src/Graphics/Pango/LowLevel/TabStops/Internal.hsc

{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE BlockArguments, LambdaCase #-}
{-# LANGUAGE PatternSynonyms #-}
{-# OPTIONS_GHC -Wall -fno-warn-tabs #-}

module Graphics.Pango.LowLevel.TabStops.Internal (
	-- * PANGO TAB ARRAY
	PangoTabArray(..), makePangoTabArray, pangoTabArrayThaw,
	pangoTabArrayGetTab, pangoTabArrayGetTabs,

	-- * PANGO TAB ARRAY NULLABLE
	PangoTabArrayNullable(..), makePangoTabArrayNullable,
	pangoTabArrayFromNullable, pangoTabArrayToNullable,

	-- * PANGO TAB ARRAY FIXED
	PangoTabArrayFixed, pangoTabArrayFixedFreeze,
	pangoTabArrayFixedNew, pangoTabArrayFixedSetTab,

	-- * PANGO TAB ARRAY INT
	PangoTabArrayInt, pangoTabArrayIntFreeze,
	pangoTabArrayIntNew, pangoTabArrayIntSetTab,
	) where

import Foreign.Ptr
import Foreign.Ptr.Misc
import Foreign.ForeignPtr hiding (newForeignPtr)
import Foreign.Concurrent
import Foreign.Marshal
import Foreign.Storable
import Foreign.C.Types
import Foreign.C.Enum
import Control.Arrow
import Control.Monad.Primitive
import Data.Foldable
import Data.Word
import Data.Int
import System.IO.Unsafe

import Graphics.Pango.Basic.GlyphStorage

#include <pango/pango.h>

newtype PangoTabArray = PangoTabArray (ForeignPtr PangoTabArray) deriving Show

makePangoTabArray :: Ptr PangoTabArray -> IO PangoTabArray
makePangoTabArray = \case
	NullPtr -> error "bad"
	p -> PangoTabArray <$> newForeignPtr p (c_pango_tab_array_free p)

data PangoTabArrayNullable
	= PangoTabArrayNull
	| PangoTabArrayNotNull (ForeignPtr PangoTabArray)
	deriving Show

makePangoTabArrayNullable :: Ptr PangoTabArray -> IO PangoTabArrayNullable
makePangoTabArrayNullable = \case
	NullPtr -> pure PangoTabArrayNull
	p -> PangoTabArrayNotNull <$> newForeignPtr p (c_pango_tab_array_free p)

pangoTabArrayFromNullable :: PangoTabArrayNullable -> Maybe PangoTabArray
pangoTabArrayFromNullable = \case
	PangoTabArrayNull -> Nothing
	PangoTabArrayNotNull fta -> Just $ PangoTabArray fta

pangoTabArrayToNullable :: Maybe PangoTabArray -> PangoTabArrayNullable
pangoTabArrayToNullable = \case
	Nothing -> PangoTabArrayNull
	Just (PangoTabArray fta) -> PangoTabArrayNotNull fta

newtype PangoTabArrayFixed s =
	PangoTabArrayFixed (ForeignPtr PangoTabArray) deriving Show

mkPangoTabArrayFixed ::
	Ptr PangoTabArray -> IO (PangoTabArrayFixed s)
mkPangoTabArrayFixed p =
	PangoTabArrayFixed <$> newForeignPtr p (c_pango_tab_array_free p)

newtype PangoTabArrayInt s =
	PangoTabArrayInt (ForeignPtr PangoTabArray) deriving Show

mkPangoTabArrayInt ::
	Ptr PangoTabArray -> IO (PangoTabArrayInt s)
mkPangoTabArrayInt p =
	PangoTabArrayInt <$> newForeignPtr p (c_pango_tab_array_free p)

foreign import ccall "pango_tab_array_free" c_pango_tab_array_free ::
	Ptr PangoTabArray -> IO ()

pangoTabArrayFixedNew ::
	PrimMonad m => m (PangoTabArrayFixed (PrimState m))
pangoTabArrayFixedNew = unsafeIOToPrim
	$ mkPangoTabArrayFixed =<< c_pango_tab_array_new 1 #{const FALSE}

pangoTabArrayIntNew ::
	PrimMonad m => m (PangoTabArrayInt (PrimState m))
pangoTabArrayIntNew = unsafeIOToPrim
	$ mkPangoTabArrayInt =<< c_pango_tab_array_new 1 #{const TRUE}

foreign import ccall "pango_tab_array_new" c_pango_tab_array_new ::
	CInt -> #{type gboolean} -> IO (Ptr PangoTabArray)

pangoTabArrayFixedSetTab :: PrimMonad m =>
	PangoTabArrayFixed (PrimState m) -> CInt -> PangoFixed -> m ()
pangoTabArrayFixedSetTab (PangoTabArrayFixed fta) idx x = unsafeIOToPrim
	$ withForeignPtr fta \pta -> do
		sz <- c_pango_tab_array_get_size_prim pta
		if idx < sz
		then c_pango_tab_array_set_tab pta idx #{const PANGO_TAB_LEFT} $ toCInt x
		else do	lst <- tempPangoTabArrayGetTab pta (sz - 1)
			let	Just (sz', tss) = calculateFixed sz idx (fromCInt lst) x
			c_pango_tab_array_resize pta sz'
			for_ (zip [sz .. sz' - 1] tss) \(i, xx) ->
				c_pango_tab_array_set_tab pta i #{const PANGO_TAB_LEFT}
					. round $ xx * #{const PANGO_SCALE}

foreign import ccall "pango_tab_array_set_tab" c_pango_tab_array_set_tab ::
	Ptr PangoTabArray -> CInt -> #{type PangoTabAlign} -> CInt -> IO ()

tempPangoTabArrayGetTab :: Ptr PangoTabArray -> CInt -> IO CInt
tempPangoTabArrayGetTab pta idx = alloca \px -> do
	c_pango_tab_array_get_tab_prim pta idx nullPtr px
	peek px

foreign import ccall "pango_tab_array_get_tab" c_pango_tab_array_get_tab_prim ::
	Ptr PangoTabArray -> CInt -> Ptr #{type PangoTabAlign} -> Ptr CInt -> IO ()

calculateFixed :: CInt -> CInt -> PangoFixed -> PangoFixed -> Maybe (CInt, [PangoFixed])
calculateFixed sz idx lst x
	| idx < sz = Nothing
	| otherwise = Just (sz', take (fromIntegral $ sz' - sz) [lst + dx, lst + 2 * dx ..])
	where
	sz' = nextPow2 $ idx + 1
	dx = (x - lst) / fromIntegral (idx - sz + 1)

calculateDouble :: CInt -> CInt -> Double -> Double -> Maybe (CInt, [Double])
calculateDouble sz idx lst x
	| idx < sz = Nothing
	| otherwise = Just (sz', take (fromIntegral $ sz' - sz) [lst + dx, lst + 2 * dx ..])
	where
	sz' = nextPow2 $ idx + 1
	dx = (x - lst) / fromIntegral (idx - sz + 1)

nextPow2 :: (Ord n, Num n) => n -> n
nextPow2 k = head . dropWhile (< k) $ (2 ^) <$> [0 :: Int ..]

pangoTabArrayIntSetTab :: PrimMonad m =>
	PangoTabArrayInt (PrimState m) -> CInt -> CInt -> m ()
pangoTabArrayIntSetTab (PangoTabArrayInt fta) idx x = unsafeIOToPrim
	$ withForeignPtr fta \pta -> do
		sz <- c_pango_tab_array_get_size_prim pta
		if idx < sz
		then c_pango_tab_array_set_tab pta idx #{const PANGO_TAB_LEFT} x
		else do	lst <- tempPangoTabArrayGetTab pta (sz - 1)
			let	Just (sz', tss) = calculateCInt sz idx lst x
			c_pango_tab_array_resize pta sz'
			for_ (zip [sz .. sz' - 1] tss) \(i, xx) ->
				c_pango_tab_array_set_tab pta i #{const PANGO_TAB_LEFT} xx

calculateCInt :: CInt -> CInt -> CInt -> CInt -> Maybe (CInt, [CInt])
calculateCInt sz idx lst x =
	second (round <$>) <$> calculateDouble sz idx (fromIntegral lst) (fromIntegral x)

foreign import ccall "pango_tab_array_get_size" c_pango_tab_array_get_size_prim ::
	Ptr PangoTabArray -> IO CInt

foreign import ccall "pango_tab_array_get_size" c_pango_tab_array_get_size ::
	Ptr PangoTabArray -> IO CInt

foreign import ccall "pango_tab_array_resize" c_pango_tab_array_resize ::
	Ptr PangoTabArray -> CInt -> IO ()

enum "PangoTabAlign" ''#{type PangoTabAlign} [''Show] [
	("PangoTabLeft", #{const PANGO_TAB_LEFT}) ]

pangoTabArrayGetTab :: PangoTabArray -> CInt -> Maybe (Either Double CInt)
pangoTabArrayGetTab (PangoTabArray fta) idx = unsafePerformIO
	$ withForeignPtr fta \pta -> alloca \loc -> do
		px <- c_pango_tab_array_get_positions_in_pixels pta
		sz <- c_pango_tab_array_get_size pta
		if 0 <= idx && idx < sz then Just <$> do
			c_pango_tab_array_get_tab pta idx nullPtr loc
			(<$> peek loc) case px of
				#{const FALSE} ->
					Left . (/ #{const PANGO_SCALE}) . fromIntegral
				#{const TRUE} -> Right
				_ -> error "never occur"
		else pure Nothing

foreign import ccall "pango_tab_array_get_tab" c_pango_tab_array_get_tab ::
	Ptr PangoTabArray -> CInt -> Ptr #{type PangoTabAlign} -> Ptr CInt -> IO ()

pangoTabArrayGetTabs :: PangoTabArray -> Either [Double] [CInt]
pangoTabArrayGetTabs (PangoTabArray fta) = unsafePerformIO
	$ withForeignPtr fta \pta -> alloca \locs -> do
		n <- c_pango_tab_array_get_size pta
		px <- c_pango_tab_array_get_positions_in_pixels pta
		c_pango_tab_array_get_tabs pta nullPtr locs
		(<$> (peekArrayAndFree n =<< peek locs)) case px of
			#{const FALSE} -> Left
				. ((/ #{const PANGO_SCALE}) . fromIntegral <$>)
			#{const TRUE} -> Right
			_ -> error "never occur"

foreign import ccall "pango_tab_array_get_tabs" c_pango_tab_array_get_tabs ::
	Ptr PangoTabArray -> Ptr (Ptr #type PangoTabAlign) -> Ptr (Ptr CInt) -> IO ()

peekArrayAndFree :: Storable a => CInt -> Ptr a -> IO [a]
peekArrayAndFree n p = peekArray (fromIntegral n) p <* free p

foreign import ccall "pango_tab_array_get_positions_in_pixels"
	c_pango_tab_array_get_positions_in_pixels ::
	Ptr PangoTabArray -> IO #type gboolean

pangoTabArrayIntFreeze :: PrimMonad m =>
	PangoTabArrayInt (PrimState m) -> m PangoTabArray
pangoTabArrayIntFreeze (PangoTabArrayInt fta) =
	unsafeIOToPrim $ withForeignPtr fta \pta ->
		makePangoTabArray =<< c_pango_tab_array_freeze pta

pangoTabArrayFixedFreeze :: PrimMonad m =>
	PangoTabArrayFixed (PrimState m) -> m PangoTabArray
pangoTabArrayFixedFreeze (PangoTabArrayFixed fta) =
	unsafeIOToPrim $ withForeignPtr fta \pta ->
		makePangoTabArray =<< c_pango_tab_array_freeze pta

pangoTabArrayThaw :: PrimMonad m => PangoTabArray -> m (Either
	(PangoTabArrayFixed (PrimState m))
	(PangoTabArrayInt (PrimState m)))
pangoTabArrayThaw (PangoTabArray fta) =
	unsafeIOToPrim $ withForeignPtr fta \pta -> do
		px <- c_pango_tab_array_get_positions_in_pixels pta
		c_pango_tab_array_thaw pta >>= case px of
			#{const FALSE} -> (Left <$>) . mkPangoTabArrayFixed
			#{const TRUE} -> (Right <$>) . mkPangoTabArrayInt
			_ -> error "never occur"

foreign import ccall "pango_tab_array_copy" c_pango_tab_array_freeze ::
	Ptr PangoTabArray -> IO (Ptr PangoTabArray)

foreign import ccall "pango_tab_array_copy" c_pango_tab_array_thaw ::
	Ptr PangoTabArray -> IO (Ptr PangoTabArray)