Javascript HTML5 Canvas drawImage ratio bug iOS

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/11929099/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-24 07:43:25  来源:igfitidea点击:

HTML5 Canvas drawImage ratio bug iOS

javascriptioshtmlcanvas

提问by Brice Lechatellier

I want to resize the image taken from the iOS camera on the client side with HTML5 Canvas but I keep running in this weird bug where the image has a wrong ratio if bigger than ~1.5mb

我想使用 HTML5 Canvas 在客户端调整从 iOS 相机拍摄的图像的大小,但我一直在这个奇怪的错误中运行,如果图像大于 ~1.5mb,则图像比例错误

It works on the desktop but not in the latest iOS version with the media upload API.

它适用于桌面,但不适用于带有媒体上传 API 的最新 iOS 版本。

You can see an example here: http://jsbin.com/ekuros/1

你可以在这里看到一个例子:http: //jsbin.com/ekuros/1

Any idea how to fix this please? Is this a memory issue?

知道如何解决这个问题吗?这是内存问题吗?

$('#file').on('change', function (e) {
    var file = e.currentTarget.files[0];
    var reader = new FileReader();
    reader.onload = function (e) {
        var image = $('<img/>');
        image.on('load', function () {
            var square = 320;
            var canvas = document.createElement('canvas');

            canvas.width = square;
            canvas.height = square;

            var context = canvas.getContext('2d');
            context.clearRect(0, 0, square, square);
            var imageWidth;
            var imageHeight;
            var offsetX = 0;
            var offsetY = 0;

            if (this.width > this.height) {
                imageWidth = Math.round(square * this.width / this.height);
                imageHeight = square;
                offsetX = - Math.round((imageWidth - square) / 2);
            } else {
                imageHeight = Math.round(square * this.height / this.width);
                imageWidth = square;    
                offsetY = - Math.round((imageHeight - square) / 2);            
            }

            context.drawImage(this, offsetX, offsetY, imageWidth, imageHeight);
            var data = canvas.toDataURL('image/jpeg');

            var thumb = $('<img/>');
            thumb.attr('src', data);
            $('body').append(thumb);
        });
        image.attr('src', e.target.result);
    };
    reader.readAsDataURL(file);
});

采纳答案by Sebastian Tschan

There is a JavaScript canvas resize library which works around the subsampling and vertical squash issues encountered when drawing scaled images on canvas on iOS devices: http://github.com/stomita/ios-imagefile-megapixel

有一个 JavaScript 画布大小调整库可以解决在 iOS 设备上的画布上绘制缩放图像时遇到的二次采样和垂直挤压问题:http: //github.com/stomita/ios-imagefile-million

There are side issues when scaling images with alpha channel (as it uses the alpha channel for the issues detection) and when trying to resize existing canvas elements, however it's the first solution I've found that actually works with the issue at hand.

使用 alpha 通道缩放图像(因为它使用 alpha 通道进行问题检测)以及尝试调整现有画布元素的大小时,存在一些附带问题,但这是我发现的第一个实际解决手头问题的解决方案。

stomita is also a StackOverflow user and posted his solution here: https://stackoverflow.com/a/12615436/644048

stomita 也是 StackOverflow 用户,并在此处发布了他的解决方案:https://stackoverflow.com/a/12615436/644048

回答by matt burns

If you still need to use the long version of the drawImage function you can change this:

如果你仍然需要使用 drawImage 函数的长版本,你可以改变这个:

context.drawImage(img, sx, sy, sw, sh, dx, dy, dw, dh);

to this:

对此:

drawImageIOSFix(context, img, sx, sy, sw, sh, dx, dy, dw, dh);

You just need to include these two functions somewhere:

你只需要在某处包含这两个函数:

/**
 * Detecting vertical squash in loaded image.
 * Fixes a bug which squash image vertically while drawing into canvas for some images.
 * This is a bug in iOS6 devices. This function from https://github.com/stomita/ios-imagefile-megapixel
 * 
 */
function detectVerticalSquash(img) {
    var iw = img.naturalWidth, ih = img.naturalHeight;
    var canvas = document.createElement('canvas');
    canvas.width = 1;
    canvas.height = ih;
    var ctx = canvas.getContext('2d');
    ctx.drawImage(img, 0, 0);
    var data = ctx.getImageData(0, 0, 1, ih).data;
    // search image edge pixel position in case it is squashed vertically.
    var sy = 0;
    var ey = ih;
    var py = ih;
    while (py > sy) {
        var alpha = data[(py - 1) * 4 + 3];
        if (alpha === 0) {
            ey = py;
        } else {
            sy = py;
        }
        py = (ey + sy) >> 1;
    }
    var ratio = (py / ih);
    return (ratio===0)?1:ratio;
}

