liveBook · Manning

Bitmap image

As explained in Learn Haskell by Example

A bitmap is an image representation whose pixels have two possible values, typically corresponding to on and off.

A bitmap image represents every pixel with one of two values: 0 or 1. In PNM, bitmap files use magic numbers P1 for ASCII data and P4 for binary data. Unlike graymaps and pixmaps, a bitmap pixel is a single value rather than an intensity or RGB triple. section 12.2.3

An ASCII bitmap stores pixels as the characters `0` and `1`, so each pixel can be parsed directly. A binary bitmap packs eight pixels into one byte. Bits are read from most significant to least significant, preserving their order as pixels. section 12.2.3

Parsing one packed byte

A binary bitmap parser reads one `Word8`, tests bit positions 7 through 0, and turns each bit into a `Single 1` or `Single 0` pixel. One byte therefore yields eight bitmap pixels. section 12.2.3 section 12.2.3

Binary bitmap data is arranged as raster rows, but each row is packed into bytes. When an image width is not divisible by eight, the final byte contains extra bits; the parser keeps only the bits needed for that row, then begins the next row at a new byte. section 12.2.4

Listing 12.29 A parser for image data in binary bitmap files
p4DataP :: Int -> Int -> Parser [RawPixel]
p4DataP width height = do
  rows <- count height (reverse <$> readRow width [])   #1
  return $ concat rows
  where
    readRow n pxs
      | n <= 0 = return pxs                 #2
      | otherwise = do
        (b7, b6, b5, b4, b3, b2, b1, b0) <- p4PixelP    #3
        case n of
          1 -> return $ b7 : pxs                                        #4
          2 -> return $ b6 : b7 : pxs                                   #4
          3 -> return $ b5 : b6 : b7 : pxs                              #4
          4 -> return $ b4 : b5 : b6 : b7 : pxs                         #4
          5 -> return $ b3 : b4 : b5 : b6 : b7 : pxs                    #4
          6 -> return $ b2 : b3 : b4 : b5 : b6 : b7 : pxs               #4
          7 -> return $ b1 : b2 : b3 : b4 : b5 : b6 : b7 : pxs          #4
          8 -> return $ b0 : b1 : b2 : b3 : b4 : b5 : b6 : b7 : pxs     #4
          _ -> #4
            readRow            #5
              (n - 8) #4
              (b0 : b1 : b2 : b3 : b4 : b5 : b6 : b7 : pxs) #4

Browse the row parser to see how image width determines which bits from a final byte become pixels and which are discarded. section 12.2.4

Bitmap parsing produces raw pixel data first. Later validation checks that the pixel count matches width times height, that pixel types agree with the PNM magic number, and that values respect the header’s limits where applicable. section 13.1.1

How Learn Haskell by Example covers it

How deep it goes
Explains direct ASCII parsing and byte-level binary unpacking, including row padding, then defers correctness checks to validation.
Examples
Demonstrates a parser that converts one Word8 into eight Single 0 or Single 1 pixels and a row parser that drops padding bits.
What it assumes
Introduces bitmap-specific parsing through PNM files while using Haskell parsers, Word8 values, and bit operations.
What you can do after
Build it from scratch by parsing ASCII bitmap values and unpacking binary PNM rows into raw pixels.
How it connects
Connects bitmap images to PNM, magic numbers, RawPixel, binary encoding, raster data, and validation.

Also explained in

Compare how 4 books explain bitmap image →

Related concepts

This explainer was generated by Manning's knowledge graph from the text of Learn Haskell by Example. The section links point to the parts of the book it draws on.