changeset 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 a7be56e84870
children d071a3555011
files src/color-man.c src/color-man.h src/image.c src/layout_util.c src/menu.c src/pixbuf-renderer.c src/pixbuf-renderer.h src/typedefs.h
diffstat 8 files changed, 697 insertions(+), 77 deletions(-) [+]
line wrap: on
line diff
--- a/src/color-man.c	Thu Apr 17 17:35:51 2008 +0000
+++ b/src/color-man.c	Thu Apr 17 17:44:54 2008 +0000
@@ -277,18 +277,22 @@
 		}
 }
 
-static void color_man_correct_region(ColorMan *cm, gint x, gint y, gint w, gint h,
-				     gint pixbuf_width, gint pixbuf_height)
+void color_man_correct_region(ColorMan *cm, GdkPixbuf *pixbuf, gint x, gint y, gint w, gint h)
 {
 	ColorManCache *cc;
 	guchar *pix;
 	gint rs;
 	gint i;
+	gint pixbuf_width, pixbuf_height;
+	
+
+	pixbuf_width = gdk_pixbuf_get_width(pixbuf);
+	pixbuf_height = gdk_pixbuf_get_height(pixbuf);
 
 	cc = cm->profile;
 
-	pix = gdk_pixbuf_get_pixels(cm->pixbuf);
-	rs = gdk_pixbuf_get_rowstride(cm->pixbuf);
+	pix = gdk_pixbuf_get_pixels(pixbuf);
+	rs = gdk_pixbuf_get_rowstride(pixbuf);
 
 	w = MIN(w, pixbuf_width - x);
 	h = MIN(h, pixbuf_height - y);
@@ -299,10 +303,10 @@
 		guchar *pbuf;
 
 		pbuf = pix + ((y + i) * rs);
+		
 		cmsDoTransform(cc->transform, pbuf, pbuf, w);
 		}
 
-	if (cm->incremental_sync && cm->imd) image_area_changed(cm->imd, x, y, w, h);
 }
 
 static gint color_man_idle_cb(gpointer data)
@@ -310,6 +314,7 @@
 	ColorMan *cm = data;
 	gint width, height;
 	gint rh;
+	if (!cm->pixbuf) return FALSE;
 
 	if (cm->imd &&
 	    cm->pixbuf != image_get_pixbuf(cm->imd))
@@ -335,7 +340,8 @@
 		}
 
 	rh = COLOR_MAN_CHUNK_SIZE / width + 1;
-	color_man_correct_region(cm, 0, cm->row, width, rh, width, height);
+	color_man_correct_region(cm, cm->pixbuf, 0, cm->row, width, rh);
+	if (cm->incremental_sync && cm->imd) image_area_changed(cm->imd, 0, cm->row, width, rh);
 	cm->row += rh;
 
 	return TRUE;
@@ -344,28 +350,24 @@
 static ColorMan *color_man_new_real(ImageWindow *imd, GdkPixbuf *pixbuf,
 				    ColorManProfileType input_type, const gchar *input_file,
 				    unsigned char *input_data, guint input_data_len,
-				    ColorManProfileType screen_type, const gchar *screen_file,
-				    ColorManDoneFunc done_func, gpointer done_data)
+				    ColorManProfileType screen_type, const gchar *screen_file)
 {
 	ColorMan *cm;
 	gint has_alpha;
 
 	if (imd) pixbuf = image_get_pixbuf(imd);
-	if (!pixbuf) return NULL;
 
 	cm = g_new0(ColorMan, 1);
 	cm->imd = imd;
 	cm->pixbuf = pixbuf;
-	g_object_ref(cm->pixbuf);
+	if (cm->pixbuf) g_object_ref(cm->pixbuf);
 
 	cm->incremental_sync = FALSE;
 	cm->row = 0;
 	cm->idle_id = -1;
 
-	cm->func_done = done_func;
-	cm->func_done_data = done_data;
+	has_alpha = pixbuf ? gdk_pixbuf_get_has_alpha(pixbuf) : FALSE;
 
-	has_alpha = gdk_pixbuf_get_has_alpha(pixbuf);
 	cm->profile = color_man_cache_get(input_type, input_file, input_data, input_data_len,
 					  screen_type, screen_file, has_alpha);
 	if (!cm->profile)
@@ -374,31 +376,32 @@
 		return NULL;
 		}
 
