diff options
Diffstat (limited to 'src/page-view.vala')
-rw-r--r-- | src/page-view.vala | 503 |
1 files changed, 55 insertions, 448 deletions
diff --git a/src/page-view.vala b/src/page-view.vala index 148dcca..9ef83de 100644 --- a/src/page-view.vala +++ b/src/page-view.vala @@ -29,7 +29,7 @@ public class PageView : Object public Page page { get; private set; } /* Image to render at current resolution */ - private Gdk.Pixbuf? image = null; + private PageViewTexture page_texture; /* Border around image */ private bool selected_ = false; @@ -52,12 +52,6 @@ public class PageView : Object /* 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_; @@ -75,7 +69,7 @@ public class PageView : Object private int selected_crop_h; /* Cursor over this page */ - public Gdk.CursorType cursor { get; private set; default = Gdk.CursorType.ARROW; } + public string cursor { get; private set; default = "arrow"; } private int animate_n_segments = 7; private int animate_segment; @@ -92,6 +86,9 @@ public class PageView : Object 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); + + page_texture = new PageViewTexture(page); + page_texture.new_buffer.connect (new_buffer_cb); } ~PageView () @@ -101,411 +98,13 @@ public class PageView : Object page.crop_changed.disconnect (page_overlay_changed_cb); page.scan_line_changed.disconnect (page_overlay_changed_cb); page.scan_direction_changed.disconnect (scan_direction_changed_cb); - } - - 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.scan_direction) - { - case ScanDirection.TOP_TO_BOTTOM: - break; - case ScanDirection.BOTTOM_TO_TOP: - x = page.scan_width - x - 1; - y = page.scan_height - y - 1; - break; - case ScanDirection.LEFT_TO_RIGHT: - var t = x; - x = page.scan_width - y - 1; - y = t; - break; - case ScanDirection.RIGHT_TO_LEFT: - var t = x; - x = y; - y = page.scan_height - t - 1; - break; - } - - var depth = page.depth; - var n_channels = page.n_channels; - unowned uchar[] pixels = page.get_pixels (); - var offset = page.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); + page_texture.new_buffer.disconnect (new_buffer_cb); } - - 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) + + private void new_buffer_cb() { - var input_width = page.width; - var input_height = page.height; - - /* Create new image if one does not exist or has changed size */ - int L, R, T, B; - if (output_image == null || - output_image.width != output_width || - output_image.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.rowstride; - var output_n_channels = output_image.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); - } - } + changed (); } private int get_preview_width () @@ -518,30 +117,6 @@ public class PageView : Object return height_ - (border_width + ruler_width) * 2; } - private void update_page_view () - { - if (!update_image) - return; - - var old_scan_line = scan_line; - var scan_line = page.scan_line; - - /* Delete old image if scan direction changed */ - var left_steps = scan_direction - page.scan_direction; - if (left_steps != 0 && image != null) - image = null; - scan_direction = page.scan_direction; - - update_preview (page, - ref image, - get_preview_width (), - get_preview_height (), - page.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.width + 0.5); @@ -642,38 +217,39 @@ public class PageView : Object public void motion (int x, int y) { var location = get_crop_location (x, y); - Gdk.CursorType cursor; + + string cursor; switch (location) { case CropLocation.MIDDLE: - cursor = Gdk.CursorType.HAND1; + cursor = "hand1"; break; case CropLocation.TOP: - cursor = Gdk.CursorType.TOP_SIDE; + cursor = "top_side"; break; case CropLocation.BOTTOM: - cursor = Gdk.CursorType.BOTTOM_SIDE; + cursor = "bottom_side"; break; case CropLocation.LEFT: - cursor = Gdk.CursorType.LEFT_SIDE; + cursor = "left_side"; break; case CropLocation.RIGHT: - cursor = Gdk.CursorType.RIGHT_SIDE; + cursor = "right_side"; break; case CropLocation.TOP_LEFT: - cursor = Gdk.CursorType.TOP_LEFT_CORNER; + cursor = "top_left_corner"; break; case CropLocation.TOP_RIGHT: - cursor = Gdk.CursorType.TOP_RIGHT_CORNER; + cursor = "top_right_corner"; break; case CropLocation.BOTTOM_LEFT: - cursor = Gdk.CursorType.BOTTOM_LEFT_CORNER; + cursor = "bottom_left_corner"; break; case CropLocation.BOTTOM_RIGHT: - cursor = Gdk.CursorType.BOTTOM_RIGHT_CORNER; + cursor = "bottom_right_corner"; break; default: - cursor = Gdk.CursorType.ARROW; + cursor = "arrow"; break; } @@ -828,7 +404,18 @@ public class PageView : Object public void render (Cairo.Context context, Gdk.RGBA ruler_color) { update_animation (); - update_page_view (); + + page_texture.request_resize (get_preview_width (), get_preview_height ()); + + try { + page_texture.queue_update (); + } + catch (Error e) + { + warning ("Failed to queue_update of the texture: %s", e.message); + // Ask for another redraw + changed (); + } var w = get_preview_width (); var h = get_preview_height (); @@ -838,8 +425,26 @@ public class PageView : Object /* Draw image */ context.translate (border_width + ruler_width, border_width + ruler_width); - Gdk.cairo_set_source_pixbuf (context, image, 0, 0); - context.paint (); + + if (page_texture.pixbuf != null) + { + float x_scale = (float) w / (float) page_texture.pixbuf.width; + float y_scale = (float) h / (float) page_texture.pixbuf.height; + + context.save (); + context.scale(x_scale, y_scale); + + // context.rectangle (0, 0.0, w, h); + Gdk.cairo_set_source_pixbuf (context, page_texture.pixbuf, 0, 0); + context.paint (); + context.restore (); + } + else + { + Gdk.cairo_set_source_rgba (context, {1.0f, 1.0f, 1.0f, 1.0f}); + context.rectangle (0, 0.0, w, h); + context.fill (); + } /* Draw page border */ Gdk.cairo_set_source_rgba (context, ruler_color); @@ -1037,6 +642,7 @@ public class PageView : Object { /* Regenerate image */ update_image = true; + page_texture.request_update (); changed (); } @@ -1057,6 +663,7 @@ public class PageView : Object { /* Regenerate image */ update_image = true; + page_texture.request_update (); size_changed (); changed (); } |