Re: GTK+ back buffers?



Pete Ryland <pdr-lists@pdr.ml.org> writes:

| > Use X pixmap.  Just "draw" to the pixmap, and gdk_window_copy_area
| > (XCopyArea without gtk) from the pixmap to the target window.

| When you say "draw" to the pixmap, can I do this from an "unsigned char*"?

The following example shows the technique. It uses gdk_draw_row to
fill a preview and copies the data from the preview into a pixmap.
After all images are loaded the frames are played by switching the
pixmaps. The pixmap switching uses 1% of a PII333 with Millenium1 in
Hicolor but much memory, because pixmaps are stored in the format the
running X11 server can use best.

  PID USER     PRI  NI  SIZE  RSS SHARE STAT  LIB %CPU %MEM   TIME COMMAND
  391 root       0   0 69140  64M   792 R       0  0.3 51.5   0:49 X

----------------------------------------------------------------------
#define FRAME_WIDTH 352
#define FRAME_HEIGHT 288
#define FRAME_SIZE (FRAME_WIDTH * FRAME_HEIGHT)
#define MAX_FRAMES 300

static guchar buf[FRAME_SIZE * MAX_FRAMES];

static int gray_idle = 0;

static GdkPixmap *cached[MAX_FRAMES];
static int cache_full = 0;
GtkWidget *window = NULL;

gint
gray_idle_func (GtkWidget *preview)
{
  static int frame = 0;
  GdkGC    *gc;

  if (!cache_full) {
    cached[frame] = gdk_pixmap_new (window->window, 352, 288, 16);
    gc = gdk_gc_new (cached[frame]);
    gtk_preview_put (GTK_PREVIEW (preview), cached[frame], gc, 0,0,0,0,352,288);
    gdk_gc_destroy(gc);
  }

  frame += 1;
  if (frame >= MAX_FRAMES) {
    frame = 0;
    cache_full = 1;
  }

  if (cache_full) {

    gc = gdk_gc_new (cached[frame]);
    gdk_draw_pixmap (preview->window,
                     gc,
                     cached[frame],
                     0,0,0,0, 352,288
                     );
    gdk_gc_destroy(gc);

  } else {
    
    gtk_preview_draw_row (GTK_PREVIEW (preview),
                          &(buf[FRAME_SIZE * frame]), 0, 0, FRAME_SIZE);
    gtk_widget_draw (preview, NULL);
    
  }

  return TRUE;
}
----------------------------------------------------------------------

If you do not want to use a preview, you can do everything done by the
preview on your own. The steps are:

1) Create a image
2) Fill the image according to the X11 server settings
3) Create a pixmap
4) Copy the image into the pixmap

To understand this overhead, you have to remember the client/server
architecture of X11. Pixmaps are stored in the the server (the system
with your screen), and images are stored in the client (the system
where you programm is running). You can not work on pixmaps memory
directly, because it is on a different machine.

One fault of X11 is, that it does not provide image conversion
routines. You are responsible to merge your data into a form suitable
for the X11 server. This means, that you have to do dithering, if you
program runs on a 8 bit display, or you have to clip your RGB date from
888 to 565, or what else a X11 implementor can imagine...

The followin example shows how to create X11 images on a 565 display
from 422 YUV data:

----------------------------------------------------------------------
#include "gtk/gtk.h"
#include <string.h>


void exit_program()
{
  gtk_exit(0);
}

GtkWidget *window = NULL;

GtkWidget *drawing_area;
GdkPixmap *pixmap;


#define FRAME_WIDTH 352
#define FRAME_HEIGHT 288
#define FRAME_SIZE (FRAME_WIDTH * FRAME_HEIGHT)
#define MAX_FRAMES 300



#define RGB565_FROM_RGB888(R8, G8, B8) \
  (((guint16)(((guchar)R8) & ((guchar)0xf8u))) << 8) | \
  (((guint16)(((guchar)G8) & ((guchar)0xfcu))) << 3) | \
  ((guint16)(((guchar)B8) >> 3))

