module OpenCV.HighGui
(
Window
, makeWindow
, destroyWindow
, withWindow
, resizeWindow
, waitKey
, Event(..)
, EventFlags
, hasLButton
, hasRButton
, hasMButton
, hasCtrlKey
, hasShiftKey
, hasAltKey
, EventFlagsRec(..)
, flagsToRec
, MouseCallback
, setMouseCallback
, TrackbarCallback
, createTrackbar
, imshow
, imshowM
) where
import "base" Control.Concurrent.MVar
import "base" Control.Exception ( mask_, bracket )
import "base" Data.Bits ( (.&.) )
import "base" Data.Int ( Int32 )
import "base" Data.Monoid ( (<>) )
import "base" Data.Unique ( newUnique, hashUnique )
import "base" Foreign.C.String ( CString, newCString, withCString )
import "base" Foreign.Ptr ( Ptr, FunPtr, freeHaskellFunPtr )
import "base" Foreign.Marshal.Alloc ( free )
import "base" Foreign.Marshal.Utils ( new )
import "containers" Data.Map ( Map )
import qualified "containers" Data.Map as M
import qualified "inline-c" Language.C.Inline as C
import qualified "inline-c-cpp" Language.C.Inline.Cpp as C
import "primitive" Control.Monad.Primitive ( PrimState )
import "this" OpenCV.Internal.C.Inline ( openCvCtx )
import "this" OpenCV.Internal.C.Types
import "this" OpenCV.Core.Types.Mat
import "this" OpenCV.Internal.Mutable
import "this" OpenCV.TypeLevel
C.context (C.cppCtx <> openCvCtx)
C.include "opencv2/core.hpp"
C.include "opencv2/highgui.hpp"
C.using "namespace cv"
data TrackbarState
= TrackbarState
{ trackbarCallback :: !(FunPtr C'TrackbarCallback)
, trackbarValuePtr :: !(Ptr Int32)
}
data Window
= Window
{ windowName :: !CString
, windowMouseCallback :: !(MVar (Maybe (FunPtr C'MouseCallback)))
, windowTrackbars :: !(MVar (Map String TrackbarState))
}
freeTrackbar :: TrackbarState -> IO ()
freeTrackbar trackbar = do
freeHaskellFunPtr $ trackbarCallback trackbar
free $ trackbarValuePtr trackbar
makeWindow :: String -> IO Window
makeWindow title = do
name <- show . hashUnique <$> newUnique
c'name <- newCString name
mouseCallback <- newMVar Nothing
trackbars <- newMVar M.empty
withCString title $ \c'title ->
[C.block| void {
char * cname = $(char * c'name);
cv::namedWindow(cname, cv::WINDOW_NORMAL | cv::WINDOW_KEEPRATIO);
cv::setWindowTitle(cname, $(char * c'title));
}|]
pure Window
{ windowName = c'name
, windowMouseCallback = mouseCallback
, windowTrackbars = trackbars
}
destroyWindow :: Window -> IO ()
destroyWindow window = mask_ $ do
[C.exp| void { cv::destroyWindow($(char * c'name)); }|]
free c'name
modifyMVar_ (windowMouseCallback window) $ \mbMouseCallback -> do
mapM_ freeHaskellFunPtr mbMouseCallback
pure Nothing
modifyMVar_ (windowTrackbars window) $ \trackbars -> do
mapM_ freeTrackbar trackbars
pure M.empty
where
c'name :: CString
c'name = windowName window
withWindow :: String -> (Window -> IO a) -> IO a
withWindow title = bracket (makeWindow title) destroyWindow
resizeWindow :: Window -> Int32 -> Int32 -> IO ()
resizeWindow window width height =
[C.exp| void { cv::resizeWindow($(char * c'name), $(int32_t width), $(int32_t height)); }|]
where
c'name :: CString
c'name = windowName window
waitKey :: Int32 -> IO Int32
waitKey delay = [C.exp| int32_t { cv::waitKey($(int32_t delay)) }|]
data Event
= EventMouseMove
| EventLButtonDown
| EventRButtonDown
| EventMButtonDown
| EventLButtonUp
| EventRButtonUp
| EventMButtonUp
| EventLButtonDbClick
| EventRButtonDbClick
| EventMButtonDbClick
| EventMouseWheel
| EventMouseHWheel
deriving Show
newtype EventFlags = EventFlags Int32
matchEventFlag :: Int32 -> EventFlags -> Bool
matchEventFlag flag = \(EventFlags flags) -> flags .&. flag /= 0
hasLButton :: EventFlags -> Bool
hasRButton :: EventFlags -> Bool
hasMButton :: EventFlags -> Bool
hasCtrlKey :: EventFlags -> Bool
hasShiftKey :: EventFlags -> Bool
hasAltKey :: EventFlags -> Bool
c'EVENT_FLAG_LBUTTON = 1
c'EVENT_FLAG_LBUTTON :: (Num a) => a
c'EVENT_FLAG_RBUTTON = 2
c'EVENT_FLAG_RBUTTON :: (Num a) => a
c'EVENT_FLAG_MBUTTON = 4
c'EVENT_FLAG_MBUTTON :: (Num a) => a
c'EVENT_FLAG_CTRLKEY = 8
c'EVENT_FLAG_CTRLKEY :: (Num a) => a
c'EVENT_FLAG_SHIFTKEY = 16
c'EVENT_FLAG_SHIFTKEY :: (Num a) => a
c'EVENT_FLAG_ALTKEY = 32
c'EVENT_FLAG_ALTKEY :: (Num a) => a
hasLButton = matchEventFlag c'EVENT_FLAG_LBUTTON
hasRButton = matchEventFlag c'EVENT_FLAG_RBUTTON
hasMButton = matchEventFlag c'EVENT_FLAG_MBUTTON
hasCtrlKey = matchEventFlag c'EVENT_FLAG_CTRLKEY
hasShiftKey = matchEventFlag c'EVENT_FLAG_SHIFTKEY
hasAltKey = matchEventFlag c'EVENT_FLAG_ALTKEY
data EventFlagsRec
= EventFlagsRec
{ flagsLButton :: !Bool
, flagsRButton :: !Bool
, flagsMButton :: !Bool
, flagsCtrlKey :: !Bool
, flagsShiftKey :: !Bool
, flagsAltKey :: !Bool
} deriving Show
flagsToRec :: EventFlags -> EventFlagsRec
flagsToRec flags =
EventFlagsRec
{ flagsLButton = hasLButton flags
, flagsRButton = hasRButton flags
, flagsMButton = hasMButton flags
, flagsCtrlKey = hasCtrlKey flags
, flagsShiftKey = hasShiftKey flags
, flagsAltKey = hasAltKey flags
}
c'EVENT_MOUSEMOVE = 0
c'EVENT_MOUSEMOVE :: (Num a) => a
c'EVENT_LBUTTONDOWN = 1
c'EVENT_LBUTTONDOWN :: (Num a) => a
c'EVENT_RBUTTONDOWN = 2
c'EVENT_RBUTTONDOWN :: (Num a) => a
c'EVENT_MBUTTONDOWN = 3
c'EVENT_MBUTTONDOWN :: (Num a) => a
c'EVENT_LBUTTONUP = 4
c'EVENT_LBUTTONUP :: (Num a) => a
c'EVENT_RBUTTONUP = 5
c'EVENT_RBUTTONUP :: (Num a) => a
c'EVENT_MBUTTONUP = 6
c'EVENT_MBUTTONUP :: (Num a) => a
c'EVENT_LBUTTONDBLCLK = 7
c'EVENT_LBUTTONDBLCLK :: (Num a) => a
c'EVENT_RBUTTONDBLCLK = 8
c'EVENT_RBUTTONDBLCLK :: (Num a) => a
c'EVENT_MBUTTONDBLCLK = 9
c'EVENT_MBUTTONDBLCLK :: (Num a) => a
c'EVENT_MOUSEWHEEL = 10
c'EVENT_MOUSEWHEEL :: (Num a) => a
c'EVENT_MOUSEHWHEEL = 11
c'EVENT_MOUSEHWHEEL :: (Num a) => a
unmarshalEvent :: Int32 -> Event
unmarshalEvent event
| event == c'EVENT_MOUSEMOVE = EventMouseMove
| event == c'EVENT_LBUTTONDOWN = EventLButtonDown
| event == c'EVENT_RBUTTONDOWN = EventRButtonDown
| event == c'EVENT_MBUTTONDOWN = EventMButtonDown
| event == c'EVENT_LBUTTONUP = EventLButtonUp
| event == c'EVENT_RBUTTONUP = EventRButtonUp
| event == c'EVENT_MBUTTONUP = EventMButtonUp
| event == c'EVENT_LBUTTONDBLCLK = EventLButtonDbClick
| event == c'EVENT_RBUTTONDBLCLK = EventRButtonDbClick
| event == c'EVENT_MBUTTONDBLCLK = EventMButtonDbClick
| event == c'EVENT_MOUSEWHEEL = EventMouseWheel
| event == c'EVENT_MOUSEHWHEEL = EventMouseHWheel
| otherwise = error $ "unmarshalEvent - unknown event " <> show event
type MouseCallback
= Event
-> Int32
-> Int32
-> EventFlags
-> IO ()
setMouseCallback :: Window -> MouseCallback -> IO ()
setMouseCallback window callback =
modifyMVar_ (windowMouseCallback window) $ \mbPrevCallback -> do
callbackPtr <- $(C.mkFunPtr [t| C'MouseCallback |]) c'callback
[C.exp| void { cv::setMouseCallback($(char * c'name), $(MouseCallback callbackPtr)) }|]
mapM_ freeHaskellFunPtr mbPrevCallback
pure $ Just callbackPtr
where
c'name :: CString
c'name = windowName window
c'callback :: C'MouseCallback
c'callback c'event x y c'flags _c'userDataPtr = callback event x y flags
where
event = unmarshalEvent c'event
flags = EventFlags $ fromIntegral c'flags
type TrackbarCallback
= Int32
-> IO ()
createTrackbar
:: Window
-> String
-> Int32
-> Int32
-> TrackbarCallback
-> IO ()
createTrackbar window trackbarName value count callback =
modifyMVar_ (windowTrackbars window) $ \trackbars ->
withCString trackbarName $ \c'trackbarName -> mask_ $ do
valuePtr <- new value
callbackPtr <- $(C.mkFunPtr [t| C'TrackbarCallback |]) c'callback
[C.exp| void {
(void)cv::createTrackbar
( $(char * c'trackbarName)
, $(char * c'name)
, $(int32_t * valuePtr)
, $(int32_t count)
, $(TrackbarCallback callbackPtr)
)
}|]
let (mbPrevCallback, trackbars') =
M.updateLookupWithKey (\_k _v -> Just trackbar)
trackbarName
trackbars
trackbar = TrackbarState
{ trackbarCallback = callbackPtr
, trackbarValuePtr = valuePtr
}
mapM_ freeTrackbar mbPrevCallback
pure trackbars'
where
c'name :: CString
c'name = windowName window
c'callback :: C'TrackbarCallback
c'callback pos _c'userDataPtr = callback pos
imshow
:: Window
-> Mat ('S [height, width]) channels depth
-> IO ()
imshow window mat =
withPtr mat $ \matPtr ->
[C.exp| void { cv::imshow($(char * c'name), *$(Mat * matPtr)); }|]
where
c'name :: CString
c'name = windowName window
imshowM
:: Window
-> Mut (Mat ('S [height, width]) channels depth) (PrimState IO)
-> IO ()
imshowM window mat = imshow window =<< unsafeFreeze mat