-	cm->idle_id = g_idle_add(color_man_idle_cb, cm);
-
 	return cm;
 }
 
 ColorMan *color_man_new(ImageWindow *imd, GdkPixbuf *pixbuf,
 			ColorManProfileType input_type, const gchar *input_file,
-			ColorManProfileType screen_type, const gchar *screen_file,
-			ColorManDoneFunc done_func, gpointer done_data)
+			ColorManProfileType screen_type, const gchar *screen_file)
 {
 	return color_man_new_real(imd, pixbuf,
 				  input_type, input_file, NULL, 0,
-				  screen_type, screen_file,
-				  done_func, done_data);
+				  screen_type, screen_file);
+}
+
+void color_man_start_bg(ColorMan *cm, ColorManDoneFunc done_func, gpointer done_data)
+{
+	cm->func_done = done_func;
+	cm->func_done_data = done_data;
+	cm->idle_id = g_idle_add(color_man_idle_cb, cm);
 }
 
 ColorMan *color_man_new_embedded(ImageWindow *imd, GdkPixbuf *pixbuf,
 				 unsigned char *input_data, guint input_data_len,
-				 ColorManProfileType screen_type, const gchar *screen_file,
-				 ColorManDoneFunc done_func, gpointer done_data)
+				 ColorManProfileType screen_type, const gchar *screen_file)
 {
 	return color_man_new_real(imd, pixbuf,
 				  COLOR_PROFILE_MEM, NULL, input_data, input_data_len,
-				  screen_type, screen_file,
-				  done_func, done_data);
+				  screen_type, screen_file);
 }
 
 void color_man_free(ColorMan *cm)
@@ -424,8 +427,7 @@
 
 ColorMan *color_man_new(ImageWindow *imd, GdkPixbuf *pixbuf,
 			ColorManProfileType input_type, const gchar *input_file,
-			ColorManProfileType screen_type, const gchar *screen_file,
-			ColorManDoneFunc don_func, gpointer done_data)
+			ColorManProfileType screen_type, const gchar *screen_file)
 {
 	/* no op */
 	return NULL;
@@ -433,8 +435,7 @@
 
 ColorMan *color_man_new_embedded(ImageWindow *imd, GdkPixbuf *pixbuf,
 				 unsigned char *input_data, guint input_data_len,
-				 ColorManProfileType screen_type, const gchar *screen_file,
-				 ColorManDoneFunc done_func, gpointer done_data)
+				 ColorManProfileType screen_type, const gchar *screen_file)
 {
 	/* no op */
 	return NULL;
@@ -450,6 +451,15 @@
 	/* no op */
 }
 
+void color_man_correct_region(ColorMan *cm, GdkPixbuf *pixbuf, gint x, gint y, gint w, gint h)
+{
+	/* no op */
+}
+
+void color_man_start_bg(ColorMan *cm, ColorManDoneFunc done_func, gpointer done_data)
+{
+	/* no op */
+}
 
 #endif
 
--- a/src/color-man.h	Thu Apr 17 17:35:51 2008 +0000
+++ b/src/color-man.h	Thu Apr 17 17:44:54 2008 +0000
@@ -47,16 +47,17 @@
 
 ColorMan *color_man_new(ImageWindow *imd, GdkPixbuf *pixbuf,
 			ColorManProfileType input_type, const gchar *input_file,
-			ColorManProfileType screen_type, const gchar *screen_file,
-			ColorManDoneFunc done_func, gpointer done_data);
+			ColorManProfileType screen_type, const gchar *screen_file);
 ColorMan *color_man_new_embedded(ImageWindow *imd, GdkPixbuf *pixbuf,
 				 unsigned char *input_data, guint input_data_len,
-				 ColorManProfileType screen_type, const gchar *screen_file,
-				 ColorManDoneFunc done_func, gpointer done_data);
+				 ColorManProfileType screen_type, const gchar *screen_file);
 void color_man_free(ColorMan *cm);
 
 void color_man_update(void);
 
