如何在不缩放的情况下使用 HTML5 canvas 翻转图像

How to flip an image with the HTML5 canvas without scaling

我使用 HTML5 canvas 元素在 Web 应用程序中显示图像,我想水平翻转图像 而不 应用缩放变换到 canvas。这意味着我不想为此目的使用 CanvasRenderingContext2D.scale(),因为我不想翻转其他任何东西。

// 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();

在我看来,我应该能够使用 CanvasRenderingContext2D.drawImage() 方法来完成此操作,因为该页面显示:

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.

我是这样画的:

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);

这里的工作示例:https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/drawImage#Using_the_drawImage_method

但是如果我尝试按照描述翻转图像,我在 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

我不明白我在这里做错了什么。如何在不缩放的情况下水平翻转图像 canvas?

一个相当简单的解决方案是使用第二个 canvas,在那里绘制仅翻转一次的图像,然后将另一个 canvas 的那部分绘制到主 canvas 上。这将成功创建图像的翻转版本,缓存,你可以在任何你想画的地方画它。[=​​10=]

似乎不​​支持负区域(还?),或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.

无论如何,我们对此无能为力,只能寻找替代方法 -

虽然这给您留下了一些选择 - 我假设您想避免使用 save/restore,并且您可以 -

重置转换

这是最快的方法,但您需要注意它会重置任何转换。在大多数情况下这可能没问题,所以:

ctx.scale(-1, 1);
ctx.drawImage(image, -image.width, 0);
ctx.setTransform(1, 0, 0, 1, 0, 0);

最后一次调用是使用单位矩阵重置变换矩阵。

反转上次变换操作

如果依赖其他变换,只需将上次的变换操作逆向即可。这是第二快的选项(它需要在内部进行矩阵乘法):

ctx.scale(-1, 1);
ctx.drawImage(image, -image.width, 0);
ctx.scale(-1, 1);   // revert scale only

使用save/restore

如您所知...但速度较慢,因为它保存和恢复 canvas 的整个状态,而不仅仅是转换。

手动翻转

如果出于某种原因要求根本不使用转换,您可以随时将其逐条扫描线翻转。这是第二个效率最低的方法,但允许您在没有转换的情况下工作,并且它确实允许您做其他事情,例如置换:

for(var x = 0; x < width; x++)
    ctx.drawImage(img, x, 0, 1, height, width - x, 0, 1, height);

(宽度和高度是图像的宽度和高度)。

像素操作

最后,记录一下,当然是获取像素数据并循环,切换位置等。这是最慢的方法,它取决于CORS要求,不推荐这样做。