Commit a6ab975a authored by Jim Nelson's avatar Jim Nelson

#774: In full-window/fullscreen mode, images now shown in actual size. In...

#774: In full-window/fullscreen mode, images now shown in actual size.  In slideshow, images shown fullscreen.
parent 146d6753
......@@ -2,12 +2,12 @@ The Shotwell team would like to thank the following contributors:
Joeny Ang <ang.joeny@gmail.com>
Matthias Clasen <matthias.clasen@gmail.com>
Emanuele Grande <caccolangrifata@gmail.com>
David Jeske <davidj@gmail.com>
Shan Xiong <shan.xiong@gmail.com>
Translations courtesy of:
Emanuele Grande <caccolangrifata@gmail.com>
Piotr Drąg <piotrdrag@gmail.com>
Ingo Lütkebohle <iluetkeb@techfak.uni-bielefeld.de>
Mattias Põldaru <mahfiaz@gmail.com>
......
......@@ -179,38 +179,40 @@ public struct Scaling {
private ScaleConstraint constraint;
private int scale;
private Dimensions viewport;
private bool scale_up_to_viewport;
private Scaling(ScaleConstraint constraint, int scale, Dimensions viewport) {
private Scaling(ScaleConstraint constraint, int scale, Dimensions viewport, bool scale_up_to_viewport) {
this.constraint = constraint;
this.scale = scale;
this.viewport = viewport;
this.scale_up_to_viewport = scale_up_to_viewport;
}
public static Scaling for_original() {
return Scaling(ScaleConstraint.ORIGINAL, NO_SCALE, Dimensions());
return Scaling(ScaleConstraint.ORIGINAL, NO_SCALE, Dimensions(), false);
}
public static Scaling for_screen(Gtk.Window window) {
return for_viewport(get_screen_dimensions(window));
public static Scaling for_screen(Gtk.Window window, bool scale_up_for_viewport) {
return for_viewport(get_screen_dimensions(window), scale_up_for_viewport);
}
public static Scaling for_best_fit(int pixels) {
assert(pixels > 0);
return Scaling(ScaleConstraint.DIMENSIONS, pixels, Dimensions());
return Scaling(ScaleConstraint.DIMENSIONS, pixels, Dimensions(), false);
}
public static Scaling for_viewport(Dimensions viewport) {
public static Scaling for_viewport(Dimensions viewport, bool scale_up_for_viewport) {
assert(viewport.has_area());
return Scaling(ScaleConstraint.DIMENSIONS, NO_SCALE, viewport);
return Scaling(ScaleConstraint.DIMENSIONS, NO_SCALE, viewport, scale_up_for_viewport);
}
public static Scaling for_widget(Gtk.Widget widget) {
public static Scaling for_widget(Gtk.Widget widget, bool scale_up_for_viewport) {
Dimensions viewport = Dimensions.for_allocation(widget.allocation);
assert(viewport.has_area());
return Scaling(ScaleConstraint.DIMENSIONS, NO_SCALE, viewport);
return Scaling(ScaleConstraint.DIMENSIONS, NO_SCALE, viewport, scale_up_for_viewport);
}
private static Dimensions get_screen_dimensions(Gtk.Window window) {
......@@ -218,7 +220,7 @@ public struct Scaling {
return Dimensions(screen.get_width(), screen.get_height());
}
private int scale_to_pixels() {
return (scale >= 0) ? scale : 0;
}
......@@ -252,7 +254,11 @@ public struct Scaling {
return false;
assert(viewport.has_area());
scaled = original.get_scaled_proportional(viewport);
if (!scale_up_to_viewport && original.width < viewport.width && original.height < viewport.height)
scaled = original;
else
scaled = original.get_scaled_proportional(viewport);
return true;
}
......@@ -294,7 +300,8 @@ public struct Scaling {
else if (scale != NO_SCALE)
return "scaling: best-fit (%d pixels)".printf(scale_to_pixels());
else
return "scaling: viewport %s".printf(viewport.to_string());
return "scaling: viewport %s (%s)".printf(viewport.to_string(),
scale_up_to_viewport ? "scaled up" : "not scaled up");
}
public bool equals(Scaling scaling) {
......
......@@ -177,7 +177,7 @@ public abstract class PhotoCanvas {
int width, height;
drawable.get_size(out width, out height);
return Scaling.for_viewport(Dimensions(width, height));
return Scaling.for_viewport(Dimensions(width, height), false);
}
public void set_drawable(Gdk.GC default_gc, Gdk.Drawable drawable) {
......@@ -309,7 +309,8 @@ public abstract class EditingTool {
public signal void deactivated();
public signal void applied(Command? command, Gdk.Pixbuf? new_pixbuf, bool needs_improvement);
public signal void applied(Command? command, Gdk.Pixbuf? new_pixbuf, Dimensions new_max_dim,
bool needs_improvement);
public signal void cancelled();
......@@ -366,9 +367,13 @@ public abstract class EditingTool {
// the tool is on the screen, and before paint_full() is hooked in. It also means the PhotoCanvas
// will have this pixbuf rather than one from the Photo class.
//
// If returns non-null, should also fill max_dim with the maximum dimensions of the original
// image, as the editing host may not always scale images up to fit the viewport.
//
// Note this this method doesn't need to be returning the "proper" pixbuf on-the-fly (i.e.
// a pixbuf with unsaved tool edits in it). That can be handled in the paint() virtual method.
public virtual Gdk.Pixbuf? get_display_pixbuf(Scaling scaling, TransformablePhoto photo) throws Error {
public virtual Gdk.Pixbuf? get_display_pixbuf(Scaling scaling, TransformablePhoto photo,
out Dimensions max_dim) throws Error {
return null;
}
......@@ -928,7 +933,7 @@ public class CropTool : EditingTool {
bind_window_handlers();
// obtain crop dimensions and paint against the uncropped photo
Dimensions uncropped_dim = canvas.get_photo().get_uncropped_dimensions();
Dimensions uncropped_dim = canvas.get_photo().get_original_dimensions();
Box crop;
if (!canvas.get_photo().get_crop(out crop)) {
......@@ -1026,10 +1031,16 @@ public class CropTool : EditingTool {
return crop_tool_window;
}
public override Gdk.Pixbuf? get_display_pixbuf(Scaling scaling, TransformablePhoto photo) throws Error {
public override Gdk.Pixbuf? get_display_pixbuf(Scaling scaling, TransformablePhoto photo,
out Dimensions max_dim) throws Error {
// show the uncropped photo for editing, but return null if no crop so the current pixbuf
// is used
return photo.has_crop() ? photo.get_pixbuf_with_exceptions(scaling, TransformablePhoto.Exception.CROP) : null;
if (!photo.has_crop())
return null;
max_dim = photo.get_original_dimensions();
return photo.get_pixbuf_with_exceptions(scaling, TransformablePhoto.Exception.CROP);
}
private void prepare_gc(Gdk.GC default_gc, Gdk.Drawable drawable) {
......@@ -1064,7 +1075,7 @@ public class CropTool : EditingTool {
private void on_resized_pixbuf(Dimensions old_dim, Gdk.Pixbuf scaled, Gdk.Rectangle scaled_position) {
Dimensions new_dim = Dimensions.for_pixbuf(scaled);
Dimensions uncropped_dim = canvas.get_photo().get_uncropped_dimensions();
Dimensions uncropped_dim = canvas.get_photo().get_original_dimensions();
// rescale to full crop
Box crop = scaled_crop.get_scaled_similar(old_dim, uncropped_dim);
......@@ -1138,7 +1149,7 @@ public class CropTool : EditingTool {
// scale screen-coordinate crop to photo's coordinate system
Box crop = scaled_crop.get_scaled_similar(
Dimensions.for_rectangle(canvas.get_scaled_pixbuf_position()),
canvas.get_photo().get_uncropped_dimensions());
canvas.get_photo().get_original_dimensions());
// crop the current pixbuf and offer it to the editing host
Gdk.Pixbuf cropped = new Gdk.Pixbuf.subpixbuf(canvas.get_scaled_pixbuf(), scaled_crop.left,
......@@ -1146,7 +1157,8 @@ public class CropTool : EditingTool {
// signal host; we have a cropped image, but it will be scaled upward, and so a better one
// should be fetched
applied(new CropCommand(canvas.get_photo(), crop, TOOL_LABEL, TOOL_TOOLTIP), cropped, true);
applied(new CropCommand(canvas.get_photo(), crop, TOOL_LABEL, TOOL_TOOLTIP), cropped,
crop.get_dimensions(), true);
}
private void update_cursor(int x, int y) {
......@@ -1798,7 +1810,7 @@ public class RedeyeTool : EditingTool {
}
private void on_close() {
applied(null, current_pixbuf, false);
applied(null, current_pixbuf, canvas.get_photo().get_dimensions(), false);
}
private void on_canvas_resize() {
......@@ -2337,10 +2349,14 @@ public class AdjustTool : EditingTool {
canvas.paint_pixbuf(draw_to_pixbuf);
}
public override Gdk.Pixbuf? get_display_pixbuf(Scaling scaling, TransformablePhoto photo) throws Error {
return photo.has_color_adjustments()
? photo.get_pixbuf_with_exceptions(scaling, TransformablePhoto.Exception.ADJUST)
: null;
public override Gdk.Pixbuf? get_display_pixbuf(Scaling scaling, TransformablePhoto photo,
out Dimensions max_dim) throws Error {
if (!photo.has_color_adjustments())
return null;
max_dim = photo.get_dimensions();
return photo.get_pixbuf_with_exceptions(scaling, TransformablePhoto.Exception.ADJUST);
}
private void on_reset() {
......@@ -2354,7 +2370,7 @@ public class AdjustTool : EditingTool {
get_tool_window().hide();
applied(new AdjustColorsCommand(canvas.get_photo(), transformations, TOOL_LABEL, TOOL_TOOLTIP),
draw_to_pixbuf, false);
draw_to_pixbuf, canvas.get_photo().get_dimensions(), false);
}
private void update_transformations(PixelTransformationBundle new_transformations) {
......
......@@ -987,7 +987,7 @@ public class ImportQueuePage : SinglePhotoPage {
private uint64 total_bytes = 0;
public ImportQueuePage() {
base(_("Importing..."));
base(_("Importing..."), false);
init_ui("import_queue.ui", "/ImportQueueMenuBar", "ImportQueueActionGroup",
create_actions());
......@@ -1083,7 +1083,7 @@ public class ImportQueuePage : SinglePhotoPage {
private void on_imported(LibraryPhoto photo) {
try {
set_pixbuf(photo.get_pixbuf(get_canvas_scaling()));
set_pixbuf(photo.get_pixbuf(get_canvas_scaling()), photo.get_dimensions());
} catch (Error err) {
warning("%s", err.message);
}
......
......@@ -1231,15 +1231,19 @@ public abstract class SinglePhotoPage : Page {
protected Gtk.Viewport viewport = new Gtk.Viewport(null, null);
protected Gdk.GC text_gc = null;
private bool scale_up_to_viewport;
private Gdk.Pixmap pixmap = null;
private Dimensions pixmap_dim = Dimensions();
private Gdk.Pixbuf unscaled = null;
private Dimensions max_dim = Dimensions();
private Gdk.Pixbuf scaled = null;
private Gdk.Rectangle scaled_pos = Gdk.Rectangle();
public SinglePhotoPage(string page_name) {
public SinglePhotoPage(string page_name, bool scale_up_to_viewport) {
base(page_name);
this.scale_up_to_viewport = scale_up_to_viewport;
// With the current code automatically resizing the image to the viewport, scrollbars
// should never be shown, but this may change if/when zooming is supported
set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC);
......@@ -1281,8 +1285,13 @@ public abstract class SinglePhotoPage : Page {
set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC);
}
public void set_pixbuf(Gdk.Pixbuf unscaled) {
// max_dim represents the maximum size of the original pixbuf (i.e. pixbuf may be scaled and
// the caller capable of producing larger ones depending on the viewport size). max_dim
// is used when scale_up_to_viewport is set to true. Pass a Dimensions with no area if
// max_dim should be ignored (i.e. scale_up_to_viewport is false).
public void set_pixbuf(Gdk.Pixbuf unscaled, Dimensions max_dim) {
this.unscaled = unscaled;
this.max_dim = max_dim;
scaled = null;
// need to make sure this has happened
......@@ -1293,6 +1302,7 @@ public abstract class SinglePhotoPage : Page {
public void blank_display() {
unscaled = null;
max_dim = Dimensions();
scaled = null;
pixmap = null;
......@@ -1312,8 +1322,8 @@ public abstract class SinglePhotoPage : Page {
}
public Scaling get_canvas_scaling() {
return (get_container() is FullscreenWindow) ? Scaling.for_screen(get_container())
: Scaling.for_widget(viewport);
return (get_container() is FullscreenWindow) ? Scaling.for_screen(get_container(), scale_up_to_viewport)
: Scaling.for_widget(viewport, scale_up_to_viewport);
}
public Gdk.Pixbuf? get_unscaled_pixbuf() {
......@@ -1345,7 +1355,7 @@ public abstract class SinglePhotoPage : Page {
private void on_viewport_resize() {
// do fast repaints while resizing
internal_repaint(FAST_INTERP);
internal_repaint(true);
}
private override void on_resize_finished(Gdk.Rectangle rect) {
......@@ -1385,10 +1395,10 @@ public abstract class SinglePhotoPage : Page {
}
public void repaint() {
internal_repaint(QUALITY_INTERP);
internal_repaint(false);
}
private void internal_repaint(Gdk.InterpType interp) {
private void internal_repaint(bool fast) {
// if not in view, assume a full repaint needed in future but do nothing more
if (!is_in_view()) {
pixmap = null;
......@@ -1424,8 +1434,15 @@ public abstract class SinglePhotoPage : Page {
}
if (new_pixbuf || new_pixmap) {
// determine size of pixbuf that will fit on the canvas
Dimensions scaled_dim = Dimensions.for_pixbuf(unscaled).get_scaled_proportional(pixmap_dim);
Dimensions unscaled_dim = Dimensions.for_pixbuf(unscaled);
// determine scaled size of pixbuf ... if a max dimensions is set and not scaling up,
// respect it
Dimensions scaled_dim = Dimensions();
if (!scale_up_to_viewport && max_dim.has_area() && max_dim.width < width && max_dim.height < height)
scaled_dim = max_dim;
else
scaled_dim = unscaled_dim.get_scaled_proportional(pixmap_dim);
assert(width >= scaled_dim.width);
assert(height >= scaled_dim.height);
......@@ -1440,6 +1457,8 @@ public abstract class SinglePhotoPage : Page {
pixmap.draw_rectangle(canvas.style.black_gc, true, 0, 0, width, height);
}
Gdk.InterpType interp = (fast) ? FAST_INTERP : QUALITY_INTERP;
// rescale if canvas rescaled or better quality is requested
if (scaled == null) {
scaled = resize_pixbuf(unscaled, Dimensions.for_rectangle(scaled_pos), interp);
......
......@@ -606,7 +606,7 @@ public abstract class TransformablePhoto: PhotoSource {
if (get_crop(out crop))
return crop.get_dimensions();
return get_uncropped_dimensions();
return get_original_dimensions();
}
// This method *must* be called with row locked.
......@@ -1509,7 +1509,7 @@ public abstract class TransformablePhoto: PhotoSource {
// Aggregate/helper/translation functions
// Returns uncropped (but rotated) dimensions
public Dimensions get_uncropped_dimensions() {
public Dimensions get_original_dimensions() {
Dimensions dim = get_raw_dimensions();
Orientation orientation = get_orientation();
......
......@@ -54,7 +54,7 @@ public abstract class EditingHostPage : SinglePhotoPage {
public signal void photo_changed(TransformablePhoto? old_photo, TransformablePhoto new_photo);
public EditingHostPage(SourceCollection sources, string name, bool use_readahead) {
base(name);
base(name, false);
this.sources = sources;
this.use_readahead = use_readahead;
......@@ -221,10 +221,11 @@ public abstract class EditingHostPage : SinglePhotoPage {
if (pixbuf != null) {
// if no tool, use the pixbuf directly, otherwise, let the tool decide what should be
// displayed
Dimensions max_dim = photo.get_dimensions();
if (current_tool != null) {
try {
Gdk.Pixbuf? tool_pixbuf = current_tool.get_display_pixbuf(get_canvas_scaling(),
photo);
photo, out max_dim);
if (tool_pixbuf != null)
pixbuf = tool_pixbuf;
} catch(Error err) {
......@@ -235,7 +236,7 @@ public abstract class EditingHostPage : SinglePhotoPage {
}
}
set_pixbuf(pixbuf);
set_pixbuf(pixbuf, max_dim);
pixbuf_dirty = false;
} else if (err != null) {
set_photo_missing(true);
......@@ -409,7 +410,7 @@ public abstract class EditingHostPage : SinglePhotoPage {
pixbuf = pixbuf.composite_color_simple(pixbuf.get_width(), pixbuf.get_height(),
Gdk.InterpType.NEAREST, 100, 2, 0, 0);
set_pixbuf(pixbuf);
set_pixbuf(pixbuf, get_photo().get_dimensions());
} catch (GLib.Error err) {
warning("%s", err.message);
}
......@@ -471,7 +472,7 @@ public abstract class EditingHostPage : SinglePhotoPage {
private void quick_update_pixbuf() {
Gdk.Pixbuf pixbuf = cache.get_ready_pixbuf(get_photo());
if (pixbuf != null) {
set_pixbuf(pixbuf);
set_pixbuf(pixbuf, get_photo().get_dimensions());
pixbuf_dirty = false;
return;
......@@ -484,7 +485,7 @@ public abstract class EditingHostPage : SinglePhotoPage {
// throw a resized large thumbnail up to get an image on the screen quickly,
// and when ready decode and display the full image
try {
set_pixbuf(get_photo().get_preview_pixbuf(scaling));
set_pixbuf(get_photo().get_preview_pixbuf(scaling), get_photo().get_dimensions());
} catch (Error err) {
warning("%s", err.message);
}
......@@ -501,10 +502,11 @@ public abstract class EditingHostPage : SinglePhotoPage {
Timer timer = new Timer();
#endif
Gdk.Pixbuf pixbuf = null;
Dimensions max_dim = get_photo().get_dimensions();
try {
if (current_tool != null)
pixbuf = current_tool.get_display_pixbuf(get_canvas_scaling(), get_photo());
pixbuf = current_tool.get_display_pixbuf(get_canvas_scaling(), get_photo(), out max_dim);
if (pixbuf == null)
pixbuf = cache.fetch(get_photo());
......@@ -514,7 +516,7 @@ public abstract class EditingHostPage : SinglePhotoPage {
}
if (!photo_missing) {
set_pixbuf(pixbuf);
set_pixbuf(pixbuf, max_dim);
pixbuf_dirty = false;
}
......@@ -556,7 +558,7 @@ public abstract class EditingHostPage : SinglePhotoPage {
// store what's currently displayed only for the duration of the shift pressing
swapped = get_unscaled_pixbuf();
set_pixbuf(original);
set_pixbuf(original, get_photo().get_original_dimensions());
}
}
......@@ -565,7 +567,7 @@ public abstract class EditingHostPage : SinglePhotoPage {
private override bool on_shift_released(Gdk.EventKey? event) {
if (current_tool == null && swapped != null) {
set_pixbuf(swapped);
set_pixbuf(swapped, get_photo().get_dimensions());
// only store swapped once; it'll be set the next on_shift_pressed
swapped = null;
......@@ -582,10 +584,11 @@ public abstract class EditingHostPage : SinglePhotoPage {
// save current pixbuf to use if user cancels operation
cancel_editing_pixbuf = get_unscaled_pixbuf();
// see if the tool wants a different pixbuf displayed
// see if the tool wants a different pixbuf displayed and what its max dimensions should be
Gdk.Pixbuf unscaled;
Dimensions max_dim = get_photo().get_dimensions();
try {
unscaled = tool.get_display_pixbuf(get_canvas_scaling(), get_photo());
unscaled = tool.get_display_pixbuf(get_canvas_scaling(), get_photo(), out max_dim);
} catch (Error err) {
warning("%s", err.message);
set_photo_missing(true);
......@@ -598,7 +601,7 @@ public abstract class EditingHostPage : SinglePhotoPage {
}
if (unscaled != null)
set_pixbuf(unscaled);
set_pixbuf(unscaled, max_dim);
// create the PhotoCanvas object for a two-way interface to the tool
PhotoCanvas photo_canvas = new EditingHostCanvas(this);
......@@ -615,7 +618,7 @@ public abstract class EditingHostPage : SinglePhotoPage {
}
private void deactivate_tool(Command? command = null, Gdk.Pixbuf? new_pixbuf = null,
bool needs_improvement = false) {
Dimensions new_max_dim = Dimensions(), bool needs_improvement = false) {
if (current_tool == null)
return;
......@@ -642,7 +645,7 @@ public abstract class EditingHostPage : SinglePhotoPage {
}
if (replacement != null)
set_pixbuf(replacement);
set_pixbuf(replacement, new_max_dim);
cancel_editing_pixbuf = null;
// if this is a rough pixbuf, schedule an improvement
......@@ -990,8 +993,9 @@ public abstract class EditingHostPage : SinglePhotoPage {
current_editing_toggle.active = false;
}
private void on_tool_applied(Command? command, Gdk.Pixbuf? new_pixbuf, bool needs_improvement) {
deactivate_tool(command, new_pixbuf, needs_improvement);
private void on_tool_applied(Command? command, Gdk.Pixbuf? new_pixbuf, Dimensions new_max_dim,
bool needs_improvement) {
deactivate_tool(command, new_pixbuf, new_max_dim, needs_improvement);
}
private void on_tool_cancelled() {
......
......@@ -86,7 +86,7 @@ class SlideshowPage : SinglePhotoPage {
}
public SlideshowPage(ViewCollection controller, Thumbnail start) {
base(_("Slideshow"));
base(_("Slideshow"), true);
this.controller = controller;
current = start;
......@@ -132,7 +132,7 @@ class SlideshowPage : SinglePhotoPage {
if (!get_fullscreen_pixbuf(current, true, out current, out pixbuf))
return;
set_pixbuf(pixbuf);
set_pixbuf(pixbuf, current.get_photo().get_dimensions());
// start the auto-advance timer
Timeout.add(CHECK_ADVANCE_MSEC, auto_advance);
......@@ -159,7 +159,7 @@ class SlideshowPage : SinglePhotoPage {
for (;;) {
try {
// Fails if a photo source file is missing.
next_pixbuf = next.get_photo().get_pixbuf(Scaling.for_screen(get_container()));
next_pixbuf = next.get_photo().get_pixbuf(Scaling.for_screen(get_container(), true));
} catch (Error err) {
warning("%s", err.message);
......@@ -233,7 +233,7 @@ class SlideshowPage : SinglePhotoPage {
}
if (pixbuf != null)
set_pixbuf(pixbuf);
set_pixbuf(pixbuf, current.get_photo().get_dimensions());
// reset the timer
timer.start();
......@@ -252,7 +252,7 @@ class SlideshowPage : SinglePhotoPage {
// set pixbuf
Gdk.Pixbuf next_pixbuf;
get_fullscreen_pixbuf(current, forward, out current, out next_pixbuf);
set_pixbuf(next_pixbuf);
set_pixbuf(next_pixbuf, current.get_photo().get_dimensions());
// reset the advance timer
timer.start();
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment