[whatwg] Canvas: need getTransform() and getInverseTransform() methods
David Flanagan
david at davidflanagan.com
Wed Jul 28 10:27:16 PDT 2010
The Canvas API has a setTransform() method to set an arbitrary
transformation matrix, but has no corresponding getTransform() method to
query the current transformation matrix or even to use the CTM to
transform a point in the current coordinate system to the default
coordinate system. The only way current to achieve this is to replace
translate(), scale(), and rotate() methods with instrumented versions
that explicitly keep track of the CTM.
Here are the two use cases that I've run into in which I've needed a way
to convert from the current coordinates to the device coordinates.
1) The shadowOffsetX,Y properties are not subject to the CTM (except in
Chrome, which gets it wrong). This is probably as it should be. But
suppose I've written a method draw_scene() that draws a pretty picture
with shadows. Now suppose I want to render my scene at high-resolution
to produce a version suitable for printing with code like this, for example:
var bigcanvas = document.createElement("canvas");
bigcanvas.width = 5*canvas.width;
bigcanvas.height = 5*canvas.height;
var context = bigcanvas.getContext("2d");
context.scale(5,5);
draw_scene(context);
var img = bigcanvas.toDataURL();
window.open(img).print();
In this scenario, I naturally want my shadows to scale along with the
rest of the picture, so that my high-resolution printable version of the
scene looks just like the on-screen version.
In order to make this work right, I need some way to transform shadow
offsets in the current coordinate system into shadow offsets in the
default coordinate system. In my draw_scene() method, for example, I
need to be able to write code like this:
// Set shadow offsets to 5 units in the the current coordinates
var offset0 = c.transformPoint(0,0);
var offset1 = c.transformPoint(5,5);
c.shadowOffsetX = offset1[0]-offset0[0];
c.shadowOffsetY = offset1[1]-offset0[1];
(That code is pretty ugly, but I think it accomplishes what I need.
Better might be a tranformDimension() method or tranformPoints() that
can take any number of pairs of x,y coordinates).
2) The second use case I've encountered is when I want to perform some
kind of drawing operation on a portion of the canvas but first want to
make a backup copy so I can restore the dirty rectangle later. To do
this, I obviously use drawImage() or getImageData() to extract the
pixels I want to save. But both of these methods expect the coordinates
of the source rectangle in the default coordinate system. If I don't
have a way to convert from the current coordinates back to default
coordinates then I end up having to back up the entire canvas rather
than just a rectangular portion of it. (As an aside a
getPathBoundingBox() method would be nice for this scenario, too)
In addition to the need to be able to transform points and dimensions
from the current coordinate system to the default coordinate system,
there is also a related need to be able to transform in the opposite
direction. The use case I've thought about is when you want to be able
to determine the coordinates (in the current system) of the corners of
the canvas. That is, I'd like to be able to transform canvas.width and
canvas.height to the current coordinates.
From an ease-of-specification perspective, the easiest way to enable
these things would be to add getTransform() and getInverseTransform()
methods that would return arrays of the 6 matrix elements.
This would be a no-op:
CanvasRenderingContext2D.prototype.setTransform.apply(c,c.getTransform())
And this would be the same as calling setTransform(1,0,0,1,0,0):
CanvasRenderingContext2D.prototype.transform.apply(c,c.getInverseTransform())
getInverseTransform() would throw a suitable exception if the CTM was
not invertible.
From a programmer usability perspective, perhaps adding methods like
transformPoints(), transformBoundingBox(), and transformDimensions()
would be more helpful. But I'm not sure what the optimal set of such
methods would be.
David Flanagan
More information about the whatwg
mailing list