[perl-Gtk2] Correct length of the Gtk2::Gdk::Pixbuf->get_pixels() return



commit 2cd14d266700d93aabdd5fe871f0a0ac35869f43
Author: Kevin Ryde <user42 zip com au>
Date:   Sat Jul 31 08:03:29 2010 +1000

    Correct length of the Gtk2::Gdk::Pixbuf->get_pixels() return
    
    Shorten the assumed length to exclude rowstride padding on last row. It's not
    always present, and in particular from $pixbuf->copy() it's not. Reading it
    would go past the end of a malloced block.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=625692

 NEWS            |    5 +++++
 t/GdkPixbuf.t   |    5 +++--
 xs/GdkPixbuf.xs |   33 ++++++++++++++++++++++++++++++---
 3 files changed, 38 insertions(+), 5 deletions(-)
---
diff --git a/NEWS b/NEWS
index 82feeb4..9147c8f 100644
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,8 @@
+Overview of changes in the next unstable release
+================================================
+
+* Fix length of Gtk2::Gdk::Pixbuf->get_pixels() return.
+
 Overview of changes in Gtk2 1.230
 =================================
 
diff --git a/t/GdkPixbuf.t b/t/GdkPixbuf.t
index a78a5e7..fb5b790 100644
--- a/t/GdkPixbuf.t
+++ b/t/GdkPixbuf.t
@@ -57,7 +57,7 @@ is ($pixbuf->get_height, 33);
 is ($pixbuf->get_rowstride, 244);
 $pixels = $pixbuf->get_pixels;
 ok ($pixels);
-is (length($pixels), ($pixbuf->get_height * $pixbuf->get_rowstride)); 
+is (length($pixels), 8052);
 
 
 $pixbuf = Gtk2::Gdk::Pixbuf->new ('rgb', FALSE, 8, 33, 61);
@@ -71,7 +71,8 @@ is ($pixbuf->get_height, 61);
 is ($pixbuf->get_rowstride, 100); # 100 is aligned, 99 is actual
 $pixels = $pixbuf->get_pixels;
 ok ($pixels);
-is (length($pixels), ($pixbuf->get_height * $pixbuf->get_rowstride)); 
+# last row is not padded to the rowstride, hence 6099 not 6100
+is (length($pixels), 6099); 
 
 
 isa_ok ($pixbuf->copy, 'Gtk2::Gdk::Pixbuf', 'copy');
diff --git a/xs/GdkPixbuf.xs b/xs/GdkPixbuf.xs
index 14274cc..43116d4 100644
--- a/xs/GdkPixbuf.xs
+++ b/xs/GdkPixbuf.xs
@@ -303,16 +303,43 @@ gdk_pixbuf_get_bits_per_sample (pixbuf)
 	GdkPixbuf *pixbuf
 
 ##  guchar *gdk_pixbuf_get_pixels (const GdkPixbuf *pixbuf) 
+=for apidoc
+Return a copy of the bytes comprising the pixel data of C<$pixbuf>.
+
+As described in the Gdk Pixbuf reference manual (the "Note" section of
+"The GdkPixbuf Structure"), the last row does not extend to the
+rowstride, but ends with the last byte of the last pixel.  The length
+of the C<get_pixels> return reflects this.
+=cut
 SV *
 gdk_pixbuf_get_pixels (pixbuf)
 	GdkPixbuf *pixbuf
     PREINIT:
 	guchar * pixels;
+	STRLEN size;
     CODE:
+	/* For reference, most pixbuf mallocs are height*rowstride, for
+	   example gdk_pixbuf_new() does that.  But gdk_pixbuf_copy()
+	   mallocs only the lesser "last row unpadded" size.  If the code
+	   here used height*rowstride it would read past the end of such a
+	   block.
+
+	   Most of the time rowstride is the next multiple of 4, and a
+	   malloced block is the next multiple of 4 too, so it's ok, but for
+	   a bigger rowstride it's not.
+
+	   The following calculation adapted from gdk_pixbuf_copy() circa
+	   Gtk 2.16.  bits_per_sample is only ever 8 currently, making it
+	   simply n_channels many bytes-per-pixel, but the calculation
+	   anticipates bits not a multiple of 8.  */
+
+	size = ((gdk_pixbuf_get_height(pixbuf) - 1)
+		* gdk_pixbuf_get_rowstride(pixbuf)
+		+ gdk_pixbuf_get_width(pixbuf)
+		* ((gdk_pixbuf_get_n_channels(pixbuf)
+		    * gdk_pixbuf_get_bits_per_sample(pixbuf) + 7) / 8));
 	pixels = gdk_pixbuf_get_pixels (pixbuf);
-	RETVAL = newSVpv ((gchar *) pixels,
-			  gdk_pixbuf_get_height (pixbuf)
-			  * gdk_pixbuf_get_rowstride (pixbuf));
+	RETVAL = newSVpv ((gchar *) pixels, size);
     OUTPUT:
 	RETVAL
 



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