MediaFile: fix thumbnail dimension calculation.

Calculate the dimensions of thumbnails ourselves instead of letting
libvips calculate them for us. This way we know the exact size of
thumbnails, so we can set the right width and height for <img> tags. If
we let libvips calculate thumbnail sizes for us, then we can't predict
the exact size of thumbnails, because sometimes libvips rounds numbers
differently than us.
This commit is contained in:
evazion
2021-11-30 04:17:17 -06:00
parent c2e6202da6
commit e5ba6d4afc
5 changed files with 65 additions and 14 deletions

View File

@@ -42,13 +42,16 @@ class PostPreviewComponent < ApplicationComponent
end
end
def preview_dimensions
downscale_ratio = Danbooru.config.small_image_width.to_f / [post.image_width, post.image_height].max
def preview_width
variant.width
end
{
width: [(downscale_ratio * post.image_width).floor, post.image_width].min,
height: [(downscale_ratio * post.image_height).floor, post.image_height].min,
}
def preview_height
variant.height
end
def variant
@variant ||= media_asset.variant(:preview)
end
def tooltip

View File

@@ -16,7 +16,7 @@
<%= tag.picture do -%>
<%= tag.source media: "(max-width: 660px)", srcset: cropped_url -%>
<%= tag.source media: "(min-width: 660px)", srcset: post.preview_file_url -%>
<%= tag.img class: "has-cropped-#{post.has_cropped?}", src: post.preview_file_url, style: "min-width: #{preview_dimensions[:width]}px; min-height: #{preview_dimensions[:height]}px;", title: tooltip, alt: "post ##{post.id}", crossorigin: "anonymous" -%>
<%= tag.img class: "has-cropped-#{post.has_cropped?}", src: post.preview_file_url, style: "min-width: #{preview_width}px; min-height: #{preview_height}px;", title: tooltip, alt: "post ##{post.id}", crossorigin: "anonymous" -%>
<% end -%>
<% end -%>
<% if pool -%>

View File

@@ -202,5 +202,18 @@ class MediaFile
}.stringify_keys
end
# Scale `width` and `height` to fit within `max_width` and `max_height`.
def self.scale_dimensions(width, height, max_width, max_height)
max_width ||= Float::INFINITY
max_height ||= Float::INFINITY
if width <= max_width && height <= max_height
[width, height]
else
scale = [max_width.to_f / width.to_f, max_height.to_f / height.to_f].min
[(width * scale).round.to_i, (height * scale).round.to_i]
end
end
memoize :file_ext, :file_size, :md5, :metadata
end

View File

@@ -72,8 +72,9 @@ class MediaFile::Image < MediaFile
MediaFile::Image.new(output_file)
end
def preview(max_width, max_height)
resize(max_width, max_height, size: :down)
def preview(max_width, max_height, **options)
w, h = MediaFile.scale_dimensions(width, height, max_width, max_height)
resize(w, h, size: :force, **options)
end
def crop(max_width, max_height)

View File

@@ -25,12 +25,14 @@ class MediaAsset < ApplicationRecord
validates :md5, uniqueness: { conditions: -> { where(status: [:processing, :active]) } }
class Variant
extend Memoist
attr_reader :media_asset, :variant
delegate :md5, :storage_service, :backup_storage_service, to: :media_asset
def initialize(media_asset, variant)
@media_asset = media_asset
@variant = variant
@variant = variant.to_sym
raise ArgumentError, "asset doesn't have #{variant} variant" unless Variant.exists?(media_asset, variant)
end
@@ -55,13 +57,13 @@ class MediaAsset < ApplicationRecord
def convert_file(media_file)
case variant
in :preview
media_file.preview(Danbooru.config.small_image_width, Danbooru.config.small_image_width)
media_file.preview(width, height)
in :crop
media_file.crop(Danbooru.config.small_image_width, Danbooru.config.small_image_width)
media_file.crop(width, height)
in :sample if media_asset.is_ugoira?
media_file.convert
in :sample if media_asset.is_static_image? && media_asset.image_width > Danbooru.config.large_image_width
media_file.preview(Danbooru.config.large_image_width, media_asset.image_height)
in :sample if media_asset.is_static_image?
media_file.preview(width, height)
in :original
media_file
end
@@ -105,6 +107,36 @@ class MediaAsset < ApplicationRecord
end
end
def max_dimensions
case variant
when :preview
[150, 150]
when :crop
[150, 150]
when :sample
[850, nil]
when :original
[nil, nil]
end
end
def dimensions
case variant
when :crop
max_dimensions
else
MediaFile.scale_dimensions(media_asset.image_width, media_asset.image_height, max_dimensions[0], max_dimensions[1])
end
end
def width
dimensions[0]
end
def height
dimensions[1]
end
def self.exists?(media_asset, variant)
case variant
when :preview
@@ -117,6 +149,8 @@ class MediaAsset < ApplicationRecord
true
end
end
memoize :file_name, :file_ext, :max_dimensions, :dimensions
end
concerning :SearchMethods do