- From: David Flanagan <david@davidflanagan.com>
- Date: Wed, 28 Jul 2010 10:27:16 -0700
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
Received on Wednesday, 28 July 2010 10:27:16 UTC