tesseract  3.05.02
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 = reinterpret_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  int64_t 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(
658  const_cast<void*>(reinterpret_cast<const void*>(line)), x)
659  : 255;
660  int pix_x_prevy =
661  x < width && y > 0
662  ? GET_DATA_BYTE(
663  const_cast<void*>(reinterpret_cast<const void*>(line - wpl)), x)
664  : 255;
665  int pix_prevx_prevy =
666  x > 0 && y > 0
667  ? GET_DATA_BYTE(
668  const_cast<void*>(reinterpret_cast<void const*>(line - wpl)),
669  x - 1)
670  : 255;
671  int pix_prevx_y =
672  x > 0 && y < height
673  ? GET_DATA_BYTE(
674  const_cast<void*>(reinterpret_cast<const void*>(line)), x - 1)
675  : 255;
676  gradient->set_x(pix_x_y + pix_x_prevy - (pix_prevx_y + pix_prevx_prevy));
677  gradient->set_y(pix_x_prevy + pix_prevx_prevy - (pix_x_y + pix_prevx_y));
678 }
679 
685 static bool EvaluateVerticalDiff(const l_uint32* data, int wpl, int diff_sign,
686  int x, int y, int height,
687  int* best_diff, int* best_sum, int* best_y) {
688  if (y <= 0 || y >= height)
689  return false;
690  const l_uint32* line = data + y * wpl;
691  int pixel1 = GET_DATA_BYTE(
692  const_cast<void*>(reinterpret_cast<const void*>(line - wpl)), x);
693  int pixel2 =
694  GET_DATA_BYTE(const_cast<void*>(reinterpret_cast<const void*>(line)), x);
695  int diff = (pixel2 - pixel1) * diff_sign;
696  if (diff > *best_diff) {
697  *best_diff = diff;
698  *best_sum = pixel1 + pixel2;
699  *best_y = y;
700  }
701  return diff > 0;
702 }
703 
709 static bool EvaluateHorizontalDiff(const l_uint32* line, int diff_sign,
710  int x, int width,
711  int* best_diff, int* best_sum, int* best_x) {
712  if (x <= 0 || x >= width)
713  return false;
714  int pixel1 = GET_DATA_BYTE(
715  const_cast<void*>(reinterpret_cast<const void*>(line)), x - 1);
716  int pixel2 =
717  GET_DATA_BYTE(const_cast<void*>(reinterpret_cast<const void*>(line)), x);
718  int diff = (pixel2 - pixel1) * diff_sign;
719  if (diff > *best_diff) {
720  *best_diff = diff;
721  *best_sum = pixel1 + pixel2;
722  *best_x = x;
723  }
724  return diff > 0;
725 }
726 
742 void C_OUTLINE::ComputeEdgeOffsets(int threshold, Pix* pix) {
743  if (pixGetDepth(pix) != 8) return;
744  const l_uint32* data = pixGetData(pix);
745  int wpl = pixGetWpl(pix);
746  int width = pixGetWidth(pix);
747  int height = pixGetHeight(pix);
748  bool negative = flag(COUT_INVERSE);
749  delete [] offsets;
750  offsets = new EdgeOffset[stepcount];
751  ICOORD pos = start;
752  ICOORD prev_gradient;
753  ComputeGradient(data, wpl, pos.x(), height - pos.y(), width, height,
754  &prev_gradient);
755  for (int s = 0; s < stepcount; ++s) {
756  ICOORD step_vec = step(s);
757  TPOINT pt1(pos);
758  pos += step_vec;
759  TPOINT pt2(pos);
760  ICOORD next_gradient;
761  ComputeGradient(data, wpl, pos.x(), height - pos.y(), width, height,
762  &next_gradient);
763  // Use the sum of the prev and next as the working gradient.
764  ICOORD gradient = prev_gradient + next_gradient;
765  // best_diff will be manipulated to be always positive.
766  int best_diff = 0;
767  // offset will be the extrapolation of the location of the greyscale
768  // threshold from the edge with the largest difference, relative to the
769  // location of the binary edge.
770  int offset = 0;
771  if (pt1.y == pt2.y && abs(gradient.y()) * 2 >= abs(gradient.x())) {
772  // Horizontal step. diff_sign == 1 indicates black above.
773  int diff_sign = (pt1.x > pt2.x) == negative ? 1 : -1;
774  int x = MIN(pt1.x, pt2.x);
775  int y = height - pt1.y;
776  int best_sum = 0;
777  int best_y = y;
778  EvaluateVerticalDiff(data, wpl, diff_sign, x, y, height,
779  &best_diff, &best_sum, &best_y);
780  // Find the strongest edge.
781  int test_y = y;
782  do {
783  ++test_y;
784  } while (EvaluateVerticalDiff(data, wpl, diff_sign, x, test_y, height,
785  &best_diff, &best_sum, &best_y));
786  test_y = y;
787  do {
788  --test_y;
789  } while (EvaluateVerticalDiff(data, wpl, diff_sign, x, test_y, height,
790  &best_diff, &best_sum, &best_y));
791  offset = diff_sign * (best_sum / 2 - threshold) +
792  (y - best_y) * best_diff;
793  } else if (pt1.x == pt2.x && abs(gradient.x()) * 2 >= abs(gradient.y())) {
794  // Vertical step. diff_sign == 1 indicates black on the left.
795  int diff_sign = (pt1.y > pt2.y) == negative ? 1 : -1;
796  int x = pt1.x;
797  int y = height - MAX(pt1.y, pt2.y);
798  const l_uint32* line = pixGetData(pix) + y * wpl;
799  int best_sum = 0;
800  int best_x = x;
801  EvaluateHorizontalDiff(line, diff_sign, x, width,
802  &best_diff, &best_sum, &best_x);
803  // Find the strongest edge.
804  int test_x = x;
805  do {
806  ++test_x;
807  } while (EvaluateHorizontalDiff(line, diff_sign, test_x, width,
808  &best_diff, &best_sum, &best_x));
809  test_x = x;
810  do {
811  --test_x;
812  } while (EvaluateHorizontalDiff(line, diff_sign, test_x, width,
813  &best_diff, &best_sum, &best_x));
814  offset = diff_sign * (threshold - best_sum / 2) +
815  (best_x - x) * best_diff;
816  }
817  offsets[s].offset_numerator =
818  static_cast<inT8>(ClipToRange(offset, -MAX_INT8, MAX_INT8));
819  offsets[s].pixel_diff = static_cast<uinT8>(ClipToRange(best_diff, 0 ,
820  MAX_UINT8));
821  if (negative) gradient = -gradient;
822  // Compute gradient angle quantized to 256 directions, rotated by 64 (pi/2)
823  // to convert from gradient direction to edge direction.
824  offsets[s].direction =
825  Modulo(FCOORD::binary_angle_plus_pi(gradient.angle()) + 64, 256);
826  prev_gradient = next_gradient;
827  }
828 }
829 
860  delete [] offsets;
861  offsets = new EdgeOffset[stepcount];
862  // Count of the number of steps in each direction in the sliding window.
863  int dir_counts[4];
864  // Sum of the positions (y for a horizontal step, x for vertical) in each
865  // direction in the sliding window.
866  int pos_totals[4];
867  memset(dir_counts, 0, sizeof(dir_counts));
868  memset(pos_totals, 0, sizeof(pos_totals));
869  ICOORD pos = start;
870  ICOORD tail_pos = pos;
871  // tail_pos is the trailing position, with the next point to be lost from
872  // the window.
873  tail_pos -= step(stepcount - 1);
874  tail_pos -= step(stepcount - 2);
875  // head_pos is the leading position, with the next point to be added to the
876  // window.
877  ICOORD head_pos = tail_pos;
878  // Set up the initial window with 4 points in [-2, 2)
879  for (int s = -2; s < 2; ++s) {
880  increment_step(s, 1, &head_pos, dir_counts, pos_totals);
881  }
882  for (int s = 0; s < stepcount; pos += step(s++)) {
883  // At step s, s in in the middle of [s-2, s+2].
884  increment_step(s + 2, 1, &head_pos, dir_counts, pos_totals);
885  int dir_index = chain_code(s);
886  ICOORD step_vec = step(s);
887  int best_diff = 0;
888  int offset = 0;
889  // Use only steps that have a count of >=2 OR the strong U-turn with a
890  // single d and 2 at d-1 and 2 at d+1 (mod 4).
891  if (dir_counts[dir_index] >= 2 || (dir_counts[dir_index] == 1 &&
892  dir_counts[Modulo(dir_index - 1, 4)] == 2 &&
893  dir_counts[Modulo(dir_index + 1, 4)] == 2)) {
894  // Valid step direction.
895  best_diff = dir_counts[dir_index];
896  int edge_pos = step_vec.x() == 0 ? pos.x() : pos.y();
897  // The offset proposes that the actual step should be positioned at
898  // the mean position of the steps in the window of the same direction.
899  // See ASCII art above.
900  offset = pos_totals[dir_index] - best_diff * edge_pos;
901  }
902  offsets[s].offset_numerator =
903  static_cast<inT8>(ClipToRange(offset, -MAX_INT8, MAX_INT8));
904  offsets[s].pixel_diff = static_cast<uinT8>(ClipToRange(best_diff, 0 ,
905  MAX_UINT8));
906  // The direction is just the vector from start to end of the window.
907  FCOORD direction(head_pos.x() - tail_pos.x(), head_pos.y() - tail_pos.y());
908  offsets[s].direction = direction.to_direction();
909  increment_step(s - 2, -1, &tail_pos, dir_counts, pos_totals);
910  }
911 }
912 
917 void C_OUTLINE::render(int left, int top, Pix* pix) const {
918  ICOORD pos = start;
919  for (int stepindex = 0; stepindex < stepcount; ++stepindex) {
920  ICOORD next_step = step(stepindex);
921  if (next_step.y() < 0) {
922  pixRasterop(pix, 0, top - pos.y(), pos.x() - left, 1,
923  PIX_NOT(PIX_DST), NULL, 0, 0);
924  } else if (next_step.y() > 0) {
925  pixRasterop(pix, 0, top - pos.y() - 1, pos.x() - left, 1,
926  PIX_NOT(PIX_DST), NULL, 0, 0);
927  }
928  pos += next_step;
929  }
930 }
931 
939 void C_OUTLINE::render_outline(int left, int top, Pix* pix) const {
940  ICOORD pos = start;
941  for (int stepindex = 0; stepindex < stepcount; ++stepindex) {
942  ICOORD next_step = step(stepindex);
943  if (next_step.y() < 0) {
944  pixSetPixel(pix, pos.x() - left, top - pos.y(), 1);
945  } else if (next_step.y() > 0) {
946  pixSetPixel(pix, pos.x() - left - 1, top - pos.y() - 1, 1);
947  } else if (next_step.x() < 0) {
948  pixSetPixel(pix, pos.x() - left - 1, top - pos.y(), 1);
949  } else if (next_step.x() > 0) {
950  pixSetPixel(pix, pos.x() - left, top - pos.y() - 1, 1);
951  }
952  pos += next_step;
953  }
954 }
955 
964 #ifndef GRAPHICS_DISABLED
965 void C_OUTLINE::plot(ScrollView* window, ScrollView::Color colour) const {
966  inT16 stepindex; // index to cstep
967  ICOORD pos; // current position
968  DIR128 stepdir; // direction of step
969 
970  pos = start; // current position
971  window->Pen(colour);
972  if (stepcount == 0) {
973  window->Rectangle(box.left(), box.top(), box.right(), box.bottom());
974  return;
975  }
976  window->SetCursor(pos.x(), pos.y());
977 
978  stepindex = 0;
979  while (stepindex < stepcount) {
980  pos += step(stepindex); // step to next
981  stepdir = step_dir(stepindex);
982  stepindex++; // count steps
983  // merge straight lines
984  while (stepindex < stepcount &&
985  stepdir.get_dir() == step_dir(stepindex).get_dir()) {
986  pos += step(stepindex);
987  stepindex++;
988  }
989  window->DrawTo(pos.x(), pos.y());
990  }
991 }
992 
998  ScrollView* window) const {
999  window->Pen(colour);
1000  if (stepcount == 0) {
1001  window->Rectangle(box.left(), box.top(), box.right(), box.bottom());
1002  return;
1003  }
1004  const DENORM* root_denorm = denorm.RootDenorm();
1005  ICOORD pos = start; // current position
1006  FCOORD f_pos = sub_pixel_pos_at_index(pos, 0);
1007  FCOORD pos_normed;
1008  denorm.NormTransform(root_denorm, f_pos, &pos_normed);
1009  window->SetCursor(IntCastRounded(pos_normed.x()),
1010  IntCastRounded(pos_normed.y()));
1011  for (int s = 0; s < stepcount; pos += step(s++)) {
1012  int edge_weight = edge_strength_at_index(s);
1013  if (edge_weight == 0) {
1014  // This point has conflicting gradient and step direction, so ignore it.
1015  continue;
1016  }
1017  FCOORD f_pos = sub_pixel_pos_at_index(pos, s);
1018  FCOORD pos_normed;
1019  denorm.NormTransform(root_denorm, f_pos, &pos_normed);
1020  window->DrawTo(IntCastRounded(pos_normed.x()),
1021  IntCastRounded(pos_normed.y()));
1022  }
1023 }
1024 #endif
1025 
1034  box = source.box;
1035  start = source.start;
1036  if (steps != NULL)
1037  free_mem(steps);
1038  stepcount = source.stepcount;
1039  steps = (uinT8 *) alloc_mem (step_mem());
1040  memmove (steps, source.steps, step_mem());
1041  if (!children.empty ())
1042  children.clear ();
1043  children.deep_copy(&source.children, &deep_copy);
1044  delete [] offsets;
1045  if (source.offsets != NULL) {
1046  offsets = new EdgeOffset[stepcount];
1047  memcpy(offsets, source.offsets, stepcount * sizeof(*offsets));
1048  } else {
1049  offsets = NULL;
1050  }
1051  return *this;
1052 }
1053 
1060 void C_OUTLINE::increment_step(int s, int increment, ICOORD* pos,
1061  int* dir_counts, int* pos_totals) const {
1062  int step_index = Modulo(s, stepcount);
1063  int dir_index = chain_code(step_index);
1064  dir_counts[dir_index] += increment;
1065  ICOORD step_vec = step(step_index);
1066  if (step_vec.x() == 0)
1067  pos_totals[dir_index] += pos->x() * increment;
1068  else
1069  pos_totals[dir_index] += pos->y() * increment;
1070  *pos += step_vec;
1071 }
1072 
1074  return step_coords[chaindir % 4];
1075 }
C_OUTLINE()
Definition: coutln.h:71
#define ELISTIZE(CLASSNAME)
Definition: elst.h:961
void DrawTo(int x, int y)
Definition: scrollview.cpp:531
inT32 count_transitions(inT32 threshold)
Definition: coutln.cpp:340
void rotate(const FCOORD &vec)
Definition: rect.h:189
static uinT8 binary_angle_plus_pi(double angle)
Definition: points.cpp:124
int count(LIST var_list)
Definition: oldlist.cpp:103
inT8 stepdir
Definition: crakedge.h:33
void render_outline(int left, int top, Pix *pix) const
Definition: coutln.cpp:939
inT16 winding_number(ICOORD testpt) const
Definition: coutln.cpp:500
bool overlap(const TBOX &box) const
Definition: rect.h:345
#define TRUE
Definition: capi.h:45
BOOL8 operator<(const C_OUTLINE &other) const
Definition: coutln.cpp:467
int IntCastRounded(double x)
Definition: helpers.h:172
inT16 turn_direction() const
Definition: coutln.cpp:537
short inT16
Definition: host.h:33
uinT8 pixel_diff
Definition: coutln.h:61
integer coordinate
Definition: points.h:30
inT32 outer_area() const
Definition: coutln.cpp:308
void SetCursor(int x, int y)
Definition: scrollview.cpp:525
#define MAX_INT8
Definition: host.h:51
void render(int left, int top, Pix *pix) const
Definition: coutln.cpp:917
Definition: blobs.h:50
inT16 width() const
Definition: rect.h:111
#define MIN(x, y)
Definition: ndminx.h:28
void NormTransform(const DENORM *first_norm, const TPOINT &pt, TPOINT *transformed) const
Definition: normalis.cpp:334
inT32 pathlength() const
Definition: coutln.h:133
ICOORD step(int index) const
Definition: coutln.h:142
T ClipToRange(const T &x, const T &lower_bound, const T &upper_bound)
Definition: helpers.h:115
ICOORD pos
Definition: crakedge.h:30
inT8 offset_numerator
Definition: coutln.h:60
unsigned char uinT8
Definition: host.h:32
inT8 get_dir() const
Definition: mod128.h:77
unsigned char BOOL8
Definition: host.h:46
inT16 y
Definition: blobs.h:72
BOOL8 flag(C_OUTLINE_FLAGS mask) const
Definition: coutln.h:96
void set_step(inT16 stepindex, inT8 stepdir)
Definition: coutln.h:114
inT16 bottom() const
Definition: rect.h:61
#define MAX_UINT8
Definition: host.h:54
inT32 perimeter() const
Definition: coutln.cpp:289
void move(const ICOORD vec)
Definition: rect.h:153
int chain_code(int index) const
Definition: coutln.h:193
inT32 area() const
Definition: coutln.cpp:255
bool contains(const FCOORD pt) const
Definition: rect.h:323
void RemoveSmallRecursive(int min_size, C_OUTLINE_IT *it)
Definition: coutln.cpp:627
C_OUTLINE & operator=(const C_OUTLINE &source)
Definition: coutln.cpp:1033
#define FALSE
Definition: capi.h:46
void * alloc_mem(inT32 count)
Definition: memry.cpp:47
inT16 x() const
access function
Definition: points.h:52
void ComputeEdgeOffsets(int threshold, Pix *pix)
Definition: coutln.cpp:742
SIGNED char inT8
Definition: host.h:31
C_OUTLINE_LIST * child()
Definition: coutln.h:106
inT16 left() const
Definition: rect.h:68
void Pen(Color color)
Definition: scrollview.cpp:726
float y() const
Definition: points.h:212
Definition: mod128.h:29
bool IsLegallyNested() const
Definition: coutln.cpp:604
#define INTERSECTING
Definition: coutln.h:32
int edge_strength_at_index(int index) const
Definition: coutln.h:185
void rotate(const FCOORD &vec)
Definition: ipoints.h:241
inT16 x
Definition: blobs.h:71
inT32 area() const
Definition: rect.h:118
int direction(EDGEPT *point)
Definition: vecfuncs.cpp:43
void plot(ScrollView *window, ScrollView::Color colour) const
Definition: coutln.cpp:965
inT16 height() const
Definition: rect.h:104
#define MAX(x, y)
Definition: ndminx.h:24
int inT32
Definition: host.h:35
void Rectangle(int x1, int y1, int x2, int y2)
Definition: scrollview.cpp:606
static ICOORD chain_step(int chaindir)
Definition: coutln.cpp:1073
Definition: points.h:189
uinT8 direction
Definition: coutln.h:62
ICOORD botright() const
Definition: rect.h:92
CRACKEDGE * next
Definition: crakedge.h:35
void set_y(inT16 yin)
rewrite function
Definition: points.h:65
inT16 top() const
Definition: rect.h:54
ICOORD topleft() const
Definition: rect.h:96
#define MODULUS
Definition: mod128.h:25
void set_x(inT16 xin)
rewrite function
Definition: points.h:61
const ICOORD & start_pos() const
Definition: coutln.h:146
int Modulo(int a, int b)
Definition: helpers.h:157
float x() const
Definition: points.h:209
DIR128 step_dir(int index) const
Definition: coutln.h:137
Definition: rect.h:30
inT16 right() const
Definition: rect.h:75
void plot_normed(const DENORM &denorm, ScrollView::Color colour, ScrollView *window) const
Definition: coutln.cpp:997
static C_OUTLINE * deep_copy(const C_OUTLINE *src)
Definition: coutln.h:259
static void FakeOutline(const TBOX &box, C_OUTLINE_LIST *outlines)
Definition: coutln.cpp:239
void free_mem(void *oldchunk)
Definition: memry.cpp:55
void move(const ICOORD vec)
Definition: coutln.cpp:588
FCOORD sub_pixel_pos_at_index(const ICOORD &pos, int index) const
Definition: coutln.h:161
void ComputeBinaryOffsets()
Definition: coutln.cpp:859
#define ASSERT_HOST(x)
Definition: errcode.h:84
const DENORM * RootDenorm() const
Definition: normalis.h:260
void reverse()
Definition: coutln.cpp:565
inT16 y() const
access_function
Definition: points.h:56