   -> Int -> Int
   -> Int -> Int
   -> IO (Vector (Value a))
-readBandIO band xoff yoff sx sy bx by = do
-  toValue <- getToValue band
-  fp <- mallocForeignPtrArray (bx * by)
-  let dtype = fromEnumC (datatype (Proxy :: Proxy a))
-  withForeignPtr fp $ \ptr -> do
-    throwIfError "could not advise read" $
-      adviseRead_
-        band
-        (fromIntegral xoff)
-        (fromIntegral yoff)
-        (fromIntegral sx)
-        (fromIntegral sy)
-        (fromIntegral bx)
-        (fromIntegral by)
-        dtype
-        (castPtr nullPtr)
-    throwIfError "could not read band" $
-      rasterIO_
-        band
-        (fromEnumC GF_Read) 
-        (fromIntegral xoff)
-        (fromIntegral yoff)
-        (fromIntegral sx)
-        (fromIntegral sy)
-        (castPtr ptr)
-        (fromIntegral bx)
-        (fromIntegral by)
-        dtype
-        0
-        0
-  return $ St.map toValue $ St.unsafeFromForeignPtr0 fp (bx * by)
+readBandIO band xoff yoff sx sy bx by =
+  readMasked band $ \(b :: Band s t a') -> do
+    fp <- mallocForeignPtrArray (bx * by)
+    let dtype = fromEnumC (datatype (Proxy :: Proxy a'))
+    withForeignPtr fp $ \ptr -> do
+      throwIfError "could not advise read" $
+        adviseRead_
+          b
+          (fromIntegral xoff)
+          (fromIntegral yoff)
+          (fromIntegral sx)
+          (fromIntegral sy)
+          (fromIntegral bx)
+          (fromIntegral by)
+          dtype
+          (castPtr nullPtr)
+      throwIfError "could not read band" $
+        rasterIO_
+          b
+          (fromEnumC GF_Read) 
+          (fromIntegral xoff)
+          (fromIntegral yoff)
+          (fromIntegral sx)
+          (fromIntegral sy)
+          (castPtr ptr)
+          (fromIntegral bx)
+          (fromIntegral by)
+          dtype
+          0
+          0
+    return $ St.unsafeFromForeignPtr0 fp (bx * by)
 {-# INLINE readBandIO #-}
 
-getToValue band = do
-  mNodata <- c_bandNodataValue band
-  return $ case mNodata of
-                  Nothing -> Value
-                  Just nd -> \v -> if toNodata v == nd then NoData else Value v
-{-# INLINE getToValue #-}
-
+readMasked
+  :: GDALType a
+  => Band s t a
+  -> (forall a'. GDALType a' => Band s t a' -> IO (Vector a'))
+  -> IO (Vector (Value a))
+readMasked band reader
+  | hasFlag MaskPerDataset = useMaskBand
+  | hasFlag MaskNoData     = useNoData
+  | hasFlag MaskAllValid   = useAsIs
+  | otherwise              = useMaskBand
+  where
+    hasFlag f = fromEnumC f .&. c_getMaskFlags band == fromEnumC f
+    useAsIs = fmap (St.map Value) (reader band)
+    useNoData = do
+      mNodata <- c_bandNodataValue band
+      let toValue = case mNodata of
+                      Nothing -> Value
+                      Just nd ->
+                        \v -> if toNodata v == nd then NoData else Value v
+      fmap (St.map toValue) (reader band)
+    useMaskBand = do
+      vs <- reader band
+      mask <- c_getMaskBand band
+      ms <- reader mask
+      return $ St.zipWith (\v m -> if m/=0 then Value v else NoData) vs ms
+{-# INLINE readMasked #-}
 
 foreign import ccall safe "gdal.h GDALRasterAdviseRead" adviseRead_
     :: (Band s a t) -> CInt -> CInt -> CInt -> CInt -> CInt -> CInt -> CInt
     -> Ptr (Ptr CChar) -> IO CInt
 
-        
+
 writeBand :: forall s a. GDALType a
   => (RWBand s a)
   -> Int -> Int

  => Band s t a -> Int -> Int -> GDAL s (Vector (Value a))
readBandBlock band x y = do
  len <- bandBlockLen band
  liftIO $ do
    toValue <- getToValue band
    f <- mallocForeignPtrArray len
    withForeignPtr f $ \ptr ->
      throwIfError "could not read block" $
        readBlock_ band (fromIntegral x) (fromIntegral y) (castPtr ptr)
    return $ St.map toValue $ St.unsafeFromForeignPtr0 f len
{-# INLINE readBandBlock #-}

foreign import ccall safe "gdal.h GDALReadBlock" readBlock_


{# enum GDALDataType as Datatype {upcaseFirstLetter} deriving (Eq) #}

data Value a = Value !a  | NoData deriving (Eq, Show)

unValue :: Value a -> a
unValue (Value a) = a
