Logo Search packages:      
Sourcecode: faumachine version File versions  Download package

patternm_gfx.c

/* $Id: patternm_gfx.c,v 1.39 2009-01-27 16:27:03 potyra Exp $ 
 *
 * graphic pattern matcher subcomponente (patternm_gfx), to be included
 * in the patternm component.
 *
 * Copyright (C) 2007-2009 FAUmachine Team <info@faumachine.org>.
 * This program is free software. You can redistribute it and/or modify it
 * under the terms of the GNU General Public License, either version 2 of
 * the License, or (at your option) any later version. See COPYING.
 */

/* ********************* exported definitions ************************** */

#ifdef DEFINITIONS

/* maximum number of matched elements for a pattern check */
#define MAX_MATCHES     100

/* should match sig_match.h */
#define MAX_FOLDS       16

/** one pixel of a pattern to check for (all pixels of a pattern get
 *  randomized so that if an image has a border, a negative check can
 *  be determined faster).
 */
typedef struct {
      struct point coord;     /**< coordinate of the pixel */
      uint32_t color;         /**< ARGB value of pattern pixel */
} PatternPix;

typedef struct {
      /** fold in use (i.e. pattern loaded and getting used for a check) */
      bool in_use;
      /** was there a match? */
      bool matched;
      /** dimensions of the pattern */
      struct area pattern_dimensions;
      /** randomized pattern pixels */
      PatternPix *pattern;
      /** search area, in case matching shouldn't be done fullscreen */
      struct rectangle search;
      /** match the whole screen? */
      bool whole_screen;
      /** matched coordinates. */
      struct point matches[MAX_MATCHES];
      /** number of matches. */
      unsigned short num_matches;
      /** link back to instance of whole pattern matcher */
      void* _cpssp;
} PatternCheck_FOLD;

#endif /* DEFINITIONS */

/* ***************************** STATE ********************************* */

#ifdef STATE

struct {
      /* Config */

      /* Signals */

      /** all match slots. */
      const struct sig_match *port_slot[MAX_FOLDS];

      /* State */

      /** available fold slots */
      PatternCheck_FOLD fold[MAX_FOLDS];
      /** need to do a check? */
      bool check_necessary;
} NAME;

#endif /* STATE */


/* ************************** BEHAVIOR ********************************* */

#ifdef BEHAVIOR

/* debugging macro */

#define DEBUG_ENABLE    0

#if DEBUG_ENABLE
#define DEBUG(fmt, arg...) \
faum_log(FAUM_LOG_DEBUG, "patternmatcher", "gfx", "%35s:% 4d: " fmt , \
      __FUNCTION__, __LINE__, arg);
#else
#define DEBUG(fmt, arg...)
#endif

#define MATCH_BITS      3     /* number of bits to ignore when matching */

/***********************************************************************/

static bool
NAME_(match_pixel)(uint32_t pixel0, uint32_t pixel1)
{
#if 1
      unsigned int b0;
      unsigned int b1;

      b0 = (pixel0 >> 16) & 0xff; b0 >>= (8 - MATCH_BITS);
      b1 = (pixel1 >> 16) & 0xff; b1 >>= (8 - MATCH_BITS);
      if (b0 != b1 - 1 && b0 != b1 && b0 != b1 + 1) {
            return false;
      }

      b0 = (pixel0 >>  8) & 0xff; b0 >>= (8 - MATCH_BITS);
      b1 = (pixel1 >>  8) & 0xff; b1 >>= (8 - MATCH_BITS);
      if (b0 != b1 - 1 && b0 != b1 && b0 != b1 + 1) {
            return false;
      }

      b0 = (pixel0 >>  0) & 0xff; b0 >>= (8 - MATCH_BITS);
      b1 = (pixel1 >>  0) & 0xff; b1 >>= (8 - MATCH_BITS);
      if (b0 != b1 - 1 && b0 != b1 && b0 != b1 + 1) {
            return false;
      }
      return true;
#else
      return pixel0 == pixel1;
#endif
}

/** check if the pattern can be matched at pos. pos must be inside the 
 *  current screen, so that every pixel of the pattern is located inside the 
 *  screen.
 *  @param css pointer to config struct
 *  @param pos position to look for pattern
 *  @param pattern flattened pattern.
 *  @return true if matched, false otherwise.
 */
