Adding Algorithms to xv

With the addition of the Algorithms menu in the xv controls window, xv can now perform standard image-processing algorithms. However, I'm not really into the whole image-processing scene, so I've only implemented a few algorithms.

Please! Feel free to add your own algorithms, it's easy, and if you'd care to donate them, they may find their way into future official releases of xv, and eternal fame and glory will be yours, in the form of a credit in the "Hall of Fame" listing..

Adding an Algorithm

For the purposes of this example, I'll be adding a new algorithm called 'Noise' which will simply add (or subtract) a small random amount from each pixel in the image. I can't see that this would be a very useful algorithm (which is why it's not already in xv ), but then again, what do I know about such things...

Edit xv.h , and find the block that starts with:

#define ALG_NONE  0
#define ALG_SEP1  1  /* separator */
#define ALG_BLUR3 2

and add an additional definition at the end of the list (right before ALG_MAX ) for your algorithm. Don't forget to increment ALG_MAX to reflect the additional algorithm:

#define ALG_TINF  6
#define ALG_OIL   7
#define ALG_NOISE 8
#define ALG_MAX   9

Edit xvctrl.c , and find where the array algMList[] is initialized. Add a string for your new algorithm. The string's position in the list must match the number that you assigned to the ALG_* value in xv.h :

static char *algMList[] = { "Undo All",
                            MBSEP,
                            "Blur (3x3)",
                            "Blur (7x7)",
                            "Edge Detection",
                            "Emboss",
                            "Oil Painting",
                            "Add Noise"};

Edit xvalg.c , and find the DoAlg() function. This function is called with an ALG_* value whenever something is selected from the Algorithms menu. Add a case for the new ALG_NOISE value, and have it call your top-level function, with no parameters:

  case ALG_TINF:  EdgeDetect(1);  break;
  case ALG_OIL:   OilPaint();     break;
  case ALG_NOISE: Noise();        break;
}

Write your top-level function:

/************************/
static void Noise()
{
  byte *pic24, *tmpPic;

  /* turn on flapping fish cursor */
  WaitCursor();

  /* mention progress... */
  SetISTR(ISTR_INFO, "Running Noise algorithm...");

  /* generates a 24-bit version of  pic , if necessary.
     also generates a w*h*3 buffer (tmpPic) to hold intermediate results */
  if (start24bitAlg(&pic24, &tmpPic)) return;

  /* do the noise algorithm */
  doNoise(pic24, pWIDE,pHIGH, tmpPic);

  /* if we're in PIC8 mode, convert pic24 back to PIC8. free pic24 & tmppic */ 
  end24bitAlg(pic24, tmpPic);
} 

Now write the function that does the work of your algorithm. It will be passed a 24-bit RGB source image srcpic, its dimensions w,h, and a destination 24-bit image dstpic of the same size. If your algorithm is normally meant to be run on greyscale images (as so many image algorithms are), you should simply run it separately for each of the Red, Green, and Blue planes, and glue the results back together at the end of the algorithm.

/************************/
static void doNoise(srcpic, w, h, dstpic)
  byte *srcpic, *dstpic;
  int   w, h;
{
  byte *sp, *dp;
  int   x,y,newr,newg,newb;

  printUTime("start of doNoise");   /* print timing info */

  for (y=0; y<h; y++) {
    if ((y & 15) == 0) WaitCursor();

    sp = srcpic + y*w*3;     /* position sp,dp at start of line #y */
    dp = dstpic + y*w*3;
    for (x=0; x<w; x++) {
      newr = sp[0] + (random()&0x3f)-0x20;  /* add noise to red   component */
      newg = sp[1] + (random()&0x3f)-0x20;  /* add noise to green component */
      newb = sp[2] + (random()&0x3f)-0x20;  /* add noise to blue  component */
      RANGE(newr, 0, 255);   /* clip values to range[0..255] inclusive */
      RANGE(newg, 0, 255);   /* RANGE() is defined in xv.h */
      RANGE(newb, 0, 255);
      dp[0] = (byte) newr;   /* store new values in dstpic */
      dp[1] = (byte) newg;
      dp[2] = (byte) newb;

      sp += 3;  dp += 3;     /* advance to next 3-byte pixel in images */
    }
  }
  printUTime("end of doConvolv");
}

Note that this algorithm is written in about as non-optimal a way as possible, for the sake of clarity.

Also note that if you define TIMING_TEST at the beginning of xvalg.c , it will turn on code that will let you measure the CPU time your algorithm requires. Once you have a working algorithm, you may find this useful if you wish to try to optimize your algorithm for increased performance.

And that's all there is to it!