packages feed

simple-cairo-0.1.0.5: src/Graphics/Cairo/Surfaces/PngSupport.hsc

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

module Graphics.Cairo.Surfaces.PngSupport (
	cairoSurfaceCreateFromPng, cairoSurfaceCreateFromPngByteString,
	cairoSurfaceWriteToPng
	) where

import Foreign.Ptr
import Foreign.ForeignPtr
import Foreign.Marshal.Utils
import Foreign.C
import Control.Monad.Primitive
import Control.Concurrent.STM
import Data.Word
import Data.ByteString qualified as BS
import Data.ByteString.Internal qualified as BS

import Graphics.Cairo.Surfaces.CairoSurfaceT.Internal
import Graphics.Cairo.Exception

import Graphics.Cairo.Surfaces.ImageSurfaces

#include <cairo.h>

foreign import ccall "cairo_surface_write_to_png" c_cairo_surface_write_to_png ::
	Ptr (CairoSurfaceT s ps) -> CString -> IO #type cairo_status_t

cairoSurfaceWriteToPng :: IsCairoSurfaceT sfc => sfc s ps -> FilePath -> IO CairoStatusT
cairoSurfaceWriteToPng (toCairoSurfaceT -> CairoSurfaceT s) fp = withCString fp \cs -> CairoStatusT
	<$> (withForeignPtr s \p -> c_cairo_surface_write_to_png p cs)

foreign import ccall "cairo_image_surface_create_from_png" c_cairo_surface_create_from_png ::
	CString -> IO (Ptr (CairoSurfaceT s ps))

cairoSurfaceCreateFromPng :: FilePath -> IO (CairoSurfaceImageT s ps)
cairoSurfaceCreateFromPng fp = withCString fp \cs ->
	mkCairoSurfaceImageT =<< c_cairo_surface_create_from_png cs

cairoSurfaceCreateFromPngByteString :: PrimMonad m =>
	BS.ByteString -> m (CairoSurfaceImageT s (PrimState m))
cairoSurfaceCreateFromPngByteString bs = unsafeIOToPrim do
	tbs <- atomically $ newTVar bs
	fn <- c_cairo_read_func_t $ byteStringToCCairoReadFunc tbs
	mkCairoSurfaceImageT
		=<< c_cairo_image_surface_create_from_png_stream fn nullPtr

foreign import ccall "cairo_image_surface_create_from_png_stream"
	c_cairo_image_surface_create_from_png_stream ::
	FunPtr (CCairoReadFunc a) -> Ptr a -> IO (Ptr (CairoSurfaceT s ps))

type CCairoReadFunc a = Ptr a -> CString -> #{type unsigned int} -> IO #{type cairo_status_t}

foreign import ccall "wrapper" c_cairo_read_func_t ::
	CCairoReadFunc a -> IO (FunPtr (CCairoReadFunc a))

byteStringToCCairoReadFunc :: TVar BS.ByteString -> CCairoReadFunc a
byteStringToCCairoReadFunc tbs _cls dt ln = do
	bs <- atomically $ readTVar tbs
	if BS.length bs < (fromIntegral ln) then
		pure #{const CAIRO_STATUS_READ_ERROR} else do
		tx' <- atomically do
			let	(tx, bs') = BS.splitAt (fromIntegral ln) bs
			writeTVar tbs bs'
			pure tx
		let	(fptr, ln') = BS.toForeignPtr0 tx'
		withForeignPtr fptr \ptr -> do
			copyBytes dt (castPtr ptr) ln'
			pure #{const CAIRO_STATUS_SUCCESS}