static bool
NAME_(check_pattern_at)(
      const struct cpssp *cpssp,
      struct point pos,
      const PatternPix *pattern
)
{
      int i;
      uint32_t pat_col;
      uint32_t pic_col;
      struct point picpos;

      for (i = 0; ; i++) {
            if (pattern[i].coord.x == 0xffff
             && pattern[i].coord.y == 0xffff) {
                  /* reached eof pattern */
                  /* Pattern matched. */
                  return true;
            }

            pat_col = pattern[i].color;

            picpos.x = pos.x + pattern[i].coord.x;
            picpos.y = pos.y + pattern[i].coord.y;

            pic_col = cpssp->current_screen[picpos.y][picpos.x];

            if (! NAME_(match_pixel)(pic_col, pat_col)) {
                  /* No match, cancel this check. */
                  return false;
            }
      }

}

/** search if pattern is in current_screen inside the area search.
 *  @param css current instance.
 *  @param search area of interest.
 *  @param pattern pattern to look for.
 *  @param psize dimensions of pattern.
 *  @param match_map will store matches here.
 *  @param match_map_size size counter of match_map.
 *  @return true if pattern is found at least once, false otherwise.
 */
static bool
NAME_(search_for_pattern)(
      const struct cpssp *cpssp,
      const struct rectangle search,
      const PatternPix *pattern,
      const struct area psize,
      struct point *match_map,
      unsigned short *match_map_size)
{
      struct point cp;
      bool match;

      assert(pattern != NULL);
      assert(match_map != NULL);
      assert(match_map_size != NULL);

      /* iterate over search area minus the bounds of the pattern */
      for (cp.y = search.origin.y; 
          cp.y < search.origin.y + search.dimensions.h - psize.h + 1;
          cp.y++) {
            for (cp.x = search.origin.x; 
                cp.x < search.origin.x + search.dimensions.w - psize.w + 1;
                cp.x++) {
                  assert(point_inside(cp, search));

                  match = NAME_(check_pattern_at)(cpssp, cp, pattern);
                  if (match) {
                        assert(*match_map_size < MAX_MATCHES);

                        match_map[*match_map_size] = cp;
                        (*match_map_size)++;

#if 0 /* currently, nobody can make use of more than one match/check */
                        if (*match_map_size == MAX_MATCHES) {
                              goto patternm_search_out;
                        }
#else
                        goto patternm_search_out;
#endif /* look for more than one match */
                  }
            }
      }

patternm_search_out:
      if (*match_map_size == 0) {
            return false;
      } else {
            return true;
      }
}


/** run a check with fold check for the current screen.
 *  @param css current instance
 *  @param check fold to check for.
 */
static void
NAME_(fold_check)(struct cpssp *cpssp, PatternCheck_FOLD *check)
{
      struct rectangle screen_area = {
            .origin = { .x = 0, .y = 0 },
            .dimensions = cpssp->screen_dimensions
      };

      assert(check != NULL);
      assert(check->in_use);
      /* unknown semantics for an empty pattern, should it yield false or 
       * true all the time? -> abort */
      assert(0 < check->pattern_dimensions.h);
      assert(0 < check->pattern_dimensions.w);

      check->num_matches = 0;

      if (check->whole_screen == true) {
            check->search = screen_area;
      } else {
            /* search area fits into screen area? */
            if (! rect_fits_into(check->search, screen_area)) {
                  check->matched = false;
                  return;
            }
      }

      /* check if pattern fits into search area at all */
      if (! area_smaller_or_same(check->pattern_dimensions, 
                           check->search.dimensions)) {
            check->matched = false;
            return;
      }

      check->matched = NAME_(search_for_pattern)(
            cpssp,
            check->search,
            check->pattern,
            check->pattern_dimensions,
            check->matches,
            &check->num_matches);
}

/** run all registered checks.
 *  @param css current instance.
 */
