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:
@@ -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
|
||||
|
||||
@@ -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 -%>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user