Index: trunk/Ohana/src/opihi/cmd.data/Makefile
===================================================================
--- trunk/Ohana/src/opihi/cmd.data/Makefile	(revision 9359)
+++ trunk/Ohana/src/opihi/cmd.data/Makefile	(revision 9726)
@@ -64,4 +64,5 @@
 $(SDIR)/load.$(ARCH).o		\
 $(SDIR)/lookup.$(ARCH).o	\
+$(SDIR)/mkrgb.$(ARCH).o	\
 $(SDIR)/mcreate.$(ARCH).o	\
 $(SDIR)/medacc.$(ARCH).o	\
Index: trunk/Ohana/src/opihi/cmd.data/init.c
===================================================================
--- trunk/Ohana/src/opihi/cmd.data/init.c	(revision 9359)
+++ trunk/Ohana/src/opihi/cmd.data/init.c	(revision 9726)
@@ -49,4 +49,5 @@
 int load             PROTO((int, char **));
 int lookup           PROTO((int, char **));
+int mkrgb            PROTO((int, char **));
 int mcreate          PROTO((int, char **));
 int medacc           PROTO((int, char **));
@@ -160,4 +161,5 @@
   {"load",         load,	     "load an SAOimage style overlay"},
   {"lookup",       lookup,	     "convert vector via lookup table (vector pair)"},
+  {"mkrgb", 	   mkrgb,	     "convert 3 images to rgb jpeg"},
   {"mcreate", 	   mcreate,	     "create a matrix"},
   {"medacc",       medacc,	     "accumulate vector values in another vector"},
