summaryrefslogtreecommitdiff
path: root/src/page-view.vala
diff options
context:
space:
mode:
Diffstat (limited to 'src/page-view.vala')
-rw-r--r--src/page-view.vala503
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 ();
}