pcb 4.1.1
An interactive printed circuit board layout editor.
|
00001 00018 /* decompose.c 146 2007-04-09 00:43:46Z selinger */ 00019 00020 #include <stdio.h> 00021 #include <stdlib.h> 00022 #include <string.h> 00023 #include <limits.h> 00024 00025 #include "potracelib.h" 00026 #include "curve.h" 00027 #include "lists.h" 00028 #include "auxiliary.h" 00029 #include "bitmap.h" 00030 #include "decompose.h" 00031 //#include "progress.h" 00032 00036 static void 00037 bm_clearexcess (potrace_bitmap_t * bm) 00038 { 00039 potrace_word mask; 00040 int y; 00041 00042 if (bm->w % BM_WORDBITS != 0) 00043 { 00044 mask = BM_ALLBITS << (BM_WORDBITS - (bm->w % BM_WORDBITS)); 00045 for (y = 0; y < bm->h; y++) 00046 { 00047 *bm_index (bm, bm->w, y) &= mask; 00048 } 00049 } 00050 } 00051 00052 struct bbox_s 00053 { 00054 int x0, x1, y0, y1; /* bounding box */ 00055 }; 00056 typedef struct bbox_s bbox_t; 00057 00063 static void 00064 clear_bm_with_bbox (potrace_bitmap_t * bm, bbox_t * bbox) 00065 { 00066 int imin = (bbox->x0 / BM_WORDBITS); 00067 int imax = ((bbox->x1 + BM_WORDBITS - 1) / BM_WORDBITS); 00068 int i, y; 00069 00070 for (y = bbox->y0; y < bbox->y1; y++) 00071 { 00072 for (i = imin; i < imax; i++) 00073 { 00074 bm_scanline (bm, y)[i] = 0; 00075 } 00076 } 00077 } 00078 00079 /* ---------------------------------------------------------------------- */ 00080 /* auxiliary functions */ 00081 00086 static inline int 00087 detrand (int x, int y) 00088 { 00089 unsigned int z; 00090 static const unsigned char t[256] = { 00091 /* non-linear sequence: constant term of inverse in GF(8), 00092 mod x^8+x^4+x^3+x+1 */ 00093 0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 1, 00094 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 00095 0, 1, 0, 0, 1, 1, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 00096 1, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 00097 0, 0, 1, 1, 1, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 00098 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 1, 0, 1, 0, 00099 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 1, 0, 1, 0, 00100 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 00101 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 0, 0, 00102 0, 1, 0, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 1, 00103 1, 1, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 00104 }; 00105 00106 /* 0x04b3e375 and 0x05a8ef93 are chosen to contain every possible 00107 5-bit sequence */ 00108 z = ((0x04b3e375 * x) ^ y) * 0x05a8ef93; 00109 z = 00110 t[z & 0xff] ^ t[(z >> 8) & 0xff] ^ t[(z >> 16) & 0xff] ^ t[(z >> 24) & 00111 0xff]; 00112 return z & 1; 00113 } 00114 00121 static int 00122 majority (potrace_bitmap_t * bm, int x, int y) 00123 { 00124 int i, a, ct; 00125 00126 for (i = 2; i < 5; i++) 00127 { /* check at "radius" i */ 00128 ct = 0; 00129 for (a = -i + 1; a <= i - 1; a++) 00130 { 00131 ct += BM_GET (bm, x + a, y + i - 1) ? 1 : -1; 00132 ct += BM_GET (bm, x + i - 1, y + a - 1) ? 1 : -1; 00133 ct += BM_GET (bm, x + a - 1, y - i) ? 1 : -1; 00134 ct += BM_GET (bm, x - i, y + a) ? 1 : -1; 00135 } 00136 if (ct > 0) 00137 { 00138 return 1; 00139 } 00140 else if (ct < 0) 00141 { 00142 return 0; 00143 } 00144 } 00145 return 0; 00146 } 00147 00148 /* ---------------------------------------------------------------------- */ 00149 /* decompose image into paths */ 00150 00156 static void 00157 xor_to_ref (potrace_bitmap_t * bm, int x, int y, int xa) 00158 { 00159 int xhi = x & -BM_WORDBITS; 00160 int xlo = x & (BM_WORDBITS - 1); /* = x % BM_WORDBITS */ 00161 int i; 00162 00163 if (xhi < xa) 00164 { 00165 for (i = xhi; i < xa; i += BM_WORDBITS) 00166 { 00167 *bm_index (bm, i, y) ^= BM_ALLBITS; 00168 } 00169 } 00170 else 00171 { 00172 for (i = xa; i < xhi; i += BM_WORDBITS) 00173 { 00174 *bm_index (bm, i, y) ^= BM_ALLBITS; 00175 } 00176 } 00177 /* note: the following "if" is needed because x86 treats a<<b as 00178 a<<(b&31). I spent hours looking for this bug. */ 00179 if (xlo) 00180 { 00181 *bm_index (bm, xhi, y) ^= (BM_ALLBITS << (BM_WORDBITS - xlo)); 00182 } 00183 } 00184 00196 static void 00197 xor_path (potrace_bitmap_t * bm, path_t * p) 00198 { 00199 int xa, x, y, k, y1; 00200 00201 if (p->priv->len <= 0) 00202 { /* a path of length 0 is silly, but legal */ 00203 return; 00204 } 00205 00206 y1 = p->priv->pt[p->priv->len - 1].y; 00207 00208 xa = p->priv->pt[0].x & -BM_WORDBITS; 00209 for (k = 0; k < p->priv->len; k++) 00210 { 00211 x = p->priv->pt[k].x; 00212 y = p->priv->pt[k].y; 00213 00214 if (y != y1) 00215 { 00216 /* efficiently invert the rectangle [x,xa] x [y,y1] */ 00217 xor_to_ref (bm, x, min (y, y1), xa); 00218 y1 = y; 00219 } 00220 } 00221 } 00222 00228 static void 00229 setbbox_path (bbox_t * bbox, path_t * p) 00230 { 00231 int x, y; 00232 int k; 00233 00234 bbox->y0 = INT_MAX; 00235 bbox->y1 = 0; 00236 bbox->x0 = INT_MAX; 00237 bbox->x1 = 0; 00238 00239 for (k = 0; k < p->priv->len; k++) 00240 { 00241 x = p->priv->pt[k].x; 00242 y = p->priv->pt[k].y; 00243 00244 if (x < bbox->x0) 00245 { 00246 bbox->x0 = x; 00247 } 00248 if (x > bbox->x1) 00249 { 00250 bbox->x1 = x; 00251 } 00252 if (y < bbox->y0) 00253 { 00254 bbox->y0 = y; 00255 } 00256 if (y > bbox->y1) 00257 { 00258 bbox->y1 = y; 00259 } 00260 } 00261 } 00262 00273 static path_t * 00274 findpath (potrace_bitmap_t * bm, int x0, int y0, int sign, int turnpolicy) 00275 { 00276 int x, y, dirx, diry, len, size, area; 00277 int c, d, tmp; 00278 point_t *pt, *pt1; 00279 path_t *p = NULL; 00280 00281 x = x0; 00282 y = y0; 00283 dirx = 0; 00284 diry = -1; 00285 00286 len = size = 0; 00287 pt = NULL; 00288 area = 0; 00289 00290 while (1) 00291 { 00292 /* add point to path */ 00293 if (len >= size) 00294 { 00295 size += 100; 00296 size = (int) (1.3 * size); 00297 pt1 = (point_t *) realloc (pt, size * sizeof (point_t)); 00298 if (!pt1) 00299 { 00300 goto error; 00301 } 00302 pt = pt1; 00303 } 00304 pt[len].x = x; 00305 pt[len].y = y; 00306 len++; 00307 00308 /* move to next point */ 00309 x += dirx; 00310 y += diry; 00311 area += x * diry; 00312 00313 /* path complete? */ 00314 if (x == x0 && y == y0) 00315 { 00316 break; 00317 } 00318 00319 /* determine next direction */ 00320 c = BM_GET (bm, x + (dirx + diry - 1) / 2, y + (diry - dirx - 1) / 2); 00321 d = BM_GET (bm, x + (dirx - diry - 1) / 2, y + (diry + dirx - 1) / 2); 00322 00323 if (c && !d) 00324 { /* ambiguous turn */ 00325 if (turnpolicy == POTRACE_TURNPOLICY_RIGHT 00326 || (turnpolicy == POTRACE_TURNPOLICY_BLACK && sign == '+') 00327 || (turnpolicy == POTRACE_TURNPOLICY_WHITE && sign == '-') 00328 || (turnpolicy == POTRACE_TURNPOLICY_RANDOM && detrand (x, y)) 00329 || (turnpolicy == POTRACE_TURNPOLICY_MAJORITY 00330 && majority (bm, x, y)) 00331 || (turnpolicy == POTRACE_TURNPOLICY_MINORITY 00332 && !majority (bm, x, y))) 00333 { 00334 tmp = dirx; /* right turn */ 00335 dirx = diry; 00336 diry = -tmp; 00337 } 00338 else 00339 { 00340 tmp = dirx; /* left turn */ 00341 dirx = -diry; 00342 diry = tmp; 00343 } 00344 } 00345 else if (c) 00346 { /* right turn */ 00347 tmp = dirx; 00348 dirx = diry; 00349 diry = -tmp; 00350 } 00351 else if (!d) 00352 { /* left turn */ 00353 tmp = dirx; 00354 dirx = -diry; 00355 diry = tmp; 00356 } 00357 } /* while this path */ 00358 00359 /* allocate new path object */ 00360 p = path_new (); 00361 if (!p) 00362 { 00363 goto error; 00364 } 00365 00366 p->priv->pt = pt; 00367 p->priv->len = len; 00368 p->area = area; 00369 p->sign = sign; 00370 00371 return p; 00372 00373 error: 00374 free (pt); 00375 return NULL; 00376 } 00377 00399 static void 00400 pathlist_to_tree (path_t * plist, potrace_bitmap_t * bm) 00401 { 00402 path_t *p, *p1; 00403 path_t *heap, *heap1; 00404 path_t *cur; 00405 path_t *head; 00406 path_t **hook, **hook_in, **hook_out; /* for fast appending to linked list */ 00407 bbox_t bbox; 00408 00409 bm_clear (bm, 0); 00410 00411 /* save original "next" pointers */ 00412 list_forall (p, plist) 00413 { 00414 p->sibling = p->next; 00415 p->childlist = NULL; 00416 } 00417 00418 heap = plist; 00419 00420 /* the heap holds a list of lists of paths. Use "childlist" field 00421 for outer list, "next" field for inner list. Each of the sublists 00422 is to be turned into a tree. This code is messy, but it is 00423 actually fast. Each path is rendered exactly once. We use the 00424 heap to get a tail recursive algorithm: the heap holds a list of 00425 pathlists which still need to be transformed. */ 00426 00427 while (heap) 00428 { 00429 /* unlink first sublist */ 00430 cur = heap; 00431 heap = heap->childlist; 00432 cur->childlist = NULL; 00433 00434 /* unlink first path */ 00435 head = cur; 00436 cur = cur->next; 00437 head->next = NULL; 00438 00439 /* render path */ 00440 xor_path (bm, head); 00441 setbbox_path (&bbox, head); 00442 00443 /* now do insideness test for each element of cur; append it to 00444 head->childlist if it's inside head, else append it to 00445 head->next. */ 00446 hook_in = &head->childlist; 00447 hook_out = &head->next; 00448 list_forall_unlink (p, cur) 00449 { 00450 if (p->priv->pt[0].y <= bbox.y0) 00451 { 00452 list_insert_beforehook (p, hook_out); 00453 /* append the remainder of the list to hook_out */ 00454 *hook_out = cur; 00455 break; 00456 } 00457 if (BM_GET (bm, p->priv->pt[0].x, p->priv->pt[0].y - 1)) 00458 { 00459 list_insert_beforehook (p, hook_in); 00460 } 00461 else 00462 { 00463 list_insert_beforehook (p, hook_out); 00464 } 00465 } 00466 00467 /* clear bm */ 00468 clear_bm_with_bbox (bm, &bbox); 00469 00470 /* now schedule head->childlist and head->next for further 00471 processing */ 00472 if (head->next) 00473 { 00474 head->next->childlist = heap; 00475 heap = head->next; 00476 } 00477 if (head->childlist) 00478 { 00479 head->childlist->childlist = heap; 00480 heap = head->childlist; 00481 } 00482 } 00483 00484 /* copy sibling structure from "next" to "sibling" component */ 00485 p = plist; 00486 while (p) 00487 { 00488 p1 = p->sibling; 00489 p->sibling = p->next; 00490 p = p1; 00491 } 00492 00493 /* reconstruct a new linked list ("next") structure from tree 00494 ("childlist", "sibling") structure. This code is slightly messy, 00495 because we use a heap to make it tail recursive: the heap 00496 contains a list of childlists which still need to be 00497 processed. */ 00498 heap = plist; 00499 if (heap) 00500 { 00501 heap->next = NULL; /* heap is a linked list of childlists */ 00502 } 00503 plist = NULL; 00504 hook = &plist; 00505 while (heap) 00506 { 00507 heap1 = heap->next; 00508 for (p = heap; p; p = p->sibling) 00509 { 00510 /* p is a positive path */ 00511 /* append to linked list */ 00512 list_insert_beforehook (p, hook); 00513 00514 /* go through its children */ 00515 for (p1 = p->childlist; p1; p1 = p1->sibling) 00516 { 00517 /* append to linked list */ 00518 list_insert_beforehook (p1, hook); 00519 /* append its childlist to heap, if non-empty */ 00520 if (p1->childlist) 00521 { 00522 list_append (path_t, heap1, p1->childlist); 00523 } 00524 } 00525 } 00526 heap = heap1; 00527 } 00528 00529 return; 00530 } 00531 00542 static int 00543 findnext (potrace_bitmap_t * bm, int *xp, int *yp) 00544 { 00545 int x; 00546 int y; 00547 00548 for (y = *yp; y >= 0; y--) 00549 { 00550 for (x = 0; x < bm->w; x += BM_WORDBITS) 00551 { 00552 if (*bm_index (bm, x, y)) 00553 { 00554 while (!BM_GET (bm, x, y)) 00555 { 00556 x++; 00557 } 00558 /* found */ 00559 *xp = x; 00560 *yp = y; 00561 return 0; 00562 } 00563 } 00564 } 00565 /* not found */ 00566 return 1; 00567 } 00568 00577 int 00578 bm_to_pathlist (const potrace_bitmap_t * bm, path_t ** plistp, 00579 const potrace_param_t * param) 00580 { 00581 int x; 00582 int y; 00583 path_t *p; 00584 path_t *plist = NULL; /* linked list of path objects */ 00585 path_t **hook = &plist; /* used to speed up appending to linked list */ 00586 potrace_bitmap_t *bm1 = NULL; 00587 int sign; 00588 00589 bm1 = bm_dup (bm); 00590 if (!bm1) 00591 { 00592 goto error; 00593 } 00594 00595 /* be sure the byte padding on the right is set to 0, as the fast 00596 pixel search below relies on it */ 00597 bm_clearexcess (bm1); 00598 00599 /* iterate through components */ 00600 y = bm1->h - 1; 00601 while (findnext (bm1, &x, &y) == 0) 00602 { 00603 /* calculate the sign by looking at the original */ 00604 sign = BM_GET (bm, x, y) ? '+' : '-'; 00605 00606 /* calculate the path */ 00607 p = findpath (bm1, x, y + 1, sign, param->turnpolicy); 00608 if (p == NULL) 00609 { 00610 goto error; 00611 } 00612 00613 /* update buffered image */ 00614 xor_path (bm1, p); 00615 00616 /* if it's a turd, eliminate it, else append it to the list */ 00617 if (p->area <= param->turdsize) 00618 { 00619 path_free (p); 00620 } 00621 else 00622 { 00623 list_insert_beforehook (p, hook); 00624 } 00625 00626 if (bm1->h > 0) 00627 { /* to be sure */ 00628 //progress_update(1-y/(double)bm1->h, progress); 00629 } 00630 } 00631 00632 pathlist_to_tree (plist, bm1); 00633 bm_free (bm1); 00634 *plistp = plist; 00635 00636 // progress_update(1.0, progress); 00637 00638 return 0; 00639 00640 error: 00641 bm_free (bm1); 00642 list_forall_unlink (p, plist) 00643 { 00644 path_free (p); 00645 } 00646 return -1; 00647 }