+void color_man_correct_region(ColorMan *cm, GdkPixbuf *pixbuf, gint x, gint y, gint w, gint h);
+
+void color_man_start_bg(ColorMan *cm, ColorManDoneFunc don_func, gpointer done_data);
 
 #endif
 
--- a/src/image.c	Thu Apr 17 17:35:51 2008 +0000
+++ b/src/image.c	Thu Apr 17 17:44:54 2008 +0000
@@ -36,7 +36,7 @@
 #define IMAGE_THROTTLE_LARGER_IMAGES 1
 
 /* throttle factor to increase read bytes by (2 is double, 3 is triple, etc.) */
-#define IMAGE_THROTTLE_FACTOR 4
+#define IMAGE_THROTTLE_FACTOR 32
 
 /* the file size at which throttling take place */
 #define IMAGE_THROTTLE_THRESHOLD 1048576
@@ -201,6 +201,9 @@
  *-------------------------------------------------------------------
  */
 
+
+#if 0
+
 static void image_alter_real(ImageWindow *imd, AlterType type, gint clamp)
 {
 	PixbufRenderer *pr;
@@ -300,6 +303,7 @@
 		}
 }
 
+
 static void image_post_process_color_cb(ColorMan *cm, ColorManReturnType type, gpointer data)
 {
 	ImageWindow *imd = data;
@@ -319,8 +323,9 @@
 
 	image_read_ahead_start(imd);
 }
+#endif
 
-static gint image_post_process_color(ImageWindow *imd, gint start_row, ExifData *exif)
+static gint image_post_process_color(ImageWindow *imd, gint start_row, ExifData *exif, gint run_in_bg)
 {
 	ColorMan *cm;
 	ColorManProfileType input_type;
@@ -395,17 +400,15 @@
 		
 		data = (unsigned char *) exif_item_get_data(item, &data_len);
 
-		cm = color_man_new_embedded(imd, NULL,
+		cm = color_man_new_embedded(run_in_bg ? imd : NULL, NULL,
 					    data, data_len,
-					    screen_type, screen_file,
-					    image_post_process_color_cb, imd);
+					    screen_type, screen_file);
 		}
 	else 
 		{
-		cm = color_man_new(imd, NULL,
+		cm = color_man_new(run_in_bg ? imd : NULL, NULL,
 				   input_type, input_file,
-				   screen_type, screen_file,
-				   image_post_process_color_cb, imd);
+				   screen_type, screen_file);
 		}
 
 	if (cm)
@@ -417,6 +420,9 @@
 			}
 
 		imd->cm = (gpointer)cm;
+#if 0		
+		if (run_in_bg) color_man_start_bg(imd->cm, image_post_process_color_cb, imd);
+#endif
 		return TRUE;
 		}
 
@@ -425,6 +431,7 @@
 
 static void image_post_process(ImageWindow *imd, gint clamp)
 {
+#if 0
 	ExifData *exif = NULL;
 
 	if (!image_get_pixbuf(imd)) return;
@@ -436,7 +443,6 @@
 		{
 		exif = exif_read_fd(imd->image_fd, (imd->color_profile_enable && imd->color_profile_use_image));
 		}
-
 	if (options->image.exif_rotate_enable && exif)
 		{
 		gint orientation;
@@ -497,21 +503,77 @@
 			if (rotate) image_state_set(imd, IMAGE_STATE_ROTATE_AUTO);
 			}
 		}
