tesseract  4.00.00dev
coutln.cpp
Go to the documentation of this file.
1 /**********************************************************************
2  * File: coutln.c (Formerly coutline.c)
3  * Description: Code for the C_OUTLINE class.
4  * Author: Ray Smith
5  * Created: Mon Oct 07 16:01:57 BST 1991
6  *
7  * (C) Copyright 1991, Hewlett-Packard Ltd.
8  ** Licensed under the Apache License, Version 2.0 (the "License");
9  ** you may not use this file except in compliance with the License.
10  ** You may obtain a copy of the License at
11  ** http://www.apache.org/licenses/LICENSE-2.0
12  ** Unless required by applicable law or agreed to in writing, software
13  ** distributed under the License is distributed on an "AS IS" BASIS,
14  ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  ** See the License for the specific language governing permissions and
16  ** limitations under the License.
17  *
18  **********************************************************************/
19 
20 #include <string.h>
21 #ifdef __UNIX__
22 #include <assert.h>
23 #endif
24 
25 #include "coutln.h"
26 
27 #include "allheaders.h"
28 #include "blobs.h"
29 #include "normalis.h"
30 
31 // Include automatically generated configuration file if running autoconf.
32 #ifdef HAVE_CONFIG_H
33 #include "config_auto.h"
34 #endif
35 
37 ICOORD C_OUTLINE::step_coords[4] = {
38  ICOORD (-1, 0), ICOORD (0, -1), ICOORD (1, 0), ICOORD (0, 1)
39 };
40 
51 C_OUTLINE::C_OUTLINE(CRACKEDGE* startpt, ICOORD bot_left, ICOORD top_right,
52  inT16 length)
53  : box(bot_left, top_right), start(startpt->pos), offsets(NULL) {
54  inT16 stepindex; //index to step
55  CRACKEDGE *edgept; //current point
56 
57  stepcount = length; //no of steps
58  if (length == 0) {
59  steps = NULL;
60  return;
61  }
62  //get memory
63  steps = (uinT8 *) alloc_mem (step_mem());
64  memset(steps, 0, step_mem());
65  edgept = startpt;
66 
67  for (stepindex = 0; stepindex < length; stepindex++) {
68  //set compact step
69  set_step (stepindex, edgept->stepdir);
70  edgept = edgept->next;
71  }
72 }
73 
80 //constructor
81  //steps to copy
82 ICOORD startpt, DIR128 * new_steps,
83 inT16 length //length of loop
84 ):start (startpt), offsets(NULL) {
85  inT8 dirdiff; //direction difference
86  DIR128 prevdir; //previous direction
87  DIR128 dir; //current direction
88  DIR128 lastdir; //dir of last step
89  TBOX new_box; //easy bounding
90  inT16 stepindex; //index to step
91  inT16 srcindex; //source steps
92  ICOORD pos; //current position
93 
94  pos = startpt;
95  stepcount = length; // No. of steps.
96  ASSERT_HOST(length >= 0);
97  steps = static_cast<uinT8*>(alloc_mem(step_mem())); // Get memory.
98  memset(steps, 0, step_mem());
99 
100  lastdir = new_steps[length - 1];
101  prevdir = lastdir;
102  for (stepindex = 0, srcindex = 0; srcindex < length;
103  stepindex++, srcindex++) {
104  new_box = TBOX (pos, pos);
105  box += new_box;
106  //copy steps
107  dir = new_steps[srcindex];
108  set_step(stepindex, dir);
109  dirdiff = dir - prevdir;
110  pos += step (stepindex);
111  if ((dirdiff == 64 || dirdiff == -64) && stepindex > 0) {
112  stepindex -= 2; //cancel there-and-back
113  prevdir = stepindex >= 0 ? step_dir (stepindex) : lastdir;
114  }
115  else
116  prevdir = dir;
117  }
118  ASSERT_HOST (pos.x () == startpt.x () && pos.y () == startpt.y ());
119  do {
120  dirdiff = step_dir (stepindex - 1) - step_dir (0);
121  if (dirdiff == 64 || dirdiff == -64) {
122  start += step (0);
123  stepindex -= 2; //cancel there-and-back
124  for (int i = 0; i < stepindex; ++i)
125  set_step(i, step_dir(i + 1));
126  }
127  }
128  while (stepindex > 1 && (dirdiff == 64 || dirdiff == -64));
129  stepcount = stepindex;
130  ASSERT_HOST (stepcount >= 4);
131 }
132 
141 C_OUTLINE::C_OUTLINE(C_OUTLINE* srcline, FCOORD rotation) : offsets(NULL) {
142  TBOX new_box; //easy bounding
143  inT16 stepindex; //index to step
144  inT16 dirdiff; //direction change
145  ICOORD pos; //current position
146  ICOORD prevpos; //previous dest point
147 
148  ICOORD destpos; //destination point
149  inT16 destindex; //index to step
150  DIR128 dir; //coded direction
151  uinT8 new_step;
152 
153  stepcount = srcline->stepcount * 2;
154  if (stepcount == 0) {
155  steps = NULL;
156  box = srcline->box;
157  box.rotate(rotation);
158  return;
159  }
160  //get memory
161  steps = (uinT8 *) alloc_mem (step_mem());
162  memset(steps, 0, step_mem());
163 
164  for (int iteration = 0; iteration < 2; ++iteration) {
165  DIR128 round1 = iteration == 0 ? 32 : 0;
166  DIR128 round2 = iteration != 0 ? 32 : 0;
167  pos = srcline->start;
168  prevpos = pos;
169  prevpos.rotate (rotation);
170  start = prevpos;
171  box = TBOX (start, start);
172  destindex = 0;
173  for (stepindex = 0; stepindex < srcline->stepcount; stepindex++) {
174  pos += srcline->step (stepindex);
175  destpos = pos;
176  destpos.rotate (rotation);
177  // tprintf("%i %i %i %i ", destpos.x(), destpos.y(), pos.x(), pos.y());
178  while (destpos.x () != prevpos.x () || destpos.y () != prevpos.y ()) {
179  dir = DIR128 (FCOORD (destpos - prevpos));
180  dir += 64; //turn to step style
181  new_step = dir.get_dir ();
182  // tprintf(" %i\n", new_step);
183  if (new_step & 31) {
184  set_step(destindex++, dir + round1);
185  prevpos += step(destindex - 1);
186  if (destindex < 2
187  || ((dirdiff =
188  step_dir (destindex - 1) - step_dir (destindex - 2)) !=
189  -64 && dirdiff != 64)) {
190  set_step(destindex++, dir + round2);
191  prevpos += step(destindex - 1);
192  } else {
193  prevpos -= step(destindex - 1);
194  destindex--;
195  prevpos -= step(destindex - 1);
196  set_step(destindex - 1, dir + round2);
197  prevpos += step(destindex - 1);
198  }
199  }
200  else {
201  set_step(destindex++, dir);
202  prevpos += step(destindex - 1);
203  }
204  while (destindex >= 2 &&
205  ((dirdiff =
206  step_dir (destindex - 1) - step_dir (destindex - 2)) == -64 ||
207  dirdiff == 64)) {
208  prevpos -= step(destindex - 1);
209  prevpos -= step(destindex - 2);
210  destindex -= 2; // Forget u turn
211  }
212  //ASSERT_HOST(prevpos.x() == destpos.x() && prevpos.y() == destpos.y());
213  new_box = TBOX (destpos, destpos);
214  box += new_box;
215  }
216  }
217  ASSERT_HOST (destpos.x () == start.x () && destpos.y () == start.y ());
218  dirdiff = step_dir (destindex - 1) - step_dir (0);
219  while ((dirdiff == 64 || dirdiff == -64) && destindex > 1) {
220  start += step (0);
221  destindex -= 2;
222  for (int i = 0; i < destindex; ++i)
223  set_step(i, step_dir(i + 1));
224  dirdiff = step_dir (destindex - 1) - step_dir (0);
225  }
226  if (destindex >= 4)
227  break;
228  }
229  ASSERT_HOST(destindex <= stepcount);
230  stepcount = destindex;
231  destpos = start;
232  for (stepindex = 0; stepindex < stepcount; stepindex++) {
233  destpos += step (stepindex);
234  }
235  ASSERT_HOST (destpos.x () == start.x () && destpos.y () == start.y ());
236 }
237 
238 // Build a fake outline, given just a bounding box and append to the list.
239 void C_OUTLINE::FakeOutline(const TBOX& box, C_OUTLINE_LIST* outlines) {
240  C_OUTLINE_IT ol_it(outlines);
241  // Make a C_OUTLINE from the bounds. This is a bit of a hack,
242  // as there is no outline, just a bounding box, but it works nicely.
243  CRACKEDGE start;
244  start.pos = box.topleft();
245  C_OUTLINE* outline = new C_OUTLINE(&start, box.topleft(), box.botright(), 0);
246  ol_it.add_to_end(outline);
247 }
248 
256  int stepindex; //current step
257  inT32 total_steps; //steps to do
258  inT32 total; //total area
259  ICOORD pos; //position of point
260  ICOORD next_step; //step to next pix
261  // We aren't going to modify the list, or its contents, but there is
262  // no const iterator.
263  C_OUTLINE_IT it(const_cast<C_OUTLINE_LIST*>(&children));
264 
265  pos = start_pos ();
266  total_steps = pathlength ();
267  total = 0;
268  for (stepindex = 0; stepindex < total_steps; stepindex++) {
269  //all intersected
270  next_step = step (stepindex);
271  if (next_step.x () < 0)
272  total += pos.y ();
273  else if (next_step.x () > 0)
274  total -= pos.y ();
275  pos += next_step;
276  }
277  for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ())
278  total += it.data ()->area ();//add areas of children
279 
280  return total;
281 }
282 
290  inT32 total_steps; // Return value.
291  // We aren't going to modify the list, or its contents, but there is
292  // no const iterator.
293  C_OUTLINE_IT it(const_cast<C_OUTLINE_LIST*>(&children));
294 
295  total_steps = pathlength();
296  for (it.mark_cycle_pt(); !it.cycled_list(); it.forward())
297  total_steps += it.data()->pathlength(); // Add perimeters of children.
298 
299  return total_steps;
300 }
301 
309  int stepindex; //current step
310  inT32 total_steps; //steps to do
311  inT32 total; //total area
312  ICOORD pos; //position of point
313  ICOORD next_step; //step to next pix
314 
315  pos = start_pos ();
316  total_steps = pathlength ();
317  if (total_steps == 0)
318  return box.area();
319  total = 0;
320  for (stepindex = 0; stepindex < total_steps; stepindex++) {
321  //all intersected
322  next_step = step (stepindex);
323  if (next_step.x () < 0)
324  total += pos.y ();
325  else if (next_step.x () > 0)
326  total -= pos.y ();
327  pos += next_step;
328  }
329 
330  return total;
331 }
332 
341  BOOL8 first_was_max_x; //what was first
342  BOOL8 first_was_max_y;
343  BOOL8 looking_for_max_x; //what is next
344  BOOL8 looking_for_min_x;
345  BOOL8 looking_for_max_y; //what is next
346  BOOL8 looking_for_min_y;
347  int stepindex; //current step
348  inT32 total_steps; //steps to do
349  //current limits
350  inT32 max_x, min_x, max_y, min_y;
351  inT32 initial_x, initial_y; //initial limits
352  inT32 total; //total changes
353  ICOORD pos; //position of point
354  ICOORD next_step; //step to next pix
355 
356  pos = start_pos ();
357  total_steps = pathlength ();
358  total = 0;
359  max_x = min_x = pos.x ();
360  max_y = min_y = pos.y ();
361  looking_for_max_x = TRUE;
362  looking_for_min_x = TRUE;
363  looking_for_max_y = TRUE;
364  looking_for_min_y = TRUE;
365  first_was_max_x = FALSE;
366  first_was_max_y = FALSE;
367  initial_x = pos.x ();
368  initial_y = pos.y (); //stop uninit warning
369  for (stepindex = 0; stepindex < total_steps; stepindex++) {
370  //all intersected
371  next_step = step (stepindex);
372  pos += next_step;
373  if (next_step.x () < 0) {
374  if (looking_for_max_x && pos.x () < min_x)
375  min_x = pos.x ();
376  if (looking_for_min_x && max_x - pos.x () > threshold) {
377  if (looking_for_max_x) {
378  initial_x = max_x;
379  first_was_max_x = FALSE;
380  }
381  total++;
382  looking_for_max_x = TRUE;
383  looking_for_min_x = FALSE;
384  min_x = pos.x (); //reset min
385  }
386  }
387  else if (next_step.x () > 0) {
388  if (looking_for_min_x && pos.x () > max_x)
389  max_x = pos.x ();
390  if (looking_for_max_x && pos.x () - min_x > threshold) {
391  if (looking_for_min_x) {
392  initial_x = min_x; //remember first min
393  first_was_max_x = TRUE;
394  }
395  total++;
396  looking_for_max_x = FALSE;
397  looking_for_min_x = TRUE;
398  max_x = pos.x ();
399  }
400  }
401  else if (next_step.y () < 0) {
402  if (looking_for_max_y && pos.y () < min_y)
403  min_y = pos.y ();
404  if (looking_for_min_y && max_y - pos.y () > threshold) {
405  if (looking_for_max_y) {
406  initial_y = max_y; //remember first max
407  first_was_max_y = FALSE;
408  }
409  total++;
410  looking_for_max_y = TRUE;
411  looking_for_min_y = FALSE;
412  min_y = pos.y (); //reset min
413  }
414  }
415  else {
416  if (looking_for_min_y && pos.y () > max_y)
417  max_y = pos.y ();
418  if (looking_for_max_y && pos.y () - min_y > threshold) {
419  if (looking_for_min_y) {
420  initial_y = min_y; //remember first min
421  first_was_max_y = TRUE;
422  }
423  total++;
424  looking_for_max_y = FALSE;
425  looking_for_min_y = TRUE;
426  max_y = pos.y ();
427  }
428  }
429 
430  }
431  if (first_was_max_x && looking_for_min_x) {
432  if (max_x - initial_x > threshold)
433  total++;
434  else
435  total--;
436  }
437  else if (!first_was_max_x && looking_for_max_x) {
438  if (initial_x - min_x > threshold)
439  total++;
440  else
441  total--;
442  }
443  if (first_was_max_y && looking_for_min_y) {
444  if (max_y - initial_y > threshold)
445  total++;
446  else
447  total--;
448  }
449  else if (!first_was_max_y && looking_for_max_y) {
450  if (initial_y - min_y > threshold)
451  total++;
452  else
453  total--;
454  }
455 
456  return total;
457 }
458 
466 BOOL8
467 C_OUTLINE::operator<(const C_OUTLINE& other) const {
468  inT16 count = 0; //winding count
469  ICOORD pos; //position of point
470  inT32 stepindex; //index to cstep
471 
472  if (!box.overlap (other.box))
473  return FALSE; //can't be contained
474  if (stepcount == 0)
475  return other.box.contains(this->box);
476 
477  pos = start;
478  for (stepindex = 0; stepindex < stepcount
479  && (count = other.winding_number (pos)) == INTERSECTING; stepindex++)
480  pos += step (stepindex); //try all points
481  if (count == INTERSECTING) {
482  //all intersected
483  pos = other.start;
484  for (stepindex = 0; stepindex < other.stepcount
485  && (count = winding_number (pos)) == INTERSECTING; stepindex++)
486  //try other way round
487  pos += other.step (stepindex);
488  return count == INTERSECTING || count == 0;
489  }
490  return count != 0;
491 }
492 
501  inT16 stepindex; //index to cstep
502  inT16 count; //winding count
503  ICOORD vec; //to current point
504  ICOORD stepvec; //step vector
505  inT32 cross; //cross product
506 
507  vec = start - point; //vector to it
508  count = 0;
509  for (stepindex = 0; stepindex < stepcount; stepindex++) {
510  stepvec = step (stepindex); //get the step
511  //crossing the line
512  if (vec.y () <= 0 && vec.y () + stepvec.y () > 0) {
513  cross = vec * stepvec; //cross product
514  if (cross > 0)
515  count++; //crossing right half
516  else if (cross == 0)
517  return INTERSECTING; //going through point
518  }
519  else if (vec.y () > 0 && vec.y () + stepvec.y () <= 0) {
520  cross = vec * stepvec;
521  if (cross < 0)
522  count--; //crossing back
523  else if (cross == 0)
524  return INTERSECTING; //illegal
525  }
526  vec += stepvec; //sum vectors
527  }
528  return count; //winding number
529 }
530 
537 inT16 C_OUTLINE::turn_direction() const { //winding number
538  DIR128 prevdir; //previous direction
539  DIR128 dir; //current direction
540  inT16 stepindex; //index to cstep
541  inT8 dirdiff; //direction difference
542  inT16 count; //winding count
543 
544  if (stepcount == 0)
545  return 128;
546  count = 0;
547  prevdir = step_dir (stepcount - 1);
548  for (stepindex = 0; stepindex < stepcount; stepindex++) {
549  dir = step_dir (stepindex);
550  dirdiff = dir - prevdir;
551  ASSERT_HOST (dirdiff == 0 || dirdiff == 32 || dirdiff == -32);
552  count += dirdiff;
553  prevdir = dir;
554  }
555  ASSERT_HOST (count == 128 || count == -128);
556  return count; //winding number
557 }
558 
565 void C_OUTLINE::reverse() { //reverse drection
566  DIR128 halfturn = MODULUS / 2; //amount to shift
567  DIR128 stepdir; //direction of step
568  inT16 stepindex; //index to cstep
569  inT16 farindex; //index to other side
570  inT16 halfsteps; //half of stepcount
571 
572  halfsteps = (stepcount + 1) / 2;
573  for (stepindex = 0; stepindex < halfsteps; stepindex++) {
574  farindex = stepcount - stepindex - 1;
575  stepdir = step_dir (stepindex);
576  set_step (stepindex, step_dir (farindex) + halfturn);
577  set_step (farindex, stepdir + halfturn);
578  }
579 }
580 
588 void C_OUTLINE::move(const ICOORD vec) {
589  C_OUTLINE_IT it(&children); // iterator
590 
591  box.move (vec);
592  start += vec;
593 
594  for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ())
595  it.data ()->move (vec); // move child outlines
596 }
597 
605  if (stepcount == 0) return true;
606  int parent_area = outer_area();
607  // We aren't going to modify the list, or its contents, but there is
608  // no const iterator.
609  C_OUTLINE_IT child_it(const_cast<C_OUTLINE_LIST*>(&children));
610  for (child_it.mark_cycle_pt(); !child_it.cycled_list(); child_it.forward()) {
611  const C_OUTLINE* child = child_it.data();
612  if (child->outer_area() * parent_area > 0 || !child->IsLegallyNested())
613  return false;
614  }
615  return true;
616 }
617 
627 void C_OUTLINE::RemoveSmallRecursive(int min_size, C_OUTLINE_IT* it) {
628  if (box.width() < min_size || box.height() < min_size) {
629  ASSERT_HOST(this == it->data());
630  delete it->extract(); // Too small so get rid of it and any children.
631  } else if (!children.empty()) {
632  // Search the children of this, deleting any that are too small.
633  C_OUTLINE_IT child_it(&children);
634  for (child_it.mark_cycle_pt(); !child_it.cycled_list();
635  child_it.forward()) {
636  C_OUTLINE* child = child_it.data();
637  child->RemoveSmallRecursive(min_size, &child_it);
638  }
639  }
640 }
641 
642 // Factored out helpers below are used only by ComputeEdgeOffsets to operate
643 // on data from an 8-bit Pix, and assume that any input x and/or y are already
644 // constrained to be legal Pix coordinates.
645 
651 static void ComputeGradient(const l_uint32* data, int wpl,
652  int x, int y, int width, int height,
653  ICOORD* gradient) {
654  const l_uint32* line = data + y * wpl;
655  int pix_x_y =
656  x < width && y < height
657  ? GET_DATA_BYTE(line, x)
658  : 255;
659  int pix_x_prevy =
660  x < width && y > 0
661  ? GET_DATA_BYTE(line - wpl, x)
662  : 255;
663  int pix_prevx_prevy =
664  x > 0 && y > 0
665  ? GET_DATA_BYTE(line - wpl, x - 1)
666  : 255;
667  int pix_prevx_y =
668  x > 0 && y < height
669  ? GET_DATA_BYTE(line, x - 1)
670  : 255;
671  gradient->set_x(pix_x_y + pix_x_prevy - (pix_prevx_y + pix_prevx_prevy));
672  gradient->set_y(pix_x_prevy + pix_prevx_prevy - (pix_x_y + pix_prevx_y));
673 }
674 
680 static bool EvaluateVerticalDiff(const l_uint32* data, int wpl, int diff_sign,
681  int x, int y, int height,
682  int* best_diff, int* best_sum, int* best_y) {
683  if (y <= 0 || y >= height)
684  return false;
685  const l_uint32* line = data + y * wpl;
686  int pixel1 = GET_DATA_BYTE(line - wpl, x);
687  int pixel2 = GET_DATA_BYTE(line, x);
688  int diff = (pixel2 - pixel1) * diff_sign;
689  if (diff > *best_diff) {
690  *best_diff = diff;
691  *best_sum = pixel1 + pixel2;
692  *best_y = y;
693  }
694  return diff > 0;
695 }
696 
702 static bool EvaluateHorizontalDiff(const l_uint32* line, int diff_sign,
703  int x, int width,
704  int* best_diff, int* best_sum, int* best_x) {
705  if (x <= 0 || x >= width)
706  return false;
707  int pixel1 = GET_DATA_BYTE(line, x - 1);
708  int pixel2 = GET_DATA_BYTE(line, x);
709  int diff = (pixel2 - pixel1) * diff_sign;
710  if (diff > *best_diff) {
711  *best_diff = diff;
712  *best_sum = pixel1 + pixel2;
713  *best_x = x;
714  }
715  return diff > 0;
716 }
717 
733 void C_OUTLINE::ComputeEdgeOffsets(int threshold, Pix* pix) {
734  if (pixGetDepth(pix) != 8) return;
735  const l_uint32* data = pixGetData(pix);
736  int wpl = pixGetWpl(pix);
737  int width = pixGetWidth(pix);
738  int height = pixGetHeight(pix);
739  bool negative = flag(COUT_INVERSE);
740  delete [] offsets;
741  offsets = new EdgeOffset[stepcount];
742  ICOORD pos = start;
743  ICOORD prev_gradient;
744  ComputeGradient(data, wpl, pos.x(), height - pos.y(), width, height,
745  &prev_gradient);
746  for (int s = 0; s < stepcount; ++s) {
747  ICOORD step_vec = step(s);
748  TPOINT pt1(pos);
749  pos += step_vec;
750  TPOINT pt2(pos);
751  ICOORD next_gradient;
752  ComputeGradient(data, wpl, pos.x(), height - pos.y(), width, height,
753  &next_gradient);
754  // Use the sum of the prev and next as the working gradient.
755  ICOORD gradient = prev_gradient + next_gradient;
756  // best_diff will be manipulated to be always positive.
757  int best_diff = 0;
758  // offset will be the extrapolation of the location of the greyscale
759  // threshold from the edge with the largest difference, relative to the
760  // location of the binary edge.
761  int offset = 0;
762  if (pt1.y == pt2.y && abs(gradient.y()) * 2 >= abs(gradient.x())) {
763  // Horizontal step. diff_sign == 1 indicates black above.
764  int diff_sign = (pt1.x > pt2.x) == negative ? 1 : -1;
765  int x = MIN(pt1.x, pt2.x);
766  int y = height - pt1.y;
767  int best_sum = 0;
768  int best_y = y;
769  EvaluateVerticalDiff(data, wpl, diff_sign, x, y, height,
770  &best_diff, &best_sum, &best_y);
771  // Find the strongest edge.
772  int test_y = y;
773  do {
774  ++test_y;
775  } while (EvaluateVerticalDiff(data, wpl, diff_sign, x, test_y, height,
776  &best_diff, &best_sum, &best_y));
777  test_y = y;
778  do {
779  --test_y;
780  } while (EvaluateVerticalDiff(data, wpl, diff_sign, x, test_y, height,
781  &best_diff, &best_sum, &best_y));
782  offset = diff_sign * (best_sum / 2 - threshold) +
783  (y - best_y) * best_diff;
784  } else if (pt1.x == pt2.x && abs(gradient.x()) * 2 >= abs(gradient.y())) {
785  // Vertical step. diff_sign == 1 indicates black on the left.
786  int diff_sign = (pt1.y > pt2.y) == negative ? 1 : -1;
787  int x = pt1.x;
788  int y = height - MAX(pt1.y, pt2.y);
789  const l_uint32* line = pixGetData(pix) + y * wpl;
790  int best_sum = 0;
791  int best_x = x;
792  EvaluateHorizontalDiff(line, diff_sign, x, width,
793  &best_diff, &best_sum, &best_x);
794  // Find the strongest edge.
795  int test_x = x;
796  do {
797  ++test_x;
798  } while (EvaluateHorizontalDiff(line, diff_sign, test_x, width,
799  &best_diff, &best_sum, &best_x));
800  test_x = x;
801  do {
802  --test_x;
803  } while (EvaluateHorizontalDiff(line, diff_sign, test_x, width,
804  &best_diff, &best_sum, &best_x));
805  offset = diff_sign * (threshold - best_sum / 2) +
806  (best_x - x) * best_diff;
807  }
808  offsets[s].offset_numerator =
809  static_cast<inT8>(ClipToRange(offset, -MAX_INT8, MAX_INT8));
810  offsets[s].pixel_diff = static_cast<uinT8>(ClipToRange(best_diff, 0 ,
811  MAX_UINT8));
812  if (negative) gradient = -gradient;
813  // Compute gradient angle quantized to 256 directions, rotated by 64 (pi/2)
814  // to convert from gradient direction to edge direction.
815  offsets[s].direction =
816  Modulo(FCOORD::binary_angle_plus_pi(gradient.angle()) + 64, 256);
817  prev_gradient = next_gradient;
818  }
819 }
820 
851  delete [] offsets;
852  offsets = new EdgeOffset[stepcount];
853  // Count of the number of steps in each direction in the sliding window.
854  int dir_counts[4];
855  // Sum of the positions (y for a horizontal step, x for vertical) in each
856  // direction in the sliding window.
857  int pos_totals[4];
858  memset(dir_counts, 0, sizeof(dir_counts));
859  memset(pos_totals, 0, sizeof(pos_totals));
860  ICOORD pos = start;
861  ICOORD tail_pos = pos;
862  // tail_pos is the trailing position, with the next point to be lost from
863  // the window.
864  tail_pos -= step(stepcount - 1);
865  tail_pos -= step(stepcount - 2);
866  // head_pos is the leading position, with the next point to be added to the
867  // window.
868  ICOORD head_pos = tail_pos;
869  // Set up the initial window with 4 points in [-2, 2)
870  for (int s = -2; s < 2; ++s) {
871  increment_step(s, 1, &head_pos, dir_counts, pos_totals);
872  }
873  for (int s = 0; s < stepcount; pos += step(s++)) {
874  // At step s, s in in the middle of [s-2, s+2].
875  increment_step(s + 2, 1, &head_pos, dir_counts, pos_totals);
876  int dir_index = chain_code(s);
877  ICOORD step_vec = step(s);
878  int best_diff = 0;
879  int offset = 0;
880  // Use only steps that have a count of >=2 OR the strong U-turn with a
881  // single d and 2 at d-1 and 2 at d+1 (mod 4).
882  if (dir_counts[dir_index] >= 2 || (dir_counts[dir_index] == 1 &&
883  dir_counts[Modulo(dir_index - 1, 4)] == 2 &&
884  dir_counts[Modulo(dir_index + 1, 4)] == 2)) {
885  // Valid step direction.
886  best_diff = dir_counts[dir_index];
887  int edge_pos = step_vec.x() == 0 ? pos.x() : pos.y();
888  // The offset proposes that the actual step should be positioned at
889  // the mean position of the steps in the window of the same direction.
890  // See ASCII art above.
891  offset = pos_totals[dir_index] - best_diff * edge_pos;
892  }
893  offsets[s].offset_numerator =
894  static_cast<inT8>(ClipToRange(offset, -MAX_INT8, MAX_INT8));
895  offsets[s].pixel_diff = static_cast<uinT8>(ClipToRange(best_diff, 0 ,
896  MAX_UINT8));
897  // The direction is just the vector from start to end of the window.
898  FCOORD direction(head_pos.x() - tail_pos.x(), head_pos.y() - tail_pos.y());
899  offsets[s].direction = direction.to_direction();
900  increment_step(s - 2, -1, &tail_pos, dir_counts, pos_totals);
901  }
902 }
903 
908 void C_OUTLINE::render(int left, int top, Pix* pix) const {
909  ICOORD pos = start;
910  for (int stepindex = 0; stepindex < stepcount; ++stepindex) {
911  ICOORD next_step = step(stepindex);
912  if (next_step.y() < 0) {
913  pixRasterop(pix, 0, top - pos.y(), pos.x() - left, 1,
914  PIX_NOT(PIX_DST), NULL, 0, 0);
915  } else if (next_step.y() > 0) {
916  pixRasterop(pix, 0, top - pos.y() - 1, pos.x() - left, 1,
917  PIX_NOT(PIX_DST), NULL, 0, 0);
918  }
919  pos += next_step;
920  }
921 }
922 
930 void C_OUTLINE::render_outline(int left, int top, Pix* pix) const {
931  ICOORD pos = start;
932  for (int stepindex = 0; stepindex < stepcount; ++stepindex) {
933  ICOORD next_step = step(stepindex);
934  if (next_step.y() < 0) {
935  pixSetPixel(pix, pos.x() - left, top - pos.y(), 1);
936  } else if (next_step.y() > 0) {
937  pixSetPixel(pix, pos.x() - left - 1, top - pos.y() - 1, 1);
938  } else if (next_step.x() < 0) {
939  pixSetPixel(pix, pos.x() - left - 1, top - pos.y(), 1);
940  } else if (next_step.x() > 0) {
941  pixSetPixel(pix, pos.x() - left, top - pos.y() - 1, 1);
942  }
943  pos += next_step;
944  }
945 }
946 
955 #ifndef GRAPHICS_DISABLED
956 void C_OUTLINE::plot(ScrollView* window, ScrollView::Color colour) const {
957  inT16 stepindex; // index to cstep
958  ICOORD pos; // current position
959  DIR128 stepdir; // direction of step
960 
961  pos = start; // current position
962  window->Pen(colour);
963  if (stepcount == 0) {
964  window->Rectangle(box.left(), box.top(), box.right(), box.bottom());
965  return;
966  }
967  window->SetCursor(pos.x(), pos.y());
968 
969  stepindex = 0;
970  while (stepindex < stepcount) {
971  pos += step(stepindex); // step to next
972  stepdir = step_dir(stepindex);
973  stepindex++; // count steps
974  // merge straight lines
975  while (stepindex < stepcount &&
976  stepdir.get_dir() == step_dir(stepindex).get_dir()) {
977  pos += step(stepindex);
978  stepindex++;
979  }
980  window->DrawTo(pos.x(), pos.y());
981  }
982 }
983 
989  ScrollView* window) const {
990  window->Pen(colour);
991  if (stepcount == 0) {
992  window->Rectangle(box.left(), box.top(), box.right(), box.bottom());
993  return;
994  }
995  const DENORM* root_denorm = denorm.RootDenorm();
996  ICOORD pos = start; // current position
997  FCOORD f_pos = sub_pixel_pos_at_index(pos, 0);
998  FCOORD pos_normed;
999  denorm.NormTransform(root_denorm, f_pos, &pos_normed);
1000  window->SetCursor(IntCastRounded(pos_normed.x()),
1001  IntCastRounded(pos_normed.y()));
1002  for (int s = 0; s < stepcount; pos += step(s++)) {
1003  int edge_weight = edge_strength_at_index(s);
1004  if (edge_weight == 0) {
1005  // This point has conflicting gradient and step direction, so ignore it.
1006  continue;
1007  }
1008  FCOORD f_pos = sub_pixel_pos_at_index(pos, s);
1009  FCOORD pos_normed;
1010  denorm.NormTransform(root_denorm, f_pos, &pos_normed);
1011  window->DrawTo(IntCastRounded(pos_normed.x()),
1012  IntCastRounded(pos_normed.y()));
1013  }
1014 }
1015 #endif
1016 
1025  box = source.box;
1026  start = source.start;
1027  if (steps != NULL)
1028  free_mem(steps);
1029  stepcount = source.stepcount;
1030  steps = (uinT8 *) alloc_mem (step_mem());
1031  memmove (steps, source.steps, step_mem());
1032  if (!children.empty ())
1033  children.clear ();
1034  children.deep_copy(&source.children, &deep_copy);
1035  delete [] offsets;
1036  if (source.offsets != NULL) {
1037  offsets = new EdgeOffset[stepcount];
1038  memcpy(offsets, source.offsets, stepcount * sizeof(*offsets));
1039  } else {
1040  offsets = NULL;
1041  }
1042  return *this;
1043 }
1044 
1051 void C_OUTLINE::increment_step(int s, int increment, ICOORD* pos,
1052  int* dir_counts, int* pos_totals) const {
1053  int step_index = Modulo(s, stepcount);
1054  int dir_index = chain_code(step_index);
1055  dir_counts[dir_index] += increment;
1056  ICOORD step_vec = step(step_index);
1057  if (step_vec.x() == 0)
1058  pos_totals[dir_index] += pos->x() * increment;
1059  else
1060  pos_totals[dir_index] += pos->y() * increment;
1061  *pos += step_vec;
1062 }
1063 
1065  return step_coords[chaindir % 4];
1066 }
static void FakeOutline(const TBOX &box, C_OUTLINE_LIST *outlines)
Definition: coutln.cpp:239
void free_mem(void *oldchunk)
Definition: memry.cpp:47
void ComputeBinaryOffsets()
Definition: coutln.cpp:850
const DENORM * RootDenorm() const
Definition: normalis.h:260
void rotate(const FCOORD &vec)
Definition: ipoints.h:241
C_OUTLINE_LIST * child()
Definition: coutln.h:106
bool overlap(const TBOX &box) const
Definition: rect.h:345
void set_step(inT16 stepindex, inT8 stepdir)
Definition: coutln.h:114
#define TRUE
Definition: capi.h:45
Definition: points.h:189
void RemoveSmallRecursive(int min_size, C_OUTLINE_IT *it)
Definition: coutln.cpp:627
void render(int left, int top, Pix *pix) const
Definition: coutln.cpp:908
void NormTransform(const DENORM *first_norm, const TPOINT &pt, TPOINT *transformed) const
Definition: normalis.cpp:334
int32_t inT32
Definition: host.h:38
static uinT8 binary_angle_plus_pi(double angle)
Definition: points.cpp:124
void move(const ICOORD vec)
Definition: coutln.cpp:588
void plot_normed(const DENORM &denorm, ScrollView::Color colour, ScrollView *window) const
Definition: coutln.cpp:988
inT32 area() const
Definition: rect.h:118
uinT8 direction
Definition: coutln.h:62
void set_x(inT16 xin)
rewrite function
Definition: points.h:61
#define MAX_UINT8
Definition: host.h:63
BOOL8 flag(C_OUTLINE_FLAGS mask) const
Definition: coutln.h:96
#define INTERSECTING
Definition: coutln.h:32
inT16 x() const
access function
Definition: points.h:52
static ICOORD chain_step(int chaindir)
Definition: coutln.cpp:1064
inT16 turn_direction() const
Definition: coutln.cpp:537
int direction(EDGEPT *point)
Definition: vecfuncs.cpp:43
voidpf uLong offset
Definition: ioapi.h:42
void ComputeEdgeOffsets(int threshold, Pix *pix)
Definition: coutln.cpp:733
void plot(ScrollView *window, ScrollView::Color colour) const
Definition: coutln.cpp:956
int IntCastRounded(double x)
Definition: helpers.h:179
int16_t inT16
Definition: host.h:36
void SetCursor(int x, int y)
Definition: scrollview.cpp:525
void reverse()
Definition: coutln.cpp:565
#define ASSERT_HOST(x)
Definition: errcode.h:84
inT16 left() const
Definition: rect.h:68
BOOL8 operator<(const C_OUTLINE &other) const
Definition: coutln.cpp:467
inT8 stepdir
Definition: crakedge.h:33
void Rectangle(int x1, int y1, int x2, int y2)
Definition: scrollview.cpp:606
unsigned char BOOL8
Definition: host.h:44
int edge_strength_at_index(int index) const
Definition: coutln.h:185
#define FALSE
Definition: capi.h:46
#define MODULUS
Definition: mod128.h:25
inT16 x
Definition: blobs.h:71
inT16 y() const
access_function
Definition: points.h:56
T ClipToRange(const T &x, const T &lower_bound, const T &upper_bound)
Definition: helpers.h:122
CRACKEDGE * next
Definition: crakedge.h:35
inT16 winding_number(ICOORD testpt) const
Definition: coutln.cpp:500
void render_outline(int left, int top, Pix *pix) const
Definition: coutln.cpp:930
bool contains(const FCOORD pt) const
Definition: rect.h:323
ICOORD topleft() const
Definition: rect.h:96
inT32 outer_area() const
Definition: coutln.cpp:308
inT16 top() const
Definition: rect.h:54
#define MAX(x, y)
Definition: ndminx.h:24
void * alloc_mem(inT32 count)
Definition: memry.cpp:39
DIR128 step_dir(int index) const
Definition: coutln.h:137
inT32 area() const
Definition: coutln.cpp:255
static C_OUTLINE * deep_copy(const C_OUTLINE *src)
Definition: coutln.h:259
inT16 y
Definition: blobs.h:72
Definition: rect.h:30
ICOORD pos
Definition: crakedge.h:30
int8_t inT8
Definition: host.h:34
bool IsLegallyNested() const
Definition: coutln.cpp:604
#define MIN(x, y)
Definition: ndminx.h:28
ICOORD step(int index) const
Definition: coutln.h:142
FCOORD sub_pixel_pos_at_index(const ICOORD &pos, int index) const
Definition: coutln.h:161
ICOORD botright() const
Definition: rect.h:92
const ICOORD & start_pos() const
Definition: coutln.h:146
Definition: blobs.h:50
inT16 height() const
Definition: rect.h:104
float y() const
Definition: points.h:212
uint8_t uinT8
Definition: host.h:35
inT16 right() const
Definition: rect.h:75
inT8 offset_numerator
Definition: coutln.h:60
inT16 width() const
Definition: rect.h:111
C_OUTLINE()
Definition: coutln.h:71
int chain_code(int index) const
Definition: coutln.h:193
inT32 perimeter() const
Definition: coutln.cpp:289
#define MAX_INT8
Definition: host.h:60
inT8 get_dir() const
Definition: mod128.h:77
inT16 bottom() const
Definition: rect.h:61
uinT8 pixel_diff
Definition: coutln.h:61
#define ELISTIZE(CLASSNAME)
Definition: elst.h:961
Definition: mod128.h:29
void move(const ICOORD vec)
Definition: rect.h:153
inT32 pathlength() const
Definition: coutln.h:133
float x() const
Definition: points.h:209
int count(LIST var_list)
Definition: oldlist.cpp:103
void rotate(const FCOORD &vec)
Definition: rect.h:189
C_OUTLINE & operator=(const C_OUTLINE &source)
Definition: coutln.cpp:1024
inT32 count_transitions(inT32 threshold)
Definition: coutln.cpp:340
void set_y(inT16 yin)
rewrite function
Definition: points.h:65
void Pen(Color color)
Definition: scrollview.cpp:726
int Modulo(int a, int b)
Definition: helpers.h:164
void DrawTo(int x, int y)
Definition: scrollview.cpp:531
integer coordinate
Definition: points.h:30