diff --git a/lib/identify.c b/lib/identify.c index 3cf2d28..44fa48d 100644 --- a/lib/identify.c +++ b/lib/identify.c @@ -16,6 +16,7 @@ #include #include +#include #include #include #include "quirc_internal.h" @@ -122,21 +123,23 @@ static void perspective_unmap(const double *c, * Span-based floodfill routine */ -#define FLOOD_FILL_MAX_DEPTH 4096 - typedef void (*span_func_t)(void *user_data, int y, int left, int right); -static void flood_fill_seed(struct quirc *q, int x, int y, int from, int to, +static void flood_fill_line(struct quirc *q, int x, int y, + int from, int to, span_func_t func, void *user_data, - int depth) + int *leftp, int *rightp) { - int left = x; - int right = x; + quirc_pixel_t *row; + int left; + int right; int i; - quirc_pixel_t *row = q->pixels + y * q->w; - if (depth >= FLOOD_FILL_MAX_DEPTH) - return; + row = q->pixels + y * q->w; + QUIRC_ASSERT(row[x] == from); + + left = x; + right = x; while (left > 0 && row[left - 1] == from) left--; @@ -148,26 +151,132 @@ static void flood_fill_seed(struct quirc *q, int x, int y, int from, int to, for (i = left; i <= right; i++) row[i] = to; + /* Return the processed range */ + *leftp = left; + *rightp = right; + if (func) func(user_data, y, left, right); +} - /* Seed new flood-fills */ - if (y > 0) { - row = q->pixels + (y - 1) * q->w; +static struct quirc_flood_fill_vars *flood_fill_call_next( + struct quirc *q, + quirc_pixel_t *row, + int from, int to, + span_func_t func, void *user_data, + struct quirc_flood_fill_vars *vars, + int direction) +{ + int *leftp; - for (i = left; i <= right; i++) - if (row[i] == from) - flood_fill_seed(q, i, y - 1, from, to, - func, user_data, depth + 1); + if (direction < 0) { + leftp = &vars->left_up; + } else { + leftp = &vars->left_down; } - if (y < q->h - 1) { - row = q->pixels + (y + 1) * q->w; + while (*leftp <= vars->right) { + if (row[*leftp] == from) { + struct quirc_flood_fill_vars *next_vars; + int next_left; - for (i = left; i <= right; i++) - if (row[i] == from) - flood_fill_seed(q, i, y + 1, from, to, - func, user_data, depth + 1); + /* Set up the next context */ + next_vars = vars + 1; + next_vars->y = vars->y + direction; + + /* Fill the extent */ + flood_fill_line(q, + *leftp, + next_vars->y, + from, to, + func, user_data, + &next_left, + &next_vars->right); + next_vars->left_down = next_left; + next_vars->left_up = next_left; + + return next_vars; + } + (*leftp)++; + } + return NULL; +} + +static void flood_fill_seed(struct quirc *q, + int x0, int y0, + int from, int to, + span_func_t func, void *user_data) +{ + struct quirc_flood_fill_vars *const stack = q->flood_fill_vars; + const size_t stack_size = q->num_flood_fill_vars; + const struct quirc_flood_fill_vars *const last_vars = + &stack[stack_size - 1]; + + QUIRC_ASSERT(from != to); + QUIRC_ASSERT(q->pixels[y0 * q->w + x0] == from); + + struct quirc_flood_fill_vars *next_vars; + int next_left; + + /* Set up the first context */ + next_vars = stack; + next_vars->y = y0; + + /* Fill the extent */ + flood_fill_line(q, x0, next_vars->y, from, to, + func, user_data, + &next_left, &next_vars->right); + next_vars->left_down = next_left; + next_vars->left_up = next_left; + + while (true) { + struct quirc_flood_fill_vars * const vars = next_vars; + quirc_pixel_t *row; + + if (vars == last_vars) { + /* + * "Stack overflow". + * Just stop and return. + * This can be caused by very complex shapes in + * the image, which is not likely a part of + * a valid QR code anyway. + */ + break; + } + + /* Seed new flood-fills */ + if (vars->y > 0) { + row = q->pixels + (vars->y - 1) * q->w; + + next_vars = flood_fill_call_next(q, row, + from, to, + func, user_data, + vars, -1); + if (next_vars != NULL) { + continue; + } + } + + if (vars->y < q->h - 1) { + row = q->pixels + (vars->y + 1) * q->w; + + next_vars = flood_fill_call_next(q, row, + from, to, + func, user_data, + vars, 1); + if (next_vars != NULL) { + continue; + } + } + + if (vars > stack) { + /* Restore the previous context */ + next_vars = vars - 1; + continue; + } + + /* We've done. */ + break; } } @@ -260,7 +369,7 @@ static int region_code(struct quirc *q, int x, int y) box->seed.y = y; box->capstone = -1; - flood_fill_seed(q, x, y, pixel, region, area_count, box, 0); + flood_fill_seed(q, x, y, pixel, region, area_count, box); return region; } @@ -330,7 +439,7 @@ static void find_region_corners(struct quirc *q, psd.scores[0] = -1; flood_fill_seed(q, region->seed.x, region->seed.y, rcode, QUIRC_PIXEL_BLACK, - find_one_corner, &psd, 0); + find_one_corner, &psd); psd.ref.x = psd.corners[0].x - psd.ref.x; psd.ref.y = psd.corners[0].y - psd.ref.y; @@ -348,7 +457,7 @@ static void find_region_corners(struct quirc *q, flood_fill_seed(q, region->seed.x, region->seed.y, QUIRC_PIXEL_BLACK, rcode, - find_other_corners, &psd, 0); + find_other_corners, &psd); } static void record_capstone(struct quirc *q, int ring, int stone) @@ -966,10 +1075,10 @@ static void record_qr_grid(struct quirc *q, int a, int b, int c) flood_fill_seed(q, reg->seed.x, reg->seed.y, qr->align_region, QUIRC_PIXEL_BLACK, - NULL, NULL, 0); + NULL, NULL); flood_fill_seed(q, reg->seed.x, reg->seed.y, QUIRC_PIXEL_BLACK, qr->align_region, - find_leftmost_to_line, &psd, 0); + find_leftmost_to_line, &psd); } } diff --git a/lib/quirc.c b/lib/quirc.c index bbe1fe9..3cf75b9 100644 --- a/lib/quirc.c +++ b/lib/quirc.c @@ -41,6 +41,7 @@ void quirc_destroy(struct quirc *q) same size, so we need to be careful here to avoid a double free */ if (!QUIRC_PIXEL_ALIAS_IMAGE) free(q->pixels); + free(q->flood_fill_vars); free(q); } @@ -48,6 +49,9 @@ int quirc_resize(struct quirc *q, int w, int h) { uint8_t *image = NULL; quirc_pixel_t *pixels = NULL; + size_t num_vars; + size_t vars_byte_size; + struct quirc_flood_fill_vars *vars = NULL; /* * XXX: w and h should be size_t (or at least unsigned) as negatives @@ -86,6 +90,33 @@ int quirc_resize(struct quirc *q, int w, int h) goto fail; } + /* + * alloc the work area for the flood filling logic. + * + * the size was chosen with the following assumptions and observations: + * + * - rings are the regions which requires the biggest work area. + * - they consumes the most when they are rotated by about 45 degree. + * in that case, the necessary depth is about (2 * height_of_the_ring). + * - the maximum height of rings would be about 1/3 of the image height. + */ + + if ((size_t)h * 2 / 2 != h) { + goto fail; /* size_t overflow */ + } + num_vars = (size_t)h * 2 / 3; + if (num_vars == 0) { + num_vars = 1; + } + + vars_byte_size = sizeof(*vars) * num_vars; + if (vars_byte_size / sizeof(*vars) != num_vars) { + goto fail; /* size_t overflow */ + } + vars = malloc(vars_byte_size); + if (!vars) + goto fail; + /* alloc succeeded, update `q` with the new size and buffers */ q->w = w; q->h = h; @@ -95,12 +126,16 @@ int quirc_resize(struct quirc *q, int w, int h) free(q->pixels); q->pixels = pixels; } + free(q->flood_fill_vars); + q->flood_fill_vars = vars; + q->num_flood_fill_vars = num_vars; return 0; /* NOTREACHED */ fail: free(image); free(pixels); + free(vars); return -1; } diff --git a/lib/quirc_internal.h b/lib/quirc_internal.h index 371572f..0d8a229 100644 --- a/lib/quirc_internal.h +++ b/lib/quirc_internal.h @@ -17,8 +17,13 @@ #ifndef QUIRC_INTERNAL_H_ #define QUIRC_INTERNAL_H_ +#include +#include + #include "quirc.h" +#define QUIRC_ASSERT(a) assert(a) + #define QUIRC_PIXEL_WHITE 0 #define QUIRC_PIXEL_BLACK 1 #define QUIRC_PIXEL_REGION 2 @@ -75,6 +80,13 @@ struct quirc_grid { double c[QUIRC_PERSPECTIVE_PARAMS]; }; +struct quirc_flood_fill_vars { + int y; + int right; + int left_up; + int left_down; +}; + struct quirc { uint8_t *image; quirc_pixel_t *pixels; @@ -89,6 +101,9 @@ struct quirc { int num_grids; struct quirc_grid grids[QUIRC_MAX_GRIDS]; + + size_t num_flood_fill_vars; + struct quirc_flood_fill_vars *flood_fill_vars; }; /************************************************************************