-
 	if (imd->color_profile_enable)
 		{
-		if (!image_post_process_color(imd, 0, exif))
+		if (!image_post_process_color(imd, 0, exif, TRUE))
 			{
 			/* fixme: note error to user */
 			image_state_set(imd, IMAGE_STATE_COLOR_ADJ);
 			}
 		}
 
+
 	if (!imd->cm) image_post_process_alter(imd, clamp);
 
 	exif_free(exif);
+#endif
 }
 
+static void image_post_process_tile_color_cb(PixbufRenderer *pr, GdkPixbuf **pixbuf, gint x, gint y, gint w, gint h, gpointer data)
+{
+	ImageWindow *imd = (ImageWindow *)data;
+        if (imd->cm) color_man_correct_region(imd->cm, *pixbuf, x, y, w, h);
+	if (imd->desaturate) pixbuf_desaturate_rect(*pixbuf, x, y, w, h);
+
+}
+
+void image_alter(ImageWindow *imd, AlterType type)
+{
+
+	const static gint rotate_90[]    = {1,   6, 7, 8, 5, 2, 3, 4, 1};
+	const static gint rotate_90_cc[] = {1,   8, 5, 6, 7, 4, 1, 2, 3};
+	const static gint rotate_180[]   = {1,   3, 4, 1, 2, 7, 8, 5, 6};
+	const static gint mirror[]       = {1,   2, 1, 4, 3, 6, 5, 8, 7};
+	const static gint flip[]         = {1,   4, 3, 2, 1, 8, 7, 6, 5};
+
+	
+	if (!imd || !imd->pr) return;
+	
+	if (imd->orientation < 1 || imd->orientation > 8) imd->orientation = 1;
+	
+	switch (type)
+		{
+		case ALTER_ROTATE_90:
+			imd->orientation = rotate_90[imd->orientation];
+			break;
+		case ALTER_ROTATE_90_CC:
+			imd->orientation = rotate_90_cc[imd->orientation];
+			break;
+		case ALTER_ROTATE_180:
+			imd->orientation = rotate_180[imd->orientation];
+			break;
+		case ALTER_MIRROR:
+			imd->orientation = mirror[imd->orientation];
+			break;
+		case ALTER_FLIP:
+			imd->orientation = flip[imd->orientation];
+			break;
+		case ALTER_DESATURATE:
+			imd->desaturate = !imd->desaturate;
+			break;
+		case ALTER_NONE:
+		default:
+			return;
+			break;
+		}
+	pixbuf_renderer_set_orientation((PixbufRenderer *)imd->pr, imd->orientation);
+	if (imd->cm || imd->desaturate) 
+		pixbuf_renderer_set_post_process_func((PixbufRenderer *)imd->pr, image_post_process_tile_color_cb, (gpointer) imd, (imd->cm != NULL) );
+	else
+		pixbuf_renderer_set_post_process_func((PixbufRenderer *)imd->pr, NULL, NULL, TRUE);
+}
+
+
 /*
  *-------------------------------------------------------------------
  * read ahead (prebuffer)
@@ -565,7 +627,7 @@
 	if (!imd->read_ahead_fd || imd->read_ahead_il || imd->read_ahead_pixbuf) return;
 
 	/* still loading ?, do later */
-	if (imd->il || imd->cm) return;
+	if (imd->il /*|| imd->cm*/) return;
 
 	if (debug) printf("%s read ahead started for :%s\n", get_exec_time(), imd->read_ahead_fd->path);
 
@@ -634,7 +696,7 @@
 			ExifData *exif = NULL;
 
 			if (imd->color_profile_use_image) exif = exif_read_fd(imd->image_fd, TRUE);
-			image_post_process_color(imd, imd->prev_color_row, exif);
+//			image_post_process_color(imd, imd->prev_color_row, exif, TRUE);
 			exif_free(exif);
 			}
 		success = TRUE;
