diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/book.c | 76 | ||||
-rw-r--r-- | src/book.h | 2 | ||||
-rw-r--r-- | src/page-view.c | 209 | ||||
-rw-r--r-- | src/page.c | 575 | ||||
-rw-r--r-- | src/page.h | 37 | ||||
-rw-r--r-- | src/scanner.c | 70 | ||||
-rw-r--r-- | src/scanner.h | 14 | ||||
-rw-r--r-- | src/simple-scan.c | 8 | ||||
-rw-r--r-- | src/ui.c | 41 |
9 files changed, 579 insertions, 453 deletions
@@ -73,14 +73,13 @@ page_changed_cb (Page *page, Book *book) Page * -book_append_page (Book *book, gint width, gint height, gint dpi, Orientation orientation) +book_append_page (Book *book, gint width, gint height, gint dpi, ScanDirection scan_direction) { Page *page; - page = page_new (); - g_signal_connect (page, "image-changed", G_CALLBACK (page_changed_cb), book); + page = page_new (width, height, dpi, scan_direction); + g_signal_connect (page, "pixels-changed", G_CALLBACK (page_changed_cb), book); g_signal_connect (page, "crop-changed", G_CALLBACK (page_changed_cb), book); - page_setup (page, width, height, dpi, orientation); book->priv->pages = g_list_append (book->priv->pages, page); @@ -222,7 +221,7 @@ book_save_ps (Book *book, GFile *file, GError **error) double width, height; GdkPixbuf *image; - image = page_get_cropped_image (page); + image = page_get_image (page, TRUE); width = gdk_pixbuf_get_width (image) * 72.0 / page_get_dpi (page); height = gdk_pixbuf_get_height (image) * 72.0 / page_get_dpi (page); @@ -444,7 +443,7 @@ book_save_pdf (Book *book, GFile *file, GError **error) height = page_get_height (page); page_width = width * 72. / page_get_dpi (page); page_height = height * 72. / page_get_dpi (page); - image = page_get_cropped_image (page); + image = page_get_image (page, TRUE); pixels = gdk_pixbuf_get_pixels (image); if (page_is_color (page)) { @@ -470,13 +469,53 @@ book_save_pdf (Book *book, GFile *file, GError **error) } } } + else if (page_get_depth (page) == 2) { + int row, shift_count = 6; + guchar *write_ptr; + + depth = 2; + color_space = "DeviceGray"; + data_length = height * ((width * 2 + 7) / 8); + data = g_malloc (sizeof (guchar) * data_length); + write_ptr = data; + write_ptr[0] = 0; + for (row = 0; row < height; row++) { + int x; + guchar *in_line; + + /* Pad to the next line */ + if (shift_count != 6) { + write_ptr++; + write_ptr[0] = 0; + shift_count = 6; + } + + in_line = pixels + row * gdk_pixbuf_get_rowstride (image); + for (x = 0; x < width; x++) { + guchar *in_p = in_line + x*3; + if (in_p[0] >= 192) + write_ptr[0] |= 3 << shift_count; + else if (in_p[0] >= 128) + write_ptr[0] |= 2 << shift_count; + else if (in_p[0] >= 64) + write_ptr[0] |= 1 << shift_count; + if (shift_count == 0) { + write_ptr++; + write_ptr[0] = 0; + shift_count = 6; + } + else + shift_count -= 2; + } + } + } else if (page_get_depth (page) == 1) { - int row, offset = 7; + int row, mask = 0x80; guchar *write_ptr; depth = 1; color_space = "DeviceGray"; - data_length = (height * width + 7) / 8; + data_length = height * ((width + 7) / 8); data = g_malloc (sizeof (guchar) * data_length); write_ptr = data; write_ptr[0] = 0; @@ -484,16 +523,23 @@ book_save_pdf (Book *book, GFile *file, GError **error) int x; guchar *in_line; + /* Pad to the next line */ + if (mask != 0x80) { + write_ptr++; + write_ptr[0] = 0; + mask = 0x80; + } + in_line = pixels + row * gdk_pixbuf_get_rowstride (image); for (x = 0; x < width; x++) { guchar *in_p = in_line + x*3; - if (in_p[0]) - write_ptr[0] |= 1 << offset; - offset--; - if (offset < 0) { + if (in_p[0] != 0) + write_ptr[0] |= mask; + mask >>= 1; + if (mask == 0) { write_ptr++; write_ptr[0] = 0; - offset = 7; + mask = 0x80; } } } @@ -583,9 +629,9 @@ book_save_pdf (Book *book, GFile *file, GError **error) /* Page contents */ command = g_strdup_printf ("q\n" - "%d 0 0 %d 0 0 cm\n" + "%f 0 0 %f 0 0 cm\n" "/Im%d Do\n" - "Q", width, height, i); + "Q", page_width, page_height, i); pdf_printf (writer, "\n"); number = pdf_start_object (writer); pdf_printf (writer, "%d 0 obj\n", number); @@ -47,7 +47,7 @@ Book *book_new (void); void book_clear (Book *book); -Page *book_append_page (Book *book, gint width, gint height, gint dpi, Orientation orientation); +Page *book_append_page (Book *book, gint width, gint height, gint dpi, ScanDirection orientation); void book_delete_page (Book *book, Page *page); diff --git a/src/page-view.c b/src/page-view.c index 2c37fc2..40823f6 100644 --- a/src/page-view.c +++ b/src/page-view.c @@ -43,7 +43,7 @@ struct PageViewPrivate { /* Page being rendered */ Page *page; - + /* Image to render at current resolution */ GdkPixbuf *image; @@ -53,6 +53,9 @@ struct PageViewPrivate /* True if image needs to be regenerated */ gboolean update_image; + + /* Direction of currently scanned image */ + ScanDirection scan_direction; /* Next scan line to render */ gint scan_line; @@ -104,7 +107,8 @@ page_view_set_selected (PageView *view, gboolean selected) } -gboolean page_view_get_selected (PageView *view) +gboolean +page_view_get_selected (PageView *view) { g_return_val_if_fail (view != NULL, FALSE); return view->priv->selected; @@ -143,19 +147,92 @@ page_view_get_y_offset (PageView *view) } -static guchar * -get_pixel (guchar *input, gint rowstride, gint n_channels, gint x, gint y) +static guchar +get_sample (const guchar *line, gint x, gint depth, gint sample) +{ + // FIXME + return 0xFF; +} + + +static void +get_pixel (Page *page, gint x, gint y, guchar *pixel) { - return input + rowstride * y + x * n_channels; + gint t, depth, n_channels; + const guchar *p, *line; + + switch (page_get_scan_direction (page)) + { + case TOP_TO_BOTTOM: + break; + case BOTTOM_TO_TOP: + x = page_get_scan_width (page) - x; + y = page_get_scan_height (page) - y; + break; + case LEFT_TO_RIGHT: + t = x; + x = page_get_scan_width (page) - y; + y = t; + break; + case RIGHT_TO_LEFT: + t = x; + x = y; + y = page_get_scan_height (page) - t; + break; + } + + depth = page_get_depth (page); + n_channels = page_get_n_channels (page); + line = page_get_pixels (page) + page_get_rowstride (page) * y; + + /* Optimise for 8 bit images */ + if (depth == 8 && n_channels == 3) { + p = line + x * n_channels; + pixel[0] = p[0]; + pixel[1] = p[1]; + pixel[2] = p[2]; + return; + } + else if (depth == 8 && n_channels == 1) { + p = line + x; + pixel[0] = pixel[1] = pixel[2] = p[0]; + return; + } + + /* Optimise for bitmaps */ + else if (depth == 1 && n_channels == 1) { + p = line + (x / 8); + pixel[0] = pixel[1] = pixel[2] = p[0] & (0x80 >> (x % 8)) ? 0x00 : 0xFF; + return; + } + + /* Optimise for 2 bit images */ + else if (depth == 2 && n_channels == 1) { + gint sample; + gint block_shift[4] = { 6, 4, 2, 0 }; + + p = line + (x / 4); + sample = (p[0] >> block_shift[x % 4]) & 0x3; + sample = sample * 255 / 3; + + pixel[0] = pixel[1] = pixel[2] = sample; + return; + } + + /* Use slow method */ + pixel[0] = get_sample (line, x, depth, x * n_channels); + pixel[0] = get_sample (line, x, depth, x * n_channels + 1); + pixel[0] = get_sample (line, x, depth, x * n_channels + 2); } static void -set_pixel (guchar *input, gint rowstride, gint n_channels, +set_pixel (Page *page, double l, double r, double t, double b, guchar *pixel) { gint x, y; gint L, R, T, B; + guchar p[3]; double scale, red, green, blue; /* Decimation: @@ -225,7 +302,7 @@ set_pixel (guchar *input, gint rowstride, gint n_channels, if ((r - l <= 1.0 && (gint)r == (gint)l) || (b - t <= 1.0 && (gint)b == (gint)t)) { /* Inside */ if ((gint)l == (gint)r || (gint)t == (gint)b) { - guchar *p = get_pixel (input, rowstride, n_channels, (gint)l, (gint)t); + get_pixel (page, (gint)l, (gint)t, p); pixel[0] = p[0]; pixel[1] = p[1]; pixel[2] = p[2]; @@ -234,34 +311,34 @@ set_pixel (guchar *input, gint rowstride, gint n_channels, /* Stradling horizontal edge */ if (L > R) { - guchar *p = get_pixel (input, rowstride, n_channels, R, T-1); + 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 (y = T; y < B; y++) { - guchar *p = get_pixel (input, rowstride, n_channels, R, y); + get_pixel (page, R, y, p); red += p[0] * (r-l); green += p[1] * (r-l); blue += p[2] * (r-l); } - p = get_pixel (input, rowstride, n_channels, R, B); + 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 { - guchar *p = get_pixel (input, rowstride, n_channels, L - 1, B); + 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 (x = L; x < R; x++) { - guchar *p = get_pixel (input, rowstride, n_channels, x, B); + get_pixel (page, x, B, p); red += p[0] * (b-t); green += p[1] * (b-t); blue += p[2] * (b-t); } - p = get_pixel (input, rowstride, n_channels, R, B); + 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); @@ -277,7 +354,7 @@ set_pixel (guchar *input, gint rowstride, gint n_channels, /* Add the middle pixels */ for (x = L; x < R; x++) { for (y = T; y < B; y++) { - guchar *p = get_pixel (input, rowstride, n_channels, x, y); + get_pixel (page, x, y, p); red += p[0]; green += p[1]; blue += p[2]; @@ -287,14 +364,14 @@ set_pixel (guchar *input, gint rowstride, gint n_channels, /* Add the weighted top and bottom pixels */ for (x = L; x < R; x++) { if (t != T) { - guchar *p = get_pixel (input, rowstride, n_channels, x, T - 1); + 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) { - guchar *p = get_pixel (input, rowstride, n_channels, x, B); + get_pixel (page, x, B, p); red += p[0] * (b - B); green += p[1] * (b - B); blue += p[2] * (b - B); @@ -304,14 +381,14 @@ set_pixel (guchar *input, gint rowstride, gint n_channels, /* Add the left and right pixels */ for (y = T; y < B; y++) { if (l != L) { - guchar *p = get_pixel (input, rowstride, n_channels, L - 1, y); + 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) { - guchar *p = get_pixel (input, rowstride, n_channels, R, y); + get_pixel (page, R, y, p); red += p[0] * (r - R); green += p[1] * (r - R); blue += p[2] * (r - R); @@ -320,25 +397,25 @@ set_pixel (guchar *input, gint rowstride, gint n_channels, /* Add the corner pixels */ if (l != L && t != T) { - guchar *p = get_pixel (input, rowstride, n_channels, L - 1, T - 1); + 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) { - guchar *p = get_pixel (input, rowstride, n_channels, R, T - 1); + 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) { - guchar *p = get_pixel (input, rowstride, n_channels, R, B); + 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) { - guchar *p = get_pixel (input, rowstride, n_channels, L - 1, B); + 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); @@ -353,23 +430,19 @@ set_pixel (guchar *input, gint rowstride, gint n_channels, static void -update_preview (GdkPixbuf *image, +update_preview (Page *page, GdkPixbuf **output_image, gint output_width, gint output_height, - Orientation orientation, gint old_scan_line, gint scan_line) + ScanDirection scan_direction, gint old_scan_line, gint scan_line) { - guchar *input, *output; + guchar *output; gint input_width, input_height; - gint input_rowstride, input_n_channels; gint output_rowstride, output_n_channels; gint x, y; gint L, R, T, B; - input = gdk_pixbuf_get_pixels (image); - input_width = gdk_pixbuf_get_width (image); - input_height = gdk_pixbuf_get_height (image); - input_rowstride = gdk_pixbuf_get_rowstride (image); - input_n_channels = gdk_pixbuf_get_n_channels (image); - + input_width = page_get_width (page); + input_height = page_get_height (page); + /* Create new image if one does not exist or has changed size */ if (!*output_image || gdk_pixbuf_get_width (*output_image) != output_width || @@ -381,7 +454,7 @@ update_preview (GdkPixbuf *image, 8, output_width, output_height); - + /* Update entire image */ L = 0; R = output_width - 1; @@ -390,7 +463,7 @@ update_preview (GdkPixbuf *image, } /* Otherwise only update changed area */ else { - switch (orientation) { + switch (scan_direction) { case TOP_TO_BOTTOM: L = 0; R = output_width - 1; @@ -432,15 +505,25 @@ update_preview (GdkPixbuf *image, g_return_if_fail (T >= 0); g_return_if_fail (B < output_height); g_return_if_fail (*output_image != NULL); - + output = gdk_pixbuf_get_pixels (*output_image); output_rowstride = gdk_pixbuf_get_rowstride (*output_image); output_n_channels = gdk_pixbuf_get_n_channels (*output_image); + + if (!page_has_data (page)) { + for (x = L; x <= R; x++) + for (y = T; y <= B; y++) { + guchar *pixel; + pixel = output + output_rowstride * y + x * output_n_channels; + pixel[0] = pixel[1] = pixel[2] = 0xFF; + } + return; + } /* Update changed area */ for (x = L; x <= R; x++) { double l, r; - + l = (double)x * input_width / output_width; r = (double)(x + 1) * input_width / output_width; @@ -450,9 +533,9 @@ update_preview (GdkPixbuf *image, t = (double)y * input_height / output_height; b = (double)(y + 1) * input_height / output_height; - set_pixel (input, input_rowstride, input_n_channels, + set_pixel (page, l, r, t, b, - get_pixel (output, output_rowstride, output_n_channels, x, y)); + output + output_rowstride * y + x * output_n_channels); } } } @@ -475,22 +558,27 @@ get_preview_height (PageView *view) static void update_page_view (PageView *view) { - GdkPixbuf *image; - gint old_scan_line, scan_line; + gint old_scan_line, scan_line, left_steps; if (!view->priv->update_image) return; - image = page_get_image (view->priv->page); old_scan_line = view->priv->scan_line; scan_line = page_get_scan_line (view->priv->page); - update_preview (image, + /* Delete old image if scan direction changed */ + left_steps = view->priv->scan_direction - page_get_scan_direction (view->priv->page); + if (left_steps && view->priv->image) { + g_object_unref (view->priv->image); + view->priv->image = NULL; + } + view->priv->scan_direction = page_get_scan_direction (view->priv->page); + + update_preview (view->priv->page, &view->priv->image, get_preview_width (view), get_preview_height (view), - page_get_orientation (view->priv->page), old_scan_line, scan_line); - g_object_unref (image); + page_get_scan_direction (view->priv->page), old_scan_line, scan_line); view->priv->update_image = FALSE; view->priv->scan_line = scan_line; @@ -844,18 +932,8 @@ page_view_render (PageView *view, cairo_t *context) /* Draw image */ cairo_translate (context, view->priv->border_width, view->priv->border_width); - if (view->priv->image) { - gdk_cairo_set_source_pixbuf (context, view->priv->image, 0, 0); - cairo_paint (context); - } - else { - cairo_scale (context, - (double) get_preview_width (view) / page_get_width (view->priv->page), - (double) get_preview_height (view) / page_get_height (view->priv->page)); - gdk_cairo_set_source_pixbuf (context, page_get_image (view->priv->page), 0, 0); - - cairo_paint (context); - } + gdk_cairo_set_source_pixbuf (context, view->priv->image, 0, 0); + cairo_paint (context); /* Draw throbber */ if (page_is_scanning (view->priv->page) && !page_has_data (view->priv->page)) { @@ -895,8 +973,8 @@ page_view_render (PageView *view, cairo_t *context) double x1, y1, x2, y2; scan_line = page_get_scan_line (view->priv->page); - - switch (page_get_orientation (view->priv->page)) { + + switch (page_get_scan_direction (view->priv->page)) { case TOP_TO_BOTTOM: s = page_to_screen_y (view, scan_line); x1 = 0; y1 = s + 0.5; @@ -1024,7 +1102,7 @@ page_view_get_height (PageView *view) static void -page_image_changed_cb (Page *p, PageView *view) +page_pixels_changed_cb (Page *p, PageView *view) { /* Regenerate image */ view->priv->update_image = TRUE; @@ -1050,16 +1128,27 @@ page_overlay_changed_cb (Page *p, PageView *view) static void +scan_direction_changed_cb (Page *p, PageView *view) +{ + /* Regenerate image */ + view->priv->update_image = TRUE; + g_signal_emit (view, signals[SIZE_CHANGED], 0); + g_signal_emit (view, signals[CHANGED], 0); +} + + +static void page_view_set_page (PageView *view, Page *page) { g_return_if_fail (view != NULL); g_return_if_fail (view->priv->page == NULL); view->priv->page = g_object_ref (page); - g_signal_connect (view->priv->page, "image-changed", G_CALLBACK (page_image_changed_cb), view); + g_signal_connect (view->priv->page, "pixels-changed", G_CALLBACK (page_pixels_changed_cb), view); g_signal_connect (view->priv->page, "size-changed", G_CALLBACK (page_size_changed_cb), view); g_signal_connect (view->priv->page, "crop-changed", G_CALLBACK (page_overlay_changed_cb), view); g_signal_connect (view->priv->page, "scan-line-changed", G_CALLBACK (page_overlay_changed_cb), view); + g_signal_connect (view->priv->page, "scan-direction-changed", G_CALLBACK (scan_direction_changed_cb), view); } @@ -12,12 +12,11 @@ #include <string.h> #include "page.h" - enum { - IMAGE_CHANGED, + PIXELS_CHANGED, SIZE_CHANGED, SCAN_LINE_CHANGED, - ORIENTATION_CHANGED, + SCAN_DIRECTION_CHANGED, CROP_CHANGED, LAST_SIGNAL }; @@ -29,7 +28,7 @@ struct PagePrivate gint dpi; /* Number of rows in this page or -1 if currently unknown */ - gint rows; + gint expected_rows; /* Bit depth */ gint depth; @@ -38,7 +37,8 @@ struct PagePrivate gchar *color_profile; /* Scanned image data */ - GdkPixbuf *image; + gint width, n_rows, rowstride, n_channels; + guchar *pixels; /* Page is getting data */ gboolean scanning; @@ -46,14 +46,11 @@ struct PagePrivate /* TRUE if have some page data */ gboolean has_data; - /* TRUE if have color data */ - gboolean is_color; - /* Expected next scan row */ gint scan_line; /* Rotation of scanned data */ - Orientation orientation; + ScanDirection scan_direction; /* Crop */ gboolean has_crop; @@ -65,67 +62,54 @@ G_DEFINE_TYPE (Page, page, G_TYPE_OBJECT); Page * -page_new () +page_new (gint width, gint height, gint dpi, ScanDirection scan_direction) { - return g_object_new (PAGE_TYPE, NULL); -} - - -void -page_setup (Page *page, gint width, gint height, gint dpi, Orientation orientation) -{ - page->priv->orientation = orientation; + Page *page; + + page = g_object_new (PAGE_TYPE, NULL); + if (scan_direction == TOP_TO_BOTTOM || scan_direction == BOTTOM_TO_TOP) { + page->priv->width = width; + page->priv->n_rows = height; + } + else { + page->priv->width = height; + page->priv->n_rows = width; + } page->priv->dpi = dpi; - if (orientation == LEFT_TO_RIGHT || orientation == RIGHT_TO_LEFT) - page->priv->rows = width; - else - page->priv->rows = height; - page->priv->image = gdk_pixbuf_new (GDK_COLORSPACE_RGB, FALSE, - 8, - width, - height); - g_return_if_fail (page->priv->image != NULL); - gdk_pixbuf_fill (page->priv->image, 0xFFFFFFFF); + page->priv->scan_direction = scan_direction; + + return page; } void -page_set_scan_area (Page *page, gint width, gint rows, gint dpi) +page_set_page_info (Page *page, ScanPageInfo *info) { - gint height; - g_return_if_fail (page != NULL); + page->priv->expected_rows = info->height; + page->priv->dpi = info->dpi; + + /* Create a white page */ + page->priv->width = info->width; + page->priv->n_rows = info->height; /* Variable height, try 50% of the width for now */ - if (rows < 0) - height = width / 2; + if (page->priv->n_rows < 0) + page->priv->n_rows = page->priv->width / 2; + page->priv->depth = info->depth; + page->priv->n_channels = info->n_channels; + page->priv->rowstride = (page->priv->width * page->priv->depth * page->priv->n_channels + 7) / 8; + page->priv->pixels = g_realloc (page->priv->pixels, page->priv->n_rows * page->priv->rowstride); + g_return_if_fail (page->priv->pixels != NULL); + + /* Fill with white */ + if (page->priv->depth == 1) + memset (page->priv->pixels, 0x00, page->priv->n_rows * page->priv->rowstride); else - height = rows; + memset (page->priv->pixels, 0xFF, page->priv->n_rows * page->priv->rowstride); - /* Rotate page */ - if (page->priv->orientation == LEFT_TO_RIGHT || page->priv->orientation == RIGHT_TO_LEFT) { - gint t; - t = width; - width = height; - height = t; - } - - page->priv->rows = rows; - page->priv->dpi = dpi; - - /* Create a white page */ - /* NOTE: Pixbuf only supports 8 bit RGB images */ - if (page->priv->image) - g_object_unref (page->priv->image); - page->priv->image = gdk_pixbuf_new (GDK_COLORSPACE_RGB, FALSE, - 8, - width, - height); - g_return_if_fail (page->priv->image != NULL); - - gdk_pixbuf_fill (page->priv->image, 0xFFFFFFFF); g_signal_emit (page, signals[SIZE_CHANGED], 0); - g_signal_emit (page, signals[IMAGE_CHANGED], 0); + g_signal_emit (page, signals[PIXELS_CHANGED], 0); } @@ -139,7 +123,8 @@ page_start (Page *page) } -gboolean page_is_scanning (Page *page) +gboolean +page_is_scanning (Page *page) { g_return_val_if_fail (page != NULL, FALSE); @@ -147,60 +132,24 @@ gboolean page_is_scanning (Page *page) } -static gint -get_sample (guchar *data, gint depth, gint index) -{ - gint i, offset, value, n_bits; - - /* Optimise if using 8 bit samples */ - if (depth == 8) - return data[index]; - - /* Bit offset for this sample */ - offset = depth * index; - - /* Get the remaining bits in the octet this sample starts in */ - i = offset / 8; - n_bits = 8 - offset % 8; - value = data[i] & (0xFF >> (8 - n_bits)); - - /* Add additional octets until get enough bits */ - while (n_bits < depth) { - value = value << 8 | data[i++]; - n_bits += 8; - } - - /* Trim remaining bits off */ - if (n_bits > depth) - value >>= n_bits - depth; - - return value; -} - - -gboolean page_has_data (Page *page) +gboolean +page_has_data (Page *page) { g_return_val_if_fail (page != NULL, FALSE); return page->priv->has_data; } -gboolean page_is_color (Page *page) +gboolean +page_is_color (Page *page) { g_return_val_if_fail (page != NULL, FALSE); - return page->priv->is_color; + return page->priv->n_channels > 1; } gint -page_get_depth (Page *page) -{ - g_return_val_if_fail (page != NULL, 0); - return page->priv->depth; -} - - -gint page_get_scan_line (Page *page) +page_get_scan_line (Page *page) { g_return_val_if_fail (page != NULL, -1); return page->priv->scan_line; @@ -208,126 +157,27 @@ gint page_get_scan_line (Page *page) static void -set_pixel (ScanLine *line, gint n, gint x, guchar *pixel) -{ - gint sample; - guchar *data; - - data = line->data + line->data_length * n; - - switch (line->format) { - case LINE_RGB: - pixel[0] = get_sample (data, line->depth, x*3) * 0xFF / ((1 << line->depth) - 1); - pixel[1] = get_sample (data, line->depth, x*3+1) * 0xFF / ((1 << line->depth) - 1); - pixel[2] = get_sample (data, line->depth, x*3+2) * 0xFF / ((1 << line->depth) - 1); - break; - case LINE_GRAY: - /* Bitmap, 0 = white, 1 = black */ - sample = get_sample (data, line->depth, x) * 0xFF / ((1 << line->depth) - 1); - if (line->depth == 1) - sample = sample ? 0x00 : 0xFF; - - pixel[0] = pixel[1] = pixel[2] = sample; - break; - case LINE_RED: - pixel[0] = get_sample (data, line->depth, x) * 0xFF / ((1 << line->depth) - 1); - break; - case LINE_GREEN: - pixel[1] = get_sample (data, line->depth, x) * 0xFF / ((1 << line->depth) - 1); - break; - case LINE_BLUE: - pixel[2] = get_sample (data, line->depth, x) * 0xFF / ((1 << line->depth) - 1); - break; - } -} - - -static void parse_line (Page *page, ScanLine *line, gint n, gboolean *size_changed) { - guchar *pixels; gint line_number; - gint i, x = 0, y = 0, x_step = 0, y_step = 0; - gint rowstride, n_channels; line_number = line->number + n; - if (line->format != LINE_GRAY) - page->priv->is_color = TRUE; - if (line->depth > page->priv->depth) - page->priv->depth = line->depth; - /* Extend image if necessary */ while (line_number >= page_get_scan_height (page)) { - GdkPixbuf *image; - gint height, width, new_width, new_height; + gint rows; /* Extend image */ - new_width = width = gdk_pixbuf_get_width (page->priv->image); - new_height = height = gdk_pixbuf_get_height (page->priv->image); - if (page->priv->orientation == TOP_TO_BOTTOM || page->priv->orientation == BOTTOM_TO_TOP) { - new_height = height + width / 2; - g_debug("Extending image height from %d pixels to %d pixels", height, new_height); - } - else { - new_width = width + height / 2; - g_debug("Extending image width from %d pixels to %d pixels", width, new_width); - } - image = gdk_pixbuf_new (GDK_COLORSPACE_RGB, FALSE, - 8, new_width, new_height); - - /* Copy old data */ - gdk_pixbuf_fill (page->priv->image, 0xFFFFFFFF); - if (page->priv->orientation == TOP_TO_BOTTOM || page->priv->orientation == LEFT_TO_RIGHT) - gdk_pixbuf_copy_area (page->priv->image, 0, 0, width, height, - image, 0, 0); - else - gdk_pixbuf_copy_area (page->priv->image, 0, 0, width, height, - image, new_width - width, new_height - height); - - g_object_unref (page->priv->image); - page->priv->image = image; + rows = page->priv->n_rows; + page->priv->n_rows = rows + page->priv->width / 2; + g_debug("Extending image from %d lines to %d lines", rows, page->priv->n_rows); + page->priv->pixels = g_realloc (page->priv->pixels, page->priv->n_rows * page->priv->rowstride); *size_changed = TRUE; } - - switch (page->priv->orientation) { - case TOP_TO_BOTTOM: - x = 0; - y = line_number; - x_step = 1; - y_step = 0; - break; - case BOTTOM_TO_TOP: - x = page_get_width (page) - 1; - y = page_get_height (page) - line_number - 1; - x_step = -1; - y_step = 0; - break; - case LEFT_TO_RIGHT: - x = line_number; - y = page_get_height (page) - 1; - x_step = 0; - y_step = -1; - break; - case RIGHT_TO_LEFT: - x = page_get_width (page) - line_number - 1; - y = 0; - x_step = 0; - y_step = 1; - break; - } - pixels = gdk_pixbuf_get_pixels (page->priv->image); - rowstride = gdk_pixbuf_get_rowstride (page->priv->image); - n_channels = gdk_pixbuf_get_n_channels (page->priv->image); - for (i = 0; i < line->width; i++) { - guchar *pixel; - - pixel = pixels + y * rowstride + x * n_channels; - set_pixel (line, n, i, pixel); - x += x_step; - y += y_step; - } + + /* Copy in new row */ + memcpy (page->priv->pixels + line_number * page->priv->rowstride, line->data + n * line->data_length, line->data_length); page->priv->scan_line = line_number; } @@ -349,7 +199,7 @@ page_parse_scan_line (Page *page, ScanLine *line) if (size_changed) g_signal_emit (page, signals[SIZE_CHANGED], 0); g_signal_emit (page, signals[SCAN_LINE_CHANGED], 0); - g_signal_emit (page, signals[IMAGE_CHANGED], 0); + g_signal_emit (page, signals[PIXELS_CHANGED], 0); } @@ -361,35 +211,15 @@ page_finish (Page *page) g_return_if_fail (page != NULL); /* Trim page */ - if (page->priv->rows < 0 && - page->priv->scan_line != gdk_pixbuf_get_height (page->priv->image)) { - GdkPixbuf *image; - gint width, height, new_width, new_height; - - new_width = width = gdk_pixbuf_get_width (page->priv->image); - new_height = height = gdk_pixbuf_get_height (page->priv->image); - if (page->priv->orientation == TOP_TO_BOTTOM || page->priv->orientation == BOTTOM_TO_TOP) { - new_height = page->priv->scan_line; - g_debug("Trimming image height from %d pixels to %d pixels", height, new_height); - } - else { - new_width = page->priv->scan_line; - g_debug("Trimming image width from %d pixels to %d pixels", width, new_width); - } - image = gdk_pixbuf_new (GDK_COLORSPACE_RGB, FALSE, - 8, - new_width, new_height); - - /* Copy old data */ - if (page->priv->orientation == TOP_TO_BOTTOM || page->priv->orientation == LEFT_TO_RIGHT) - gdk_pixbuf_copy_area (page->priv->image, 0, 0, width, height, - image, 0, 0); - else - gdk_pixbuf_copy_area (page->priv->image, width - new_width, height - new_height, width, height, - image, 0, 0); - - g_object_unref (page->priv->image); - page->priv->image = image; + if (page->priv->expected_rows < 0 && + page->priv->scan_line != page_get_scan_height (page)) { + gint rows; + + rows = page->priv->n_rows; + page->priv->n_rows = page->priv->scan_line; + page->priv->pixels = g_realloc (page->priv->pixels, page->priv->n_rows * page->priv->rowstride); + g_debug("Trimming page from %d lines to %d lines", rows, page->priv->n_rows); + size_changed = TRUE; } page->priv->scanning = FALSE; @@ -400,47 +230,36 @@ page_finish (Page *page) } -Orientation -page_get_orientation (Page *page) +ScanDirection +page_get_scan_direction (Page *page) { g_return_val_if_fail (page != NULL, TOP_TO_BOTTOM); - return page->priv->orientation; + return page->priv->scan_direction; } -void -page_set_orientation (Page *page, Orientation orientation) +static void +page_set_scan_direction (Page *page, ScanDirection scan_direction) { gint left_steps, t; - GdkPixbuf *image; gboolean size_changed = FALSE; gint width, height; g_return_if_fail (page != NULL); - if (page->priv->orientation == orientation) + if (page->priv->scan_direction == scan_direction) return; /* Work out how many times it has been rotated to the left */ - left_steps = orientation - page->priv->orientation; + left_steps = scan_direction - page->priv->scan_direction; if (left_steps < 0) left_steps += 4; + if (left_steps != 2) + size_changed = TRUE; width = page_get_width (page); height = page_get_height (page); - - /* Rotate image */ - if (left_steps == 1) - image = gdk_pixbuf_rotate_simple (page->priv->image, GDK_PIXBUF_ROTATE_COUNTERCLOCKWISE); - else if (left_steps == 2) - image = gdk_pixbuf_rotate_simple (page->priv->image, GDK_PIXBUF_ROTATE_UPSIDEDOWN); - else - image = gdk_pixbuf_rotate_simple (page->priv->image, GDK_PIXBUF_ROTATE_CLOCKWISE); - g_object_unref (page->priv->image); - page->priv->image = image; - if (left_steps != 2) - size_changed = TRUE; /* Rotate crop */ if (page->priv->has_crop) { @@ -471,42 +290,42 @@ page_set_orientation (Page *page, Orientation orientation) } } - page->priv->orientation = orientation; + page->priv->scan_direction = scan_direction; if (size_changed) g_signal_emit (page, signals[SIZE_CHANGED], 0); - g_signal_emit (page, signals[IMAGE_CHANGED], 0); - g_signal_emit (page, signals[ORIENTATION_CHANGED], 0); - g_signal_emit (page, signals[CROP_CHANGED], 0); + g_signal_emit (page, signals[SCAN_DIRECTION_CHANGED], 0); + if (page->priv->has_crop) + g_signal_emit (page, signals[CROP_CHANGED], 0); } void page_rotate_left (Page *page) { - Orientation orientation; + ScanDirection scan_direction; g_return_if_fail (page != NULL); - orientation = page_get_orientation (page); - if (orientation == RIGHT_TO_LEFT) - orientation = TOP_TO_BOTTOM; + scan_direction = page_get_scan_direction (page); + if (scan_direction == RIGHT_TO_LEFT) + scan_direction = TOP_TO_BOTTOM; else - orientation++; - page_set_orientation (page, orientation); + scan_direction++; + page_set_scan_direction (page, scan_direction); } void page_rotate_right (Page *page) { - Orientation orientation; + ScanDirection scan_direction; - orientation = page_get_orientation (page); - if (orientation == TOP_TO_BOTTOM) - orientation = RIGHT_TO_LEFT; + scan_direction = page_get_scan_direction (page); + if (scan_direction == TOP_TO_BOTTOM) + scan_direction = RIGHT_TO_LEFT; else - orientation--; - page_set_orientation (page, orientation); + scan_direction--; + page_set_scan_direction (page, scan_direction); } @@ -530,7 +349,11 @@ gint page_get_width (Page *page) { g_return_val_if_fail (page != NULL, 0); - return gdk_pixbuf_get_width (page->priv->image); + + if (page->priv->scan_direction == TOP_TO_BOTTOM || page->priv->scan_direction == BOTTOM_TO_TOP) + return page->priv->width; + else + return page->priv->n_rows; } @@ -538,7 +361,33 @@ gint page_get_height (Page *page) { g_return_val_if_fail (page != NULL, 0); - return gdk_pixbuf_get_height (page->priv->image); + + if (page->priv->scan_direction == TOP_TO_BOTTOM || page->priv->scan_direction == BOTTOM_TO_TOP) + return page->priv->n_rows; + else + return page->priv->width; +} + + +gint +page_get_depth (Page *page) +{ + g_return_val_if_fail (page != NULL, 0); + return page->priv->depth; +} + + +gint page_get_n_channels (Page *page) +{ + g_return_val_if_fail (page != NULL, 0); + return page->priv->n_channels; +} + + +gint page_get_rowstride (Page *page) +{ + g_return_val_if_fail (page != NULL, 0); + return page->priv->rowstride; } @@ -547,10 +396,7 @@ page_get_scan_width (Page *page) { g_return_val_if_fail (page != NULL, 0); - if (page->priv->orientation == TOP_TO_BOTTOM || page->priv->orientation == BOTTOM_TO_TOP) - return gdk_pixbuf_get_width (page->priv->image); - else - return gdk_pixbuf_get_height (page->priv->image); + return page->priv->width; } @@ -559,10 +405,7 @@ page_get_scan_height (Page *page) { g_return_val_if_fail (page != NULL, 0); - if (page->priv->orientation == TOP_TO_BOTTOM || page->priv->orientation == BOTTOM_TO_TOP) - return gdk_pixbuf_get_height (page->priv->image); - else - return gdk_pixbuf_get_width (page->priv->image); + return page->priv->n_rows; } @@ -779,44 +622,136 @@ page_get_named_crop (Page *page) } -GdkPixbuf * -page_get_image (Page *page) +const guchar * +page_get_pixels (Page *page) { g_return_val_if_fail (page != NULL, NULL); - return g_object_ref (page->priv->image); + return page->priv->pixels; +} + + +// FIXME: Copied from page-view, should be shared code +static guchar +get_sample (const guchar *line, gint x, gint depth, gint n_channels, gint channel) +{ + // FIXME + return 0xFF; +} + + +// FIXME: Copied from page-view, should be shared code +static void +get_pixel (Page *page, gint x, gint y, guchar *pixel) +{ + gint t, depth, n_channels; + const guchar *p, *line; + + switch (page_get_scan_direction (page)) + { + case TOP_TO_BOTTOM: + break; + case BOTTOM_TO_TOP: + x = page_get_scan_width (page) - x; + y = page_get_scan_height (page) - y; + break; + case LEFT_TO_RIGHT: + t = x; + x = page_get_scan_width (page) - y; + y = t; + break; + case RIGHT_TO_LEFT: + t = x; + x = y; + y = page_get_scan_height (page) - t; + break; + } + + depth = page_get_depth (page); + n_channels = page_get_n_channels (page); + line = page_get_pixels (page) + page_get_rowstride (page) * y; + + /* Optimise for 8 bit images */ + if (depth == 8 && n_channels == 3) { + p = line + x * n_channels; + pixel[0] = p[0]; + pixel[1] = p[1]; + pixel[2] = p[2]; + return; + } + else if (depth == 8 && n_channels == 1) { + p = line + x; + pixel[0] = pixel[1] = pixel[2] = p[0]; + return; + } + + /* Optimise for bitmaps */ + else if (depth == 1 && n_channels == 1) { + p = line + (x / 8); + pixel[0] = pixel[1] = pixel[2] = p[0] & (0x80 >> (x % 8)) ? 0x00 : 0xFF; + return; + } + + /* Optimise for 2 bit images */ + else if (depth == 2 && n_channels == 1) { + gint sample; + gint block_shift[4] = { 6, 4, 2, 0 }; + + p = line + (x / 4); + sample = (p[0] >> block_shift[x % 4]) & 0x3; + sample = sample * 255 / 3; + + pixel[0] = pixel[1] = pixel[2] = sample; + return; + } + + /* Use slow method */ + pixel[0] = get_sample (line, x, depth, n_channels, 0); + pixel[0] = get_sample (line, x, depth, n_channels, 1); + pixel[0] = get_sample (line, x, depth, n_channels, 2); } GdkPixbuf * -page_get_cropped_image (Page *page) +page_get_image (Page *page, gboolean apply_crop) { - GdkPixbuf *image, *cropped_image; - gint x, y, w, h, pw, ph; + GdkPixbuf *image; + gint x, y, l, r, t, b; + + if (apply_crop && page->priv->has_crop) { + l = page->priv->crop_x; + r = l + page->priv->crop_width; + t = page->priv->crop_y; + b = l + page->priv->crop_height; + + if (l < 0) + l = 0; + if (r > page_get_width (page)) + r = page_get_width (page); + if (t < 0) + t = 0; + if (b > page_get_height (page)) + b = page_get_height (page); + } + else { + l = 0; + r = page_get_width (page); + t = 0; + b = page_get_height (page); + } - g_return_val_if_fail (page != NULL, NULL); + image = gdk_pixbuf_new (GDK_COLORSPACE_RGB, FALSE, 8, r - l, b - t); - image = page_get_image (page); + for (y = t; y < b; y++) { + guchar *line = gdk_pixbuf_get_pixels (image) + gdk_pixbuf_get_rowstride (image) * (y - t); + for (x = l; x < r; x++) { + guchar *pixel; - if (!page->priv->has_crop) - return image; - - x = page->priv->crop_x; - y = page->priv->crop_y; - w = page->priv->crop_width; - h = page->priv->crop_height; - pw = gdk_pixbuf_get_width (image); - ph = gdk_pixbuf_get_height (image); - - /* Trim crop */ - if (x + w >= pw) - w = pw - x; - if (y + h >= ph) - h = ph - y; - - cropped_image = gdk_pixbuf_new_subpixbuf (image, x, y, w, h); - g_object_unref (image); + pixel = line + (x - l) * 3; + get_pixel (page, x, y, pixel); + } + } - return cropped_image; + return image; } @@ -864,7 +799,7 @@ page_save (Page *page, const gchar *type, GFile *file, GError **error) if (!stream) return FALSE; - image = page_get_cropped_image (page); + image = page_get_image (page, TRUE); if (page->priv->color_profile != NULL) icc_profile_data = get_icc_data_encoded (page->priv->color_profile); @@ -910,9 +845,7 @@ static void page_finalize (GObject *object) { Page *page = PAGE (object); - if (page->priv->image) - g_object_unref (page->priv->image); - page->priv->image = NULL; + g_free (page->priv->pixels); G_OBJECT_CLASS (page_parent_class)->finalize (object); } @@ -924,11 +857,11 @@ page_class_init (PageClass *klass) object_class->finalize = page_finalize; - signals[IMAGE_CHANGED] = - g_signal_new ("image-changed", + signals[PIXELS_CHANGED] = + g_signal_new ("pixels-changed", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (PageClass, image_changed), + G_STRUCT_OFFSET (PageClass, pixels_changed), NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); @@ -948,11 +881,11 @@ page_class_init (PageClass *klass) NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); - signals[ORIENTATION_CHANGED] = - g_signal_new ("orientation-changed", + signals[SCAN_DIRECTION_CHANGED] = + g_signal_new ("scan-direction-changed", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (PageClass, orientation_changed), + G_STRUCT_OFFSET (PageClass, scan_direction_changed), NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); @@ -973,5 +906,5 @@ static void page_init (Page *page) { page->priv = G_TYPE_INSTANCE_GET_PRIVATE (page, PAGE_TYPE, PagePrivate); - page->priv->orientation = TOP_TO_BOTTOM; + page->priv->scan_direction = TOP_TO_BOTTOM; } @@ -28,7 +28,7 @@ typedef enum LEFT_TO_RIGHT, BOTTOM_TO_TOP, RIGHT_TO_LEFT -} Orientation; +} ScanDirection; typedef struct PagePrivate PagePrivate; @@ -43,31 +43,38 @@ typedef struct { GObjectClass parent_class; - void (*image_changed) (Page *page); + void (*pixels_changed) (Page *page); void (*size_changed) (Page *page); void (*scan_line_changed) (Page *page); - void (*orientation_changed) (Page *page); + void (*scan_direction_changed) (Page *page); void (*crop_changed) (Page *page); } PageClass; GType page_get_type (void); -Page *page_new (void); +Page *page_new (gint width, gint height, gint dpi, ScanDirection scan_direction); -// FIXME: Should be part of page_new -void page_setup (Page *page, gint width, gint height, gint dpi, Orientation orientation); - -void page_set_scan_area (Page *page, gint width, gint rows, gint dpi); +void page_set_page_info (Page *page, ScanPageInfo *info); gint page_get_dpi (Page *page); -gboolean page_is_landscape (Page *page); - gint page_get_width (Page *page); gint page_get_height (Page *page); +gint page_get_depth (Page *page); + +gint page_get_n_channels (Page *page); + +gint page_get_rowstride (Page *page); + +const guchar *page_get_pixels (Page *page); + +guchar *page_get_pixel (Page *page, gint x, gint y); + +gboolean page_is_landscape (Page *page); + gint page_get_scan_width (Page *page); gint page_get_scan_height (Page *page); @@ -84,17 +91,13 @@ gboolean page_has_data (Page *page); gboolean page_is_color (Page *page); -gint page_get_depth (Page *page); - gint page_get_scan_line (Page *page); void page_parse_scan_line (Page *page, ScanLine *line); void page_finish (Page *page); -Orientation page_get_orientation (Page *page); - -void page_set_orientation (Page *page, Orientation orientation); +ScanDirection page_get_scan_direction (Page *page); void page_rotate_left (Page *page); @@ -116,9 +119,7 @@ void page_get_crop (Page *page, gint *x, gint *y, gint *width, gint *height); gchar *page_get_named_crop (Page *page); -GdkPixbuf *page_get_image (Page *page); - -GdkPixbuf *page_get_cropped_image (Page *page); +GdkPixbuf *page_get_image (Page *page, gboolean apply_crop); gboolean page_save (Page *page, const gchar *type, GFile *file, GError **error); diff --git a/src/scanner.c b/src/scanner.c index 8383f9f..f4eb08f 100644 --- a/src/scanner.c +++ b/src/scanner.c @@ -1160,7 +1160,7 @@ do_get_option (Scanner *scanner) } else if (strcmp (option->name, "three-pass") == 0) { set_bool_option (scanner->priv->handle, option, option_index, FALSE, NULL); - } + } else if (strcmp (option->name, "test-picture") == 0) { set_string_option (scanner->priv->handle, option, option_index, "Color pattern", NULL); } @@ -1255,6 +1255,11 @@ do_get_parameters (Scanner *scanner) info->width = scanner->priv->parameters.pixels_per_line; info->height = scanner->priv->parameters.lines; info->depth = scanner->priv->parameters.depth; + /* Reduce bit depth if requested lower than received */ + // FIXME: This a hack and only works on 8 bit gray to 2 bit gray + if (scanner->priv->parameters.depth == 8 && scanner->priv->parameters.format == SANE_FRAME_GRAY && job->depth == 2 && job->scan_mode == SCAN_MODE_GRAY) + info->depth = job->depth; + info->n_channels = scanner->priv->parameters.format == SANE_FRAME_GRAY ? 1 : 3; info->dpi = job->dpi; // FIXME: This is the requested DPI, not the actual DPI info->device = g_strdup (scanner->priv->current_device); @@ -1308,10 +1313,13 @@ do_complete_page (Scanner *scanner) static void do_read (Scanner *scanner) { + ScanJob *job; SANE_Status status; SANE_Int n_to_read, n_read; gboolean full_read = FALSE; + job = (ScanJob *) scanner->priv->job_queue->data; + /* Read as many bytes as we expect */ n_to_read = scanner->priv->buffer_size - scanner->priv->n_used; @@ -1351,19 +1359,19 @@ do_read (Scanner *scanner) line = g_malloc(sizeof(ScanLine)); switch (scanner->priv->parameters.format) { case SANE_FRAME_GRAY: - line->format = LINE_GRAY; + line->channel = 0; break; case SANE_FRAME_RGB: - line->format = LINE_RGB; + line->channel = -1; break; case SANE_FRAME_RED: - line->format = LINE_RED; + line->channel = 0; break; case SANE_FRAME_GREEN: - line->format = LINE_GREEN; + line->channel = 1; break; case SANE_FRAME_BLUE: - line->format = LINE_BLUE; + line->channel = 2; break; } line->width = scanner->priv->parameters.pixels_per_line; @@ -1387,6 +1395,56 @@ do_read (Scanner *scanner) scanner->priv->buffer[i] = line->data[i + (line->n_lines * line->data_length)]; scanner->priv->n_used++; } + + /* Reduce bit depth if requested lower than received */ + // FIXME: This a hack and only works on 8 bit gray to 2 bit gray + if (scanner->priv->parameters.depth == 8 && scanner->priv->parameters.format == SANE_FRAME_GRAY && + job->depth == 2 && job->scan_mode == SCAN_MODE_GRAY) { + gint block_shift = 6; + guchar block = 0; + guchar *write_ptr = line->data; + + for (i = 0; i < line->n_lines; i++) { + guchar *in_line = line->data + i * line->data_length; + gint x; + + for (x = 0; x < line->width; x++) { + guchar *p = in_line + x; + guchar sample; + + if (p[0] >= 192) + sample = 3; + else if (p[0] >= 128) + sample = 2; + else if (p[0] >= 64) + sample = 1; + else + sample = 0; + + block |= sample << block_shift; + if (block_shift == 0) { + *write_ptr = block; + write_ptr++; + block = 0; + block_shift = 6; + } + else { + block_shift -= 2; + } + } + + /* Finish each line on a byte boundary */ + if (block_shift != 6) { + *write_ptr = block; + write_ptr++; + block = 0; + block_shift = 6; + } + } + + line->data_length = (line->width * 2 + 7) / 8; + } + emit_signal (scanner, GOT_LINE, line); } } diff --git a/src/scanner.h b/src/scanner.h index 5acdc84..72a4ffd 100644 --- a/src/scanner.h +++ b/src/scanner.h @@ -33,6 +33,9 @@ typedef struct /* Bit depth */ gint depth; + /* Number of colour channels */ + gint n_channels; + /* Resolution */ gdouble dpi; @@ -50,14 +53,9 @@ typedef struct /* Width in pixels and format */ gint width, depth; - enum - { - LINE_GRAY, - LINE_RGB, - LINE_RED, - LINE_GREEN, - LINE_BLUE - } format; + + /* Channel for this line or -1 for all channels */ + gint channel; /* Raw line data */ guchar *data; diff --git a/src/simple-scan.c b/src/simple-scan.c index 54236ae..bb7e036 100644 --- a/src/simple-scan.c +++ b/src/simple-scan.c @@ -86,7 +86,7 @@ static Page * append_page () { Page *page; - Orientation orientation = TOP_TO_BOTTOM; + ScanDirection scan_direction = TOP_TO_BOTTOM; gboolean do_crop = FALSE; gchar *named_crop = NULL; gint width = 100, height = 100, dpi = 100, cx, cy, cw, ch; @@ -101,7 +101,7 @@ append_page () /* Copy info from previous page */ if (page) { - orientation = page_get_orientation (page); + scan_direction = page_get_scan_direction (page); width = page_get_width (page); height = page_get_height (page); dpi = page_get_dpi (page); @@ -113,7 +113,7 @@ append_page () } } - page = book_append_page (book, width, height, dpi, orientation); + page = book_append_page (book, width, height, dpi, scan_direction); if (do_crop) { if (named_crop) { page_set_named_crop (page, named_crop); @@ -205,7 +205,7 @@ scanner_page_info_cb (Scanner *scanner, ScanPageInfo *info) /* Add a new page */ page = append_page (); - page_set_scan_area (page, info->width, info->height, info->dpi); + page_set_page_info (page, info); /* Get ICC color profile */ /* FIXME: The ICC profile could change */ @@ -70,7 +70,7 @@ struct SimpleScanPrivate BookView *book_view; gboolean updating_page_menu; gint default_page_width, default_page_height, default_page_dpi; - Orientation default_page_orientation; + ScanDirection default_page_scan_direction; gchar *document_hint; @@ -86,8 +86,8 @@ G_DEFINE_TYPE (SimpleScan, ui, G_TYPE_OBJECT); static struct { const gchar *key; - Orientation orientation; -} orientation_keys[] = + ScanDirection scan_direction; +} scan_direction_keys[] = { { "top-to-bottom", TOP_TO_BOTTOM }, { "bottom-to-top", BOTTOM_TO_TOP }, @@ -326,7 +326,7 @@ add_default_page (SimpleScan *ui) ui->priv->default_page_width, ui->priv->default_page_height, ui->priv->default_page_dpi, - ui->priv->default_page_orientation); + ui->priv->default_page_scan_direction); book_view_select_page (ui->priv->book_view, page); } @@ -742,11 +742,12 @@ get_scan_options (SimpleScan *ui) struct { const gchar *name; ScanMode mode; + gint depth; } profiles[] = { - { "text", SCAN_MODE_LINEART }, - { "photo", SCAN_MODE_COLOR }, - { NULL, SCAN_MODE_COLOR } + { "text", SCAN_MODE_GRAY, 2 }, + { "photo", SCAN_MODE_COLOR, 8 }, + { NULL, SCAN_MODE_COLOR, 8 } }; gint i; ScanOptions *options; @@ -757,7 +758,7 @@ get_scan_options (SimpleScan *ui) options = g_malloc0 (sizeof (ScanOptions)); options->scan_mode = profiles[i].mode; - options->depth = 8; + options->depth = profiles[i].depth; if (options->scan_mode == SCAN_MODE_COLOR) options->dpi = get_photo_dpi (ui); else @@ -1190,7 +1191,7 @@ draw_page (GtkPrintOperation *operation, gtk_print_context_get_dpi_x (print_context) / page_get_dpi (page), gtk_print_context_get_dpi_y (print_context) / page_get_dpi (page)); - image = page_get_cropped_image (page); + image = page_get_image (page, TRUE); gdk_cairo_set_source_pixbuf (context, image, 0, 0); cairo_paint (context); @@ -1324,9 +1325,9 @@ quit (SimpleScan *ui) gconf_client_set_int(ui->priv->client, GCONF_DIR "/window_height", ui->priv->window_height, NULL); gconf_client_set_bool(ui->priv->client, GCONF_DIR "/window_is_maximized", ui->priv->window_is_maximized, NULL); - for (i = 0; orientation_keys[i].key != NULL && orientation_keys[i].orientation != ui->priv->default_page_orientation; i++); - if (orientation_keys[i].key != NULL) - gconf_client_set_string(ui->priv->client, GCONF_DIR "/scan_direction", orientation_keys[i].key, NULL); + for (i = 0; scan_direction_keys[i].key != NULL && scan_direction_keys[i].scan_direction != ui->priv->default_page_scan_direction; i++); + if (scan_direction_keys[i].key != NULL) + gconf_client_set_string(ui->priv->client, GCONF_DIR "/scan_direction", scan_direction_keys[i].key, NULL); gconf_client_set_int (ui->priv->client, GCONF_DIR "/page_width", ui->priv->default_page_width, NULL); gconf_client_set_int (ui->priv->client, GCONF_DIR "/page_height", ui->priv->default_page_height, NULL); gconf_client_set_int (ui->priv->client, GCONF_DIR "/page_dpi", ui->priv->default_page_dpi, NULL); @@ -1408,9 +1409,9 @@ page_size_changed_cb (Page *page, SimpleScan *ui) static void -page_orientation_changed_cb (Page *page, SimpleScan *ui) +page_scan_direction_changed_cb (Page *page, SimpleScan *ui) { - ui->priv->default_page_orientation = page_get_orientation (page); + ui->priv->default_page_scan_direction = page_get_scan_direction (page); } @@ -1420,9 +1421,9 @@ page_added_cb (Book *book, Page *page, SimpleScan *ui) ui->priv->default_page_width = page_get_width (page); ui->priv->default_page_height = page_get_height (page); ui->priv->default_page_dpi = page_get_dpi (page); - ui->priv->default_page_orientation = page_get_orientation (page); + ui->priv->default_page_scan_direction = page_get_scan_direction (page); g_signal_connect (page, "size-changed", G_CALLBACK (page_size_changed_cb), ui); - g_signal_connect (page, "orientation-changed", G_CALLBACK (page_orientation_changed_cb), ui); + g_signal_connect (page, "scan-direction-changed", G_CALLBACK (page_scan_direction_changed_cb), ui); } @@ -1651,12 +1652,12 @@ ui_load (SimpleScan *ui) /* Find default page details */ scan_direction = gconf_client_get_string(ui->priv->client, GCONF_DIR "/scan_direction", NULL); - ui->priv->default_page_orientation = TOP_TO_BOTTOM; + ui->priv->default_page_scan_direction = TOP_TO_BOTTOM; if (scan_direction) { gint i; - for (i = 0; orientation_keys[i].key != NULL && strcmp (orientation_keys[i].key, scan_direction) != 0; i++); - if (orientation_keys[i].key != NULL) - ui->priv->default_page_orientation = orientation_keys[i].orientation; + for (i = 0; scan_direction_keys[i].key != NULL && strcmp (scan_direction_keys[i].key, scan_direction) != 0; i++); + if (scan_direction_keys[i].key != NULL) + ui->priv->default_page_scan_direction = scan_direction_keys[i].scan_direction; g_free (scan_direction); } ui->priv->default_page_width = gconf_client_get_int (ui->priv->client, GCONF_DIR "/page_width", NULL); |