packages feed

bitmap 0.0.0 → 0.0.1

raw patch · 8 files changed

+1074/−79 lines, 8 filesdep −arraydep −containersPVP: major bump suggested

API removals or changes: PVP suggests a major version bump

Dependencies removed: array, containers

API changes (from Hackage documentation)

+ Data.Bitmap.Base: bitmapAspect :: Fractional a => Bitmap t -> a
+ Data.Bitmap.Base: pixelComponentSize :: PixelComponentType -> Int
+ Data.Bitmap.IO: bilinearResample :: PixelComponent t => Bitmap t -> Size -> Maybe Alignment -> IO (Bitmap t)
+ Data.Bitmap.IO: bilinearResampleChannel :: PixelComponent t => Bitmap t -> Int -> Size -> Maybe Alignment -> IO (Bitmap t)
+ Data.Bitmap.IO: bilinearResampleChannelInto :: PixelComponent t => Bitmap t -> Int -> Bitmap t -> Int -> IO ()
+ Data.Bitmap.IO: blendBitmaps :: PixelComponent t => Float -> Float -> Bitmap t -> Bitmap t -> Maybe Alignment -> IO (Bitmap t)
+ Data.Bitmap.IO: blendChannels :: PixelComponent t => Float -> Float -> Bitmap t -> Int -> Bitmap t -> Int -> Maybe Alignment -> IO (Bitmap t)
+ Data.Bitmap.IO: blendChannelsInto :: PixelComponent t => Float -> Float -> Bitmap t -> Int -> Bitmap t -> Int -> Bitmap t -> Int -> IO ()
+ Data.Bitmap.IO: createSingleChannelBitmap :: PixelComponent t => Size -> Maybe Alignment -> (Int -> Int -> t) -> IO (Bitmap t)
+ Data.Bitmap.IO: powerlawGammaCorrection :: PixelComponent t => Float -> Bitmap t -> Maybe Alignment -> IO (Bitmap t)
+ Data.Bitmap.IO: powerlawGammaCorrectionChannel :: PixelComponent t => Float -> Bitmap t -> Int -> Maybe Alignment -> IO (Bitmap t)
+ Data.Bitmap.IO: powerlawGammaCorrectionChannelInto :: PixelComponent t => Float -> Bitmap t -> Int -> Bitmap t -> Int -> IO ()
+ Data.Bitmap.IO: unsafeReadComponent :: PixelComponent t => Bitmap t -> Offset -> Int -> IO t
+ Data.Bitmap.IO: unsafeReadComponents :: PixelComponent t => Bitmap t -> Offset -> Int -> Int -> IO [t]
+ Data.Bitmap.IO: unsafeReadPixel :: PixelComponent t => Bitmap t -> Offset -> IO [t]
+ Data.Bitmap.IO: unsafeReadPixel1 :: PixelComponent t => Bitmap t -> Offset -> IO t
+ Data.Bitmap.IO: unsafeReadPixel2 :: PixelComponent t => Bitmap t -> Offset -> IO (t, t)
+ Data.Bitmap.IO: unsafeReadPixel3 :: PixelComponent t => Bitmap t -> Offset -> IO (t, t, t)
+ Data.Bitmap.IO: unsafeReadPixel4 :: PixelComponent t => Bitmap t -> Offset -> IO (t, t, t, t)
+ Data.Bitmap.IO: unsafeWriteComponent :: PixelComponent t => Bitmap t -> Offset -> Int -> t -> IO ()
+ Data.Bitmap.IO: unsafeWriteComponents :: PixelComponent t => Bitmap t -> Offset -> Int -> [t] -> IO ()
+ Data.Bitmap.IO: unsafeWritePixel1 :: PixelComponent t => Bitmap t -> Offset -> t -> IO ()
+ Data.Bitmap.IO: unsafeWritePixel2 :: PixelComponent t => Bitmap t -> Offset -> (t, t) -> IO ()
+ Data.Bitmap.IO: unsafeWritePixel3 :: PixelComponent t => Bitmap t -> Offset -> (t, t, t) -> IO ()
+ Data.Bitmap.IO: unsafeWritePixel4 :: PixelComponent t => Bitmap t -> Offset -> (t, t, t, t) -> IO ()
+ Data.Bitmap.IO: withComponentPtr :: PixelComponent t => Bitmap t -> Offset -> Int -> (Ptr t -> IO a) -> IO a
+ Data.Bitmap.Pure: bilinearResample :: PixelComponent t => Bitmap t -> Size -> Maybe Alignment -> Bitmap t
+ Data.Bitmap.Pure: bilinearResampleChannel :: PixelComponent t => Bitmap t -> Int -> Size -> Maybe Alignment -> Bitmap t
+ Data.Bitmap.Pure: blendBitmaps :: PixelComponent t => Float -> Float -> Bitmap t -> Bitmap t -> Maybe Alignment -> Bitmap t
+ Data.Bitmap.Pure: blendChannels :: PixelComponent t => Float -> Float -> Bitmap t -> Int -> Bitmap t -> Int -> Maybe Alignment -> Bitmap t
+ Data.Bitmap.Pure: createSingleChannelBitmap :: PixelComponent t => Size -> Maybe Alignment -> (Int -> Int -> t) -> Bitmap t
+ Data.Bitmap.Pure: powerlawGammaCorrection :: PixelComponent t => Float -> Bitmap t -> Maybe Alignment -> Bitmap t
+ Data.Bitmap.Pure: powerlawGammaCorrectionChannel :: PixelComponent t => Float -> Bitmap t -> Int -> Maybe Alignment -> Bitmap t
- Data.Bitmap.Base: bitmapComponentSizeInBytes :: (PixelComponent t) => Bitmap t -> Int
+ Data.Bitmap.Base: bitmapComponentSizeInBytes :: PixelComponent t => Bitmap t -> Int
- Data.Bitmap.Base: bitmapPaddedRowSizeInBytes :: (PixelComponent t) => Bitmap t -> Int
+ Data.Bitmap.Base: bitmapPaddedRowSizeInBytes :: PixelComponent t => Bitmap t -> Int
- Data.Bitmap.Base: bitmapPixelSizeInBytes :: (PixelComponent t) => Bitmap t -> Int
+ Data.Bitmap.Base: bitmapPixelSizeInBytes :: PixelComponent t => Bitmap t -> Int
- Data.Bitmap.Base: bitmapSizeInBytes :: (PixelComponent t) => Bitmap t -> Int
+ Data.Bitmap.Base: bitmapSizeInBytes :: PixelComponent t => Bitmap t -> Int
- Data.Bitmap.Base: bitmapUnpaddedRowSizeInBytes :: (PixelComponent t) => Bitmap t -> Int
+ Data.Bitmap.Base: bitmapUnpaddedRowSizeInBytes :: PixelComponent t => Bitmap t -> Int
- Data.Bitmap.Base: pixelComponentType :: (PixelComponent t) => t -> PixelComponentType
+ Data.Bitmap.Base: pixelComponentType :: PixelComponent t => t -> PixelComponentType
- Data.Bitmap.IO: bitmapFromForeignPtrUnsafe :: (PixelComponent t) => Size -> NChn -> Alignment -> Padding -> ForeignPtr t -> Bitmap t
+ Data.Bitmap.IO: bitmapFromForeignPtrUnsafe :: PixelComponent t => Size -> NChn -> Alignment -> Padding -> ForeignPtr t -> Bitmap t
- Data.Bitmap.IO: combineChannels :: (PixelComponent t) => [Bitmap t] -> Maybe Alignment -> IO (Bitmap t)
+ Data.Bitmap.IO: combineChannels :: PixelComponent t => [Bitmap t] -> Maybe Alignment -> IO (Bitmap t)
- Data.Bitmap.IO: componentMap :: (PixelComponent s) => (s -> s) -> Bitmap s -> IO (Bitmap s)
+ Data.Bitmap.IO: componentMap :: PixelComponent s => (s -> s) -> Bitmap s -> IO (Bitmap s)
- Data.Bitmap.IO: componentMapInPlace :: (PixelComponent s) => (s -> s) -> Bitmap s -> IO ()
+ Data.Bitmap.IO: componentMapInPlace :: PixelComponent s => (s -> s) -> Bitmap s -> IO ()
- Data.Bitmap.IO: copyBitmapFromByteString :: (PixelComponent t) => ByteString -> Size -> NChn -> Padding -> IO (Bitmap t)
+ Data.Bitmap.IO: copyBitmapFromByteString :: PixelComponent t => ByteString -> Size -> NChn -> Padding -> IO (Bitmap t)
- Data.Bitmap.IO: copyBitmapFromPtr :: (PixelComponent t) => Size -> NChn -> Padding -> Ptr t -> Maybe Alignment -> IO (Bitmap t)
+ Data.Bitmap.IO: copyBitmapFromPtr :: PixelComponent t => Size -> NChn -> Padding -> Ptr t -> Maybe Alignment -> IO (Bitmap t)
- Data.Bitmap.IO: copyBitmapToByteString :: (PixelComponent t) => Bitmap t -> IO ByteString
+ Data.Bitmap.IO: copyBitmapToByteString :: PixelComponent t => Bitmap t -> IO ByteString
- Data.Bitmap.IO: copySubImage :: (PixelComponent t) => Bitmap t -> Offset -> Size -> IO (Bitmap t)
+ Data.Bitmap.IO: copySubImage :: PixelComponent t => Bitmap t -> Offset -> Size -> IO (Bitmap t)
- Data.Bitmap.IO: copySubImage' :: (PixelComponent t) => Bitmap t -> Offset -> Size -> Size -> Offset -> IO (Bitmap t)
+ Data.Bitmap.IO: copySubImage' :: PixelComponent t => Bitmap t -> Offset -> Size -> Size -> Offset -> IO (Bitmap t)
- Data.Bitmap.IO: copySubImageInto :: (PixelComponent t) => Bitmap t -> Offset -> Size -> Bitmap t -> Offset -> IO ()
+ Data.Bitmap.IO: copySubImageInto :: PixelComponent t => Bitmap t -> Offset -> Size -> Bitmap t -> Offset -> IO ()
- Data.Bitmap.IO: extractChannelInto :: (PixelComponent t) => Bitmap t -> Int -> Bitmap t -> Int -> IO ()
+ Data.Bitmap.IO: extractChannelInto :: PixelComponent t => Bitmap t -> Int -> Bitmap t -> Int -> IO ()
- Data.Bitmap.IO: extractChannels :: (PixelComponent t) => Bitmap t -> Maybe Alignment -> IO [Bitmap t]
+ Data.Bitmap.IO: extractChannels :: PixelComponent t => Bitmap t -> Maybe Alignment -> IO [Bitmap t]
- Data.Bitmap.IO: extractSingleChannel :: (PixelComponent t) => Bitmap t -> Maybe Alignment -> Int -> IO (Bitmap t)
+ Data.Bitmap.IO: extractSingleChannel :: PixelComponent t => Bitmap t -> Maybe Alignment -> Int -> IO (Bitmap t)
- Data.Bitmap.IO: newBitmap :: (PixelComponent t) => Size -> NChn -> Maybe Alignment -> IO (Bitmap t)
+ Data.Bitmap.IO: newBitmap :: PixelComponent t => Size -> NChn -> Maybe Alignment -> IO (Bitmap t)
- Data.Bitmap.IO: newBitmapUninitialized :: (PixelComponent t) => Size -> NChn -> Maybe Alignment -> IO (Bitmap t)
+ Data.Bitmap.IO: newBitmapUninitialized :: PixelComponent t => Size -> NChn -> Maybe Alignment -> IO (Bitmap t)
- Data.Bitmap.IO: withBitmap :: (PixelComponent t) => Bitmap t -> (Size -> NChn -> Padding -> Ptr t -> IO a) -> IO a
+ Data.Bitmap.IO: withBitmap :: PixelComponent t => Bitmap t -> (Size -> NChn -> Padding -> Ptr t -> IO a) -> IO a
- Data.Bitmap.Pure: bitmapToByteString :: (PixelComponent t) => Bitmap t -> ByteString
+ Data.Bitmap.Pure: bitmapToByteString :: PixelComponent t => Bitmap t -> ByteString
- Data.Bitmap.Pure: combineChannels :: (PixelComponent t) => [Bitmap t] -> Maybe Alignment -> Bitmap t
+ Data.Bitmap.Pure: combineChannels :: PixelComponent t => [Bitmap t] -> Maybe Alignment -> Bitmap t
- Data.Bitmap.Pure: componentMap :: (PixelComponent s) => (s -> s) -> Bitmap s -> Bitmap s
+ Data.Bitmap.Pure: componentMap :: PixelComponent s => (s -> s) -> Bitmap s -> Bitmap s
- Data.Bitmap.Pure: copySubImage :: (PixelComponent t) => Bitmap t -> Offset -> Size -> Bitmap t
+ Data.Bitmap.Pure: copySubImage :: PixelComponent t => Bitmap t -> Offset -> Size -> Bitmap t
- Data.Bitmap.Pure: copySubImage' :: (PixelComponent t) => Bitmap t -> Offset -> Size -> Size -> Offset -> Bitmap t
+ Data.Bitmap.Pure: copySubImage' :: PixelComponent t => Bitmap t -> Offset -> Size -> Size -> Offset -> Bitmap t
- Data.Bitmap.Pure: emptyBitmap :: (PixelComponent t) => Size -> NChn -> Maybe Alignment -> Bitmap t
+ Data.Bitmap.Pure: emptyBitmap :: PixelComponent t => Size -> NChn -> Maybe Alignment -> Bitmap t
- Data.Bitmap.Pure: extractChannels :: (PixelComponent t) => Bitmap t -> Maybe Alignment -> [Bitmap t]
+ Data.Bitmap.Pure: extractChannels :: PixelComponent t => Bitmap t -> Maybe Alignment -> [Bitmap t]
- Data.Bitmap.Pure: extractSingleChannel :: (PixelComponent t) => Bitmap t -> Maybe Alignment -> Int -> Bitmap t
+ Data.Bitmap.Pure: extractSingleChannel :: PixelComponent t => Bitmap t -> Maybe Alignment -> Int -> Bitmap t

