Tentative patch for gdk-pixbuf's GIF loader



Hi,

Here is a patch I applied to the 1.4 branch of gdk-pixbuf.  It allows
you to load animations where frames have bounding boxes that go outside
of the base bounding box.  For example, see

	http://www.isc.tamu.edu/~lewing/linux/peng-movie.4.gif

(Mozilla can display it just fine).

The patch essentially creates frames that do fit in the base bounding
box.  It does this by clipping in the drawing functions where
appropriate.

With this patch in 1.4, GtkHTML can display the penguin animation
correctly.  I don't have other GIFs that are broken in a similar way to
that one, so I haven't been able to do more extensive testing.

Does the patch look correct?  Mind you, it is for 1.4.  If it looks
right I'll do the forward-port to 2.0 and HEAD.

  Federico

Index: io-gif.c
===================================================================
RCS file: /cvs/gnome/gdk-pixbuf/gdk-pixbuf/io-gif.c,v
retrieving revision 1.51
retrieving revision 1.52
diff -u -r1.51 -r1.52
--- io-gif.c	27 Sep 2002 19:50:16 -0000	1.51
+++ io-gif.c	18 Dec 2002 03:32:19 -0000	1.52
@@ -608,22 +608,55 @@
 	context->draw_pass = 0;
 }
 
+/* Clips the current frame to the base dimensions.  Returns the clipped offsets and width/height. */
+static void
+clip_frame (GifContext *context, int *x, int *y, int *w, int *h)
+{
+	*x = MAX (0, context->x_offset);
+	*y = MAX (0, context->y_offset);
+	*w = MIN (context->width, context->x_offset + context->frame_len) - *x;
+	*h = MIN (context->height, context->y_offset + context->frame_height) - *y;
+
+	if (w > 0 && h > 0)
+		return;
+
+	/* The frame is completely off-bounds */
+
+	*x = 0;
+	*y = 0;
+	*w = 0;
+	*h = 0;
+}
+
 static void
 gif_fill_in_pixels (GifContext *context, guchar *dest, gint offset, guchar v)
 {
 	guchar *pixel = NULL;
+	int frame_x, frame_y, frame_width, frame_height;
+	int xpos, ypos;
+	
+	clip_frame (context, &frame_x, &frame_y, &frame_width, &frame_height);
 
-	if (context->gif89.transparent != -1) {
-		pixel = dest + (context->draw_ypos + offset) * gdk_pixbuf_get_rowstride (context->pixbuf) + context->draw_xpos * 4;
-		*pixel = context->color_map [0][(guchar) v];
-		*(pixel+1) = context->color_map [1][(guchar) v];
-		*(pixel+2) = context->color_map [2][(guchar) v];
-		*(pixel+3) = (guchar) ((v == context->gif89.transparent) ? 0 : 65535);
-	} else {
-		pixel = dest + (context->draw_ypos + offset) * gdk_pixbuf_get_rowstride (context->pixbuf) + context->draw_xpos * 3;
-		*pixel = context->color_map [0][(guchar) v];
-		*(pixel+1) = context->color_map [1][(guchar) v];
-		*(pixel+2) = context->color_map [2][(guchar) v];
+	xpos = context->draw_xpos + context->x_offset;
+	ypos = context->draw_ypos + offset + context->y_offset;
+
+	if (xpos >= frame_x && xpos < frame_x + frame_width &&
+	    ypos >= frame_y && ypos < frame_y + frame_height) {
+		xpos -= frame_x;
+		ypos -= frame_y;
+
+		if (context->gif89.transparent != -1) {
+			pixel = dest + ypos * gdk_pixbuf_get_rowstride (context->pixbuf) + xpos * 4;
+			*pixel = context->color_map [0][(guchar) v];
+			*(pixel+1) = context->color_map [1][(guchar) v];
+			*(pixel+2) = context->color_map [2][(guchar) v];
+			*(pixel+3) = (guchar) ((v == context->gif89.transparent) ? 0 : 65535);
+		} else {
+			pixel = dest + ypos * gdk_pixbuf_get_rowstride (context->pixbuf) + xpos * 3;
+			*pixel = context->color_map [0][(guchar) v];
+			*(pixel+1) = context->color_map [1][(guchar) v];
+			*(pixel+2) = context->color_map [2][(guchar) v];
+		}
 	}
 }
 
@@ -670,20 +703,40 @@
 	gboolean bound_flag;
 	gint first_pass; /* bounds for emitting the area_updated signal */
 	gint v;
+	int frame_x, frame_y, frame_width, frame_height;
+	int rowstride;
+
+	clip_frame (context, &frame_x, &frame_y, &frame_width, &frame_height);
 
 	if (context->pixbuf == NULL) {
-		context->pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB,
-						  context->gif89.transparent != -1,
-						  8,
-						  context->frame_len,
-						  context->frame_height);
+		if (frame_width == 0 || frame_height == 0) {
+			guchar *pixels;
+
+			/* If it is a clipped-out frame, we just output a single transparent
+			 * pixel.
+			 */
+			context->pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8, 1, 1);
+			if (!context->pixbuf)
+				return -2;
+
+			pixels = gdk_pixbuf_get_pixels (context->pixbuf);
+			pixels[0] = 0;
+			pixels[1] = 0;
+			pixels[2] = 0;
+			pixels[3] = 0;
+		} else
+			context->pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB,
+							  context->gif89.transparent != -1,
+							  8,
+							  frame_width,
+							  frame_height);
 
 		if (context->prepare_func)
 			(* context->prepare_func) (context->pixbuf, context->user_data);
 		if (context->animation || context->frame_done_func || context->anim_done_func) {
 			context->frame = g_new (GdkPixbufFrame, 1);
-			context->frame->x_offset = context->x_offset;
-			context->frame->y_offset = context->y_offset;;
+			context->frame->x_offset = frame_x;
+			context->frame->y_offset = frame_y;
 			context->frame->delay_time = context->gif89.delay_time;
 			switch (context->gif89.disposal) {
 			case 0:
@@ -714,30 +767,43 @@
 			}
 		}
 	}
