Mercurial > geeqie
diff src/pixbuf-renderer.c @ 398:c4080362d619
image post-processing (rotation and color management) moved to
pixbuf-renderer
author | nadvornik |
---|---|
date | Thu, 17 Apr 2008 17:44:54 +0000 |
parents | 0226daf8c30b |
children | d9afd1636ed8 |
line wrap: on
line diff
--- a/src/pixbuf-renderer.c Thu Apr 17 17:35:51 2008 +0000 +++ b/src/pixbuf-renderer.c Thu Apr 17 17:44:54 2008 +0000 @@ -63,6 +63,18 @@ */ #define PR_MIN_SCALE_SIZE 8 +typedef enum { + EXIF_ORIENTATION_UNKNOWN = 0, + EXIF_ORIENTATION_TOP_LEFT = 1, + EXIF_ORIENTATION_TOP_RIGHT = 2, + EXIF_ORIENTATION_BOTTOM_RIGHT = 3, + EXIF_ORIENTATION_BOTTOM_LEFT = 4, + EXIF_ORIENTATION_LEFT_TOP = 5, + EXIF_ORIENTATION_RIGHT_TOP = 6, + EXIF_ORIENTATION_RIGHT_BOTTOM = 7, + EXIF_ORIENTATION_LEFT_BOTTOM = 8 +} ExifOrientationType; + typedef enum { TILE_RENDER_NONE = 0, /* do nothing */ @@ -491,6 +503,8 @@ pr->source_tiles_enabled = FALSE; pr->source_tiles = NULL; + pr->orientation = 1; + gtk_widget_set_double_buffered(box, FALSE); g_signal_connect_after(G_OBJECT(box), "size_allocate", G_CALLBACK(pr_size_cb), pr); @@ -508,6 +522,7 @@ pr_tile_free_all(pr); if (pr->pixbuf) g_object_unref(pr->pixbuf); + if (pr->spare_tile) gdk_pixbuf_unref(pr->spare_tile); pr_scroller_timer_set(pr, FALSE); pr_overlay_list_clear(pr); @@ -2002,12 +2017,13 @@ pr->tile_cache_size += size; } - if ((pr->zoom != 1.0 || pr->source_tiles_enabled || (pr->pixbuf && gdk_pixbuf_get_has_alpha(pr->pixbuf)) ) && - !it->pixbuf) + if ((pr->zoom != 1.0 || pr->source_tiles_enabled || (pr->pixbuf && gdk_pixbuf_get_has_alpha(pr->pixbuf)) || + pr->orientation != EXIF_ORIENTATION_TOP_LEFT || pr->func_post_process) && !it->pixbuf) { GdkPixbuf *pixbuf; guint size; - +#if 0 +/* I don't think that we need a pixbuf with alpha channel here */ if (pr->pixbuf) { pixbuf = gdk_pixbuf_new(gdk_pixbuf_get_colorspace(pr->pixbuf), @@ -2016,6 +2032,7 @@ pr->tile_width, pr->tile_height); } else +#endif { pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, FALSE, 8, pr->tile_width, pr->tile_height); } @@ -2034,6 +2051,362 @@ * drawing *------------------------------------------------------------------- */ + + +static void pr_tile_coords_map_orientation(PixbufRenderer *pr, + double tile_x, double tile_y, /* coordinates of the tile */ + gint image_w, gint image_h, + double tile_w, double tile_h, + double *res_x, double *res_y) +{ + *res_x = tile_x; + *res_y = tile_y; + switch (pr->orientation) + { + case EXIF_ORIENTATION_TOP_LEFT: + /* normal -- nothing to do */ + break; + case EXIF_ORIENTATION_TOP_RIGHT: + /* mirrored */ + *res_x = image_w - tile_x - tile_w; + break; + case EXIF_ORIENTATION_BOTTOM_RIGHT: + /* upside down */ + *res_x = image_w - tile_x - tile_w; + *res_y = image_h - tile_y - tile_h; + break; + case EXIF_ORIENTATION_BOTTOM_LEFT: + /* flipped */ + *res_y = image_h - tile_y - tile_h; + break; + case EXIF_ORIENTATION_LEFT_TOP: + *res_x = tile_y; + *res_y = tile_x; + break; + case EXIF_ORIENTATION_RIGHT_TOP: + /* rotated -90 (270) */ + *res_x = tile_y; + *res_y = image_w - tile_x - tile_w; + break; + case EXIF_ORIENTATION_RIGHT_BOTTOM: + *res_x = image_h - tile_y - tile_h; + *res_y = image_w - tile_x - tile_w; + break; + case EXIF_ORIENTATION_LEFT_BOTTOM: + /* rotated 90 */ + *res_x = image_h - tile_y - tile_h; + *res_y = tile_x; + break; + default: + /* The other values are out of range */ + break; + } +// printf("tile coord y:%f, ih:%d, th:%f ry:%f\n", tile_y, image_h, tile_h, *res_x); +} + +static void pr_tile_region_map_orientation(PixbufRenderer *pr, + gint area_x, gint area_y, /* coordinates of the area inside tile */ + gint tile_w, gint tile_h, + gint area_w, gint area_h, + gint *res_x, gint *res_y, + gint *res_w, gint *res_h) +{ + *res_x = area_x; + *res_y = area_y; + *res_w = area_w; + *res_h = area_h; + + switch (pr->orientation) + { + case EXIF_ORIENTATION_TOP_LEFT: + /* normal -- nothing to do */ + break; + case EXIF_ORIENTATION_TOP_RIGHT: + /* mirrored */ + *res_x = tile_w - area_x - area_w; + break; + case EXIF_ORIENTATION_BOTTOM_RIGHT: + /* upside down */ + *res_x = tile_w - area_x - area_w; + *res_y = tile_h - area_y - area_h; + break; + case EXIF_ORIENTATION_BOTTOM_LEFT: + /* flipped */ + *res_y = tile_h - area_y - area_h; + break; + case EXIF_ORIENTATION_LEFT_TOP: + *res_x = area_y; + *res_y = area_x; + *res_w = area_h; + *res_h = area_w; + break; + case EXIF_ORIENTATION_RIGHT_TOP: + /* rotated -90 (270) */ + *res_x = area_y; + *res_y = tile_w - area_x - area_w; + *res_w = area_h; + *res_h = area_w; + break; + case EXIF_ORIENTATION_RIGHT_BOTTOM: + *res_x = tile_h - area_y - area_h; + *res_y = tile_w - area_x - area_w; + *res_w = area_h; + *res_h = area_w; + break; + case EXIF_ORIENTATION_LEFT_BOTTOM: + /* rotated 90 */ + *res_x = tile_h - area_y - area_h; + *res_y = area_x; + *res_w = area_h; + *res_h = area_w; + break; + default: + /* The other values are out of range */ + break; + } +// printf("inside y:%d, th:%d, ah:%d ry:%d\n", area_y, tile_h, area_h, *res_x); +} + +static void pr_coords_map_orientation_reverse(PixbufRenderer *pr, + gint area_x, gint area_y, + gint tile_w, gint tile_h, + gint area_w, gint area_h, + gint *res_x, gint *res_y, + gint *res_w, gint *res_h) +{ + *res_x = area_x; + *res_y = area_y; + *res_w = area_w; + *res_h = area_h; + + switch (pr->orientation) + { + case EXIF_ORIENTATION_TOP_LEFT: + /* normal -- nothing to do */ + break; + case EXIF_ORIENTATION_TOP_RIGHT: + /* mirrored */ + *res_x = tile_w - area_x - area_w; + break; + case EXIF_ORIENTATION_BOTTOM_RIGHT: + /* upside down */ + *res_x = tile_w - area_x - area_w; + *res_y = tile_h - area_y - area_h; + break; + case EXIF_ORIENTATION_BOTTOM_LEFT: + /* flipped */ + *res_y = tile_h - area_y - area_h; + break; + case EXIF_ORIENTATION_LEFT_TOP: + *res_x = area_y; + *res_y = area_x; + *res_w = area_h; + *res_h = area_w; + break; + case EXIF_ORIENTATION_RIGHT_TOP: + /* rotated -90 (270) */ + *res_x = tile_w - area_y - area_h; + *res_y = area_x; + *res_w = area_h; + *res_h = area_w; + break; + case EXIF_ORIENTATION_RIGHT_BOTTOM: + *res_x = tile_w - area_y - area_h; + *res_y = tile_h - area_x - area_w; + *res_w = area_h; + *res_h = area_w; + break; + case EXIF_ORIENTATION_LEFT_BOTTOM: + /* rotated 90 */ + *res_x = area_y; + *res_y = tile_h - area_x - area_w; + *res_w = area_h; + *res_h = area_w; + break; + default: + /* The other values are out of range */ + break; + } +} + + +static GdkPixbuf *pr_get_spare_tile(PixbufRenderer *pr) +{ + if (!pr->spare_tile) pr->spare_tile = gdk_pixbuf_new(GDK_COLORSPACE_RGB, FALSE, 8, pr->tile_width, pr->tile_height); + return pr->spare_tile; +} + +static void pr_tile_rotate_90(PixbufRenderer *pr, GdkPixbuf **tile, gint counter_clockwise, gint x, gint y, gint w, gint h) +{ + GdkPixbuf *src = *tile; + GdkPixbuf *dest; + gint srs; + gint drs; + guchar *s_pix; + guchar *d_pix; + guchar *sp; + guchar *dp; + gint i, j; + gint a = 3; + + gint tw = pr->tile_width; + gint th = pr->tile_height; + + + srs = gdk_pixbuf_get_rowstride(src); + s_pix = gdk_pixbuf_get_pixels(src); + + dest = pr_get_spare_tile(pr); + drs = gdk_pixbuf_get_rowstride(dest); + d_pix = gdk_pixbuf_get_pixels(dest); + + for (i = y; i < y + h; i++) + { + sp = s_pix + (i * srs) + (x * a); + for (j = x; j < x + w; j++) + { + if (counter_clockwise) + { + dp = d_pix + ((th - j - 1) * drs) + (i * a); + } + else + { + dp = d_pix + (j * drs) + ((tw - i - 1) * a); + } + + *(dp++) = *(sp++); /* r */ + *(dp++) = *(sp++); /* g */ + *(dp++) = *(sp++); /* b */ + } + } + + pr->spare_tile = src; + *tile = dest; +} + +/* + * Returns a copy of pixbuf mirrored and or flipped. + * TO do a 180 degree rotations set both mirror and flipped TRUE + * if mirror and flip are FALSE, result is a simple copy. + */ +static void pr_tile_mirror(PixbufRenderer *pr, GdkPixbuf **tile, gint mirror, gint flip, gint x, gint y, gint w, gint h) +{ + GdkPixbuf *src = *tile; + GdkPixbuf *dest; + gint srs; + gint drs; + guchar *s_pix; + guchar *d_pix; + guchar *sp; + guchar *dp; + gint i, j; + gint a = 3; + + gint tw = pr->tile_width; + gint th = pr->tile_height; + + srs = gdk_pixbuf_get_rowstride(src); + s_pix = gdk_pixbuf_get_pixels(src); + + dest = pr_get_spare_tile(pr); + drs = gdk_pixbuf_get_rowstride(dest); + d_pix = gdk_pixbuf_get_pixels(dest); + + for (i = y; i < y + h; i++) + { + sp = s_pix + (i * srs) + (x * a); + if (flip) + { + dp = d_pix + ((th - i - 1) * drs); + } + else + { + dp = d_pix + (i * drs); + } + if (mirror) + { + dp += (tw - x - 1) * a; + for (j = 0; j < w; j++) + { + *(dp++) = *(sp++); /* r */ + *(dp++) = *(sp++); /* g */ + *(dp++) = *(sp++); /* b */ + dp -= (a + 3); + } + } + else + { + dp += x * a; + for (j = 0; j < w; j++) + { + *(dp++) = *(sp++); /* r */ + *(dp++) = *(sp++); /* g */ + *(dp++) = *(sp++); /* b */ + } + } + } + + pr->spare_tile = src; + *tile = dest; +} + + +static void pr_tile_apply_orientation(PixbufRenderer *pr, GdkPixbuf **pixbuf, gint x, gint y, gint w, gint h) +{ + switch (pr->orientation) + { + case EXIF_ORIENTATION_TOP_LEFT: + /* normal -- nothing to do */ + break; + case EXIF_ORIENTATION_TOP_RIGHT: + /* mirrored */ + { + pr_tile_mirror(pr, pixbuf, TRUE, FALSE, x, y, w, h); + } + break; + case EXIF_ORIENTATION_BOTTOM_RIGHT: + /* upside down */ + { + pr_tile_mirror(pr, pixbuf, TRUE, TRUE, x, y, w, h); + } + break; + case EXIF_ORIENTATION_BOTTOM_LEFT: + /* flipped */ + { + pr_tile_mirror(pr, pixbuf, FALSE, TRUE, x, y, w, h); + } + break; + case EXIF_ORIENTATION_LEFT_TOP: + { + pr_tile_mirror(pr, pixbuf, FALSE, TRUE, x, y, w, h); + pr_tile_rotate_90(pr, pixbuf, FALSE, x, pr->tile_height - y - h, w, h); + } + break; + case EXIF_ORIENTATION_RIGHT_TOP: + /* rotated -90 (270) */ + { + pr_tile_rotate_90(pr, pixbuf, FALSE, x, y, w, h); + } + break; + case EXIF_ORIENTATION_RIGHT_BOTTOM: + { + pr_tile_mirror(pr, pixbuf, FALSE, TRUE, x, y, w, h); + pr_tile_rotate_90(pr, pixbuf, TRUE, x, pr->tile_height - y - h, w, h); + } + break; + case EXIF_ORIENTATION_LEFT_BOTTOM: + /* rotated 90 */ + { + pr_tile_rotate_90(pr, pixbuf, TRUE, x, y, w, h); + } + break; + default: + /* The other values are out of range */ + break; + } + +} + static void pr_tile_render(PixbufRenderer *pr, ImageTile *it, gint x, gint y, gint w, gint h, @@ -2084,36 +2457,80 @@ } else if (pr->zoom == 1.0 || pr->scale == 1.0) { + + double src_x, src_y; + gint pb_x, pb_y; + gint pb_w, pb_h; + pr_tile_coords_map_orientation(pr, it->x, it->y, + pr->image_width, pr->image_height, + pr->tile_width, pr->tile_height, + &src_x, &src_y); + pr_tile_region_map_orientation(pr, x, y, + pr->tile_width, pr->tile_height, + w, h, + &pb_x, &pb_y, + &pb_w, &pb_h); + if (has_alpha) { - gdk_pixbuf_composite_color(pr->pixbuf, it->pixbuf, x, y, w, h, - (double) 0.0 - it->x, - (double) 0.0 - it->y, + gdk_pixbuf_composite_color(pr->pixbuf, it->pixbuf, pb_x, pb_y, pb_w, pb_h, + (double) 0.0 - src_x, + (double) 0.0 - src_y, 1.0, 1.0, GDK_INTERP_NEAREST, - 255, it->x + x, it->y + y, + 255, it->x + pb_x, it->y + pb_y, PR_ALPHA_CHECK_SIZE, PR_ALPHA_CHECK1, PR_ALPHA_CHECK2); + pr_tile_apply_orientation(pr, &it->pixbuf, pb_x, pb_y, pb_w, pb_h); draw = TRUE; } else { - /* faster, simple */ - gdk_draw_pixbuf(it->pixmap, - box->style->fg_gc[GTK_WIDGET_STATE(box)], - pr->pixbuf, - it->x + x, it->y + y, - x, y, - w, h, - pr->dither_quality, it->x + x, it->y + y); + + + if (pr->orientation == EXIF_ORIENTATION_TOP_LEFT && !(pr->func_post_process && !(pr->post_process_slow && fast))) + { + /* faster, simple, base orientation, no postprocessing */ + gdk_draw_pixbuf(it->pixmap, + box->style->fg_gc[GTK_WIDGET_STATE(box)], + pr->pixbuf, + it->x + x, it->y + y, + x, y, + w, h, + pr->dither_quality, it->x + x, it->y + y); + } + else + { + gdk_pixbuf_copy_area(pr->pixbuf, + src_x + pb_x, src_y + pb_y, + pb_w, pb_h, + it->pixbuf, + pb_x, pb_y); + pr_tile_apply_orientation(pr, &it->pixbuf, pb_x, pb_y, pb_w, pb_h); + draw = TRUE; + } } } else { double scale_x, scale_y; + double src_x, src_y; + gint pb_x, pb_y; + gint pb_w, pb_h; if (pr->image_width == 0 || pr->image_height == 0) return; + scale_x = (double)pr->width / pr->image_width; scale_y = (double)pr->height / pr->image_height; + pr_tile_coords_map_orientation(pr, it->x / scale_x, it->y /scale_y , + pr->image_width, pr->image_height, + pr->tile_width / scale_x , pr->tile_height / scale_y, + &src_x, &src_y); + pr_tile_region_map_orientation(pr, x, y, + pr->tile_width, pr->tile_height, + w, h, + &pb_x, &pb_y, + &pb_w, &pb_h); + /* HACK: The pixbuf scalers get kinda buggy(crash) with extremely * small sizes for anything but GDK_INTERP_NEAREST */ @@ -2121,27 +2538,32 @@ if (!has_alpha) { - gdk_pixbuf_scale(pr->pixbuf, it->pixbuf, x, y, w, h, - (double) 0.0 - it->x, - (double) 0.0 - it->y, + gdk_pixbuf_scale(pr->pixbuf, it->pixbuf, pb_x, pb_y, pb_w, pb_h, + (double) 0.0 - src_x * scale_x, + (double) 0.0 - src_y * scale_y, scale_x, scale_y, (fast) ? GDK_INTERP_NEAREST : pr->zoom_quality); } else { - gdk_pixbuf_composite_color(pr->pixbuf, it->pixbuf, x, y, w, h, - (double) 0.0 - it->x, - (double) 0.0 - it->y, + gdk_pixbuf_composite_color(pr->pixbuf, it->pixbuf, pb_x, pb_y, pb_w, pb_h, + (double) 0.0 - src_x * scale_x, + (double) 0.0 - src_y * scale_y, scale_x, scale_y, (fast) ? GDK_INTERP_NEAREST : pr->zoom_quality, - 255, it->x + x, it->y + y, + 255, it->x + pb_x, it->y + pb_y, PR_ALPHA_CHECK_SIZE, PR_ALPHA_CHECK1, PR_ALPHA_CHECK2); } + pr_tile_apply_orientation(pr, &it->pixbuf, pb_x, pb_y, pb_w, pb_h); draw = TRUE; } if (draw && it->pixbuf && !it->blank) { + + if (pr->func_post_process && !(pr->post_process_slow && fast)) + pr->func_post_process(pr, &it->pixbuf, x, y, w, h, pr->post_process_user_data); + gdk_draw_pixbuf(it->pixmap, box->style->fg_gc[GTK_WIDGET_STATE(box)], it->pixbuf, @@ -2155,6 +2577,8 @@ /* enable this line for debugging the edges of tiles */ gdk_draw_rectangle(it->pixmap, box->style->white_gc, FALSE, 0, 0, it->w, it->h); + gdk_draw_rectangle(it->pixmap, box->style->white_gc, + FALSE, x, y, w, h); #endif } @@ -2214,7 +2638,7 @@ if (pr->draw_queue) { qd = pr->draw_queue->data; - fast = (pr->zoom_2pass && pr->zoom_quality != GDK_INTERP_NEAREST && pr->scale != 1.0); + fast = ((pr->zoom_2pass && pr->zoom_quality != GDK_INTERP_NEAREST && pr->scale != 1.0) || pr->post_process_slow); } else { @@ -3222,6 +3646,23 @@ * public *------------------------------------------------------------------- */ +static void pr_pixbuf_size_sync(PixbufRenderer *pr) +{ + if (!pr->pixbuf) return; + switch (pr->orientation) + { + case EXIF_ORIENTATION_LEFT_TOP: + case EXIF_ORIENTATION_RIGHT_TOP: + case EXIF_ORIENTATION_RIGHT_BOTTOM: + case EXIF_ORIENTATION_LEFT_BOTTOM: + pr->image_width = gdk_pixbuf_get_height(pr->pixbuf); + pr->image_height = gdk_pixbuf_get_width(pr->pixbuf); + break; + default: + pr->image_width = gdk_pixbuf_get_width(pr->pixbuf); + pr->image_height = gdk_pixbuf_get_height(pr->pixbuf); + } +} static void pr_pixbuf_sync(PixbufRenderer *pr, gdouble zoom) { @@ -3246,10 +3687,8 @@ return; } - - pr->image_width = gdk_pixbuf_get_width(pr->pixbuf); - pr->image_height = gdk_pixbuf_get_height(pr->pixbuf); - + + pr_pixbuf_size_sync(pr); pr_zoom_sync(pr, zoom, TRUE, TRUE, FALSE, 0, 0); } @@ -3280,6 +3719,33 @@ return pr->pixbuf; } +void pixbuf_renderer_set_orientation(PixbufRenderer *pr, gint orientation) +{ + g_return_if_fail(IS_PIXBUF_RENDERER(pr)); + + pr->orientation = orientation; + + pr_pixbuf_size_sync(pr); + pr_zoom_sync(pr, pr->zoom, TRUE, FALSE, FALSE, 0, 0); +} + +gint pixbuf_renderer_get_orientation(PixbufRenderer *pr) +{ + if (!pr) return 1; + return pr->orientation; +} + +void pixbuf_renderer_set_post_process_func(PixbufRenderer *pr, PixbufRendererPostProcessFunc func, gpointer user_data, gint slow) +{ + g_return_if_fail(IS_PIXBUF_RENDERER(pr)); + + pr->func_post_process = func; + pr->post_process_user_data = user_data; + pr->post_process_slow = func && slow; + +} + + void pixbuf_renderer_move(PixbufRenderer *pr, PixbufRenderer *source) { GObject *object; @@ -3304,6 +3770,11 @@ scroll_reset = pr->scroll_reset; pr->scroll_reset = PR_SCROLL_RESET_NOCHANGE; + pr->func_post_process = source->func_post_process; + pr->post_process_user_data = source->post_process_user_data; + pr->post_process_slow = source->post_process_slow; + pr->orientation = source->orientation; + if (source->source_tiles_enabled) { pr_source_tile_unset(pr); @@ -3337,12 +3808,19 @@ pr_tile_free_all(source); } -void pixbuf_renderer_area_changed(PixbufRenderer *pr, gint x, gint y, gint width, gint height) +void pixbuf_renderer_area_changed(PixbufRenderer *pr, gint src_x, gint src_y, gint src_w, gint src_h) { - gint x1, y1, x2, y2; + gint x, y, width, height, x1, y1, x2, y2; g_return_if_fail(IS_PIXBUF_RENDERER(pr)); + pr_coords_map_orientation_reverse(pr, + src_x, src_y, + pr->image_width, pr->image_height, + src_w, src_h, + &x, &y, + &width, &height); + if (pr->source_tiles_enabled) { pr_source_tile_changed(pr, x, y, width, height);