55 votes

HTML5 Canvas drawImage ratio bug iOS

Je veux redimensionner l'image prise par l'appareil photo d'iOS du côté client avec HTML5 Canvas mais je continue à rencontrer ce bug bizarre où l'image a un mauvais ratio si elle est plus grande que ~1.5mb.

Cela fonctionne sur le bureau mais pas dans la dernière version d'iOS avec l'API de téléchargement des médias.

Vous pouvez voir un exemple ici : http://jsbin.com/ekuros/1

Une idée de la façon de résoudre ce problème ? Est-ce un problème de mémoire ?

$('#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);
});

54voto

matt burns Points 6072

Si vous avez toujours besoin d'utiliser la version longue de la fonction drawImage, vous pouvez modifier ce paramètre :

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

à ça :

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

Il vous suffit d'inclure ces deux fonctions quelque part :

/**
 * 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 );
}

Ce système fonctionnera parfaitement, qu'il soit exécuté sur iOS ou sur d'autres plateformes.

Ceci est basé sur le grand travail de stomita et vous devriez le créditer dans votre travail.

30voto

Sebastian Tschan Points 616

Il existe une bibliothèque JavaScript de redimensionnement du canevas qui permet de contourner les problèmes de sous-échantillonnage et d'écrasement vertical rencontrés lors du dessin d'images à l'échelle sur le canevas des appareils iOS : http://github.com/stomita/ios-imagefile-megapixel

Il y a des problèmes secondaires lors de la mise à l'échelle d'images avec un canal alpha (car il utilise le canal alpha pour la détection des problèmes) et lorsque l'on essaie de redimensionner des éléments de toile existants, mais c'est la première solution que j'ai trouvée qui fonctionne réellement avec le problème en question.

stomita est également un utilisateur de StackOverflow et a posté sa solution ici : http://stackoverflow.com/a/12615436/644048

6voto

Torchify Points 76

Il semble que ce soit un bug d'iOS 6. Il n'y a aucune raison pour que l'aspect soit déréglé par votre code. J'ai le même problème qui n'a été introduit que dans iOS 6. Il semble que leur routine de sous-échantillonnage donne la mauvaise hauteur. J'ai soumis un rapport de bogue à Apple, et vous devriez faire de même. Plus ils recevront de rapports de bogue à ce sujet, mieux ce sera.

2voto

Paul Points 21

J'ai rencontré le même problème. Il semble que ce soit une limitation d'iOS, les jpg de plus de 2 mégapixels sont sous-échantillonnés.

Voir Création de contenu Web compatible avec Safari sur IPhone

-1voto

Agamemnus Points 154

Une version modifiée du code ci-dessus.

Edit : vu le code de L0LN1NJ4 à http://jsfiddle.net/gWY2a/24/ ... je suppose que celui-là est un peu mieux...

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
 }
}

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X