@@ -1203,7 +1265,45 @@
 
 void image_change_pixbuf(ImageWindow *imd, GdkPixbuf *pixbuf, gdouble zoom)
 {
+
+	ExifData *exif = NULL;
+	gint orientation;
+
+	if (options->image.exif_rotate_enable ||
+	    (imd->color_profile_enable && imd->color_profile_use_image) )
+		{
+		exif = exif_read_fd(imd->image_fd, (imd->color_profile_enable && imd->color_profile_use_image));
+		}
+
+	if (options->image.exif_rotate_enable && exif && exif_get_integer(exif, "Exif.Image.Orientation", &orientation)) 
+		imd->orientation = orientation;
+	else
+		imd->orientation = 1;
+
+	pixbuf_renderer_set_post_process_func((PixbufRenderer *)imd->pr, NULL, NULL, FALSE);
+	if (imd->cm) 
+		{
+		color_man_free(imd->cm);
+		imd->cm = NULL;
+		}
+
 	pixbuf_renderer_set_pixbuf((PixbufRenderer *)imd->pr, pixbuf, zoom);
+	pixbuf_renderer_set_orientation((PixbufRenderer *)imd->pr, imd->orientation);
+
+	if (imd->color_profile_enable)
+		{
+		if (!image_post_process_color(imd, 0, exif, FALSE))
+			{
+			/* fixme: note error to user */
+//			image_state_set(imd, IMAGE_STATE_COLOR_ADJ);
+			}
+		}
+		
+	exif_free(exif);
+
+	if (imd->cm || imd->desaturate) 
+		pixbuf_renderer_set_post_process_func((PixbufRenderer *)imd->pr, image_post_process_tile_color_cb, (gpointer) imd, (imd->cm != NULL) );
+		
 	image_state_set(imd, IMAGE_STATE_IMAGE);
 }
 
@@ -1317,8 +1417,17 @@
 	imd->completed = source->completed;
 	imd->state = source->state;
 	source->state = IMAGE_STATE_NONE;
+	
+	imd->orientation = source->orientation;
+	imd->desaturate = source->desaturate;
 
 	pixbuf_renderer_move(PIXBUF_RENDERER(imd->pr), PIXBUF_RENDERER(source->pr));
+
+	if (imd->cm || imd->desaturate) 
+		pixbuf_renderer_set_post_process_func((PixbufRenderer *)imd->pr, image_post_process_tile_color_cb, (gpointer) imd, (imd->cm != NULL) );
+	else
+		pixbuf_renderer_set_post_process_func((PixbufRenderer *)imd->pr, NULL, NULL, TRUE);
+
 }
 
 /* manipulation */
@@ -1357,7 +1466,7 @@
 }
 
 
-
+#if 0
 void image_alter(ImageWindow *imd, AlterType type)
 {
 	if (pixbuf_renderer_get_tiles((PixbufRenderer *)imd->pr)) return;
@@ -1377,6 +1486,7 @@
 
 	image_alter_real(imd, type, TRUE);
 }
