bitmap (empty) → 0.0.0
raw patch · 10 files changed
+1087/−0 lines, 10 filesdep +arraydep +basedep +bytestringsetup-changed
Dependencies added: array, base, bytestring, containers
Files
- Data/Bitmap.hs +53/−0
- Data/Bitmap/Base.hs +68/−0
- Data/Bitmap/IO.hs +539/−0
- Data/Bitmap/Internal.hs +105/−0
- Data/Bitmap/Pure.hs +145/−0
- LICENSE +29/−0
- Setup.lhs +4/−0
- bitmap.cabal +43/−0
- cbits/bm.c +79/−0
- cbits/bm.h +22/−0
+ Data/Bitmap.hs view
@@ -0,0 +1,53 @@++--------------------------------------------------------------------------------+-- Module : Data.Bitmap+-- Version : 0.0.0+-- License : BSD3+-- Copyright : (c) 2009 Balazs Komuves+-- Author : Balazs Komuves+-- Maintainer : bkomuves (plus) hackage (at) gmail (dot) com+-- Stability : experimental+-- Portability : requires FFI, CPP and ScopedTypeVariables+-- Tested with : GHC 6.10.1+--------------------------------------------------------------------------------++-- | A library to handle bitmaps (uncompressed pixel rectangles).+-- The smallest storage unit is 1 byte (thus /bit/maps, in the literal sense+-- of the word, are not supported).+-- You may want to import this module qualified.+-- +-- Terminology: +-- Pixels are made out of one or more \"components\". These components +-- are also referred as \"channels\"; for example a color image could be made out +-- of three channels, the red, green and blue one. The components can be unsigned+-- bytes, words, dwords, or floats. The pixels are stored in horizontal order,+-- and the channels are interleaved: That is, the structure of an RGB image is +-- @R0 G0 B0 R1 G1 B1 ...@. +-- Most of the library is agnostic to the meaning of different channels.+--+-- \"Padding\" refers to unused bytes at the end of each row. This is sometimes+-- necessary because other software components want the rows aligned to machine+-- word boundary, for example. +--+-- The library should be relatively fast (except where noted), but +-- performance is not the primary goal (thus there is no inline assembly,+-- no SSE, etc).+--+-- There are both pure and IO versions of the API; you shouldn't mix+-- the two without taking special care.+-- This module re-exports the pure interface; if you want the IO one,+-- import 'Data.Bitmap.IO' instead.+--++{-# LANGUAGE CPP, ForeignFunctionInterface, ScopedTypeVariables #-}+module Data.Bitmap + ( module Data.Bitmap.Pure+ ) + where++--------------------------------------------------------------------------------++import Data.Bitmap.Pure++--------------------------------------------------------------------------------+
+ Data/Bitmap/Base.hs view
@@ -0,0 +1,68 @@++--------------------------------------------------------------------------------+-- Module : Data.Bitmap.Base+-- Version : 0.0.0+-- License : BSD3+-- Copyright : (c) 2009 Balazs Komuves+-- Author : Balazs Komuves+-- Maintainer : bkomuves (plus) hackage (at) gmail (dot) com+-- Stability : experimental+-- Portability : requires FFI, CPP and ScopedTypeVariables+-- Tested with : GHC 6.10.1+--------------------------------------------------------------------------------++--+-- Terminology: +-- Pixels are made out of one or more \"components\". These components +-- are also referred as \"channels\"; for example a color image could be made out +-- of three channels, the red, green and blue one. The components can be unsigned+-- bytes, words, dwords, or floats. The pixels are stored in horizontal order,+-- and the channels are interleaved: That is, the structure of an RGB image is +-- @R0 G0 B0 R1 G1 B1 ...@. +-- Most of the library is agnostic to the meaning of different channels.+-- The fixed point types (byte, word and dword) are interpreted as numbers+-- between 0 and 1; the floating point type can exceed the [0..1] interval.+--+-- \"Padding\" refers to unused bytes at the end of each row. This is sometimes+-- necessary because other software components want the rows aligned to machine+-- word boundary, for example. +--++{-# LANGUAGE CPP, ForeignFunctionInterface, ScopedTypeVariables #-}+module Data.Bitmap.Base + ( + -- * Types+ PixelComponent+ , PixelComponentType(..)+ , pixelComponentType+ --+ , Word8+ , Word16+ , Word32+ --+ , Size+ , Offset+ , NChn+ , Alignment+ , Padding+ --+ , Bitmap+ , bitmapSize + , bitmapNChannels+ , bitmapRowPadding + , bitmapRowAlignment + --+ , bitmapComponentSizeInBytes+ , bitmapPixelSizeInBytes+ , bitmapPaddedRowSizeInBytes+ , bitmapUnpaddedRowSizeInBytes+ , bitmapSizeInBytes+ ) + where++--------------------------------------------------------------------------------++import Data.Word+import Data.Bitmap.Internal++--------------------------------------------------------------------------------
+ Data/Bitmap/IO.hs view
@@ -0,0 +1,539 @@++--------------------------------------------------------------------------------+-- Module : Data.Bitmap.IO+-- Version : 0.0.0+-- License : BSD3+-- Copyright : (c) 2009 Balazs Komuves+-- Author : Balazs Komuves+-- Maintainer : bkomuves (plus) hackage (at) gmail (dot) com+-- Stability : experimental+-- Portability : requires FFI, CPP and ScopedTypeVariables+-- Tested with : GHC 6.10.1+--------------------------------------------------------------------------------++-- | The full, mutable API in the IO monad.++{-# LANGUAGE CPP, ForeignFunctionInterface, ScopedTypeVariables #-}+{-# CFILES cbits/bm.c #-} -- for Hugs +module Data.Bitmap.IO+ ( + module Data.Bitmap.Base+ -- * Creating and accessing bitmaps+ , newBitmap+ , newBitmapUninitialized+ , copyBitmapFromPtr+ , bitmapFromForeignPtrUnsafe+ , withBitmap+ -- * Mapping over bitmaps+ , componentMap+ , componentMap'+ , componentMapInPlace+ -- * Cropping and extending+ , copySubImage+ , copySubImage' + , copySubImageInto+ -- * Manipulating channels+ , combineChannels + , extractChannels + , extractSingleChannel + , extractChannelInto+ -- * Conversion to\/from ByteString+ , copyBitmapToByteString+ , copyBitmapFromByteString+ ) + where+ +--------------------------------------------------------------------------------++import Control.Monad++--import Data.Array.IArray++import Data.Word+import Data.List (nub)++import Foreign+import Foreign.C++import Data.ByteString (ByteString)+import qualified Data.ByteString as B+import qualified Data.ByteString.Internal as B++import Data.Bitmap.Internal+import Data.Bitmap.Base++--------------------------------------------------------------------------------+ +-- @newBitmapRaw (width,row) nchannels padding@. We do not initialize the new bitmap!+newBitmapRaw :: forall t. PixelComponent t => Size -> NChn -> Padding -> Alignment -> IO (Bitmap t) +newBitmapRaw siz nchn pad align = do+ let bm0 = Bitmap + { bitmapSize = siz+ , bitmapNChannels = nchn+ , bitmapPtr = undefined + , bitmapRowPadding = pad+ , bitmapRowAlignment = align+ } :: Bitmap t+ len = bitmapSizeInBytes bm0+ fptr <- mallocForeignPtrBytes len :: IO (ForeignPtr t)+ return $ bm0 { bitmapPtr = fptr }+ +-- | Note: we /cannot/ guarantee the alignment+-- of the memory block (but typically it is aligned at least to machine word boundary),+-- but what we /can/ guarantee is that the rows are properly padded.+-- +-- The resulting new bitmap is filled with zeros.+newBitmap + :: forall t. PixelComponent t + => Size -- ^ (width,height)+ -> NChn -- ^ number of channels (components\/pixel)+ -> Maybe Alignment -- ^ the row alignment of the new image+ -> IO (Bitmap t)+newBitmap siz nchn malign = do + bm <- newBitmapUninitialized siz nchn malign -- :: IO (Bitmap t)+ let fptr = bitmapPtr bm+ len = bitmapSizeInBytes bm+ withForeignPtr fptr $ \p -> c_memset (castPtr p) len 0+ return bm++newBitmapUninitialized :: forall t. PixelComponent t => Size -> NChn -> Maybe Alignment -> IO (Bitmap t)+newBitmapUninitialized siz nchn malign = do+ let bm0 = Bitmap + { bitmapSize = siz+ , bitmapNChannels = nchn+ , bitmapPtr = undefined + , bitmapRowPadding = pad+ , bitmapRowAlignment = align+ } :: Bitmap t+ x0 = bitmapUnpaddedRowSizeInBytes bm0+ (pad,align) = case malign of+ Nothing -> (0,1)+ Just align -> let x1 = align * ((x0 + align - 1) `div` align) in (x1 - x0, align)+ newBitmapRaw siz nchn pad align+ +copyBitmapFromPtr + :: forall t. PixelComponent t + => Size -- ^ (width,height) of the source+ -> NChn -- ^ number of channels in the source + -> Padding -- ^ source padding+ -> Ptr t -- ^ the source+ -> Maybe Alignment -- ^ target alignment+ -> IO (Bitmap t)+copyBitmapFromPtr siz@(w,h) nchn srcpad srcptr tgtmalign = do+ bm <- newBitmapUninitialized siz nchn tgtmalign+ withBitmap bm $ \_ _ _ tgtptr -> do+ let pure_line = bitmapUnpaddedRowSizeInBytes bm+ src_line = pure_line + srcpad+ tgt_line = bitmapPaddedRowSizeInBytes bm+ forM_ [0..h-1] $ \y -> do+ let p = srcptr `myPlusPtr` (y*src_line)+ q = tgtptr `myPlusPtr` (y*tgt_line)+ c_memcpy (castPtr p) (castPtr q) pure_line + return bm ++bitmapFromForeignPtrUnsafe + :: forall t. PixelComponent t + => Size -> NChn -> Alignment -> Padding -> ForeignPtr t -> Bitmap t+bitmapFromForeignPtrUnsafe siz nchn align pad fptr = Bitmap+ { bitmapSize = siz + , bitmapNChannels = nchn + , bitmapPtr = fptr + , bitmapRowPadding = pad + , bitmapRowAlignment = align+ }+ +-- | @withBitmap bitmap $ \\(w,h) nchn padding ptr -> ...@+withBitmap :: PixelComponent t => Bitmap t -> (Size -> NChn -> Padding -> Ptr t -> IO a) -> IO a+withBitmap bm action = + withForeignPtr (bitmapPtr bm) $ \p -> + action (bitmapSize bm) (bitmapNChannels bm) (bitmapRowPadding bm) p++--------------------------------------------------------------------------------++{-# SPECIALIZE genericComponentRowMap + :: (Int -> Ptr Word8 -> Ptr Word8 -> IO ()) -> Bitmap Word8 -> Bitmap Word8 -> IO () #-}+{-# SPECIALIZE genericComponentRowMap + :: (Int -> Ptr Word16 -> Ptr Word16 -> IO ()) -> Bitmap Word16 -> Bitmap Word16 -> IO () #-}+{-# SPECIALIZE genericComponentRowMap + :: (Int -> Ptr Word32 -> Ptr Word32 -> IO ()) -> Bitmap Word32 -> Bitmap Word32 -> IO () #-}+{-# SPECIALIZE genericComponentRowMap + :: (Int -> Ptr Float -> Ptr Float -> IO ()) -> Bitmap Float -> Bitmap Float -> IO () #-}++{-# SPECIALIZE genericComponentRowMap + :: (Int -> Ptr Word8 -> Ptr Float -> IO ()) -> Bitmap Word8 -> Bitmap Float -> IO () #-}+{-# SPECIALIZE genericComponentRowMap + :: (Int -> Ptr Float -> Ptr Word8 -> IO ()) -> Bitmap Float -> Bitmap Word8 -> IO () #-}++{-# SPECIALIZE genericComponentRowMap + :: (Int -> Ptr Word16 -> Ptr Float -> IO ()) -> Bitmap Word16 -> Bitmap Float -> IO () #-}+{-# SPECIALIZE genericComponentRowMap + :: (Int -> Ptr Float -> Ptr Word16 -> IO ()) -> Bitmap Float -> Bitmap Word16 -> IO () #-}++-- the @Int@ is the number of pixel components (nchn*width)+genericComponentRowMap + :: (PixelComponent s, PixelComponent t) + => (Int -> Ptr s -> Ptr t -> IO ()) + -> Bitmap s -> Bitmap t -> IO ()+genericComponentRowMap rowAction bm1 bm2 = do++ let (w1,h1) = bitmapSize bm1+ pad1 = bitmapRowPadding bm1+ nchn1 = bitmapNChannels bm1+ fptr1 = bitmapPtr bm1+ xlen1 = bitmapPaddedRowSizeInBytes bm1+ + let (w2,h2) = bitmapSize bm2+ pad2 = bitmapRowPadding bm2+ nchn2 = bitmapNChannels bm2+ fptr2 = bitmapPtr bm2+ xlen2 = bitmapPaddedRowSizeInBytes bm2+ + let minw = min w1 w2 + npc = nchn1 * minw+ + when (nchn1 /= nchn2) $ + error "bitmap/genericRowMap: number of channels disagree" + + withForeignPtr fptr1 $ \ptr1 -> withForeignPtr fptr2 $ \ptr2 -> + forM_ (zip (map (*xlen1) [0..h1-1]) + (map (*xlen2) [0..h2-1])) $ \(vo1,vo2) -> do+ let p1 = ptr1 `myPlusPtr` vo1 + p2 = ptr2 `myPlusPtr` vo2 + rowAction npc p1 p2 ++-------++-- the @Int@ is the number of pixels (width)+genericPixelRowMap + :: (PixelComponent s, PixelComponent t) + => (Int -> Ptr s -> NChn -> Ptr t -> NChn -> IO ()) -- width ptr1 nchn1 ptr2 nchn2 + -> Bitmap s -> Bitmap t -> IO ()+genericPixelRowMap rowAction bm1 bm2 = do++ let (w1,h1) = bitmapSize bm1+ pad1 = bitmapRowPadding bm1+ nchn1 = bitmapNChannels bm1+ fptr1 = bitmapPtr bm1+ xlen1 = bitmapPaddedRowSizeInBytes bm1+ + let (w2,h2) = bitmapSize bm2+ pad2 = bitmapRowPadding bm2+ nchn2 = bitmapNChannels bm2+ fptr2 = bitmapPtr bm2+ xlen2 = bitmapPaddedRowSizeInBytes bm2+ + let minw = min w1 w2 ++ withForeignPtr fptr1 $ \ptr1 -> withForeignPtr fptr2 $ \ptr2 -> + forM_ (zip (map (*xlen1) [0..h1-1]) (map (*xlen2) [0..h2-1])) $ \(o1,o2) -> do+ let p1 = ptr1 `plusPtr` o1 + p2 = ptr2 `plusPtr` o2 + rowAction minw p1 nchn1 p2 nchn2++--------------------------------------------------------------------------------+ +{-# SPECIALIZE genericComponentMap :: (Word8 -> Word8 ) -> Bitmap Word8 -> Bitmap Word8 -> IO () #-} +{-# SPECIALIZE genericComponentMap :: (Word16 -> Word16) -> Bitmap Word16 -> Bitmap Word16 -> IO () #-} +{-# SPECIALIZE genericComponentMap :: (Word32 -> Word32) -> Bitmap Word32 -> Bitmap Word32 -> IO () #-} +{-# SPECIALIZE genericComponentMap :: (Float -> Float ) -> Bitmap Float -> Bitmap Float -> IO () #-} ++{-# SPECIALIZE genericComponentMap :: (Word8 -> Float ) -> Bitmap Word8 -> Bitmap Float -> IO () #-} +{-# SPECIALIZE genericComponentMap :: (Float -> Word8 ) -> Bitmap Float -> Bitmap Word8 -> IO () #-}+ +{-# SPECIALIZE genericComponentMap :: (Word16 -> Float ) -> Bitmap Word16 -> Bitmap Float -> IO () #-} +{-# SPECIALIZE genericComponentMap :: (Float -> Word16) -> Bitmap Float -> Bitmap Word16 -> IO () #-} + +genericComponentMap + :: forall s t . (PixelComponent s, PixelComponent t) + => (s -> t) -> Bitmap s -> Bitmap t -> IO () +genericComponentMap f bm1 bm2 = genericComponentRowMap g bm1 bm2 where+ h :: (Ptr s, Ptr t) -> Int -> IO (Ptr s, Ptr t)+ h (q1,q2) _ = do+ x <- peek q1+ poke q2 (f x)+ return (advancePtr1 q1, advancePtr1 q2)+ g :: Int -> Ptr s -> Ptr t -> IO ()+ g n p1 p2 = do+ foldM_ h (p1,p2) [0..n-1]+++-- | Maps a function over each component of each pixel. Warning: this is slow!+-- Use a specialized function if there is one for your task.+-- +-- Note: We don't do the more general (s->t) here, because then we would have no idea +-- about the padding in the new bitmap. See `componentMap'` for that.+componentMap :: PixelComponent s => (s -> s) -> Bitmap s -> IO (Bitmap s)+componentMap f bm1 = do+ let siz = bitmapSize bm1+ nchn = bitmapNChannels bm1+ align = bitmapRowAlignment bm1+-- x = bitmapPaddedRowSizeInBytes bm1 + bm2 <- newBitmapUninitialized siz nchn (Just align) + genericComponentMap f bm1 bm2 + return bm2++componentMapInPlace :: PixelComponent s => (s -> s) -> Bitmap s -> IO ()+componentMapInPlace f bm = do+ genericComponentMap f bm bm+ +-- | See the comments at 'componentMap'.+componentMap' + :: (PixelComponent s, PixelComponent t) + => (s -> t) + -> Bitmap s -- ^ source bitmap+ -> Maybe Alignment -- ^ row alignment of the resulting bitmap+ -> IO (Bitmap t)+componentMap' f bm1 malign = do+ let siz = bitmapSize bm1+ nchn = bitmapNChannels bm1+ x = bitmapPaddedRowSizeInBytes bm1 + bm2 <- newBitmapUninitialized siz nchn malign + genericComponentMap f bm1 bm2 + return bm2+ +--------------------------------------------------------------------------------++-- | Copies a subrectangle of the source image into a new image. +copySubImage+ :: PixelComponent t + => Bitmap t -- ^ source image+ -> Offset -- ^ source rectangle offset+ -> Size -- ^ source rectangle size+ -> IO (Bitmap t)+copySubImage bm ofs1 siz1 = copySubImage' bm ofs1 siz1 (0,0) siz1 ++-- | Copy into a new \"black\" bitmap; common generalization of crop and extend.+copySubImage'+ :: PixelComponent t + => Bitmap t -- ^ source image+ -> Offset -- ^ source rectangle offset+ -> Size -- ^ source rectangle size+ -> Size -- ^ target image size+ -> Offset -- ^ target rectangle offset+ -> IO (Bitmap t)+copySubImage' bm1 ofs1 rsiz tsiz ofs2 = do+ let align = bitmapRowAlignment bm1+ nchn = bitmapNChannels bm1+ bm2 <- newBitmap tsiz nchn (Just align)+ copySubImageInto bm1 ofs1 rsiz bm2 ofs2+ return bm2++-- | The source rectangle may be arbitrary, may or may not intersect the+-- source image in any way. We only copy the intersection of the rectangle+-- with the image. +copySubImageInto + :: PixelComponent t + => Bitmap t -- ^ source image+ -> Offset -- ^ source rectangle offset+ -> Size -- ^ source rectangle size+ -> Bitmap t -- ^ target image+ -> Offset -- ^ target rectangle offset+ -> IO ()+ +copySubImageInto bm1 ofs1@(o1x0,o1y0) siz1@(sx0,sy0) bm2 ofs2@(o2x0,o2y0) = do++ let (bm1xs,bm1ys) = bitmapSize bm1+ pad1 = bitmapRowPadding bm1+ align1 = bitmapRowAlignment bm1+ nchn1 = bitmapNChannels bm1+ pixsiz1 = bitmapPixelSizeInBytes bm1+ fptr1 = bitmapPtr bm1+ xlen1 = bitmapPaddedRowSizeInBytes bm1++ let (bm2xs,bm2ys) = bitmapSize bm2+ pad2 = bitmapRowPadding bm2+ align2 = bitmapRowAlignment bm2+ nchn2 = bitmapNChannels bm2+ pixsiz2 = bitmapPixelSizeInBytes bm2+ fptr2 = bitmapPtr bm2+ xlen2 = bitmapPaddedRowSizeInBytes bm2++ when (nchn1/=nchn2) $ error "bitmap/copySubImageInto: number of channels disagree" ++ -- handle negative offsets+ let (o1x1,sx1,o2x1) = if o1x0 >= 0 then (o1x0, sx0, o2x0) else (0, sx0+o1x0, o2x0-o1x0) + (o1y1,sy1,o2y1) = if o1y0 >= 0 then (o1y0, sy0, o2y0) else (0, sy0+o1y0, o2y0-o1y0) ++ (o1x ,sx ,o2x ) = if o2x1 >= 0 then (o1x1, sx1, o2x1) else (o1x1-o2x1, sx1+o2x1, 0) + (o1y ,sy ,o2y ) = if o2y1 >= 0 then (o1y1, sy1, o2y1) else (o1y1-o2y1, sy1+o2y1, 0) + + -- size of the rectangle we actually copy+ let xs = minimum [ sx , (bm1xs - o1x) , (bm2xs - o2x) ] + ys = minimum [ sy , (bm1ys - o1y) , (bm2ys - o2y) ] + pixsiz = pixsiz1++ when (xs>0 && ys>0) $ do+ withForeignPtr fptr1 $ \ptr1' -> withForeignPtr fptr2 $ \ptr2' -> do+ let ptr1 = ptr1' `myPlusPtr` (pixsiz*o1x)+ ptr2 = ptr2' `myPlusPtr` (pixsiz*o2x)+ nbytes = pixsiz*xs+ forM_ (zip (map (*xlen1) [o1y..o1y+ys-1]) + (map (*xlen2) [o2y..o2y+ys-1])) $ \(vo1,vo2) -> do+ let p1 = ptr1 `plusPtr` vo1 + p2 = ptr2 `plusPtr` vo2 + c_memcpy p1 p2 nbytes++--------------------------------------------------------------------------------++extractSingleChannel + :: PixelComponent t + => Bitmap t -- ^ source image+ -> Maybe Alignment -- ^ target image row alignment+ -> Int -- ^ source channel index+ -> IO (Bitmap t) +extractSingleChannel bm1 malign j = do+ let nchn = bitmapNChannels bm1+ siz@(w,h) = bitmapSize bm1+ -- pad1 = bitmapRowPadding bm1+ -- fptr1 = bitmapPtr bm1++ when (j<0 || j>=nchn) $ error "bitmap/extractSingleChannel: invalid channel index"++ bm2 <- newBitmapUninitialized siz 1 malign++ extractChannelInto bm1 j bm2 0+ return bm2+ + +extractChannels :: PixelComponent t => Bitmap t -> Maybe Alignment -> IO [Bitmap t]+extractChannels bm malign = + mapM (extractSingleChannel bm malign) [0..nchn-1] + where nchn = bitmapNChannels bm++combineChannels :: forall t. PixelComponent t => [Bitmap t] -> Maybe Alignment -> IO (Bitmap t)+combineChannels [] _ = error "bitmap/combineChannels: no channel data"+combineChannels bms malign = do+ let sizes = map bitmapSize bms+ nchns = map bitmapNChannels bms+ pixsizs = map bitmapPixelSizeInBytes bms + sumchn = sum nchns + siz@(w,h) = head sizes+ + when (length (nub sizes) /= 1) $ error "bitmap/combineChannels: incompatible sizes"++ bm2 <- newBitmapUninitialized siz sumchn malign+ let pad2 = bitmapRowPadding bm2+ fptr2 = bitmapPtr bm2++ let loop = concatMap (\bm -> zip (repeat bm) [0..bitmapNChannels bm - 1]) bms++ withForeignPtr fptr2 $ \ptr2 -> do+ forM_ (zip [0..] loop) $ \(i,(bm1,j)) -> do+ let pad1 = bitmapRowPadding bm1+ fptr1 = bitmapPtr bm1 + nchn1 = bitmapNChannels bm1 + withForeignPtr fptr1 $ \ptr1 -> + c_extract_channel + (c_type (undefined::t))+ (ci w) (ci h)+ ptr1 (ci nchn1) (ci pad1) (ci j)+ ptr2 (ci sumchn) (ci pad2) (ci i)+ + return bm2++extractChannelInto + :: forall t. PixelComponent t + => Bitmap t -- ^ source image+ -> Int -- ^ source channel index + -> Bitmap t -- ^ target image+ -> Int -- ^ target channel index+ -> IO ()+extractChannelInto bm1 ofs1 bm2 ofs2 = do++ let nchn1 = bitmapNChannels bm1+ siz1@(w,h) = bitmapSize bm1+ pad1 = bitmapRowPadding bm1+ fptr1 = bitmapPtr bm1++ let nchn2 = bitmapNChannels bm2+ siz2 = bitmapSize bm2+ pad2 = bitmapRowPadding bm2+ fptr2 = bitmapPtr bm2++ when (siz1 /= siz2) $ error "bitmap/extractChannelInto: incompatible dimensions"++ withForeignPtr fptr1 $ \ptr1 -> + withForeignPtr fptr2 $ \ptr2 -> + c_extract_channel + (c_type (undefined::t))+ (ci w) (ci h)+ ptr1 (ci nchn1) (ci pad1) (ci ofs1)+ ptr2 (ci nchn2) (ci pad2) (ci ofs2)++--------------------------------------------------------------------------------++-- | The data is copied, not shared.+copyBitmapToByteString :: PixelComponent t => Bitmap t -> IO ByteString+copyBitmapToByteString bm = do+ let n = bitmapSizeInBytes bm+ newfp <- B.mallocByteString n+ withBitmap bm $ \_ _ _ src -> + withForeignPtr newfp $ \tgt -> do+ c_memcpy (castPtr src) tgt n+ return $ B.fromForeignPtr (castForeignPtr newfp) 0 n++-- | The data is copied, not shared.+copyBitmapFromByteString :: forall t. PixelComponent t => ByteString -> Size -> NChn -> Padding -> IO (Bitmap t)+copyBitmapFromByteString bs siz nchn pad = do+ let (bsfptr0,ofs,len) = B.toForeignPtr bs+ bm = Bitmap + { bitmapSize = siz+ , bitmapNChannels = nchn+ , bitmapPtr = undefined + , bitmapRowPadding = pad+ , bitmapRowAlignment = 1+ } :: Bitmap t+ n = bitmapSizeInBytes bm+ if n > len-ofs+ then error "copyBitmapFromByteString: ByteString is too short"+ else do+ newfptr <- mallocForeignPtrBytes n+ withForeignPtr bsfptr0 $ \src0 -> do+ let src = src0 `plusPtr` ofs+ withForeignPtr newfptr $ \tgt ->+ c_memcpy src tgt n+ return $ bm { bitmapPtr = castForeignPtr newfptr } + +--------------------------------------------------------------------------------++-- no multiplication+{-# SPECIALIZE advancePtr1 :: Ptr Word8 -> Ptr Word8 #-}+{-# SPECIALIZE advancePtr1 :: Ptr Float -> Ptr Float #-}+advancePtr1 :: forall a. Storable a => Ptr a -> Ptr a+advancePtr1 p = p `plusPtr` (sizeOf (undefined::a))++-- restricted type+{-# SPECIALIZE myPlusPtr :: Ptr Word8 -> Int -> Ptr Word8 #-}+{-# SPECIALIZE myPlusPtr :: Ptr Float -> Int -> Ptr Float #-}+myPlusPtr :: Ptr a -> Int -> Ptr a+myPlusPtr = plusPtr++ci :: Int -> CInt+ci = fromIntegral++foreign import ccall unsafe "bm.h c_memset" + c_memset :: Ptr Word8 -> Int -> Word8 -> IO ()++-- @c_memcpy from to cnt@.+-- Note that we use /nonstandard/ argument order!+foreign import ccall unsafe "bm.h c_memcpy" + c_memcpy :: Ptr Word8 -> Ptr Word8 -> Int -> IO ()++{-+void c_extract_channel(+ ( int k_type+ , int width, int height + , void *p1, int nchn1, int pad1, int ofs1 + , void *p2, int nchn2, int pad2, int ofs2 + );+-}++foreign import ccall unsafe "bm.h c_extract_channel"+ c_extract_channel + :: CInt + -> CInt -> CInt + -> Ptr a -> CInt -> CInt -> CInt+ -> Ptr a -> CInt -> CInt -> CInt+ -> IO ()+ +--------------------------------------------------------------------------------
+ Data/Bitmap/Internal.hs view
@@ -0,0 +1,105 @@++--------------------------------------------------------------------------------+-- Module : Data.Bitmap.Internal+-- Version : 0.0.0+-- License : BSD3+-- Copyright : (c) 2009 Balazs Komuves+-- Author : Balazs Komuves+-- Maintainer : bkomuves (plus) hackage (at) gmail (dot) com+-- Stability : experimental+-- Portability : requires FFI, CPP and ScopedTypeVariables+-- Tested with : GHC 6.10.1+--------------------------------------------------------------------------------++{-# LANGUAGE CPP, ForeignFunctionInterface, ScopedTypeVariables #-}+module Data.Bitmap.Internal where++--------------------------------------------------------------------------------++import Control.Monad++--import Data.Array.IArray+import Data.Word++import Foreign+import Foreign.C++--------------------------------------------------------------------------------++data PixelComponentType + = PctWord8 + | PctWord16+ | PctWord32 + | PctFloat+ deriving Show+ +class (Num t, Storable t) => PixelComponent t where+ c_type :: t -> CInt+ nbytes :: t -> Int+ nbytes x = sizeOf x+ toFloat :: t -> Float+ fromFloat :: Float -> t+ +pixelComponentType :: PixelComponent t => t -> PixelComponentType+pixelComponentType t = case c_type t of+ 1 -> PctWord8+ 2 -> PctWord16+ 3 -> PctWord32+ 4 -> PctFloat+ +instance PixelComponent Word8 where + c_type _ = 1+ fromFloat = floor . (*255.99999)+ toFloat = (*3.92156862745098e-3) . fromIntegral -- 1/255++instance PixelComponent Word16 where + c_type _ = 2+ fromFloat = floor . (*65535.99999)+ toFloat = (*1.5259021896696422e-5) . fromIntegral -- 1/65535++instance PixelComponent Word32 where + c_type _ = 3+ fromFloat = floor . (*4294967295.99999)+ toFloat = (*2.3283064370807974e-10) . fromIntegral -- 1/(2^32-1)+ +instance PixelComponent Float where + c_type _ = 4+ toFloat = id+ fromFloat = id+ +-- to provide better documentation+type Size = (Int,Int)+type Offset = (Int,Int)+type NChn = Int+type Padding = Int+type Alignment = Int++data Bitmap t = Bitmap+ { bitmapSize :: Size -- ^ (width,height)+ , bitmapNChannels :: NChn -- ^ number of channels (eg. 3 for RGB)+ , bitmapPtr :: ForeignPtr t -- ^ pointer to the data+ , bitmapRowPadding :: Padding -- ^ the padding of the rows, measured in /bytes/+ , bitmapRowAlignment :: Alignment -- ^ the alignment of the rows (in bytes)+ }+ deriving Show++bitmapComponentSizeInBytes :: forall t. PixelComponent t => Bitmap t -> Int+bitmapComponentSizeInBytes _ = sizeOf (undefined::t) ++bitmapPixelSizeInBytes :: PixelComponent t => Bitmap t -> Int+bitmapPixelSizeInBytes bm = bitmapNChannels bm * bitmapComponentSizeInBytes bm+ +bitmapUnpaddedRowSizeInBytes :: forall t. PixelComponent t => Bitmap t -> Int +bitmapUnpaddedRowSizeInBytes bm = w * sizeOf (undefined::t) * nchn where+ (w,h) = bitmapSize bm+ nchn = bitmapNChannels bm + +bitmapPaddedRowSizeInBytes :: PixelComponent t => Bitmap t -> Int +bitmapPaddedRowSizeInBytes bm = bitmapUnpaddedRowSizeInBytes bm + bitmapRowPadding bm + +bitmapSizeInBytes :: PixelComponent t => Bitmap t -> Int +bitmapSizeInBytes bm = h*x where+ x = bitmapPaddedRowSizeInBytes bm+ (_,h) = bitmapSize bm ++--------------------------------------------------------------------------------
+ Data/Bitmap/Pure.hs view
@@ -0,0 +1,145 @@++--------------------------------------------------------------------------------+-- Module : Data.Bitmap.Pure+-- Version : 0.0.0+-- License : BSD3+-- Copyright : (c) 2009 Balazs Komuves+-- Author : Balazs Komuves+-- Maintainer : bkomuves (plus) hackage (at) gmail (dot) com+-- Stability : experimental+-- Portability : requires FFI, CPP and ScopedTypeVariables+-- Tested with : GHC 6.10.1+--------------------------------------------------------------------------------++{-# LANGUAGE CPP, ScopedTypeVariables #-}+module Data.Bitmap.Pure + ( + module Data.Bitmap.Base+ + , emptyBitmap+ -- * Mapping over bitmaps+ , componentMap+ , componentMap'+ -- * Cropping and extending+ , copySubImage+ , copySubImage' + -- * Manipulating channels+ , combineChannels + , extractChannels + , extractSingleChannel + -- * Conversion to ByteString+ , bitmapToByteString + ) + where++--------------------------------------------------------------------------------++import Data.Word++import Foreign++import Data.ByteString (ByteString)+import qualified Data.ByteString as B+import qualified Data.ByteString.Internal as B++import Data.Bitmap.Base+import Data.Bitmap.Internal+import qualified Data.Bitmap.IO as IO++import System.IO.Unsafe++--------------------------------------------------------------------------------++-- | A bitmap filled with zero values.+-- Note: we /cannot/ guarantee the alignment+-- of the memory block (but typically it is aligned at least to machine word boundary),+-- but what we /can/ guarantee is that the rows are properly padded.+emptyBitmap + :: forall t. PixelComponent t + => Size -- ^ (width,height)+ -> NChn -- ^ number of channels (components\/pixel)+ -> Maybe Alignment -- ^ the row alignment of the new image+ -> Bitmap t+emptyBitmap siz nchn malign = unsafePerformIO $ IO.newBitmap siz nchn malign++--------------------------------------------------------------------------------++-- | Warning: this is probably slow+componentMap :: PixelComponent s => (s -> s) -> Bitmap s -> Bitmap s+componentMap f bm = unsafePerformIO $ IO.componentMap f bm++-- | Warning: this is probably slow+componentMap' :: (PixelComponent s, PixelComponent t) => (s -> t) -> Bitmap s -> Maybe Alignment -> Bitmap t+componentMap' f bm malign = unsafePerformIO $ IO.componentMap' f bm malign++--------------------------------------------------------------------------------++-- | Copies a subrectangle of the source image into a new image. +copySubImage+ :: PixelComponent t + => Bitmap t -- ^ source image+ -> Offset -- ^ source rectangle offset+ -> Size -- ^ source rectangle size+ -> Bitmap t+copySubImage bm ofs siz = unsafePerformIO $ IO.copySubImage bm ofs siz++-- | Copy into a new \"black\" bitmap; common generalization of crop and extend.+copySubImage'+ :: PixelComponent t + => Bitmap t -- ^ source image+ -> Offset -- ^ source rectangle offset+ -> Size -- ^ source rectangle size+ -> Size -- ^ target image size+ -> Offset -- ^ target rectangle offset+ -> Bitmap t+copySubImage' bm1 ofs1 rsiz tsiz ofs2 = unsafePerformIO $+ IO.copySubImage' bm1 ofs1 rsiz tsiz ofs2+ +--------------------------------------------------------------------------------++extractSingleChannel + :: PixelComponent t + => Bitmap t -- ^ source image+ -> Maybe Alignment -- ^ target image row alignment+ -> Int -- ^ source channel index+ -> Bitmap t+extractSingleChannel bm1 malign j = unsafePerformIO $ + IO.extractSingleChannel bm1 malign j ++extractChannels :: PixelComponent t => Bitmap t -> Maybe Alignment -> [Bitmap t]+extractChannels bm malign = unsafePerformIO $+ IO.extractChannels bm malign ++combineChannels :: forall t. PixelComponent t => [Bitmap t] -> Maybe Alignment -> Bitmap t+combineChannels bms malign = unsafePerformIO $+ IO.combineChannels bms malign+ +--------------------------------------------------------------------------------++-- | Note that the data is /shared/.+bitmapToByteString :: PixelComponent t => Bitmap t -> ByteString+bitmapToByteString bm = bs where+ bs = B.fromForeignPtr (castForeignPtr $ bitmapPtr bm) 0 n+ n = bitmapSizeInBytes bm ++{- +-- | As its name says, this pretty much unsafe!+reallyUnsafeBitmapFromByteString :: forall t. ByteString -> Size -> NChn -> Padding -> Bitmap t+reallyUnsafeBitmapFromByteString bs siz nchn pad = + if n > len || ofs /= 0 + then error "reallyUnsafeBitmapFromByteString: better than segfault :)"+ else bm + where+ bm = Bitmap + { bitmapSize = siz+ , bitmapNChannels = nchn+ , bitmapPtr = fptr + , bitmapRowPadding = pad+ , bitmapRowAlignment = 1+ } :: Bitmap t+ n = bitmapSizeInBytes bm+ (fptr,ofs,len) = B.toForeignPtr bm+-}+ +--------------------------------------------------------------------------------+
+ LICENSE view
@@ -0,0 +1,29 @@+Copyright (c) 2009, Balazs Komuves+All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met:++- Redistributions of source code must retain the above copyright notice,+this list of conditions and the following disclaimer.+ +- Redistributions in binary form must reproduce the above copyright notice,+this list of conditions and the following disclaimer in the documentation+and/or other materials provided with the distribution.+ +- Neither names of the copyright holders nor the names of the contributors+may be used to endorse or promote products derived from this software without+specific prior written permission. ++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER +OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,+EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.+
+ Setup.lhs view
@@ -0,0 +1,4 @@+#! /usr/bin/env runhaskell + +> import Distribution.Simple +> main = defaultMain
+ bitmap.cabal view
@@ -0,0 +1,43 @@+Name: bitmap+Version: 0.0.0+Synopsis: A library for handling and manipulating bitmaps.+Description: A library for handling and manipulating bitmaps (that is,+ rectangular pixel arrays).+License: BSD3+License-file: LICENSE+Copyright: (c) 2009 Balazs Komuves+Author: Balazs Komuves+Maintainer: bkomuves (plus) hackage (at) gmail (dot) com+Homepage: http://code.haskell.org/~bkomuves/+Stability: Experimental+Category: Graphics, Data+Tested-With: GHC == 6.10.1+Cabal-Version: >= 1.2+Build-Type: Simple++extra-source-files: cbits/bm.h++Flag base4+ Description: Base v4++Library+ if flag(base4)+ Build-Depends: base >= 4 && < 5, array, containers, bytestring >= 0.9+ cpp-options: -DBASE_VERSION=4+ else+ Build-Depends: base >= 3 && < 4, array, containers, bytestring >= 0.9+ cpp-options: -DBASE_VERSION=3+ + Exposed-Modules: Data.Bitmap,+ Data.Bitmap.Base,+ Data.Bitmap.IO,+ Data.Bitmap.Pure++ Other-Modules: Data.Bitmap.Internal++ Hs-Source-Dirs: .+ Extensions: ForeignFunctionInterface, CPP, ScopedTypeVariables++ C-Sources: cbits/bm.c + Include-Dirs: cbits+
+ cbits/bm.c view
@@ -0,0 +1,79 @@++/*+the C part of the Data.Bitmap library+(c) 2009 Balazs Komuves+*/++#include <string.h>+#include "bm.h"++// -----------------------------------------------------------------------------+ +void c_memset(word8 *q, int count, word8 x)+{ memset(q,x,count);+}++// (from, to, count)+void c_memcpy(word8 *p, word8 *q, int count)+{ memcpy(q,p,count);+}++// -----------------------------------------------------------------------------++#define K_WORD8 1+#define K_WORD16 2+#define K_WORD32 3+#define K_FLOAT 4++#define PLUSPTR(P,TYP,K) P=(TYP*)(((word8*)P)+K);++// -----------------------------------------------------------------------------++#define C_EXTRACT_CHANNEL(TYP) \+void c_extract_channel_ ## TYP \+ ( int width, int height \+ , TYP *p1, int nchn1, int pad1, int ofs1 \+ , TYP *p2, int nchn2, int pad2, int ofs2 \+ ) \+{ int x,y; \+ TYP *q1,*q2; \+ q1 = p1 + ofs1; \+ q2 = p2 + ofs2; \+ for(y=0;y<height;y++) \+ { for(x=0;x<width;x++) \+ { *q2 = *q1; \+ q1 += nchn1; \+ q2 += nchn2; \+ } \+ PLUSPTR(q1,TYP,pad1); \+ PLUSPTR(q2,TYP,pad2); \+ } \+} ++C_EXTRACT_CHANNEL(word8 )+C_EXTRACT_CHANNEL(word16)+C_EXTRACT_CHANNEL(word32)+C_EXTRACT_CHANNEL(float )++void c_extract_channel+ ( int k_type+ , int width, int height + , void *p1, int nchn1, int pad1, int ofs1 + , void *p2, int nchn2, int pad2, int ofs2 + ) +{ switch(k_type)+ { case K_WORD8: + c_extract_channel_word8 ( width,height, p1,nchn1,pad1,ofs1, p2,nchn2,pad2,ofs2 );+ break;+ case K_WORD16: + c_extract_channel_word16( width,height, p1,nchn1,pad1,ofs1, p2,nchn2,pad2,ofs2 );+ break;+ case K_WORD32: + c_extract_channel_word32( width,height, p1,nchn1,pad1,ofs1, p2,nchn2,pad2,ofs2 );+ break;+ case K_FLOAT: + c_extract_channel_float ( width,height, p1,nchn1,pad1,ofs1, p2,nchn2,pad2,ofs2 );+ break;+ }+}+
+ cbits/bm.h view
@@ -0,0 +1,22 @@++#include <stdint.h>++typedef uint8_t word8;+typedef uint16_t word16;+typedef uint32_t word32;++//------------------------------------------------------------------------------++void c_memset(word8 *q, int count, word8 x);++// (from, to, count)+void c_memcpy(word8 *p, word8 *q, int count);++//------------------------------------------------------------------------------++void c_extract_channel+ ( int k_type+ , int width, int height + , void *p1, int nchn1, int pad1, int ofs1 + , void *p2, int nchn2, int pad2, int ofs2 + );