#include "../png.h"
#include "../devices/record.h"
#include "../types.h"
+#include "bbox.h"
#define UNKNOWN_BOUNDING_BOX 0,0,0,0
this->gfxdev->setPageMap(page2page, num_pages);
}
-typedef struct _ibbox {
- int xmin,ymin,xmax,ymax;
- struct _ibbox*next;
-} ibbox_t;
-
-static ibbox_t* ibbox_new(int x1, int y1, int x2, int y2)
-{
- ibbox_t*b = (ibbox_t*)rfx_calloc(sizeof(ibbox_t));
- b->xmin = x1;
- b->ymin = y1;
- b->xmax = x2;
- b->ymax = y2;
- return b;
-}
-
-static void ibbox_destroy(ibbox_t*b)
-{
- while(b) {
- ibbox_t*next = b->next;
- free(b);
- b = next;
- }
-}
-
-static ibbox_t*getBitmapBBoxes(Guchar*alpha, int width, int height)
-{
- int ymin = -1;
- int ymax = -1;
- int xmin = width;
- int xmax = 0;
-
- int x,y;
- for(y=0;y<height;y++) {
- Guchar*a = &alpha[y*width];
- for(x=0;x<width;x++) {
- if(a[x]) break;
- }
- int left = x; //first occupied pixel from left
- int right = x+1; //last non-occupied pixel from right
- for(;x<width;x++) {
- if(a[x]) right=x+1;
- }
-
- if(left!=width) {
- if(ymin<0)
- ymin=y;
- ymax=y+1;
- if(left<xmin) xmin = left;
- if(right>xmax) xmax = right;
- }
- }
- ibbox_t* bbox = 0;
- if(xmin<xmax || ymin<ymax) {
- bbox = ibbox_new(xmin, ymin, xmax, ymax);
- }
- return bbox;
-}
-
void BitmapOutputDev::flushBitmap()
{
int width = rgbdev->getBitmapWidth();
Guchar*alpha = rgbbitmap->getAlphaPtr();
Guchar*alpha2 = boolpolybitmap->getAlphaPtr();
- ibbox_t* boxes = getBitmapBBoxes(alpha, width, height);
+ ibbox_t* boxes = get_bitmap_bboxes((unsigned char*)alpha, width, height);
ibbox_t*b;
- for(b=boxes;boxes;boxes=boxes->next) {
+ for(b=boxes;b;b=b->next) {
int xmin = b->xmin;
int ymin = b->ymin;
int xmax = b->xmax;
#include <stdio.h>
#include <stdlib.h>
#include <memory.h>
+#include <math.h>
#include <assert.h>
#include "../types.h"
#include "../mem.h"
}
}
-ibbox_t*get_bitmap_bboxes_old(unsigned char*alpha, int width, int height)
+ibbox_t*get_bitmap_bboxes_simple(unsigned char*alpha, int width, int height)
{
int ymin = -1;
int ymax = -1;
int nr;
int pos;
int rank;
+ int x,y;
+ char seen;
struct _head*next;
struct _head*prev;
} head_t;
#define HEAD_MAGIC ((ptroff_t)-1)
-head_t*head_new(context_t*context, int x, int y)
+static head_t*head_new(context_t*context, int x, int y)
{
int pos = context->width*y+x;
head_t*h = rfx_calloc(sizeof(head_t));
h->magic = HEAD_MAGIC;
h->nr = context->count++;
h->pos = pos;
+ h->x = x;
+ h->y = y;
h->bbox.xmin = h->bbox.xmax = x;
h->bbox.ymin = h->bbox.ymax = y;
h->next = context->heads;
return h;
}
-void head_delete(context_t*context, head_t*h)
+static void head_delete(context_t*context, head_t*h)
{
if(h->prev) {
h->prev->next = h->next;
#define POINTS_TO_HEAD(ptr) (((head_t*)(ptr))->magic==HEAD_MAGIC)
-static inline link_to(context_t*context, int from, int to)
+static inline void link_to(context_t*context, int from, int to)
{
// path compression
void**data = context->group;
int head = to;
assert(data[head]);
while(!POINTS_TO_HEAD(data[head])) {
+ assert(data[head]!=(void*)&data[head]); // check that we're not in an infinite loop
head=(void**)data[head]-(void**)data;
}
head_t*h = (head_t*)data[head];
}
static char ibbox_does_overlap(ibbox_t*b1, ibbox_t*b2)
{
- if(b1->xmax < b1->xmin) return 0;
- if(b2->xmax < b2->xmin) return 0;
- if(b1->ymax < b1->ymin) return 0;
- if(b2->ymax < b2->ymin) return 0;
+ if(b1->xmax < b2->xmin) return 0;
+ if(b2->xmax < b1->xmin) return 0;
+ if(b1->ymax < b2->ymin) return 0;
+ if(b2->ymax < b1->ymin) return 0;
return 1;
}
static void ibbox_expand(ibbox_t*src, ibbox_t*add)
if(add->ymax > src->ymax)
src->ymax = add->ymax;
}
-static inline merge(context_t*context, int set1, int set2)
+static inline void merge(context_t*context, int set1, int set2)
{
void**data = context->group;
assert(data[set1]);
}
head_t*h1 = (head_t*)data[head1];
head_t*h2 = (head_t*)data[head2];
+ if(h1==h2)
+ return;
if(h1->rank>h2->rank) {
h1->rank++;
}
}
-void** annotate(context_t*context)
+static void** annotate(context_t*context)
{
unsigned char*alpha = context->alpha;
int width = context->width;
return group;
}
-ibbox_t*get_bitmap_bboxes(unsigned char*alpha, int width, int height)
+static void overlap_bboxes(context_t*context)
{
- int size = width*height;
- if(width<=1 || height<=1)
- return get_bitmap_bboxes_old(alpha, width, height);
-
- context_t context;
- context.alpha = alpha;
- context.width = width;
- context.height = height;
- context.heads = 0;
- context.count = 1;
+ char changed;
+ do {
+ head_t*h1 = context->heads;
+ changed = 0;
+ while(h1) {
+ head_t*next = h1->next;
+ head_t*h2 = context->heads;
+ while(h2) {
+ if(h1!=h2) {
+ if(ibbox_does_overlap(&h1->bbox, &h2->bbox)) {
+ merge(context, h1->pos, h2->pos);
+ changed = 1;
+ break;
+ }
+ }
+ h2 = h2->next;
+ }
+ h1 = next;
+ }
+ } while(changed);
+}
+
+static head_t* search_vicinity(context_t*context, head_t*h, int max_radius, double*cos, double*sin)
+{
+ int t;
+ void**data = context->group;
+ /* FIXME: this function isn't very efficient. We need a better way to iterate
+ through all points, sorted by distance, in the neighborhood of a coordinate */
+ for(t=3;t<max_radius*2;t++) {
+ int s;
+ for(s=0;s<256;s++) {
+ int x=h->x+cos[s]*t/4.0+0.5;
+ int y=h->y+sin[s]*t/4.0+0.5;
+ if(x>=0 && y>=0 && x<context->width && y<context->height) {
+ int pos = y*context->width+x;
+ if(data[pos]) {
+ while(!POINTS_TO_HEAD(data[pos])) {
+ pos=(void**)data[pos]-(void**)data;
+ }
+ head_t*new_head = (head_t*)data[pos];
+ if(new_head != h) {
+ return new_head;
+ }
+ }
+ }
+ }
+ }
+ return 0;
+}
+
+static void fix_small_boxes(context_t*context)
+{
+ double sintab[256];
+ double costab[256];
+ int t;
+ for(t=0;t<256;t++) {
+ sintab[t] = sin(t*M_PI/128);
+ costab[t] = cos(t*M_PI/128);
+ }
+
+ head_t*h = context->heads;
+ while(h) {
+ h->seen = 0;
+ h = h->next;
+ }
+
+ char changed;
+ do {
+ changed = 0;
+ head_t*h = context->heads;
+ while(h) {
+ head_t*next = h->next;
+ if(!h->seen) {
+ if(h->bbox.xmax - h->bbox.ymin < 16
+ && h->bbox.ymax - h->bbox.ymin < 16) {
+ head_t*other = search_vicinity(context, h, 64, costab, sintab);
+ if(other) {
+ merge(context, h->pos, other->pos);
+ changed = 1;
+ break;
+ } else {
+ h->seen = 1;
+ }
+ }
+ }
+ h = next;
+ }
+ } while(changed);
+}
+
+static void display(context_t*context)
+{
+ int width = context->width;
+ int height = context->height;
+ void**group = context->group;
- void**group = annotate(&context);
int x,y;
for(y=0;y<height;y++) {
for(x=0;x<width;x++) {
printf("\n");
}
- head_t*h1 = context.heads;
- while(h1) {
- head_t*next = h1->next;
- head_t*h2 = context.heads;
- while(h2) {
- if(ibbox_does_overlap(&h1->bbox, &h2->bbox)) {
- merge(&context, h1->pos, h2->pos);
- break;
- }
- h2 = h2->next;
- }
- h1 = next;
- }
-
- head_t*h = context.heads;
+ head_t*h = context->heads;
while(h) {
printf("head: %d\n", h->nr);
printf(" pos: %d/%d\n", h->pos%width, h->pos/width);
}
}
+ibbox_t*get_bitmap_bboxes(unsigned char*alpha, int width, int height)
+{
+ int size = width*height;
+ if(width<=1 || height<=1)
+ return get_bitmap_bboxes_simple(alpha, width, height);
+
+ context_t context;
+ context.alpha = alpha;
+ context.width = width;
+ context.height = height;
+ context.heads = 0;
+ context.count = 1;
+
+ void**group = annotate(&context);
+ fix_small_boxes(&context);
+ overlap_bboxes(&context);
+
+ ibbox_t*bboxes = 0;
+
+ head_t*h = context.heads;
+ while(h) {
+ head_t*next = h->next;
+ ibbox_t*bbox = malloc(sizeof(ibbox_t));
+ memcpy(bbox, &h->bbox, sizeof(ibbox_t));
+
+ /* ibbox_t defines the open upper bound */
+ bbox->xmax++;
+ bbox->ymax++;
+
+ bbox->next = bboxes;
+ bboxes = bbox;
+ free(h);
+ h = next;
+ }
+ free(context.group);
+ return bboxes;
+}
+
#ifdef MAIN
int main(int argn, char*argv[])
{