monogtk#cairogdkpixbuf

Problem with gdk.Pixbuf in gtk# Mono


I'm creating a small drawing program in Mono gtk# and using the Cairo graphics library. I'm coding and compiling on a MacOs X system. I have a drawable object which I put into Pixbuf at a certain time and then retrieve it later into the drawable object! The idea is to take a "snapshot" of the image in the drawable and then draw on top of it.

The problem is that when I put the Pixbuf back into the drawable it looks obscure, all yellow with stripes and it looks like a portion of the image is missing.

UPDATE: I ran the program on my linux and windows machines and there it works flawlessly! So this error is only on MacOs X. Here's the code:

// use: gmcs -pkg:gtk-sharp-2.0 -pkg:mono-cairo ttv1.cs

using Gtk;
using Cairo;
using System;

public class Draw : Window
{   
DrawingArea canvas;

public Gdk.Pixbuf pixbuf;

public Draw() : base("teikniteink")
{
    canvas = new DrawingArea();
    canvas.ExposeEvent += canvasExposed;
    DeleteEvent += delegate { Application.Quit();};

    KeyPressEvent += onKey;
    SetDefaultSize(400,400);
    SetPosition(WindowPosition.Center);
    Add(canvas);
    ShowAll();
}


private void onKey(object o, KeyPressEventArgs args)
{
    switch (args.Event.Key)
    {
        case Gdk.Key.w:
        Console.WriteLine("Key Pressed {0}", args.Event.Key);
        // Send to Pixbuf
        pixbuf = Gdk.Pixbuf.FromDrawable(canvas.GdkWindow, Gdk.Colormap.System,0,0,0,0,400,400);
        // Save to output.png
                    pixbuf.Save ("output.png", "png");  
        break;
        case Gdk.Key.e:
        Console.WriteLine("Key Pressed {0}", args.Event.Key);

        Gdk.GC g = new Gdk.GC(canvas.GdkWindow);
        // Retrive from pixbuf
        canvas.GdkWindow.DrawPixbuf (g,pixbuf,0,0,0,0,-1,-1,Gdk.RgbDither.Normal,0,0);      
        break;
    }
}

private void canvasExposed(object o, ExposeEventArgs args)
{
    using (Cairo.Context ctx = Gdk.CairoHelper.Create(canvas.GdkWindow))
    {
        PointD start = new PointD(100,100);
        PointD end = new PointD(300,300);
        double width = Math.Abs(start.X - end.X);
        double height = Math.Abs(start.Y - end.Y);
        double xcenter = start.X + (end.X - start.X) / 2.0;
        double ycenter = start.Y + (end.Y - start.Y) / 2.0;

        ctx.Save();
        ctx.Translate(xcenter, ycenter);
        ctx.Scale(width/2.0, height/2.0);
        ctx.Arc(0.0, 0.0, 1.0, 0.0, 2*Math.PI);
        ctx.Restore();
        ctx.Stroke();
    }
}

public static void Main()
{
    Application.Init();
    new Draw();
    Application.Run();
}
}

It would be very much appreciated if someone knows whats going on here and can point me in the right direction to fix it.


Solution

  • I triggered the same problem in this manner:

    gw = gtk_widget_get_window(GTK_WIDGET(GLOBALS->mainwindow));
    if(gw)
        {       
        gdk_drawable_get_size(gw, &w, &h);
        cm = gdk_drawable_get_colormap(gw);
        if(cm)
                {
                dest = gdk_pixbuf_new(GDK_COLORSPACE_RGB, FALSE, 8, w, h);
                if(dest)
                        {
                        dest2 = gdk_pixbuf_get_from_drawable(dest, gw, cm, 0, 0, 0, 0, w, h);
                        if(dest2)
                                {
                                succ = gdk_pixbuf_save (dest2, *GLOBALS->fileselbox_text, "png", &err, NULL);
                                }
                        }
                }
        }
    

    The gdk_pixbuf_get_from_drawable() function when the source drawable is a Quartz window has issues, specifically in how _gdk_quartz_image_copy_to_image() services it. In short, 256 bit vertical strips are converted but the conversion routine assumes the pixels are 24-bit RGB rather than 32-bit RGBA. The following patch fixed the problem for me:

    --- gtk+/gdk/quartz/gdkimage-quartz.c   2011-12-03 14:24:03.000000000 -0600
    +++ gtk+664894/gdk/quartz/gdkimage-quartz.c     2013-10-15 18:52:24.000000000 -0500
    @@ -150,6 +150,10 @@ _gdk_quartz_image_copy_to_image (GdkDraw
           data = [rep bitmapData];
           size = [rep size];
    
    +      int bpr = [rep bytesPerRow];
    +      int wid = size.width;
    +      int bpx = bpr/wid;
    +
          for (y = 0; y < size.height; y++)
            {
             guchar *src = data + y * [rep bytesPerRow];
    @@ -158,12 +162,15 @@ _gdk_quartz_image_copy_to_image (GdkDraw
                {
                  gint32 pixel;
    
    +              if (bpx == 4) // fix gdk_pixbuf_get_from_drawable "yellow stripes"
    +                pixel = src[0] << 16 | src[1] << 8 | src[2];
    +              else
                  if (image->byte_order == GDK_LSB_FIRST)
                    pixel = src[0] << 8 | src[1] << 16 |src[2] << 24;
                  else
                    pixel = src[0] << 16 | src[1] << 8 |src[2];
    
    -             src += 3;
    +             src += bpx;
    
                  gdk_image_put_pixel (image, dest_x + x, dest_y + y, pixel);
                }
    

    I don't know if this was fixed in future versions of the GTK OSX source. I use my own for producing binaries of gtkwave as I have some necessary patches that were seemingly never integrated into the jhbuild source tree long ago.

    -Tony