2

Is there a way to read the Color of a specific Point of a Canvas?

Something like:

getColor :: Canvas -> Point -> Color

I checked the Documentation at Graphics.UI.Threepenny.Canvas, but couldn't find any function for that. Maybe I just didn't see it, for I am not that long using Haskell.

If you have any hints for me, please let me know.

Thanks a lot in advance, Clem

EDIT: Thanks to Heinrich Apfelmus' answer I was able to write a working solution and wanted to share it in case someone needs the same function. Of course if you use it and make adjustments feel free to share it :)

import qualified Graphics.UI.Threepenny as UI
import Graphics.UI.Threepenny.Core
import Codec.Picture.Types

-- to UI (PixelRGB8) is also possible just change from fst to snd after the return
getCanvCol :: UI.Canvas -> UI.Point -> UI (UI.Color) 
getCanvCol canvas (x,y) = do  
-- str returns a string with comma separated values i.e. "255,0,255"  
str <- callFunction $ ffi ("(%1.getContext('2d').getImageData(%2,%3,1,1).data[0])+\
                            \\",\"+(%1.getContext('2d').getImageData(%2,%3,1,1).data[1])+\
                            \\",\"+(%1.getContext('2d').getImageData(%2,%3,1,1).data[2])") 
                            canvas x y
  return $ fst $ tripleToCol $ lsToRGB $ wordsWhen (==',') str
   where
   -- could also use splitOn
   wordsWhen     :: (Char -> Bool) -> String -> [String]
   wordsWhen p s =  case dropWhile p s of
                         "" -> []
                         s' -> w : wordsWhen p s''
                               where (w, s'') = break p s'
   -- take a list of strings and make a triple of ints 
   lsToRGB :: [String] -> (Int,Int,Int)
   lsToRGB (a:b:c:xs) = (read a, read b, read c)
   lsToRGB _          = (0,0,0) 
   -- make a triple of Int to Color needed
   tripleToCol :: (Int,Int,Int) -> (UI.Color, PixelRGB8)
   tripleToCol (r,g,b) = ((UI.RGB r g b),(PixelRGB8 r' g' b'))
     where (r',g',b') = (fromIntegral r,fromIntegral g,fromIntegral b)
clem niem
  • 35
  • 5

1 Answers1

1

(Author here)

As of threepenny-gui-0.5.0.0, there is currently no predefined function that can do this. However, you can use the included JavaScript FFI to call a JavaScript function that returns the value you need. For example, here is the source code for the drawImage function:

drawImage :: Element -> Vector -> Canvas -> UI ()
drawImage image (x,y) canvas =
    runFunction $ ffi "%1.getContext('2d').drawImage(%2,%3,%4)" canvas image x y

The ffi function allows you to call an arbitrary JavaScript function. The only trouble is that you will have to marshal the result to the type Color; at the moment, only a couple of types like Int or String are supported as return values. Have a look at the source code for examples.

Heinrich Apfelmus
  • 11,034
  • 1
  • 39
  • 67
  • Thanks a lot for the quick reply! I was able to get it working (see EDIT in post above). Although it might not be perfect, right now its good enough for me :) threepenny-gui is really awesome, thanks for that, too. – clem niem Feb 23 '15 at 18:55
  • 1
    My pleasure. :-) If you're feeling fancy, you can also try `Value` as the return type to marshal from, this is a JavaScript object thing from the `aeson` library; I think the Graphics.UI.Threepenny.Event modules has some examples for that. But since your code works, there's no need to change it. – Heinrich Apfelmus Feb 23 '15 at 22:03