123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179 |
- {-# LANGUAGE GeneralizedNewtypeDeriving #-}
- {-# LANGUAGE FlexibleInstances #-}
- {-# LANGUAGE TypeFamilies #-}
- module Image.Pixels where
- import Data.Array (Array)
- import qualified Data.Array as Array
- import qualified Data.ByteString.Builder as BS
- import qualified Data.ByteString.Lazy as BS
- import qualified Data.Ix as Ix
- import Data.Monoid ((<>))
- import Data.Word
- import MonadLib
- data Index = Index {-# UNPACK #-} !Word32 {-# UNPACK #-} !Word32 deriving (Eq, Ord, Show)
- instance Ix.Ix Index where
- range (Index x1 y1, Index x2 y2) =
- concat [ [ Index x y
- | x <- [x1..x2] ]
- | y <- [y1..y2] ]
- index (Index x1 _, Index x2 _) (Index x y) =
- let w = (x2 - x1) + 1
- in fromIntegral (x + w * y)
- inRange (Index x1 y1, Index x2 y2) (Index x y) =
- Ix.inRange (x1, x2) x && Ix.inRange (y1, y2) y
- data RGB8 =
- RGB8 {-# UNPACK #-} !Word8
- {-# UNPACK #-} !Word8
- {-# UNPACK #-} !Word8
- deriving (Eq)
- data RGB16 =
- RGB16 {-# UNPACK #-} !Word16
- {-# UNPACK #-} !Word16
- {-# UNPACK #-} !Word16
- deriving (Eq)
- data RGBA8 =
- RGBA8 {-# UNPACK #-} !Word8
- {-# UNPACK #-} !Word8
- {-# UNPACK #-} !Word8
- {-# UNPACK #-} !Word8
- deriving (Eq)
- data RGBA16 =
- RGBA16 {-# UNPACK #-} !Word16
- {-# UNPACK #-} !Word16
- {-# UNPACK #-} !Word16
- {-# UNPACK #-} !Word16
- deriving (Eq)
- data Gray8 = Gray8 {-# UNPACK #-} !Word8 deriving (Eq)
- data Gray16 = Gray16 {-# UNPACK #-} !Word16 deriving (Eq)
- newtype BW = BW Bool deriving (Eq, Show)
- class BWPixel pixel where
- black :: pixel
- white :: pixel
- class GrayPixel pixel where
- gray :: Double -> pixel
- class RGBPixel pixel where
- red :: Double -> pixel
- green :: Double -> pixel
- blue :: Double -> pixel
- rgb :: Double -> pixel
- instance BWPixel BW where
- black = BW False
- white = BW True
- data Image pixel = Image
- { imgWidth :: Word32
- , imgHeight :: Word32
- , imgPixels :: Array Index pixel
- } deriving Show
- instance Functor Image where
- fmap f img = img { imgPixels = fmap f (imgPixels img) }
- image :: pixel -> Word32 -> Word32 -> PixelM pixel () -> Image pixel
- image def w h (PixelM mote) = snd (runId (runStateT img mote))
- where
- img = Image
- { imgWidth = w
- , imgHeight = h
- , imgPixels =
- let range = (Index 0 0, Index (w - 1) (h - 1))
- in Array.array range [ (ix, def)
- | ix <- Ix.range range
- ]
- }
- squareImg :: pixel -> Word32 -> PixelM pixel () -> Image pixel
- squareImg def s mote = image def s s mote
- getWidth :: PixelM pixel Word32
- getWidth = PixelM (fmap imgWidth get)
- getHeight :: PixelM pixel Word32
- getHeight = PixelM (fmap imgHeight get)
- horizontal :: PixelM pixel () -> PixelM pixel ()
- horizontal (PixelM mote) = PixelM $ do
- w <- fmap imgWidth get
- let flipHoriz = sets_ $ \i ->
- i { imgPixels =
- imgPixels i Array.//
- [ (ix, imgPixels i Array.! Index ((w - 1) - x) y)
- | ix@(Index x y) <- Ix.range (Array.bounds (imgPixels i))
- ]
- }
- mote
- flipHoriz
- mote
- flipHoriz
- vertical :: PixelM pixel () -> PixelM pixel ()
- vertical (PixelM mote) = PixelM $ do
- h <- fmap imgHeight get
- let flipVert = sets_ $ \i ->
- i { imgPixels =
- imgPixels i Array.//
- [ (ix, imgPixels i Array.! Index x ((h - 1) - y))
- | ix@(Index x y) <- Ix.range (Array.bounds (imgPixels i))
- ]
- }
- mote
- flipVert
- mote
- flipVert
- newtype PixelM pixel a = PixelM { runPixelM :: StateT (Image pixel) Id a }
- deriving (Functor, Applicative, Monad)
- inImage :: Word32 -> Word32 -> Image pixel -> Bool
- inImage x y Image { imgWidth = w, imgHeight = h } =
- Ix.inRange (Index 0 0, Index w h) (Index x y)
- draw :: pixel -> Word32 -> Word32 -> PixelM pixel ()
- draw p x y = PixelM $ sets_ $ \ img ->
- img { imgPixels = imgPixels img Array.// [ (Index x y, p) ] }
- scaleUp :: Word8 -> Image pixel -> Image pixel
- scaleUp n img =
- let n' = fromIntegral n
- range = ( Index 0 0
- , Index (imgWidth img * n' - 1)
- (imgHeight img * n' - 1)
- )
- in Image
- { imgWidth = imgWidth img * n'
- , imgHeight = imgHeight img * n'
- , imgPixels = Array.array range
- [ (ix, imgPixels img Array.! Index (x `div` n') (y `div` n'))
- | ix@(Index x y) <- Ix.range range
- ]
- }
- toPBM :: Image BW -> BS.ByteString
- toPBM img = BS.toLazyByteString pbmFile
- where pbmFile = BS.string7 "P1\n" <>
- BS.word32Dec (imgWidth img) <>
- BS.char7 ' ' <>
- BS.word32Dec (imgHeight img) <>
- BS.char7 '\n' <>
- mconcat [ go (imgPixels img Array.! ix)
- | ix <- Ix.range (Array.bounds (imgPixels img))
- ]
- go (BW True) = BS.char7 '0' <> BS.char7 ' '
- go (BW False) = BS.char7 '1' <> BS.char7 ' '
- savePBM :: FilePath -> Image BW -> IO ()
- savePBM path img = BS.writeFile path (toPBM img)
|