Index: /branches/eam_branches/ohana.20190329/src/libkapa/include/kapa.h
===================================================================
--- /branches/eam_branches/ohana.20190329/src/libkapa/include/kapa.h	(revision 40729)
+++ /branches/eam_branches/ohana.20190329/src/libkapa/include/kapa.h	(revision 40730)
@@ -172,6 +172,8 @@
   int Nx, Ny, Nbyte;
   bDrawColor **pixels;
+  char **mask;
   png_color *palette;
   int Npalette;
+
   // current drawing values:
   int bWeight;
@@ -256,4 +258,5 @@
 int KapaGetImageData (int fd, KapaImageData *graphmode);
 int KapaSetToolbox (int fd, int location);
+int KapaSetSmoothSigma (int fd, float sigma);
 
 /* KapaColors */
@@ -292,4 +295,5 @@
 bDrawBuffer *bDrawBufferCreate (int Nx, int Ny, int Nbyte, png_color *palette, int Npalette);
 void bDrawBufferFree (bDrawBuffer *buffer);
+int bDrawMerge (bDrawBuffer *base, bDrawBuffer *layer);
 void bDrawSetBuffer (bDrawBuffer *buffer);
 void bDrawSetColor (bDrawBuffer *buffer, bDrawColor color);
@@ -312,4 +316,6 @@
 void bDrawTriOpen  (bDrawBuffer *buffer, double x1, double y1, double x2, double y2, double x3, double y3);
 void bDrawTriFill  (bDrawBuffer *buffer, double x1, double y1, double dx, double dy);
+
+void bDrawSmooth (bDrawBuffer *buffer, float sigma);
 
 /* bDrawRotFont.c */
Index: /branches/eam_branches/ohana.20190329/src/libkapa/src/KapaWindow.c
===================================================================
--- /branches/eam_branches/ohana.20190329/src/libkapa/src/KapaWindow.c	(revision 40729)
+++ /branches/eam_branches/ohana.20190329/src/libkapa/src/KapaWindow.c	(revision 40730)
@@ -455,2 +455,9 @@
 }
 
+int KapaSetSmoothSigma (int fd, float sigma) {
+
+  KiiSendCommand (fd, 4, "SIGM");
+  KiiSendMessage (fd, "%4.1f ", sigma);
+  KiiWaitAnswer (fd, "DONE");
+  return (TRUE);
+}
Index: /branches/eam_branches/ohana.20190329/src/libkapa/src/bDrawFuncs.c
===================================================================
--- /branches/eam_branches/ohana.20190329/src/libkapa/src/bDrawFuncs.c	(revision 40729)
+++ /branches/eam_branches/ohana.20190329/src/libkapa/src/bDrawFuncs.c	(revision 40730)
@@ -1,14 +1,34 @@
 # include <kapa_internal.h>
 
-// move these to the bDrawBuffer type
-// static int bWeight;
-// static int bType;
-// static bDrawColor bColor;
-// static bDrawColor bColor_R;
-// static bDrawColor bColor_G;
-// static bDrawColor bColor_B;
-// static bDrawBuffer *bBuffer;
+// buffer->pixels carries the plot image
+// buffer->mask is 0 if the pixel is untouched, 1 if the pixel has data
 
 void bDrawCircleSingle (bDrawBuffer *buffer, double xc, double yc, double radius);
+
+int bDrawMerge (bDrawBuffer *base, bDrawBuffer *layer) {
+
+  // the two bDrawBuffers must match in size and depth
+
+  if (base->Nx != layer->Nx) return FALSE;
+  if (base->Ny != layer->Ny) return FALSE;
+  if (base->Nbyte != layer->Nbyte) return FALSE;
+
+  for (int j = 0; j < base->Ny; j++) {
+    for (int i = 0; i < base->Nx; i++) {
+
+      if (!layer->mask[j][i]) continue;
+      
+      // completely opaque top layer:
+      if (base->Nbyte == 1) {
+	base->pixels[j][i] = layer->pixels[j][i];
+      } else {
+	base->pixels[j][3*i+0] = layer->pixels[j][3*i+0];
+	base->pixels[j][3*i+1] = layer->pixels[j][3*i+1];
+	base->pixels[j][3*i+2] = layer->pixels[j][3*i+2];
+      }
+    }
+  }
+  return TRUE;
+}
 
 // create a drawing buffer with either 1 or 3 byte colors
