问题
I use the HTML5 canvas element to display an image in a web application and I want to flip the image horizontally without applying a scaling transformation to the canvas. This means that I do not want to use CanvasRenderingContext2D.scale() for this purpose, because I don't want to flip anything else.
// I don't want this, because it breaks the rest of the application. I have
// implemented zooming and landmark placement functionality, which no longer
// work properly after scaling.
ctx.save();
ctx.scale(-1, 1);
ctx.drawImage(image, -image.width, 0, image.width, image.height);
ctx.restore();
It seems to me that I should be able to do this with the CanvasRenderingContext2D.drawImage() method, since that page reads:
sWidth: The width of the sub-rectangle of the source image to draw into the destination context. If not specified, the entire rectangle from the coordinates specified by sx and sy to the bottom-right corner of the image is used. If you specify a negative value, the image is flipped horizontally when drawn.
This is how I draw the image:
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
var image = document.getElementById('source');
ctx.drawImage(image, 0, 0, image.width, image.height, 0, 0, image.width, image.height);
Working example here: https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/drawImage#Using_the_drawImage_method
But if I try to flip the image as per the description, I get the following error in Firefox:
ctx.drawImage(image, 0, 0, -image.width, image.height, 0, 0, image.width, image.height);
IndexSizeError: Index or size is negative or greater than the allowed amount
I don't understand what I'm doing wrong here. How can I flip an image horizontally without scaling the canvas?
回答1:
Negative region does not seem to be supported (yet?), or this line may affect how the implementation is done, ref. step 4:
The image data must be processed in the original direction, even if the dimensions given are negative.
In any case, we can't do much about it but to look at alternative ways -
This leaves you with some options though - I assume you want to avoid using save/restore, and you can -
Reset transformation
This is the fastest method, but you need to be aware of that it will reset any transformation. And this may be OK in most cases, so:
ctx.scale(-1, 1);
ctx.drawImage(image, -image.width, 0);
ctx.setTransform(1, 0, 0, 1, 0, 0);
The last call is resetting the transformation matrix using an identity matrix.
Reversing last transformation operation
If you depend on other transformations, you can simply reverse the last transformation operation. This is the second fastest option (it need to do a matrix multiplication internally):
ctx.scale(-1, 1);
ctx.drawImage(image, -image.width, 0);
ctx.scale(-1, 1); // revert scale only
Using save/restore
As you already know... but slow as it saves and restores the whole state of the canvas, not just the transformation.
Flipping manually
If there for some reason is a requirement not to use transformation at all, you can always flip it scanline by scanline. This is the second least efficient method but allows you to work without transformations, and it does allow you to do other things like displacing:
for(var x = 0; x < width; x++)
ctx.drawImage(img, x, 0, 1, height, width - x, 0, 1, height);
(width and height being the image's width and height).
Pixel manipulation
And the last, just for the record, is of course to get the pixel data and loop through, switch places etc. This is the slowest method and it depends on CORS requirement, and is not recommended for this.
回答2:
A fairly simple solution would be to use a second canvas, draw the image there flipped only once, then draw that portion of the other canvas onto your main canvas. That would successfully create a flipped version of your image, cached, and you can draw it wherever you want.
来源:https://stackoverflow.com/questions/29237305/how-to-flip-an-image-with-the-html5-canvas-without-scaling