# Varvara Graphics

The unit at gr/varvara.fs is an implementation of the screen from
[emul/varvara]. This unit stands by itself and doesn't need uxn or varvara to be
used.

While this implementation is original, the design is from Devine Lu Linvega.

## Theory of operation

A varvara screen (vscreen) is built around two pixel maps, which we call layers,
named foreground (fg) and background (bg).

On those layers, we draw pixels and sprites with a palette of 4 colors. Sprites
are squares of 8x8 pixels drawn from either a 1bpp (bits per pixel) format
called ICN or a 2bpp format called CHR.

A ICN sprite is a 8 bytes long structure where each byte represent a row in a
8x8 tile. The most significant bit is the leftmost pixel. For example, a ICN
sprite with a dot in its upper left corner and one in its lower right one would
be:

  create example 8 nc, $80 $00 $00 $00 $00 $00 $00 $01

A CHR sprite is a ICN sprite with an extra 8 bytes channel appended to it. The
first 8 bytes determines color and the other 8 bytes determine color bit 1. For
example, if we want our example above to have the top left corner on color 2 and
the top right corner on color 3, we would have:

  create example 16 nc, $00 $00 $00 $00 $00 $00 $00 $01
                        $80 $00 $00 $00 $00 $00 $00 $01

Drawing pixels is done by choosing one of the 4 colors, but drawing sprite is
done by choosing what we call a "blend". There's 16 of them and they allow us
to rearrange colors using the same data sprite. Four of those blends include
a transparency layer, which means that drawing a sprite on a layer using one of
those blends will preserve the color that was previously drawn under the
transparent pixels.

Here is the list of blends. The numbers "3/2/1/0" means that a pixel with value
"0" will be assigned the color 3, value 1, color 2, value 2, color 1 and value
3, color 0. "T" means transparent:

  0-3    T/0/1/2  0/1/2/3  0/2/3/1  0/3/1/2
  4-7    1/0/1/2  T/1/2/3  1/2/3/1  1/3/1/2
  8-11   2/0/1/2  2/1/2/3  T/2/3/1  2/3/1/2
  12-15  3/0/1/2  3/1/2/3  3/2/3/1  T/3/1/2

There's an additional transparency mechanism: color 0 on the foreground layer
is transparent. While transparent pixels in blend preserve pixels previously in
that layer, color 0 on foreground "reveals" background.

## Sprite and pixel options

[emul/varvara] has a bitfield format for specifying pixel and sprite drawing
options. We keep that format in our API but combine auto/sprite/pixel
in the same 32-bit number for PS compactness.

We also supply an API to conveniently build those options. For example, if you
want to draw a 2bpp X-flipped heart sprite with blend 7 at pos 42,54, you could
do:

  42 54 heart 7 flipX) drawsprite

The idea is that because the blend/color is the "lowest" part of the number, it
"starts" our options. Then, we can flip some more options switches with words
like "flipX".

Pixels have no blend, only a color index from 0 to 3. However, it works the same
way as blends, using the same options.

## Flipping

Sprite options include "Flip X" and "Flip Y". If they're set, the sprite will be
draw horizontally and/or vertically flipped.

## Fill

Pixel options include a "Fill" flag. When set, we don't draw a pixel anymore,
but we fill a region with the pixel position as one of the corners. Which corner
depends on the Flip options.

* No flip: fill lower right part of the screen
* Flip X: fill lower left part of the screen
* Flip Y: fill upper right part of the screen
* Flip X+Y: fill upper left part of the screen

## Auto options

The "auto" part of the options closely work together. When "Auto X" or "Auto Y"
are set, drawing position will increased by 1 for a pixel draw and 8 for a
sprite draw. If flipping is enabled, position is decreased instead.

If "Auto Addr" is set, sprite address is automatically increased by 8 or 16
(1bpp or 2bpp) bytes.

Length determines the number of times that the sprite will draw, plus 1. That
is, 0 is once, 15 is 16 times.

Addr/X/Y increases are applied each time the "Length loop" runs, but in the case
of the X/Y increase, things are counter-intuitive: The "inner Length loop"
increases the *other* dimension. For example, if "Auto X" is set, it's "Y"
that's going to be increased in the inner length loop, then once the loop is
done, Y is brought back to its previous value, and only *then*, X is increased.

It's weird, but when you think about what it enables, it's smart. The idea is to
allow to draw a large tile. For example, if you want to draw a 32x32 pixels
tile, you can arrange your sprite data accordingly, set X and Y and Addr, then
call "drawsprite" with Length=4 4 times without ever having to adjust X or Y or
Addr in the middle of your calls.

## Bitfield reference

For both pixels and sprites, "auto" is the same and lives at b15:8.

b15:12 Length
b11    unused
b10    Auto Addr
b9     Auto Y
b8     Auto X

Pixel:

b7     Fill
b6     Layer (0=bg 1=fg)
b5     Flip Y
b4     Flip X
b3     unused
b2     unused
b1:0   Color index

Sprite:

b7     2bpp (0=1bpp 1=2bpp)
b6     Layer (0=bg 1=fg)
b5     Flip Y
b4     Flip X
b3:0   Blend index

## API

### Values

fg       Points to a pixbuf representing the foreground layer
bg       Points to a pixbuf representing the background layer
vpalette Points to a palette with the 4 varvara colors set. But be in main
         screen (see [io/screen]) color encoding.
sprite   Address of the sprite to draw
X        Current draw position X
Y        Current draw position Y

### Words

flipX) ( opts -- opts )
flipY) ( opts -- opts )
fill) ( opts -- opts )
fg) ( opts -- opts )
2bpp) ( opts -- opts )
addr+) ( opts -- opts )
X+) ( opts -- opts )
Y+) ( opts -- opts )
times) ( opts n -- )
  All these words take an existing options field and activate the right bits in
  it. Negative options such as 1bpp) and bg) don't exist.

drawsprite ( opts -- )
  Draw the specified sprite to the appropriate layer.

drawpixel ( opts -- )
  Draw a pixel to the appropriate layer.

vscreen# ( -- )
  Ensures that fg/bg/vpalette are initialized. If they're not, create them with
  default values (512x320 pixels, cute colors).

refreshscreen ( -- )
  Draw bg and fg layers to "screen" [io/screen] using mapyalers and vpalette.
  We keep track of [gr/damage] to "bg" and "fg" layers and only redraw the
  damaged portions, if any.

clearlayers ( -- )
  Fill fg and bg layers with 0.

resizelayers ( width height -- )
  Resize fg and bg layers to specified width and height. If the new height
  doesn't fit the previous bufsz [gr/pix], reallocate new pixbufs entirely,
  wasting system memory.
