Files
danbooru/app/models/media_asset.rb
evazion 74b03a7bd0 posts: fix incorrect exif rotation for PNGs.
Fix a bug where where PNG images could be incorrectly detected as
exif-rotated. This would happen when a PNG contained the
IFD0:Orientation flag. It's technically possible for a PNG to contain
this flag, but it's ignored by libvips and by browsers.

post #3762340 (nsfw) is an example of a PNG like this.

The fix is to use `autorot` to let libvips apply the rotation instead of
trying to interpret the exif data ourselves. Note that libvips-8.9 has a
bug where it doesn't strip the orientation flag after applying
`autorot`, which leads to the image being incorrectly rotated a second
time when generating the thumbnail. Use libvips-8.11 instead.
2021-09-23 00:10:00 -05:00

77 lines
2.8 KiB
Ruby

class MediaAsset < ApplicationRecord
has_one :media_metadata, dependent: :destroy
delegate :metadata, to: :media_metadata
def self.search(params)
q = search_attributes(params, :id, :created_at, :updated_at, :md5, :file_ext, :file_size, :image_width, :image_height)
q = q.apply_default_order(params)
q
end
def file=(file_or_path)
media_file = file_or_path.is_a?(MediaFile) ? file_or_path : MediaFile.open(file_or_path)
self.md5 = media_file.md5
self.file_ext = media_file.file_ext
self.file_size = media_file.file_size
self.image_width = media_file.width
self.image_height = media_file.height
self.media_metadata = MediaMetadata.new(file: media_file)
end
def is_animated?
is_animated_gif? || is_animated_png?
end
def is_animated_gif?
file_ext == "gif" && metadata.fetch("GIF:FrameCount", 1) > 1
end
def is_animated_png?
file_ext == "png" && metadata.fetch("PNG:AnimationFrames", 1) > 1
end
# @see https://exiftool.org/TagNames/JPEG.html
# @see https://exiftool.org/TagNames/PNG.html
# @see https://danbooru.donmai.us/posts?tags=exif:File:ColorComponents=1
# @see https://danbooru.donmai.us/posts?tags=exif:PNG:ColorType=Grayscale
def is_greyscale?
metadata["File:ColorComponents"] == 1 ||
metadata["PNG:ColorType"] == "Grayscale" ||
metadata["PNG:ColorType"] == "Grayscale with Alpha"
# Not always accurate:
# metadata["ICC-header:ColorSpaceData"] == "GRAY" ||
# metadata["XMP-photoshop:ColorMode"] == "Grayscale" ||
# metadata["XMP-photoshop:ICCProfileName"] == "EPSON Gray - Gamma 2.2" ||
# metadata["XMP-photoshop:ICCProfileName"] == "Gray Gamma 2.2"
end
# https://exiftool.org/TagNames/EXIF.html
def is_rotated?
file_ext == "jpg" && metadata["IFD0:Orientation"].in?(["Rotate 90 CW", "Rotate 270 CW", "Rotate 180"])
end
# Some animations technically have a finite loop count, but loop for hundreds
# or thousands of times. Only count animations with a low loop count as non-repeating.
def is_non_repeating_animation?
loop_count.in?(0..10)
end
# @see https://exiftool.org/TagNames/GIF.html
# @see https://exiftool.org/TagNames/PNG.html
# @see https://danbooru.donmai.us/posts?tags=-exif:GIF:AnimationIterations=Infinite+animated_gif
# @see https://danbooru.donmai.us/posts?tags=-exif:PNG:AnimationPlays=inf+animated_png
def loop_count
return Float::INFINITY if metadata["GIF:AnimationIterations"] == "Infinite"
return Float::INFINITY if metadata["PNG:AnimationPlays"] == "inf"
return metadata["GIF:AnimationIterations"] if metadata["GIF:AnimationIterations"].present?
return metadata["PNG:AnimationPlays"] if metadata["PNG:AnimationPlays"].present?
# If the AnimationIterations tag isn't present, then it's counted as a loop count of 0.
return 0 if is_animated_gif? && metadata["GIF:AnimationIterations"].nil?
nil
end
end