diff options
Diffstat (limited to 'src/page-view.vala')
-rw-r--r-- | src/page-view.vala | 1043 |
1 files changed, 1043 insertions, 0 deletions
diff --git a/src/page-view.vala b/src/page-view.vala new file mode 100644 index 0000000..97bcaf0 --- /dev/null +++ b/src/page-view.vala @@ -0,0 +1,1043 @@ +/* + * Copyright (C) 2009-2011 Canonical Ltd. + * Author: Robert Ancell <robert.ancell@canonical.com> + * + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. See http://www.gnu.org/copyleft/gpl.html the full text of the + * license. + */ + +public enum CropLocation +{ + NONE = 0, + MIDDLE, + TOP, + BOTTOM, + LEFT, + RIGHT, + TOP_LEFT, + TOP_RIGHT, + BOTTOM_LEFT, + BOTTOM_RIGHT +} + +public class PageView +{ + /* Page being rendered */ + private Page page; + + /* Image to render at current resolution */ + private Gdk.Pixbuf? image = null; + + /* Border around image */ + private bool selected; + private int border_width = 1; + + /* True if image needs to be regenerated */ + private bool update_image = true; + + /* Direction of currently scanned image */ + private ScanDirection scan_direction; + + /* Next scan line to render */ + private int scan_line; + + /* Dimensions of image to generate */ + private int width; + private int height; + + /* Location to place this page */ + private int x_offset; + private int y_offset; + + private CropLocation crop_location; + private double selected_crop_px; + private double selected_crop_py; + private int selected_crop_x; + private int selected_crop_y; + private int selected_crop_w; + private int selected_crop_h; + + /* Cursor over this page */ + private Gdk.CursorType cursor = Gdk.CursorType.ARROW; + + private int animate_n_segments = 7; + private int animate_segment; + private uint animate_timeout; + + public signal void size_changed (); + public signal void changed (); + + public PageView (Page page) + { + this.page = page; + page.pixels_changed.connect (page_pixels_changed_cb); + page.size_changed.connect (page_size_changed_cb); + page.crop_changed.connect (page_overlay_changed_cb); + page.scan_line_changed.connect (page_overlay_changed_cb); + page.scan_direction_changed.connect (scan_direction_changed_cb); + } + + public Page get_page () + { + return page; + } + + public void set_selected (bool selected) + { + if ((this.selected && selected) || (!this.selected && !selected)) + return; + this.selected = selected; + changed (); + } + + public bool get_selected () + { + return selected; + } + + public void set_x_offset (int offset) + { + x_offset = offset; + } + + public void set_y_offset (int offset) + { + y_offset = offset; + } + + public int get_x_offset () + { + return x_offset; + } + + public int get_y_offset () + { + return y_offset; + } + + private uchar get_sample (uchar[] pixels, int offset, int x, int depth, int sample) + { + // FIXME + return 0xFF; + } + + private void get_pixel (Page page, int x, int y, uchar[] pixel) + { + switch (page.get_scan_direction ()) + { + case ScanDirection.TOP_TO_BOTTOM: + break; + case ScanDirection.BOTTOM_TO_TOP: + x = page.get_scan_width () - x - 1; + y = page.get_scan_height () - y - 1; + break; + case ScanDirection.LEFT_TO_RIGHT: + var t = x; + x = page.get_scan_width () - y - 1; + y = t; + break; + case ScanDirection.RIGHT_TO_LEFT: + var t = x; + x = y; + y = page.get_scan_height () - t - 1; + break; + } + + var depth = page.get_depth (); + var n_channels = page.get_n_channels (); + unowned uchar[] pixels = page.get_pixels (); + var offset = page.get_rowstride () * y; + + /* Optimise for 8 bit images */ + if (depth == 8 && n_channels == 3) + { + var o = offset + x * n_channels; + pixel[0] = pixels[o]; + pixel[1] = pixels[o+1]; + pixel[2] = pixels[o+2]; + return; + } + else if (depth == 8 && n_channels == 1) + { + pixel[0] = pixel[1] = pixel[2] = pixels[offset + x]; + return; + } + + /* Optimise for bitmaps */ + else if (depth == 1 && n_channels == 1) + { + var o = offset + (x / 8); + pixel[0] = pixel[1] = pixel[2] = (pixels[o] & (0x80 >> (x % 8))) != 0 ? 0x00 : 0xFF; + return; + } + + /* Optimise for 2 bit images */ + else if (depth == 2 && n_channels == 1) + { + int block_shift[4] = { 6, 4, 2, 0 }; + + var o = offset + (x / 4); + var sample = (pixels[o] >> block_shift[x % 4]) & 0x3; + sample = sample * 255 / 3; + + pixel[0] = pixel[1] = pixel[2] = (uchar) sample; + return; + } + + /* Use slow method */ + pixel[0] = get_sample (pixels, offset, x, depth, x * n_channels); + pixel[1] = get_sample (pixels, offset, x, depth, x * n_channels + 1); + pixel[2] = get_sample (pixels, offset, x, depth, x * n_channels + 2); + } + + private void set_pixel (Page page, double l, double r, double t, double b, uchar[] output, int offset) + { + /* Decimation: + * + * Target pixel is defined by (t,l)-(b,r) + * It touches 16 pixels in original image + * It completely covers 4 pixels in original image (T,L)-(B,R) + * Add covered pixels and add weighted partially covered pixels. + * Divide by total area. + * + * l L R r + * +-----+-----+-----+-----+ + * | | | | | + * t | +--+-----+-----+---+ | + * T +--+--+-----+-----+---+-+ + * | | | | | | | + * | | | | | | | + * +--+--+-----+-----+---+-+ + * | | | | | | | + * | | | | | | | + * B +--+--+-----+-----+---+-+ + * | | | | | | | + * b | +--+-----+-----+---+ | + * +-----+-----+-----+-----+ + * + * + * Interpolation: + * + * l r + * +-----+-----+-----+-----+ + * | | | | | + * | | | | | + * +-----+-----+-----+-----+ + * t | | +-+--+ | | + * | | | | | | | + * +-----+---+-+--+--+-----+ + * b | | +-+--+ | | + * | | | | | + * +-----+-----+-----+-----+ + * | | | | | + * | | | | | + * +-----+-----+-----+-----+ + * + * Same again, just no completely covered pixels. + */ + + var L = (int) l; + if (L != l) + L++; + var R = (int) r; + var T = (int) t; + if (T != t) + T++; + var B = (int) b; + + var red = 0.0; + var green = 0.0; + var blue = 0.0; + + /* Target can fit inside one source pixel + * +-----+ + * | | + * | +--+| +-----+-----+ +-----+ +-----+ +-----+ + * +-+--++ or | +-++ | or | +-+ | or | +--+| or | +--+ + * | +--+| | +-++ | | +-+ | | | || | | | + * | | +-----+-----+ +-----+ +-+--++ +--+--+ + * +-----+ + */ + if ((r - l <= 1.0 && (int)r == (int)l) || (b - t <= 1.0 && (int)b == (int)t)) + { + /* Inside */ + if ((int)l == (int)r || (int)t == (int)b) + { + uchar p[3]; + get_pixel (page, (int)l, (int)t, p); + output[offset] = p[0]; + output[offset+1] = p[1]; + output[offset+2] = p[2]; + return; + } + + /* Stradling horizontal edge */ + if (L > R) + { + uchar p[3]; + get_pixel (page, R, T-1, p); + red += p[0] * (r-l)*(T-t); + green += p[1] * (r-l)*(T-t); + blue += p[2] * (r-l)*(T-t); + for (var y = T; y < B; y++) + { + get_pixel (page, R, y, p); + red += p[0] * (r-l); + green += p[1] * (r-l); + blue += p[2] * (r-l); + } + get_pixel (page, R, B, p); + red += p[0] * (r-l)*(b-B); + green += p[1] * (r-l)*(b-B); + blue += p[2] * (r-l)*(b-B); + } + /* Stradling vertical edge */ + else + { + uchar p[3]; + get_pixel (page, L - 1, B, p); + red += p[0] * (b-t)*(L-l); + green += p[1] * (b-t)*(L-l); + blue += p[2] * (b-t)*(L-l); + for (var x = L; x < R; x++) { + get_pixel (page, x, B, p); + red += p[0] * (b-t); + green += p[1] * (b-t); + blue += p[2] * (b-t); + } + get_pixel (page, R, B, p); + red += p[0] * (b-t)*(r-R); + green += p[1] * (b-t)*(r-R); + blue += p[2] * (b-t)*(r-R); + } + + var scale = 1.0 / ((r - l) * (b - t)); + output[offset] = (uchar)(red * scale + 0.5); + output[offset+1] = (uchar)(green * scale + 0.5); + output[offset+2] = (uchar)(blue * scale + 0.5); + return; + } + + /* Add the middle pixels */ + for (var x = L; x < R; x++) + { + for (var y = T; y < B; y++) + { + uchar p[3]; + get_pixel (page, x, y, p); + red += p[0]; + green += p[1]; + blue += p[2]; + } + } + + /* Add the weighted top and bottom pixels */ + for (var x = L; x < R; x++) + { + if (t != T) + { + uchar p[3]; + get_pixel (page, x, T - 1, p); + red += p[0] * (T - t); + green += p[1] * (T - t); + blue += p[2] * (T - t); + } + + if (b != B) + { + uchar p[3]; + get_pixel (page, x, B, p); + red += p[0] * (b - B); + green += p[1] * (b - B); + blue += p[2] * (b - B); + } + } + + /* Add the left and right pixels */ + for (var y = T; y < B; y++) + { + if (l != L) + { + uchar p[3]; + get_pixel (page, L - 1, y, p); + red += p[0] * (L - l); + green += p[1] * (L - l); + blue += p[2] * (L - l); + } + + if (r != R) + { + uchar p[3]; + get_pixel (page, R, y, p); + red += p[0] * (r - R); + green += p[1] * (r - R); + blue += p[2] * (r - R); + } + } + + /* Add the corner pixels */ + if (l != L && t != T) + { + uchar p[3]; + get_pixel (page, L - 1, T - 1, p); + red += p[0] * (L - l)*(T - t); + green += p[1] * (L - l)*(T - t); + blue += p[2] * (L - l)*(T - t); + } + if (r != R && t != T) + { + uchar p[3]; + get_pixel (page, R, T - 1, p); + red += p[0] * (r - R)*(T - t); + green += p[1] * (r - R)*(T - t); + blue += p[2] * (r - R)*(T - t); + } + if (r != R && b != B) + { + uchar p[3]; + get_pixel (page, R, B, p); + red += p[0] * (r - R)*(b - B); + green += p[1] * (r - R)*(b - B); + blue += p[2] * (r - R)*(b - B); + } + if (l != L && b != B) + { + uchar p[3]; + get_pixel (page, L - 1, B, p); + red += p[0] * (L - l)*(b - B); + green += p[1] * (L - l)*(b - B); + blue += p[2] * (L - l)*(b - B); + } + + /* Scale pixel values and clamp in range [0, 255] */ + var scale = 1.0 / ((r - l) * (b - t)); + output[offset] = (uchar)(red * scale + 0.5); + output[offset+1] = (uchar)(green * scale + 0.5); + output[offset+2] = (uchar)(blue * scale + 0.5); + } + + private void update_preview (Page page, ref Gdk.Pixbuf? output_image, int output_width, int output_height, + ScanDirection scan_direction, int old_scan_line, int scan_line) + { + var input_width = page.get_width (); + var input_height = page.get_height (); + + /* Create new image if one does not exist or has changed size */ + int L, R, T, B; + if (output_image == null || + output_image.get_width () != output_width || + output_image.get_height () != output_height) + { + output_image = new Gdk.Pixbuf (Gdk.Colorspace.RGB, + false, + 8, + output_width, + output_height); + + /* Update entire image */ + L = 0; + R = output_width - 1; + T = 0; + B = output_height - 1; + } + /* Otherwise only update changed area */ + else + { + switch (scan_direction) + { + case ScanDirection.TOP_TO_BOTTOM: + L = 0; + R = output_width - 1; + T = (int)((double)old_scan_line * output_height / input_height); + B = (int)((double)scan_line * output_height / input_height + 0.5); + break; + case ScanDirection.LEFT_TO_RIGHT: + L = (int)((double)old_scan_line * output_width / input_width); + R = (int)((double)scan_line * output_width / input_width + 0.5); + T = 0; + B = output_height - 1; + break; + case ScanDirection.BOTTOM_TO_TOP: + L = 0; + R = output_width - 1; + T = (int)((double)(input_height - scan_line) * output_height / input_height); + B = (int)((double)(input_height - old_scan_line) * output_height / input_height + 0.5); + break; + case ScanDirection.RIGHT_TO_LEFT: + L = (int)((double)(input_width - scan_line) * output_width / input_width); + R = (int)((double)(input_width - old_scan_line) * output_width / input_width + 0.5); + T = 0; + B = output_height - 1; + break; + default: + L = R = B = T = 0; + break; + } + } + + /* FIXME: There's an off by one error in there somewhere... */ + if (R >= output_width) + R = output_width - 1; + if (B >= output_height) + B = output_height - 1; + + return_if_fail (L >= 0); + return_if_fail (R < output_width); + return_if_fail (T >= 0); + return_if_fail (B < output_height); + return_if_fail (output_image != null); + + unowned uchar[] output = output_image.get_pixels (); + var output_rowstride = output_image.get_rowstride (); + var output_n_channels = output_image.get_n_channels (); + + if (!page.has_data ()) + { + for (var x = L; x <= R; x++) + for (var y = T; y <= B; y++) + { + var o = output_rowstride * y + x * output_n_channels; + output[o] = output[o+1] = output[o+2] = 0xFF; + } + return; + } + + /* Update changed area */ + for (var x = L; x <= R; x++) + { + var l = (double)x * input_width / output_width; + var r = (double)(x + 1) * input_width / output_width; + + for (var y = T; y <= B; y++) + { + var t = (double)y * input_height / output_height; + var b = (double)(y + 1) * input_height / output_height; + + set_pixel (page, + l, r, t, b, + output, output_rowstride * y + x * output_n_channels); + } + } + } + + private int get_preview_width () + { + return width - border_width * 2; + } + + private int get_preview_height () + { + return height - border_width * 2; + } + + private void update_page_view () + { + if (!update_image) + return; + + var old_scan_line = scan_line; + var scan_line = page.get_scan_line (); + + /* Delete old image if scan direction changed */ + var left_steps = scan_direction - page.get_scan_direction (); + if (left_steps != 0 && image != null) + image = null; + scan_direction = page.get_scan_direction (); + + update_preview (page, + ref image, + get_preview_width (), + get_preview_height (), + page.get_scan_direction (), old_scan_line, scan_line); + + update_image = false; + this.scan_line = scan_line; + } + + private int page_to_screen_x (int x) + { + return (int) ((double)x * get_preview_width () / page.get_width () + 0.5); + } + + private int page_to_screen_y (int y) + { + return (int) ((double)y * get_preview_height () / page.get_height () + 0.5); + } + + private int screen_to_page_x (int x) + { + return (int) ((double)x * page.get_width () / get_preview_width () + 0.5); + } + + private int screen_to_page_y (int y) + { + return (int) ((double)y * page.get_height () / get_preview_height () + 0.5); + } + + private CropLocation get_crop_location (int x, int y) + { + if (!page.has_crop ()) + return 0; + + int cx, cy, cw, ch; + page.get_crop (out cx, out cy, out cw, out ch); + var dx = page_to_screen_x (cx); + var dy = page_to_screen_y (cy); + var dw = page_to_screen_x (cw); + var dh = page_to_screen_y (ch); + var ix = x - dx; + var iy = y - dy; + + if (ix < 0 || ix > dw || iy < 0 || iy > dh) + return CropLocation.NONE; + + /* Can't resize named crops */ + var name = page.get_named_crop (); + if (name != null) + return CropLocation.MIDDLE; + + /* Adjust borders so can select */ + int crop_border = 20; + if (dw < crop_border * 3) + crop_border = dw / 3; + if (dh < crop_border * 3) + crop_border = dh / 3; + + /* Top left */ + if (ix < crop_border && iy < crop_border) + return CropLocation.TOP_LEFT; + /* Top right */ + if (ix > dw - crop_border && iy < crop_border) + return CropLocation.TOP_RIGHT; + /* Bottom left */ + if (ix < crop_border && iy > dh - crop_border) + return CropLocation.BOTTOM_LEFT; + /* Bottom right */ + if (ix > dw - crop_border && iy > dh - crop_border) + return CropLocation.BOTTOM_RIGHT; + + /* Left */ + if (ix < crop_border) + return CropLocation.LEFT; + /* Right */ + if (ix > dw - crop_border) + return CropLocation.RIGHT; + /* Top */ + if (iy < crop_border) + return CropLocation.TOP; + /* Bottom */ + if (iy > dh - crop_border) + return CropLocation.BOTTOM; + + /* In the middle */ + return CropLocation.MIDDLE; + } + + public void button_press (int x, int y) + { + CropLocation location; + + /* See if selecting crop */ + location = get_crop_location (x, y);; + if (location != CropLocation.NONE) + { + crop_location = location; + selected_crop_px = x; + selected_crop_py = y; + page.get_crop (out selected_crop_x, + out selected_crop_y, + out selected_crop_w, + out selected_crop_h); + } + } + + public void motion (int x, int y) + { + var location = get_crop_location (x, y); + Gdk.CursorType cursor; + switch (location) + { + case CropLocation.MIDDLE: + cursor = Gdk.CursorType.HAND1; + break; + case CropLocation.TOP: + cursor = Gdk.CursorType.TOP_SIDE; + break; + case CropLocation.BOTTOM: + cursor = Gdk.CursorType.BOTTOM_SIDE; + break; + case CropLocation.LEFT: + cursor = Gdk.CursorType.LEFT_SIDE; + break; + case CropLocation.RIGHT: + cursor = Gdk.CursorType.RIGHT_SIDE; + break; + case CropLocation.TOP_LEFT: + cursor = Gdk.CursorType.TOP_LEFT_CORNER; + break; + case CropLocation.TOP_RIGHT: + cursor = Gdk.CursorType.TOP_RIGHT_CORNER; + break; + case CropLocation.BOTTOM_LEFT: + cursor = Gdk.CursorType.BOTTOM_LEFT_CORNER; + break; + case CropLocation.BOTTOM_RIGHT: + cursor = Gdk.CursorType.BOTTOM_RIGHT_CORNER; + break; + default: + cursor = Gdk.CursorType.ARROW; + break; + } + + if (crop_location == CropLocation.NONE) + { + this.cursor = cursor; + return; + } + + /* Move the crop */ + var pw = page.get_width (); + var ph = page.get_height (); + int cx, cy, cw, ch; + page.get_crop (out cx, out cy, out cw, out ch); + + var dx = screen_to_page_x (x - (int) selected_crop_px); + var dy = screen_to_page_y (y - (int) selected_crop_py); + + var new_x = selected_crop_x; + var new_y = selected_crop_y; + var new_w = selected_crop_w; + var new_h = selected_crop_h; + + /* Limit motion to remain within page and minimum crop size */ + var min_size = screen_to_page_x (15); + if (crop_location == CropLocation.TOP_LEFT || + crop_location == CropLocation.LEFT || + crop_location == CropLocation.BOTTOM_LEFT) + { + if (dx > new_w - min_size) + dx = new_w - min_size; + if (new_x + dx < 0) + dx = -new_x; + } + if (crop_location == CropLocation.TOP_LEFT || + crop_location == CropLocation.TOP || + crop_location == CropLocation.TOP_RIGHT) + { + if (dy > new_h - min_size) + dy = new_h - min_size; + if (new_y + dy < 0) + dy = -new_y; + } + + if (crop_location == CropLocation.TOP_RIGHT || + crop_location == CropLocation.RIGHT || + crop_location == CropLocation.BOTTOM_RIGHT) + { + if (dx < min_size - new_w) + dx = min_size - new_w; + if (new_x + new_w + dx > pw) + dx = pw - new_x - new_w; + } + if (crop_location == CropLocation.BOTTOM_LEFT || + crop_location == CropLocation.BOTTOM || + crop_location == CropLocation.BOTTOM_RIGHT) + { + if (dy < min_size - new_h) + dy = min_size - new_h; + if (new_y + new_h + dy > ph) + dy = ph - new_y - new_h; + } + if (crop_location == CropLocation.MIDDLE) + { + if (new_x + dx + new_w > pw) + dx = pw - new_x - new_w; + if (new_x + dx < 0) + dx = -new_x; + if (new_y + dy + new_h > ph) + dy = ph - new_y - new_h; + if (new_y + dy < 0) + dy = -new_y; + } + + /* Move crop */ + if (crop_location == CropLocation.MIDDLE) + { + new_x += dx; + new_y += dy; + } + if (crop_location == CropLocation.TOP_LEFT || + crop_location == CropLocation.LEFT || + crop_location == CropLocation.BOTTOM_LEFT) + { + new_x += dx; + new_w -= dx; + } + if (crop_location == CropLocation.TOP_LEFT || + crop_location == CropLocation.TOP || + crop_location == CropLocation.TOP_RIGHT) + { + new_y += dy; + new_h -= dy; + } + + if (crop_location == CropLocation.TOP_RIGHT || + crop_location == CropLocation.RIGHT || + crop_location == CropLocation.BOTTOM_RIGHT) + new_w += dx; + if (crop_location == CropLocation.BOTTOM_LEFT || + crop_location == CropLocation.BOTTOM || + crop_location == CropLocation.BOTTOM_RIGHT) + new_h += dy; + + page.move_crop (new_x, new_y); + + /* If reshaped crop, must be a custom crop */ + if (new_w != cw || new_h != ch) + page.set_custom_crop (new_w, new_h); + } + + public void button_release (int x, int y) + { + /* Complete crop */ + crop_location = CropLocation.NONE; + changed (); + } + + public Gdk.CursorType get_cursor () + { + return cursor; + } + + private bool animation_cb () + { + animate_segment = (animate_segment + 1) % animate_n_segments; + changed (); + return true; + } + + private void update_animation () + { + bool animate, is_animating; + + animate = page.is_scanning () && !page.has_data (); + is_animating = animate_timeout != 0; + if (animate == is_animating) + return; + + if (animate) + { + animate_segment = 0; + if (animate_timeout == 0) + animate_timeout = Timeout.add (150, animation_cb); + } + else + { + if (animate_timeout != 0) + Source.remove (animate_timeout); + animate_timeout = 0; + } + } + + public void render (Cairo.Context context) + { + update_animation (); + update_page_view (); + + var w = get_preview_width (); + var h = get_preview_height (); + + context.set_line_width (1); + context.translate (x_offset, y_offset); + + /* Draw page border */ + context.set_source_rgb (0, 0, 0); + context.set_line_width (border_width); + context.rectangle ((double)border_width / 2, + (double)border_width / 2, + width - border_width, + height - border_width); + context.stroke (); + + /* Draw image */ + context.translate (border_width, border_width); + Gdk.cairo_set_source_pixbuf (context, image, 0, 0); + context.paint (); + + /* Draw throbber */ + if (page.is_scanning () && !page.has_data ()) + { + double outer_radius; + if (w > h) + outer_radius = 0.15 * w; + else + outer_radius = 0.15 * h; + var arc = Math.PI / animate_n_segments; + + /* Space circles */ + var x = outer_radius * Math.sin (arc); + var y = outer_radius * (Math.cos (arc) - 1.0); + var inner_radius = 0.6 * Math.sqrt (x*x + y*y); + + double offset = 0.0; + for (var i = 0; i < animate_n_segments; i++, offset += arc * 2) + { + x = w / 2 + outer_radius * Math.sin (offset); + y = h / 2 - outer_radius * Math.cos (offset); + context.arc (x, y, inner_radius, 0, 2 * Math.PI); + + if (i == animate_segment) + { + context.set_source_rgb (0.75, 0.75, 0.75); + context.fill_preserve (); + } + + context.set_source_rgb (0.5, 0.5, 0.5); + context.stroke (); + } + } + + /* Draw scan line */ + if (page.is_scanning () && page.get_scan_line () > 0) + { + var scan_line = page.get_scan_line (); + + double s; + double x1, y1, x2, y2; + switch (page.get_scan_direction ()) + { + case ScanDirection.TOP_TO_BOTTOM: + s = page_to_screen_y (scan_line); + x1 = 0; y1 = s + 0.5; + x2 = w; y2 = s + 0.5; + break; + case ScanDirection.BOTTOM_TO_TOP: + s = page_to_screen_y (scan_line); + x1 = 0; y1 = h - s + 0.5; + x2 = w; y2 = h - s + 0.5; + break; + case ScanDirection.LEFT_TO_RIGHT: + s = page_to_screen_x (scan_line); + x1 = s + 0.5; y1 = 0; + x2 = s + 0.5; y2 = h; + break; + case ScanDirection.RIGHT_TO_LEFT: + s = page_to_screen_x (scan_line); + x1 = w - s + 0.5; y1 = 0; + x2 = w - s + 0.5; y2 = h; + break; + default: + x1 = y1 = x2 = y2 = 0; + break; + } + + context.move_to (x1, y1); + context.line_to (x2, y2); + context.set_source_rgb (1.0, 0.0, 0.0); + context.stroke (); + } + + /* Draw crop */ + if (page.has_crop ()) + { + int x, y, crop_width, crop_height; + page.get_crop (out x, out y, out crop_width, out crop_height); + + var dx = page_to_screen_x (x); + var dy = page_to_screen_y (y); + var dw = page_to_screen_x (crop_width); + var dh = page_to_screen_y (crop_height); + + /* Shade out cropped area */ + context.rectangle (0, 0, w, h); + context.new_sub_path (); + context.rectangle (dx, dy, dw, dh); + context.set_fill_rule (Cairo.FillRule.EVEN_ODD); + context.set_source_rgba (0.25, 0.25, 0.25, 0.2); + context.fill (); + + /* Show new edge */ + context.rectangle (dx - 1.5, dy - 1.5, dw + 3, dh + 3); + context.set_source_rgb (1.0, 1.0, 1.0); + context.stroke (); + context.rectangle (dx - 0.5, dy - 0.5, dw + 1, dh + 1); + context.set_source_rgb (0.0, 0.0, 0.0); + context.stroke (); + } + } + + public void set_width (int width) + { + // FIXME: Automatically update when get updated image + var height = (int) ((double)width * page.get_height () / page.get_width ()); + if (this.width == width && this.height == height) + return; + + this.width = width; + this.height = height; + + /* Regenerate image */ + update_image = true; + + size_changed (); + changed (); + } + + public void set_height (int height) + { + // FIXME: Automatically update when get updated image + var width = (int) ((double)height * page.get_width () / page.get_height ()); + if (this.width == width && this.height == height) + return; + + this.width = width; + this.height = height; + + /* Regenerate image */ + update_image = true; + + size_changed (); + changed (); + } + + public int get_width () + { + return width; + } + + public int get_height () + { + return height; + } + + private void page_pixels_changed_cb (Page p) + { + /* Regenerate image */ + update_image = true; + changed (); + } + + private void page_size_changed_cb (Page p) + { + /* Regenerate image */ + update_image = true; + size_changed (); + changed (); + } + + private void page_overlay_changed_cb (Page p) + { + changed (); + } + + private void scan_direction_changed_cb (Page p) + { + /* Regenerate image */ + update_image = true; + size_changed (); + changed (); + } +} |