9 typedef struct _ibbox {
10 int xmin,ymin,xmax,ymax;
14 ibbox_t* ibbox_new(int x1, int y1, int x2, int y2)
16 ibbox_t*b = (ibbox_t*)rfx_calloc(sizeof(ibbox_t));
24 void ibbox_destroy(ibbox_t*b)
27 ibbox_t*next = b->next;
33 ibbox_t*get_bitmap_bboxes_simple(unsigned char*alpha, int width, int height, int rowsize)
41 for(y=0;y<height;y++) {
42 unsigned char*a = &alpha[y*rowsize];
43 for(x=0;x<width;x++) {
46 int left = x; //first occupied pixel from left
47 int right = x+1; //last non-occupied pixel from right
56 if(left<xmin) xmin = left;
57 if(right>xmax) xmax = right;
61 if(xmin<xmax || ymin<ymax) {
62 bbox = ibbox_new(xmin, ymin, xmax, ymax);
67 typedef struct _head {
79 typedef struct _context {
89 #define HEAD_MAGIC ((ptroff_t)-1)
91 static head_t*head_new(context_t*context, int x, int y)
93 int pos = context->width*y+x;
94 head_t*h = rfx_calloc(sizeof(head_t));
95 h->magic = HEAD_MAGIC;
96 h->nr = context->count++;
100 h->bbox.xmin = h->bbox.xmax = x;
101 h->bbox.ymin = h->bbox.ymax = y;
102 h->next = context->heads;
110 static void head_delete(context_t*context, head_t*h)
113 h->prev->next = h->next;
116 h->next->prev = h->prev;
118 if(h==context->heads) {
120 context->heads = h->next;
125 #define POINTS_TO_HEAD(ptr) (((head_t*)(ptr))->magic==HEAD_MAGIC)
127 static inline void link_to(context_t*context, int from, int to)
130 void**data = context->group;
133 while(!POINTS_TO_HEAD(data[head])) {
134 assert(data[head]!=(void*)&data[head]); // check that we're not in an infinite loop
135 head=(void**)data[head]-(void**)data;
137 head_t*h = (head_t*)data[head];
138 int x = from%context->width;
139 int y = from/context->width;
140 if(x < h->bbox.xmin) h->bbox.xmin = x;
141 if(y < h->bbox.ymin) h->bbox.ymin = y;
142 if(x > h->bbox.xmax) h->bbox.xmax = x;
143 if(y > h->bbox.ymax) h->bbox.ymax = y;
145 data[from] = (void*)&data[head];
147 static char ibbox_does_overlap(ibbox_t*b1, ibbox_t*b2)
149 if(b1->xmax < b2->xmin) return 0;
150 if(b2->xmax < b1->xmin) return 0;
151 if(b1->ymax < b2->ymin) return 0;
152 if(b2->ymax < b1->ymin) return 0;
155 static void ibbox_expand(ibbox_t*src, ibbox_t*add)
157 if(add->xmin < src->xmin)
158 src->xmin = add->xmin;
159 if(add->ymin < src->ymin)
160 src->ymin = add->ymin;
161 if(add->xmax > src->xmax)
162 src->xmax = add->xmax;
163 if(add->ymax > src->ymax)
164 src->ymax = add->ymax;
166 static inline void merge(context_t*context, int set1, int set2)
168 void**data = context->group;
173 while(!POINTS_TO_HEAD(data[head1])) {
174 head1=(void**)data[head1]-(void**)data;
176 while(!POINTS_TO_HEAD(data[head2])) {
177 head2=(void**)data[head2]-(void**)data;
179 head_t*h1 = (head_t*)data[head1];
180 head_t*h2 = (head_t*)data[head2];
184 if(h1->rank>h2->rank) {
186 ibbox_expand(&h1->bbox,&h2->bbox);
187 data[head2] = (void*)&data[head1];
188 head_delete(context, h2);
191 ibbox_expand(&h2->bbox,&h1->bbox);
192 data[head1] = (void*)&data[head2];
193 head_delete(context, h1);
197 ibbox_t ibbox_clip(ibbox_t* outer, ibbox_t* inner)
199 ibbox_t i = {inner->xmin, inner->ymin, inner->xmax, inner->ymax, 0};
200 if(i.xmax > outer->xmax) i.xmax = outer->xmax;
201 if(i.ymax > outer->ymax) i.ymax = outer->ymax;
202 if(i.xmax < outer->xmin) i.xmax = outer->xmin;
203 if(i.ymax < outer->ymin) i.ymax = outer->ymin;
205 if(i.xmin > outer->xmax) i.xmin = outer->xmax;
206 if(i.ymin > outer->ymax) i.ymin = outer->ymax;
207 if(i.xmin < outer->xmin) i.xmin = outer->xmin;
208 if(i.ymin < outer->ymin) i.ymin = outer->ymin;
212 static void** annotate(context_t*context)
214 unsigned char*alpha = context->alpha;
215 int width = context->width;
216 int height = context->height;
217 void** group = rfx_calloc(width*height*sizeof(void*));
218 context->group = group;
221 for(x=1;x<width;x++) {
224 link_to(context,x,x-1);
226 group[x]=head_new(context,x,0);
231 for(y=1;y<height;y++) {
233 apos += context->rowsize;
236 link_to(context,pos,pos-width);
238 group[pos]=head_new(context,0,y);
240 for(x=1;x<width;x++) {
241 /* once this code is stable we should copy&paste it
242 out of the loop, change the loop end to width-1 and
243 add the pos-width+1 case */
245 if(group[pos+x-width]) {
246 link_to(context,pos+x,pos+x-width);
248 merge(context,pos+x,pos+x-1);
249 } else if(group[pos+x-1]) {
250 link_to(context,pos+x,pos+x-1);
251 } else if(group[pos+x-width-1]) {
252 link_to(context,pos+x,pos+x-width-1);
254 group[pos+x]=head_new(context,x,y);
262 static void overlap_bboxes(context_t*context)
266 head_t*h1 = context->heads;
269 head_t*next = h1->next;
270 head_t*h2 = context->heads;
273 if(ibbox_does_overlap(&h1->bbox, &h2->bbox)) {
274 merge(context, h1->pos, h2->pos);
286 typedef struct _circle_coord {
290 static int compare_circle_coord(const void *_v1, const void *_v2)
292 circle_coord_t*v1=(circle_coord_t*)_v1;
293 circle_coord_t*v2=(circle_coord_t*)_v2;
294 return (v1->x*v1->x + v1->y*v1->y) - (v2->x*v2->x + v2->y*v2->y);
297 static head_t* search_vicinity(context_t*context, head_t*h, int max_radius, double*cos, double*sin)
299 static circle_coord_t*circle_order = 0;
300 static int circle_order_size = 0;
303 circle_order_size = (max_radius*(max_radius+1))/2;
304 circle_order = malloc(sizeof(circle_coord_t)*circle_order_size);
307 for(y=0;y<max_radius;y++) {
314 assert(i==circle_order_size);
315 qsort(circle_order, circle_order_size, sizeof(circle_coord_t), compare_circle_coord);
319 void**data = context->group;
320 int signx[4] = {-1,1,-1,1};
321 int signy[4] = {-1,-1,1,1};
322 for(t=1;t<circle_order_size;t++) {
323 int xx = circle_order[t].x;
324 int yy = circle_order[t].y;
327 int x=h->x+xx*signx[s];
328 int y=h->y+yy*signy[s];
329 if(x>=0 && y>=0 && x<context->width && y<context->height) {
330 int pos = y*context->width+x;
332 while(!POINTS_TO_HEAD(data[pos])) {
333 pos=(void**)data[pos]-(void**)data;
335 head_t*new_head = (head_t*)data[pos];
346 static void fix_small_boxes(context_t*context)
352 sintab[t] = sin(t*M_PI/128);
353 costab[t] = cos(t*M_PI/128);
356 head_t*h = context->heads;
365 head_t*h = context->heads;
367 head_t*next = h->next;
369 if(h->bbox.xmax - h->bbox.xmin < 32
370 || h->bbox.ymax - h->bbox.ymin < 32) {
371 head_t*other = search_vicinity(context, h, 64, costab, sintab);
373 merge(context, h->pos, other->pos);
377 //printf("nothing in the vicinity of %d,%d,%d,%d\n", h->bbox);
381 printf("area %d,%d,%d,%d is large enough (%dx%d)\n",
386 h->bbox.xmax - h->bbox.xmin,
387 h->bbox.ymax - h->bbox.ymin);
395 static void display(context_t*context)
397 int width = context->width;
398 int height = context->height;
399 void**group = context->group;
402 for(y=0;y<height;y++) {
403 for(x=0;x<width;x++) {
404 if(!group[y*width+x]) {
406 } else if(POINTS_TO_HEAD(group[y*width+x])) {
407 printf("g%02d ", ((head_t*)group[y*width+x])->nr);
409 printf("x%02d ", (void**)group[y*width+x]-(void**)group);
415 head_t*h = context->heads;
417 printf("head: %d\n", h->nr);
418 printf(" pos: %d/%d\n", h->pos%width, h->pos/width);
419 printf(" bbox: [%d/%d,%d/%d]\n", h->bbox.xmin, h->bbox.ymin, h->bbox.xmax, h->bbox.ymax);
424 ibbox_t*get_bitmap_bboxes(unsigned char*alpha, int width, int height, int rowsize)
426 int size = width*height;
427 if(width<=1 || height<=1)
428 return get_bitmap_bboxes_simple(alpha, width, height, rowsize);
431 context.alpha = alpha;
432 context.rowsize = rowsize;
433 context.width = width;
434 context.height = height;
438 void**group = annotate(&context);
439 fix_small_boxes(&context);
440 overlap_bboxes(&context);
447 head_t*h = context.heads;
449 head_t*next = h->next;
450 ibbox_t*bbox = malloc(sizeof(ibbox_t));
451 memcpy(bbox, &h->bbox, sizeof(ibbox_t));
453 /* ibbox_t defines the open upper bound */
467 int main(int argn, char*argv[])
469 unsigned char alpha[8*8]=
479 get_bitmap_bboxes(alpha, 8,8, 8);