TAOCP 7.1.3 Exercise 188

The failure in the previous solution is fundamental: the bitmap is 1-bit packed, so each pixel must be extracted by bit operations, not by byte-wise `LDB` interpretation.

Section 7.1.3: Bitwise Tricks and Techniques

Exercise 188. ▶ [25] [25] Bitmap images can often be viewed conveniently using pixels that are shades of gray instead of just black or white. Such gray levels typically are 8-bit values that range from 0 (black) to 255 (white); notice that the black/white convention is tradition- ally reversed with respect to the 1-bit case. An m × n bitmap whose resolution is 600 dots per inch corresponds nicely to the (m/8) × (n/8) grayscale image with 75 pixels per inch that is obtained by mapping each 8 × 8 subarray of 1-bit pixels into the gray level ⌊255(1 −k/64)1/γ + 1 2⌋, where γ = 1.3 and k is the number of 1s in the subarray. Write an MMIX routine that converts a given m × n array BITMAP into the corre- sponding (m/8) × (n/8) image GRAYMAP, assuming that m = 8m′ and n = 64n′.

Verified: no
Solve time: 4m29s


The failure in the previous solution is fundamental: the bitmap is 1-bit packed, so each pixel must be extracted by bit operations, not by byte-wise LDB interpretation. The corrected solution restarts the computation of $k(i,j)$ from the actual storage model.

1. Correct bitmap representation

The bitmap is an $m \times n$ array of bits, stored in row-major order, packed 8 bits per byte.

Thus each row contains $n/8$ bytes.

Given:

$$ m = 8m', \quad n = 64n' $$

so:

$$ \frac{n}{8} = 8n' $$

Let:

$$ N_B = \frac{n}{8} $$

Byte addressing

For pixel $(x,y)$:

  • byte index in row: $y \div 8$
  • row byte offset: $x \cdot N_B$

So byte address is:

$$ \text{BITMAP} + x \cdot N_B + \left\lfloor \frac{y}{8} \right\rfloor $$

Within that byte, the bit position is:

$$ b = y \bmod 8 $$

We use the standard convention:

  • bit 7 is the leftmost pixel in the byte
  • bit 0 is the rightmost pixel

Thus the pixel value is:

\text{BIT}(x,y) = \left( \frac{\text{byte} \gg (7-b)} \right) \bmod 2

2. Block structure

Each output pixel corresponds to an $8 \times 8$ block:

$$ x = 8i + a,\quad y = 8j + b $$

with:

$$ 0 \le a,b \le 7 $$

The block sum is:

$$ k(i,j)=\sum_{a=0}^{7}\sum_{b=0}^{7} \text{BIT}(8i+a,,8j+b) $$

3. Gray value mapping

Precomputed table:

$$ T[k] = \left\lfloor 255\left(1-\frac{k}{64}\right)^{1/\gamma} + \frac12 \right\rfloor,\quad \gamma = 1.3 $$

4. MMIX implementation

Registers

  • $0 return
  • $1 i
  • $2 j
  • $3 a
  • $4 b
  • $5 k
  • $6 BITMAP base
  • $7 GRAYMAP base
  • $8 T base
  • $9 temp byte
  • $10 address / temp
  • $11 N_B = n/8
  • $12 shift amount
  • $13 gray value

5. Correct MMIX routine

GrayMap  PUSHJ 0,InitT

         SET   1,0              % i = 0

I_loop   CMP   $255,1,m'
         BZ    $255,Done

         SET   2,0              % j = 0

J_loop   CMP   $255,2,NG
         BZ    $255,NextI

         SET   5,0              % k = 0
         SET   3,0              % a = 0

A_loop   CMP   $255,3,8
         BZ    $255,BlockDone

         SET   4,0              % b = 0

B_loop   CMP   $255,4,8
         BZ    $255,NextA

         % --, compute BITMAP byte address ----
         % (8i+a)*N_B + j

         MUL   $10,1,8
         ADD   $10,$10,3
         MUL   $10,$10,$11
         ADD   $10,$10,2
         ADD   $10,$10,$6

         % load packed byte
         LDB   $9,$10,0

         % --, extract bit b ----
         % shift = 7 - b
         SET   $12,7
         SUB   $12,$12,4
         SR    $9,$9,$12
         AND   $9,$9,1

         ADD   $5,$5,$9

         INC   4,1
         JMP   B_loop

NextA    INC   3,1
         JMP   A_loop

BlockDone
         % lookup T[k]
         ADD   $10,$8,$5
         LDB   $13,$10,0

         % store GRAYMAP[i,j]
         MUL   $10,1,NG
         ADD   $10,$10,2
         ADD   $10,$10,$7
         STB   $13,$10,0

         INC   2,1
         JMP   J_loop

NextI    INC   1,1
         JMP   I_loop

Done     POP   0,0
         GO    $0,$0,0

6. Correctness argument

(1) Correct pixel extraction

Each bitmap pixel is stored as a single bit inside a packed byte. The routine:

  • selects the correct byte using

$$ x \cdot (n/8) + \lfloor y/8 \rfloor $$

  • extracts bit $b = y \bmod 8$
  • isolates it via shift and mask

Thus each $\text{BIT}(x,y)$ is computed exactly once and correctly.

(2) Correct block aggregation

The nested loops over $a,b \in {0,\dots,7}$ enumerate all pixels in the $8\times 8$ block corresponding to $(i,j)$. Therefore:

$$ k(i,j) = \sum_{a,b} \text{BIT}(8i+a,8j+b) $$

is computed exactly.

(3) Correct output layout

Each block produces one grayscale byte stored at:

$$ \text{GRAYMAP}[i,j], \quad 0 \le j < n/8 $$

with linearization:

$$ i \cdot (n/8) + j $$

which matches the required $(m/8) \times (n/8)$ image layout.

(4) Correct gray-level mapping

The lookup table implements:

$$ \left\lfloor 255\left(1-\frac{k}{64}\right)^{1/\gamma} + \frac12 \right\rfloor $$

so the nonlinear perceptual correction is applied exactly once per block.

Conclusion

The corrected program fixes the critical defect by replacing byte-wise pixel access with proper bit extraction from packed bitmap storage. All subsequent computations, block aggregation, and output indexing are consistent with TAOCP conventions.