Commit c1e79cd0 authored by Jim Nelson's avatar Jim Nelson

#60: Crop tool.

parent b5a223f6
......@@ -66,6 +66,10 @@ public struct Box {
return Box(left + xofs, top + yofs, right + xofs, bottom + yofs);
}
public Dimensions get_dimensions() {
return Dimensions(get_width(), get_height());
}
public Gdk.Rectangle get_rectangle() {
Gdk.Rectangle rect = Gdk.Rectangle();
rect.x = left;
......
......@@ -480,7 +480,11 @@ public class PhotoTable : DatabaseTable {
return null;
}
return stmt.column_text(0);
string trans = stmt.column_text(0);
if (trans != null && trans.length == 0)
trans = null;
return trans;
}
private bool set_raw_transformations(PhotoID photo_id, string trans) {
......@@ -562,7 +566,7 @@ public class PhotoTable : DatabaseTable {
public bool remove_transformation(PhotoID photo_id, string object) {
string trans = get_raw_transformations(photo_id);
if (trans == null || trans.length == 0)
if (trans == null)
return true;
try {
......@@ -575,7 +579,6 @@ public class PhotoTable : DatabaseTable {
int length;
trans = keyfile.to_data(out length);
assert(trans != null);
assert(trans.length > 0);
} catch (Error err) {
error("%s", err.message);
......
......@@ -164,16 +164,21 @@ public class Photo : Object {
photo_altered();
}
public Dimensions get_dimensions() {
// Returns uncropped (but rotated) dimensions
public Dimensions get_uncropped_dimensions() {
Dimensions dim = photo_table.get_dimensions(photo_id);
dim = dim.get_rotated(photo_table.get_orientation(photo_id));
return dim.get_rotated(photo_table.get_orientation(photo_id));
}
// Returns cropped and rotate dimensions
public Dimensions get_dimensions() {
Box crop;
if (get_crop(out crop)) {
return crop.get_dimensions();
}
return dim;
return get_uncropped_dimensions();
}
public Dimensions get_scaled_dimensions(int scale) {
......@@ -214,6 +219,24 @@ public class Photo : Object {
return res;
}
public bool remove_crop() {
bool res = photo_table.remove_transformation(photo_id, "crop");
if (res)
photo_altered();
return res;
}
// Returns uncropped pixbuf with modifications applied
public Gdk.Pixbuf get_uncropped_pixbuf() throws Error {
Gdk.Pixbuf pixbuf = get_unmodified_pixbuf();
// orientation
pixbuf = rotate_to_exif(pixbuf, photo_table.get_orientation(photo_id));
return pixbuf;
}
// Returns unscaled pixbuf with all modifications applied
public Gdk.Pixbuf get_pixbuf() throws Error {
Gdk.Pixbuf pixbuf = get_unmodified_pixbuf();
......@@ -224,6 +247,8 @@ public class Photo : Object {
// crop
Box crop;
if (get_crop(out crop)) {
pixbuf = new Gdk.Pixbuf.subpixbuf(pixbuf, crop.left, crop.top, crop.get_width(),
crop.get_height());
}
return pixbuf;
......
......@@ -163,6 +163,10 @@ public class PhotoPage : Page {
return thumbnail;
}
public override void switching_from() {
deactivate_crop();
}
public void display(CheckerboardPage controller, Thumbnail thumbnail) {
this.controller = controller;
this.thumbnail = thumbnail;
......@@ -173,6 +177,10 @@ public class PhotoPage : Page {
private void update_display() {
photo = thumbnail.get_photo();
photo.altered += on_photo_altered;
// fetch and cache original unscaled pixbuf ... this is more efficient than going to Photo
// for each resize, as Photo doesn't itself cache it
original = photo.get_pixbuf();
// flush old image
......@@ -200,6 +208,20 @@ public class PhotoPage : Page {
AppWindow.get_instance().switch_to_page(controller);
}
private void on_photo_altered(Photo p) {
debug("on_photo_altered");
assert(photo.equals(p));
// fetch a new original to work with
original = photo.get_pixbuf();
// flush pixmap to force redraw
pixmap = null;
repaint();
}
private bool on_canvas_motion(Gtk.DrawingArea da, Gdk.EventMotion event) {
if (!show_crop)
return false;
......@@ -529,7 +551,7 @@ public class PhotoPage : Page {
// determine size of pixbuf that will fit on the canvas
Dimensions old_pixbuf_dim = Dimensions.for_rectangle(pixbuf_rect);
Dimensions pixbuf_dim = photo.get_dimensions().get_scaled_proportional(pixmap_dim);
Dimensions pixbuf_dim = Dimensions.for_pixbuf(original).get_scaled_proportional(pixmap_dim);
// center pixbuf on the canvas
int photo_x = (width - pixbuf_dim.width) / 2;
......@@ -617,15 +639,10 @@ public class PhotoPage : Page {
}
private void rotate(Photo.Rotation rotation) {
photo.rotate(rotation);
// fetch a new original to work with
original = photo.get_pixbuf();
deactivate_crop();
// flush pixmap to force redraw
pixmap = null;
repaint();
// let the signal generate a repaint
photo.rotate(rotation);
}
private void on_rotate_clockwise() {
......@@ -661,23 +678,34 @@ public class PhotoPage : Page {
activate_crop();
else
deactivate_crop();
repaint();
}
private void activate_crop() {
Dimensions photo_dim = photo.get_dimensions();
if (show_crop)
return;
// show uncropped photo for editing
original = photo.get_uncropped_pixbuf();
Dimensions photo_dim = photo.get_uncropped_dimensions();
// flush to force repaint
pixmap = null;
// initialize the actual crop in absolute coordinates, not relative
// to the photo's position on the canvas
Box crop = Box(CROP_INIT_X, CROP_INIT_Y, photo_dim.width - CROP_INIT_X,
photo_dim.height - CROP_INIT_Y);
Box crop;
if (!photo.get_crop(out crop)) {
// initialize the actual crop in absolute coordinates, not relative
// to the photo's position on the canvas
crop = Box(CROP_INIT_X, CROP_INIT_Y, photo_dim.width - CROP_INIT_X,
photo_dim.height - CROP_INIT_Y);
}
// scale the crop to the scaled photo's size ... the scaled crop is maintained in absolute
// coordinates non-relative to photo's position on canvas
scaled_crop = crop.get_scaled(photo_dim, Dimensions.for_rectangle(pixbuf_rect));
debug("crop:%s scaled_crop:%s", crop.to_string(), scaled_crop.to_string());
crop_button.set_active(true);
show_crop = true;
......@@ -700,18 +728,30 @@ public class PhotoPage : Page {
Gtk.Requisition req;
crop_tool_window.size_request(out req);
crop_tool_window.move(rx + cx + (cwidth / 2) - (req.width / 2), ry + cy + cheight);
repaint();
}
private void deactivate_crop() {
if (!show_crop)
return;
if (crop_tool_window != null) {
crop_tool_window.hide();
crop_tool_window = null;
}
crop_button.set_active(false);
show_crop = false;
crop_tool_window.hide();
crop_tool_window = null;
repaint();
}
private void rescale_crop(Dimensions old_pixbuf_dim, Dimensions new_pixbuf_dim) {
assert(show_crop);
Dimensions photo_dim = photo.get_dimensions();
Dimensions photo_dim = photo.get_uncropped_dimensions();
// rescale back to full crop
Box crop = scaled_crop.get_scaled(old_pixbuf_dim, photo_dim);
......@@ -781,17 +821,33 @@ public class PhotoPage : Page {
}
private void on_crop_apply() {
// up-scale scaled crop to photo's dimensions
Box crop = scaled_crop.get_scaled(Dimensions.for_rectangle(pixbuf_rect),
photo.get_uncropped_dimensions());
deactivate_crop();
// let the signal generate a repaint
photo.set_crop(crop);
}
private void on_crop_cancel() {
deactivate_crop();
// let the signal generate a repaint
photo.remove_crop();
}
private void on_next_photo() {
deactivate_crop();
this.thumbnail = (Thumbnail) controller.get_next_item(thumbnail);
update_display();
}
private void on_previous_photo() {
deactivate_crop();
this.thumbnail = (Thumbnail) controller.get_previous_item(thumbnail);
update_display();
}
......
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