tesseract  4.0.0-1-g2a2b
coutln.cpp
Go to the documentation of this file.
1 /**********************************************************************
2  * File: coutln.cpp (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 "coutln.h"
21 #include <algorithm> // for max, min
22 #include <cmath> // for abs
23 #include <cstdlib> // for abs
24 #include <cstring> // for memset, memcpy, memmove
25 #include "allheaders.h" // for pixSetPixel, pixGetData, pixRasterop, pixGe...
26 #include "arrayaccess.h" // for GET_DATA_BYTE
27 #include "blobs.h" // for TPOINT
28 #include "crakedge.h" // for CRACKEDGE
29 #include "environ.h" // for l_uint32
30 #include "errcode.h" // for ASSERT_HOST
31 #include "helpers.h" // for ClipToRange, IntCastRounded, Modulo
32 #include "normalis.h" // for DENORM
33 #include "pix.h" // for Pix (ptr only), PIX_DST, PIX_NOT
34 
35 // Include automatically generated configuration file if running autoconf.
36 #ifdef HAVE_CONFIG_H
37 #include "config_auto.h"
38 #endif
39 
41 ICOORD C_OUTLINE::step_coords[4] = {
42  ICOORD (-1, 0), ICOORD (0, -1), ICOORD (1, 0), ICOORD (0, 1)
43 };
44 
55 C_OUTLINE::C_OUTLINE(CRACKEDGE* startpt, ICOORD bot_left, ICOORD top_right,
56  int16_t length)
57  : box(bot_left, top_right), start(startpt->pos), offsets(nullptr) {
58  int16_t stepindex; //index to step
59  CRACKEDGE *edgept; //current point
60 
61  stepcount = length; //no of steps
62  if (length == 0) {
63  steps = nullptr;
64  return;
65  }
66  //get memory
67  steps = (uint8_t *)calloc(step_mem(), 1);
68  edgept = startpt;
69 
70  for (stepindex = 0; stepindex < length; stepindex++) {
71  //set compact step
72  set_step (stepindex, edgept->stepdir);
73  edgept = edgept->next;
74  }
75 }
76 
83 //constructor
84  //steps to copy
85 ICOORD startpt, DIR128 * new_steps,
86 int16_t length //length of loop
87 ):start (startpt), offsets(nullptr) {
88  int8_t dirdiff; //direction difference
89  DIR128 prevdir; //previous direction
90  DIR128 dir; //current direction
91  DIR128 lastdir; //dir of last step
92  TBOX new_box; //easy bounding
93  int16_t stepindex; //index to step
94  int16_t srcindex; //source steps
95  ICOORD pos; //current position
96 
97  pos = startpt;
98  stepcount = length; // No. of steps.
99  ASSERT_HOST(length >= 0);
100  steps = static_cast<uint8_t*>(calloc(step_mem(), 1)); // Get memory.
101 
102  lastdir = new_steps[length - 1];
103  prevdir = lastdir;
104  for (stepindex = 0, srcindex = 0; srcindex < length;
105  stepindex++, srcindex++) {
106  new_box = TBOX (pos, pos);
107  box += new_box;
108  //copy steps
109  dir = new_steps[srcindex];
110  set_step(stepindex, dir);
111  dirdiff = dir - prevdir;
112  pos += step (stepindex);
113  if ((dirdiff == 64 || dirdiff == -64) && stepindex > 0) {
114  stepindex -= 2; //cancel there-and-back
115  prevdir = stepindex >= 0 ? step_dir (stepindex) : lastdir;
116  }
117  else
118  prevdir = dir;
119  }
120  ASSERT_HOST (pos.x () == startpt.x () && pos.y () == startpt.y ());
121  do {
122  dirdiff = step_dir (stepindex - 1) - step_dir (0);
123  if (dirdiff == 64 || dirdiff == -64) {
124  start += step (0);
125  stepindex -= 2; //cancel there-and-back
126  for (int i = 0; i < stepindex; ++i)
127  set_step(i, step_dir(i + 1));
128  }
129  }
130  while (stepindex > 1 && (dirdiff == 64 || dirdiff == -64));
131  stepcount = stepindex;
132  ASSERT_HOST (stepcount >= 4);
133 }
134 
143 C_OUTLINE::C_OUTLINE(C_OUTLINE* srcline, FCOORD rotation) : offsets(nullptr) {
144  TBOX new_box; //easy bounding
145  int16_t stepindex; //index to step
146  int16_t dirdiff; //direction change
147  ICOORD pos; //current position
148  ICOORD prevpos; //previous dest point
149 
150  ICOORD destpos; //destination point
151  int16_t destindex = INT16_MAX; //index to step
152  DIR128 dir; //coded direction
153  uint8_t new_step;
154 
155  stepcount = srcline->stepcount * 2;
156  if (stepcount == 0) {
157  steps = nullptr;
158  box = srcline->box;
159  box.rotate(rotation);
160  return;
161  }
162  //get memory
163  steps = (uint8_t *)calloc(step_mem(), 1);
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 
256 int32_t C_OUTLINE::area() const {
257  int stepindex; //current step
258  int32_t total_steps; //steps to do
259  int32_t 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 
290 int32_t C_OUTLINE::perimeter() const {
291  int32_t 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 
309 int32_t C_OUTLINE::outer_area() const {
310  int stepindex; //current step
311  int32_t total_steps; //steps to do
312  int32_t total; //total area
313  ICOORD pos; //position of point
314  ICOORD next_step; //step to next pix
315 
316  pos = start_pos ();
317  total_steps = pathlength ();
318  if (total_steps == 0)
319  return box.area();
320  total = 0;
321  for (stepindex = 0; stepindex < total_steps; stepindex++) {
322  //all intersected
323  next_step = step (stepindex);
324  if (next_step.x () < 0)
325  total += pos.y ();
326  else if (next_step.x () > 0)
327  total -= pos.y ();
328  pos += next_step;
329  }
330 
331  return total;
332 }
333 
341 int32_t C_OUTLINE::count_transitions(int32_t threshold) {
342  bool first_was_max_x; //what was first
343  bool first_was_max_y;
344  bool looking_for_max_x; //what is next
345  bool looking_for_min_x;
346  bool looking_for_max_y; //what is next
347  bool looking_for_min_y;
348  int stepindex; //current step
349  int32_t total_steps; //steps to do
350  //current limits
351  int32_t max_x, min_x, max_y, min_y;
352  int32_t initial_x, initial_y; //initial limits
353  int32_t total; //total changes
354  ICOORD pos; //position of point
355  ICOORD next_step; //step to next pix
356 
357  pos = start_pos();
358  total_steps = pathlength();
359  total = 0;
360  max_x = min_x = pos.x();
361  max_y = min_y = pos.y();
362  looking_for_max_x = true;
363  looking_for_min_x = true;
364  looking_for_max_y = true;
365  looking_for_min_y = true;
366  first_was_max_x = false;
367  first_was_max_y = false;
368  initial_x = pos.x();
369  initial_y = pos.y(); //stop uninit warning
370  for (stepindex = 0; stepindex < total_steps; stepindex++) {
371  //all intersected
372  next_step = step(stepindex);
373  pos += next_step;
374  if (next_step.x() < 0) {
375  if (looking_for_max_x && pos.x() < min_x)
376  min_x = pos.x();
377  if (looking_for_min_x && max_x - pos.x() > threshold) {
378  if (looking_for_max_x) {
379  initial_x = max_x;
380  first_was_max_x = false;
381  }
382  total++;
383  looking_for_max_x = true;
384  looking_for_min_x = false;
385  min_x = pos.x(); //reset min
386  }
387  }
388  else if (next_step.x() > 0) {
389  if (looking_for_min_x && pos.x() > max_x)
390  max_x = pos.x();
391  if (looking_for_max_x && pos.x() - min_x > threshold) {
392  if (looking_for_min_x) {
393  initial_x = min_x; //remember first min
394  first_was_max_x = true;
395  }
396  total++;
397  looking_for_max_x = false;
398  looking_for_min_x = true;
399  max_x = pos.x();
400  }
401  }
402  else if (next_step.y() < 0) {
403  if (looking_for_max_y && pos.y() < min_y)
404  min_y = pos.y();
405  if (looking_for_min_y && max_y - pos.y() > threshold) {
406  if (looking_for_max_y) {
407  initial_y = max_y; //remember first max
408  first_was_max_y = false;
409  }
410  total++;
411  looking_for_max_y = true;
412  looking_for_min_y = false;
413  min_y = pos.y(); //reset min
414  }
415  }
416  else {
417  if (looking_for_min_y && pos.y() > max_y)
418  max_y = pos.y();
419  if (looking_for_max_y && pos.y() - min_y > threshold) {
420  if (looking_for_min_y) {
421  initial_y = min_y; //remember first min
422  first_was_max_y = true;
423  }
424  total++;
425  looking_for_max_y = false;
426  looking_for_min_y = true;
427  max_y = pos.y();
428  }
429  }
430 
431  }
432  if (first_was_max_x && looking_for_min_x) {
433  if (max_x - initial_x > threshold)
434  total++;
435  else
436  total--;
437  }
438  else if (!first_was_max_x && looking_for_max_x) {
439  if (initial_x - min_x > threshold)
440  total++;
441  else
442  total--;
443  }
444  if (first_was_max_y && looking_for_min_y) {
445  if (max_y - initial_y > threshold)
446  total++;
447  else
448  total--;
449  }
450  else if (!first_was_max_y && looking_for_max_y) {
451  if (initial_y - min_y > threshold)
452  total++;
453  else
454  total--;
455  }
456 
457  return total;
458 }
459 
467 bool
468 C_OUTLINE::operator<(const C_OUTLINE& other) const {
469  int16_t count = 0; //winding count
470  ICOORD pos; //position of point
471  int32_t stepindex; //index to cstep
472 
473  if (!box.overlap (other.box))
474  return false; //can't be contained
475  if (stepcount == 0)
476  return other.box.contains(this->box);
477 
478  pos = start;
479  for (stepindex = 0; stepindex < stepcount
480  && (count = other.winding_number (pos)) == INTERSECTING; stepindex++)
481  pos += step (stepindex); //try all points
482  if (count == INTERSECTING) {
483  //all intersected
484  pos = other.start;
485  for (stepindex = 0; stepindex < other.stepcount
486  && (count = winding_number (pos)) == INTERSECTING; stepindex++)
487  //try other way round
488  pos += other.step (stepindex);
489  return count == INTERSECTING || count == 0;
490  }
491  return count != 0;
492 }
493 
501 int16_t C_OUTLINE::winding_number(ICOORD point) const {
502  int16_t stepindex; //index to cstep
503  int16_t count; //winding count
504  ICOORD vec; //to current point
505  ICOORD stepvec; //step vector
506  int32_t cross; //cross product
507 
508  vec = start - point; //vector to it
509  count = 0;
510  for (stepindex = 0; stepindex < stepcount; stepindex++) {
511  stepvec = step (stepindex); //get the step
512  //crossing the line
513  if (vec.y () <= 0 && vec.y () + stepvec.y () > 0) {
514  cross = vec * stepvec; //cross product
515  if (cross > 0)
516  count++; //crossing right half
517  else if (cross == 0)
518  return INTERSECTING; //going through point
519  }
520  else if (vec.y () > 0 && vec.y () + stepvec.y () <= 0) {
521  cross = vec * stepvec;
522  if (cross < 0)
523  count--; //crossing back
524  else if (cross == 0)
525  return INTERSECTING; //illegal
526  }
527  vec += stepvec; //sum vectors
528  }
529  return count; //winding number
530 }
531 
538 int16_t C_OUTLINE::turn_direction() const { //winding number
539  DIR128 prevdir; //previous direction
540  DIR128 dir; //current direction
541  int16_t stepindex; //index to cstep
542  int8_t dirdiff; //direction difference
543  int16_t count; //winding count
544 
545  if (stepcount == 0)
546  return 128;
547  count = 0;
548  prevdir = step_dir (stepcount - 1);
549  for (stepindex = 0; stepindex < stepcount; stepindex++) {
550  dir = step_dir (stepindex);
551  dirdiff = dir - prevdir;
552  ASSERT_HOST (dirdiff == 0 || dirdiff == 32 || dirdiff == -32);
553  count += dirdiff;
554  prevdir = dir;
555  }
556  ASSERT_HOST (count == 128 || count == -128);
557  return count; //winding number
558 }
559 
566 void C_OUTLINE::reverse() { //reverse drection
567  DIR128 halfturn = MODULUS / 2; //amount to shift
568  DIR128 stepdir; //direction of step
569  int16_t stepindex; //index to cstep
570  int16_t farindex; //index to other side
571  int16_t halfsteps; //half of stepcount
572 
573  halfsteps = (stepcount + 1) / 2;
574  for (stepindex = 0; stepindex < halfsteps; stepindex++) {
575  farindex = stepcount - stepindex - 1;
576  stepdir = step_dir (stepindex);
577  set_step (stepindex, step_dir (farindex) + halfturn);
578  set_step (farindex, stepdir + halfturn);
579  }
580 }
581 
589 void C_OUTLINE::move(const ICOORD vec) {
590  C_OUTLINE_IT it(&children); // iterator
591 
592  box.move (vec);
593  start += vec;
594 
595  for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ())
596  it.data ()->move (vec); // move child outlines
597 }
598 
606  if (stepcount == 0) return true;
607  int64_t parent_area = outer_area();
608  // We aren't going to modify the list, or its contents, but there is
609  // no const iterator.
610  C_OUTLINE_IT child_it(const_cast<C_OUTLINE_LIST*>(&children));
611  for (child_it.mark_cycle_pt(); !child_it.cycled_list(); child_it.forward()) {
612  const C_OUTLINE* child = child_it.data();
613  if (child->outer_area() * parent_area > 0 || !child->IsLegallyNested())
614  return false;
615  }
616  return true;
617 }
618 
628 void C_OUTLINE::RemoveSmallRecursive(int min_size, C_OUTLINE_IT* it) {
629  if (box.width() < min_size || box.height() < min_size) {
630  ASSERT_HOST(this == it->data());
631  delete it->extract(); // Too small so get rid of it and any children.
632  } else if (!children.empty()) {
633  // Search the children of this, deleting any that are too small.
634  C_OUTLINE_IT child_it(&children);
635  for (child_it.mark_cycle_pt(); !child_it.cycled_list();
636  child_it.forward()) {
637  C_OUTLINE* child = child_it.data();
638  child->RemoveSmallRecursive(min_size, &child_it);
639  }
640  }
641 }
642 
643 // Factored out helpers below are used only by ComputeEdgeOffsets to operate
644 // on data from an 8-bit Pix, and assume that any input x and/or y are already
645 // constrained to be legal Pix coordinates.
646 
652 static void ComputeGradient(const l_uint32* data, int wpl,
653  int x, int y, int width, int height,
654  ICOORD* gradient) {
655  const l_uint32* line = data + y * wpl;
656  int pix_x_y = x < width && y < height ? GET_DATA_BYTE(line, x) : 255;
657  int pix_x_prevy = x < width && y > 0 ? GET_DATA_BYTE(line - wpl, x) : 255;
658  int pix_prevx_prevy = x > 0 && y > 0 ? GET_DATA_BYTE(line - wpl, x - 1) : 255;
659  int pix_prevx_y = x > 0 && y < height ? GET_DATA_BYTE(line, x - 1) : 255;
660  gradient->set_x(pix_x_y + pix_x_prevy - (pix_prevx_y + pix_prevx_prevy));
661  gradient->set_y(pix_x_prevy + pix_prevx_prevy - (pix_x_y + pix_prevx_y));
662 }
663 
669 static bool EvaluateVerticalDiff(const l_uint32* data, int wpl, int diff_sign,
670  int x, int y, int height,
671  int* best_diff, int* best_sum, int* best_y) {
672  if (y <= 0 || y >= height)
673  return false;
674  const l_uint32* line = data + y * wpl;
675  int pixel1 = GET_DATA_BYTE(line - wpl, x);
676  int pixel2 = GET_DATA_BYTE(line, x);
677  int diff = (pixel2 - pixel1) * diff_sign;
678  if (diff > *best_diff) {
679  *best_diff = diff;
680  *best_sum = pixel1 + pixel2;
681  *best_y = y;
682  }
683  return diff > 0;
684 }
685 
691 static bool EvaluateHorizontalDiff(const l_uint32* line, int diff_sign,
692  int x, int width,
693  int* best_diff, int* best_sum, int* best_x) {
694  if (x <= 0 || x >= width)
695  return false;
696  int pixel1 = GET_DATA_BYTE(line, x - 1);
697  int pixel2 = GET_DATA_BYTE(line, x);
698  int diff = (pixel2 - pixel1) * diff_sign;
699  if (diff > *best_diff) {
700  *best_diff = diff;
701  *best_sum = pixel1 + pixel2;
702  *best_x = x;
703  }
704  return diff > 0;
705 }
706 
722 void C_OUTLINE::ComputeEdgeOffsets(int threshold, Pix* pix) {
723  if (pixGetDepth(pix) != 8) return;
724  const l_uint32* data = pixGetData(pix);
725  int wpl = pixGetWpl(pix);
726  int width = pixGetWidth(pix);
727  int height = pixGetHeight(pix);
728  bool negative = flag(COUT_INVERSE);
729  delete [] offsets;
730  offsets = new EdgeOffset[stepcount];
731  ICOORD pos = start;
732  ICOORD prev_gradient;
733  ComputeGradient(data, wpl, pos.x(), height - pos.y(), width, height,
734  &prev_gradient);
735  for (int s = 0; s < stepcount; ++s) {
736  ICOORD step_vec = step(s);
737  TPOINT pt1(pos);
738  pos += step_vec;
739  TPOINT pt2(pos);
740  ICOORD next_gradient;
741  ComputeGradient(data, wpl, pos.x(), height - pos.y(), width, height,
742  &next_gradient);
743  // Use the sum of the prev and next as the working gradient.
744  ICOORD gradient = prev_gradient + next_gradient;
745  // best_diff will be manipulated to be always positive.
746  int best_diff = 0;
747  // offset will be the extrapolation of the location of the greyscale
748  // threshold from the edge with the largest difference, relative to the
749  // location of the binary edge.
750  int offset = 0;
751  if (pt1.y == pt2.y && abs(gradient.y()) * 2 >= abs(gradient.x())) {
752  // Horizontal step. diff_sign == 1 indicates black above.
753  int diff_sign = (pt1.x > pt2.x) == negative ? 1 : -1;
754  int x = std::min(pt1.x, pt2.x);
755  int y = height - pt1.y;
756  int best_sum = 0;
757  int best_y = y;
758  EvaluateVerticalDiff(data, wpl, diff_sign, x, y, height,
759  &best_diff, &best_sum, &best_y);
760  // Find the strongest edge.
761  int test_y = y;
762  do {
763  ++test_y;
764  } while (EvaluateVerticalDiff(data, wpl, diff_sign, x, test_y, height,
765  &best_diff, &best_sum, &best_y));
766  test_y = y;
767  do {
768  --test_y;
769  } while (EvaluateVerticalDiff(data, wpl, diff_sign, x, test_y, height,
770  &best_diff, &best_sum, &best_y));
771  offset = diff_sign * (best_sum / 2 - threshold) +
772  (y - best_y) * best_diff;
773  } else if (pt1.x == pt2.x && abs(gradient.x()) * 2 >= abs(gradient.y())) {
774  // Vertical step. diff_sign == 1 indicates black on the left.
775  int diff_sign = (pt1.y > pt2.y) == negative ? 1 : -1;
776  int x = pt1.x;
777  int y = height - std::max(pt1.y, pt2.y);
778  const l_uint32* line = pixGetData(pix) + y * wpl;
779  int best_sum = 0;
780  int best_x = x;
781  EvaluateHorizontalDiff(line, diff_sign, x, width,
782  &best_diff, &best_sum, &best_x);
783  // Find the strongest edge.
784  int test_x = x;
785  do {
786  ++test_x;
787  } while (EvaluateHorizontalDiff(line, diff_sign, test_x, width,
788  &best_diff, &best_sum, &best_x));
789  test_x = x;
790  do {
791  --test_x;
792  } while (EvaluateHorizontalDiff(line, diff_sign, test_x, width,
793  &best_diff, &best_sum, &best_x));
794  offset = diff_sign * (threshold - best_sum / 2) +
795  (best_x - x) * best_diff;
796  }
797  offsets[s].offset_numerator =
798  ClipToRange<int>(offset, -INT8_MAX, INT8_MAX);
799  offsets[s].pixel_diff = ClipToRange<int>(best_diff, 0, UINT8_MAX);
800  if (negative) gradient = -gradient;
801  // Compute gradient angle quantized to 256 directions, rotated by 64 (pi/2)
802  // to convert from gradient direction to edge direction.
803  offsets[s].direction =
804  Modulo(FCOORD::binary_angle_plus_pi(gradient.angle()) + 64, 256);
805  prev_gradient = next_gradient;
806  }
807 }
808 
839  delete [] offsets;
840  offsets = new EdgeOffset[stepcount];
841  // Count of the number of steps in each direction in the sliding window.
842  int dir_counts[4];
843  // Sum of the positions (y for a horizontal step, x for vertical) in each
844  // direction in the sliding window.
845  int pos_totals[4];
846  memset(dir_counts, 0, sizeof(dir_counts));
847  memset(pos_totals, 0, sizeof(pos_totals));
848  ICOORD pos = start;
849  ICOORD tail_pos = pos;
850  // tail_pos is the trailing position, with the next point to be lost from
851  // the window.
852  tail_pos -= step(stepcount - 1);
853  tail_pos -= step(stepcount - 2);
854  // head_pos is the leading position, with the next point to be added to the
855  // window.
856  ICOORD head_pos = tail_pos;
857  // Set up the initial window with 4 points in [-2, 2)
858  for (int s = -2; s < 2; ++s) {
859  increment_step(s, 1, &head_pos, dir_counts, pos_totals);
860  }
861  for (int s = 0; s < stepcount; pos += step(s++)) {
862  // At step s, s in in the middle of [s-2, s+2].
863  increment_step(s + 2, 1, &head_pos, dir_counts, pos_totals);
864  int dir_index = chain_code(s);
865  ICOORD step_vec = step(s);
866  int best_diff = 0;
867  int offset = 0;
868  // Use only steps that have a count of >=2 OR the strong U-turn with a
869  // single d and 2 at d-1 and 2 at d+1 (mod 4).
870  if (dir_counts[dir_index] >= 2 || (dir_counts[dir_index] == 1 &&
871  dir_counts[Modulo(dir_index - 1, 4)] == 2 &&
872  dir_counts[Modulo(dir_index + 1, 4)] == 2)) {
873  // Valid step direction.
874  best_diff = dir_counts[dir_index];
875  int edge_pos = step_vec.x() == 0 ? pos.x() : pos.y();
876  // The offset proposes that the actual step should be positioned at
877  // the mean position of the steps in the window of the same direction.
878  // See ASCII art above.
879  offset = pos_totals[dir_index] - best_diff * edge_pos;
880  }
881  offsets[s].offset_numerator =
882  ClipToRange<int>(offset, -INT8_MAX, INT8_MAX);
883  offsets[s].pixel_diff = ClipToRange<int>(best_diff, 0, UINT8_MAX);
884  // The direction is just the vector from start to end of the window.
885  FCOORD direction(head_pos.x() - tail_pos.x(), head_pos.y() - tail_pos.y());
886  offsets[s].direction = direction.to_direction();
887  increment_step(s - 2, -1, &tail_pos, dir_counts, pos_totals);
888  }
889 }
890 
895 void C_OUTLINE::render(int left, int top, Pix* pix) const {
896  ICOORD pos = start;
897  for (int stepindex = 0; stepindex < stepcount; ++stepindex) {
898  ICOORD next_step = step(stepindex);
899  if (next_step.y() < 0) {
900  pixRasterop(pix, 0, top - pos.y(), pos.x() - left, 1,
901  PIX_NOT(PIX_DST), nullptr, 0, 0);
902  } else if (next_step.y() > 0) {
903  pixRasterop(pix, 0, top - pos.y() - 1, pos.x() - left, 1,
904  PIX_NOT(PIX_DST), nullptr, 0, 0);
905  }
906  pos += next_step;
907  }
908 }
909 
917 void C_OUTLINE::render_outline(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  pixSetPixel(pix, pos.x() - left, top - pos.y(), 1);
923  } else if (next_step.y() > 0) {
924  pixSetPixel(pix, pos.x() - left - 1, top - pos.y() - 1, 1);
925  } else if (next_step.x() < 0) {
926  pixSetPixel(pix, pos.x() - left - 1, top - pos.y(), 1);
927  } else if (next_step.x() > 0) {
928  pixSetPixel(pix, pos.x() - left, top - pos.y() - 1, 1);
929  }
930  pos += next_step;
931  }
932 }
933 
942 #ifndef GRAPHICS_DISABLED
943 void C_OUTLINE::plot(ScrollView* window, ScrollView::Color colour) const {
944  int16_t stepindex; // index to cstep
945  ICOORD pos; // current position
946  DIR128 stepdir; // direction of step
947 
948  pos = start; // current position
949  window->Pen(colour);
950  if (stepcount == 0) {
951  window->Rectangle(box.left(), box.top(), box.right(), box.bottom());
952  return;
953  }
954  window->SetCursor(pos.x(), pos.y());
955 
956  stepindex = 0;
957  while (stepindex < stepcount) {
958  pos += step(stepindex); // step to next
959  stepdir = step_dir(stepindex);
960  stepindex++; // count steps
961  // merge straight lines
962  while (stepindex < stepcount &&
963  stepdir.get_dir() == step_dir(stepindex).get_dir()) {
964  pos += step(stepindex);
965  stepindex++;
966  }
967  window->DrawTo(pos.x(), pos.y());
968  }
969 }
970 
976  ScrollView* window) const {
977  window->Pen(colour);
978  if (stepcount == 0) {
979  window->Rectangle(box.left(), box.top(), box.right(), box.bottom());
980  return;
981  }
982  const DENORM* root_denorm = denorm.RootDenorm();
983  ICOORD pos = start; // current position
984  FCOORD f_pos = sub_pixel_pos_at_index(pos, 0);
985  FCOORD pos_normed;
986  denorm.NormTransform(root_denorm, f_pos, &pos_normed);
987  window->SetCursor(IntCastRounded(pos_normed.x()),
988  IntCastRounded(pos_normed.y()));
989  for (int s = 0; s < stepcount; pos += step(s++)) {
990  int edge_weight = edge_strength_at_index(s);
991  if (edge_weight == 0) {
992  // This point has conflicting gradient and step direction, so ignore it.
993  continue;
994  }
995  FCOORD f_pos = sub_pixel_pos_at_index(pos, s);
996  FCOORD pos_normed;
997  denorm.NormTransform(root_denorm, f_pos, &pos_normed);
998  window->DrawTo(IntCastRounded(pos_normed.x()),
999  IntCastRounded(pos_normed.y()));
1000  }
1001 }
1002 #endif
1003 
1012  box = source.box;
1013  start = source.start;
1014  free(steps);
1015  stepcount = source.stepcount;
1016  steps = (uint8_t *)malloc(step_mem());
1017  memmove (steps, source.steps, step_mem());
1018  if (!children.empty ())
1019  children.clear ();
1020  children.deep_copy(&source.children, &deep_copy);
1021  delete [] offsets;
1022  if (source.offsets != nullptr) {
1023  offsets = new EdgeOffset[stepcount];
1024  memcpy(offsets, source.offsets, stepcount * sizeof(*offsets));
1025  } else {
1026  offsets = nullptr;
1027  }
1028  return *this;
1029 }
1030 
1037 void C_OUTLINE::increment_step(int s, int increment, ICOORD* pos,
1038  int* dir_counts, int* pos_totals) const {
1039  int step_index = Modulo(s, stepcount);
1040  int dir_index = chain_code(step_index);
1041  dir_counts[dir_index] += increment;
1042  ICOORD step_vec = step(step_index);
1043  if (step_vec.x() == 0)
1044  pos_totals[dir_index] += pos->x() * increment;
1045  else
1046  pos_totals[dir_index] += pos->y() * increment;
1047  *pos += step_vec;
1048 }
1049 
1051  return step_coords[chaindir % 4];
1052 }
void ComputeBinaryOffsets()
Definition: coutln.cpp:838
void NormTransform(const DENORM *first_norm, const TPOINT &pt, TPOINT *transformed) const
Definition: normalis.cpp:335
int16_t winding_number(ICOORD testpt) const
Definition: coutln.cpp:501
void DrawTo(int x, int y)
Definition: scrollview.cpp:527
const DENORM * RootDenorm() const
Definition: normalis.h:258
void rotate(const FCOORD &vec)
Definition: rect.h:197
FCOORD sub_pixel_pos_at_index(const ICOORD &pos, int index) const
Definition: coutln.h:163
void set_x(int16_t xin)
rewrite function
Definition: points.h:62
void ComputeEdgeOffsets(int threshold, Pix *pix)
Definition: coutln.cpp:722
int32_t area() const
Definition: coutln.cpp:256
#define INTERSECTING
Definition: coutln.h:35
int count(LIST var_list)
Definition: oldlist.cpp:98
int16_t y() const
access_function
Definition: points.h:57
Definition: rect.h:34
int32_t perimeter() const
Definition: coutln.cpp:290
const ICOORD & start_pos() const
Definition: coutln.h:148
void set_step(int16_t stepindex, int8_t stepdir)
Definition: coutln.h:116
int Modulo(int a, int b)
Definition: helpers.h:153
int direction(EDGEPT *point)
Definition: vecfuncs.cpp:43
bool flag(C_OUTLINE_FLAGS mask) const
Definition: coutln.h:98
void SetCursor(int x, int y)
Definition: scrollview.cpp:521
void move(const ICOORD vec)
Definition: rect.h:157
int16_t width() const
Definition: rect.h:115
static ICOORD chain_step(int chaindir)
Definition: coutln.cpp:1050
int16_t left() const
Definition: rect.h:72
int16_t turn_direction() const
Definition: coutln.cpp:538
int16_t top() const
Definition: rect.h:58
int8_t stepdir
Definition: crakedge.h:33
static uint8_t binary_angle_plus_pi(double angle)
Definition: points.cpp:123
int8_t get_dir() const
Definition: mod128.h:76
void render_outline(int left, int top, Pix *pix) const
Definition: coutln.cpp:917
integer coordinate
Definition: points.h:32
int16_t x() const
access function
Definition: points.h:53
Definition: mod128.h:29
#define MODULUS
Definition: mod128.h:25
int edge_strength_at_index(int index) const
Definition: coutln.h:187
int IntCastRounded(double x)
Definition: helpers.h:168
uint8_t pixel_diff
Definition: coutln.h:64
ICOORD pos
Definition: crakedge.h:30
void render(int left, int top, Pix *pix) const
Definition: coutln.cpp:895
#define ELISTIZE(CLASSNAME)
Definition: elst.h:961
C_OUTLINE & operator=(const C_OUTLINE &source)
Definition: coutln.cpp:1011
int32_t pathlength() const
Definition: coutln.h:135
C_OUTLINE()
Definition: coutln.h:74
int32_t area() const
Definition: rect.h:122
CRACKEDGE * next
Definition: crakedge.h:35
void plot(ScrollView *window, ScrollView::Color colour) const
Definition: coutln.cpp:943
static C_OUTLINE * deep_copy(const C_OUTLINE *src)
Definition: coutln.h:261
void rotate(const FCOORD &vec)
Definition: points.h:537
C_OUTLINE_LIST * child()
Definition: coutln.h:108
void plot_normed(const DENORM &denorm, ScrollView::Color colour, ScrollView *window) const
Definition: coutln.cpp:975
int16_t x
Definition: blobs.h:78
ICOORD topleft() const
Definition: rect.h:100
void reverse()
Definition: coutln.cpp:566
bool operator<(const C_OUTLINE &other) const
Definition: coutln.cpp:468
void move(const ICOORD vec)
Definition: coutln.cpp:589
bool overlap(const TBOX &box) const
Definition: rect.h:355
int32_t count_transitions(int32_t threshold)
Definition: coutln.cpp:341
bool contains(const FCOORD pt) const
Definition: rect.h:333
void RemoveSmallRecursive(int min_size, C_OUTLINE_IT *it)
Definition: coutln.cpp:628
Definition: points.h:189
bool IsLegallyNested() const
Definition: coutln.cpp:605
uint8_t direction
Definition: coutln.h:65
int16_t right() const
Definition: rect.h:79
float x() const
Definition: points.h:208
ICOORD botright() const
Definition: rect.h:96
DIR128 step_dir(int index) const
Definition: coutln.h:139
void Rectangle(int x1, int y1, int x2, int y2)
Definition: scrollview.cpp:602
int chain_code(int index) const
Definition: coutln.h:195
static void FakeOutline(const TBOX &box, C_OUTLINE_LIST *outlines)
Definition: coutln.cpp:240
Definition: blobs.h:57
int8_t offset_numerator
Definition: coutln.h:63
int16_t y
Definition: blobs.h:79
void Pen(Color color)
Definition: scrollview.cpp:722
void set_y(int16_t yin)
rewrite function
Definition: points.h:66
int16_t bottom() const
Definition: rect.h:65
int32_t outer_area() const
Definition: coutln.cpp:309
ICOORD step(int index) const
Definition: coutln.h:144
int16_t height() const
Definition: rect.h:108
float y() const
Definition: points.h:211
#define ASSERT_HOST(x)
Definition: errcode.h:84