static void
NAME_(run_check)(struct cpssp *cpssp)
{
      int i;
      PatternCheck_FOLD *check;
      struct point old_match = { .x = 0, .y = 0 };
      bool old_matched;

      for (i = 0; i < MAX_FOLDS; i++) {
            check = &cpssp->NAME.fold[i];

            if (! check->in_use) {
                  continue;
            }

            old_matched = check->matched;
            if (old_matched) {
                  assert(0 < check->num_matches);
                  old_match = check->matches[0];
            }

            NAME_(fold_check)(cpssp, check);

            if (old_matched
             && ! check->matched) {
                  /*
                   * Last match became invisible.
                   */
                  sig_match_invisible(cpssp->NAME.port_slot[i], check);

            } else if (old_matched
                  && check->matched) {
                  /*
                   * New match at different position than old one.
                   */
                  assert(0 < check->num_matches);

                  if (old_match.x != check->matches[0].x
                   || old_match.y != check->matches[0].y) {
                        sig_match_event(cpssp->NAME.port_slot[i],
                                    check,
                                    check->matches[0].x, 
                                    check->matches[0].y,
                                    check->pattern_dimensions.w,
                                    check->pattern_dimensions.h);
                  }

            } else if (check->matched) {
                  /*
                   * New match event.
                   */
                  assert(! old_matched);
                  assert(0 < check->num_matches);

                  sig_match_event(cpssp->NAME.port_slot[i],
                              check,
                              check->matches[0].x,
                              check->matches[0].y, 
                              check->pattern_dimensions.w,
                              check->pattern_dimensions.h);
            }
      }
}

static void
NAME_(build_1d_array)(
      const uint32_t *picture,
      unsigned int picture_w,
      unsigned int picture_h,
      PatternPix *array /* num elems is picture_w*picture_h */
)
{
      int x, y, i;
      unsigned int size = 0;
      uint32_t pixel;

      assert(picture);
      assert(array);

      for (y = 0; y < picture_h; y++) {
            for (x = 0; x < picture_w; x++) {
                  pixel = picture[y * picture_w + x];

                  if (GET_ALPHA(pixel) == ALPHA_TRANSPARENT) {
                        continue;
                  }

                  array[size].coord.x = x;
                  array[size].coord.y = y;
                  array[size].color = pixel;
                  size++;
            }
      }

      /* mark the end of the array */
      array[size].coord.x = 0xffff;
      array[size].coord.y = 0xffff;
      array[size].color = 0; /* Doesn't matter... */

      for (i=0; i < size; i++) {
            int rand1;
            int rand2;
            PatternPix swap;

            rand1 = random() % size;
            rand2 = random() % size;

            swap.coord.x = array[rand1].coord.x;
            swap.coord.y = array[rand1].coord.y;
            swap.color = array[rand1].color;

            array[rand1].coord.x = array[rand2].coord.x;
            array[rand1].coord.y = array[rand2].coord.y;
            array[rand1].color = array[rand2].color;

            array[rand2].coord.x = swap.coord.x;
            array[rand2].coord.y = swap.coord.y;
            array[rand2].color = swap.color;
      }
}

static void
NAME_(remove_check)(PatternCheck_FOLD *check)
{
      struct cpssp *cpssp = (struct cpssp*)check->_cpssp;

      assert(check);

      if (check->in_use) {
            free(check->pattern);
            check->pattern = NULL;
      } else {
            /* FIXME what to do here? */
            DEBUG("%s\n", "removing unused pattern");
      }

      check->in_use = false;
      cpssp->NAME.check_necessary = true;
}

static void
NAME_(image_set)(
      void *s, 
      const char *str, 
      bool fullscreen, 
      const struct rectangle *limit
)
{
      PatternCheck_FOLD *fold = (PatternCheck_FOLD*) s;
      struct cpssp *cpssp = (struct cpssp*) fold->_cpssp;
      bool ret;
      uint32_t *data = NULL;
      int w, h;

      if (fold->in_use) {
            /* remove old check */
            NAME_(remove_check)(fold);
      }

      /* load new check */
      ret = png_read(&data, &w, &h, str);
      if (! ret) {
            ret = ppm_read(&data, &w, &h, str);
      }

      if (! ret) {
            faum_log(FAUM_LOG_WARNING, "pattern-matcher", "",
                  "couldn load image %s\n", str);
            fold->in_use = false;
            return;
      }

      fold->pattern = malloc((w * h + 1) * sizeof(PatternPix));
      assert(fold->pattern);

      NAME_(build_1d_array)(data, w, h, fold->pattern);

      /* initialize members */
      fold->in_use = true;
      fold->matched = false;
      fold->whole_screen = fullscreen;
      fold->num_matches = 0;
      fold->pattern_dimensions.w = w;
      fold->pattern_dimensions.h = h;

      if (! fullscreen) {
            assert(limit != NULL);
            fold->search = *limit;
      }

      free(data);
      cpssp->NAME.check_necessary = true;
}


