# 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/varvara] 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.

## Invalidate

Whenever the buffer is changed, its "invalidate ( pixbuf -- )" method is called
one all drawing is finished. It allows for all kinds of little tricks such as
updating [gr/damage] struct or implementing proxy schemes.

## Map words

Words being passed to "mappixels" have a very specific API. These words need to
be fast, which is why the API is built that way. The base signature of a map
word is:

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

The mapping word is responsible for doing any read operation it needs to do,
incrementing srcaddr in an appropriate manner.

For the write part, unless the mapping word is really special, it will want to
jump to either "pixwr)" or "pixskip)", which are sysvars' HAL accessors
(see [doc/data]) that dynamically point to words with semantics described below.
They are set up by "mappixels" depending on destination pixel size and flip
options.

For a simple mapping word, this is enough. For example, here's a noop mapping
word for a source with 8-bit pixels that skips when the pixel is $ff:

  code mynoop W) 8b) S>) @+, $ff i) S>) compare, pixskip) Z) brc, pixwr) br,

During the whole mapping operation, many variables are put onto PS. For simple
mapping words, you don't need to know about them, but for more complex ones, or
if you want to access your own variables, this knowledge is needed. The actual
full signature of the mapping word is:

  ( hcnt w wcnt srcoff dstpitch dstoff dstpixw srcaddr )

With W=srcaddr, this means PSP+0=dstpixw and PSP+24=hcnt. If you placed
something on PS before calling "mappixels", you could access it with PSP+24.

  A   dstaddr  Address where we write pixels
  W   srcaddr  Address where we read pixels
  +0  dstpixw  Width in bytes of each pixel in dst
  +4  dstoff   What we add to "dstaddr" after we're done drawing a line
  +8  dstpitch Size in bytes of a line in dst
  +12 srcoff   What we add to "srcaddr" after we're done drawing a line
  +16 wcnt     Width counter. Decremented by 1 after a pixel draw
  +20 w        wcnt is reset to this at the beginning of a line
  +24 hcnt     Height counter. Decremented by 1 after a line draw

pixwr) ( A=dst S=pixel -- A=dst+ )
  Write a pixel in the context of a "mappixels" operation. In your map word, you
  will most likely want to jump to this word once your pixel is ready in S.

  Unless flipX is active, this word uses [hal/bwr] for speed.

pixskip) ( A=dst -- A=dst+ )
  In the context of a "mappixels", don't write any pixel and skip this address.

## API

### Struct

struct Pixbuf
  fields buffer width height pixsz rowsz bufsz
  method invalidate ( pixbuf -- )
endstruct

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
bufsz     Size in bytes of the allocated buffer

invalidate ( pixbuf -- )
  Called after a buffer-mutating operation is done. At that point, all src/dst
  values are still valid and can be used to determine the scope of the
  operation. Defaults to "drop".

### 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 ( pixsz width height -- pixbuf )
  Allocate a new pixbuf with the specified field values. The "buffer" fields
  is initialized to 0 and must be either allocated to assigned to something
  before the pixbuf can be used.

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.

pixsrcdst! ( x y w h src dst -- )
  A shortcut for calling "pixsrc!" and "pixdst!" using the same "x" and "y"
  values.

drawnrect ( -- x y w h )
  Shortcut for typing "dstX dstY srcW srcH". Goes well with [gr/damage].

?resize ( width height pixbuf -- f )
  Change the "width" and "height" fields of "pixbuf". No buffer reallocation
  take place, which means that the new width and height must fit in the
  previously existing buffer. If it does, proceed and yield f=1. Otherwise,
  don't resize and yield f=0.

  Geometry change invalidates the content. Whatever was in there previously
  becomes garbage after a resize.

resize# ( width height pixbuf -- )
  Try to resize and abort with error message if we can't.

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. See "Map words" above.

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

flipX? ( -- f )
flipY? ( -- f )
  Yield the current associated flag.

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