+
 	dest = gdk_pixbuf_get_pixels (context->pixbuf);
+	rowstride = gdk_pixbuf_get_rowstride (context->pixbuf);
 
 	bound_flag = FALSE;
 	lower_bound = upper_bound = context->draw_ypos;
 	first_pass = context->draw_pass;
 
 	while (TRUE) {
+		int xpos, ypos;
+
 		v = lzw_read_byte (context);
 		if (v < 0) {
 			goto finished_data;
 		}
 		bound_flag = TRUE;
 
-		if (context->gif89.transparent != -1) {
-			temp = dest + context->draw_ypos * gdk_pixbuf_get_rowstride (context->pixbuf) + context->draw_xpos * 4;
-			*temp = context->color_map [0][(guchar) v];
-			*(temp+1) = context->color_map [1][(guchar) v];
-			*(temp+2) = context->color_map [2][(guchar) v];
-			*(temp+3) = (guchar) ((v == context->gif89.transparent) ? 0 : -1);
-		} else {
-			temp = dest + context->draw_ypos * gdk_pixbuf_get_rowstride (context->pixbuf) + context->draw_xpos * 3;
-			*temp = context->color_map [0][(guchar) v];
-			*(temp+1) = context->color_map [1][(guchar) v];
-			*(temp+2) = context->color_map [2][(guchar) v];
+		xpos = context->draw_xpos + context->x_offset;
+		ypos = context->draw_ypos + context->y_offset;
+
+		if (xpos >= frame_x && xpos < frame_x + frame_width &&
+		    ypos >= frame_y && ypos < frame_y + frame_height) {
+			xpos -= frame_x;
+			ypos -= frame_y;
+
+			if (context->gif89.transparent != -1) {
+				temp = dest + ypos * rowstride + xpos * 4;
+				*temp = context->color_map [0][(guchar) v];
+				*(temp+1) = context->color_map [1][(guchar) v];
+				*(temp+2) = context->color_map [2][(guchar) v];
+				*(temp+3) = (guchar) ((v == context->gif89.transparent) ? 0 : -1);
+			} else {
+				temp = dest + ypos * rowstride + xpos * 3;
+				*temp = context->color_map [0][(guchar) v];
+				*(temp+1) = context->color_map [1][(guchar) v];
+				*(temp+2) = context->color_map [2][(guchar) v];
+			}
 		}
 
 		if (context->prepare_func && context->frame_interlace)
@@ -802,13 +868,22 @@
 	v = 0;
  finished_data:
 	if (bound_flag && context->update_func) {
+		int y, height;
+
 		if (lower_bound <= upper_bound && first_pass == context->draw_pass) {
-			(* context->update_func)
-				(context->pixbuf,
-				 0, lower_bound,
-				 gdk_pixbuf_get_width (context->pixbuf),
-				 upper_bound - lower_bound,
-				 context->user_data);
+			y = lower_bound + context->y_offset;
+			height = upper_bound - lower_bound;
+
+			y = MAX (frame_y, y);
+			height = MIN (frame_height, lower_bound + context->y_offset + height);
+
+			if (height > 0)
+				(* context->update_func) (context->pixbuf,
+							  0,
+							  y - frame_y,
+							  gdk_pixbuf_get_width (context->pixbuf),
+							  height,
+							  context->user_data);
 		} else {
 			if (lower_bound <= upper_bound) {
 				(* context->update_func)
@@ -818,18 +893,23 @@
 					 gdk_pixbuf_get_height (context->pixbuf),
 					 context->user_data);
 			} else {
-				(* context->update_func)
-					(context->pixbuf,
-					 0, 0,
-					 gdk_pixbuf_get_width (context->pixbuf),
-					 upper_bound,
-					 context->user_data);
-				(* context->update_func)
-					(context->pixbuf,
-					 0, lower_bound,
-					 gdk_pixbuf_get_width (context->pixbuf),
-					 gdk_pixbuf_get_height (context->pixbuf),
-					 context->user_data);
+				height = MAX (frame_height, upper_bound + context->y_offset);
+				if (height > 0)
+					(* context->update_func)
+						(context->pixbuf,
+						 0, 0,
+						 gdk_pixbuf_get_width (context->pixbuf),
+						 height,
+						 context->user_data);
+
+				y = MAX (0, lower_bound + context->y_offset);
+				if (y < frame_height)
+					(* context->update_func)
+						(context->pixbuf,
+						 0, y,
+						 gdk_pixbuf_get_width (context->pixbuf),
+						 gdk_pixbuf_get_height (context->pixbuf),
+						 context->user_data);
 			}
 		}
 	}
@@ -944,6 +1024,7 @@
 gif_get_frame_info (GifContext *context)
 {
 	unsigned char buf[9];
+
 	if (!gif_read (context, buf, 9)) {
 		return -1;
 	}
@@ -953,9 +1034,7 @@
 	context->x_offset = LM_to_uint (buf[0], buf[1]);
 	context->y_offset = LM_to_uint (buf[2], buf[3]);
 
-	if (context->frame_height > context->height
-	    || context->frame_height <= 0 || context->frame_len <= 0) {
-		/* we don't want to resize things.  So we exit */
+	if (context->frame_len <= 0 || context->frame_height <= 0) {
 		context->state = GIF_DONE;
 		return -2;
 	}


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]