diagrams-wx-0.1.0.0: src/Diagrams/Backend/WX.hs
{-# LANGUAGE ForeignFunctionInterface #-}
-- | An optional add-on to the
-- <https://hackage.haskell.org/package/diagrams-cairo diagrams-cairo>
-- package which allows rendering diagrams in wxWidgets (using
-- <https://wiki.haskell.org/WxHaskell wxHaskell>).
--
-- 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 ()