diff options
| author | Alessio Treglia <alessio@debian.org> | 2010-08-16 14:20:18 +0200 | 
|---|---|---|
| committer | Alessio Treglia <alessio@debian.org> | 2010-08-16 14:20:18 +0200 | 
| commit | 37152f58469b7595694f7c42555d21d5198e3672 (patch) | |
| tree | 5f23e309f8923a417fcc7e177a95bef584362fa7 /src | |
| parent | 2a72bdeb81030847462135698e1cbff1c685ee18 (diff) | |
| parent | bbf22aaaa2fd5fe725a979f801472d52a1323242 (diff) | |
Merge commit 'upstream/2.31.90.1'
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);  | 
