----------------------------------------------------------------------------- -- | -- Module : Graphics.Rendering.Cairo.Internal.Drawing.Paths -- Copyright : (c) Paolo Martini 2005 -- License : BSD-style (see cairo/COPYRIGHT) -- -- Maintainer : p.martini@neuralnoise.com -- Stability : experimental -- Portability : portable -- -- Creating paths and manipulating path data. ----------------------------------------------------------------------------- module Graphics.Rendering.Cairo.Internal.Drawing.Paths where {#import Graphics.Rendering.Cairo.Types#} import Foreign import Foreign.C import Foreign.Marshal.Alloc (mallocBytes,finalizerFree) import Graphics.Rendering.Cairo.Internal.Utilities (CairoString(..)) {#context lib="cairo" prefix="cairo"#} {#pointer *path_t as CPath newtype#} unPath :: CPath -> Ptr CPath unPath (CPath p) = p {#fun get_current_point as getCurrentPoint { unCairo `Cairo', alloca- `Double' peekFloatConv*, alloca- `Double' peekFloatConv* } -> `()'#} {#fun new_path as newPath { unCairo `Cairo' } -> `()'#} {#fun close_path as closePath { unCairo `Cairo' } -> `()'#} {#fun arc as arc { unCairo `Cairo', `Double', `Double', `Double', `Double', `Double' } -> `()'#} {#fun arc_negative as arcNegative { unCairo `Cairo', `Double', `Double', `Double', `Double', `Double' } -> `()'#} {#fun curve_to as curveTo { unCairo `Cairo', `Double', `Double', `Double', `Double', `Double', `Double' } -> `()'#} {#fun line_to as lineTo { unCairo `Cairo', `Double', `Double' } -> `()'#} {#fun move_to as moveTo { unCairo `Cairo', `Double', `Double' } -> `()'#} {#fun rectangle as rectangle { unCairo `Cairo', `Double', `Double', `Double', `Double' } -> `()'#} textPath :: CairoString string => Cairo -> string -> IO () textPath c string = withUTFString string $ \string' -> {# call text_path #} c string' {#fun rel_curve_to as relCurveTo { unCairo `Cairo', `Double', `Double', `Double', `Double', `Double', `Double' } -> `()'#} {#fun rel_line_to as relLineTo { unCairo `Cairo', `Double', `Double' } -> `()'#} {#fun rel_move_to as relMoveTo { unCairo `Cairo', `Double', `Double' } -> `()'#} {#fun copy_path as copyPathC { unCairo `Cairo' } -> `CPath' CPath #} {#fun copy_path_flat as copyPathFlatC { unCairo `Cairo' } -> `CPath' CPath #} {#fun append_path as appendPathC { unCairo `Cairo', unPath `CPath' } -> `()' #} {#fun path_destroy as pathDestroy { unPath `CPath' } -> `()' #} {#fun path_extents as pathExtents { unCairo `Cairo', alloca- `Double' peekFloatConv*, alloca- `Double' peekFloatConv*, alloca- `Double' peekFloatConv*, alloca- `Double' peekFloatConv* } -> `()' #} {#enum path_data_type_t as PathDataRecordType {underscoreToCase} deriving(Eq,Show)#} data PathDataRecord = PathHeaderRecord PathDataRecordType Int | PathPointRecord Double Double deriving (Eq,Show) copyPath :: Cairo -> IO [PathElement] copyPath ctx = do p <- copyPathC ctx xs <- pathToList p pathDestroy p return xs copyPathFlat :: Cairo -> IO [PathElement] copyPathFlat ctx = do p <- copyPathFlatC ctx xs <- pathToList p pathDestroy p return xs appendPath :: Cairo -> [PathElement] -> IO () appendPath ctx es = do path <- mkPathPtr es appendPathC ctx path deallocPath path pathToList :: CPath -> IO [PathElement] pathToList p = pathToList' <$> pathToList'' p pathToList' :: [PathDataRecord] -> [PathElement] pathToList' [] = [] pathToList' ((PathHeaderRecord htype hlen):rs) | hlen >= 1 = let (mine,rest) = splitAt (hlen-1) rs in (consElem htype mine) : pathToList' rest | otherwise = error "invalid path data (invalid header length)" pathToList' _ = error "invalid path data (expected header record)" pathToList'' :: CPath -> IO [PathDataRecord] pathToList'' (CPath p) = do numdata <- {#get path_t->num_data #} p dptr <- {#get path_t->data#} p getPathData 0 (cIntConv numdata) (castPtr dptr) where size = {#sizeof path_data_t#} getPathData :: Int -> Int -> Ptr PathDataRecord -> IO [PathDataRecord] getPathData currpos numdata dptr | currpos < numdata = do let dptr' = dptr `plusPtr` (size*currpos) h@(PathHeaderRecord _ hlen) <- peekHeader dptr' ds <- peekPoints dptr' hlen rest <- getPathData (currpos+hlen) numdata dptr return$ h:(ds++rest) | otherwise = return [] peekHeader :: Ptr PathDataRecord -> IO PathDataRecord peekHeader p = do -- the more intuitive statement -- htype <- {#get path_data_t->header.type #} p -- generates an error -- "CHS module contains errors: The phrase `type' is not allowed here." htype <- peekByteOff p 0 :: IO CInt hlen <- {#get path_data_t->header.length #} p return$ PathHeaderRecord (cToEnum htype) (cIntConv hlen) peekPoint :: Ptr PathDataRecord -> IO PathDataRecord peekPoint p = do x <- {#get path_data_t->point.x #} p y <- {#get path_data_t->point.y #} p return$ PathPointRecord (cFloatConv x) (cFloatConv y) peekPoints :: Ptr PathDataRecord -> Int -> IO [PathDataRecord] peekPoints p n = mapM (\i -> peekPoint (p `plusPtr` (size*i))) [1..(n-1)] getPts = \(PathPointRecord x y) -> (x,y) pokeRecord :: Ptr PathDataRecord -> PathDataRecord -> IO () pokeRecord ptr (PathHeaderRecord htype hlen) = do pokeByteOff ptr 0 (cFromEnum htype :: CInt) -- the member named 'type' of the header is misunderstood by c2hs (see above) {#set path_data_t->header.length #} ptr (cIntConv hlen) pokeRecord ptr (PathPointRecord x y) = do {#set path_data_t->point.x #} ptr (cFloatConv x) {#set path_data_t->point.y #} ptr (cFloatConv y) consElem :: PathDataRecordType -> [PathDataRecord] -> PathElement consElem PathMoveTo ps | length ps < 1 = error "invalid path data (not enough points)" | otherwise = uncurry MoveTo $ getPts (ps!!0) consElem PathLineTo ps | length ps < 1 = error "invalid path data (not enough points)" | otherwise = uncurry LineTo $ getPts (ps!!0) consElem PathCurveTo ps | length ps < 3 = error "invalid path data (not enough points)" | otherwise = let ps' = map getPts (take 3 ps) in uncurry (uncurry (uncurry CurveTo (ps'!!0)) (ps'!!1)) (ps'!!2) consElem PathClosePath ps = ClosePath consRecs :: PathElement -> [PathDataRecord] consRecs (MoveTo x y) = [ PathHeaderRecord PathMoveTo 2, PathPointRecord x y] consRecs (LineTo x y) = [ PathHeaderRecord PathLineTo 2, PathPointRecord x y] consRecs (CurveTo x₀ y₀ x₁ y₁ x₂ y₂) = [ PathHeaderRecord PathCurveTo 4 , PathPointRecord x₀ y₀ , PathPointRecord x₁ y₁ , PathPointRecord x₂ y₂ ] consRecs ClosePath = [PathHeaderRecord PathClosePath 1] mkPathPtr :: [PathElement] -> IO CPath mkPathPtr es = do (dptr,numdata) <- mkDataPtr es ptr <- mallocBytes {#sizeof path_t#} {#set path_t->status #} ptr (cFromEnum StatusSuccess) {#set path_t->data #} ptr (castPtr dptr) {#set path_t->num_data #} ptr (cIntConv numdata) return (CPath ptr) mkDataPtr :: [PathElement] -> IO (Ptr PathDataRecord, Int) mkDataPtr es = do let rs = concatMap consRecs es len = length rs size = {#sizeof path_data_t#} dptr <- mallocBytes (len*size) :: IO (Ptr PathDataRecord) mapM_ (\(r,i) -> pokeRecord (dptr `plusPtr` (i*size)) r) (zip rs [0..]) return (dptr,len) deallocPath :: CPath -> IO () deallocPath (CPath ptr) = do dptr <- {#get path_t->data#} ptr free dptr free ptr