web-dev-qa-db-fra.com

HTML5 Canvas drawImage ratio bug iOS

Je veux redimensionner l'image prise à partir de la caméra iOS côté client avec HTML5 Canvas mais je continue de fonctionner dans ce bug bizarre où l'image a un mauvais rapport si elle est supérieure à ~ 1,5 Mo

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

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

Une idée de comment résoudre ce problème, s'il vous plaît? 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);
});
55
Brice Lechatellier

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

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

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

30
Sebastian Tschan

Si vous devez toujours utiliser la version longue de la fonction drawImage, vous pouvez changer cela:

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

pour ça:

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

Vous avez juste besoin 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 );
}

Cela fonctionnera bien, qu'il soit exécuté sur iOS ou sur d'autres plates-formes.

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

54
matt burns

Il semble que ce soit un bug iOS 6. Il n'y a aucune raison pour que l'aspect se détache de 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 obtiennent de rapports de bogues, mieux c'est.

6
Torchify

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

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

2
Paul