# Shove pixels around with pixel buffers

The unit at gr/pix.fs defines the "pixel buffer" (pixbuf) struct which is used
to shove pixels around. To this unit, the concept of pixel is immaterial. It's
just a unit composed of a series of 1, 2, 3 or 4 bytes chunks aligned as a grid
where rows are horizontal and columns are vertical. The buffer memory is
organized as a sequence of rows which each row being a sequence of pixels.

Other units such as [io/screen] specify the meaning of "pixel" as a [gr/color],
but [gr/pmap] defines pixel as an index in a palette. In gr/pix, it has no
meaning.

The thing we need to do here in gr/pix is to locate pixels through X/Y
coordinates and be able to deal with sub-regions of the pixbuf. This requires us
to know about its geometry. Therefore, we hold a pointer to the buffer, of
course, as well as width and height, in pixels.

We also need to know the width in byte of each pixel, but also the size in byte
of each line, because it's not always width*pixwidth. Sometimes there's
alignment padding between the lines.

But once we have that, we're good to go and can move pixels around.

## Move, Map and Fill

The three operations supplied by this unit are Move, Map and Fill.

Move copies pixels verbatim from a source to a destination. Map runs an
arbitrary word on each pixel read from source before writing it to destination.
Fill writes an arbitrary value, always the same, to destination.

Move is the same as running Map with a noop word, except it's significantly
faster.

## Source and destination

Before running an operation, one must set the destination and, except for Fill,
the source. Both must be a pixbuf.

You set them with "pixsrc!" and "pixdst!" and then you can call your operation
word. If you have multiple operations being done on the same source or
destination, you can omit the "pixsrc!" and/or "pixdst!" call in between those
operations. Moreover, you can manipulate source/dest variables such as "srcX" to
conveniently move around coordinates between your operations.

Source can be targeted whole, but also partially. The variables "srcW" and
"srcH" will be the same as the pixbuf width and height when we target the whole
of it, but it can be smaller if we want to read a smaller part of it.

## Bounds check

All operations are checked for destination bounds. If the width/height of the
operation doesn't fit destination, the part of the source being read is cropped.

If dstX and/or dstY are negative, they are brought to 0, with srcX/srcY/srcW and
srcY adjusted accordingly, that is, adjusted so that the part of the source
being part of the negative coordinates is cropped.

These adjustments are made to srcX, srcY, srcW and srcH. srcW and srcH can only
end up being 0, never negative.

srcX/srcY/srcW/srcH should never be set as negative or yielding pixels that are
out of bounds of the source pixmap. Bound checks on that front are not made.

If that reduction brings srcW and/or srcH to 0, the operation is a noop.

## Damage

If "damageptr" is nonzero, every operation word will update the damage struct
afterwards. See [gr/damage] for details.

## API

### Fields

buffer    Points to the first byte of the pixbuf
width     Width in pixels of the pixbuf
height    Height in pixels of the pixbuf
pixsz     Size in bytes of a pixel
rowsz     Size in bytes of a row
damageptr Pointer to gr/damage struct. Can be 0.

### Constants

PIXBUFSZ  Size in bytes of a pixbuf struct

### Values

src       Points to the source pixbuf
srcX      Source X position
srcY      Source Y position
srcW      Width in pixels of the region in source being read
srcH      Height in pixels of the region in source being read
dst       Points to the destination pixbuf
dstX      Destination X position
dstY      Destination Y position

### Words

newpixbuf ( pixwidth width height buffer -- pixbuf )
  Allocate a new pixbuf with the specified field values.

allotbuf ( pixbuf -- )
  Allocate a new buffer of the proper size (considering height and pixsz) and
  set "buffer" to it.

pixsrc! ( x y w h pixbuf -- )
  Set the source for the next operation.

pixsrcwhole! ( pixbuf -- )
  Shortcut for calling "pixsrc!" targeting the whole of "pixbuf".

pixdst! ( x y pixbuf -- )
  Set the destination for the next operation.

fillpixels ( pixel width height -- )
  Perform a Fill operation in destination of width/height pixels, using value
  "pixel".

movepixels ( -- )
  Perform a Move operation.

mappixels ( w -- )
  Perform a Map operation, using "w" as the mapped word. That word is expected
  to have the signature:

    ( srcaddr+ A=dstaddr S=pixel -- srcaddr+ A=dstaddr S=pixel )

  Yes, that's a weird signature, and it gets weirder: W and A must be preserved.
  These constraints are for speed purposes. If your processing is simple and can
  operate directly on the S register, good for you! processing will be fast.
  Otherwise, save W and/or A somewhere and do your stuff.

  "dstaddr" is the address that "pixel" is about to be written at. "srcaddr+"
  is the address, already post-increased, from where "pixel" was read.

  Then, once "w" return, we write that pixel to destination. src and dst can
  have a different pixwidth.

  If the resulting pixel is -1, nothing is written and the destination pixel is
  skipped.

  We could say that "movepixels" is the same as calling mappixels with "noop",
  but faster.

flipX! ( -- )
  The next mappixels operation will flip its pixels horizontally.

flipY! ( -- )
  The next mappixels operation will flip its pixels vertically.

pixel! ( pixel pixbuf -- )
  Set a pixel at current dst position value of "pixel".