Index: trunk/Ohana/src/opihi/cmd.data/mkrgb.c
===================================================================
--- trunk/Ohana/src/opihi/cmd.data/mkrgb.c	(revision 9726)
+++ trunk/Ohana/src/opihi/cmd.data/mkrgb.c	(revision 9726)
@@ -0,0 +1,78 @@
+# include "data.h"
+# include "jpeglib.h"
+
+int mkrgb (int argc, char **argv) {
+ 
+  int i, j, Nx, Ny;
+  FILE *f;
+  Buffer *red, *green, *blue;
+  float *Vr, *Vg, *Vb;
+
+  struct jpeg_compress_struct cinfo;
+  struct jpeg_error_mgr jerr;
+  JSAMPROW row_pointer[1];	/* pointer to JSAMPLE row[s] */
+  JSAMPLE *image_buffer;	/* Points to data for current line */
+
+  if (argc != 5) {
+    gprint (GP_ERR, "USAGE: mkrgb (red) (green) (blue) (output)\n");
+    return (FALSE);
+  }
+
+  // define the input buffer and examine the shift
+  if ((red    = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+  if ((green  = SelectBuffer (argv[2], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+  if ((blue   = SelectBuffer (argv[3], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+
+  Nx = red[0].matrix.Naxis[0];
+  Ny = red[0].matrix.Naxis[1];
+  if (Nx != blue[0].matrix.Naxis[0]) return (FALSE);
+  if (Ny != blue[0].matrix.Naxis[1]) return (FALSE);
+  if (Nx != green[0].matrix.Naxis[0]) return (FALSE);
+  if (Ny != green[0].matrix.Naxis[1]) return (FALSE);
+
+  f = fopen (argv[4], "w");
+  if (f == (FILE *) NULL) {
+    fprintf (stderr, "failed to open %s for output\n", argv[4]);
+    return (FALSE);
+  }
+
+  /* set up the error handler , initialize the JPEG compression object. */
+  cinfo.err = jpeg_std_error (&jerr);
+  jpeg_create_compress (&cinfo);
+  jpeg_stdio_dest(&cinfo, f);
+  
+  // set up the basic jpeg output file
+  cinfo.image_width = Nx; 	/* image width and height, in pixels */
+  cinfo.image_height = Ny;
+  cinfo.input_components = 3;		        
+  cinfo.in_color_space = JCS_RGB; 	
+  jpeg_set_defaults (&cinfo);
+  jpeg_set_quality (&cinfo, 75, TRUE       /* limit to baseline-JPEG values */);
+  jpeg_start_compress (&cinfo, TRUE);
+
+  ALLOCATE (image_buffer, JSAMPLE, 3*Nx);
+
+  // ??
+  // && (cinfo.next_scanline < cinfo.image_height)
+
+  Vr = (float *) red[0].matrix.buffer;
+  Vg = (float *) green[0].matrix.buffer;
+  Vb = (float *) blue[0].matrix.buffer;
+
+  for (j = 0; j < Ny; j++) {
+    for (i = 0; i < Nx; i++, Vr++, Vg++, Vb++) {
+      image_buffer[3*i+0] = MAX (0.0, MIN (255.0, *Vr));
+      image_buffer[3*i+1] = MAX (0.0, MIN (255.0, *Vg));
+      image_buffer[3*i+2] = MAX (0.0, MIN (255.0, *Vb));
+    }
+    row_pointer[0] = image_buffer;
+    (void) jpeg_write_scanlines (&cinfo, row_pointer, 1);
+  }
+
+  jpeg_finish_compress (&cinfo);
+  fclose (f);
+
+  jpeg_destroy_compress (&cinfo);
+
+  return (TRUE);
+}
Index: trunk/Ohana/src/opihi/cmd.data/shift.c
===================================================================
--- trunk/Ohana/src/opihi/cmd.data/shift.c	(revision 9359)
+++ trunk/Ohana/src/opihi/cmd.data/shift.c	(revision 9726)
@@ -7,5 +7,5 @@
   float *Vin, *Vot;
   double Dx, Dy, fdx, fdy;
-  Buffer *buf;
+  Buffer *in, *out;
 
   ROLL = FALSE;
@@ -15,13 +15,14 @@
   }
 
-  if (argc != 4) {
-    gprint (GP_ERR, "USAGE: shift <buffer> dx dy\n");
+  if (argc != 5) {
+    gprint (GP_ERR, "USAGE: shift (input) (output) dx dy\n");
     return (FALSE);
   }
 
-  if ((buf = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+  // define the input buffer and examine the shift
+  if ((in  = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
 
-  Dx = -atof (argv[2]);
-  Dy = -atof (argv[3]);
+  Dx = atof (argv[3]);
+  Dy = atof (argv[4]);
 
   dx = Dx;
@@ -29,66 +30,54 @@
   fdx = Dx - dx;
   fdy = Dy - dy;
-  if (fdx < -0.001) {dx -= 1; fdx += 1;}
-  if (fdy < -0.001) {dy -= 1; fdy += 1;}
+  if (fdx < -0.000001) {dx -= 1; fdx += 1;}
+  if (fdy < -0.000001) {dy -= 1; fdy += 1;}
+  // we always specify a positive fractional shift
+  // the above choice defines a minimum fractional shift of 1e-5
 
-  nx = buf[0].matrix.Naxis[0];
-  ny = buf[0].matrix.Naxis[1];
+  nx = in[0].matrix.Naxis[0];
+  ny = in[0].matrix.Naxis[1];
 
-  if ((dx > buf[0].matrix.Naxis[0]) || (dy > buf[0].matrix.Naxis[1])) {
+  if ((dx > nx) || (dy > ny)) {
     gprint (GP_ERR, "shifting data out of image\n");
     return (FALSE);
   }
   
-  if (dx < 0) {
-    DXin = 0;
-    DXot = -dx;
-  } else {
-    DXin = dx;
-    DXot = 0;
+  // define the output buffer
+  if ((out = SelectBuffer (argv[2], ANYBUFFER, TRUE)) == NULL) return (FALSE);
+
+  gfits_free_matrix (&out[0].matrix);
+  gfits_free_header (&out[0].header);
+  CreateBuffer (out, nx, ny, -32, 0.0, 1.0);
+
+  DXin = (dx < 0) ? -dx : 0;
+  DXot = (dx < 0) ?   0 : dx;
+  DYin = (dy < 0) ? -dy : 0;
+  DYot = (dy < 0) ?   0 : dy;
+  
+  for (j = 0; j < ny - abs(dy); j++) {
+    Vin = (float *)(in[0].matrix.buffer)  + (j + DYin)*nx + DXin;  
+    Vot = (float *)(out[0].matrix.buffer) + (j + DYot)*nx + DXot; 
+    for (i = 0; i < nx - abs(dx); i++, Vin++, Vot++) {
+      *Vot = *Vin;
+    }
+    // fill in the exposed x-border with 0.0
+    Vot = (dx > 0) ? 
+      (float *)(out[0].matrix.buffer) + (j + DYot)*nx : 
+      (float *)(out[0].matrix.buffer) + (j + DYot)*nx + nx - abs(dx);
+    for (i = 0; i < abs(dx); i++, Vot++) {
+      *Vot = 0.0;
+    }	
   }
-  if (dy < 0) {
-    DYin = 0;
-    DYot = -dy;
-  } else {
-    DYin = dy;
-    DYot = 0;
-  }
-  
-  if ((dy > 0) || ((dy == 0) && (dx > 0))) {
-    for (j = 0; j < ny - fabs(dy); j++) {
-      Vin = (float *)(buf[0].matrix.buffer) + (j + DYin)*nx + DXin;  
-      Vot = (float *)(buf[0].matrix.buffer) + (j + DYot)*nx + DXot; 
-      for (i = 0; i < nx - fabs(dx); i++, Vin++, Vot++) {
-	*Vot = *Vin;
-      }
-      if (dx < 0) 
-	Vot = (float *)(buf[0].matrix.buffer) + (j + DYot)*nx; 
-      for (i = 0; i < fabs(dx); i++, Vot++) {
-	*Vot = 0.0;
-      }	
-    }
-    Vot = (float *)(buf[0].matrix.buffer) + nx*(ny - abs(dy));
-    for (j = 0; j < nx * fabs(dy); j++, Vot++) {
-      *Vot = 0.0;
-    }   
-  } else {
-    for (j = ny - fabs(dy) - 1; j >= 0; j--) {
-      Vin = (float *)(buf[0].matrix.buffer) + (j + DYin)*nx + DXin + nx - abs(dx) - 1; 
-      Vot = (float *)(buf[0].matrix.buffer) + (j + DYot)*nx + DXot + nx - abs(dx) - 1; 
-      for (i = 0; i < nx - fabs(dx); i++, Vin--, Vot--) {
-	*Vot = *Vin;
-      }
-      if (dx < 0) 
-	Vot = (float *)(buf[0].matrix.buffer) + (j + DYot)*nx; 
-      for (i = 0; i < fabs(dx); i++, Vot++) {
-	*Vot = 0.0;
-      }	
-    }
-    Vot = (float *)(buf[0].matrix.buffer);
-    for (j = 0; j < nx * fabs(dy); j++, Vot++) {
-      *Vot = 0.0;
-    }
-  } 
 
+  // fill in the exposed y-border with 0.0
+  Vot = (dy > 0) ? 
+    (float *)(out[0].matrix.buffer) :
+    (float *)(out[0].matrix.buffer) + (ny - abs(dy))*nx;
+
+  for (j = 0; j < nx * abs(dy); j++, Vot++) {
+    *Vot = 0.0;
+  }   
+
+  // apply the fractional shift 
   gprint (GP_ERR, "%f %f\n", fdx, fdy);
   if ((fdx > 0) || (fdy > 0)) {
@@ -101,12 +90,12 @@
     f11 =    fdx *   fdy;
 
-    Vin = (float *)buf[0].matrix.buffer;
-    Vot = (float *)buf[0].matrix.buffer;
+    Vin = (float *)out[0].matrix.buffer;
+    Vot = (float *)out[0].matrix.buffer;
     for (j = 0; j < ny - 1; j++) {
       for (i = 0; i < nx - 1; i++, Vin++, Vot++) {
 	value  = Vin[   0] * f00;
 	value += Vin[   1] * f01;
-	value += Vin[ny  ] * f10;
-	value += Vin[ny+1] * f11;
+	value += Vin[nx  ] * f10;
+	value += Vin[nx+1] * f11;
 	*Vot = value;
       }
