GNOME Bugzilla – Bug 757187
Assertion violation in gdk_image_new_for_depth
Last modified: 2018-05-02 16:49:14 UTC
EiffelStudio does not run under GTK for MacOS using quartz. The issue is an assert violation which causes EiffelStudio to crash. This violation is: Gdk:ERROR:gdkimage-quartz.c:330:_gdk_image_new_for_depth: assertion failed: (depth == 24 || depth == 32) And it is not called directly by our code, but indirectly by a call to "gdk_pixbuf_get_from_drawable". The same code works fine on MacOS or other platforms using the X11 backend. You can easily reproduce this bug by downloading and installing EiffelStudio from Homebrew in the hombrew/x11 tap (name is confusing since it is not x11 anymore). To launch EiffelStudio and observe the crash, type "ec -gui" assuming you have /usr/local/bin in your path. To debug, you need to set 2 environment variables: - ISE_EIFFEL=/usr/local/Cellar/eiffelstudio/15.08/ - ISE_PLATFORM=macosx-x86-64 Let me know how I can help to solve this issue. Thanks, Manu
I missed one step to debug, you then need to launch /usr/local/Cellar/eiffelstudio/15.08/studio/spec/macosx-x86-64/bin/ec -gui
Sorry, installing your software (never mind Homebrew) to reproduce a bug is not acceptable. Please provide a small example program in C that demonstrates it or at least a link to where gdk_pixbuf_get_from_drawable() is called. Note, however, that this is an assert. Asserts exist to enforce invariants. In this case, the invariant is that the Quartz backend supports only bit depths of 24 and 32 bits. If your code is trying to use a different bit depth, then you'll have to fix it. If the bit-depth you want is 1, see bug 557780.
My code definitely wants 1-bit depth as we use them to do our own masking. The assert is in my opinion wrong since the function `depth_supported` reports 1, 24 and 32 as valid depth for GdpPixmap (see line gdkpixmap-quartz.c:214 in version 2.24.30 of GTK) and the code of `_gdk_quartz_image_copy_to_image` do support 1-bit image too. The solution from bug 557780 is not sufficient. It will take some time to get a simple reproducible example, but what we do is take a transparent png image, get a GdkPixbuf from it, and split it into 2 GdkImage (actual image and its mask), we use gdk_pixbuf_render_pixmap_and_mask for that.
Spent a good deal of time debugging and managed to add support for 1-bit pixmap. Along with the proposed fix of bug https://bugzilla.gnome.org/show_bug.cgi?id=768631, I can render things better (still facing some rendering and other various issues but not assert anymore). diff --git a/gdk/quartz/gdkimage-quartz.c b/gdk/quartz/gdkimage-quartz.c --- a/gdk/quartz/gdkimage-quartz.c +++ b/gdk/quartz/gdkimage-quartz.c @@ -37,15 +37,16 @@ _gdk_quartz_image_copy_to_image (GdkDrawable *drawable, gint height) { GdkScreen *screen; + int depth; g_return_val_if_fail (GDK_IS_DRAWABLE_IMPL_QUARTZ (drawable), NULL); g_return_val_if_fail (image != NULL || (dest_x == 0 && dest_y == 0), NULL); screen = gdk_drawable_get_screen (drawable); + depth = gdk_drawable_get_depth (drawable); if (!image) image = _gdk_image_new_for_depth (screen, GDK_IMAGE_FASTEST, NULL, - width, height, - gdk_drawable_get_depth (drawable)); + width, height, depth); if (GDK_IS_PIXMAP_IMPL_QUARTZ (drawable)) { @@ -63,7 +64,7 @@ _gdk_quartz_image_copy_to_image (GdkDrawable *drawable, return image; } - switch (gdk_drawable_get_depth (drawable)) + switch (depth) { case 24: bytes_per_row = pix_impl->width * 4; @@ -112,7 +113,7 @@ _gdk_quartz_image_copy_to_image (GdkDrawable *drawable, for (x = 0; x < width; x++) { gint32 pixel; - + /* 8 bits */ pixel = src[0]; src++; @@ -123,7 +124,7 @@ _gdk_quartz_image_copy_to_image (GdkDrawable *drawable, break; default: - g_warning ("Unsupported bit depth %d\n", gdk_drawable_get_depth (drawable)); + g_warning ("Unsupported bit depth %d\n", depth); return image; } } @@ -146,7 +147,7 @@ _gdk_quartz_image_copy_to_image (GdkDrawable *drawable, if (GDK_WINDOW_IMPL_QUARTZ (drawable) == GDK_WINDOW_IMPL_QUARTZ (GDK_WINDOW_OBJECT (_gdk_root)->impl)) { /* Special case for the root window. */ - CGRect rect = CGRectMake (src_x, src_y, width, height); + CGRect rect = CGRectMake (src_x, src_y, width, height); CGImageRef root_image_ref = CGWindowListCreateImage (rect, kCGWindowListOptionOnScreenOnly, kCGNullWindowID, @@ -173,7 +174,7 @@ _gdk_quartz_image_copy_to_image (GdkDrawable *drawable, } else { - NSRect rect = NSMakeRect (src_x, src_y, width, height); + NSRect rect = NSMakeRect (src_x, src_y, width, height); view = GDK_WINDOW_IMPL_QUARTZ (drawable)->view; /* We return the image even if we can't copy to it. */ @@ -322,7 +323,7 @@ _gdk_image_new_for_depth (GdkScreen *screen, if (visual) depth = visual->depth; - g_assert (depth == 24 || depth == 32); + g_assert((depth == 1) || (depth == 24) || (depth == 32)); image = g_object_new (gdk_image_get_type (), NULL); image->type = type; @@ -333,11 +334,16 @@ _gdk_image_new_for_depth (GdkScreen *screen, image->byte_order = (G_BYTE_ORDER == G_LITTLE_ENDIAN) ? GDK_LSB_FIRST : GDK_MSB_FIRST; - /* We only support images with bpp 4 */ - image->bpp = 4; - image->bpl = image->width * image->bpp; - image->bits_per_pixel = image->bpp * 8; - + if (depth == 1) { + image->bpp = 1; + image->bpl = (image->width >> 3) + 1; + image->bits_per_pixel = 1; + } else { + image->bpp = 4; + image->bpl = image->width * image->bpp; + image->bits_per_pixel = image->bpp * 8; + } + image->mem = g_malloc (image->bpl * image->height); memset (image->mem, 0x00, image->bpl * image->height); @@ -355,9 +361,14 @@ gdk_image_get_pixel (GdkImage *image, g_return_val_if_fail (x >= 0 && x < image->width, 0); g_return_val_if_fail (y >= 0 && y < image->height, 0); - ptr = image->mem + y * image->bpl + x * image->bpp; - - return *(guint32 *)ptr; + ptr = image->mem + y * image->bpl; + if (image->depth == 1) { + guchar data = (image->byte_order == GDK_MSB_FIRST ? (0x80 >> (x & 7)) : (1 << (x & 7))); + return (ptr[x >> 3] & data) ? 0x1 : 0x0; + } else { + ptr += x * image->bpp; + return *(guint32 *)ptr; + } } void @@ -366,18 +377,27 @@ gdk_image_put_pixel (GdkImage *image, gint y, guint32 pixel) { - guchar *ptr; - - ptr = image->mem + y * image->bpl + x * image->bpp; - - *(guint32 *)ptr = pixel; + guchar *ptr = image->mem + y * image->bpl; + if (image->depth == 1) { + guchar data = (image->byte_order == GDK_MSB_FIRST ? (0x80 >> (x & 7)) : (1 << (x & 7))); + if (pixel) { + ptr[x >> 3] |= data; + } else { + ptr[x >> 3] &= ~data; + } + } else { + ptr += x * image->bpp; + *(guint32 *)ptr = pixel; + } } gint _gdk_windowing_get_bits_for_depth (GdkDisplay *display, - gint depth) + gint depth) { - if (depth == 24 || depth == 32) + if (depth == 1) + return 1; + else if (depth == 24 || depth == 32) return 32; else g_assert_not_reached ();
Created attachment 331158 [details] [review] Proposed patch against 2.24.30
Review of attachment 331158 [details] [review]: Thanks for the patch. Could you please attach a patch generated by `git format-patch` instead of a diff? This allows proper review and attribution. ::: gdk/quartz/gdkimage-quartz.c @@ +148,3 @@ { /* Special case for the root window. */ + CGRect rect = CGRectMake (src_x, src_y, width, height); This looks like whitespace change; please, skip this. @@ +175,3 @@ else { + NSRect rect = NSMakeRect (src_x, src_y, width, height); This looks like whitespace change; pleace, skip this. @@ +324,3 @@ depth = visual->depth; + g_assert((depth == 1) || (depth == 24) || (depth == 32)); Coding style: missing space between `g_assert` and opening parenthesis. @@ +335,3 @@ image->byte_order = (G_BYTE_ORDER == G_LITTLE_ENDIAN) ? GDK_LSB_FIRST : GDK_MSB_FIRST; + if (depth == 1) { Coding style: braces on a new line, indented. @@ +339,3 @@ + image->bpl = (image->width >> 3) + 1; + image->bits_per_pixel = 1; + } else { Coding style: `else` and braces go on a separate line. @@ +363,3 @@ + ptr = image->mem + y * image->bpl; + if (image->depth == 1) { Coding style: braces on a new line. @@ +366,3 @@ + guchar data = (image->byte_order == GDK_MSB_FIRST ? (0x80 >> (x & 7)) : (1 << (x & 7))); + return (ptr[x >> 3] & data) ? 0x1 : 0x0; + } else { Coding style: `else` and braces go on a separate line. @@ +379,3 @@ { + guchar *ptr = image->mem + y * image->bpl; + if (image->depth == 1) { Coding style: braces on a new indented line. @@ +382,3 @@ + guchar data = (image->byte_order == GDK_MSB_FIRST ? (0x80 >> (x & 7)) : (1 << (x & 7))); + if (pixel) { + ptr[x >> 3] |= data; Coding style: too much indentation. @@ +384,3 @@ + ptr[x >> 3] |= data; + } else { + ptr[x >> 3] &= ~data; Coding style: too much indentation. @@ +386,3 @@ + ptr[x >> 3] &= ~data; + } + if (pixel) { Coding style: `else` and braces go on a new line. @@ +397,2 @@ { + if (depth == 1) I would change this to a switch: switch (depth) { case 1: return 1; case 24: case 32: return 32; default: g_assert_not_reached (); }
Created attachment 331171 [details] [review] Patch using git format-patch command
Just resubmitted it now.
Created attachment 331173 [details] [review] Patch using git format-patch command Update patch to use the git format-patch command
We're moving to gitlab! As part of this move, we are moving bugs to NEEDINFO if they haven't seen activity in more than a year. If this issue is still important to you and still relevant with GTK+ 3.22 or master, please reopen it and we will migrate it to gitlab.
It is still imporant for me but I cannot change the status to re-open it.
As per comment 11; thanks for your patience!
-- GitLab Migration Automatic Message -- This bug has been migrated to GNOME's GitLab instance and has been closed from further activity. You can subscribe and participate further through the new bug through this link to our GitLab instance: https://gitlab.gnome.org/GNOME/gtk/issues/580.