The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
/* $Id$ */

#include "IPAsupp.h"
#include "Point.h"
#include "PointSupp.h"

#undef METHOD
#define METHOD "IPA::Point::mask"

static PImage constant( int w, int h, int type, I32 vv) {
   PImage r = createNamedImage( w, h, type, "(temporary)");
   U8 *line;
   int y;
   if (!r) croak( "%s: error creating temporary image", METHOD);
   line = r-> data;
   switch ( type) {
      case imByte: memset( line, (U8)vv, w); break;
      case imShort: {
         I16 v = (I16)vv;
         I16 *l = (I16*)line;
         while (w--) *l++ = v;
      }
      break;
      case imLong: {
         I32 *l = (I32*)line;
         while (w--) *l++ = vv;
      }
      break;
   }
   for ( y = 1; y < h; y++) memcpy( r-> data + r-> lineSize*y, line, r-> lineSize);
   return r;
}

PImage IPA__Point_mask( PImage mask, HV *profile) {
   dPROFILE;
   PImage ifMatch = nil;
   PImage ifNoMatch = nil;
   PImage itest = nil;
   PImage o;
   I32 test = 0;
   I32 match = 0;
   I32 nomatch = 255;
   U8 *src, *dst;
   U8 *nnn, *mmm, *ttt;
   int y, sz, w, h;
   Bool freeMatch = false;
   Bool freeNoMatch = false;
   Bool freeTest = false;

  if ( !mask || !kind_of(( Handle) mask, CImage))
       croak("%s: not an image passed", METHOD);

   switch ( mask-> type) {
      case imByte:      sz = sizeof(U8);  break;
      case imShort:     sz = sizeof(I16); break;
      case imLong:      sz = sizeof(I32); break;
      default:
         croak( "%s: mask image should have integer grayscale type", METHOD);
   }

   if ( profile) {
      SV *dummy;
      char *key;
      I32 l;
      hv_iterinit( profile);
      for (;;) {
         dummy = hv_iternextsv( profile, &key, &l);
         if ( key == nil || dummy == nil) break;
         if ( strcmp( key, "test") != 0 &&
              strcmp( key, "match") != 0 &&
              strcmp( key, "mismatch") != 0 &&
              strncmp( key, "__", 2) != 0)
            croak( "%s: illegal option ``%s'' passed", METHOD, key);
      }
   }

   if ( profile && pexist(test)) {
      SV *sv = pget_sv(test);
      if (sv && SvROK(sv))  itest = (PImage)pget_H(test);
      else test = pget_i(test);
   }
   if ( profile && pexist(match)) {
      SV *sv = pget_sv(match);
      if (sv && SvROK(sv))  ifMatch = (PImage)pget_H(match);
      else match = pget_i(match);
   }
   if ( profile && pexist(mismatch)) {
      SV *sv = pget_sv(mismatch);
      if (sv && SvROK(sv))  ifNoMatch = (PImage)pget_H(mismatch);
      else nomatch = pget_i(mismatch);
   }
   if (ifMatch &&
       (!kind_of((Handle)ifMatch,CImage) ||
        ifMatch-> w != mask-> w || ifMatch-> h != mask-> h ||
        ifMatch-> type != mask-> type))
      croak( "%s: illegal ``ifMatch'' image passed", METHOD);
   if (itest &&
       (!kind_of((Handle)itest,CImage) ||
        itest-> w != mask-> w || itest-> h != mask-> h ||
        itest-> type != mask-> type))
      croak( "%s: illegal ``test'' image passed", METHOD);
   if (ifNoMatch &&
       (!kind_of((Handle)ifNoMatch,CImage) ||
        ifNoMatch-> w != mask-> w || ifNoMatch-> h != mask-> h ||
        ifNoMatch-> type != mask-> type))
      croak( "%s: illegal ``ifNoMatch'' image passed", METHOD);

   o = createNamedImage( mask-> w, mask-> h, mask-> type, METHOD);
   if (!o) croak( "%s: error creating output image", METHOD);

   w = mask-> w;
   h = mask-> h;
   if ( !itest) {
      freeTest = true;
      itest = constant( w, h, mask-> type, test);
   }
   if ( !ifMatch) {
      freeMatch = true;
      ifMatch = constant( w, h, mask-> type, match);
   }
   if ( !ifNoMatch) {
      freeNoMatch = true;
      ifNoMatch = constant( w, h, mask-> type, nomatch);
   }

   src = mask-> data;
   dst = o-> data;
#define DOMASKING(TYP)                                        \
   {                                                          \
      TYP *m, *n, *t, *s, *d, *stop;                          \
      mmm = ifMatch-> data;                                   \
      nnn = ifNoMatch-> data;                                 \
      ttt = itest-> data;                                     \
      for ( y = 0; y < mask-> h; y++) {                       \
         s = (TYP*)src;  d = (TYP*)dst;  stop = s + w;        \
         m = (TYP*)mmm;  n = (TYP*)nnn;  t = (TYP*)ttt;       \
         while ( s != stop) {                                 \
            *d = *s == *t ? *m : *n;                          \
            m++; n++; s++; t++; d++;                          \
         }                                                    \
         src += mask-> lineSize;                              \
         dst += o-> lineSize;                                 \
         mmm += ifMatch-> lineSize;                           \
         nnn += ifNoMatch-> lineSize;                         \
         ttt += itest-> lineSize;                             \
      }                                                       \
   }
   switch ( mask-> type) {
      case imByte:  DOMASKING(U8);  break;
      case imShort: DOMASKING(I16); break;
      case imLong:  DOMASKING(I32); break;
   }
#undef DOMASKING

   if ( freeTest) destroyImage( itest);
   if ( freeMatch) destroyImage( ifMatch);
   if ( freeNoMatch) destroyImage( ifNoMatch);

   return o;
}