diff --git a/app/components/post_preview_component.rb b/app/components/post_preview_component.rb index 8c9e7369a..3b9ca1784 100644 --- a/app/components/post_preview_component.rb +++ b/app/components/post_preview_component.rb @@ -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 diff --git a/app/components/post_preview_component/post_preview_component.html.erb b/app/components/post_preview_component/post_preview_component.html.erb index 49d90e5c7..8a82b801e 100644 --- a/app/components/post_preview_component/post_preview_component.html.erb +++ b/app/components/post_preview_component/post_preview_component.html.erb @@ -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 -%> diff --git a/app/logical/media_file.rb b/app/logical/media_file.rb index ac4dd8bcd..6b3ea53e5 100644 --- a/app/logical/media_file.rb +++ b/app/logical/media_file.rb @@ -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 diff --git a/app/logical/media_file/image.rb b/app/logical/media_file/image.rb index 5e137f7e2..3535f43ab 100644 --- a/app/logical/media_file/image.rb +++ b/app/logical/media_file/image.rb @@ -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) diff --git a/app/models/media_asset.rb b/app/models/media_asset.rb index 8336a5e5f..0620786dc 100644 --- a/app/models/media_asset.rb +++ b/app/models/media_asset.rb @@ -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