예제 #1
0
/* check_flood_line:
 *  Checks a line segment, using the scratch buffer is to store a list of
 *  segments which have already been drawn in order to minimise the required
 *  number of tests.
 */
static int check_flood_line(Image* image, int y, int left, int right,
  const gfx::Rect& bounds,
  int src_color, int tolerance, void *data, AlgoHLine proc)
{
  int c;
  FLOODED_LINE *p;
  int ret = false;

  while (left <= right) {
    c = y;

    for (;;) {
      p = FLOOD_LINE(c);

      if ((left >= p->lpos) && (left <= p->rpos)) {
        left = p->rpos+2;
        break;
      }

      c = p->next;

      if (!c) {
        left = flooder(image, left, y, bounds, src_color, tolerance, data, proc);
        ret = true;
        break;
      }
    }
  }

  return ret;
}
예제 #2
0
/* floodfill:
 *  Fills an enclosed area (starting at point x, y) with the specified color.
 */
void algo_floodfill(Image* image, int x, int y,
  const gfx::Rect& bounds,
  int tolerance, bool contiguous,
  void* data, AlgoHLine proc)
{
  // Make sure we have a valid starting point
  if ((x < 0) || (x >= image->width()) ||
      (y < 0) || (y >= image->height()))
    return;

  // What color to replace?
  color_t src_color = get_pixel(image, x, y);

  // Non-contiguous case, we replace colors in the whole image.
  if (!contiguous) {
    switch (image->pixelFormat()) {
      case IMAGE_RGB:
        replace_color<RgbTraits>(image, bounds, src_color, tolerance, data, proc);
        break;
      case IMAGE_GRAYSCALE:
        replace_color<GrayscaleTraits>(image, bounds, src_color, tolerance, data, proc);
        break;
      case IMAGE_INDEXED:
        replace_color<IndexedTraits>(image, bounds, src_color, tolerance, data, proc);
        break;
    }
    return;
  }

  /* set up the list of flooded segments */
  flood_buf.resize(image->height());
  flood_count = image->height();
  FLOODED_LINE* p = (FLOODED_LINE*)&flood_buf[0];
  for (int c=0; c<flood_count; c++) {
    p[c].flags = 0;
    p[c].lpos = SHRT_MAX;
    p[c].rpos = SHRT_MIN;
    p[c].y = y;
    p[c].next = 0;
  }

  // Start up the flood algorithm
  flooder(image, x, y, bounds, src_color, tolerance, data, proc);

  // Continue as long as there are some segments still to test
  bool done;
  do {
    done = true;

    // For each line on the screen
    for (int c=0; c<flood_count; c++) {

      p = FLOOD_LINE(c);

      // Check below the segment?
      if (p->flags & FLOOD_TODO_BELOW) {
        p->flags &= ~FLOOD_TODO_BELOW;
        if (check_flood_line(image, p->y+1, p->lpos, p->rpos, bounds,
            src_color, tolerance, data, proc)) {
          done = false;
          p = FLOOD_LINE(c);
        }
      }

      // Check above the segment?
      if (p->flags & FLOOD_TODO_ABOVE) {
        p->flags &= ~FLOOD_TODO_ABOVE;
        if (check_flood_line(image, p->y-1, p->lpos, p->rpos, bounds,
                             src_color, tolerance, data, proc)) {
          done = false;
          // Special case shortcut for going backwards
          if ((c > bounds.y) && (c < bounds.y2()))
            c -= 2;
        }
      }
    }
  } while (!done);
}
예제 #3
0
/* flooder:
 *  Fills a horizontal line around the specified position, and adds it
 *  to the list of drawn segments. Returns the first x coordinate after
 *  the part of the line which it has dealt with.
 */
