mono / gtk-sharp

Gtk# is a Mono/.NET binding to the cross platform Gtk+ GUI toolkit and the foundation of most GUI apps built with Mono
http://www.mono-project.com/GtkSharp
Other
428 stars 139 forks source link

Gtk#: GtkImage with transparency crashes on Linux (Mint) (but not MacOS) #235

Open marek-safar opened 6 years ago

marek-safar commented 6 years ago

From @jeffechua on July 13, 2018 15:35

Steps to Reproduce

  1. Compile either of the following test cases:
using System;
using Gtk;

namespace TransparencyTest {
    class MainClass {
        public static void Main (string[] args) {

            Application.Init();
            Window win = new Window(WindowType.Toplevel);

            const byte TRANSPARENCY = 0; // Change this at will

            win.Mapped += delegate {
                Gdk.GC red = new Gdk.GC(win.GdkWindow) { RgbFgColor = new Gdk.Color(255, 0, 0) };
                Gdk.GC black = new Gdk.GC(win.GdkWindow) { RgbFgColor = new Gdk.Color(0, 0, 0) };
                Gdk.GC translucent = new Gdk.GC(win.GdkWindow) { RgbFgColor = new Gdk.Color(TRANSPARENCY, TRANSPARENCY, TRANSPARENCY) };
                Gdk.GC visible = new Gdk.GC(win.GdkWindow) { RgbFgColor = new Gdk.Color(255, 255, 255) };

                Gdk.Pixmap pixmap = new Gdk.Pixmap(win.GdkWindow, 200, 200);
                pixmap.DrawRectangle(black, true, 0, 0, 200, 200);
                pixmap.DrawArc(red, true, 20, 20, 160, 160, 0, 23040);

                Gdk.Pixmap mask = new Gdk.Pixmap(win.GdkWindow, 200, 200);
                mask.DrawRectangle(visible, true, 0, 0, 200, 200);
                mask.DrawArc(translucent, true, 0, 0, 100, 100, 0, 23040);
                mask.DrawArc(translucent, true, 100, 100, 100, 100, 0, 23040);

                Image image = new Image(pixmap, mask);
                win.Add(image);

                win.ShowAll();
            };

            win.ShowAll();
            Application.Run();
        }
    }
}

OR

using System;
using Gtk;

namespace TransparencyTest {
    class MainClass {
        public static void Main (string[] args) {

            Application.Init();
            Window win = new Window(WindowType.Toplevel);

            VBox vBox = new VBox();
            HScale transparencyScale = new HScale(0, 255, 1) { Value = 255 };
            Image image = new Image();
            image.SetSizeRequest(200, 200);

            vBox.PackStart(transparencyScale);
            vBox.PackStart(image);
            win.Add(vBox);

            transparencyScale.ValueChanged += delegate {

                int transparency = (int)transparencyScale.Value;
                byte[] tBytes = BitConverter.GetBytes(transparency);

                Gdk.GC red = new Gdk.GC(win.GdkWindow) { RgbFgColor = new Gdk.Color(255, 0, 0) };
                Gdk.GC black = new Gdk.GC(win.GdkWindow) { RgbFgColor = new Gdk.Color(0, 0, 0) };
                Gdk.GC translucent = new Gdk.GC(win.GdkWindow) { RgbFgColor = new Gdk.Color(tBytes[0], tBytes[0], tBytes[0]) };
                Gdk.GC visible = new Gdk.GC(win.GdkWindow) { RgbFgColor = new Gdk.Color(255, 255, 255) };

                Gdk.Pixmap pixmap = new Gdk.Pixmap(win.GdkWindow, 200, 200);
                pixmap.DrawRectangle(black, true, 0, 0, 200, 200);
                pixmap.DrawArc(red, true, 20, 20, 160, 160, 0, 23040);

                Gdk.Pixmap mask = new Gdk.Pixmap(win.GdkWindow, 200, 200);
                mask.DrawRectangle(visible, true, 0, 0, 200, 200);
                mask.DrawArc(translucent, true, 0, 0, 100, 100, 0, 23040);
                mask.DrawArc(translucent, true, 100, 100, 100, 100, 0, 23040);

                image.SetFromPixmap(pixmap, mask);
                image.ShowAll();

            };

            win.ShowAll();
            Application.Run();
        }
    }
}
  1. Run on MacOS with mono.
  2. Run on Linux (Mint) with mono. Compare behavior with Mac.