Files

Data/Bitmap.hs view
@@ -1,7 +1,7 @@  -------------------------------------------------------------------------------- -- Module      : Data.Bitmap--- Version     : 0.0.0+-- Version     : 0.0.1 -- License     : BSD3 -- Copyright   : (c) 2009 Balazs Komuves -- Author      : Balazs Komuves@@ -14,7 +14,10 @@ -- | 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.+--+-- For loading JPEG/PNG images into 'Bitmap's, see the @stb-image@ +-- library (version 0.2 or newer):+-- <http://hackage.haskell.org/package/stb-image>. --  -- Terminology:  -- Pixels are made out of one or more \"components\". These components @@ -23,7 +26,7 @@ -- 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.+-- Most of the library is indifferent 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@@ -31,7 +34,7 @@ -- -- 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).+-- no SSE, etc.; but the critical functions are coded in C). -- -- There are both pure and IO versions of the API; you shouldn't mix -- the two without taking special care.
Data/Bitmap/Base.hs view
@@ -1,7 +1,7 @@  -------------------------------------------------------------------------------- -- Module      : Data.Bitmap.Base--- Version     : 0.0.0+-- Version     : 0.0.1 -- License     : BSD3 -- Copyright   : (c) 2009 Balazs Komuves -- Author      : Balazs Komuves@@ -19,7 +19,7 @@ -- 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.+-- Most of the library is indifferent 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. --@@ -34,6 +34,7 @@     -- * Types     PixelComponent   , PixelComponentType(..)+  , pixelComponentSize   , pixelComponentType     --   , Word8@@ -51,6 +52,7 @@   , bitmapNChannels   , bitmapRowPadding    , bitmapRowAlignment +  , bitmapAspect     --   , bitmapComponentSizeInBytes   , bitmapPixelSizeInBytes
Data/Bitmap/IO.hs view
@@ -1,7 +1,7 @@  -------------------------------------------------------------------------------- -- Module      : Data.Bitmap.IO--- Version     : 0.0.0+-- Version     : 0.0.1 -- License     : BSD3 -- Copyright   : (c) 2009 Balazs Komuves -- Author      : Balazs Komuves@@ -21,6 +21,7 @@     -- * Creating and accessing bitmaps   , newBitmap   , newBitmapUninitialized+  , createSingleChannelBitmap   , copyBitmapFromPtr   , bitmapFromForeignPtrUnsafe   , withBitmap@@ -37,9 +38,36 @@   , extractChannels    , extractSingleChannel    , extractChannelInto+    -- * Bilinear resampling+  , bilinearResample+  , bilinearResampleChannel+  , bilinearResampleChannelInto+    -- * Blending+  , blendBitmaps+  , blendChannels+  , blendChannelsInto+    -- * Gamma correction+  , powerlawGammaCorrection+  , powerlawGammaCorrectionChannel  +  , powerlawGammaCorrectionChannelInto       -- * Conversion to\/from ByteString   , copyBitmapToByteString   , copyBitmapFromByteString+    -- * Reading and writing pixels+  , withComponentPtr+  , unsafeReadComponent+  , unsafeWriteComponent+  , unsafeReadComponents+  , unsafeWriteComponents+  , unsafeReadPixel+  , unsafeReadPixel1+  , unsafeReadPixel2+  , unsafeReadPixel3+  , unsafeReadPixel4+  , unsafeWritePixel1+  , unsafeWritePixel2+  , unsafeWritePixel3+  , unsafeWritePixel4   )    where   @@ -63,8 +91,22 @@ import Data.Bitmap.Base  --------------------------------------------------------------------------------++defaultAlignment :: Int+defaultAlignment = 4++validateMaybeAlignment :: Maybe Alignment -> Alignment+validateMaybeAlignment = maybe defaultAlignment validateAlignment++validateAlignment :: Alignment -> Alignment+validateAlignment k = +  if isValidAlignment k +    then k +    else error "invalid row alignment (allowed values: 1, 2, 4 and 8)"   --- @newBitmapRaw (width,row) nchannels padding@. We do not initialize the new bitmap!+--------------------------------------------------------------------------------+  +-- 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 @@ -81,6 +123,10 @@ -- | 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.+--+-- At the moment, the default alignment is 4, valid alignments are 1, 2, 4, 8 and 16,+-- and the padding method is compatible with the OpenGL one (that is, the padding is the+-- smallest multiple of a component size such that the next row is aligned). --  -- The resulting new bitmap is filled with zeros. newBitmap @@ -106,11 +152,52 @@         , bitmapRowAlignment = align         } :: Bitmap t       x0 = bitmapUnpaddedRowSizeInBytes bm0+      +      align = validateMaybeAlignment malign+      pad = recommendedPadding 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   +-- | Creates a new single-channel bitmap, using the given function to compute+-- the pixel values.+-- Warning, this is probably slow!  +createSingleChannelBitmap +  :: forall t. PixelComponent t +  => Size               -- ^ (width,height)+  -> Maybe Alignment    -- ^ the row alignment of the new image+  -> (Int -> Int -> t)  -- ^ the function we will use to fill the bitmap+  -> IO (Bitmap t)+createSingleChannelBitmap siz malign fun = do  +  bm <- newBitmapUninitialized siz 1 malign +  let fptr = bitmapPtr bm+      len  = bitmapSizeInBytes bm+      f :: Int -> Int -> t -> t+      f x y _ = fun x y +  genericComponentMapWithPos f bm bm+  return bm++{-    +createBitmap    +  :: forall t. PixelComponent t +  => Size               -- ^ (width,height)+  -> Maybe Alignment    -- ^ the row alignment of the new image+  -> [Int -> Int -> t]  -- ^ the functions we will use to fill the bitmap+  -> IO (Bitmap t)+createBitmap siz malign funs = do +  let nchn = length funs +  bm <- newBitmapUninitialized siz nchn malign +  let fptr = bitmapPtr bm+      len  = bitmapSizeInBytes bm+      f :: Int -> Int -> t -> t+      f x y _ = fun x y +  genericComponentMapWithPos f bm bm+  return bm+-}+     copyBitmapFromPtr    :: forall t. PixelComponent t    => Size       -- ^ (width,height) of the source@@ -150,29 +237,120 @@  -------------------------------------------------------------------------------- +-- | Note that the resulting pointer is valid only within a line (because of the padding)+withComponentPtr +  :: forall t a. PixelComponent t +  => Bitmap t         -- ^ the bitmap+  -> Offset           -- ^ position (x,y)+  -> Int              -- ^ channel index {0,1,...,nchannels-1}+  -> (Ptr t -> IO a)  -- ^ user action+  -> IO a+withComponentPtr bm (x,y) ofs action = +  withForeignPtr (bitmapPtr bm) $ \p -> do+    let nchn = bitmapNChannels bm+        rowsize = bitmapPaddedRowSizeInBytes bm+        q = p `myPlusPtr` ( ( nchn*x + ofs ) * sizeOf (undefined::t) + y * rowsize ) +    action q+    +-- | It is not very efficient to read\/write lots of pixels this way.+unsafeReadComponent +  :: PixelComponent t +  => Bitmap t      -- ^ the bitmap+  -> Offset        -- ^ position (x,y)+  -> Int           -- ^ channel index {0,1,...,nchannels-1}+  -> IO t+unsafeReadComponent bm xy ofs = withComponentPtr bm xy ofs $ peek+    +unsafeWriteComponent+  :: PixelComponent t +  => Bitmap t      -- ^ the bitmap+  -> Offset        -- ^ position (x,y)+  -> Int           -- ^ channel index {0,1,...,nchannels-1}+  -> t             -- ^ the value to write+  -> IO ()+unsafeWriteComponent bm xy ofs value = withComponentPtr bm xy ofs $ \q -> poke q value++-- | Please note that the component array to read shouldn't cross +-- the boundary between lines.+unsafeReadComponents+  :: PixelComponent t +  => Bitmap t      -- ^ the bitmap+  -> Offset        -- ^ position (x,y)+  -> Int           -- ^ channel index {0,1,...,nchannels-1}+  -> Int           -- ^ the number of components to read+  -> IO [t]+unsafeReadComponents bm xy ofs k = withComponentPtr bm xy ofs $ \p -> peekArray k p++-- | Please note that the component array to write shouldn't cross +-- the boundary between lines.+unsafeWriteComponents+  :: PixelComponent t +  => Bitmap t      -- ^ the bitmap+  -> Offset        -- ^ position (x,y)+  -> Int           -- ^ channel index {0,1,...,nchannels-1}+  -> [t]           -- ^ the components to write+  -> IO ()+unsafeWriteComponents bm xy ofs values = withComponentPtr bm xy ofs $ \q -> pokeArray q values++unsafeReadPixel +  :: PixelComponent t +  => Bitmap t      -- ^ the bitmap+  -> Offset        -- ^ position (x,y)+  -> IO [t]+unsafeReadPixel bm xy = unsafeReadComponents bm xy 0 (bitmapNChannels bm)+   +-- | These functions assume that the number of channels of the bitmap+-- agrees with the number suffix of the function. +--+-- (Maybe I should put+-- the number of components into the Bitmap type? But that would cause+-- different problems...)+unsafeReadPixel1 :: PixelComponent t => Bitmap t -> Offset -> IO t+unsafeReadPixel2 :: PixelComponent t => Bitmap t -> Offset -> IO (t,t)+unsafeReadPixel3 :: PixelComponent t => Bitmap t -> Offset -> IO (t,t,t)+unsafeReadPixel4 :: PixelComponent t => Bitmap t -> Offset -> IO (t,t,t,t)++unsafeWritePixel1 :: PixelComponent t => Bitmap t -> Offset -> t -> IO ()+unsafeWritePixel2 :: PixelComponent t => Bitmap t -> Offset -> (t,t) -> IO ()+unsafeWritePixel3 :: PixelComponent t => Bitmap t -> Offset -> (t,t,t) -> IO ()+unsafeWritePixel4 :: PixelComponent t => Bitmap t -> Offset -> (t,t,t,t) -> IO ()++unsafeReadPixel1 bm xy = withComponentPtr bm xy 0 $ \p -> liftM (\[x]       ->  x       ) $ peekArray 1 p+unsafeReadPixel2 bm xy = withComponentPtr bm xy 0 $ \p -> liftM (\[x,y]     -> (x,y)    ) $ peekArray 2 p+unsafeReadPixel3 bm xy = withComponentPtr bm xy 0 $ \p -> liftM (\[x,y,z]   -> (x,y,z)  ) $ peekArray 3 p+unsafeReadPixel4 bm xy = withComponentPtr bm xy 0 $ \p -> liftM (\[x,y,z,w] -> (x,y,z,w)) $ peekArray 4 p++unsafeWritePixel1 bm xy  x        = withComponentPtr bm xy 0 $ \q -> pokeArray q [x]+unsafeWritePixel2 bm xy (x,y)     = withComponentPtr bm xy 0 $ \q -> pokeArray q [x,y]+unsafeWritePixel3 bm xy (x,y,z)   = withComponentPtr bm xy 0 $ \q -> pokeArray q [x,y,z]+unsafeWritePixel4 bm xy (x,y,z,w) = withComponentPtr bm xy 0 $ \q -> pokeArray q [x,y,z,w]++--------------------------------------------------------------------------------+ {-# SPECIALIZE genericComponentRowMap -      :: (Int -> Ptr Word8  -> Ptr Word8  -> IO ()) -> Bitmap Word8  -> Bitmap Word8  -> IO () #-}+      :: (Int -> Int -> Ptr Word8  -> Ptr Word8  -> IO ()) -> Bitmap Word8  -> Bitmap Word8  -> IO () #-} {-# SPECIALIZE genericComponentRowMap -      :: (Int -> Ptr Word16 -> Ptr Word16 -> IO ()) -> Bitmap Word16 -> Bitmap Word16 -> IO () #-}+      :: (Int -> Int -> Ptr Word16 -> Ptr Word16 -> IO ()) -> Bitmap Word16 -> Bitmap Word16 -> IO () #-} {-# SPECIALIZE genericComponentRowMap -      :: (Int -> Ptr Word32 -> Ptr Word32 -> IO ()) -> Bitmap Word32 -> Bitmap Word32 -> IO () #-}+      :: (Int -> Int -> Ptr Word32 -> Ptr Word32 -> IO ()) -> Bitmap Word32 -> Bitmap Word32 -> IO () #-} {-# SPECIALIZE genericComponentRowMap -      :: (Int -> Ptr Float  -> Ptr Float  -> IO ()) -> Bitmap Float  -> Bitmap Float  -> IO () #-}+      :: (Int -> Int -> Ptr Float  -> Ptr Float  -> IO ()) -> Bitmap Float  -> Bitmap Float  -> IO () #-}  {-# SPECIALIZE genericComponentRowMap -      :: (Int -> Ptr Word8  -> Ptr Float  -> IO ()) -> Bitmap Word8  -> Bitmap Float  -> IO () #-}+      :: (Int -> Int -> Ptr Word8  -> Ptr Float  -> IO ()) -> Bitmap Word8  -> Bitmap Float  -> IO () #-} {-# SPECIALIZE genericComponentRowMap -      :: (Int -> Ptr Float  -> Ptr Word8  -> IO ()) -> Bitmap Float  -> Bitmap Word8  -> IO () #-}+      :: (Int -> Int -> Ptr Float  -> Ptr Word8  -> IO ()) -> Bitmap Float  -> Bitmap Word8  -> IO () #-}  {-# SPECIALIZE genericComponentRowMap -      :: (Int -> Ptr Word16 -> Ptr Float  -> IO ()) -> Bitmap Word16 -> Bitmap Float  -> IO () #-}+      :: (Int -> Int -> Ptr Word16 -> Ptr Float  -> IO ()) -> Bitmap Word16 -> Bitmap Float  -> IO () #-} {-# SPECIALIZE genericComponentRowMap -      :: (Int -> Ptr Float  -> Ptr Word16 -> IO ()) -> Bitmap Float  -> Bitmap Word16 -> IO () #-}+      :: (Int -> Int -> Ptr Float  -> Ptr Word16 -> IO ()) -> Bitmap Float  -> Bitmap Word16 -> IO () #-} --- the @Int@ is the number of pixel components (nchn*width)+-- the first Int is the y position+-- the second Int is the number of pixel components (nchn*width) genericComponentRowMap    :: (PixelComponent s, PixelComponent t) -  => (Int -> Ptr s -> Ptr t -> IO ()) +  => (Int -> Int -> Ptr s -> Ptr t -> IO ())        -- ^ ypos totalNumberOfComps src tgt   -> Bitmap s -> Bitmap t -> IO () genericComponentRowMap rowAction bm1 bm2 = do @@ -195,18 +373,19 @@     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+    forM_ (zip3 [0..h1-1]+               (map (*xlen1) [0..h1-1]) +               (map (*xlen2) [0..h2-1])) $ \(ypos,vo1,vo2) -> do       let p1 = ptr1 `myPlusPtr` vo1                p2 = ptr2 `myPlusPtr` vo2     -      rowAction npc p1 p2 +      rowAction ypos npc p1 p2   ------- --- the @Int@ is the number of pixels (width)+-- userAction ypos width ptr1 nchn1 ptr2 nchn2  genericPixelRowMap    :: (PixelComponent s, PixelComponent t) -  => (Int -> Ptr s -> NChn -> Ptr t -> NChn -> IO ())    -- width ptr1 nchn1 ptr2 nchn2 +  => (Int -> Int -> Ptr s -> NChn -> Ptr t -> NChn -> IO ())    -- ^ ypos width ptr1 nchn1 ptr2 nchn2    -> Bitmap s -> Bitmap t -> IO () genericPixelRowMap rowAction bm1 bm2 = do @@ -225,10 +404,12 @@   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+    forM_ (zip3 [0..h1-1] +                (map (*xlen1) [0..h1-1]) +                (map (*xlen2) [0..h2-1])) $ \(ypos,o1,o2) -> do+      let p1 = ptr1 `myPlusPtr` o1     +          p2 = ptr2 `myPlusPtr` o2     +      rowAction ypos minw p1 nchn1 p2 nchn2  --------------------------------------------------------------------------------       @@ -252,22 +433,41 @@     x <- peek q1     poke q2 (f x)     return (advancePtr1 q1, advancePtr1 q2)-  g :: Int -> Ptr s -> Ptr t -> IO ()-  g n p1 p2 = do+  g :: Int -> Int -> Ptr s -> Ptr t -> IO ()+  g ypos n p1 p2 = do     foldM_ h (p1,p2) [0..n-1] +{-# SPECIALIZE genericComponentMapWithPos :: (Int -> Int -> Word8  -> Word8 ) -> Bitmap Word8  -> Bitmap Word8  -> IO () #-}      +{-# SPECIALIZE genericComponentMapWithPos :: (Int -> Int -> Word16 -> Word16) -> Bitmap Word16 -> Bitmap Word16 -> IO () #-}      +{-# SPECIALIZE genericComponentMapWithPos :: (Int -> Int -> Word32 -> Word32) -> Bitmap Word32 -> Bitmap Word32 -> IO () #-}      +{-# SPECIALIZE genericComponentMapWithPos :: (Int -> Int -> Float  -> Float ) -> Bitmap Float  -> Bitmap Float  -> IO () #-}       --- | Maps a function over each component of each pixel. Warning: this is slow!+genericComponentMapWithPos +  :: forall s t . (PixelComponent s, PixelComponent t) +  => (Int -> Int -> s -> t) -> Bitmap s -> Bitmap t -> IO ()  +genericComponentMapWithPos f bm1 bm2 = genericComponentRowMap g bm1 bm2 where+  h :: Int -> (Ptr s, Ptr t) -> Int -> IO (Ptr s, Ptr t)+  h ypos (q1,q2) xpos = do+    x <- peek q1+    poke q2 (f xpos ypos x)+    return (advancePtr1 q1, advancePtr1 q2)+  g :: Int -> Int -> Ptr s -> Ptr t -> IO ()+  g ypos n p1 p2 = do+    foldM_ (h ypos) (p1,p2) [0..n-1]++--------------------------------------------------------------------------------++-- | Maps a function over each component of each pixel. Warning: this is probably 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@@ -276,7 +476,7 @@ componentMapInPlace f bm = do   genericComponentMap f bm bm     --- | See the comments at 'componentMap'.+-- See the comments at 'componentMap'. componentMap'    :: (PixelComponent s, PixelComponent t)    => (s -> t) @@ -384,13 +584,8 @@ 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   @@ -400,6 +595,7 @@   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@@ -450,8 +646,10 @@       pad2  = bitmapRowPadding bm2       fptr2 = bitmapPtr bm2 -  when (siz1 /= siz2) $ error "bitmap/extractChannelInto: incompatible dimensions"-+  when (siz1 /= siz2)          $ error "bitmap/extractChannelInto: incompatible dimensions"+  when (ofs1<0 || ofs1>=nchn1) $ error "bitmap/extractChannelInto: invalid source channel index"+  when (ofs2<0 || ofs2>=nchn2) $ error "bitmap/extractChannelInto: invalid target channel index"+     withForeignPtr fptr1 $ \ptr1 ->      withForeignPtr fptr2 $ \ptr2 ->            c_extract_channel @@ -462,7 +660,241 @@  -------------------------------------------------------------------------------- --- | The data is copied, not shared.+bilinearResample+  :: PixelComponent t +  => Bitmap t        -- ^ source image+  -> Size            -- ^ target image size+  -> Maybe Alignment -- ^ target image alignment+  -> IO (Bitmap t)   +bilinearResample bm1 siz2@(w2,h2) malign = do+  let nchn1 = bitmapNChannels bm1+  bm2 <- newBitmapUninitialized siz2 nchn1 malign+  forM_ [0..nchn1-1] $ \ofs ->+    bilinearResampleChannelInto bm1 ofs bm2 ofs+  return bm2+++bilinearResampleChannel+  :: PixelComponent t +  => Bitmap t        -- ^ source image+  -> Int             -- ^ source channel index +  -> Size            -- ^ target image size+  -> Maybe Alignment -- ^ target image alignment+  -> IO (Bitmap t)   +bilinearResampleChannel bm1 ofs1 siz2@(w2,h2) malign = do+  let nchn1 = bitmapNChannels bm1+  when (ofs1<0 || ofs1>=nchn1) $ error "bitmap/bilinearResampleChannel: invalid channel index"+  bm2 <- newBitmapUninitialized siz2 1 malign+  bilinearResampleChannelInto bm1 ofs1 bm2 0+  return bm2+++bilinearResampleChannelInto +  :: forall t. PixelComponent t +  => Bitmap t     -- ^ source image+  -> Int          -- ^ source channel index +  -> Bitmap t     -- ^ target image+  -> Int          -- ^ target channel index+  -> IO ()+bilinearResampleChannelInto bm1 ofs1 bm2 ofs2 = do++  let nchn1 = bitmapNChannels bm1+      siz1@(w1,h1) = bitmapSize bm1+      pad1  = bitmapRowPadding bm1+      fptr1 = bitmapPtr bm1++  let nchn2 = bitmapNChannels bm2+      siz2@(w2,h2) = bitmapSize bm2+      pad2  = bitmapRowPadding bm2+      fptr2 = bitmapPtr bm2++  when (ofs1<0 || ofs1>=nchn1) $ error "bitmap/bilinearResampleChannelInto: invalid source channel index"+  when (ofs2<0 || ofs2>=nchn2) $ error "bitmap/bilinearResampleChannelInto: invalid target channel index"+  +  withForeignPtr fptr1 $ \ptr1 -> +    withForeignPtr fptr2 $ \ptr2 ->     +      c_bilinear_resample_channel +        (c_type (undefined::t))+        (ci w1) (ci h1) ptr1 (ci nchn1) (ci pad1) (ci ofs1)+        (ci w2) (ci h2) ptr2 (ci nchn2) (ci pad2) (ci ofs2)++--------------------------------------------------------------------------------++-- | This is equivalent to @componentMap (\c -> c^gamma)@, except that+-- @(^)@ is defined only for integral exponents; but should be faster anyway.+powerlawGammaCorrection +  :: forall t. PixelComponent t +  => Float             -- ^ gamma+  -> Bitmap t          -- ^ source bitmap+  -> Maybe Alignment   -- ^ target alignment+  -> IO (Bitmap t)+powerlawGammaCorrection gamma bm1 malign = do+  let nchn1 = bitmapNChannels bm1+      siz1@(w1,h1) = bitmapSize bm1+      pad1 = bitmapRowPadding bm1+      fptr1 = bitmapPtr bm1+  bm2 <- newBitmapUninitialized siz1 nchn1 malign+  let pad2 = bitmapRowPadding bm2+      fptr2 = bitmapPtr bm2+  withForeignPtr fptr1 $ \ptr1 -> +    withForeignPtr fptr2 $ \ptr2 ->     +      c_gamma_correct_all_channels +        (c_type (undefined::t))+        (realToFrac gamma) +        (ci w1) (ci h1) (ci nchn1) +        ptr1 (ci pad1) +        ptr2 (ci pad2) +  return bm2+  ++powerlawGammaCorrectionChannel+  :: PixelComponent t +  => Float           -- ^ gamma+  -> Bitmap t        -- ^ source image+  -> Int             -- ^ source channel index +  -> Maybe Alignment -- ^ target image alignment+  -> IO (Bitmap t)   +powerlawGammaCorrectionChannel gamma bm1 ofs1 malign = do+  let nchn1 = bitmapNChannels bm1+      siz1  = bitmapSize bm1+  when (ofs1<0 || ofs1>=nchn1) $ error "bitmap/powerlawGammaCorrectionChannel: invalid channel index"+  bm2 <- newBitmapUninitialized siz1 1 malign+  powerlawGammaCorrectionChannelInto gamma bm1 ofs1 bm2 0+  return bm2+  +powerlawGammaCorrectionChannelInto+  :: forall t. PixelComponent t +  => Float        -- ^ gamma+  -> Bitmap t     -- ^ source image+  -> Int          -- ^ source channel index +  -> Bitmap t     -- ^ target image+  -> Int          -- ^ target channel index+  -> IO ()+powerlawGammaCorrectionChannelInto gamma bm1 ofs1 bm2 ofs2 = do++  let nchn1 = bitmapNChannels bm1+      siz1@(w1,h1) = bitmapSize bm1+      pad1  = bitmapRowPadding bm1+      fptr1 = bitmapPtr bm1++  let nchn2 = bitmapNChannels bm2+      siz2@(w2,h2) = bitmapSize bm2+      pad2  = bitmapRowPadding bm2+      fptr2 = bitmapPtr bm2++  when (siz1 /= siz2)          $ error "bitmap/powerlawGammaCorrectionChannelInto: incompatible dimensions"+  when (ofs1<0 || ofs1>=nchn1) $ error "bitmap/powerlawGammaCorrectionChannelInto: invalid source channel index"+  when (ofs2<0 || ofs2>=nchn2) $ error "bitmap/powerlawGammaCorrectionChannelInto: invalid target channel index"+  +  withForeignPtr fptr1 $ \ptr1 -> +    withForeignPtr fptr2 $ \ptr2 ->     +      c_gamma_correct_channel +        (c_type (undefined::t))+        (realToFrac gamma) +        (ci w1) (ci h1) +        ptr1 (ci nchn1) (ci pad1) (ci ofs1)+        ptr2 (ci nchn2) (ci pad2) (ci ofs2)++--------------------------------------------------------------------------------++-- | Blends two bitmaps with the given weights; that is, the result is+-- the specified linear combination. If the values are outside the allowed+-- range (this can happen with the Word8, Word16, Word32 types and weights+-- whose sum is bigger than 1, or with a negative weight), then they are+-- clipped. The clipping /does not/ happen with the Float component type.+blendBitmaps+  :: PixelComponent t +  => Float           -- ^ weight1+  -> Float           -- ^ weight2+  -> Bitmap t        -- ^ source1 image +  -> Bitmap t        -- ^ source2 image +  -> Maybe Alignment -- ^ target alignment+  -> IO (Bitmap t)+-- this could be implemented more effectively by a specialized c routine+blendBitmaps weight1 weight2 bm1 bm2 malign = do +  let nchn1 = bitmapNChannels bm1+      siz1  = bitmapSize bm1+  let nchn2 = bitmapNChannels bm2+      siz2  = bitmapSize bm2+  when (siz1  /= siz2 ) $ error "bitmap/blend: incompatible dimensions"    +  when (nchn1 /= nchn2) $ error "bitmap/blend: incompatible number of channels"+  bm3 <- newBitmapUninitialized siz1 nchn1 malign+  forM [0..nchn1-1] $ \ofs -> +    blendChannelsInto weight1 weight2 bm1 ofs bm2 ofs bm3 ofs+  return bm3  +  +blendChannels+  :: PixelComponent t +  => Float        -- ^ weight1+  -> Float        -- ^ weight2+  -> Bitmap t     -- ^ source1 image +  -> Int          -- ^ source1 channel index  +  -> Bitmap t     -- ^ source2 image +  -> Int          -- ^ source2 channel index  +  -> Maybe Alignment -- ^ target alignment+  -> IO (Bitmap t)+blendChannels weight1 weight2 bm1 ofs1 bm2 ofs2 malign = do+  let nchn1 = bitmapNChannels bm1+      siz1  = bitmapSize bm1+  let nchn2 = bitmapNChannels bm2+      siz2  = bitmapSize bm2+  when (siz1 /= siz2)          $ error "bitmap/blendChannels: incompatible dimensions"    +  when (ofs1<0 || ofs1>=nchn1) $ error "bitmap/blendChannels: invalid channel index"+  when (ofs2<0 || ofs2>=nchn2) $ error "bitmap/blendChannels: invalid channel index"+  bm3 <- newBitmapUninitialized siz1 1 malign+  blendChannelsInto weight1 weight2 bm1 ofs1 bm2 ofs2 bm3 0+  return bm3  +  +blendChannelsInto+  :: forall t. PixelComponent t +  => Float        -- ^ weight1+  -> Float        -- ^ weight2+  -> Bitmap t     -- ^ source1 image +  -> Int          -- ^ source1 channel index  +  -> Bitmap t     -- ^ source2 image +  -> Int          -- ^ source2 channel index  +  -> Bitmap t     -- ^ target image+  -> Int          -- ^ target channel index+  -> IO ()+blendChannelsInto weight1 weight2 bm1 ofs1 bm2 ofs2 bm3 ofs3 = do++  let nchn1 = bitmapNChannels bm1+      siz1@(w1,h1) = bitmapSize bm1+      pad1  = bitmapRowPadding bm1+      fptr1 = bitmapPtr bm1++  let nchn2 = bitmapNChannels bm2+      siz2@(w2,h2) = bitmapSize bm2+      pad2  = bitmapRowPadding bm2+      fptr2 = bitmapPtr bm2++  let nchn3 = bitmapNChannels bm3+      siz3@(w3,h3) = bitmapSize bm3+      pad3  = bitmapRowPadding bm3+      fptr3 = bitmapPtr bm3++  when (siz1 /= siz2)          $ error "bitmap/blendChannelInto: incompatible dimensions"+  when (siz2 /= siz3)          $ error "bitmap/blendChannelInto: incompatible dimensions"+  when (ofs1<0 || ofs1>=nchn1) $ error "bitmap/blendChannelInto: invalid source channel index 1"+  when (ofs2<0 || ofs2>=nchn2) $ error "bitmap/blendChannelInto: invalid source channel index 2"+  when (ofs3<0 || ofs3>=nchn3) $ error "bitmap/blendChannelInto: invalid target channel index"+  +  withForeignPtr fptr1 $ \ptr1 -> +    withForeignPtr fptr2 $ \ptr2 ->     +      withForeignPtr fptr3 $ \ptr3 ->     +        c_linear_combine_channels +          (c_type (undefined::t))+          (realToFrac weight1) (realToFrac weight2) +          (ci w1) (ci h1) +          ptr1 (ci nchn1) (ci pad1) (ci ofs1)+          ptr2 (ci nchn2) (ci pad2) (ci ofs2)+          ptr3 (ci nchn3) (ci pad3) (ci ofs3)+        +--------------------------------------------------------------------------------++-- | The data is copied, not shared. Note that the resulting ByteString is+-- encoded using the host machine's endianness, so it may be not compatible+-- across different architectures! copyBitmapToByteString :: PixelComponent t => Bitmap t -> IO ByteString copyBitmapToByteString bm = do   let n = bitmapSizeInBytes bm@@ -473,6 +905,8 @@   return $ B.fromForeignPtr (castForeignPtr newfp) 0 n  -- | The data is copied, not shared.+-- Note that we expect the ByteString to be encoded+-- encoded using the host machine's endianness. 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@@ -489,7 +923,7 @@     else do       newfptr <- mallocForeignPtrBytes n       withForeignPtr bsfptr0 $ \src0 -> do-        let src = src0 `plusPtr` ofs+        let src = src0 `myPlusPtr` ofs         withForeignPtr newfptr $ \tgt ->           c_memcpy src tgt n       return $ bm { bitmapPtr = castForeignPtr newfptr } @@ -511,6 +945,8 @@ ci :: Int -> CInt ci = fromIntegral +-- @c_memset target count fill@.+-- Note that we use /nonstandard/ argument order! foreign import ccall unsafe "bm.h c_memset"    c_memset :: Ptr Word8 -> Int -> Word8 -> IO () @@ -519,6 +955,8 @@ foreign import ccall unsafe "bm.h c_memcpy"    c_memcpy :: Ptr Word8 -> Ptr Word8 -> Int -> IO () +--------------------+ {- void c_extract_channel(   ( int k_type@@ -528,12 +966,94 @@   ); -} +-- offset is measured in components, not bytes! 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+    :: CInt              -- ^ component type+    -> CInt -> CInt      -- ^ width, height+    -> Ptr a -> CInt -> CInt -> CInt  -- ^ source, nchn, pad, offset+    -> Ptr a -> CInt -> CInt -> CInt  -- ^ target, nchn, pad, offset     -> IO ()-        ++--------------------+ +{-+void c_bilinear_resample_channel+  ( int k_type+  , int width1, int height1, void *p1, int nchn1, int pad1, int ofs1 +  , int width2, int height2, void *p2, int nchn2, int pad2, int ofs2 +  );       +-}++-- offset is measured in components, not bytes!+foreign import ccall unsafe "bm.h c_bilinear_resample_channel"+  c_bilinear_resample_channel +    :: CInt                                           -- ^ component type+    -> CInt -> CInt -> Ptr a -> CInt -> CInt -> CInt  -- ^ width, height, source, nchn, pad, offset+    -> CInt -> CInt -> Ptr a -> CInt -> CInt -> CInt  -- ^ width, height, target, nchn, pad, offset+    -> IO ()++--------------------++{-+void c_gamma_correct_channel+  ( int k_type+  , float gamma+  , int width, int height+  , void *p1, int nchn1, int pad1, int ofs1 +  , void *p2, int nchn2, int pad2, int ofs2 +  );+  +void c_gamma_correct_all_channels+  ( int k_type+  , float gamma+  , int width, int height, int nchn+  , void *p1, int pad1 +  , void *p2, int pad2 +  );+-}+ +-- offset is measured in components, not bytes!+foreign import ccall unsafe "bm.h c_gamma_correct_channel"+  c_gamma_correct_channel +    :: CInt              -- ^ component type+    -> CFloat            -- ^ gamma+    -> CInt -> CInt      -- ^ width, height+    -> Ptr a -> CInt -> CInt -> CInt  -- ^ source, nchn, pad, offset+    -> Ptr a -> CInt -> CInt -> CInt  -- ^ target, nchn, pad, offset+    -> IO ()++foreign import ccall unsafe "bm.h c_gamma_correct_all_channels"+  c_gamma_correct_all_channels +    :: CInt                 -- ^ component type+    -> CFloat               -- ^ gamma+    -> CInt -> CInt -> CInt -- ^ width, height, nchn+    -> Ptr a -> CInt        -- ^ source, pad+    -> Ptr a -> CInt        -- ^ target, pad+    -> IO ()++--------------------+    +{-+void c_linear_combine_channels+  ( int k_type+  , float weight1, float weight2+  , int width, int height+  , void *p1, int nchn1, int pad1, int ofs1 +  , void *p2, int nchn2, int pad2, int ofs2 +  , void *p3, int nchn3, int pad3, int ofs3 +  );    +-}++-- offset is measured in components, not bytes!+foreign import ccall unsafe "bm.h c_linear_combine_channels"+  c_linear_combine_channels +    :: CInt              -- ^ component type+    -> CFloat -> CFloat  -- ^ weight1, weight2 +    -> CInt -> CInt      -- ^ width, height+    -> Ptr a -> CInt -> CInt -> CInt  -- ^ source1, nchn, pad, offset+    -> Ptr a -> CInt -> CInt -> CInt  -- ^ source2, nchn, pad, offset+    -> Ptr a -> CInt -> CInt -> CInt  -- ^ target,  nchn, pad, offset+    -> IO ()+ --------------------------------------------------------------------------------
Data/Bitmap/Internal.hs view
@@ -1,7 +1,7 @@  -------------------------------------------------------------------------------- -- Module      : Data.Bitmap.Internal--- Version     : 0.0.0+-- Version     : 0.0.1 -- License     : BSD3 -- Copyright   : (c) 2009 Balazs Komuves -- Author      : Balazs Komuves@@ -33,10 +33,19 @@   | PctFloat   deriving Show   +pixelComponentSize :: PixelComponentType -> Int+pixelComponentSize pct = case pct of+  PctWord8  -> 1 +  PctWord16 -> 2 +  PctWord32 -> 4 +  PctFloat  -> 4 +  +--------------------------------------------------------------------------------+ class (Num t, Storable t) => PixelComponent t where   c_type :: t -> CInt-  nbytes :: t -> Int-  nbytes x = sizeOf x+--  nbytes :: t -> Int+--  nbytes x = sizeOf x   toFloat   :: t -> Float   fromFloat :: Float -> t   @@ -46,26 +55,44 @@   2 -> PctWord16   3 -> PctWord32   4 -> PctFloat++-- hmm hmm let's hope ghc will inline this into an +-- inlined function if i explicitely ask for it... +{-# INLINE clamp #-}+clamp :: Float -> Float+clamp = min 1 . max 0   instance PixelComponent Word8 where +  {-# SPECIALIZE instance PixelComponent Word8  #-}   c_type _ = 1-  fromFloat = floor . (*255.99999)+  fromFloat = floor . (+0.5) . (*255) . min 1 . max 0   toFloat = (*3.92156862745098e-3) . fromIntegral     -- 1/255  instance PixelComponent Word16 where +  {-# SPECIALIZE instance PixelComponent Word16 #-}   c_type _ = 2-  fromFloat = floor . (*65535.99999)+  fromFloat = floor . (+0.5) . (*65535) . min 1 . max 0   toFloat = (*1.5259021896696422e-5) . fromIntegral   -- 1/65535  instance PixelComponent Word32 where +  {-# SPECIALIZE instance PixelComponent Word32 #-}   c_type _ = 3-  fromFloat = floor . (*4294967295.99999)+  fromFloat = floor . (+0.5) . (*4294967295) . min 1 . max 0   toFloat = (*2.3283064370807974e-10) . fromIntegral  -- 1/(2^32-1)    instance PixelComponent Float where +  {-# SPECIALIZE instance PixelComponent Float  #-}   c_type _ = 4-  toFloat = id   fromFloat = id+  toFloat = id++--------------------------------------------------------------------------------++{-# SPECIALIZE isValidAlignment :: Int -> Bool #-}+isValidAlignment :: Integral a => a -> Bool+isValidAlignment a = elem a [1,2,4,8,16]++--------------------------------------------------------------------------------    -- to provide better documentation type Size   = (Int,Int)@@ -101,5 +128,23 @@ bitmapSizeInBytes bm = h*x where   x = bitmapPaddedRowSizeInBytes bm   (_,h) = bitmapSize bm  + +-- | The width divided by the height.+bitmapAspect :: Fractional a => Bitmap t -> a+bitmapAspect bm = (fromIntegral x / fromIntegral y) where+  (x,y) = bitmapSize bm +--------------------------------------------------------------------------------++-- we mimic the OpenGL padding at the moment+recommendedPadding :: forall t. PixelComponent t => Bitmap t -> Int+recommendedPadding bm = pad where+  (w,_) = bitmapSize bm+  n = bitmapNChannels bm+  b = bitmapRowAlignment bm+  s = sizeOf (undefined::t)+  a = if b<s then s else b+  k = case divMod a s of (q,r) -> if r==0 then q else error "recommendedPadding: should not happen"+  pad = s * ( k * div (n*w + k-1) k - n*w )    +   --------------------------------------------------------------------------------
Data/Bitmap/Pure.hs view
@@ -1,7 +1,7 @@  -------------------------------------------------------------------------------- -- Module      : Data.Bitmap.Pure--- Version     : 0.0.0+-- Version     : 0.0.1 -- License     : BSD3 -- Copyright   : (c) 2009 Balazs Komuves -- Author      : Balazs Komuves@@ -16,7 +16,9 @@   (      module Data.Bitmap.Base     +    -- * Creating bitmaps   , emptyBitmap+  , createSingleChannelBitmap     -- * Mapping over bitmaps   , componentMap   , componentMap'@@ -27,6 +29,15 @@   , combineChannels    , extractChannels    , extractSingleChannel +    -- * Bilinear resampling+  , bilinearResample+  , bilinearResampleChannel  +    -- * Blending+  , blendBitmaps  +  , blendChannels  +    -- * Gamma correction+  , powerlawGammaCorrection+  , powerlawGammaCorrectionChannel        -- * Conversion to ByteString   , bitmapToByteString     ) @@ -62,13 +73,24 @@   -> Bitmap t emptyBitmap siz nchn malign = unsafePerformIO $ IO.newBitmap siz nchn malign +-- | Creates a single channel bitmap from a function.+-- This is probably a bit slow.+createSingleChannelBitmap+  :: forall t. PixelComponent t +  => Size               -- ^ (width,height)+  -> Maybe Alignment    -- ^ the row alignment of the new image+  -> (Int -> Int -> t)  -- ^ the function used to create the bitmap+  -> Bitmap t+createSingleChannelBitmap siz maling fun = unsafePerformIO $ +  IO.createSingleChannelBitmap siz maling fun + -------------------------------------------------------------------------------- --- | Warning: this is probably slow+-- | 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+-- | 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 @@ -113,10 +135,86 @@ combineChannels :: forall t. PixelComponent t => [Bitmap t] -> Maybe Alignment -> Bitmap t combineChannels bms malign = unsafePerformIO $   IO.combineChannels bms malign++--------------------------------------------------------------------------------++bilinearResample +  :: PixelComponent t +  => Bitmap t           -- ^ source image+  -> Size               -- ^ target image size+  -> Maybe Alignment    -- ^ target image row alignment+  -> Bitmap t  +bilinearResample bm siz malign = unsafePerformIO $ +  IO.bilinearResample bm siz malign++bilinearResampleChannel+  :: PixelComponent t +  => Bitmap t           -- ^ source image+  -> Int                -- ^ source channel indexe+  -> Size               -- ^ target image size+  -> Maybe Alignment    -- ^ target image row alignment+  -> Bitmap t  +bilinearResampleChannel bm j siz malign = unsafePerformIO $ +  IO.bilinearResampleChannel bm j siz malign    -------------------------------------------------------------------------------- --- | Note that the data is /shared/.+-- | Blends two bitmaps with the given weights; that is, the result is+-- the specified linear combination. If the values are outside the allowed+-- range (this can happen with the Word8, Word16, Word32 types and weights+-- whose sum is bigger than 1, or with a negative weight), then they are+-- clipped. The clipping /does not/ happen with the Float component type.+blendBitmaps+  :: PixelComponent t +  => Float           -- ^ weight 1+  -> Float           -- ^ weight 2+  -> Bitmap t        -- ^ source image 1 +  -> Bitmap t        -- ^ source image 2+  -> Maybe Alignment -- ^ target alignment+  -> Bitmap t+blendBitmaps w1 w2 bm1 bm2 malign = unsafePerformIO $ +  IO.blendBitmaps w1 w2 bm1 bm2 malign++blendChannels+  :: PixelComponent t +  => Float           -- ^ weight 1+  -> Float           -- ^ weight 2+  -> Bitmap t        -- ^ source image 1 +  -> Int             -- ^ channel index 1+  -> Bitmap t        -- ^ source image 2+  -> Int             -- ^ channel index 2+  -> Maybe Alignment -- ^ target alignment+  -> Bitmap t+blendChannels w1 w2 bm1 ofs1 bm2 ofs2 malign = unsafePerformIO $ +  IO.blendChannels w1 w2 bm1 ofs1 bm2 ofs2 malign+  +--------------------------------------------------------------------------------+  +-- | This is equivalent to @componentMap (\c -> c^gamma)@, except that+-- @(^)@ is defined only for integral exponents; but should be faster anyway.+powerlawGammaCorrection+  :: PixelComponent t +  => Float              -- ^ gamma+  -> Bitmap t           -- ^ source image+  -> Maybe Alignment    -- ^ target image row alignment+  -> Bitmap t  +powerlawGammaCorrection gamma bm malign = unsafePerformIO $ +  IO.powerlawGammaCorrection gamma bm malign++powerlawGammaCorrectionChannel+  :: PixelComponent t +  => Float              -- ^ gamma+  -> Bitmap t           -- ^ source image+  -> Int                -- ^ source channel indexe+  -> Maybe Alignment    -- ^ target image row alignment+  -> Bitmap t  +powerlawGammaCorrectionChannel gamma bm j malign = unsafePerformIO $ +  IO.powerlawGammaCorrectionChannel gamma bm j malign+  +--------------------------------------------------------------------------------++-- | Note that the data is /shared/; and also that the resulting ByteString+-- is encoded using the host machine's endianness. bitmapToByteString :: PixelComponent t => Bitmap t -> ByteString bitmapToByteString bm = bs where   bs = B.fromForeignPtr (castForeignPtr $ bitmapPtr bm) 0 n
bitmap.cabal view
@@ -1,5 +1,5 @@ Name:                bitmap-Version:             0.0.0+Version:             0.0.1 Synopsis:            A library for handling and manipulating bitmaps. Description:         A library for handling and manipulating bitmaps (that is,                      rectangular pixel arrays).@@ -22,10 +22,10 @@  Library   if flag(base4)-    Build-Depends:   base >= 4 && < 5, array, containers, bytestring >= 0.9+    Build-Depends:   base >= 4 && < 5, bytestring >= 0.9     cpp-options:     -DBASE_VERSION=4   else-    Build-Depends:   base >= 3 && < 4, array, containers, bytestring >= 0.9+    Build-Depends:   base >= 3 && < 4, bytestring >= 0.9     cpp-options:     -DBASE_VERSION=3        Exposed-Modules:     Data.Bitmap,
cbits/bm.c view
@@ -2,18 +2,31 @@ /* the C part of the Data.Bitmap library (c) 2009 Balazs Komuves+license: BSD3 */ +#include <stdlib.h> #include <string.h>+#include <math.h>+ #include "bm.h"  // -----------------------------------------------------------------------------++#if __STDC_VERSION__ >= 199901L+/* "inline" is a keyword */+#else+#define inline /* nothing */+#endif++// -----------------------------------------------------------------------------  +// (to, count, fill) !!!  void c_memset(word8 *q, int count, word8 x) { memset(q,x,count); } -// (from, to, count)+// (from, to, count) !!! void c_memcpy(word8 *p, word8 *q, int count) { memcpy(q,p,count); }@@ -25,10 +38,27 @@ #define K_WORD32 3 #define K_FLOAT  4 -#define PLUSPTR(P,TYP,K) P=(TYP*)(((word8*)P)+K);+#define PLUSPTR(P,TYP,K) (TYP*)(((word8*)(P))+(K)); +#define MAX(A,B) (((A)>(B))?(A):(B))+#define MIN(A,B) (((A)<(B))?(A):(B))+#define CLAMP(X,A,B) MIN(MAX(X,A),B)+#define CLAMP_01(X) MIN(MAX(X,0),1)+ // ----------------------------------------------------------------------------- +inline float toFloat_word8  (word8  x) { return ( (float)x * (3.92156862745098e-3   ) ); }+inline float toFloat_word16 (word16 x) { return ( (float)x * (1.5259021896696422e-5 ) ); }+inline float toFloat_word32 (word32 x) { return ( (float)x * (2.3283064370807974e-10) ); }+inline float toFloat_float  (float  x) { return x; }++inline word8  fromFloat_word8  (float x) { return ( floor ( 0.5 + 255.0        * CLAMP_01(x) ) ); }+inline word16 fromFloat_word16 (float x) { return ( floor ( 0.5 + 65535.0      * CLAMP_01(x) ) ); }+inline word32 fromFloat_word32 (float x) { return ( floor ( 0.5 + 4294967295.0 * CLAMP_01(x) ) ); }+inline float  fromFloat_float  (float x) { return x; }++// -----------------------------------------------------------------------------+ #define C_EXTRACT_CHANNEL(TYP) \ void c_extract_channel_ ## TYP \   ( int width, int height \@@ -45,8 +75,8 @@       q1 += nchn1;  \       q2 += nchn2;  \     }               \-    PLUSPTR(q1,TYP,pad1); \-    PLUSPTR(q2,TYP,pad2); \+    q1 = PLUSPTR(q1,TYP,pad1); \+    q2 = PLUSPTR(q2,TYP,pad2); \   }   \ }  @@ -55,25 +85,279 @@ C_EXTRACT_CHANNEL(word32) C_EXTRACT_CHANNEL(float ) +#define CALL_EXTRACT_CHANNEL(TYP) \+c_extract_channel_ ## TYP ( width,height, p1,nchn1,pad1,ofs1, p2,nchn2,pad2,ofs2 );++// offset is measured in components, not bytes! void c_extract_channel   ( int k_type-  , int width, int height +  , 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;+  { case K_WORD8:  CALL_EXTRACT_CHANNEL(word8 ); break;+    case K_WORD16: CALL_EXTRACT_CHANNEL(word16); break;+    case K_WORD32: CALL_EXTRACT_CHANNEL(word32); break;+    case K_FLOAT:  CALL_EXTRACT_CHANNEL(float ); break;   } }-     ++// -----------------------------------------------------------------------------++// hmm hmm nem jo meg, tullogunk a sorok vegen meg alul is :(++// it's a little bit hackish, but i think that's ok +void interpolate_hack(int width1,int width2, int x, float *fx, int *ix)+{ float t,f;+  int j;+  t = ((float)x+0.5) * ((float)width1) / ((float)width2) - 0.5;  +  j = floor(t);+  f = t-j;+  if ( j < 0 )+  { *ix = 0;+    *fx = 0.0;+  }+  else+  { if ( j > (width1-2) )+    { *ix = width1-2;+      *fx = 1.0;+    }+    else+    { *ix = j;+      *fx = f;+    }+  }+}++// should we ask haskell for allocating the memory for the tables instead of malloc?+#define C_BILINEAR_RESAMPLE_CHANNEL(TYP) \+void c_bilinear_resample_channel_ ## TYP  \+  ( int width1, int height1, TYP *p1, int nchn1, int pad1, int ofs1 \+  , int width2, int height2, TYP *p2, int nchn2, int pad2, int ofs2 \+  )                        \+{ int x,y;                 \+  TYP *q2,*s1,*s2,*r1,*r2; \+  float fx,fy;             \+  int ix,iy;               \+  int linesize1;           \+  float *fhoriztable;      \+  int   *ihoriztable;      \+  float a,b,c;             \+                  \+  q2 = p2 + ofs2; \+  linesize1 = nchn1 * width1 * sizeof(TYP) + pad1; \+                                                   \+  fhoriztable = malloc(sizeof(float)*width2);      \+  ihoriztable = malloc(sizeof(int  )*width2);      \+  for(x=0;x<width2;x++)    \+  { interpolate_hack( width1,width2, x, fhoriztable+x, ihoriztable+x); \+    ihoriztable[x] *= sizeof(TYP) * nchn1; /* !!! */                   \+  }                        \+                           \+  for(y=0;y<height2;y++)   \+  { interpolate_hack( height1,height2, y, &fy, &iy );   \+    s1 = PLUSPTR ( p1 + ofs1 , TYP , iy * linesize1 );  \+    s2 = PLUSPTR ( s1        , TYP ,      linesize1 );  \+    for(x=0;x<width2;x++)      \+    { fx = fhoriztable[x];     \+      ix = ihoriztable[x];     \+      r1 = PLUSPTR ( s1, TYP, ix ); /* we multiplied up during precalc! */ \+      r2 = PLUSPTR ( s2, TYP, ix ); \+      a = (1-fx) * (float)(r1[0]) + fx * (float)(r1[nchn1]); \+      b = (1-fx) * (float)(r2[0]) + fx * (float)(r2[nchn1]); \+      c = (1-fy)*a + fy*b;   \+      *q2 = (TYP)c;   /* round? but what about the floats? */ \+      q2 += nchn2;        \+    }                     \+    q2 = PLUSPTR(q2,TYP,pad2); \+  }                  \+  free(ihoriztable); \+  free(fhoriztable); \+} ++C_BILINEAR_RESAMPLE_CHANNEL(word8 )+C_BILINEAR_RESAMPLE_CHANNEL(word16)+C_BILINEAR_RESAMPLE_CHANNEL(word32)+C_BILINEAR_RESAMPLE_CHANNEL(float )++#define CALL_BILINEAR_RESAMPLE_CHANNEL(TYP) \+c_bilinear_resample_channel_ ## TYP ( width1,height1,p1,nchn1,pad1,ofs1, width2,height2,p2,nchn2,pad2,ofs2 );++// offset is measured in components, not bytes!+void c_bilinear_resample_channel+  ( int k_type+  , int width1, int height1, void *p1, int nchn1, int pad1, int ofs1 +  , int width2, int height2, void *p2, int nchn2, int pad2, int ofs2 +  ) +{ switch(k_type)+  { case K_WORD8:  CALL_BILINEAR_RESAMPLE_CHANNEL(word8 ); break; +    case K_WORD16: CALL_BILINEAR_RESAMPLE_CHANNEL(word16); break;  +    case K_WORD32: CALL_BILINEAR_RESAMPLE_CHANNEL(word32); break; +    case K_FLOAT:  CALL_BILINEAR_RESAMPLE_CHANNEL(float ); break; +  }+}++// -----------------------------------------------------------------------------++#define GAMMA_CORRECT_COMPONENT(TYP,value,gamma) \+  fromFloat_ ## TYP ( exp ( (gamma) * log ( 0.0000001 + toFloat_ ## TYP ( (value) ) ) ) )++#define C_GAMMA_CORRECT_CHANNEL(TYP) \+void c_gamma_correct_channel_ ## TYP \+  ( float gamma           \+  , 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 = GAMMA_CORRECT_COMPONENT (TYP , *q1 , gamma);    \+      q1 += nchn1;  \+      q2 += nchn2;  \+    }               \+    q1 = PLUSPTR(q1,TYP,pad1); \+    q2 = PLUSPTR(q2,TYP,pad2); \+  }   \+} ++C_GAMMA_CORRECT_CHANNEL(word8 )+C_GAMMA_CORRECT_CHANNEL(word16)+C_GAMMA_CORRECT_CHANNEL(word32)+C_GAMMA_CORRECT_CHANNEL(float )++#define C_GAMMA_CORRECT_ALL_CHANNELS(TYP) \+void c_gamma_correct_all_channels_ ## TYP \+  ( float gamma                     \+  , int width, int height, int nchn \+  , TYP *p1, int pad1 \+  , TYP *p2, int pad2 \+  ) \+{ int x,y;          \+  int compsPerLine; \+  TYP *q1,*q2;      \+  q1 = p1;   \+  q2 = p2;   \+  compsPerLine = width * nchn; \+  for(y=0;y<height;y++)   \+  { for(x=0;x<compsPerLine;x++)  \+    { *q2 = GAMMA_CORRECT_COMPONENT (TYP , *q1 , gamma);    \+      q1 ++ ;  \+      q2 ++ ;  \+    }               \+    q1 = PLUSPTR(q1,TYP,pad1); \+    q2 = PLUSPTR(q2,TYP,pad2); \+  }   \+} ++C_GAMMA_CORRECT_ALL_CHANNELS(word8 )+C_GAMMA_CORRECT_ALL_CHANNELS(word16)+C_GAMMA_CORRECT_ALL_CHANNELS(word32)+C_GAMMA_CORRECT_ALL_CHANNELS(float )++#define CALL_GAMMA_CORRECT_CHANNEL(TYP) \+c_gamma_correct_channel_ ## TYP ( gamma, width,height, p1,nchn1,pad1,ofs1, p2,nchn2,pad2,ofs2 );++// offset is measured in components, not bytes!+void c_gamma_correct_channel+  ( int k_type+  , float gamma+  , 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:  CALL_GAMMA_CORRECT_CHANNEL(word8 ); break; +    case K_WORD16: CALL_GAMMA_CORRECT_CHANNEL(word16); break;  +    case K_WORD32: CALL_GAMMA_CORRECT_CHANNEL(word32); break; +    case K_FLOAT:  CALL_GAMMA_CORRECT_CHANNEL(float ); break; +  }+}++#define CALL_GAMMA_CORRECT_ALL_CHANNELS(TYP) \+c_gamma_correct_all_channels_ ## TYP ( gamma, width,height, nchn, p1,pad1, p2,pad2 );++// offset is measured in components, not bytes!+void c_gamma_correct_all_channels+  ( int k_type+  , float gamma+  , int width, int height, int nchn+  , void *p1, int pad1 +  , void *p2, int pad2 +  ) +{ switch(k_type)+  { case K_WORD8:  CALL_GAMMA_CORRECT_ALL_CHANNELS(word8 ); break; +    case K_WORD16: CALL_GAMMA_CORRECT_ALL_CHANNELS(word16); break;  +    case K_WORD32: CALL_GAMMA_CORRECT_ALL_CHANNELS(word32); break; +    case K_FLOAT:  CALL_GAMMA_CORRECT_ALL_CHANNELS(float ); break; +  }+}++// -----------------------------------------------------------------------------+++#define LINEAR_COMBINE_COMPONENT(TYP,value1,value2,weight1,weight2) \+  fromFloat_ ## TYP ( weight1 * toFloat_ ## TYP (value1) +  weight2 * toFloat_ ## TYP (value2) ) +  +#define C_LINEAR_COMBINE_CHANNELS(TYP) \+void c_linear_combine_channels_ ## TYP \+  ( float weight1, float weight2       \+  , int width, int height \+  , TYP *p1, int nchn1, int pad1, int ofs1 \+  , TYP *p2, int nchn2, int pad2, int ofs2 \+  , TYP *p3, int nchn3, int pad3, int ofs3 \+  ) \+{ int x,y;         \+  TYP *q1,*q2,*q3; \+  q1 = p1 + ofs1;  \+  q2 = p2 + ofs2;  \+  q3 = p3 + ofs3;  \+  for(y=0;y<height;y++)   \+  { for(x=0;x<width;x++)  \+    { *q3 = LINEAR_COMBINE_COMPONENT (TYP , *q1 , *q2 , weight1 , weight2);    \+      q1 += nchn1;  \+      q2 += nchn2;  \+      q3 += nchn3;  \+    }               \+    q1 = PLUSPTR(q1,TYP,pad1); \+    q2 = PLUSPTR(q2,TYP,pad2); \+    q3 = PLUSPTR(q3,TYP,pad3); \+  }   \+} ++C_LINEAR_COMBINE_CHANNELS(word8 )+C_LINEAR_COMBINE_CHANNELS(word16)+C_LINEAR_COMBINE_CHANNELS(word32)+C_LINEAR_COMBINE_CHANNELS(float )++#define CALL_LINEAR_COMBINE_CHANNELS(TYP) \+c_linear_combine_channels_ ## TYP         \+  ( weight1,weight2, width,height,        \+    p1,nchn1,pad1,ofs1,      \+    p2,nchn2,pad2,ofs2,      \+    p3,nchn3,pad3,ofs3 );++// offset is measured in components, not bytes!+void c_linear_combine_channels+  ( int k_type+  , float weight1, float weight2+  , int width, int height+  , void *p1, int nchn1, int pad1, int ofs1 +  , void *p2, int nchn2, int pad2, int ofs2 +  , void *p3, int nchn3, int pad3, int ofs3 +  ) +{ switch(k_type)+  { case K_WORD8:  CALL_LINEAR_COMBINE_CHANNELS(word8 ); break; +    case K_WORD16: CALL_LINEAR_COMBINE_CHANNELS(word16); break;  +    case K_WORD32: CALL_LINEAR_COMBINE_CHANNELS(word32); break; +    case K_FLOAT:  CALL_LINEAR_COMBINE_CHANNELS(float ); break; +  }+}++// -----------------------------------------------------------------------------++
cbits/bm.h view
@@ -7,6 +7,7 @@  //------------------------------------------------------------------------------ +// (target, count, fill) void c_memset(word8 *q, int count, word8 x);  // (from, to, count)@@ -20,3 +21,45 @@   , void *p1, int nchn1, int pad1, int ofs1    , void *p2, int nchn2, int pad2, int ofs2    );++//------------------------------------------------------------------------------++// offset is measured in components, not bytes!+void c_bilinear_resample_channel+  ( int k_type+  , int width1, int height1, void *p1, int nchn1, int pad1, int ofs1 +  , int width2, int height2, void *p2, int nchn2, int pad2, int ofs2 +  );++//------------------------------------------------------------------------------++void c_gamma_correct_channel+  ( int k_type+  , float gamma+  , int width, int height+  , void *p1, int nchn1, int pad1, int ofs1 +  , void *p2, int nchn2, int pad2, int ofs2 +  );+  +void c_gamma_correct_all_channels+  ( int k_type+  , float gamma+  , int width, int height, int nchn+  , void *p1, int pad1 +  , void *p2, int pad2 +  );++//------------------------------------------------------------------------------++// offset is measured in components, not bytes!+void c_linear_combine_channels+  ( int k_type+  , float weight1, float weight2+  , int width, int height+  , void *p1, int nchn1, int pad1, int ofs1 +  , void *p2, int nchn2, int pad2, int ofs2 +  , void *p3, int nchn3, int pad3, int ofs3 +  );+  +//------------------------------------------------------------------------------+