static int flooder(Image *image, int x, int y,
  const gfx::Rect& bounds,
  color_t src_color, int tolerance, void *data, AlgoHLine proc)
{
  FLOODED_LINE *p;
  int left = 0, right = 0;
  int c;

  switch (image->pixelFormat()) {

    case IMAGE_RGB:
      {
        uint32_t* address = reinterpret_cast<uint32_t*>(image->getPixelAddress(0, y));

        // Check start pixel
        if (!color_equal_32((int)*(address+x), src_color, tolerance))
          return x+1;

        // Work left from starting point
        for (left=x-1; left>=bounds.x; left--) {
          if (!color_equal_32((int)*(address+left), src_color, tolerance))
            break;
        }

        // Work right from starting point
        for (right=x+1; right<bounds.x2(); right++) {
          if (!color_equal_32((int)*(address+right), src_color, tolerance))
            break;
        }
      }
      break;

    case IMAGE_GRAYSCALE:
      {
        uint16_t* address = reinterpret_cast<uint16_t*>(image->getPixelAddress(0, y));

        // Check start pixel
        if (!color_equal_16((int)*(address+x), src_color, tolerance))
          return x+1;

        // Work left from starting point
        for (left=x-1; left>=bounds.x; left--) {
          if (!color_equal_16((int)*(address+left), src_color, tolerance))
            break;
        }

        // Work right from starting point
        for (right=x+1; right<bounds.x2(); right++) {
          if (!color_equal_16((int)*(address+right), src_color, tolerance))
            break;
        }
      }
      break;

    case IMAGE_INDEXED:
      {
        uint8_t* address = image->getPixelAddress(0, y);

        // Check start pixel
        if (!color_equal_8((int)*(address+x), src_color, tolerance))
          return x+1;

        // Work left from starting point
        for (left=x-1; left>=bounds.x; left--) {
          if (!color_equal_8((int)*(address+left), src_color, tolerance))
            break;
        }

        // Work right from starting point
        for (right=x+1; right<bounds.x2(); right++) {
          if (!color_equal_8((int)*(address+right), src_color, tolerance))
            break;
        }
      }
      break;

    default:
      // Check start pixel
      if (get_pixel(image, x, y) != src_color)
        return x+1;

      // Work left from starting point
      for (left=x-1; left>=bounds.x; left--) {
        if (get_pixel(image, left, y) != src_color)
          break;
      }

      // Work right from starting point
      for (right=x+1; right<bounds.x2(); right++) {
        if (get_pixel(image, right, y) != src_color)
          break;
      }
      break;
  }

  left++;
  right--;

  /* draw the line */
  (*proc)(left, y, right, data);

  /* store it in the list of flooded segments */
  c = y;
  p = FLOOD_LINE(c);

  if (p->flags) {
    while (p->next) {
      c = p->next;
      p = FLOOD_LINE(c);
    }

    p->next = c = flood_count++;
    flood_buf.resize(flood_count);
    p = FLOOD_LINE(c);
  }

  p->flags = FLOOD_IN_USE;
  p->lpos = left;
  p->rpos = right;
  p->y = y;
  p->next = 0;

  if (y > bounds.y)
    p->flags |= FLOOD_TODO_ABOVE;

  if (y+1 < bounds.y2())
    p->flags |= FLOOD_TODO_BELOW;

  return right+2;
}
예제 #4
0
/* flooder:
 *  Fills a horizontal line around the specified position, and adds it
 *  to the list of drawn segments. Returns the first x coordinate after
 *  the part of the line which it has dealt with.
 */
static int flooder (Image *image, int x, int y,
                    int src_color, int tolerance, void *data, AlgoHLine proc)
{
  FLOODED_LINE *p;
  int left = 0, right = 0;
  int c;

  switch (image->getPixelFormat()) {

    case IMAGE_RGB:
      {
        uint32_t* address = ((uint32_t**)image->line)[y];

        /* check start pixel */
        if (!color_equal_32((int)*(address+x), src_color, tolerance))
          return x+1;

        /* work left from starting point */
        for (left=x-1; left>=0; left--) {
          if (!color_equal_32((int)*(address+left), src_color, tolerance))
            break;
        }

        /* work right from starting point */
        for (right=x+1; right<image->w; right++) {
          if (!color_equal_32((int)*(address+right), src_color, tolerance))
            break;
        }
      }
      break;

    case IMAGE_GRAYSCALE:
      {
        uint16_t* address = ((uint16_t**)image->line)[y];

        /* check start pixel */
        if (!color_equal_16((int)*(address+x), src_color, tolerance))
          return x+1;

        /* work left from starting point */
        for (left=x-1; left>=0; left--) {
          if (!color_equal_16((int)*(address+left), src_color, tolerance))
            break;
        }

        /* work right from starting point */
        for (right=x+1; right<image->w; right++) {
          if (!color_equal_16((int)*(address+right), src_color, tolerance))
            break;
        }
      }
      break;

    case IMAGE_INDEXED:
      {
        uint8_t* address = ((uint8_t**)image->line)[y];

        /* check start pixel */
        if (!color_equal_8((int)*(address+x), src_color, tolerance))
          return x+1;

        /* work left from starting point */
        for (left=x-1; left>=0; left--) {
          if (!color_equal_8((int)*(address+left), src_color, tolerance))
            break;
        }

        /* work right from starting point */
        for (right=x+1; right<image->w; right++) {
          if (!color_equal_8((int)*(address+right), src_color, tolerance))
            break;
        }
      }
      break;

    default:
      /* check start pixel */
      if (image_getpixel(image, x, y) != src_color)
        return x+1;

      /* work left from starting point */
      for (left=x-1; left>=0; left--) {
        if (image_getpixel(image, left, y) != src_color)
          break;
      }

      /* work right from starting point */
      for (right=x+1; right<image->w; right++) {
        if (image_getpixel(image, right, y) != src_color)
          break;
      }
      break;
  }

  left++;
  right--;

  /* draw the line */
  (*proc)(left, y, right, data);

  /* store it in the list of flooded segments */
  c = y;
  p = FLOOD_LINE(c);

  if (p->flags) {
    while (p->next) {
      c = p->next;
      p = FLOOD_LINE(c);
    }

    p->next = c = flood_count++;
    _grow_scratch_mem(sizeof(FLOODED_LINE) * flood_count);
    p = FLOOD_LINE(c);
  }

  p->flags = FLOOD_IN_USE;
  p->lpos = left;
  p->rpos = right;
  p->y = y;
  p->next = 0;

  if (y > 0)
    p->flags |= FLOOD_TODO_ABOVE;

  if (y+1 < image->h)
    p->flags |= FLOOD_TODO_BELOW;

  return right+2;
}
예제 #5
0
/* floodfill:
 *  Fills an enclosed area (starting at point x, y) with the specified color.
 */