Current Behavior

On MacOS, behavior is as expected for both test cases.

On Linux, the first test case crashes immediately whenever TRANSPARENCY == 0, and displays the supposedly translucent circles as fully opaque (i.e. indifferentiable from surrounding opacity) whenever TRANSPARENCY != 0. The second test case momentarily displays the fully opaque image (same as the first test case where TRANSPARENCY != 0) and then immediately crashes, whenever the the slider is moved, i.e. whenever Image.SetFromPixmap is invoked.

The error message in all crash cases is as below:

The program 'TransparencyTest' received an X Window System error.
This probably reflects a bug in the program.
The error was 'BadMatch (invalid parameter attributes)'.
  (Details: serial 1158 error_code 8 request_code 56 minor_code 0)
  (Note to programmers: normally, X errors are reported asynchronously;
   that is, you will receive the error a while after causing it.
   To debug your program, run it with the --sync command line
   option to change this behavior. You can then get a meaningful
   backtrace from your debugger if you break on the gdk_x_error() function.)

Expected Behavior

In the first test case, the two translucent circles should appear with transparency according to the value of the constant TRANSPARENCY. In the second test case, the circles should change transparency as the sliders are moved. In both test cases, the image itself (discounting alpha) is a red circle on a black background.

On which platforms did you notice this

[x] macOS (High Sierra 10.13.5) [x] Linux (Linux Mint 17.2 Cinnamon 64-bit, Cinnamon 2.6.13, Linux Kernel 3.13.0-141-generic) [ ] Windows (untested)

Version Used: 5.10.1.57 for MacOS, 5.12.0.226 for Linux Mint

Stacktrace

Using gdb and a breakpoint at "_XError" ("gdk_x_error" didn't work), the stacktrace obtained (hopefully correctly) was

"<unnamed thread>" tid=0x0x7ffff7fc8780 this=0x0x7ffff7f24130 , thread handle : 0xa39360, state : not waiting
  at <unknown> <0xffffffff>
  at (wrapper managed-to-native) Gtk.Application.gtk_main () [0x00008] in <e8d21baece374fadb4e2b3899720848c>:0
  at Gtk.Application.Run () [0x00000] in <e8d21baece374fadb4e2b3899720848c>:0
  at TransparencyTest.MainClass.Main (string[]) [0x000c1] in /Users/jefferson/Dropbox/Parahumans/TransparencyTest/Program.cs:85
  at (wrapper runtime-invoke) <Module>.runtime_invoke_void_object (object,intptr,intptr,intptr) [0x00051] in <d80c7dc187f741b8b94961002c8f0c80>:0

Copied from original issue: mono/mono#9537

marek-safar commented 6 years ago

From @jeffechua on July 13, 2018 15:38

I'm not sure if here or the gtk-sharp repo is the best place to file this report. The gtk-sharp repo's issue page is pretty inactive, and the gtk-sharp website's "bug reporting" link directed to mono's bug reporting section which directed me here, and I'm not sure if this is an issue with the gtk-sharp wrapper or mono itself... so here it is.

Therzok commented 6 years ago

This looks more like a gtk bug rather than a gtk# bug.

Can you see if this repros with a native gtk app? Doesn't seem to be anything platform specific in the transparency bindings.

jeffechua commented 6 years ago

After some investigation, it appears that the cause of this problem is that X11's alpha channels are of depth 1, while Mac's are of depth 8. I need to pass a Pixmap of depth 1 (i.e. a bitmap) into the image constructor for it to work on Linux, which in turn won't work and gives a (non-fatal) error on Mac. The easy workaround is to detect the platform and then use the corresponding depth for alpha.

Thus, I don't think that this strictly classifies as a bug, per se. It's just a quirk of the different platforms. I haven't gotten around to reproducing it in plain C gtk+, but it likely behaves similarly. Nevertheless, perhaps it might be a good idea to note this behavior in the documentation (both Gtk# and the original Gtk+) so people like me don't get tripped up unnecessarily (though the existence of this discussion mitigates that to an extent for proficient Googlers).