From octave-sources-request at bevo dot che dot wisc dot edu Wed Sep 9 14:11:45 1998 Subject: image2X - oct function for displaying images on an X display From: Doug Warner To: octave-sources at bevo dot che dot wisc dot edu Date: Wed, 9 Sep 1998 13:11:23 -0600 (MDT) --Gaggle_of_Geese_331_000 Content-Type: TEXT/plain; charset=us-ascii Content-MD5: RbyA06BIySGyOCeo5jXAkQ== Hi, Here is a very simple oct function that displays an image using X. It most certainly has bugs. But it does enough for me and is faster than running data through xv. Caveat emptor! It uses the same colormap setup as the standard image & imagesc routines, but every window uses a private copy. It only works with 8-bit images. I suggest that you experiment with it before trying to use it with critical data (i.e. save your data before you use it!). For example, using a window manager to close the window, rather than pressing `q', can terminate your octave process. In other words, image2X does minimal X event handling. It compiles for me on sparc-sun-solaris2.6 and requires pthreads. I don't know if it will work on systems before X11R6 because of the multithreading. On my system (for Octave 2.0.13), I compile it by mkoctfile image2X.cc -I/usr/openwin/include -lX11 -lpthread Cheers! Doug Warner --Gaggle_of_Geese_331_000 Content-Type: TEXT/plain; name="imagesc2X.m"; charset=us-ascii; x-unix-mode=0644 Content-Description: imagesc2X.m Content-MD5: 1f2ZUnv+ZEkKvwiYYQ3WBg== function out = imagesc2X (x, name) ## imagesc2X - scaled image display on X windows ## ## See image2X if (nargin < 1 || nargin > 2) error ("usage: imagesc2X (image [, name])"); endif [ high, wide ] = size (x); minval = min (min (x)); maxval = max (max (x)); if (maxval == minval) x = zeros (high, wide); else % Rescale values to the range [0, size (colormap, 1) - 1] inclusive. x = fix ((x - minval) / (maxval - minval) * (size (colormap, 1) - 1)); end if (nargin == 2) image2X (x, name); else image2X (x); endif if (nargout) out = x; endif endfunction --Gaggle_of_Geese_331_000 Content-Type: TEXT/plain; name="image2X.cc"; charset=us-ascii; x-unix-mode=0644 Content-Description: image2X.cc Content-MD5: EB6Ucxnm5qooFzkAgRkgPA== //============================================================================= // Copyright (C): 1998, All Rights Reserved. // File: image2X.cc // Purpose: octave function for displaying an image in a // simple window on an X display // Version: 0.0 // Language: GNU C++ 2.8.1 // Target: Solaris 2.6 // Programmer: Doug Warner //============================================================================= // HISTORY ==================================================================== // 980908 : Created // INCLUDES =================================================================== #include #include #include #include #include // for octave declarations #include // for reading octave globals #include #include #include #include // Image ====================================================================== class Image { public: //--------------------------------------------------------------------- // CONSTRUCTORS ------------------------------------------------------------- Image () : my_data (NULL), my_rows (0), my_cols (0) {} Image (unsigned rows, unsigned cols) : my_data (new char [rows * cols]), my_rows (rows), my_cols (cols) {} // ACCESSORS ---------------------------------------------------------------- char const* data () const { return my_data; } char* data () { return my_data; } unsigned rows () const { return my_rows; } unsigned cols () const { return my_cols; } // MISC --------------------------------------------------------------------- ~Image() { if (my_data) delete [] my_data; } Image (Image& other) : my_data (other.my_data), my_rows (other.my_rows), my_cols (other.my_cols) { other.my_data = NULL; other.my_rows = 0; other.my_cols = 0; } Image& operator= (Image& other) { if (this != &other) { this->~Image (); new (this) Image (other); } return *this; } private: //-------------------------------------------------------------------- // DATA MEMBERS ------------------------------------------------------------- char* my_data; // array of pixel values unsigned my_rows; // row dimension unsigned my_cols; // column dimension }; // Pthread_arg ================================================================ struct Pthread_arg { Image image; string name; }; // get_global ================================================================= octave_value get_global (string const& nm) // PURPOSE: Get the value of a global symbol // RECEIVES: name - global symbol to get // RETURNS: result - value of NAME { octave_value result; symbol_record *sr = global_sym_tab->lookup (nm); if (sr) { octave_value val = sr->variable_value (); if (val.is_defined ()) result = val; } return result; } // allocate_colors ============================================================ unsigned allocate_colors (Display* display, Colormap colormap, XcmsColor* p, unsigned n) { unsigned skipped = 0; for (XcmsColor* q = p + n; p < q; ++p) if (XcmsAllocColor (display, colormap, p, XcmsRGBFormat) == XcmsFailure) ++skipped; return n - skipped; } // draw_image ================================================================= void draw_image (Display* display, XImage* ximage, Window window, GC gc, int x, int y, int width, int height) { XPutImage (display, window, gc, ximage, x, y, x, y, width, height); return; } // make_ximage ================================================================ XImage* make_ximage (Display* display, Visual* visual, unsigned depth, unsigned width, unsigned height, Image const* image) { double sc_width = (width - 1) / double (image->cols () - 1); double sc_height = (height - 1) / double (image->rows () - 1); char const* image_data = image->data (); char* data = (char*) malloc (sizeof (char) * width * height); unsigned cols = image->cols (); for (unsigned i = 0; i < height; ++i) { unsigned row = unsigned (i / sc_height); for (unsigned j = 0; j < width; ++j) { unsigned col = unsigned (j / sc_width); data [width*i + j] = image_data [cols*row + col]; } } return XCreateImage (display, visual, depth, ZPixmap, 0, data, width, height, 8, 0); } // setup_window =============================================================== Window setup_window (Display* display, Window parent, int screen, unsigned width, unsigned height, char const* name) { int x = 0; // upper left x-coordinate int y = 0; // upper left y-coordinate int bd_width = 0; // window border width XColor bg; // window background color bg.pixel = BlackPixel (display, screen); // make it black XColor bd; // window border color bd.pixel = BlackPixel (display, screen); // make it black // We only need a simple window. Window window = XCreateSimpleWindow( display, parent, x, y, width, height, bd_width, bg.pixel, bd.pixel ); // Turn on backing store. XSetWindowAttributes xswa; xswa.backing_store = WhenMapped; XChangeWindowAttributes (display, window, CWBackingStore, &xswa); // Initialize XSizeHints Structure XSizeHints xsize_hints; xsize_hints.flags = USPosition | USSize; xsize_hints.x = x; xsize_hints.y = y; xsize_hints.width = width; xsize_hints.height = height; XSetStandardProperties( display, window, name, name, 0, NULL, 0, &xsize_hints ); XSelectInput( display, window, (KeyPressMask | ExposureMask | StructureNotifyMask) ); return window; } // get_colormap =============================================================== void get_colormap (XcmsColor*& colors, unsigned& n) { Matrix colormap; // n-by-3 array of RGB intensities // We look for a global variable containing RGB intensities. If // we can't find it, we make a linear grayscale colormap. octave_value oct_colormap = get_global ("__current_color_map__"); if (oct_colormap.is_undefined () || !oct_colormap.is_real_matrix ()) { Matrix ramp = Range (0.0, 1.0, 1 / 255.0).matrix_value (); assert (ramp.rows () == 1 && ramp.cols () == 256); colormap.resize (3, 256); colormap.insert (ramp, 0, 0); colormap.insert (ramp, 1, 0); colormap.insert (ramp, 2, 0); colormap = colormap.transpose (); assert (colormap.rows () == 256 && colormap.cols () == 3); } else colormap = oct_colormap.matrix_value (); if (!error_state) { n = colormap.rows (); colors = new XcmsColor [n]; for (unsigned i = 0; i < n; ++i ) { XcmsColor& color = colors [i]; color.format = XcmsRGBiFormat; color.spec.RGBi.red = colormap (i, 0); color.spec.RGBi.green = colormap (i, 1); color.spec.RGBi.blue = colormap (i, 2); } } return; } // show_image ================================================================= void* show_image (Pthread_arg const* arg) { Display* display = NULL; int screen; int depth; Colormap colormap; Visual* visual; Window root; { char* display_name = NULL; if ((display = XOpenDisplay (display_name)) == NULL) ; // should some kind of error reporting here else { // We get some default properties of the display. screen = DefaultScreen( display ); root = RootWindow( display, screen ); depth = DefaultDepth( display, screen ); visual = DefaultVisual( display, screen ); } } Image const* image = &arg->image; unsigned width = image->cols (); // window width unsigned height = image->rows (); // window height char const* name = arg->name.c_str (); // window name Window window = setup_window (display, root, screen, width, height, name); // create our window // Get the colormap and select it. XcmsColor* colors; unsigned ncolors; get_colormap (colors, ncolors); colormap = XCreateColormap (display, window, visual, AllocNone); if (allocate_colors (display, colormap, colors, ncolors) != ncolors) ; // Do some error reporting here. XSetWindowColormap( display, window, colormap ); GC gc (XCreateGC (display, window, 0, 0)); XImage* ximage = make_ximage (display, visual, depth, width, height, image); XMapWindow( display, window ); draw_image (display, ximage, window, gc, 0, 0, width, height); XFlush( display ); bool done = true; XEvent myevent; while (done) { XNextEvent( display, &myevent ); switch (myevent.type) { case Expose: draw_image (display, ximage, window, gc, myevent.xexpose.x, myevent.xexpose.y, myevent.xexpose.width, myevent.xexpose.height); break; case KeyPress: if (XLookupKeysym (&myevent.xkey, 0) == XK_q) done = false; break; case ConfigureNotify: width = myevent.xconfigure.width; height = myevent.xconfigure.height; XDestroyImage (ximage); ximage = make_ximage (display, visual, depth, width, height, image); draw_image (display, ximage, window, gc, 0, 0, width, height); break; default: break; } XSync( display, False ); } // Clean before this thread exits. XFreeGC( display, gc ); XFreeColormap( display, colormap ); XDestroyImage (ximage); XDestroyWindow( display, window ); XFlush( display ); XCloseDisplay (display); delete colors; delete arg; return NULL; } // matrix_to_image ============================================================ Image matrix_to_image (Matrix const& mat) // PURPOSE: Extract pixel values from an Octave matrix. // ARGUMENTS: mat - matrix of pixel values // RETURNS: result - pointer to a structure for the image pixel values // callee assumes ownership of result { unsigned rows = mat.rows (); unsigned cols = mat.cols (); Image result (rows, cols); char* data = result.data (); for (unsigned i = 0; i < rows; ++i) for (unsigned j = 0; j < cols; ++j) *data++ = char (mat (i, j)); return result; } // image2X ==================================================================== DEFUN_DLD( image2X, args, , "\ image2X (image [, name]) - display image in an X window with title \"name\" image2X (image) creates a window on the X display using the dimensions of image and the current colormap. image2X (image, \"name\") does the same, but using \"name\" as the window name. To close windows created by image2X, press `q' (case matters!) in that window WARNING! (LIMITATIONS) image2X can only handle 8-bit images. The values in image must be integers between 0 and 255. Thus it is not compatible with the output from imagesc. Every window has its own colormap, resulting in \"flashing.\" image2X may kill your octave process. BEWARE! See also: imagesc2X, colormap, image, imagesc " ) { static char* fun_name = "image2X"; // Check for correct usage -- 1 or 2 input arguments. switch (args.length ()) { case 2: if (!args (1).is_string ()) // window name error ("%s: expecting string as second argument", fun_name); case 1: if (!args (0).is_real_matrix ()) // image error ("%s: expecting real matrix as first argument", fun_name); break; default: error ("Usage: %s (matrix [, name])", fun_name); } if (!error_state) { // Create the thread argument. Pthread_arg* arg = new Pthread_arg; if (args.length () == 2) arg->name = args (1).string_value (); else arg->name = fun_name; arg->image = matrix_to_image (args (0).matrix_value ()); // Create the thread. show_image assumes ownership of arg. pthread_t thread; pthread_attr_t tattr; pthread_attr_init (&tattr); pthread_attr_setdetachstate (&tattr, PTHREAD_CREATE_DETACHED); pthread_create (&thread, &tattr, show_image, arg); } return octave_value_list (); } //============================================================================= --Gaggle_of_Geese_331_000--