about summary refs log tree commit diff
diff options
context:
space:
mode:
authorEugen Rochko <eugen@zeonfederated.com>2018-07-28 03:33:00 +0200
committerGitHub <noreply@github.com>2018-07-28 03:33:00 +0200
commit0fb0037ca7ea9910b490818a1cc13f4005ba6134 (patch)
tree14e6979b62c92c46d81f8d20315baf9686f3fa17
parent3c35b34b612c210c97ce8cffda33798ffa8f22e8 (diff)
Resize images by area instead of fixed dimensions (#8083)
To improve the way super tall or super ride images are treated, the
numbers remain the same, 1280x1280 and 400x400, but if an image
is less in one dimension than the other, the other can become larger

Thanks to @WAHa_06x36@mastodon.social for the tip
-rw-r--r--app/javascript/mastodon/utils/resize_image.js18
-rw-r--r--app/models/media_attachment.rb6
-rw-r--r--lib/paperclip/lazy_thumbnail.rb13
-rw-r--r--spec/models/media_attachment_spec.rb6
4 files changed, 20 insertions, 23 deletions
diff --git a/app/javascript/mastodon/utils/resize_image.js b/app/javascript/mastodon/utils/resize_image.js
index 279a858ca..d1608094f 100644
--- a/app/javascript/mastodon/utils/resize_image.js
+++ b/app/javascript/mastodon/utils/resize_image.js
@@ -1,6 +1,6 @@
 import EXIF from 'exif-js';
 
-const MAX_IMAGE_DIMENSION = 1280;
+const MAX_IMAGE_PIXELS = 1638400; // 1280x1280px
 
 const getImageUrl = inputFile => new Promise((resolve, reject) => {
   if (window.URL && URL.createObjectURL) {
@@ -73,18 +73,8 @@ const processImage = (img, { width, height, orientation, type = 'image/png' }) =
 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;
-  }
+  const newWidth  = Math.round(Math.sqrt(MAX_IMAGE_PIXELS * (width / height)));
+  const newHeight = Math.round(Math.sqrt(MAX_IMAGE_PIXELS * (height / width)));
 
   getOrientation(img, type)
     .then(orientation => processImage(img, {
@@ -104,7 +94,7 @@ export default inputFile => new Promise((resolve, reject) => {
   }
 
   loadImage(inputFile).then(img => {
-    if (img.width < MAX_IMAGE_DIMENSION && img.height < MAX_IMAGE_DIMENSION) {
+    if (img.width * img.height < MAX_IMAGE_PIXELS) {
       resolve(inputFile);
       return;
     }
diff --git a/app/models/media_attachment.rb b/app/models/media_attachment.rb
index f9a8f322e..63c6d5af8 100644
--- a/app/models/media_attachment.rb
+++ b/app/models/media_attachment.rb
@@ -32,12 +32,12 @@ class MediaAttachment < ApplicationRecord
 
   IMAGE_STYLES = {
     original: {
-      geometry: '1280x1280>',
+      pixels: 1_638_400, # 1280x1280px
       file_geometry_parser: FastGeometryParser,
     },
 
     small: {
-      geometry: '400x400>',
+      pixels: 160_000, # 400x400px
       file_geometry_parser: FastGeometryParser,
     },
   }.freeze
@@ -152,7 +152,7 @@ class MediaAttachment < ApplicationRecord
       elsif VIDEO_MIME_TYPES.include? f.file_content_type
         [:video_transcoder]
       else
-        [:thumbnail]
+        [:lazy_thumbnail]
       end
     end
   end
diff --git a/lib/paperclip/lazy_thumbnail.rb b/lib/paperclip/lazy_thumbnail.rb
index aafa21343..ea675a5bf 100644
--- a/lib/paperclip/lazy_thumbnail.rb
+++ b/lib/paperclip/lazy_thumbnail.rb
@@ -5,8 +5,14 @@ module Paperclip
     def make
       return File.open(@file.path) unless needs_convert?
 
-      min_side = [@current_geometry.width, @current_geometry.height].min
-      options[:geometry] = "#{min_side.to_i}x#{min_side.to_i}#" if @target_geometry.square? && min_side < @target_geometry.width
+      if options[:geometry]
+        min_side = [@current_geometry.width, @current_geometry.height].min.to_i
+        options[:geometry] = "#{min_side}x#{min_side}#" if @target_geometry.square? && min_side < @target_geometry.width
+      elsif options[:pixels]
+        width  = Math.sqrt(options[:pixels] * (@current_geometry.width.to_f / @current_geometry.height.to_f)).round.to_i
+        height = Math.sqrt(options[:pixels] * (@current_geometry.height.to_f / @current_geometry.width.to_f)).round.to_i
+        options[:geometry] = "#{width}x#{height}>"
+      end
 
       Paperclip::Thumbnail.make(file, options, attachment)
     end
@@ -18,7 +24,8 @@ module Paperclip
     end
 
     def needs_different_geometry?
-      !@target_geometry.nil? && @current_geometry.width != @target_geometry.width && @current_geometry.height != @target_geometry.height
+      (options[:geometry] && @current_geometry.width != @target_geometry.width && @current_geometry.height != @target_geometry.height) ||
+        (options[:pixels] && @current_geometry.width * @current_geometry.height > options[:pixels])
     end
 
     def needs_different_format?
diff --git a/spec/models/media_attachment_spec.rb b/spec/models/media_attachment_spec.rb
index d2230b6d0..cb1cee518 100644
--- a/spec/models/media_attachment_spec.rb
+++ b/spec/models/media_attachment_spec.rb
@@ -129,9 +129,9 @@ RSpec.describe MediaAttachment, type: :model do
       expect(media.file.meta["original"]["width"]).to eq 600
       expect(media.file.meta["original"]["height"]).to eq 400
       expect(media.file.meta["original"]["aspect"]).to eq 1.5
-      expect(media.file.meta["small"]["width"]).to eq 400
-      expect(media.file.meta["small"]["height"]).to eq 267
-      expect(media.file.meta["small"]["aspect"]).to eq 400.0/267
+      expect(media.file.meta["small"]["width"]).to eq 490
+      expect(media.file.meta["small"]["height"]).to eq 327
+      expect(media.file.meta["small"]["aspect"]).to eq 490.0/327
     end
   end