+#endif
 
 void image_zoom_adjust(ImageWindow *imd, gdouble increment)
 {
@@ -1889,6 +1999,8 @@
 
 	imd->func_button = NULL;
 	imd->func_scroll = NULL;
+	
+	imd->orientation = 1;
 
 	imd->pr = GTK_WIDGET(pixbuf_renderer_new());
 
--- a/src/layout_util.c	Thu Apr 17 17:35:51 2008 +0000
+++ b/src/layout_util.c	Thu Apr 17 17:44:54 2008 +0000
@@ -1094,7 +1094,7 @@
   { "Rotate180",	NULL,		N_("Rotate 1_80"),	"<shift>R",	NULL,	CB(layout_menu_alter_180_cb) },
   { "Mirror",		NULL,		N_("_Mirror"),		"<shift>M",	NULL,	CB(layout_menu_alter_mirror_cb) },
   { "Flip",		NULL,		N_("_Flip"),		"<shift>F",	NULL,	CB(layout_menu_alter_flip_cb) },
-  { "Grayscale",	NULL,		N_("_Grayscale"),	"<shift>G",	NULL,	CB(layout_menu_alter_desaturate_cb) },
+  { "Grayscale",	NULL,		N_("Toggle _grayscale"),"<shift>G",	NULL,	CB(layout_menu_alter_desaturate_cb) },
   { "Properties",GTK_STOCK_PROPERTIES,	N_("_Properties"),	"<control>P",	NULL,	CB(layout_menu_info_cb) },
   { "SelectAll",	NULL,		N_("Select _all"),	"<control>A",	NULL,	CB(layout_menu_select_all_cb) },
   { "SelectNone",	NULL,		N_("Select _none"), "<control><shift>A",NULL,	CB(layout_menu_unselect_all_cb) },
--- a/src/menu.c	Thu Apr 17 17:35:51 2008 +0000
+++ b/src/menu.c	Thu Apr 17 17:44:54 2008 +0000
@@ -213,7 +213,7 @@
 			return _("_Flip");
 			break;
 		case ALTER_DESATURATE:
-			return _("_Grayscale");
+			return _("Toggle _grayscale");
 			break;
 		default:
 			break;
--- 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);
--- a/src/pixbuf-renderer.h	Thu Apr 17 17:35:51 2008 +0000
+++ b/src/pixbuf-renderer.h	Thu Apr 17 17:44:54 2008 +0000
@@ -32,6 +32,10 @@
 					       gint width, gint height, GdkPixbuf *pixbuf, gpointer user_data);
 typedef void (* PixbufRendererTileDisposeFunc)(PixbufRenderer *pr, gint x, gint y,
 					       gint width, gint height, GdkPixbuf *pixbuf, gpointer user_data);
+
+typedef void (* PixbufRendererPostProcessFunc)(PixbufRenderer *pr, GdkPixbuf **pixbuf, gint x, gint y,
+					       gint width, gint height, gpointer user_data);
+
 typedef enum {
 	PR_SCROLL_RESET_TOPLEFT = 0,
 	PR_SCROLL_RESET_CENTER,
@@ -119,8 +123,12 @@
 
 	PixbufRendererTileRequestFunc func_tile_request;
 	PixbufRendererTileDisposeFunc func_tile_dispose;
+	
+	gpointer func_tile_data;
 
-	gpointer func_tile_data;
+	PixbufRendererPostProcessFunc func_post_process;
+	gpointer post_process_user_data;
+	gint post_process_slow;
 
 	gboolean delay_flip;
 	gboolean loading;
@@ -138,6 +146,10 @@
 
 	GList *overlay_list;
 	GdkPixmap *overlay_buffer;
+
+	GdkPixbuf *spare_tile;
+	
+	gint orientation;
 };
 
 struct _PixbufRendererClass
@@ -165,6 +177,11 @@
 void pixbuf_renderer_set_pixbuf(PixbufRenderer *pr, GdkPixbuf *pixbuf, gdouble zoom);
 GdkPixbuf *pixbuf_renderer_get_pixbuf(PixbufRenderer *pr);
 
+void pixbuf_renderer_set_orientation(PixbufRenderer *pr, gint orientation);
+gint pixbuf_renderer_get_orientation(PixbufRenderer *pr);
+
+void pixbuf_renderer_set_post_process_func(PixbufRenderer *pr, PixbufRendererPostProcessFunc func, gpointer user_data, gint slow);
+
 /* display an on-request array of pixbuf tiles */
 
 void pixbuf_renderer_set_tiles(PixbufRenderer *pr, gint width, gint height,
--- a/src/typedefs.h	Thu Apr 17 17:35:51 2008 +0000
+++ b/src/typedefs.h	Thu Apr 17 17:44:54 2008 +0000
@@ -379,6 +379,8 @@
 	gint auto_refresh_interval;
 
 	gint delay_flip;
+	gint orientation;
+	gint desaturate;
 };
 
 #define FILEDATA_MARKS_SIZE 6