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