-#!/usr/bin/ruby
+#! /usr/bin/ruby
#
# * BOOH *
#
require 'tempfile'
require 'gtk2'
+require 'booh/gtkadds'
require 'booh/GtkAutoTable'
require 'gettext'
return [ frame, textview ]
end
+def update_shown_pixbuf(thumbnail_img, img, desired_x, desired_y)
+
+ if !$modified_pixbufs[thumbnail_img]
+ $modified_pixbufs[thumbnail_img] = { :orig => img.pixbuf }
+ elsif !$modified_pixbufs[thumbnail_img][:orig]
+ $modified_pixbufs[thumbnail_img][:orig] = img.pixbuf
+ end
+
+ pixbuf = $modified_pixbufs[thumbnail_img][:orig].dup
+
+ #- rotate
+ if $modified_pixbufs[thumbnail_img][:angle_to_orig] && $modified_pixbufs[thumbnail_img][:angle_to_orig] != 0
+ pixbuf = rotate_pixbuf(pixbuf, $modified_pixbufs[thumbnail_img][:angle_to_orig])
+ msg 3, "sizes: #{pixbuf.width} #{pixbuf.height} - desired #{desired_x}x#{desired_x}"
+ if pixbuf.height > desired_y
+ pixbuf = pixbuf.scale(pixbuf.width * (desired_y.to_f/pixbuf.height), desired_y, Gdk::Pixbuf::INTERP_BILINEAR)
+ elsif pixbuf.width < desired_x && pixbuf.height < desired_y
+ pixbuf = pixbuf.scale(desired_x, pixbuf.height * (desired_x.to_f/pixbuf.width), Gdk::Pixbuf::INTERP_BILINEAR)
+ end
+ end
+
+ #- fix white balance
+ if $modified_pixbufs[thumbnail_img][:whitebalance]
+ pixbuf.whitebalance!($modified_pixbufs[thumbnail_img][:whitebalance])
+ end
+
+ img.pixbuf = $modified_pixbufs[thumbnail_img][:pixbuf] = pixbuf
+end
+
def rotate_real(angle, thumbnail_img, img, xmlelem, attributes_prefix, desired_x, desired_y)
$modified = true
#- update rotate attribute
- xmlelem.add_attribute("#{attributes_prefix}rotate", current_angle = (xmlelem.attributes["#{attributes_prefix}rotate"].to_i + angle) % 360)
+ xmlelem.add_attribute("#{attributes_prefix}rotate", (xmlelem.attributes["#{attributes_prefix}rotate"].to_i + angle) % 360)
- if !$rotated_pixbufs[thumbnail_img]
- $rotated_pixbufs[thumbnail_img] = { :orig => img.pixbuf, :angle_to_orig => angle % 360 }
- else
- $rotated_pixbufs[thumbnail_img][:angle_to_orig] = ($rotated_pixbufs[thumbnail_img][:angle_to_orig] + angle) % 360
- end
- msg 3, "angle: #{angle}, angle to orig: #{$rotated_pixbufs[thumbnail_img][:angle_to_orig]}"
-
- #- rotate shown thumbnail
- pixbuf = rotate_pixbuf($rotated_pixbufs[thumbnail_img][:orig], $rotated_pixbufs[thumbnail_img][:angle_to_orig])
- msg 3, "sizes: #{pixbuf.width} #{pixbuf.height} - desired #{desired_x}x#{desired_x}"
- if pixbuf.height > desired_y
- img.pixbuf = $rotated_pixbufs[thumbnail_img][:pixbuf] = pixbuf.scale(pixbuf.width * (desired_y.to_f/pixbuf.height), desired_y,
- Gdk::Pixbuf::INTERP_BILINEAR)
- elsif pixbuf.width < desired_x && pixbuf.height < desired_y
- img.pixbuf = $rotated_pixbufs[thumbnail_img][:pixbuf] = pixbuf.scale(desired_x, pixbuf.height * (desired_x.to_f/pixbuf.width),
- Gdk::Pixbuf::INTERP_BILINEAR)
- else
- img.pixbuf = $rotated_pixbufs[thumbnail_img][:pixbuf] = pixbuf
- end
+ $modified_pixbufs[thumbnail_img] ||= {}
+ $modified_pixbufs[thumbnail_img][:angle_to_orig] = (($modified_pixbufs[thumbnail_img][:angle_to_orig] || 0) + angle) % 360
+ msg 3, "angle: #{angle}, angle to orig: #{$modified_pixbufs[thumbnail_img][:angle_to_orig]}"
+
+ update_shown_pixbuf(thumbnail_img, img, desired_x, desired_y)
end
def rotate(angle, thumbnail_img, img, xmlelem, attributes_prefix, desired_x, desired_y)
dialog.show_all
dialog.run { |response|
- $modified = true
newval = entry.text
dialog.destroy
if response == Gtk::Dialog::RESPONSE_OK
+ $modified = true
msg 3, "changing frame offset top #{newval}"
return { :old => value, :new => newval }
else
}
end
-def gen_real_thumbnail(type, origfile, destfile, xmldir, size, img)
- Thread.new {
- push_mousecursor_wait
+def change_whitebalance(xmlelem, attributes_prefix, value)
+ $modified = true
+ xmlelem.add_attribute("#{attributes_prefix}white-balance", value)
+end
+
+def recalc_whitebalance(level, orig, thumbnail_img, img, xmlelem, attributes_prefix, desired_x, desired_y, infotype)
+
+ #- in case the white balance was already modified in the config file and thumbnail, we cannot just revert, we need to use original file
+ if (!$modified_pixbufs[thumbnail_img] || !$modified_pixbufs[thumbnail_img][:whitebalance]) && xmlelem.attributes["#{attributes_prefix}white-balance"]
+ save_whitebalance = xmlelem.attributes["#{attributes_prefix}white-balance"]
+ xmlelem.delete_attribute("#{attributes_prefix}white-balance")
+ destfile = "#{thumbnail_img}-orig-whitebalance.jpg"
+ gen_real_thumbnail_core(attributes_prefix == '' ? 'element' : 'subdir', orig, destfile,
+ xmlelem, attributes_prefix == '' ? $default_size['thumbnails'] : $albums_thumbnail_size, infotype)
+ $modified_pixbufs[thumbnail_img] ||= {}
+ $modified_pixbufs[thumbnail_img][:orig] = pixbuf_or_nil(destfile)
system("rm -f '#{destfile}'")
- #- type can be 'element' or 'subdir'
- if type == 'element'
- gen_thumbnails_element(origfile, xmldir, false, [ { 'filename' => destfile, 'size' => size } ])
+ xmlelem.add_attribute("#{attributes_prefix}white-balance", save_whitebalance)
+ $modified_pixbufs[thumbnail_img][:angle_to_orig] = 0
+ if entry2type(orig) == 'video'
+ #- cleanup temp for videos
+ system("rm -f #{current_dest_dir}/screenshot.jpg000000.jpg")
+ end
+ end
+
+ $modified_pixbufs[thumbnail_img] ||= {}
+ $modified_pixbufs[thumbnail_img][:whitebalance] = level.to_f
+
+ update_shown_pixbuf(thumbnail_img, img, desired_x, desired_y)
+end
+
+def ask_whitebalance(orig, thumbnail_img, img_, xmlelem, attributes_prefix, desired_x, desired_y, infotype)
+ #- init $modified_pixbufs correctly
+# update_shown_pixbuf(thumbnail_img, img_, desired_x, desired_y)
+
+ value = xmlelem.attributes["#{attributes_prefix}white-balance"] || "0"
+
+ dialog = Gtk::Dialog.new(utf8(_("Fix white balance")),
+ $main_window,
+ Gtk::Dialog::MODAL | Gtk::Dialog::DESTROY_WITH_PARENT,
+ [Gtk::Stock::OK, Gtk::Dialog::RESPONSE_OK],
+ [Gtk::Stock::CANCEL, Gtk::Dialog::RESPONSE_CANCEL])
+
+ lbl = Gtk::Label.new
+ lbl.markup = utf8(
+_("You can fix the <b>white balance</b> of the image, if your image is too blue
+or too yellow because your camera didn't detect the light correctly. Drag the
+slider below the image to the left for more blue, to the right for more yellow.
+"))
+ dialog.vbox.add(lbl)
+ dialog.vbox.add(evt = Gtk::EventBox.new.add(img = Gtk::Image.new(img_.pixbuf)))
+ dialog.vbox.add(hs = Gtk::HScale.new(-200, 200, 1).set_value(value.to_i))
+
+ dialog.window_position = Gtk::Window::POS_MOUSE
+ dialog.show_all
+
+ lastval = nil
+ thread = nil
+ timeout = Gtk.timeout_add(100) {
+ if hs.value != lastval
+ lastval = hs.value
+ if thread
+ thread.kill
+ end
+ thread = Thread.new {
+ recalc_whitebalance(lastval, orig, thumbnail_img, img, xmlelem, attributes_prefix, desired_x, desired_y, infotype)
+ }
+ end
+ true
+ }
+
+ dialog.run { |response|
+ Gtk.timeout_remove(timeout)
+ if response == Gtk::Dialog::RESPONSE_OK
+ $modified = true
+ newval = hs.value.to_s
+ msg 3, "changing white balance to #{newval}"
+ dialog.destroy
+ return { :old => value, :new => newval }
else
- gen_thumbnails_subdir(origfile, xmldir, false, [ { 'filename' => destfile, 'size' => size } ], find_subalbum_info_type(xmldir))
+ $modified_pixbufs[thumbnail_img] ||= {}
+ $modified_pixbufs[thumbnail_img][:whitebalance] = value.to_f
+ dialog.destroy
+ return nil
end
+ }
+end
+
+def gen_real_thumbnail_core(type, origfile, destfile, xmldir, size, infotype)
+ system("rm -f '#{destfile}'")
+ #- type can be 'element' or 'subdir'
+ if type == 'element'
+ gen_thumbnails_element(origfile, xmldir, false, [ { 'filename' => destfile, 'size' => size } ])
+ else
+ gen_thumbnails_subdir(origfile, xmldir, false, [ { 'filename' => destfile, 'size' => size } ], infotype)
+ end
+end
+
+def gen_real_thumbnail(type, origfile, destfile, xmldir, size, img, infotype)
+ Thread.new {
+ push_mousecursor_wait
+ gen_real_thumbnail_core(type, origfile, destfile, xmldir, size, infotype)
img.set(destfile)
- $rotated_pixbufs[destfile] = { :orig => img.pixbuf, :pixbuf => img.pixbuf, :angle_to_orig => 0 }
+ $modified_pixbufs[destfile] = { :orig => img.pixbuf, :pixbuf => img.pixbuf, :angle_to_orig => 0 }
if entry2type(origfile) == 'video'
#- cleanup temp for videos
system("rm -f #{current_dest_dir}/screenshot.jpg000000.jpg")
changeimg.signal_connect('activate') { closures[:change].call }
menu.append( Gtk::SeparatorMenuItem.new)
end
- menu.append( r90 = Gtk::ImageMenuItem.new(utf8(_("Rotate clockwise"))))
+ menu.append(r90 = Gtk::ImageMenuItem.new(utf8(_("Rotate clockwise"))))
r90.image = Gtk::Image.new("#{$FPATH}/images/stock-rotate-90-16.png")
r90.signal_connect('activate') { closures[:rotate].call(90) }
- menu.append( r270 = Gtk::ImageMenuItem.new(utf8(_("Rotate counter-clockwise"))))
+ menu.append(r270 = Gtk::ImageMenuItem.new(utf8(_("Rotate counter-clockwise"))))
r270.image = Gtk::Image.new("#{$FPATH}/images/stock-rotate-270-16.png")
r270.signal_connect('activate') { closures[:rotate].call(-90) }
+ menu.append(whitebalance = Gtk::ImageMenuItem.new(utf8(_("Fix white-balance"))))
+ whitebalance.image = Gtk::Image.new("#{$FPATH}/images/stock-tool-color-balance-16.png")
+ whitebalance.signal_connect('activate') { closures[:whitebalance].call }
if type == 'video'
menu.append( Gtk::SeparatorMenuItem.new)
menu.append( color_swap = Gtk::ImageMenuItem.new(utf8(_("Red/blue color swap"))))
frame1 = Gtk::Frame.new
my_gen_real_thumbnail = proc {
- gen_real_thumbnail('element', from_utf8("#{$current_path}/#{filename}"), thumbnail_img, $xmldir, $default_size['thumbnails'], img)
+ gen_real_thumbnail('element', from_utf8("#{$current_path}/#{filename}"), thumbnail_img, $xmldir, $default_size['thumbnails'], img, '')
}
#- generate the thumbnail if missing (if image was rotated but booh was not relaunched)
- if !$rotated_pixbufs[thumbnail_img] && !File.exists?(thumbnail_img)
+ if !$modified_pixbufs[thumbnail_img] && !File.exists?(thumbnail_img)
frame1.add(img = Gtk::Image.new)
my_gen_real_thumbnail.call
else
- frame1.add(img = Gtk::Image.new($rotated_pixbufs[thumbnail_img] ? $rotated_pixbufs[thumbnail_img][:pixbuf] : thumbnail_img))
+ frame1.add(img = Gtk::Image.new($modified_pixbufs[thumbnail_img] ? $modified_pixbufs[thumbnail_img][:pixbuf] : thumbnail_img))
end
evtbox = Gtk::EventBox.new.add(Gtk::Alignment.new(0.5, 0.5, 0, 0).add(frame1.set_shadow_type(Gtk::SHADOW_ETCHED_OUT)))
end
}
+ whitebalance_and_cleanup = Proc.new {
+ if values = ask_whitebalance(from_utf8("#{$current_path}/#{filename}"), thumbnail_img, img,
+ $xmldir.elements["[@filename='#{filename}']"], '', $default_thumbnails[:x], $default_thumbnails[:y], '')
+ perform_change_whitebalance_and_cleanup = Proc.new { |val|
+ change_whitebalance($xmldir.elements["[@filename='#{filename}']"], '', val)
+ recalc_whitebalance(val, from_utf8("#{$current_path}/#{filename}"), thumbnail_img, img,
+ $xmldir.elements["[@filename='#{filename}']"], '', $default_thumbnails[:x], $default_thumbnails[:y], '')
+ cleanup_all_thumbnails.call
+ }
+ perform_change_whitebalance_and_cleanup.call(values[:new])
+
+ save_undo(_("fix white balance"),
+ Proc.new {
+ perform_change_whitebalance_and_cleanup.call(values[:old])
+ $notebook.set_page(1)
+ Proc.new {
+ perform_change_whitebalance_and_cleanup.call(values[:new])
+ $notebook.set_page(1)
+ }
+ })
+ end
+ }
+
enhance_and_cleanup = Proc.new {
perform_enhance_and_cleanup = Proc.new {
enhance($xmldir.elements["[@filename='#{filename}']"], '')
if event.event_type == Gdk::Event::BUTTON_PRESS && event.button == 3
popup_thumbnail_menu(event, ['delete'], type, $xmldir.elements["[@filename='#{filename}']"], '',
{ :rotate => rotate_and_cleanup, :color_swap => color_swap_and_cleanup, :enhance => enhance_and_cleanup,
- :frame_offset => change_frame_offset_and_cleanup, :delete => delete })
+ :frame_offset => change_frame_offset_and_cleanup, :delete => delete, :whitebalance => whitebalance_and_cleanup })
end
if event.event_type == Gdk::Event::BUTTON2_PRESS && event.button == 1
view_element(filename)
img = nil
my_gen_real_thumbnail = proc {
- gen_real_thumbnail('subdir', captionfile, thumbnail_file, xmldir, $albums_thumbnail_size, img)
+ gen_real_thumbnail('subdir', captionfile, thumbnail_file, xmldir, $albums_thumbnail_size, img, infotype)
}
- if !$rotated_pixbufs[thumbnail_file] && !File.exists?(thumbnail_file)
+ if !$modified_pixbufs[thumbnail_file] && !File.exists?(thumbnail_file)
f.add(img = Gtk::Image.new)
my_gen_real_thumbnail.call
else
- f.add(img = Gtk::Image.new($rotated_pixbufs[thumbnail_file] ? $rotated_pixbufs[thumbnail_file][:pixbuf] : thumbnail_file))
+ f.add(img = Gtk::Image.new($modified_pixbufs[thumbnail_file] ? $modified_pixbufs[thumbnail_file][:pixbuf] : thumbnail_file))
end
hbox.pack_end(Gtk::Alignment.new(0.5, 0.5, 0, 0).add(evtbox = Gtk::EventBox.new.add(f)), false, false)
$subalbums.attach(hbox,
msg 3, "new captionfile is: #{fc.filename}"
perform_changefile = Proc.new {
$subalbums_edits[xmldir.attributes['path']][:captionfile] = captionfile = new_file
- $rotated_pixbufs.delete(thumbnail_file)
+ $modified_pixbufs.delete(thumbnail_file)
xmldir.delete_attribute("#{infotype}-rotate")
xmldir.delete_attribute("#{infotype}-color-swap")
xmldir.delete_attribute("#{infotype}-enhance")
end
}
+ whitebalance_and_cleanup = Proc.new {
+ if values = ask_whitebalance(captionfile, thumbnail_file, img, xmldir, "#{infotype}-",
+ $default_albums_thumbnails[:x], $default_albums_thumbnails[:y], infotype)
+ perform_change_whitebalance_and_cleanup = Proc.new { |val|
+ change_whitebalance(xmldir, "#{infotype}-", val)
+ recalc_whitebalance(val, captionfile, thumbnail_file, img, xmldir, "#{infotype}-",
+ $default_albums_thumbnails[:x], $default_albums_thumbnails[:y], infotype)
+ system("rm -f '#{thumbnail_file}'")
+ }
+ perform_change_whitebalance_and_cleanup.call(values[:new])
+
+ save_undo(_("fix white balance"),
+ Proc.new {
+ perform_change_whitebalance_and_cleanup.call(values[:old])
+ $notebook.set_page(0)
+ Proc.new {
+ perform_change_whitebalance_and_cleanup.call(values[:new])
+ $notebook.set_page(0)
+ }
+ })
+ end
+ }
+
enhance_and_cleanup = Proc.new {
perform_enhance_and_cleanup = Proc.new {
enhance(xmldir, "#{infotype}-")
if event.event_type == Gdk::Event::BUTTON_PRESS && event.button == 3
popup_thumbnail_menu(event, ['change_image'], entry2type(captionfile), xmldir, "#{infotype}-",
{ :change => change_image, :rotate => rotate_and_cleanup, :enhance => enhance_and_cleanup,
- :color_swap => color_swap_and_cleanup, :frame_offset => change_frame_offset_and_cleanup })
+ :color_swap => color_swap_and_cleanup, :frame_offset => change_frame_offset_and_cleanup, :whitebalance => whitebalance_and_cleanup })
end
if event.event_type == Gdk::Event::BUTTON2_PRESS && event.button == 1
change_image.call
$filename = nil
$modified = false
$current_path = nil #- invalidate
- $rotated_pixbufs = {}
+ $modified_pixbufs = {}
$albums_ts.clear
$autotable.clear
$subalbums_vb.children.each { |chld|
Gdk::Pixbuf::ROTATE_NONE)
end
- def gen_thumbnails_element(orig, xmldir, allow_background, dests)
- gen_thumbnails(orig, xmldir, allow_background, dests, xmldir.elements["[@filename='#{utf8(File.basename(orig))}']"], '')
+ def gen_thumbnails_element(orig, xmldirorelem, allow_background, dests)
+ if xmldirorelem.name == 'dir'
+ xmldirorelem = xmldirorelem.elements["[@filename='#{utf8(File.basename(orig))}']"]
+ end
+ gen_thumbnails(orig, allow_background, dests, xmldirorelem, '')
end
- def gen_thumbnails_subdir(orig, xmldir, allow_background, dests, type)
+ def gen_thumbnails_subdir(orig, xmldirorelem, allow_background, dests, type)
#- type can be `subdirs' or `thumbnails'
- gen_thumbnails(orig, xmldir, allow_background, dests, xmldir, type + '-')
+ gen_thumbnails(orig, allow_background, dests, xmldirorelem, type + '-')
end
- def gen_thumbnails(orig, xmldir, allow_background, dests, felem, attributes_prefix)
+ def gen_thumbnails(orig, allow_background, dests, felem, attributes_prefix)
if !dests.detect { |dest| !File.exists?(dest['filename']) }
return true
end
convert_options = ''
+ dest_dir = make_dest_filename(File.dirname(dests[0]['filename']))
if entry2type(orig) == 'image'
if felem
+ if whitebalance = felem.attributes["#{attributes_prefix}white-balance"]
+ neworig = "#{dest_dir}/#{File.basename(orig)}-whitebalance#{whitebalance}.jpg"
+ cmd = "booh-fix-whitebalance '#{orig}' '#{neworig}' #{whitebalance}"
+ sys(cmd)
+ if File.exists?(neworig)
+ orig = neworig
+ allow_background = false
+ end
+ end
rotate = felem.attributes["#{attributes_prefix}rotate"]
if !rotate
felem.add_attribute("#{attributes_prefix}rotate", rotate = guess_rotate(orig).to_i)
#- don't resize if image is already smaller than destination size
if size = get_image_size(orig)
dest['size'] =~ /(\d+)x(\d+)/
- if (rotate == "90" || rotate == "270") && (size[:x] < $2.to_i || size[:y] < $1.to_i) ||
- size[:x] < $1.to_i || size[:y] < $2.to_i
+ if (rotate == "90" || rotate == "270" || size[:x] < size[:y]) ? size[:y] < $1.to_i : size[:x] < $1.to_i
cmd = "#{$convert} #{convert_options} '#{orig}' '#{dest['filename']}'"
end
end
end
end
end
+ if neworig
+ system("rm -f '#{neworig}'")
+ end
return true
elsif entry2type(orig) == 'video'
- dest_dir = make_dest_filename(File.dirname(dests[0]['filename']))
if felem
#- frame-offset is an attribute that allows to specify which frame to use for the thumbnail
frame_offset = felem.attributes["#{attributes_prefix}frame-offset"]
convert_options += ($config['convert-enhance'] || $convert_enhance) + " "
end
end
+ orig = "#{dest_dir}/screenshot.jpg000000.jpg"
for dest in dests
- if !File.exists?("#{dest_dir}/screenshot.jpg000000.jpg")
+ if !File.exists?(orig)
transcode_options = ''
if felem
if felem.attributes["#{attributes_prefix}color-swap"]
return false
end
end
-
+ if felem && whitebalance = felem.attributes["#{attributes_prefix}white-balance"]
+ if whitebalance.to_f != 0
+ neworig = "#{dest_dir}/#{File.basename(orig)}-whitebalance#{whitebalance}.jpg"
+ cmd = "booh-fix-whitebalance '#{orig}' '#{neworig}' #{whitebalance}"
+ sys(cmd)
+ if File.exists?(neworig)
+ orig = neworig
+ end
+ end
+ end
end
if !File.exists?(dest['filename'])
- sys("#{$convert} #{convert_options}-size #{dest['size']} -resize #{dest['size']} #{dest_dir}/screenshot.jpg000000.jpg '#{dest['filename']}'")
+ sys("#{$convert} #{convert_options}-size #{dest['size']} -resize #{dest['size']} '#{orig}' '#{dest['filename']}'")
end
end
+ if neworig
+ system("rm -f '#{neworig}'")
+ end
return true
end
end
def max(a, b)
a > b ? a : b
end
-
end
class File