Re: A Line in a Pixbuf (LONG)



On Tue, 16 Mar 2004, [iso-8859-1] Christian Schneider wrote:

Hallo,

I created a Pixbuf from a PNG-File and I show it in a
drawing area. This works fine.

But now I want to draw a red line "in" the image
(PNG-File):

1.) Is it possible to draw it directly in the pixbuf
after loading the PNG-File? How can I do this?

I think you have to draw the line in the drawing area / pixmap.  Then when
you're done, you copy the drawing area / pixmap to the pixbuf and write
the pixbuf to disk.

2.) How can I draw the line in the drawing area after "putting" the
Pixbuf in the drawing area?

Here's some sample code showing what I did using gtk 2.x.  I've not shown
the declaration of local variables, etc., to save space.  Note:  When I
draw something I draw it into both the drawing area and the associated
pixmap so that window refreshes work.

The program works like this:

(1) User selects "draw line" from a menu

(2) Program waits for a left button press; user can abort the action by
pressing the right button.

(3) Program gets mouse motion events and draws a line in a pre-specified
color from the previous mouse location to the current one.

(4) Program stops drawing the lines when the left button is released.

(5) User can choose to write the current drawing area to disk using a
"save drawing" menu item.


In more detail:


=== begin code extracts ===


(1) Set up a menuitem for drawing the line, specifying the callback
function:



static GnomeUIInfo edit_menu_uiinfo[] =
{

[...]

  {
    GNOME_APP_UI_ITEM, N_("Draw line"),
    NULL,
    (gpointer) on_draw_line_activate, NULL, NULL,
    GNOME_APP_PIXMAP_NONE, NULL,
    0, (GdkModifierType) 0, NULL
  },

[...]



(2) In the line draw_line callback function, enable button press and
release events and declare the appropriate mouse motion and button event
handlers:



void
on_draw_line_activate                 (GtkMenuItem     *menuitem,
                                       gpointer         user_data)
{

[...]

   drawing_area = gtk_object_get_data (GTK_OBJECT (main), "drawingarea");
   if (pixmap)
      g_object_unref(pixmap);
   pixmap = gdk_pixmap_new(
               drawing_area->window,
               drawing_area->allocation.width,
               drawing_area->allocation.height,
               -1);

/* refresh the window from the pixmap */

   gdk_draw_drawable(
      drawing_area->window,
      drawing_area->style->bg_gc[GTK_WIDGET_STATE (drawing_area)],
      pixmap,
      0, 0,
      0, 0,
      drawing_area->allocation.width,
      drawing_area->allocation.height);

/* save the foreground color and get a new gc */

   gdk_gc_get_values (
      drawing_area->style->fg_gc[drawing_area->state],
      (GdkGCValues *) &values);

   mask = (GDK_GC_FOREGROUND) | (GDK_GC_BACKGROUND) | (GDK_GC_FUNCTION) |
(GDK_GC_FILL);
   gc_fg = gdk_gc_new_with_values (
              drawingarea1->window,
              &values,
              mask);

/* add the events */

   gtk_widget_add_events(drawing_area, GDK_LEAVE_NOTIFY_MASK);
   gtk_widget_add_events(drawing_area, GDK_BUTTON_PRESS_MASK);
   gtk_widget_add_events(drawing_area, GDK_POINTER_MOTION_MASK);
   gtk_widget_add_events(drawing_area, GDK_POINTER_MOTION_HINT_MASK);
   gtk_widget_add_events(drawing_area, GDK_BUTTON_RELEASE_MASK);

   gtk_widget_add_events(drawing_area, GDK_EXPOSURE_MASK);
   gtk_widget_add_events(drawing_area, GDK_FOCUS_CHANGE_MASK);
   gtk_widget_add_events(drawing_area, GDK_STRUCTURE_MASK);
   gtk_widget_add_events(drawing_area, GDK_PROPERTY_CHANGE_MASK);
   gtk_widget_add_events(drawing_area, GDK_VISIBILITY_NOTIFY_MASK);
   gtk_widget_add_events(drawing_area, GDK_FOCUS_CHANGE_MASK);

/* connect the button press and release event handlers */

   g_signal_connect (
      (GtkObject *) drawing_area,
      "button_press_event",
      (GtkSignalFunc) (button_pressed),
      NULL);
   g_signal_connect (
      (GtkObject *) drawing_area,
      "button_release_event",
      (GtkSignalFunc) (button_released),
      NULL);
   gtk_widget_show (drawing_area);
}



(3) In the button_press event handler, check for proper button and
activate the mouse motion handlers:



gboolean
button_pressed                        (GtkWidget       *widget,
                                       GdkEventButton  *event,
                                       gpointer        data)
{

[...]

   gint which_button;

/* button pressed, check if it's the left or right button */

   g_signal_handlers_disconnect_by_func (
      (GtkObject *) drawing_area,
      (GtkSignalFunc) (button_pressed),
      NULL);

   which_button = event->button;
   if (which_button != LEFT_BUTTON)  {   /* disable the mouse events */
      g_signal_handlers_disconnect_by_func (
         (GtkObject *) drawing_area,
         (GtkSignalFunc) (motion_notify),
         NULL);
      g_signal_handlers_disconnect_by_func (
         (GtkObject *) drawing_area,
         (GtkSignalFunc) (button_released),
         NULL);
      gdk_gc_set_foreground (
         drawing_area->style->fg_gc[drawing_area->state],
         &(values.foreground));
      gdk_beep ();
      return FALSE;
   }

/* left button pressed, set the foreground color and turn on the mouse
motion handler */

   gdk_gc_set_foreground (
      gc_fg,
      &(colortable[line_color]));

   g_signal_connect (
      (GtkObject *) drawing_area,
      "motion_notify_event",
      (GtkSignalFunc) (motion_notify),
      NULL);
   x_start = event->x;
   y_start = event->y;
   return TRUE;
}



(4) In the mouse_motion handler, draw the line between one mouse_motion
event and the current one into the drawing area / pixmap:



gboolean
motion_notify                         (GtkWidget       *widget,
                                       GdkEventMotion  *event,
                                       gpointer        data)
{
   gint x1,y1,x2,y2;
   gint x, y;
   GdkModifierType state;

   if (event->is_hint) {
      gdk_window_get_pointer (event->window, &x, &y, &state);
   }
   else  {
      x = event->x;
      y = event->y;
      state = event->state;
   }
   x_end = event->x;
   y_end = event->y;
   x1 = x_start;
   y1 = y_start;
   x2 = x_end;
   y2 = y_end;

   if (state & GDK_BUTTON1_MASK && pixmap != NULL)  {
      gdk_draw_line (
         (GdkDrawable *) drawing_area->window,
         gc_fg,
         x1,y1,
         x2,y2);
      gdk_draw_line (
         (GdkDrawable *) pixmap,
         gc_fg,
         x1,y1,
         x2,y2);
   }

   x_start = x_end;
   y_start = y_end;
   gtk_widget_show (drawingarea1);
   return TRUE;
}



(5) In the button_release handler, disable mouse motion events, and
terminate drawing the line:



gboolean
button_released                       (GtkWidget       *widget,
                                       GdkEventButton  *event,
                                       gpointer        data)
{

   [...]

   gint which_button;

   which_button = event->button;
   if (which_button != LEFT_BUTTON)  {   /* ignore right button releases
*/
      return FALSE;
   }

/* left button released, reset foreground color */

   gdk_gc_set_foreground (
      drawing_area->style->fg_gc[drawing_area->state],
      &(values.foreground));

/* turn off the mouse motion and button released handlers */

   g_signal_handlers_disconnect_by_func (
      (GtkObject *) drawing_area,
      (GtkSignalFunc) (motion_notify),
      NULL);
   g_signal_handlers_disconnect_by_func (
      (GtkObject *) drawing_area,
      (GtkSignalFunc) (button_released),
      NULL);
   return TRUE;
}



(6) In the file_save callback, copy the drawing area / pixmap to the
pixbuf and write the pixbuf to an output file:



void
on_save_ok_button_clicked           (GtkButton       *button,
                                     gpointer         user_data)
{

   [...]

   filename = (gchar *) gtk_file_selection_get_filename
(GTK_FILE_SELECTION (file_selector));

   gtk_widget_destroy (file_selector);

/* create a pixbuf from the current pixmap    */
/*   and save the pixbuf to the selected file */
/*   as a ".png" file                         */

   pixbuf = gdk_pixbuf_get_from_drawable (
               NULL,
               pixmap,
               colormap_default,
               0, 0,
               0, 0,
               -1, -1);

   error_flag = gdk_pixbuf_save (pixbuf,
                   filename,
                   "png", NULL,
                   NULL);

   g_object_unref (pixbuf);

[snip error handling]

}


=== end code extracts ===


That code works for me.

Bryan


*---------------------------------------------------------------
* Bryan Brown      <*>              bbrown radix net
* http://www.radix.net/~bbrown
*---------------------------------------------------------------





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