/* ------------------------- callbacks --------------------------- */

static void
NAME_(add_match_partial)(
      void *s,
      const char *image,
      uint16_t x,
      uint16_t y,
      uint16_t w,
      uint16_t h
)
{
      struct rectangle search = {
            .origin.x = x,
            .origin.y = y,
            .dimensions.w = w,
            .dimensions.h = h
      };

      patternm_gfx_image_set(s, image, false, &search);
}

static void
NAME_(add_match)(void *s, const char *image)
{
      NAME_(image_set)(s, image, true, NULL);
}

static void
NAME_(remove_match)(void *s)
{
      PatternCheck_FOLD *fold = (PatternCheck_FOLD*) s;

      if (fold->in_use) {
            NAME_(remove_check)(fold);
      }
}



/* ---------------------- interface of subcomponent ----------------------- */

static void
NAME_(sync)(struct cpssp *cpssp)
{
      if (cpssp->NAME.check_necessary) {
            cpssp->NAME.check_necessary = false;
            NAME_(run_check)(cpssp);
      }

}

static void
NAME_(buffer_event)(struct cpssp *cpssp)
{
      cpssp->NAME.check_necessary = true;
}

#define SETUP_SLOT(nr) \
      assert(port_slot##nr); \
      assert(nr < MAX_FOLDS); \
      cpssp->NAME.port_slot[nr] = port_slot##nr; \
      sig_match_connect(port_slot##nr, &cpssp->NAME.fold[nr], &m_f);

static void
NAME_(init)(
      struct sig_match *port_slot0,
      struct sig_match *port_slot1,
      struct sig_match *port_slot2,
      struct sig_match *port_slot3,
      struct sig_match *port_slot4,
      struct sig_match *port_slot5,
      struct sig_match *port_slot6,
      struct sig_match *port_slot7,
      struct sig_match *port_slot8,
      struct sig_match *port_slot9,
      struct sig_match *port_slot10,
      struct sig_match *port_slot11,
      struct sig_match *port_slot12,
      struct sig_match *port_slot13,
      struct sig_match *port_slot14,
      struct sig_match *port_slot15,
      struct cpssp* cpssp
)
{
      static const struct sig_match_funcs m_f = {
            .event = NULL,
            .add_match = NAME_(add_match),
            .add_match_partial = NAME_(add_match_partial),
            .remove_match = NAME_(remove_match)
      };
      int i;

      cpssp->NAME.check_necessary = true;
      
      /* clear all folds members */
      memset(cpssp->NAME.fold, 0, sizeof(cpssp->NAME.fold));

      /* set the pointer to the complete config of each fold entry. */
      for (i = 0; i < MAX_FOLDS; i++) {
            cpssp->NAME.fold[i]._cpssp = cpssp;
      }

      SETUP_SLOT(0);
      SETUP_SLOT(1);
      SETUP_SLOT(2);
      SETUP_SLOT(3);
      SETUP_SLOT(4);
      SETUP_SLOT(5);
      SETUP_SLOT(6);
      SETUP_SLOT(7);
      SETUP_SLOT(8);
      SETUP_SLOT(9);
      SETUP_SLOT(10);
      SETUP_SLOT(11);
      SETUP_SLOT(12);
      SETUP_SLOT(13);
      SETUP_SLOT(14);
      SETUP_SLOT(15);
}

#undef SETUP_SLOT

#undef DEBUG_ENABLE
#undef MATCH_BITS

#endif /* BEHAVIOR */

Generated by  Doxygen 1.6.0   Back to index