#define RGB565_FROM_RGB888_(R8, G8, B8) (((R8) >> 3) << 11) | (((G8) >> 2) << 5) | ((B8) >> 3)

     
void
copy_y8_to_rgb565 (guchar  *src,
		   guint16 *dst,
		   guint    size)
{
  while (size--) {
    *dst = RGB565_FROM_RGB888 (*src, *src, *src);
    src++;
    dst++;
  }
}

#define CLIP_TO_UCHAR(X) ((((int)X)<0)?0:((((int)X)>255)?255:((unsigned char)X)))

void
copy_y8_i4_q4_to_rgb565 (guchar  *ysrc,
			 guchar  *isrc,
			 guchar  *qsrc,
			 guint16 *dst,
			 guint    size)
{
  guchar r, g, b;
  int    ri, gi, bi;
  int    ii, qi;

  while (size--) {
    ii  = ((int)(*isrc)) - 128;
    qi  = ((int)(*qsrc)) - 128;
    
    ri = gi = bi = *ysrc;
    ri += (int)(1.4025*(float)ii);
    gi += (int)(-0.714*(float)ii -0.389*(float)qi);
    bi += (int)(1.773*(float)qi);
    
    r = (guchar) CLIP_TO_UCHAR (ri);
    g = (guchar) CLIP_TO_UCHAR (gi);
    b = (guchar) CLIP_TO_UCHAR (bi);

    if (1) {
      static int first_ten = 5;
      if (first_ten) {
	printf("%6d %6d %6d %6d %6d %6d\n", *ysrc, *isrc, *qsrc, ri, gi, bi);
	first_ten--;
      }
    }
    
    *dst = RGB565_FROM_RGB888 (r, g, b);
    
    if (size--) {
      ysrc++;
      ri = gi = bi = *ysrc;
      ri += (int)(1.4025*(float)ii);
      gi += (int)(-0.714*(float)ii -0.389*(float)qi);
      bi += (int)(1.773*(float)qi);

      r = (guchar) CLIP_TO_UCHAR (ri);
      g = (guchar) CLIP_TO_UCHAR (gi);
      b = (guchar) CLIP_TO_UCHAR (bi);
      
      if (1) {
	static int first_ten = 5;
	if (first_ten) {
	  printf("%6d %6d %6d %6d %6d %6d\n", *ysrc, *isrc, *qsrc, ri, gi, bi);
	  first_ten--;
	}
      }

      dst++;
      *dst = RGB565_FROM_RGB888 (r, g, b);

      ysrc++;
      isrc++;
      qsrc++;
      dst++;
    }
  }
}
      
    
void
duplicate_interlaced_lines (guchar *buf,
			    guint   row_size,
			    guint   rows)
{
  while (rows) {
    rows--;
    memmove ((void*)&(buf[ rows*2   *row_size]), (void*)&(buf[rows*row_size]), row_size);
    memmove ((void*)&(buf[(rows*2+1)*row_size]), (void*)&(buf[rows*row_size]), row_size);
  }
}
	     

GdkPixmap *pixmaps[300];

