canvas/pixbuf scroll_region usage (odd behavior on solaris)



hello,

i have written an image viewer using gnome_canvas/gdk_pixbuf.
due to the nature of the data (very fuzzy/speckled, such that 
features/artifacts show up at different scale), i would like
to be able to:

- zoom in/out
- during zoom, preserve center
- navigate through the image (both click and drag)
- be able to center any pixel in the image
- not be able to center outside the image

my initial attempts were unsuccessful (the canvas would 
re-center things as i zoomed). what i ended up doing was setting
the scroll region to be the center of the image (ie, just a point),
and gtk_adjustment_value_changed to re-center.

this exposed a bug (well, i assume it is a bug), in gtklayout.
the adjustments float value is cast to guint and assigned to a
guint ([xy]offset). i don't see any guarantee that adj->value is
positive. code clipping:

gtk_layout_adjustment_changed
  layout->xoffset = (gint)layout->hadjustment->value;
  layout->yoffset = (gint)layout->vadjustment->value;
struct _GtkLayout
  guint xoffset;
  guint yoffset;

this code has disappeared in cvs, so i am hoping that i am right.
i patched my local copies, changing xoffset/yoffset to gint's,
and recompiled everything.

this works beautifully on linux. i get smooth scrolling, and 
meet all my requirements. however, on solaris, i get very odd
behavior.


solaris
SunOS Release 5.5.1 Version Generic_103640-24 [UNIX(R) System V Release 4.0]
gnome-libs 1.2.7
gtk 1.2.8
gdk-pixbuf-config 0.8.0


OCCURANCE MODEL

the symptom appears to occur only if:

1. it is run and displayed on the same box
2. that box is solaris (as above)
3. it is the first gnome canvas application

actually, 3 is a little more complicated. let test be a canvas application.
./test -> invocation A
./test -> invocation B
./test -> invocation C

results in A being faulty, but B and C are fine. now
kill %1 (ie, A)

B and C are still fine. now 
./test -> invocation D

B and C are still fine, but D is faulty.


THE SYMPTOM

during panning, large chunks of the image are not redrawn on adjustment_changed,
which leaves big hunks of background where the image should be (typically in the
lower right hand corner).


questions.

1. is my use of set_scroll_region (to a point), just plain old bad, and the fact
that it works on linux coincidence?

2. assuming that i need set_scroll_region to something more reasonable, any 
suggestions on how to accomplish my goals (perhaps set the scroll region
very large)


for anyone who has actually made it this far into my ramble, i am attaching code
that demonstrates the problem for me. it is pretty short. the image is ugly -
just something to be visible.

button 1 and drag to scroll (a little weird the first time)
shift/button 1 to zoom in
shift/button 2 to re-center on pixel the mouse points to
shift/button 3 to zoom out

gcc  -o testgn4 testgn4.c `gnome-config --cflags gnomeui`  \
  -lgdk_pixbuf -lm `gnome-config --libs gnomeui` `gdk-pixbuf-config --libs` \
  -lglib  -lgdk_pixbuf  -lgnomecanvaspixbuf


 LocalWords:  testgn config cflags gnomeui lgdk pixbuf
#include <gtk/gtk.h>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <gnome.h>
#include <gdk-pixbuf/gnome-canvas-pixbuf.h>
#include <gdk-pixbuf/gdk-pixbuf.h>

typedef struct { GnomeCanvas *canvas; GtkAdjustment *zadj; } mystuff;


static void destroy(GtkWidget *widget, gpointer data) { gtk_main_quit(); }

