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
th-downstream
Eugen Rochko 6 years ago committed by GitHub
parent d2b918522e
commit 91aadec929

@ -1,6 +1,6 @@
import EXIF from 'exif-js'; import EXIF from 'exif-js';
const MAX_IMAGE_DIMENSION = 1280; const MAX_IMAGE_PIXELS = 1638400; // 1280x1280px
const getImageUrl = inputFile => new Promise((resolve, reject) => { const getImageUrl = inputFile => new Promise((resolve, reject) => {
if (window.URL && URL.createObjectURL) { 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 resizeImage = (img, type = 'image/png') => new Promise((resolve, reject) => {
const { width, height } = img; const { width, height } = img;
let newWidth, newHeight; const newWidth = Math.round(Math.sqrt(MAX_IMAGE_PIXELS * (width / height)));
const newHeight = Math.round(Math.sqrt(MAX_IMAGE_PIXELS * (height / width)));
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) getOrientation(img, type)
.then(orientation => processImage(img, { .then(orientation => processImage(img, {
@ -104,7 +94,7 @@ export default inputFile => new Promise((resolve, reject) => {
} }
loadImage(inputFile).then(img => { 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); resolve(inputFile);
return; return;
} }

@ -32,12 +32,12 @@ class MediaAttachment < ApplicationRecord
IMAGE_STYLES = { IMAGE_STYLES = {
original: { original: {
geometry: '1280x1280>', pixels: 1_638_400, # 1280x1280px
file_geometry_parser: FastGeometryParser, file_geometry_parser: FastGeometryParser,
}, },
small: { small: {
geometry: '400x400>', pixels: 160_000, # 400x400px
file_geometry_parser: FastGeometryParser, file_geometry_parser: FastGeometryParser,
}, },
}.freeze }.freeze
@ -152,7 +152,7 @@ class MediaAttachment < ApplicationRecord
elsif VIDEO_MIME_TYPES.include? f.file_content_type elsif VIDEO_MIME_TYPES.include? f.file_content_type
[:video_transcoder] [:video_transcoder]
else else
[:thumbnail] [:lazy_thumbnail]
end end
end end
end end

@ -5,8 +5,14 @@ module Paperclip
def make def make
return File.open(@file.path) unless needs_convert? return File.open(@file.path) unless needs_convert?
min_side = [@current_geometry.width, @current_geometry.height].min if options[:geometry]
options[:geometry] = "#{min_side.to_i}x#{min_side.to_i}#" if @target_geometry.square? && min_side < @target_geometry.width 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) Paperclip::Thumbnail.make(file, options, attachment)
end end
@ -18,7 +24,8 @@ module Paperclip
end end
def needs_different_geometry? 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 end
def needs_different_format? def needs_different_format?

@ -129,9 +129,9 @@ RSpec.describe MediaAttachment, type: :model do
expect(media.file.meta["original"]["width"]).to eq 600 expect(media.file.meta["original"]["width"]).to eq 600
expect(media.file.meta["original"]["height"]).to eq 400 expect(media.file.meta["original"]["height"]).to eq 400
expect(media.file.meta["original"]["aspect"]).to eq 1.5 expect(media.file.meta["original"]["aspect"]).to eq 1.5
expect(media.file.meta["small"]["width"]).to eq 400 expect(media.file.meta["small"]["width"]).to eq 490
expect(media.file.meta["small"]["height"]).to eq 267 expect(media.file.meta["small"]["height"]).to eq 327
expect(media.file.meta["small"]["aspect"]).to eq 400.0/267 expect(media.file.meta["small"]["aspect"]).to eq 490.0/327
end end
end end

Loading…
Cancel
Save