/**
 * A replacement for context.drawImage
 * (args are for source and destination).
 */
function drawImageIOSFix(ctx, img, sx, sy, sw, sh, dx, dy, dw, dh) {
    var vertSquashRatio = detectVerticalSquash(img);
 // Works only if whole image is displayed:
 // ctx.drawImage(img, sx, sy, sw, sh, dx, dy, dw, dh / vertSquashRatio);
 // The following works correct also when only a part of the image is displayed:
    ctx.drawImage(img, sx * vertSquashRatio, sy * vertSquashRatio, 
                       sw * vertSquashRatio, sh * vertSquashRatio, 
                       dx, dy, dw, dh );
}

This will work fine whether it is run on iOS or other platforms.

无论它是在 iOS 还是其他平台上运行,这都可以正常工作。

This is based on the great work by stomitaand you should credit him in your work.

这是基于stomita的伟大工作,你应该在你的工作中归功于他。

回答by Torchify

It looks like this is an iOS 6 bug. There is no reason for the aspect to get out of whack from your code. I have the same problem which was only introduced in iOS 6. It seems that their sub-sampling routine gives the wrong height. I submitted a bug report to Apple, and you should do the same. The more bug reports they get for this the better.

看起来这是一个 iOS 6 错误。方面没有理由从您的代码中脱离出来。我有同样的问题,只在 iOS 6 中引入。似乎他们的子采样例程给出了错误的高度。我向 Apple 提交了错误报告,您也应该这样做。他们为此获得的错误报告越多越好。

回答by Paul

I've experienced the same problem. It seems that this is an iOS limitation, jpg over 2 megapixel are subsampled.

我也遇到过同样的问题。似乎这是 iOS 的限制,对超过 2 兆像素的 jpg 进行二次采样。

See Creating Compatible Web Content for Safari on IPhone

请参阅为 iPhone 上的 Safari 创建兼容的 Web 内容

回答by Agamemnus

A modified version of the above code.

上述代码的修改版本。

Edit: saw L0LN1NJ4's code at http://jsfiddle.net/gWY2a/24/.. guess that one's a bit better...

编辑:在http://jsfiddle.net/gWY2a/24/看到了 L0LN1NJ4 的代码......

function drawImageIOSFix (ctx, img) {
 var vertSquashRatio = detectVerticalSquash (img)
 var arg_count = arguments.length
 switch (arg_count) {
  case 4  : ctx.drawImage (img, arguments[2], arguments[3] / vertSquashRatio); break
  case 6  : ctx.drawImage (img, arguments[2], arguments[3], arguments[4], arguments[5] / vertSquashRatio); break
  case 8  : ctx.drawImage (img, arguments[2], arguments[3], arguments[4], arguments[5], arguments[6], arguments[7] / vertSquashRatio); break
  case 10 : ctx.drawImage (img, arguments[2], arguments[3], arguments[4], arguments[5], arguments[6], arguments[7], arguments[8], arguments[9] / vertSquashRatio); break
 }

 // Detects vertical squash in loaded image.
 // Fixes a bug which squash image vertically while drawing into canvas for some images.
 // This is a bug in iOS6 (and IOS7) devices. This function from https://github.com/stomita/ios-imagefile-megapixel
 function detectVerticalSquash (img) {
  var iw = img.naturalWidth, ih = img.naturalHeight
  var canvas = document.createElement ("canvas")
  canvas.width  = 1
  canvas.height = ih
  var ctx = canvas.getContext('2d')
  ctx.drawImage (img, 0, 0)
  var data = ctx.getImageData(0, 0, 1, ih).data
  // search image edge pixel position in case it is squashed vertically.
  var sy = 0, ey = ih, py = ih
  while (py > sy) {
   var alpha = data[(py - 1) * 4 + 3]
   if (alpha === 0) {ey = py} else {sy = py}
   py = (ey + sy) >> 1
  }
  var ratio = (py / ih)
  return (ratio === 0) ? 1 : ratio
 }
}