static gint my_events (GtkWidget *w,GdkEvent *event,mystuff *z) {
  GdkEventButton *bevent;
  GdkEventMotion *mevent;
  gint tx,ty,x, y, width, height;
  int b1;
  GnomeCanvas *canvas;
  int m2=(GDK_CONTROL_MASK|GDK_MOD1_MASK|GDK_MOD2_MASK|GDK_BUTTON2_MASK|GDK_BUTTON3_MASK);
  int m1=(GDK_SHIFT_MASK|m2);
  GtkAdjustment *adj;
  int i,j;
  double xo,yo;

  canvas=GNOME_CANVAS(w);
  gdk_window_get_size(w->window,&width,&height);
  gdk_window_get_pointer (w->window, &tx, &ty, NULL);
  x = (1.0*CLAMP (tx, 0, width-1));
  y = (1.0*CLAMP (ty, 0, height-1));
  b1=0;

  if (event->type==GDK_BUTTON_PRESS) {
    gtk_grab_add (w); bevent=(GdkEventButton *)event;
    if (bevent->button==1 && !(bevent->state & m1)) { b1=1; }
    else if (bevent->button==1 && (bevent->state & GDK_SHIFT_MASK) && !(bevent->state & m2)) { b1=2; }
    else if (bevent->button==2 && (bevent->state & GDK_SHIFT_MASK) && !(bevent->state & m2)) { b1=4; }
    else if (bevent->button==3 && (bevent->state & GDK_SHIFT_MASK) && !(bevent->state & m2)) { b1=3; }
  }
  else if (event->type==GDK_BUTTON_RELEASE) {
    gtk_grab_remove(w); bevent=(GdkEventButton *)event; 
    if (bevent->button==1 && !(bevent->state & m1)) { b1=1; }
  }
  else if (event->type==GDK_MOTION_NOTIFY) {
    mevent = (GdkEventMotion *) event; 
    if ((mevent->state & GDK_BUTTON1_MASK) && !(mevent->state & m1)) { b1=1; }
  }

  if (b1==1) {
    double xc1,xc2,yc1,yc2;
    gnome_canvas_item_get_bounds(canvas->root,&xc1,&yc1,&xc2,&yc2);
    if (width>1 && height>1) gnome_canvas_w2c(canvas,xc1+(xc2-xc1)*x/(width-1),yc1+(yc2-yc1)*y/(height-1),&i,&j); else { i=0; j=0; }
  }
  else if (b1==2 || b1==3) {
    gfloat zo,z1,z2;

    i=GTK_LAYOUT(w)->hadjustment->value;
    j=GTK_LAYOUT(w)->vadjustment->value;
    gnome_canvas_c2w(canvas,i,j,&xo,&yo);
    adj=z->zadj;
    zo=adj->value;
    z1=adj->lower;
    z2=adj->upper;

    if (b1==2) (adj->value) *= 2;
    else if (b1==3) (adj->value) /= 2;
    if (adj->value<z1) adj->value=z1;
    if (adj->value>=z2) adj->value=z2;
    if (adj->value != zo)   gnome_canvas_set_pixels_per_unit(GNOME_CANVAS(canvas),adj->value);
    gnome_canvas_w2c(canvas,xo,yo,&i,&j);
  }
  if (b1==4) {
    gnome_canvas_window_to_world(canvas,x,y,&xo,&yo);
    gnome_canvas_w2c(canvas,xo,yo,&i,&j);
  }
  if (b1==1 || b1==2 || b1==3 || b1==4) {
    adj=GTK_LAYOUT(w)->hadjustment; 
    if (adj && width>0) { adj->value=i; gtk_adjustment_value_changed(adj); }
    adj=GTK_LAYOUT(w)->vadjustment;
    if (adj && height>0) { adj->value=j; gtk_adjustment_value_changed(adj); }
  }

  if (0) printf("event: %5d %5d | %d %d \n",width,height,i,j);
  return(0);
}

int main( int argc, char *argv[] ) {
  GtkWidget *window,*canvas;
  guchar *buf3;
  GnomeCanvasGroup *root;
  GdkPixbuf *gpix;
  mystuff *z;
  int i,w=100,h=100;

  buf3=calloc(w*h*3,sizeof(guchar));
  for (i=0;i<w*h*3;i++) *(buf3+i)=i*i;
  gpix=gdk_pixbuf_new_from_data(buf3,ART_PIX_RGB,0,8*sizeof(guchar),w,h,w*3,NULL,NULL);

  gnome_init ("testGNOME", "1.2", argc, argv);
  gdk_rgb_set_verbose (TRUE);
  gdk_rgb_init ();
  gtk_widget_set_default_colormap (gdk_rgb_get_cmap ());
  gtk_widget_set_default_visual (gdk_rgb_get_visual ());

  window = gtk_window_new( GTK_WINDOW_TOPLEVEL );
  gtk_signal_connect (GTK_OBJECT (window), "destroy",(GtkSignalFunc) destroy, NULL);
  gtk_widget_show( window );


  canvas = gnome_canvas_new ();
  root = gnome_canvas_root (GNOME_CANVAS (canvas));
  gnome_canvas_item_new (root,gnome_canvas_pixbuf_get_type (), "pixbuf",gpix, NULL);

  z=(mystuff *)calloc(1,sizeof(mystuff));
  z->canvas=GNOME_CANVAS(canvas);
  z->zadj=GTK_ADJUSTMENT(gtk_adjustment_new (1.00, 0.0625,256, 0.05, 0.50, 0.50));
  gnome_canvas_set_pixels_per_unit(GNOME_CANVAS(canvas),z->zadj->value);

  gnome_canvas_set_scroll_region (GNOME_CANVAS (canvas),0.5*w,0.5*h,0.5*w,0.5*h);
  gtk_container_add (GTK_CONTAINER (window), canvas);
  gtk_widget_show (canvas);
  gtk_signal_connect_after (GTK_OBJECT (canvas),"event",(GtkSignalFunc) my_events,z);

  
  GTK_WIDGET_SET_FLAGS (canvas, GTK_CAN_FOCUS);
  gtk_widget_grab_focus (canvas);
  

  gtk_widget_show_all (window);

  gtk_main ();
  return 0;
}



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