add image cropping support

This commit is contained in:
Albert Yi
2018-05-16 17:13:36 -07:00
parent df73c682db
commit 64446d49e1
20 changed files with 150 additions and 471 deletions

View File

@@ -1,6 +1,7 @@
$(function() {
$("#maintoggle").click(function() {
$('#nav').toggle();
$('#maintoggle').toggleClass('toggler-active');
$('#maintoggle-on').toggle();
$('#maintoggle-off').toggle();
});
});

View File

@@ -21,6 +21,16 @@
display: none;
}
div#page aside#sidebar {
input#tags {
width: 75%;
}
input[type=submit] {
width: 20%;
}
}
div#page {
padding: 0 0.25vw;
> div /* div#c-$controller */ {
@@ -40,12 +50,12 @@
}
#maintoggle {
display: inline;
background-color: lighten($link_color, 13%);
padding: 0.8em 1.2em 0.8em 1.2em;
border-radius: 20% 20% 0 0;
display: block;
font-weight: bold;
color: #fff;
position: absolute;
top: 3vw;
right: 4vw;
font-size: 2em;
&.toggler-active {
background-color: lighten($link_color, 25%);
@@ -128,8 +138,8 @@
article.post-preview {
margin: 0.5vw;
width: 48.5vw;
height: 48.5vw;
width: 32vw;
height: 32vw;
text-align: center;
vertical-align: middle;
display: inline-block;
@@ -139,10 +149,10 @@
//display: block;
margin: 0 auto;
}
img {
img.cropped {
//object-fit: contain;
//width: 48.5vw;
//height: 48.5vw;
width: 32vw;
height: 32vw;
margin: 0 auto;
border: none !important;
}
@@ -190,6 +200,8 @@
}
#nav {
font-size: 2em;
line-height: 2em;
display: none;
}
@@ -210,7 +222,6 @@
}
header {
border-bottom: 2px solid lighten($link_color, 13%);
text-align: center;
line-height: 2em;
h1 {

View File

@@ -5,6 +5,7 @@ module DanbooruImageResizer
THUMBNAIL_OPTIONS = { size: :down, linear: false, auto_rotate: false, export_profile: SRGB_PROFILE }
# http://jcupitt.github.io/libvips/API/current/VipsForeignSave.html#vips-jpegsave
JPEG_OPTIONS = { background: 255, strip: true, interlace: true, optimize_coding: true }
CROP_OPTIONS = { linear: false, auto_rotate: false, export_profile: SRGB_PROFILE, crop: :attention }
# XXX libvips-8.4 on Debian doesn't support the `Vips::Image.thumbnail` method.
# On 8.4 we have to shell out to vipsthumbnail instead. Remove when Debian supports 8.5.
@@ -16,6 +17,14 @@ module DanbooruImageResizer
end
end
def self.crop(file, length, quality = 90)
if Vips.at_least_libvips?(8, 5)
crop_ruby(file, length, quality)
else
crop_shell(file, length, quality)
end
end
# https://github.com/jcupitt/libvips/wiki/HOWTO----Image-shrinking
# http://jcupitt.github.io/libvips/API/current/Using-vipsthumbnail.md.html
def self.resize_ruby(file, width, height, resize_quality)
@@ -26,6 +35,14 @@ module DanbooruImageResizer
output_file
end
def self.crop_ruby(file, length, resize_quality)
output_file = Tempfile.new
resized_image = Vips::Image.thumbnail(file.path, length, height: length, **CROP_OPTIONS)
resized_image.jpegsave(output_file.path, Q: resize_quality, **JPEG_OPTIONS)
output_file
end
def self.resize_shell(file, width, height, quality)
output_file = Tempfile.new(["resize", ".jpg"])
@@ -47,4 +64,25 @@ module DanbooruImageResizer
output_file
end
def self.crop_shell(file, length, quality)
output_file = Tempfile.new(["crop", ".jpg"])
# --size=WxH will upscale if the image is smaller than the target size.
# Fix the target size so that it's not bigger than the image.
image = Vips::Image.new_from_file(file.path)
arguments = [
file.path,
"--eprofile=#{SRGB_PROFILE}",
"--crop=none",
"--size=#{length}",
"--format=#{output_file.path}[Q=#{quality},background=255,strip,interlace,optimize_coding]"
]
success = system("vipsthumbnail", *arguments)
raise RuntimeError, "vipsthumbnail failed (exit status: #{$?.exitstatus})" if !success
output_file
end
end

View File

@@ -1,12 +0,0 @@
class ImageCropper
def self.enabled?
Danbooru.config.aws_sqs_cropper_url.present?
end
def self.notify(post)
if post.is_image?
sqs = SqsService.new(Danbooru.config.aws_sqs_cropper_url)
sqs.send_message("#{post.id},https://#{Danbooru.config.hostname}/data/#{post.md5}.#{post.file_ext}")
end
end
end

View File

@@ -64,8 +64,18 @@ class PixivUgoiraConverter
output_file
end
def self.generate_crop(ugoira_file)
file = Tempfile.new(["ugoira-crop", ".zip"], binmode: true)
zipfile = Zip::File.new(ugoira_file.path)
zipfile.entries.first.extract(file.path) { true } # 'true' means overwrite the existing tempfile.
DanbooruImageResizer.crop(file, Danbooru.config.small_image_width, 85)
ensure
file.close!
end
def self.generate_preview(ugoira_file)
file = Tempfile.new(binmode: true)
file = Tempfile.new(["ugoira-preview", ".zip"], binmode: true)
zipfile = Zip::File.new(ugoira_file.path)
zipfile.entries.first.extract(file.path) { true } # 'true' means overwrite the existing tempfile.

View File

@@ -57,6 +57,8 @@ class StorageManager
"#{root_url}/images/download-preview.png"
elsif type == :preview
"#{base_url}/preview/#{subdir}#{file}"
elsif type == :crop
"#{base_url}/crop/#{subdir}#{file}"
elsif type == :large && post.has_large?
"#{base_url}/sample/#{subdir}#{seo_tags}#{file}"
else
@@ -77,6 +79,8 @@ class StorageManager
case type
when :preview
"#{base_dir}/preview/#{subdir}#{file}"
when :crop
"#{base_dir}/crop/#{subdir}#{file}"
when :large
"#{base_dir}/sample/#{subdir}#{file}"
when :original
@@ -90,6 +94,8 @@ class StorageManager
case type
when :preview
"#{md5}.jpg"
when :crop
"#{md5}.jpg"
when :large
"#{large_image_prefix}#{md5}.#{large_file_ext}"
when :original

View File

@@ -5,6 +5,7 @@ class StorageManager::Local < StorageManager
temp_path = dest_path + "-" + SecureRandom.uuid + ".tmp"
FileUtils.mkdir_p(File.dirname(temp_path))
io.rewind
bytes_copied = IO.copy_stream(io, temp_path)
raise Error, "store failed: #{bytes_copied}/#{io.size} bytes copied" if bytes_copied != io.size

View File

@@ -130,21 +130,23 @@ class UploadService
def self.generate_resizes(file, upload)
if upload.is_video?
video = FFMPEG::Movie.new(file.path)
crop_file = generate_video_preview_for(video, Danbooru.config.small_image_width, Danbooru.config.small_image_width)
preview_file = generate_video_preview_for(video, Danbooru.config.small_image_width, Danbooru.config.small_image_width)
elsif upload.is_ugoira?
preview_file = PixivUgoiraConverter.generate_preview(file)
crop_file = PixivUgoiraConverter.generate_crop(file)
sample_file = PixivUgoiraConverter.generate_webm(file, upload.context["ugoira"]["frame_data"])
elsif upload.is_image?
preview_file = DanbooruImageResizer.resize(file, Danbooru.config.small_image_width, Danbooru.config.small_image_width, 85)
crop_file = DanbooruImageResizer.crop(file, Danbooru.config.small_image_width, 85)
if upload.image_width > Danbooru.config.large_image_width
sample_file = DanbooruImageResizer.resize(file, Danbooru.config.large_image_width, upload.image_height, 90)
end
end
[preview_file, sample_file]
[preview_file, crop_file, sample_file]
end
def self.generate_video_preview_for(video, width, height)
@@ -155,7 +157,7 @@ class UploadService
width = (height * dimension_ratio).to_i
end
output_file = Tempfile.new(binmode: true)
output_file = Tempfile.new(["video-preview", ".jpg"], binmode: true)
video.screenshot(output_file.path, {:seek_time => 0, :resolution => "#{width}x#{height}"})
output_file
end
@@ -178,14 +180,16 @@ class UploadService
upload.tag_string = "#{upload.tag_string} #{Utils.automatic_tags(upload, file)}"
preview_file, sample_file = Utils.generate_resizes(file, upload)
preview_file, crop_file, sample_file = Utils.generate_resizes(file, upload)
begin
Utils.distribute_files(file, upload, :original)
Utils.distribute_files(sample_file, upload, :large) if sample_file.present?
Utils.distribute_files(preview_file, upload, :preview) if preview_file.present?
Utils.distribute_files(crop_file, upload, :crop) if crop_file.present?
ensure
preview_file.try(:close!)
crop_file.try(:close!)
sample_file.try(:close!)
end
@@ -583,13 +587,13 @@ class UploadService
)
end
notify_cropper(@post) if ImageCropper.enabled?
upload.update(status: "completed", post_id: @post.id)
@post
end
def convert_to_post(upload)
Post.new.tap do |p|
p.has_cropped = true
p.tag_string = upload.tag_string
p.md5 = upload.md5
p.file_ext = upload.file_ext
@@ -607,8 +611,4 @@ class UploadService
end
end
end
def notify_cropper(post)
# ImageCropper.notify(post)
end
end

View File

@@ -173,6 +173,10 @@ class Post < ApplicationRecord
storage_manager.file_path(md5, file_ext, :preview)
end
def crop_file_url
storage_manager.file_url(self, :crop)
end
def open_graph_image_url
if is_image?
if has_large?

View File

@@ -92,6 +92,8 @@ class User < ApplicationRecord
has_many :post_approvals, :dependent => :destroy
has_many :post_disapprovals, :dependent => :destroy
has_many :post_votes
has_many :post_archives
has_many :note_versions
has_many :bans, -> {order("bans.id desc")}
has_one :recent_ban, -> {order("bans.id desc")}, :class_name => "Ban"

View File

@@ -34,14 +34,16 @@ class PostPresenter < Presenter
end
html << %{<a href="#{path}/#{post.id}#{tag_param}">}
if options[:show_cropped] && post.has_cropped?
src = post.cropped_file_url
if CurrentUser.id == 1 && options[:show_cropped] && post.has_cropped? && !CurrentUser.user.disable_cropped_thumbnails?
src = post.crop_file_url
imgClass = "cropped"
else
src = post.preview_file_url
imgClass = nil
end
tooltip = "#{post.tag_string} rating:#{post.rating} score:#{post.score}"
html << %{<img itemprop="thumbnailUrl" src="#{src}" title="#{h(tooltip)}" alt="#{h(post.tag_string)}">}
html << %{<img class="#{imgClass}" itemprop="thumbnailUrl" src="#{src}" title="#{h(tooltip)}" alt="#{h(post.tag_string)}">}
html << %{</a>}
if options[:pool]
@@ -71,7 +73,7 @@ class PostPresenter < Presenter
def self.preview_class(post, description = nil, options = {})
klass = "post-preview"
klass << " large-cropped" if post.has_cropped? && options[:show_cropped]
# klass << " large-cropped" if post.has_cropped? && options[:show_cropped]
klass << " pooled" if description
klass << " post-status-pending" if post.is_pending?
klass << " post-status-flagged" if post.is_flagged?

View File

@@ -12,7 +12,7 @@ module PostSetPresenters
end
posts.each do |post|
html << PostPresenter.preview(post, options.merge(:tags => @post_set.tag_string, :raw => @post_set.raw))
html << PostPresenter.preview(post, options.merge(:show_cropped => true, :tags => @post_set.tag_string, :raw => @post_set.raw))
html << "\n"
end

View File

@@ -88,7 +88,12 @@
<%= render "news_updates/listing" %>
<h1><%= link_to Danbooru.config.app_name, "/" %></h1>
<span id="maintoggle"> ☰ </span> <!-- Maybe icon would be better, I'm not sure -->
<div id="maintoggle">
<a href="#"><i id="maintoggle-on" class="fas fa-bars"></i></a>
<a href="#"><i id="maintoggle-off" class="fas fa-times" style="display: none;"></i></a>
</div>
<nav id="nav">
<%= render "layouts/main_links" %>
<%= yield :secondary_links %>

View File

@@ -33,7 +33,7 @@
<li><%= link_to "Wiki", new_wiki_page_path(wiki_page: { title: @post_set.tag_string }), id: "show-excerpt-link" %></li>
<% end %>
<li id="searchbox-redirect-link"><a href="#search-box">Search&raquo;</a></li>
<li id="searchbox-redirect-link"><a href="#search-box">Search &raquo;</a></li>
</menu>
<%= render "posts/partials/index/edit" %>

View File

@@ -13,8 +13,8 @@
<% if params[:random] %>
<%= hidden_field_tag :random, params[:random] %>
<% end %>
<%= hidden_field_tag "ms", "1" %>
<%= submit_tag "Go", :name => nil, :class => "ui-button ui-widget ui-corner-all tiny gradient" %>
<%= hidden_field_tag "ms", "1" %>
<% end %>
</section>

View File

@@ -80,7 +80,7 @@
<%= f.input :disable_post_tooltips, :as => :select, :hint => "Disable advanced tooltips when hovering over thumbnails", :collection => [["No", "false"], ["Yes", "true"]], :include_blank => false %>
<%#= f.input :disable_cropped_thumbnails, :as => :select, :collection => [["No", "false"], ["Yes", "true"]], :include_blank => false %>
<%= f.input :disable_cropped_thumbnails, :as => :select, :collection => [["No", "false"], ["Yes", "true"]], :include_blank => false %>
<div class="input text optional field_with_hint">
<label class="text optional" for="user_dmail_filter_attributes_words">Dmail filter</label>