All rendering is driven by the core. The core understands the PCB stackup, layers, layer groups, compositions within the layer groups, extra labels, layer visibility, etc. The HID is a slave device that takes drawing instructions and blindly executes them.
However, the HID has a chance to alter which layer groups are drawn, using the set_layer_group hooks return value.
The rendering is done in bursts. A burst may redraw only a small window of the screen. Each bursts draws multiple layer groups, in Z-order, from bottom (furthest from the eye of the user) to up (closest to the eye of the user). Each layer group is drawn on a transparent sketch canvas. In positive mode, objects are drawn with multiple colors on each sketch canvas. In negative mode pixels are erased from the sketch canvas, making the affected pixels transparent again. Drawing on a layer group (either positively or negatively) should overwrite pixels on the same layer group. In other words, there's no translucency between two overlapping objects drawn on the same layer group.
There is a so called output canvas, which shall be cleared to the background color upon render_burst / start. The background color shall be fetched from the config tree (appearance/color/background).
The following steps are taken by the core in a burst:
When set_drawing_mode / reset is called, the HID needs to create a new sketch canvas, all pixels initialized to transparent. The core may switch between positive and negative draw any time. When set_drawing_mode / flush is called, the current sketch canvas needs to be blitted onto the output (in a HID-specific way) and the sketch canvas can be destroyed. The output canvas shall be flushed to the screen upon render_burst / end.
Optional optimization: the most common case is when only positively drawn layers are used to draw the final output. Drawing on separate sketch canvases and blitting them onto an output buffer is a waste in this setup. To give HIDs a chance to avoid the extra copies, set_drawing_mode calls have an argument called direct. When the direct is set, it is safe to directly draw on the output canvas, without allocating a sketch canvas. The core guarantees the followings:
It is possible that the core would draw the whole board in direct mode. It is safe to ignore the direct argument and always use the more expensive, sketch-canvas+blitting method.
The API contains Graphic Context, or GC for short. A GC is a pen with properties like color and tip shape. The core will initialize multiple GCs for the drawing and will pass a GC pointer with each drawing command.
Note: this concept is orthogonal to the above canvas based drawing. Regardless of which GC is used, the object being drawn always ends up making modifications to the currently active (sketch) canvas.
A GC can be initialized only after render_burst / start and all GCs will be uninitialized before render_burst / end.
In negative drawing mode, drawing an object always clear on the current canvas, regardless of the GC color.
In positive drawing mode, objects are drawn with whatever color the current GC has. The special color "drill" is used to draw holes that go thru all layers of the board. It is possible that a drill is drawn over existing objects and new objects are drawn on top of a drilled hole. To get the proper order, the HID shall set the .holes_after to 1.
(Note: there's no "erase" color anymore.)
When switching over an existing HID from the old use_mask() API to the new composite API:
XOR draw is used as a mean to make an object always visible, no matter over what background it is drawns. It is not used as a cheap draw/erase mechanism, so HIDs are free to implement it with a different algorithm - as long as it is guaranteed that objects drawn on the same colored background will stick out.
There are two places XOR draw takes place:
In both cases the drawing mode is set to PCB_HID_COMP_POSITIVE_XOR, which does not clear the direct flag. The GC that is used for drawing the actual objects also has its xor mode set.