Instead of having two virtualizations of the canvas we push the
virtualization into the canvas code itself. This not only avoids
the duplication of this code, it also makes the exposed API for the
canvas much smaller (in terms of exported API).
It also lets us use the virtualization to implement basic support
for operations in canvas_base which is then overridden by each canvas
implementation.
Instead of passing a bunch of function pointer and an opaque
pointer we make a real type and add a vtable pointer to it.
This means we can simplify all the canvas constructors, etc.