@@ -35,6 +55,8 @@
 
   ALLOCATE (buffer[0].pixels, bDrawColor *, Ny);
+  ALLOCATE (buffer[0].mask, char *, Ny);
   for (i = 0; i < Ny; i++) {
     ALLOCATE (buffer[0].pixels[i], bDrawColor, Nbyte*Nx);
+    ALLOCATE (buffer[0].mask[i], char, Nx);
     for (j = 0; j < Nx; j++) {
       if (Nbyte == 1) {
@@ -45,4 +67,5 @@
 	buffer[0].pixels[i][3*j+2] = white_B;
       }
+      buffer[0].mask[i][j] = 0; // for now: 0 = no data, 1 = data
     }
   }
@@ -63,7 +86,9 @@
   for (i = 0; i < buffer[0].Ny; i++) {
     free (buffer[0].pixels[i]);
+    free (buffer[0].mask[i]);
   }
   free (buffer[0].pixels);
-  free (buffer[0].palette);
+  free (buffer[0].mask);
+  // free (buffer[0].palette);
   free (buffer);
   return;
@@ -121,11 +146,9 @@
     buffer[0].pixels[y][x] = buffer->bColor;
   } else {
-    // buffer[0].pixels[y][3*x+0] = buffer->bColor_R;
-    // buffer[0].pixels[y][3*x+1] = buffer->bColor_G;
-    // buffer[0].pixels[y][3*x+2] = buffer->bColor_B;
-    buffer[0].pixels[y][3*x+0] = MAX (0x00, MIN(0xff, beta * buffer[0].pixels[y][3*x+0] + alpha * buffer->bColor_R));
+    buffer[0].pixels[y][3*x+0] = MAX (0x00, MIN(0xff, beta * buffer[0].pixels[y][3*x+0] + alpha * buffer->bColor_R)); // was just buffer->bColor_R, etc
     buffer[0].pixels[y][3*x+1] = MAX (0x00, MIN(0xff, beta * buffer[0].pixels[y][3*x+1] + alpha * buffer->bColor_G));
     buffer[0].pixels[y][3*x+2] = MAX (0x00, MIN(0xff, beta * buffer[0].pixels[y][3*x+2] + alpha * buffer->bColor_B));
   }
+  buffer[0].mask[y][x] = 1;
   return;
 }
@@ -299,8 +322,6 @@
       buffer[0].pixels[Y][3*i+1] = MAX (0x00, MIN(0xff, beta * buffer[0].pixels[Y][3*i+1] + alpha * buffer->bColor_G));
       buffer[0].pixels[Y][3*i+2] = MAX (0x00, MIN(0xff, beta * buffer[0].pixels[Y][3*i+2] + alpha * buffer->bColor_B));
-      // buffer[0].pixels[Y][3*i+0] = buffer->bColor_R;
-      // buffer[0].pixels[Y][3*i+1] = buffer->bColor_G;
-      // buffer[0].pixels[Y][3*i+2] = buffer->bColor_B;
-    }
+    }
+    buffer[0].mask[Y][i] = 1;
   }
   return;
@@ -326,8 +347,6 @@
       buffer[0].pixels[i][3*X+1] = MAX (0x00, MIN(0xff, beta * buffer[0].pixels[i][3*X+1] + alpha * buffer->bColor_G));
       buffer[0].pixels[i][3*X+2] = MAX (0x00, MIN(0xff, beta * buffer[0].pixels[i][3*X+2] + alpha * buffer->bColor_B));
-      // buffer[0].pixels[i][3*X+0] = buffer->bColor_R;
-      // buffer[0].pixels[i][3*X+1] = buffer->bColor_G;
-      // buffer[0].pixels[i][3*X+2] = buffer->bColor_B;
-    }
+    }
+    buffer[0].mask[i][X] = 1;
   }
   return;
@@ -648,5 +667,110 @@
 }
 
