LoginSignup
16
14

More than 5 years have passed since last update.

スマホでカメラ撮影した画像をcanvasに描画すると、横表示されてしまう問題

Last updated at Posted at 2016-07-06

基本的には、EXIF情報のorientationを参考にビューワーが画像を自動回転してくれるのだが、 canvasにもってくるとEXIF情報が失われてしまう。結果、すべての画像が横表示されてしまうバグが発生。

そこでcanvasを作成する前にorientationを取得し、そのデータを元にcanvasを強制的に回転。回転させたcanvasから取得したbase64を使用すると、横向きにならない。

index.js
import Exif from './lib/Exif';

const exif = new Exif();

// file api
let file    = document.querySelector('input[type=file]').files[0];
let reader  = new FileReader();

reader.onloadend = function () {
    let inputbase64data = reader.result; // 入力したいbase64データ
    exif.clearOrientation(inputbase64data, function(outputbase64data) {
        // outputbase64data: 横向きが直った画像base64データ
        // これを使って改めてcanvasを描画
    })
};
// fileを読み込んだらonloadendが走る
if (file) { reader.readAsDataURL(file); }
Exif.js
export default class Exif {

    constructor (opts = {}) {}

    clearOrientation(imgDataURL, callBack) {
        var byteString = atob(imgDataURL.split(',')[1]);
        var orientaion = byteStringToOrientation(byteString);

        var img = new Image();
        img.onload = function() {

            var canvas = document.createElement('canvas');
            var ctx = canvas.getContext('2d');
            switch (orientaion) {
                case 3: //画像が180度回転している時
                    canvas.width = img.width;
                    canvas.height = img.height;
                    ctx.rotate(Math.PI);
                    ctx.drawImage(img, -img.width, -img.height);
                    ctx.rotate(-Math.PI);
                    break;
                case 6: //画像が時計回りに90度回っている時
                    canvas.width = img.height;
                    canvas.height = img.width;
                    ctx.rotate(Math.PI * 0.5);
                    ctx.drawImage(img, 0, -img.height);
                    ctx.rotate(-Math.PI * 0.5);
                    break;
                case 8: //画像が反時計回りに90度回っている時
                    canvas.width = img.height;
                    canvas.height = img.width;
                    ctx.rotate(-Math.PI * 0.5);
                    ctx.drawImage(img, -img.width, 0);
                    ctx.rotate(Math.PI * 0.5);
                    break;
                default:
                    canvas.width = img.width;
                    canvas.height = img.height;
                    ctx.drawImage(img, 0, 0);
            }
            callBack(canvas.toDataURL("image/jpeg"));
        }
        img.src = imgDataURL;

        // exif情報のorientationを取得
        function byteStringToOrientation(img) {
            var head = 0;
            var orientation;
            while (1) {
                if (img.charCodeAt(head) == 255 & img.charCodeAt(head + 1) == 218) {
                    break;
                }
                if (img.charCodeAt(head) == 255 & img.charCodeAt(head + 1) == 216) {
                    head += 2;
                } else {
                    var length = img.charCodeAt(head + 2) * 256 + img.charCodeAt(head + 3);
                    var endPoint = head + length + 2;
                    if (img.charCodeAt(head) == 255 & img.charCodeAt(head + 1) == 225) {
                        var segment = img.slice(head, endPoint);
                        var bigEndian = segment.charCodeAt(10) == 77;
                        if (bigEndian) {
                            var count = segment.charCodeAt(18) * 256 + segment.charCodeAt(19);
                        } else {
                            var count = segment.charCodeAt(18) + segment.charCodeAt(19) * 256;
                        }
                        for (var i = 0; i < count; i++) {
                            var field = segment.slice(20 + 12 * i, 32 + 12 * i);
                            if ((bigEndian && field.charCodeAt(1) == 18) || (!bigEndian && field.charCodeAt(0) == 18)) {
                                orientation = bigEndian ? field.charCodeAt(9) : field.charCodeAt(8);
                            }
                        }
                        break;
                    }
                    head = endPoint;
                }
                if (head > img.length) {
                    break;
                }
            }
            return orientation;
        }
    }

}

base64が空で返ってくるバグ

※大きい画像を回転させるのには負担がかかる。iPhone5で画像が描画されない(base64が空で返ってきてしまう)バグがあったので、リサイズ後に回転させた。正しい処理の仕方は以下のようになる。

fileAPI(base64取得)

exif(orientation取得)

resize / trimming

rotate


参考
JPEGからJavascriptでEXIFのOrientation情報のみを取得する
[JavaScript] 画像リサイズ&回転
ExifのOrientationを見て画像を回転させる
↓既存のライブラリ
ExifのOrientationを見たうえでcanvasに画像を表示できるJSライブラリ
JavaScript-Load-Image

16
14
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
16
14