void algo_floodfill(Image* image, int x, int y, int tolerance, void *data, AlgoHLine proc)
{
  int src_color;
  int c, done;
  FLOODED_LINE *p;

  /* make sure we have a valid starting point */
  if ((x < 0) || (x >= image->w) ||
      (y < 0) || (y >= image->h))
    return;

  /* what color to replace? */
  src_color = image_getpixel (image, x, y);

  /* set up the list of flooded segments */
  _grow_scratch_mem(sizeof(FLOODED_LINE) * image->h);
  flood_count = image->h;
  p = (FLOODED_LINE*)_scratch_mem;
  for (c=0; c<flood_count; c++) {
    p[c].flags = 0;
    p[c].lpos = SHRT_MAX;
    p[c].rpos = SHRT_MIN;
    p[c].y = y;
    p[c].next = 0;
  }

  /* start up the flood algorithm */
  flooder(image, x, y, src_color, tolerance, data, proc);

  /* continue as long as there are some segments still to test */
  do {
    done = true;

    /* for each line on the screen */
    for (c=0; c<flood_count; c++) {

      p = FLOOD_LINE(c);

      /* check below the segment? */
      if (p->flags & FLOOD_TODO_BELOW) {
        p->flags &= ~FLOOD_TODO_BELOW;
        if (check_flood_line(image, p->y+1, p->lpos, p->rpos,
                             src_color, tolerance, data, proc)) {
          done = false;
          p = FLOOD_LINE(c);
        }
      }

      /* check above the segment? */
      if (p->flags & FLOOD_TODO_ABOVE) {
        p->flags &= ~FLOOD_TODO_ABOVE;
        if (check_flood_line(image, p->y-1, p->lpos, p->rpos,
                             src_color, tolerance, data, proc)) {
          done = false;
          /* special case shortcut for going backwards */
          if ((c < image->h) && (c > 0))
            c -= 2;
        }
      }
    }
  } while (!done);
}
/*
 * FloodFillGenerator:
 */
BOOL GUIAPI FloodFillGenerator (void* context, const RECT* dst_rc, int x, int y,
                CB_EQUAL_PIXEL cb_equal_pixel, CB_FLOOD_FILL cb_flood_fill)
{
    FLOODER_INFO fi;
    int c, done;
    FLOODED_LINE *p;

    /* make sure we have a valid starting point */ 
    if ((x < dst_rc->left) || (x >= dst_rc->right) || (y < dst_rc->top) || (y >= dst_rc->bottom)) {
        return TRUE;
    }

    /* set up the list of flooded segments */
    fi.flood_count = RECTHP (dst_rc);
    fi.flooded_lines = (FLOODED_LINE*) malloc (sizeof(FLOODED_LINE) * fi.flood_count);
    if (!(p = fi.flooded_lines)) {
        return FALSE;
    }

    fi.context = context;
    fi.dst_rc = dst_rc;
    fi.cb_equal_pixel = cb_equal_pixel;
    fi.cb_flood_fill = cb_flood_fill;

    for (c = 0; c < fi.flood_count; c++) {
        p[c].flags = 0;
        p[c].lpos = SHRT_MAX;
        p[c].rpos = SHRT_MIN;
        p[c].y = y;
        p[c].next = 0;
    }

    /* start up the flood algorithm */
    flooder (&fi, x, y);

    /* continue as long as there are some segments still to test */
    do {
        done = TRUE;

        /* for each line on the screen */
        for (c = 0; c < fi.flood_count; c++) {

            p = FLOOD_LINE (c);
    
            /* check below the segment? */
            if (p->flags & FLOOD_TODO_BELOW) {
                p->flags &= ~FLOOD_TODO_BELOW;
            if (check_flood_line (&fi, p->y+1, p->lpos, p->rpos)) {
                done = FALSE;
                p = FLOOD_LINE (c);
            }
        }

        /* check above the segment? */
        if (p->flags & FLOOD_TODO_ABOVE) {
            p->flags &= ~FLOOD_TODO_ABOVE;
            if (check_flood_line (&fi, p->y-1, p->lpos, p->rpos)) {
                done = FALSE;
                /* special case shortcut for going backwards */
                if ((c < RECTHP (dst_rc)) && (c > 0))
                    c -= 2;
                }
            }
        }

    } while (!done);

    free (fi.flooded_lines);
    return TRUE;
}