{-# LANGUAGE ForeignFunctionInterface #-} -- | An optional add-on to the -- -- package which allows rendering diagrams in wxWidgets (using -- ). -- -- wxHaskell doesn't support transparency when drawing 'Image's, so each -- of these functions takes a 'W.Color' to use as the background color -- when rendering the 'QDiagram'. module Diagrams.Backend.WX ( -- * Drawing to DC drawDiagram -- * Drawing to uImages , withDiagramImage , renderDiagramToNewImage , renderDiagramToImage , ImageSizeException(..) ) where import Control.Exception import Control.Monad import Data.Bifunctor import Data.Typeable import Diagrams.Prelude import Diagrams.Backend.Cairo import Diagrams.Backend.Cairo.Internal import Graphics.Rendering.Cairo import Graphics.UI.WX as W hiding (bracket, when) import Graphics.UI.WX.Draw import Graphics.UI.WXCore.Image import Graphics.UI.WXCore.WxcClassesAL import Graphics.UI.WXCore.WxcClassTypes import Foreign import Foreign.C.Types -------------------------------------------------------------------------------- -- Drawing to DC --------------------------------------------------------------- -------------------------------------------------------------------------------- -- | Simple interface for drawing a diagram to a 'DC', like 'drawImage'. -- -- Note that this creates, renders to, draws, and then deletes a -- new 'Image' instance every invocation. Particularly performance-sensitive -- users may want to look at using 'renderDiagramToImage' with the same 'Image' -- instance every repaint. drawDiagram :: (Monoid b, Semigroup b) => DC a -> QDiagram Cairo V2 Double b -> W.Point -> SizeSpec V2 Double -> W.Color -> [Prop (DC a)] -> IO () drawDiagram dc diagram point size bgColor props = withDiagramImage diagram size bgColor $ \image -> drawImage dc image point props -------------------------------------------------------------------------------- -- Drawing to Images ----------------------------------------------------------- -------------------------------------------------------------------------------- -- | Safe wrapper around 'renderDiagramToNewImage' that ensures the 'Image' -- instance is properly deleted when you're done using it. -- -- > withDiagramImage diagram size = -- > bracket (renderDiagramToNewImage diagram size) imageDelete withDiagramImage :: (Monoid b, Semigroup b) => QDiagram Cairo V2 Double b -> SizeSpec V2 Double -> W.Color -> (Image () -> IO c) -> IO c withDiagramImage diagram size bgColor = bracket (renderDiagramToNewImage diagram size bgColor) imageDelete -- | Create a new 'Image' of the appropriate size and render the given -- 'QDiagram' to it with Cairo. -- -- Make sure 'imageDelete' is called to free the 'Image' instance! Using -- 'bracket' is recommended to ensure that the 'Image' is still freed in the -- case of an exception: -- -- > bracket (renderDiagramToNewImage diagram size) imageDelete doStuffWithImage -- -- 'withDiagramImage' provides a convenient wrapper for this. renderDiagramToNewImage :: (Monoid b, Semigroup b) => QDiagram Cairo V2 Double b -> SizeSpec V2 Double -> W.Color -> IO (Image ()) renderDiagramToNewImage diagram size bgColor = do let (w, h) = finalSize size diagram image <- imageCreateSized $ sz w h renderDiagramToImage' w h diagram bgColor image return image -- | Render a 'QDiagram' to an existing 'Image'. Throws an 'ImageSizeException' -- if the target 'Image' is too small for the resulting rendered diagram size. renderDiagramToImage :: (Monoid b, Semigroup b) => QDiagram Cairo V2 Double b -> SizeSpec V2 Double -> W.Color -> Image i -> IO () renderDiagramToImage diagram size bgColor image = do let (w, h) = finalSize size diagram w' <- imageGetWidth image h' <- imageGetHeight image when (w' < w || w' < h) $ throw $ ImageSizeException (w, h) (w', h') renderDiagramToImage' w h diagram bgColor image renderDiagramToImage' :: (Monoid b, Semigroup b) => Int -> Int -> QDiagram Cairo V2 Double b -> W.Color -> Image i -> IO () renderDiagramToImage' w h diagram bgColor image = do let fmt = FormatARGB32 let stride = formatStrideForWidth fmt w let size = stride * h let opt = CairoOptions { _cairoSizeSpec = fromIntegral <$> dims2D w h , _cairoOutputType = RenderOnly , _cairoBypassAdjust = False , _cairoFileName = "" } (_, r) = renderDia Cairo opt diagram bracket (callocArray size) free $ \cairoPtr -> do withImageSurfaceForData cairoPtr fmt w h stride (`renderWith` r) withImageData image $ \wxPtr -> copyCairoToWxWidgets (colorRed bgColor) (colorGreen bgColor) (colorBlue bgColor) (fromIntegral size) cairoPtr (castPtr wxPtr) finalSize :: (Monoid b, Semigroup b) => SizeSpec V2 Double -> QDiagram Cairo V2 Double b -> (Int, Int) finalSize size = unr2 . fmap ceiling . fst . sizeAdjustment size . boundingBox -- | Exception indicating that the target image was the wrong size (too small) -- for the rendered diagram size. data ImageSizeException -- | @ImageSizeException renderedDiagramSize targetImageSize@ = ImageSizeException !(Int, Int) !(Int, Int) deriving (Show, Typeable) instance Exception ImageSizeException where displayException (ImageSizeException needed avail) = "Can't render diagram with output size " ++ show needed ++ " to image of size " ++ show avail foreign import ccall "util.c copyCairoToWxWidgets" copyCairoToWxWidgets :: CFloat -> CFloat -> CFloat -> CPtrdiff -> Ptr CUChar -> Ptr CUChar -> IO ()