Pixels.hs 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179
  1. {-# LANGUAGE GeneralizedNewtypeDeriving #-}
  2. {-# LANGUAGE FlexibleInstances #-}
  3. {-# LANGUAGE TypeFamilies #-}
  4. module Image.Pixels where
  5. import Data.Array (Array)
  6. import qualified Data.Array as Array
  7. import qualified Data.ByteString.Builder as BS
  8. import qualified Data.ByteString.Lazy as BS
  9. import qualified Data.Ix as Ix
  10. import Data.Monoid ((<>))
  11. import Data.Word
  12. import MonadLib
  13. data Index = Index {-# UNPACK #-} !Word32 {-# UNPACK #-} !Word32 deriving (Eq, Ord, Show)
  14. instance Ix.Ix Index where
  15. range (Index x1 y1, Index x2 y2) =
  16. concat [ [ Index x y
  17. | x <- [x1..x2] ]
  18. | y <- [y1..y2] ]
  19. index (Index x1 _, Index x2 _) (Index x y) =
  20. let w = (x2 - x1) + 1
  21. in fromIntegral (x + w * y)
  22. inRange (Index x1 y1, Index x2 y2) (Index x y) =
  23. Ix.inRange (x1, x2) x && Ix.inRange (y1, y2) y
  24. data RGB8 =
  25. RGB8 {-# UNPACK #-} !Word8
  26. {-# UNPACK #-} !Word8
  27. {-# UNPACK #-} !Word8
  28. deriving (Eq)
  29. data RGB16 =
  30. RGB16 {-# UNPACK #-} !Word16
  31. {-# UNPACK #-} !Word16
  32. {-# UNPACK #-} !Word16
  33. deriving (Eq)
  34. data RGBA8 =
  35. RGBA8 {-# UNPACK #-} !Word8
  36. {-# UNPACK #-} !Word8
  37. {-# UNPACK #-} !Word8
  38. {-# UNPACK #-} !Word8
  39. deriving (Eq)
  40. data RGBA16 =
  41. RGBA16 {-# UNPACK #-} !Word16
  42. {-# UNPACK #-} !Word16
  43. {-# UNPACK #-} !Word16
  44. {-# UNPACK #-} !Word16
  45. deriving (Eq)
  46. data Gray8 = Gray8 {-# UNPACK #-} !Word8 deriving (Eq)
  47. data Gray16 = Gray16 {-# UNPACK #-} !Word16 deriving (Eq)
  48. newtype BW = BW Bool deriving (Eq, Show)
  49. class BWPixel pixel where
  50. black :: pixel
  51. white :: pixel
  52. class GrayPixel pixel where
  53. gray :: Double -> pixel
  54. class RGBPixel pixel where
  55. red :: Double -> pixel
  56. green :: Double -> pixel
  57. blue :: Double -> pixel
  58. rgb :: Double -> pixel
  59. instance BWPixel BW where
  60. black = BW False
  61. white = BW True
  62. data Image pixel = Image
  63. { imgWidth :: Word32
  64. , imgHeight :: Word32
  65. , imgPixels :: Array Index pixel
  66. } deriving Show
  67. instance Functor Image where
  68. fmap f img = img { imgPixels = fmap f (imgPixels img) }
  69. image :: pixel -> Word32 -> Word32 -> PixelM pixel () -> Image pixel
  70. image def w h (PixelM mote) = snd (runId (runStateT img mote))
  71. where
  72. img = Image
  73. { imgWidth = w
  74. , imgHeight = h
  75. , imgPixels =
  76. let range = (Index 0 0, Index (w - 1) (h - 1))
  77. in Array.array range [ (ix, def)
  78. | ix <- Ix.range range
  79. ]
  80. }
  81. squareImg :: pixel -> Word32 -> PixelM pixel () -> Image pixel
  82. squareImg def s mote = image def s s mote
  83. getWidth :: PixelM pixel Word32
  84. getWidth = PixelM (fmap imgWidth get)
  85. getHeight :: PixelM pixel Word32
  86. getHeight = PixelM (fmap imgHeight get)
  87. horizontal :: PixelM pixel () -> PixelM pixel ()
  88. horizontal (PixelM mote) = PixelM $ do
  89. w <- fmap imgWidth get
  90. let flipHoriz = sets_ $ \i ->
  91. i { imgPixels =
  92. imgPixels i Array.//
  93. [ (ix, imgPixels i Array.! Index ((w - 1) - x) y)
  94. | ix@(Index x y) <- Ix.range (Array.bounds (imgPixels i))
  95. ]
  96. }
  97. mote
  98. flipHoriz
  99. mote
  100. flipHoriz
  101. vertical :: PixelM pixel () -> PixelM pixel ()
  102. vertical (PixelM mote) = PixelM $ do
  103. h <- fmap imgHeight get
  104. let flipVert = sets_ $ \i ->
  105. i { imgPixels =
  106. imgPixels i Array.//
  107. [ (ix, imgPixels i Array.! Index x ((h - 1) - y))
  108. | ix@(Index x y) <- Ix.range (Array.bounds (imgPixels i))
  109. ]
  110. }
  111. mote
  112. flipVert
  113. mote
  114. flipVert
  115. newtype PixelM pixel a = PixelM { runPixelM :: StateT (Image pixel) Id a }
  116. deriving (Functor, Applicative, Monad)
  117. inImage :: Word32 -> Word32 -> Image pixel -> Bool
  118. inImage x y Image { imgWidth = w, imgHeight = h } =
  119. Ix.inRange (Index 0 0, Index w h) (Index x y)
  120. draw :: pixel -> Word32 -> Word32 -> PixelM pixel ()
  121. draw p x y = PixelM $ sets_ $ \ img ->
  122. img { imgPixels = imgPixels img Array.// [ (Index x y, p) ] }
  123. scaleUp :: Word8 -> Image pixel -> Image pixel
  124. scaleUp n img =
  125. let n' = fromIntegral n
  126. range = ( Index 0 0
  127. , Index (imgWidth img * n' - 1)
  128. (imgHeight img * n' - 1)
  129. )
  130. in Image
  131. { imgWidth = imgWidth img * n'
  132. , imgHeight = imgHeight img * n'
  133. , imgPixels = Array.array range
  134. [ (ix, imgPixels img Array.! Index (x `div` n') (y `div` n'))
  135. | ix@(Index x y) <- Ix.range range
  136. ]
  137. }
  138. toPBM :: Image BW -> BS.ByteString
  139. toPBM img = BS.toLazyByteString pbmFile
  140. where pbmFile = BS.string7 "P1\n" <>
  141. BS.word32Dec (imgWidth img) <>
  142. BS.char7 ' ' <>
  143. BS.word32Dec (imgHeight img) <>
  144. BS.char7 '\n' <>
  145. mconcat [ go (imgPixels img Array.! ix)
  146. | ix <- Ix.range (Array.bounds (imgPixels img))
  147. ]
  148. go (BW True) = BS.char7 '0' <> BS.char7 ' '
  149. go (BW False) = BS.char7 '1' <> BS.char7 ' '
  150. savePBM :: FilePath -> Image BW -> IO ()
  151. savePBM path img = BS.writeFile path (toPBM img)