void create_some_pixmaps ()
{
  int       i;
  FILE     *fp_y;
  FILE     *fp_i;
  FILE     *fp_q;
  guchar    buf_y[352*288];
  guchar    buf_i[(352*288)/2];
  guchar    buf_q[(352*288)/2];
  GdkGC    *gc;
  GdkImage *image;

  fp_y = fopen ("akiyosy.sby", "r");
  fp_i = fopen ("akiyosy.sbv", "r");
  fp_q = fopen ("akiyosy.sbu", "r");
  
  for (i=0; i<10; i++) {
    fread (buf_y, 352*288, 1, fp_y);
    fread (buf_i, (352*288)/4, 1, fp_i);
    fread (buf_q, (352*288)/4, 1, fp_q);

    duplicate_interlaced_lines (buf_i, 352/2, 144);
    duplicate_interlaced_lines (buf_q, 352/2, 144);

    if (1) {
      static int first = 1;
      if (first) {
	GdkVisual *visual = gdk_window_get_visual (drawing_area->window);
	printf("type:          %d\n", visual->type);
	printf("depth:         %d\n", visual->depth);
	printf("byte_order:    %d\n", visual->byte_order);
	printf("colormap_size: %d\n", visual->colormap_size);
	printf("bits_per_rgb:  %d\n", visual->bits_per_rgb);
	printf("red_mask:      0x%x\n", visual->red_mask);
	printf("red_shift:     0x%x\n", visual->red_shift);
	printf("red_prec:      0x%x\n", visual->red_prec);
	printf("green_mask:    0x%x\n", visual->green_mask);
	printf("green_shift:   0x%x\n", visual->green_shift);
	printf("green_prec:    0x%x\n", visual->green_prec);
	printf("blue_mask:     0x%x\n", visual->blue_mask);
	printf("blue_shift:    0x%x\n", visual->blue_shift);
	printf("blue_prec:     0x%x\n", visual->blue_prec);
	first--;
      }
    }
	
    image = gdk_image_new (GDK_IMAGE_FASTEST,
			   gdk_window_get_visual (drawing_area->window),
			   352, 288);
    
    copy_y8_i4_q4_to_rgb565 (buf_y, buf_i, buf_q, (guint16*)(image->mem), 352*288);
    
    pixmaps[i] = gdk_pixmap_new (drawing_area->window, 352, 288, 16);
    gc = gdk_gc_new (pixmaps[i]);
    gdk_draw_image (pixmaps[i], gc, image, 0,0,0,0, 352, 288);
    gdk_image_destroy (image);
    gdk_gc_destroy(gc);
  }
  fclose (fp_y);
  fclose (fp_i);
  fclose (fp_q);
}
  
int idx = 0;

void start_player()
{

  if (idx >= 10) idx = 0;
  printf ("%d\n", idx);
  
  gdk_window_set_back_pixmap (drawing_area->window, pixmaps[idx], 0);
  gdk_window_clear (drawing_area->window);
  gdk_flush ();
  
  idx++;
}

void stop_player()
{
  /**/
}



void make_main_window()
{
  GtkWidget *vbox;
  GtkWidget *hbox;
  GtkWidget *button;
  
  
  /* window */
  window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
  gtk_container_border_width (GTK_CONTAINER (window), 0);
  vbox = gtk_vbox_new (FALSE, 0);
  gtk_container_add (GTK_CONTAINER (window), vbox);
  gtk_widget_show (vbox);

  /* drawing area */
  drawing_area = gtk_drawing_area_new ();
  gtk_drawing_area_size (GTK_DRAWING_AREA (drawing_area), 352, 288);
  gtk_container_add (GTK_CONTAINER (vbox), drawing_area);
  gtk_widget_show (drawing_area);

  /* buttons */
  hbox = gtk_hbox_new (TRUE, 10);
  gtk_container_add (GTK_CONTAINER (vbox), hbox);
  gtk_widget_show (hbox);
  button = gtk_button_new_with_label ("Start");
  gtk_container_add (GTK_CONTAINER(hbox), button);
  gtk_signal_connect (GTK_OBJECT(button), "clicked",
		     (GtkSignalFunc) start_player,
		     &window);
  gtk_widget_show (button);
  button = gtk_button_new_with_label ("Stop");
  gtk_container_add (GTK_CONTAINER(hbox), button);
  gtk_signal_connect (GTK_OBJECT(button), "clicked",
		     (GtkSignalFunc) stop_player,
		     &window);
  gtk_widget_show (button);
  button = gtk_button_new_with_label ("Quit");
  gtk_container_add (GTK_CONTAINER(hbox), button);
  gtk_signal_connect (GTK_OBJECT(button), "clicked",
		     (GtkSignalFunc) exit_program,
		     &window);
  gtk_widget_show (button);

  /* show window */
  gtk_widget_show (window);

  /* read the video */
  create_some_pixmaps (); 
}

int main (int argc, char *argv[])
{
  gtk_init(&argc, &argv);
  make_main_window();
  gtk_main();
  return 0;
}
----------------------------------------------------------------------

I hope this helps a bit to understand X11. It took me a while to
figure all this stuff out with the help of the Gtk+ source, imlib
source, a Xlib Programming Manual and the Gtk+ mailing list.

-- 
http://www.ping.de/sites/aibon/



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