about summary refs log tree commit diff
path: root/app
diff options
context:
space:
mode:
authorYamagishi Kazutoshi <ykzts@desire.sh>2018-05-09 15:41:07 +0900
committerEugen Rochko <eugen@zeonfederated.com>2018-05-09 08:41:07 +0200
commit6832110af46e31831788c599b70ac047c7512e16 (patch)
tree5562c2d9080174f7a7dddc13f39498ed2d7587b2 /app
parentd2ee48977c8b663465504b44ae3d648cadd29aab (diff)
Correct rotate of image using EXIF (#7422)
Diffstat (limited to 'app')
-rw-r--r--app/javascript/mastodon/utils/resize_image.js107
1 files changed, 84 insertions, 23 deletions
diff --git a/app/javascript/mastodon/utils/resize_image.js b/app/javascript/mastodon/utils/resize_image.js
index 6442eda38..54459de3e 100644
--- a/app/javascript/mastodon/utils/resize_image.js
+++ b/app/javascript/mastodon/utils/resize_image.js
@@ -1,3 +1,5 @@
+import EXIF from 'exif-js';
+
 const MAX_IMAGE_DIMENSION = 1280;
 
 const getImageUrl = inputFile => new Promise((resolve, reject) => {
@@ -28,6 +30,84 @@ const loadImage = inputFile => new Promise((resolve, reject) => {
   }).catch(reject);
 });
 
+const getOrientation = (img, type = 'image/png') => new Promise(resolve => {
+  if (type !== 'image/jpeg') {
+    resolve(1);
+    return;
+  }
+
+  EXIF.getData(img, () => {
+    const orientation = EXIF.getTag(img, 'Orientation');
+    resolve(orientation);
+  });
+});
+
+const processImage = (img, { width, height, orientation, type = 'image/png' }) => new Promise(resolve => {
+  const canvas  = document.createElement('canvas');
+  [canvas.width, canvas.height] = orientation < 5 ? [width, height] : [height, width];
+
+  const context = canvas.getContext('2d');
+
+  switch (orientation) {
+  case 2:
+    context.translate(width, 0);
+    break;
+  case 3:
+    context.translate(width, height);
+    break;
+  case 4:
+    context.translate(0, height);
+    break;
+  case 5:
+    context.rotate(0.5 * Math.PI);
+    context.translate(1, -1);
+    break;
+  case 6:
+    context.rotate(0.5 * Math.PI);
+    context.translate(0, -height);
+    break;
+  case 7:
+    context.rotate(0.5, Math.PI);
+    context.translate(width, -height);
+    break;
+  case 8:
+    context.rotate(-0.5, Math.PI);
+    context.translate(-width, 0);
+    break;
+  }
+
+  context.drawImage(img, 0, 0, width, height);
+
+  canvas.toBlob(resolve, type);
+});
+
+const resizeImage = (img, type = 'image/png') => new Promise((resolve, reject) => {
+  const { width, height } = img;
+
+  let newWidth, newHeight;
+
+  if (width > height) {
+    newHeight = height * MAX_IMAGE_DIMENSION / width;
+    newWidth  = MAX_IMAGE_DIMENSION;
+  } else if (height > width) {
+    newWidth  = width * MAX_IMAGE_DIMENSION / height;
+    newHeight = MAX_IMAGE_DIMENSION;
+  } else {
+    newWidth  = MAX_IMAGE_DIMENSION;
+    newHeight = MAX_IMAGE_DIMENSION;
+  }
+
+  getOrientation(img, type)
+    .then(orientation => processImage(img, {
+      width: newWidth,
+      height: newHeight,
+      orientation,
+      type,
+    }))
+    .then(resolve)
+    .catch(reject);
+});
+
 export default inputFile => new Promise((resolve, reject) => {
   if (!inputFile.type.match(/image.*/) || inputFile.type === 'image/gif') {
     resolve(inputFile);
@@ -35,32 +115,13 @@ export default inputFile => new Promise((resolve, reject) => {
   }
 
   loadImage(inputFile).then(img => {
-    const canvas = document.createElement('canvas');
-    const { width, height } = img;
-
-    let newWidth, newHeight;
-
-    if (width < MAX_IMAGE_DIMENSION && height < MAX_IMAGE_DIMENSION) {
+    if (img.width < MAX_IMAGE_DIMENSION && img.height < MAX_IMAGE_DIMENSION) {
       resolve(inputFile);
       return;
     }
 
-    if (width > height) {
-      newHeight = height * MAX_IMAGE_DIMENSION / width;
-      newWidth  = MAX_IMAGE_DIMENSION;
-    } else if (height > width) {
-      newWidth  = width * MAX_IMAGE_DIMENSION / height;
-      newHeight = MAX_IMAGE_DIMENSION;
-    } else {
-      newWidth  = MAX_IMAGE_DIMENSION;
-      newHeight = MAX_IMAGE_DIMENSION;
-    }
-
-    canvas.width  = newWidth;
-    canvas.height = newHeight;
-
-    canvas.getContext('2d').drawImage(img, 0, 0, newWidth, newHeight);
-
-    canvas.toBlob(resolve, inputFile.type);
+    resizeImage(img, inputFile.type)
+      .then(resolve)
+      .catch(() => resolve(inputFile));
   }).catch(reject);
 });