+// run a smoothing kernel on each channel for anti-aliasing
+// XXX I need to consider the mask as well
+void bDrawSmooth (bDrawBuffer *buffer, float sigma) {
+
+  // generate a temp buffer for storage of the smoothed result
+  int Nx = buffer[0].Nx;
+  int Ny = buffer[0].Ny;
+  ALLOCATE_PTR (temp, bDrawColor, Nx*Ny);
+
+  //* build a 1D gaussian 
+  int Nsigma = 2;
+  int Ns = (int) (Nsigma*sigma + 0.5);
+  int Ngauss = 2*Ns + 1;
+  ALLOCATE_PTR (gaussnorm, float, Ngauss);
+  float *gauss = &gaussnorm[Ns];
+  for (int i = -Ns; i < Ns + 1; i++) {
+    gauss[i] = exp ((i*i)/(-2*sigma*sigma));
+  }
+
+  // NOTE XXX: need to handle Nbyte = 1 or 3
+  int Nchannel = 3;
+
+  // we need to smooth the 3 channels independently, do this as three passes
+  for (int ch = 0; ch < Nchannel; ch++) {
+
+    // smooth in X direction 
+    for (int j = 0; j < Ny; j++) {
+      bDrawColor *vi = (bDrawColor *) &buffer[0].pixels[j][ch];
+      bDrawColor *vo = &temp[j*Nx];
+      for (int i = 0; i < Nx; i++, vo++, vi += Nchannel) {
+	float g = 0.0, s = 0.0;
+	for (int n = -Ns; n <= Ns; n++) {
+	  if (i+n < 0) continue;
+	  if (i+n >= Nx) continue;
+	  s += gauss[n]*vi[Nchannel*n];
+	  g += gauss[n];
+	}
+	*vo = s / g;
+      }
+    }
+
+    // NOTE: output maps back into original image
+    // careful on the counting
+
+    /* smooth in Y direction */
+    for (int i = 0; i < Nx; i++) {
+      bDrawColor *vi = &temp[i];
+      for (int j = 0; j < Ny; j++) {
+	float g = 0.0, s = 0.0;
+	for (int n = -Ns; n <= Ns; n++) {
+	  if (j+n < 0) continue;
+	  if (j+n >= Ny) continue;
+	  s += gauss[n]*vi[(n+j)*Nx]; // vi is a single-index array, so this is stepping by rows
+	  g += gauss[n];
+	}
+	buffer[0].pixels[j][Nchannel*i + ch] = s / g;
+      }
+    }
+  }
+
+  // smooth the mask
+  ALLOCATE_PTR (temp_mask, float, Nx*Ny);
+
+  // smooth in X direction 
+  for (int j = 0; j < Ny; j++) {
+    char *vi = (char *) &buffer[0].mask[j][0];
+    float *vo = &temp_mask[j*Nx];
+    for (int i = 0; i < Nx; i++, vo++, vi++) {
+  	float g = 0.0, s = 0.0;
+  	for (int n = -Ns; n <= Ns; n++) {
+  	  if (i+n < 0) continue;
+  	  if (i+n >= Nx) continue;
+  	  s += gauss[n]*vi[n];
+  	  g += gauss[n];
+  	}
+  	*vo = s / g;
+    }
+  }
+
+  // NOTE: output maps back into original image
+  // careful on the counting
+
+  /* smooth in Y direction */
+  for (int i = 0; i < Nx; i++) {
+    float *vi = &temp_mask[i];
+    for (int j = 0; j < Ny; j++) {
+  	float g = 0.0, s = 0.0;
+  	for (int n = -Ns; n <= Ns; n++) {
+  	  if (j+n < 0) continue;
+  	  if (j+n >= Ny) continue;
+  	  s += gauss[n]*vi[(n+j)*Nx]; // vi is a single-index array, so this is stepping by rows
+  	  g += gauss[n];
+  	}
+  	buffer[0].mask[j][i] = (s / g > 0.05) ? 1 : 0;
+    }
+  }
+
+  free (temp);
+  free (temp_mask);
+  free (gaussnorm);
+
+  return;
+}
+
 /* 
+NOTES on Bresenham-style circles:
 the discriminant of inside or outside the circle is:
 
