|
tesseract 3.04.01
|
00001 /********************************************************************** 00002 * File: coutln.c (Formerly coutline.c) 00003 * Description: Code for the C_OUTLINE class. 00004 * Author: Ray Smith 00005 * Created: Mon Oct 07 16:01:57 BST 1991 00006 * 00007 * (C) Copyright 1991, Hewlett-Packard Ltd. 00008 ** Licensed under the Apache License, Version 2.0 (the "License"); 00009 ** you may not use this file except in compliance with the License. 00010 ** You may obtain a copy of the License at 00011 ** http://www.apache.org/licenses/LICENSE-2.0 00012 ** Unless required by applicable law or agreed to in writing, software 00013 ** distributed under the License is distributed on an "AS IS" BASIS, 00014 ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 00015 ** See the License for the specific language governing permissions and 00016 ** limitations under the License. 00017 * 00018 **********************************************************************/ 00019 00020 #include <string.h> 00021 #ifdef __UNIX__ 00022 #include <assert.h> 00023 #endif 00024 00025 #include "coutln.h" 00026 00027 #include "allheaders.h" 00028 #include "blobs.h" 00029 #include "normalis.h" 00030 00031 // Include automatically generated configuration file if running autoconf. 00032 #ifdef HAVE_CONFIG_H 00033 #include "config_auto.h" 00034 #endif 00035 00036 ELISTIZE (C_OUTLINE) 00037 ICOORD C_OUTLINE::step_coords[4] = { 00038 ICOORD (-1, 0), ICOORD (0, -1), ICOORD (1, 0), ICOORD (0, 1) 00039 }; 00040 00051 C_OUTLINE::C_OUTLINE (CRACKEDGE * startpt, ICOORD bot_left, 00052 ICOORD top_right, inT16 length) 00053 : box (bot_left, top_right), start (startpt->pos), offsets(NULL) { 00054 inT16 stepindex; //index to step 00055 CRACKEDGE *edgept; //current point 00056 00057 stepcount = length; //no of steps 00058 if (length == 0) { 00059 steps = NULL; 00060 return; 00061 } 00062 //get memory 00063 steps = (uinT8 *) alloc_mem (step_mem()); 00064 memset(steps, 0, step_mem()); 00065 edgept = startpt; 00066 00067 for (stepindex = 0; stepindex < length; stepindex++) { 00068 //set compact step 00069 set_step (stepindex, edgept->stepdir); 00070 edgept = edgept->next; 00071 } 00072 } 00073 00074 00080 C_OUTLINE::C_OUTLINE ( 00081 //constructor 00082 //steps to copy 00083 ICOORD startpt, DIR128 * new_steps, 00084 inT16 length //length of loop 00085 ):start (startpt), offsets(NULL) { 00086 inT8 dirdiff; //direction difference 00087 DIR128 prevdir; //previous direction 00088 DIR128 dir; //current direction 00089 DIR128 lastdir; //dir of last step 00090 TBOX new_box; //easy bounding 00091 inT16 stepindex; //index to step 00092 inT16 srcindex; //source steps 00093 ICOORD pos; //current position 00094 00095 pos = startpt; 00096 stepcount = length; // No. of steps. 00097 ASSERT_HOST(length >= 0); 00098 steps = reinterpret_cast<uinT8*>(alloc_mem(step_mem())); // Get memory. 00099 memset(steps, 0, step_mem()); 00100 00101 lastdir = new_steps[length - 1]; 00102 prevdir = lastdir; 00103 for (stepindex = 0, srcindex = 0; srcindex < length; 00104 stepindex++, srcindex++) { 00105 new_box = TBOX (pos, pos); 00106 box += new_box; 00107 //copy steps 00108 dir = new_steps[srcindex]; 00109 set_step(stepindex, dir); 00110 dirdiff = dir - prevdir; 00111 pos += step (stepindex); 00112 if ((dirdiff == 64 || dirdiff == -64) && stepindex > 0) { 00113 stepindex -= 2; //cancel there-and-back 00114 prevdir = stepindex >= 0 ? step_dir (stepindex) : lastdir; 00115 } 00116 else 00117 prevdir = dir; 00118 } 00119 ASSERT_HOST (pos.x () == startpt.x () && pos.y () == startpt.y ()); 00120 do { 00121 dirdiff = step_dir (stepindex - 1) - step_dir (0); 00122 if (dirdiff == 64 || dirdiff == -64) { 00123 start += step (0); 00124 stepindex -= 2; //cancel there-and-back 00125 for (int i = 0; i < stepindex; ++i) 00126 set_step(i, step_dir(i + 1)); 00127 } 00128 } 00129 while (stepindex > 1 && (dirdiff == 64 || dirdiff == -64)); 00130 stepcount = stepindex; 00131 ASSERT_HOST (stepcount >= 4); 00132 } 00133 00142 C_OUTLINE::C_OUTLINE(C_OUTLINE *srcline, FCOORD rotation) : offsets(NULL) { 00143 TBOX new_box; //easy bounding 00144 inT16 stepindex; //index to step 00145 inT16 dirdiff; //direction change 00146 ICOORD pos; //current position 00147 ICOORD prevpos; //previous dest point 00148 00149 ICOORD destpos; //destination point 00150 inT16 destindex; //index to step 00151 DIR128 dir; //coded direction 00152 uinT8 new_step; 00153 00154 stepcount = srcline->stepcount * 2; 00155 if (stepcount == 0) { 00156 steps = NULL; 00157 box = srcline->box; 00158 box.rotate(rotation); 00159 return; 00160 } 00161 //get memory 00162 steps = (uinT8 *) alloc_mem (step_mem()); 00163 memset(steps, 0, step_mem()); 00164 00165 for (int iteration = 0; iteration < 2; ++iteration) { 00166 DIR128 round1 = iteration == 0 ? 32 : 0; 00167 DIR128 round2 = iteration != 0 ? 32 : 0; 00168 pos = srcline->start; 00169 prevpos = pos; 00170 prevpos.rotate (rotation); 00171 start = prevpos; 00172 box = TBOX (start, start); 00173 destindex = 0; 00174 for (stepindex = 0; stepindex < srcline->stepcount; stepindex++) { 00175 pos += srcline->step (stepindex); 00176 destpos = pos; 00177 destpos.rotate (rotation); 00178 // tprintf("%i %i %i %i ", destpos.x(), destpos.y(), pos.x(), pos.y()); 00179 while (destpos.x () != prevpos.x () || destpos.y () != prevpos.y ()) { 00180 dir = DIR128 (FCOORD (destpos - prevpos)); 00181 dir += 64; //turn to step style 00182 new_step = dir.get_dir (); 00183 // tprintf(" %i\n", new_step); 00184 if (new_step & 31) { 00185 set_step(destindex++, dir + round1); 00186 prevpos += step(destindex - 1); 00187 if (destindex < 2 00188 || ((dirdiff = 00189 step_dir (destindex - 1) - step_dir (destindex - 2)) != 00190 -64 && dirdiff != 64)) { 00191 set_step(destindex++, dir + round2); 00192 prevpos += step(destindex - 1); 00193 } else { 00194 prevpos -= step(destindex - 1); 00195 destindex--; 00196 prevpos -= step(destindex - 1); 00197 set_step(destindex - 1, dir + round2); 00198 prevpos += step(destindex - 1); 00199 } 00200 } 00201 else { 00202 set_step(destindex++, dir); 00203 prevpos += step(destindex - 1); 00204 } 00205 while (destindex >= 2 && 00206 ((dirdiff = 00207 step_dir (destindex - 1) - step_dir (destindex - 2)) == -64 || 00208 dirdiff == 64)) { 00209 prevpos -= step(destindex - 1); 00210 prevpos -= step(destindex - 2); 00211 destindex -= 2; // Forget u turn 00212 } 00213 //ASSERT_HOST(prevpos.x() == destpos.x() && prevpos.y() == destpos.y()); 00214 new_box = TBOX (destpos, destpos); 00215 box += new_box; 00216 } 00217 } 00218 ASSERT_HOST (destpos.x () == start.x () && destpos.y () == start.y ()); 00219 dirdiff = step_dir (destindex - 1) - step_dir (0); 00220 while ((dirdiff == 64 || dirdiff == -64) && destindex > 1) { 00221 start += step (0); 00222 destindex -= 2; 00223 for (int i = 0; i < destindex; ++i) 00224 set_step(i, step_dir(i + 1)); 00225 dirdiff = step_dir (destindex - 1) - step_dir (0); 00226 } 00227 if (destindex >= 4) 00228 break; 00229 } 00230 ASSERT_HOST(destindex <= stepcount); 00231 stepcount = destindex; 00232 destpos = start; 00233 for (stepindex = 0; stepindex < stepcount; stepindex++) { 00234 destpos += step (stepindex); 00235 } 00236 ASSERT_HOST (destpos.x () == start.x () && destpos.y () == start.y ()); 00237 } 00238 00239 // Build a fake outline, given just a bounding box and append to the list. 00240 void C_OUTLINE::FakeOutline(const TBOX& box, C_OUTLINE_LIST* outlines) { 00241 C_OUTLINE_IT ol_it(outlines); 00242 // Make a C_OUTLINE from the bounds. This is a bit of a hack, 00243 // as there is no outline, just a bounding box, but it works nicely. 00244 CRACKEDGE start; 00245 start.pos = box.topleft(); 00246 C_OUTLINE* outline = new C_OUTLINE(&start, box.topleft(), box.botright(), 0); 00247 ol_it.add_to_end(outline); 00248 } 00249 00256 inT32 C_OUTLINE::area() const { 00257 int stepindex; //current step 00258 inT32 total_steps; //steps to do 00259 inT32 total; //total area 00260 ICOORD pos; //position of point 00261 ICOORD next_step; //step to next pix 00262 // We aren't going to modify the list, or its contents, but there is 00263 // no const iterator. 00264 C_OUTLINE_IT it(const_cast<C_OUTLINE_LIST*>(&children)); 00265 00266 pos = start_pos (); 00267 total_steps = pathlength (); 00268 total = 0; 00269 for (stepindex = 0; stepindex < total_steps; stepindex++) { 00270 //all intersected 00271 next_step = step (stepindex); 00272 if (next_step.x () < 0) 00273 total += pos.y (); 00274 else if (next_step.x () > 0) 00275 total -= pos.y (); 00276 pos += next_step; 00277 } 00278 for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) 00279 total += it.data ()->area ();//add areas of children 00280 00281 return total; 00282 } 00283 00290 inT32 C_OUTLINE::perimeter() const { 00291 inT32 total_steps; // Return value. 00292 // We aren't going to modify the list, or its contents, but there is 00293 // no const iterator. 00294 C_OUTLINE_IT it(const_cast<C_OUTLINE_LIST*>(&children)); 00295 00296 total_steps = pathlength(); 00297 for (it.mark_cycle_pt(); !it.cycled_list(); it.forward()) 00298 total_steps += it.data()->pathlength(); // Add perimeters of children. 00299 00300 return total_steps; 00301 } 00302 00303 00310 inT32 C_OUTLINE::outer_area() const { 00311 int stepindex; //current step 00312 inT32 total_steps; //steps to do 00313 inT32 total; //total area 00314 ICOORD pos; //position of point 00315 ICOORD next_step; //step to next pix 00316 00317 pos = start_pos (); 00318 total_steps = pathlength (); 00319 if (total_steps == 0) 00320 return box.area(); 00321 total = 0; 00322 for (stepindex = 0; stepindex < total_steps; stepindex++) { 00323 //all intersected 00324 next_step = step (stepindex); 00325 if (next_step.x () < 0) 00326 total += pos.y (); 00327 else if (next_step.x () > 0) 00328 total -= pos.y (); 00329 pos += next_step; 00330 } 00331 00332 return total; 00333 } 00334 00335 00343 inT32 C_OUTLINE::count_transitions(inT32 threshold) { 00344 BOOL8 first_was_max_x; //what was first 00345 BOOL8 first_was_max_y; 00346 BOOL8 looking_for_max_x; //what is next 00347 BOOL8 looking_for_min_x; 00348 BOOL8 looking_for_max_y; //what is next 00349 BOOL8 looking_for_min_y; 00350 int stepindex; //current step 00351 inT32 total_steps; //steps to do 00352 //current limits 00353 inT32 max_x, min_x, max_y, min_y; 00354 inT32 initial_x, initial_y; //initial limits 00355 inT32 total; //total changes 00356 ICOORD pos; //position of point 00357 ICOORD next_step; //step to next pix 00358 00359 pos = start_pos (); 00360 total_steps = pathlength (); 00361 total = 0; 00362 max_x = min_x = pos.x (); 00363 max_y = min_y = pos.y (); 00364 looking_for_max_x = TRUE; 00365 looking_for_min_x = TRUE; 00366 looking_for_max_y = TRUE; 00367 looking_for_min_y = TRUE; 00368 first_was_max_x = FALSE; 00369 first_was_max_y = FALSE; 00370 initial_x = pos.x (); 00371 initial_y = pos.y (); //stop uninit warning 00372 for (stepindex = 0; stepindex < total_steps; stepindex++) { 00373 //all intersected 00374 next_step = step (stepindex); 00375 pos += next_step; 00376 if (next_step.x () < 0) { 00377 if (looking_for_max_x && pos.x () < min_x) 00378 min_x = pos.x (); 00379 if (looking_for_min_x && max_x - pos.x () > threshold) { 00380 if (looking_for_max_x) { 00381 initial_x = max_x; 00382 first_was_max_x = FALSE; 00383 } 00384 total++; 00385 looking_for_max_x = TRUE; 00386 looking_for_min_x = FALSE; 00387 min_x = pos.x (); //reset min 00388 } 00389 } 00390 else if (next_step.x () > 0) { 00391 if (looking_for_min_x && pos.x () > max_x) 00392 max_x = pos.x (); 00393 if (looking_for_max_x && pos.x () - min_x > threshold) { 00394 if (looking_for_min_x) { 00395 initial_x = min_x; //remember first min 00396 first_was_max_x = TRUE; 00397 } 00398 total++; 00399 looking_for_max_x = FALSE; 00400 looking_for_min_x = TRUE; 00401 max_x = pos.x (); 00402 } 00403 } 00404 else if (next_step.y () < 0) { 00405 if (looking_for_max_y && pos.y () < min_y) 00406 min_y = pos.y (); 00407 if (looking_for_min_y && max_y - pos.y () > threshold) { 00408 if (looking_for_max_y) { 00409 initial_y = max_y; //remember first max 00410 first_was_max_y = FALSE; 00411 } 00412 total++; 00413 looking_for_max_y = TRUE; 00414 looking_for_min_y = FALSE; 00415 min_y = pos.y (); //reset min 00416 } 00417 } 00418 else { 00419 if (looking_for_min_y && pos.y () > max_y) 00420 max_y = pos.y (); 00421 if (looking_for_max_y && pos.y () - min_y > threshold) { 00422 if (looking_for_min_y) { 00423 initial_y = min_y; //remember first min 00424 first_was_max_y = TRUE; 00425 } 00426 total++; 00427 looking_for_max_y = FALSE; 00428 looking_for_min_y = TRUE; 00429 max_y = pos.y (); 00430 } 00431 } 00432 00433 } 00434 if (first_was_max_x && looking_for_min_x) { 00435 if (max_x - initial_x > threshold) 00436 total++; 00437 else 00438 total--; 00439 } 00440 else if (!first_was_max_x && looking_for_max_x) { 00441 if (initial_x - min_x > threshold) 00442 total++; 00443 else 00444 total--; 00445 } 00446 if (first_was_max_y && looking_for_min_y) { 00447 if (max_y - initial_y > threshold) 00448 total++; 00449 else 00450 total--; 00451 } 00452 else if (!first_was_max_y && looking_for_max_y) { 00453 if (initial_y - min_y > threshold) 00454 total++; 00455 else 00456 total--; 00457 } 00458 00459 return total; 00460 } 00461 00462 00470 BOOL8 00471 C_OUTLINE::operator< (const C_OUTLINE & other) const 00472 { 00473 inT16 count = 0; //winding count 00474 ICOORD pos; //position of point 00475 inT32 stepindex; //index to cstep 00476 00477 if (!box.overlap (other.box)) 00478 return FALSE; //can't be contained 00479 if (stepcount == 0) 00480 return other.box.contains(this->box); 00481 00482 pos = start; 00483 for (stepindex = 0; stepindex < stepcount 00484 && (count = other.winding_number (pos)) == INTERSECTING; stepindex++) 00485 pos += step (stepindex); //try all points 00486 if (count == INTERSECTING) { 00487 //all intersected 00488 pos = other.start; 00489 for (stepindex = 0; stepindex < other.stepcount 00490 && (count = winding_number (pos)) == INTERSECTING; stepindex++) 00491 //try other way round 00492 pos += other.step (stepindex); 00493 return count == INTERSECTING || count == 0; 00494 } 00495 return count != 0; 00496 } 00497 00498 00506 inT16 C_OUTLINE::winding_number(ICOORD point) const { 00507 inT16 stepindex; //index to cstep 00508 inT16 count; //winding count 00509 ICOORD vec; //to current point 00510 ICOORD stepvec; //step vector 00511 inT32 cross; //cross product 00512 00513 vec = start - point; //vector to it 00514 count = 0; 00515 for (stepindex = 0; stepindex < stepcount; stepindex++) { 00516 stepvec = step (stepindex); //get the step 00517 //crossing the line 00518 if (vec.y () <= 0 && vec.y () + stepvec.y () > 0) { 00519 cross = vec * stepvec; //cross product 00520 if (cross > 0) 00521 count++; //crossing right half 00522 else if (cross == 0) 00523 return INTERSECTING; //going through point 00524 } 00525 else if (vec.y () > 0 && vec.y () + stepvec.y () <= 0) { 00526 cross = vec * stepvec; 00527 if (cross < 0) 00528 count--; //crossing back 00529 else if (cross == 0) 00530 return INTERSECTING; //illegal 00531 } 00532 vec += stepvec; //sum vectors 00533 } 00534 return count; //winding number 00535 } 00536 00537 00544 inT16 C_OUTLINE::turn_direction() const { //winding number 00545 DIR128 prevdir; //previous direction 00546 DIR128 dir; //current direction 00547 inT16 stepindex; //index to cstep 00548 inT8 dirdiff; //direction difference 00549 inT16 count; //winding count 00550 00551 if (stepcount == 0) 00552 return 128; 00553 count = 0; 00554 prevdir = step_dir (stepcount - 1); 00555 for (stepindex = 0; stepindex < stepcount; stepindex++) { 00556 dir = step_dir (stepindex); 00557 dirdiff = dir - prevdir; 00558 ASSERT_HOST (dirdiff == 0 || dirdiff == 32 || dirdiff == -32); 00559 count += dirdiff; 00560 prevdir = dir; 00561 } 00562 ASSERT_HOST (count == 128 || count == -128); 00563 return count; //winding number 00564 } 00565 00566 00573 void C_OUTLINE::reverse() { //reverse drection 00574 DIR128 halfturn = MODULUS / 2; //amount to shift 00575 DIR128 stepdir; //direction of step 00576 inT16 stepindex; //index to cstep 00577 inT16 farindex; //index to other side 00578 inT16 halfsteps; //half of stepcount 00579 00580 halfsteps = (stepcount + 1) / 2; 00581 for (stepindex = 0; stepindex < halfsteps; stepindex++) { 00582 farindex = stepcount - stepindex - 1; 00583 stepdir = step_dir (stepindex); 00584 set_step (stepindex, step_dir (farindex) + halfturn); 00585 set_step (farindex, stepdir + halfturn); 00586 } 00587 } 00588 00589 00597 void C_OUTLINE::move(const ICOORD vec) { 00598 C_OUTLINE_IT it(&children); // iterator 00599 00600 box.move (vec); 00601 start += vec; 00602 00603 for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) 00604 it.data ()->move (vec); // move child outlines 00605 } 00606 00613 bool C_OUTLINE::IsLegallyNested() const { 00614 if (stepcount == 0) return true; 00615 int parent_area = outer_area(); 00616 // We aren't going to modify the list, or its contents, but there is 00617 // no const iterator. 00618 C_OUTLINE_IT child_it(const_cast<C_OUTLINE_LIST*>(&children)); 00619 for (child_it.mark_cycle_pt(); !child_it.cycled_list(); child_it.forward()) { 00620 const C_OUTLINE* child = child_it.data(); 00621 if (child->outer_area() * parent_area > 0 || !child->IsLegallyNested()) 00622 return false; 00623 } 00624 return true; 00625 } 00626 00636 void C_OUTLINE::RemoveSmallRecursive(int min_size, C_OUTLINE_IT* it) { 00637 if (box.width() < min_size || box.height() < min_size) { 00638 ASSERT_HOST(this == it->data()); 00639 delete it->extract(); // Too small so get rid of it and any children. 00640 } else if (!children.empty()) { 00641 // Search the children of this, deleting any that are too small. 00642 C_OUTLINE_IT child_it(&children); 00643 for (child_it.mark_cycle_pt(); !child_it.cycled_list(); 00644 child_it.forward()) { 00645 C_OUTLINE* child = child_it.data(); 00646 child->RemoveSmallRecursive(min_size, &child_it); 00647 } 00648 } 00649 } 00650 00651 // Factored out helpers below are used only by ComputeEdgeOffsets to operate 00652 // on data from an 8-bit Pix, and assume that any input x and/or y are already 00653 // constrained to be legal Pix coordinates. 00654 00660 static void ComputeGradient(const l_uint32* data, int wpl, 00661 int x, int y, int width, int height, 00662 ICOORD* gradient) { 00663 const l_uint32* line = data + y * wpl; 00664 int pix_x_y = x < width && y < height ? 00665 GET_DATA_BYTE(const_cast<void*> (reinterpret_cast<const void *>(line)), x) : 255; 00666 int pix_x_prevy = x < width && y > 0 ? 00667 GET_DATA_BYTE(const_cast<void*> (reinterpret_cast<const void *>(line - wpl)), x) : 255; 00668 int pix_prevx_prevy = x > 0 && y > 0 ? 00669 GET_DATA_BYTE(const_cast<void*> (reinterpret_cast<void const*>(line - wpl)), x - 1) : 255; 00670 int pix_prevx_y = x > 0 && y < height ? 00671 GET_DATA_BYTE(const_cast<void*> (reinterpret_cast<const void *>(line)), x - 1) : 255; 00672 gradient->set_x(pix_x_y + pix_x_prevy - (pix_prevx_y + pix_prevx_prevy)); 00673 gradient->set_y(pix_x_prevy + pix_prevx_prevy - (pix_x_y + pix_prevx_y)); 00674 } 00675 00681 static bool EvaluateVerticalDiff(const l_uint32* data, int wpl, int diff_sign, 00682 int x, int y, int height, 00683 int* best_diff, int* best_sum, int* best_y) { 00684 if (y <= 0 || y >= height) 00685 return false; 00686 const l_uint32* line = data + y * wpl; 00687 int pixel1 = GET_DATA_BYTE(const_cast<void*> (reinterpret_cast<const void *>(line - wpl)), x); 00688 int pixel2 = GET_DATA_BYTE(const_cast<void*> (reinterpret_cast<const void *>(line)), x); 00689 int diff = (pixel2 - pixel1) * diff_sign; 00690 if (diff > *best_diff) { 00691 *best_diff = diff; 00692 *best_sum = pixel1 + pixel2; 00693 *best_y = y; 00694 } 00695 return diff > 0; 00696 } 00697 00703 static bool EvaluateHorizontalDiff(const l_uint32* line, int diff_sign, 00704 int x, int width, 00705 int* best_diff, int* best_sum, int* best_x) { 00706 if (x <= 0 || x >= width) 00707 return false; 00708 int pixel1 = GET_DATA_BYTE(const_cast<void*> (reinterpret_cast<const void *>(line)), x - 1); 00709 int pixel2 = GET_DATA_BYTE(const_cast<void*> (reinterpret_cast<const void *>(line)), x); 00710 int diff = (pixel2 - pixel1) * diff_sign; 00711 if (diff > *best_diff) { 00712 *best_diff = diff; 00713 *best_sum = pixel1 + pixel2; 00714 *best_x = x; 00715 } 00716 return diff > 0; 00717 } 00718 00734 void C_OUTLINE::ComputeEdgeOffsets(int threshold, Pix* pix) { 00735 if (pixGetDepth(pix) != 8) return; 00736 const l_uint32* data = pixGetData(pix); 00737 int wpl = pixGetWpl(pix); 00738 int width = pixGetWidth(pix); 00739 int height = pixGetHeight(pix); 00740 bool negative = flag(COUT_INVERSE); 00741 delete [] offsets; 00742 offsets = new EdgeOffset[stepcount]; 00743 ICOORD pos = start; 00744 ICOORD prev_gradient; 00745 ComputeGradient(data, wpl, pos.x(), height - pos.y(), width, height, 00746 &prev_gradient); 00747 for (int s = 0; s < stepcount; ++s) { 00748 ICOORD step_vec = step(s); 00749 TPOINT pt1(pos); 00750 pos += step_vec; 00751 TPOINT pt2(pos); 00752 ICOORD next_gradient; 00753 ComputeGradient(data, wpl, pos.x(), height - pos.y(), width, height, 00754 &next_gradient); 00755 // Use the sum of the prev and next as the working gradient. 00756 ICOORD gradient = prev_gradient + next_gradient; 00757 // best_diff will be manipulated to be always positive. 00758 int best_diff = 0; 00759 // offset will be the extrapolation of the location of the greyscale 00760 // threshold from the edge with the largest difference, relative to the 00761 // location of the binary edge. 00762 int offset = 0; 00763 if (pt1.y == pt2.y && abs(gradient.y()) * 2 >= abs(gradient.x())) { 00764 // Horizontal step. diff_sign == 1 indicates black above. 00765 int diff_sign = (pt1.x > pt2.x) == negative ? 1 : -1; 00766 int x = MIN(pt1.x, pt2.x); 00767 int y = height - pt1.y; 00768 int best_sum = 0; 00769 int best_y = y; 00770 EvaluateVerticalDiff(data, wpl, diff_sign, x, y, height, 00771 &best_diff, &best_sum, &best_y); 00772 // Find the strongest edge. 00773 int test_y = y; 00774 do { 00775 ++test_y; 00776 } while (EvaluateVerticalDiff(data, wpl, diff_sign, x, test_y, height, 00777 &best_diff, &best_sum, &best_y)); 00778 test_y = y; 00779 do { 00780 --test_y; 00781 } while (EvaluateVerticalDiff(data, wpl, diff_sign, x, test_y, height, 00782 &best_diff, &best_sum, &best_y)); 00783 offset = diff_sign * (best_sum / 2 - threshold) + 00784 (y - best_y) * best_diff; 00785 } else if (pt1.x == pt2.x && abs(gradient.x()) * 2 >= abs(gradient.y())) { 00786 // Vertical step. diff_sign == 1 indicates black on the left. 00787 int diff_sign = (pt1.y > pt2.y) == negative ? 1 : -1; 00788 int x = pt1.x; 00789 int y = height - MAX(pt1.y, pt2.y); 00790 const l_uint32* line = pixGetData(pix) + y * wpl; 00791 int best_sum = 0; 00792 int best_x = x; 00793 EvaluateHorizontalDiff(line, diff_sign, x, width, 00794 &best_diff, &best_sum, &best_x); 00795 // Find the strongest edge. 00796 int test_x = x; 00797 do { 00798 ++test_x; 00799 } while (EvaluateHorizontalDiff(line, diff_sign, test_x, width, 00800 &best_diff, &best_sum, &best_x)); 00801 test_x = x; 00802 do { 00803 --test_x; 00804 } while (EvaluateHorizontalDiff(line, diff_sign, test_x, width, 00805 &best_diff, &best_sum, &best_x)); 00806 offset = diff_sign * (threshold - best_sum / 2) + 00807 (best_x - x) * best_diff; 00808 } 00809 offsets[s].offset_numerator = 00810 static_cast<inT8>(ClipToRange(offset, -MAX_INT8, MAX_INT8)); 00811 offsets[s].pixel_diff = static_cast<uinT8>(ClipToRange(best_diff, 0 , 00812 MAX_UINT8)); 00813 if (negative) gradient = -gradient; 00814 // Compute gradient angle quantized to 256 directions, rotated by 64 (pi/2) 00815 // to convert from gradient direction to edge direction. 00816 offsets[s].direction = 00817 Modulo(FCOORD::binary_angle_plus_pi(gradient.angle()) + 64, 256); 00818 prev_gradient = next_gradient; 00819 } 00820 } 00821 00851 void C_OUTLINE::ComputeBinaryOffsets() { 00852 delete [] offsets; 00853 offsets = new EdgeOffset[stepcount]; 00854 // Count of the number of steps in each direction in the sliding window. 00855 int dir_counts[4]; 00856 // Sum of the positions (y for a horizontal step, x for vertical) in each 00857 // direction in the sliding window. 00858 int pos_totals[4]; 00859 memset(dir_counts, 0, sizeof(dir_counts)); 00860 memset(pos_totals, 0, sizeof(pos_totals)); 00861 ICOORD pos = start; 00862 ICOORD tail_pos = pos; 00863 // tail_pos is the trailing position, with the next point to be lost from 00864 // the window. 00865 tail_pos -= step(stepcount - 1); 00866 tail_pos -= step(stepcount - 2); 00867 // head_pos is the leading position, with the next point to be added to the 00868 // window. 00869 ICOORD head_pos = tail_pos; 00870 // Set up the initial window with 4 points in [-2, 2) 00871 for (int s = -2; s < 2; ++s) { 00872 increment_step(s, 1, &head_pos, dir_counts, pos_totals); 00873 } 00874 for (int s = 0; s < stepcount; pos += step(s++)) { 00875 // At step s, s in in the middle of [s-2, s+2]. 00876 increment_step(s + 2, 1, &head_pos, dir_counts, pos_totals); 00877 int dir_index = chain_code(s); 00878 ICOORD step_vec = step(s); 00879 int best_diff = 0; 00880 int offset = 0; 00881 // Use only steps that have a count of >=2 OR the strong U-turn with a 00882 // single d and 2 at d-1 and 2 at d+1 (mod 4). 00883 if (dir_counts[dir_index] >= 2 || (dir_counts[dir_index] == 1 && 00884 dir_counts[Modulo(dir_index - 1, 4)] == 2 && 00885 dir_counts[Modulo(dir_index + 1, 4)] == 2)) { 00886 // Valid step direction. 00887 best_diff = dir_counts[dir_index]; 00888 int edge_pos = step_vec.x() == 0 ? pos.x() : pos.y(); 00889 // The offset proposes that the actual step should be positioned at 00890 // the mean position of the steps in the window of the same direction. 00891 // See ASCII art above. 00892 offset = pos_totals[dir_index] - best_diff * edge_pos; 00893 } 00894 offsets[s].offset_numerator = 00895 static_cast<inT8>(ClipToRange(offset, -MAX_INT8, MAX_INT8)); 00896 offsets[s].pixel_diff = static_cast<uinT8>(ClipToRange(best_diff, 0 , 00897 MAX_UINT8)); 00898 // The direction is just the vector from start to end of the window. 00899 FCOORD direction(head_pos.x() - tail_pos.x(), head_pos.y() - tail_pos.y()); 00900 offsets[s].direction = direction.to_direction(); 00901 increment_step(s - 2, -1, &tail_pos, dir_counts, pos_totals); 00902 } 00903 } 00904 00909 void C_OUTLINE::render(int left, int top, Pix* pix) const { 00910 ICOORD pos = start; 00911 for (int stepindex = 0; stepindex < stepcount; ++stepindex) { 00912 ICOORD next_step = step(stepindex); 00913 if (next_step.y() < 0) { 00914 pixRasterop(pix, 0, top - pos.y(), pos.x() - left, 1, 00915 PIX_NOT(PIX_DST), NULL, 0, 0); 00916 } else if (next_step.y() > 0) { 00917 pixRasterop(pix, 0, top - pos.y() - 1, pos.x() - left, 1, 00918 PIX_NOT(PIX_DST), NULL, 0, 0); 00919 } 00920 pos += next_step; 00921 } 00922 } 00923 00931 void C_OUTLINE::render_outline(int left, int top, Pix* pix) const { 00932 ICOORD pos = start; 00933 for (int stepindex = 0; stepindex < stepcount; ++stepindex) { 00934 ICOORD next_step = step(stepindex); 00935 if (next_step.y() < 0) { 00936 pixSetPixel(pix, pos.x() - left, top - pos.y(), 1); 00937 } else if (next_step.y() > 0) { 00938 pixSetPixel(pix, pos.x() - left - 1, top - pos.y() - 1, 1); 00939 } else if (next_step.x() < 0) { 00940 pixSetPixel(pix, pos.x() - left - 1, top - pos.y(), 1); 00941 } else if (next_step.x() > 0) { 00942 pixSetPixel(pix, pos.x() - left, top - pos.y() - 1, 1); 00943 } 00944 pos += next_step; 00945 } 00946 } 00947 00956 #ifndef GRAPHICS_DISABLED 00957 void C_OUTLINE::plot(ScrollView* window, 00958 ScrollView::Color colour) const { 00959 inT16 stepindex; // index to cstep 00960 ICOORD pos; // current position 00961 DIR128 stepdir; // direction of step 00962 00963 pos = start; // current position 00964 window->Pen(colour); 00965 if (stepcount == 0) { 00966 window->Rectangle(box.left(), box.top(), box.right(), box.bottom()); 00967 return; 00968 } 00969 window->SetCursor(pos.x(), pos.y()); 00970 00971 stepindex = 0; 00972 while (stepindex < stepcount) { 00973 pos += step(stepindex); // step to next 00974 stepdir = step_dir(stepindex); 00975 stepindex++; // count steps 00976 // merge straight lines 00977 while (stepindex < stepcount && 00978 stepdir.get_dir() == step_dir(stepindex).get_dir()) { 00979 pos += step(stepindex); 00980 stepindex++; 00981 } 00982 window->DrawTo(pos.x(), pos.y()); 00983 } 00984 } 00985 00990 void C_OUTLINE::plot_normed(const DENORM& denorm, ScrollView::Color colour, 00991 ScrollView* window) const { 00992 window->Pen(colour); 00993 if (stepcount == 0) { 00994 window->Rectangle(box.left(), box.top(), box.right(), box.bottom()); 00995 return; 00996 } 00997 const DENORM* root_denorm = denorm.RootDenorm(); 00998 ICOORD pos = start; // current position 00999 FCOORD f_pos = sub_pixel_pos_at_index(pos, 0); 01000 FCOORD pos_normed; 01001 denorm.NormTransform(root_denorm, f_pos, &pos_normed); 01002 window->SetCursor(IntCastRounded(pos_normed.x()), 01003 IntCastRounded(pos_normed.y())); 01004 for (int s = 0; s < stepcount; pos += step(s++)) { 01005 int edge_weight = edge_strength_at_index(s); 01006 if (edge_weight == 0) { 01007 // This point has conflicting gradient and step direction, so ignore it. 01008 continue; 01009 } 01010 FCOORD f_pos = sub_pixel_pos_at_index(pos, s); 01011 FCOORD pos_normed; 01012 denorm.NormTransform(root_denorm, f_pos, &pos_normed); 01013 window->DrawTo(IntCastRounded(pos_normed.x()), 01014 IntCastRounded(pos_normed.y())); 01015 } 01016 } 01017 #endif 01018 01019 01027 C_OUTLINE & C_OUTLINE::operator= (const C_OUTLINE & source) { 01028 box = source.box; 01029 start = source.start; 01030 if (steps != NULL) 01031 free_mem(steps); 01032 stepcount = source.stepcount; 01033 steps = (uinT8 *) alloc_mem (step_mem()); 01034 memmove (steps, source.steps, step_mem()); 01035 if (!children.empty ()) 01036 children.clear (); 01037 children.deep_copy(&source.children, &deep_copy); 01038 delete [] offsets; 01039 if (source.offsets != NULL) { 01040 offsets = new EdgeOffset[stepcount]; 01041 memcpy(offsets, source.offsets, stepcount * sizeof(*offsets)); 01042 } else { 01043 offsets = NULL; 01044 } 01045 return *this; 01046 } 01047 01054 void C_OUTLINE::increment_step(int s, int increment, ICOORD* pos, 01055 int* dir_counts, int* pos_totals) const { 01056 int step_index = Modulo(s, stepcount); 01057 int dir_index = chain_code(step_index); 01058 dir_counts[dir_index] += increment; 01059 ICOORD step_vec = step(step_index); 01060 if (step_vec.x() == 0) 01061 pos_totals[dir_index] += pos->x() * increment; 01062 else 01063 pos_totals[dir_index] += pos->y() * increment; 01064 *pos += step_vec; 01065 } 01066 01067 ICOORD C_OUTLINE::chain_step(int chaindir) { 01068 return step_coords[chaindir % 4]; 01069 }