# Reading and writing pixels individually

The unit at [gr/rdwr.fs] is about reading and writing pixels from and to Pixbufs
[gr/buf] as fast as possible, despite the sad fact that doing so is inherently
slow. However, it's also necessary to have this capability as soon as we're
dealing with unaligned pixel operations.

Although ultimately, this unit supplies the words "pixel@" and "pixel!", you'll
seldom use them outside temporary and debugging purposes because those accesses
are as slow as one can get.

## Compiler API for sub-8bit encoding

Reading and writing pixels of sub-8bit encoding involves bit fiddling. Of
course, pixel@ and pixel! do that bit fiddling, but you might also want to
include that bit fiddling in fast HAL routines, which is why this unit provides
a compiler API for that fiddling.

The broad idea of these routines is that we have in-register buffers from which
we serially read and to which we serially write. For example, in a 4BPPL depth,
each buffer has 8 pixels in it. We read from the "low bits" end of the buffer,
shifting right and we write to the "high bits" end of the other buffer, again
shifting right. On a 4BPPH depth, we read from high bits, shifting left and we
write to low bits, shifting left.

The name of those routines include the BPP (1/2/4) as well as "pixel endianness"
flag (l or h), which is described in [gr/buf]. For example, "rd2l," is the
routine you'll want to use on a pixbuf with the 2BPPL encoding.

These routines only clobber registers they document as clobbered. Other
registers are preserved, including those from [hal/vreg].

rd1l, rd2l, rd4l, rd1h, rd2h, rd4h, ( -- )
  IN:  A=Input buffer
  OUT: W=color
       A=Modified input buffer

  Read a single color from A, writing the result in W. A is shifted so that
  calling this routine again will read the next pixel.

?skip1l, ?skip2l, ?skip4l, ?skip1h, ?skip2h, ?skip4h, ( -- )
  IN:  A=Input buffer
       R1=Index or X position
  OUT: A=Modified input buffer
       R1=Index

  When we begin reading a bunch of pixels, it's possible that we start from a
  position other than the leftmost pixel of the buffer. This is what this
  routine is for, shifting the buffer so that the next rdXXX, is the right one.

  R1 doesn't have to be "modded" into an index. This routine does it and yields
  a R1 that is a modulo of X and the routine width.

wr1l, wr2l, wr4l, wr1h, wr2h, wr4h, ( -- )
  IN:  S=Output buffer
       W=color
  OUT: S=Modified output buffer
       W=clobbered

  Write the color in W to Output buffer S. W is expected to have all its bits
  exceeding the width of the routine to be zeroed out.

mix1l, mix2l, mix4l, mix1h, mix2h, mix4h, ( -- )
  IN:  S=Output buffer (left part)
       W=Destination buffer (right part)
       R1=Nonzero bit index
  OUT: S=Modified output buffer
       W=clobbered
       R1=clobbered

  This routine is designed to be placed at the end of a loop writing pixels to
  the S buffer, checking at each iteration whether the S buffer is full (R1
  modulo width is 0) and if it is, write the buffer to memory and reinitialize
  S to 0.

  At the end of that loop, it's possible that we have a non-empty buffer (R1
  modulo width is nonzero). This routine mixes the partial S buffer with the
  pixels in W. At that point, S is ready to write to memory. It's designed to be
  placed in a R1 modulo check like this:

    7 i) R1>) &, ifnz, A) @, mix4l, A) S>) !, then

  The junction point for the mix is R1, with S containing its left part, and
  the W containing the right part.

  For example, with a 4BPPL depth, we have 8 pixels in the buffer. If R1 is,
  let's say 3, it means that we have 3 pixels in the S register and 5 pixels in
  the W register. This means that we apply a $fffff000 mask to W and OR S in it
  (we assume that S has a value that fits in $fff, because why wouldn't it?).

?trim1l, ?trim2l, ?trim4l, ?trim1h, ?trim2h, ?trim4h, ( addrop -- )
  IN:  R1=X position
  OUT: S=Trimmed output buffer
       R1=Index

  When we begin writing an output in the middle of a pixel group, we need to
  begin with partial data in memory, that is the left part of it, but we also
  need the right part to be zeroed out for new data to be ORed in with further
  wr1/2/4, runs. This is what this routine is about.

  For example, with a 4BPPL depth, we have 8 pixels in the buffer. If R1 is,
  let's say 3, it means that we want to keep the 3 low (leftmost) pixels. We
  therefore apply a $fff mask to S.

  The code generated by this routine first transforms a X position into an
  index. If the result is 0 (right at the beginning of the buffer), set S to 0.
  Otherwise, read existing buffer from "addrop" supplied at compile time, then
  trim its "right" part.

## API

pixel@ ( x y pixbuf -- color )
  Yields color at x/y. Abort when out of bounds.

pixel! ( color x y pixbuf -- )
  Set color at x/y. Abort when out of bounds.
