tesseract  4.0.0-1-g2a2b
fpchop.cpp
Go to the documentation of this file.
1 /**********************************************************************
2  * File: fpchop.cpp (Formerly fp_chop.c)
3  * Description: Code to chop fixed pitch text into character cells.
4  * Author: Ray Smith
5  * Created: Thu Sep 16 11:14:15 BST 1993
6  *
7  * (C) Copyright 1993, 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 "blobbox.h"
21 #include "statistc.h"
22 #include "drawtord.h"
23 #include "tovars.h"
24 #include "topitch.h"
25 #include "fpchop.h"
26 
27 // Include automatically generated configuration file if running autoconf.
28 #ifdef HAVE_CONFIG_H
29 #include "config_auto.h"
30 #endif
31 
32 #define EXTERN
33 
35 "Max allowed bending of chop cells");
37 "Max distance of chop pt from vertex");
38 
40 //#undef ASSERT_HOST
41 //#define ASSERT_HOST(x) if (!(x)) AfxMessageBox(#x);
42 /**********************************************************************
43  * fixed_pitch_words
44  *
45  * Make a ROW from a fixed pitch TO_ROW.
46  **********************************************************************/
47 ROW *fixed_pitch_words( //find lines
48  TO_ROW *row, //row to do
49  FCOORD rotation //for drawing
50  ) {
51  bool bol; //start of line
52  uint8_t blanks; //in front of word
53  uint8_t new_blanks; //blanks in empty cell
54  int16_t chop_coord; //chop boundary
55  int16_t prev_chop_coord; //start of cell
56  int16_t rep_left; //left edge of rep word
57  ROW *real_row; //output row
58  C_OUTLINE_LIST left_coutlines;
59  C_OUTLINE_LIST right_coutlines;
60  C_BLOB_LIST cblobs;
61  C_BLOB_IT cblob_it = &cblobs;
62  WERD_LIST words;
63  WERD_IT word_it = &words; //new words
64  //repeated blobs
65  WERD_IT rep_it = &row->rep_words;
66  WERD *word; //new word
67  int32_t xstarts[2]; //row ends
68  int32_t prev_x; //end of prev blob
69  //iterator
70  BLOBNBOX_IT box_it = row->blob_list ();
71  //boundaries
72  ICOORDELT_IT cell_it = &row->char_cells;
73 
74 #ifndef GRAPHICS_DISABLED
75  if (textord_show_page_cuts && to_win != nullptr) {
76  plot_row_cells (to_win, ScrollView::RED, row, 0, &row->char_cells);
77  }
78 #endif
79 
80  prev_x = -INT16_MAX;
81  bol = true;
82  blanks = 0;
83  if (rep_it.empty ())
84  rep_left = INT16_MAX;
85  else
86  rep_left = rep_it.data ()->bounding_box ().left ();
87  if (box_it.empty ())
88  return nullptr; //empty row
89  xstarts[0] = box_it.data ()->bounding_box ().left ();
90  if (rep_left < xstarts[0]) {
91  xstarts[0] = rep_left;
92  }
93  if (cell_it.empty () || row->char_cells.singleton ()) {
94  tprintf ("Row without enough char cells!\n");
95  tprintf ("Leftmost blob is at (%d,%d)\n",
96  box_it.data ()->bounding_box ().left (),
97  box_it.data ()->bounding_box ().bottom ());
98  return nullptr;
99  }
100  ASSERT_HOST (!cell_it.empty () && !row->char_cells.singleton ());
101  prev_chop_coord = cell_it.data ()->x ();
102  word = nullptr;
103  while (rep_left < cell_it.data ()->x ()) {
104  word = add_repeated_word (&rep_it, rep_left, prev_chop_coord,
105  blanks, row->fixed_pitch, &word_it);
106  }
107  cell_it.mark_cycle_pt ();
108  if (prev_chop_coord >= cell_it.data ()->x ())
109  cell_it.forward ();
110  for (; !cell_it.cycled_list (); cell_it.forward ()) {
111  chop_coord = cell_it.data ()->x ();
112  while (!box_it.empty ()
113  && box_it.data ()->bounding_box ().left () <= chop_coord) {
114  if (box_it.data ()->bounding_box ().right () > prev_x)
115  prev_x = box_it.data ()->bounding_box ().right ();
116  split_to_blob (box_it.extract (), chop_coord,
117  textord_fp_chop_error + 0.5f,
118  &left_coutlines,
119  &right_coutlines);
120  box_it.forward ();
121  while (!box_it.empty() && box_it.data()->cblob() == nullptr) {
122  delete box_it.extract();
123  box_it.forward();
124  }
125  }
126  if (!right_coutlines.empty() && left_coutlines.empty())
127  split_to_blob (nullptr, chop_coord,
128  textord_fp_chop_error + 0.5f,
129  &left_coutlines,
130  &right_coutlines);
131  if (!left_coutlines.empty()) {
132  cblob_it.add_after_then_move(new C_BLOB(&left_coutlines));
133  } else {
134  if (rep_left < chop_coord) {
135  if (rep_left > prev_chop_coord)
136  new_blanks = (uint8_t) floor ((rep_left - prev_chop_coord)
137  / row->fixed_pitch + 0.5);
138  else
139  new_blanks = 0;
140  }
141  else {
142  if (chop_coord > prev_chop_coord)
143  new_blanks = (uint8_t) floor ((chop_coord - prev_chop_coord)
144  / row->fixed_pitch + 0.5);
145  else
146  new_blanks = 0;
147  }
148  if (!cblob_it.empty()) {
149  if (blanks < 1 && word != nullptr && !word->flag (W_REP_CHAR))
150  blanks = 1;
151  word = new WERD (&cblobs, blanks, nullptr);
152  cblob_it.set_to_list (&cblobs);
153  word->set_flag (W_DONT_CHOP, TRUE);
154  word_it.add_after_then_move (word);
155  if (bol) {
156  word->set_flag (W_BOL, TRUE);
157  bol = false;
158  }
159  blanks = new_blanks;
160  }
161  else
162  blanks += new_blanks;
163  while (rep_left < chop_coord) {
164  word = add_repeated_word (&rep_it, rep_left, prev_chop_coord,
165  blanks, row->fixed_pitch, &word_it);
166  }
167  }
168  if (prev_chop_coord < chop_coord)
169  prev_chop_coord = chop_coord;
170  }
171  if (!cblob_it.empty()) {
172  word = new WERD(&cblobs, blanks, nullptr);
173  word->set_flag (W_DONT_CHOP, TRUE);
174  word_it.add_after_then_move (word);
175  if (bol)
176  word->set_flag (W_BOL, TRUE);
177  }
178  ASSERT_HOST (word != nullptr);
179  while (!rep_it.empty ()) {
180  add_repeated_word (&rep_it, rep_left, prev_chop_coord,
181  blanks, row->fixed_pitch, &word_it);
182  }
183  //at end of line
184  word_it.data ()->set_flag (W_EOL, TRUE);
185  if (prev_chop_coord > prev_x)
186  prev_x = prev_chop_coord;
187  xstarts[1] = prev_x + 1;
188  real_row = new ROW (row, (int16_t) row->kern_size, (int16_t) row->space_size);
189  word_it.set_to_list (real_row->word_list ());
190  //put words in row
191  word_it.add_list_after (&words);
192  real_row->recalc_bounding_box ();
193  return real_row;
194 }
195 
196 
197 /**********************************************************************
198  * add_repeated_word
199  *
200  * Add repeated word into the row at the given point.
201  **********************************************************************/
202 
203 WERD *add_repeated_word( //move repeated word
204  WERD_IT *rep_it, //repeated words
205  int16_t &rep_left, //left edge of word
206  int16_t &prev_chop_coord, //previous word end
207  uint8_t &blanks, //no of blanks
208  float pitch, //char cell size
209  WERD_IT *word_it //list of words
210  ) {
211  WERD *word; //word to move
212  int16_t new_blanks; //extra blanks
213 
214  if (rep_left > prev_chop_coord) {
215  new_blanks = (uint8_t) floor ((rep_left - prev_chop_coord) / pitch + 0.5);
216  blanks += new_blanks;
217  }
218  word = rep_it->extract ();
219  prev_chop_coord = word->bounding_box ().right ();
220  word_it->add_after_then_move (word);
221  word->set_blanks (blanks);
222  rep_it->forward ();
223  if (rep_it->empty ())
224  rep_left = INT16_MAX;
225  else
226  rep_left = rep_it->data ()->bounding_box ().left ();
227  blanks = 0;
228  return word;
229 }
230 
231 
232 /**********************************************************************
233  * split_to_blob
234  *
235  * Split a BLOBNBOX across a vertical chop line and put the pieces
236  * into a left outline list and a right outline list.
237  **********************************************************************/
238 
239 void split_to_blob( //split the blob
240  BLOBNBOX *blob, //blob to split
241  int16_t chop_coord, //place to chop
242  float pitch_error, //allowed deviation
243  C_OUTLINE_LIST *left_coutlines, //for cblobs
244  C_OUTLINE_LIST *right_coutlines) {
245  C_BLOB *real_cblob; //cblob to chop
246 
247  if (blob != nullptr) {
248  real_cblob = blob->cblob();
249  } else {
250  real_cblob = nullptr;
251  }
252  if (!right_coutlines->empty() || real_cblob != nullptr)
253  fixed_chop_cblob(real_cblob,
254  chop_coord,
255  pitch_error,
256  left_coutlines,
257  right_coutlines);
258 
259  delete blob;
260 }
261 
262 /**********************************************************************
263  * fixed_chop_cblob
264  *
265  * Chop the given cblob (if any) and the existing right outlines to
266  * produce a list of outlines left of the chop point and more to the right.
267  **********************************************************************/
268 
269 void fixed_chop_cblob( //split the blob
270  C_BLOB *blob, //blob to split
271  int16_t chop_coord, //place to chop
272  float pitch_error, //allowed deviation
273  C_OUTLINE_LIST *left_outlines, //left half of chop
274  C_OUTLINE_LIST *right_outlines //right half of chop
275  ) {
276  C_OUTLINE *old_right; //already there
277  C_OUTLINE_LIST new_outlines; //new right ones
278  //output iterator
279  C_OUTLINE_IT left_it = left_outlines;
280  //in/out iterator
281  C_OUTLINE_IT right_it = right_outlines;
282  C_OUTLINE_IT new_it = &new_outlines;
283  C_OUTLINE_IT blob_it; //outlines in blob
284 
285  if (!right_it.empty ()) {
286  while (!right_it.empty ()) {
287  old_right = right_it.extract ();
288  right_it.forward ();
289  fixed_split_coutline(old_right,
290  chop_coord,
291  pitch_error,
292  &left_it,
293  &new_it);
294  }
295  right_it.add_list_before (&new_outlines);
296  }
297  if (blob != nullptr) {
298  blob_it.set_to_list (blob->out_list ());
299  for (blob_it.mark_cycle_pt (); !blob_it.cycled_list ();
300  blob_it.forward ())
301  fixed_split_coutline (blob_it.extract (), chop_coord, pitch_error,
302  &left_it, &right_it);
303  delete blob;
304  }
305 }
306 
307 
308 /**********************************************************************
309  * fixed_split_outline
310  *
311  * Chop the given outline (if necessary) placing the fragments which
312  * fall either side of the chop line into the appropriate list.
313  **********************************************************************/
314 
315 void fixed_split_coutline( //chop the outline
316  C_OUTLINE *srcline, //source outline
317  int16_t chop_coord, //place to chop
318  float pitch_error, //allowed deviation
319  C_OUTLINE_IT *left_it, //left half of chop
320  C_OUTLINE_IT *right_it //right half of chop
321  ) {
322  C_OUTLINE *child; //child outline
323  TBOX srcbox; //box of outline
324  C_OUTLINE_LIST left_ch; //left children
325  C_OUTLINE_LIST right_ch; //right children
326  C_OUTLINE_FRAG_LIST left_frags;//chopped fragments
327  C_OUTLINE_FRAG_LIST right_frags;;
328  C_OUTLINE_IT left_ch_it = &left_ch;
329  //for whole children
330  C_OUTLINE_IT right_ch_it = &right_ch;
331  //for holes
332  C_OUTLINE_IT child_it = srcline->child ();
333 
334  srcbox = srcline->bounding_box();
335  if (srcbox.left() + srcbox.right() <= chop_coord * 2
336  && srcbox.right() < chop_coord + pitch_error) {
337  // Whole outline is in the left side or not far over the chop_coord,
338  // so put the whole thing on the left.
339  left_it->add_after_then_move(srcline);
340  } else if (srcbox.left() + srcbox.right() > chop_coord * 2
341  && srcbox.left () > chop_coord - pitch_error) {
342  // Whole outline is in the right side or not far over the chop_coord,
343  // so put the whole thing on the right.
344  right_it->add_before_stay_put(srcline);
345  } else {
346  // Needs real chopping.
347  if (fixed_chop_coutline(srcline, chop_coord, pitch_error,
348  &left_frags, &right_frags)) {
349  for (child_it.mark_cycle_pt(); !child_it.cycled_list();
350  child_it.forward()) {
351  child = child_it.extract();
352  srcbox = child->bounding_box();
353  if (srcbox.right() < chop_coord) {
354  // Whole child is on the left.
355  left_ch_it.add_after_then_move(child);
356  } else if (srcbox.left() > chop_coord) {
357  // Whole child is on the right.
358  right_ch_it.add_after_then_move (child);
359  } else {
360  // No pitch_error is allowed when chopping children to prevent
361  // impossible outlines from being created.
362  if (fixed_chop_coutline(child, chop_coord, 0.0f,
363  &left_frags, &right_frags)) {
364  delete child;
365  } else {
366  if (srcbox.left() + srcbox.right() <= chop_coord * 2)
367  left_ch_it.add_after_then_move(child);
368  else
369  right_ch_it.add_after_then_move(child);
370  }
371  }
372  }
373  close_chopped_cfragments(&left_frags, &left_ch, pitch_error, left_it);
374  close_chopped_cfragments(&right_frags, &right_ch, pitch_error, right_it);
375  ASSERT_HOST(left_ch.empty() && right_ch.empty());
376  // No children left.
377  delete srcline; // Smashed up.
378  } else {
379  // Chop failed. Just use middle coord.
380  if (srcbox.left() + srcbox.right() <= chop_coord * 2)
381  left_it->add_after_then_move(srcline); // Stick whole in left.
382  else
383  right_it->add_before_stay_put(srcline);
384  }
385  }
386 }
387 
388 
389 /**********************************************************************
390  * fixed_chop_coutline
391  *
392  * Chop the given coutline (if necessary) placing the fragments which
393  * fall either side of the chop line into the appropriate list.
394  * If the coutline lies too heavily to one side to chop, FALSE is returned.
395  **********************************************************************/
396 
397 bool fixed_chop_coutline( //chop the outline
398  C_OUTLINE* srcline, //source outline
399  int16_t chop_coord, //place to chop
400  float pitch_error, //allowed deviation
401  C_OUTLINE_FRAG_LIST* left_frags, //left half of chop
402  C_OUTLINE_FRAG_LIST* right_frags //right half of chop
403 ) {
404  bool first_frag; //fragment
405  int16_t left_edge; //of outline
406  int16_t startindex; //in first fragment
407  int32_t length; //of outline
408  int16_t stepindex; //into outline
409  int16_t head_index; //start of fragment
410  ICOORD head_pos; //start of fragment
411  int16_t tail_index; //end of fragment
412  ICOORD tail_pos; //end of fragment
413  ICOORD pos; //current point
414  int16_t first_index = 0; //first tail
415  ICOORD first_pos; //first tail
416 
417  length = srcline->pathlength ();
418  pos = srcline->start_pos ();
419  left_edge = pos.x ();
420  tail_index = 0;
421  tail_pos = pos;
422  for (stepindex = 0; stepindex < length; stepindex++) {
423  if (pos.x () < left_edge) {
424  left_edge = pos.x ();
425  tail_index = stepindex;
426  tail_pos = pos;
427  }
428  pos += srcline->step (stepindex);
429  }
430  if (left_edge >= chop_coord - pitch_error)
431  return false; //not worth it
432 
433  startindex = tail_index;
434  first_frag = true;
435  head_index = tail_index;
436  head_pos = tail_pos;
437  do {
438  do {
439  tail_pos += srcline->step (tail_index);
440  tail_index++;
441  if (tail_index == length)
442  tail_index = 0;
443  }
444  while (tail_pos.x () != chop_coord && tail_index != startindex);
445  if (tail_index == startindex) {
446  if (first_frag)
447  return false; //doesn't cross line
448  else
449  break;
450  }
451  ASSERT_HOST (head_index != tail_index);
452  if (!first_frag) {
453  save_chop_cfragment(head_index,
454  head_pos,
455  tail_index,
456  tail_pos,
457  srcline,
458  left_frags);
459  }
460  else {
461  first_index = tail_index;
462  first_pos = tail_pos;
463  first_frag = false;
464  }
465  while (srcline->step (tail_index).x () == 0) {
466  tail_pos += srcline->step (tail_index);
467  tail_index++;
468  if (tail_index == length)
469  tail_index = 0;
470  }
471  head_index = tail_index;
472  head_pos = tail_pos;
473  while (srcline->step (tail_index).x () > 0) {
474  do {
475  tail_pos += srcline->step (tail_index);
476  tail_index++;
477  if (tail_index == length)
478  tail_index = 0;
479  }
480  while (tail_pos.x () != chop_coord);
481  ASSERT_HOST (head_index != tail_index);
482  save_chop_cfragment(head_index,
483  head_pos,
484  tail_index,
485  tail_pos,
486  srcline,
487  right_frags);
488  while (srcline->step (tail_index).x () == 0) {
489  tail_pos += srcline->step (tail_index);
490  tail_index++;
491  if (tail_index == length)
492  tail_index = 0;
493  }
494  head_index = tail_index;
495  head_pos = tail_pos;
496  }
497  }
498  while (tail_index != startindex);
499  save_chop_cfragment(head_index,
500  head_pos,
501  first_index,
502  first_pos,
503  srcline,
504  left_frags);
505  return true; //did some chopping
506 }
507 
508 /**********************************************************************
509  * save_chop_cfragment
510  *
511  * Store the given fragment in the given fragment list.
512  **********************************************************************/
513 
514 void save_chop_cfragment( //chop the outline
515  int16_t head_index, //head of fragment
516  ICOORD head_pos, //head of fragment
517  int16_t tail_index, //tail of fragment
518  ICOORD tail_pos, //tail of fragment
519  C_OUTLINE *srcline, //source of edgesteps
520  C_OUTLINE_FRAG_LIST *frags //fragment list
521  ) {
522  int16_t jump; //gap across end
523  int16_t stepcount; //total steps
524  C_OUTLINE_FRAG *head; //head of fragment
525  C_OUTLINE_FRAG *tail; //tail of fragment
526  int16_t tail_y; //ycoord of tail
527 
528  ASSERT_HOST (tail_pos.x () == head_pos.x ());
529  ASSERT_HOST (tail_index != head_index);
530  stepcount = tail_index - head_index;
531  if (stepcount < 0)
532  stepcount += srcline->pathlength ();
533  jump = tail_pos.y () - head_pos.y ();
534  if (jump < 0)
535  jump = -jump;
536  if (jump == stepcount)
537  return; //its a nop
538  tail_y = tail_pos.y ();
539  head = new C_OUTLINE_FRAG (head_pos, tail_pos, srcline,
540  head_index, tail_index);
541  tail = new C_OUTLINE_FRAG (head, tail_y);
542  head->other_end = tail;
543  add_frag_to_list(head, frags);
544  add_frag_to_list(tail, frags);
545 }
546 
547 
548 /**********************************************************************
549  * C_OUTLINE_FRAG::C_OUTLINE_FRAG
550  *
551  * Constructors for C_OUTLINE_FRAG.
552  **********************************************************************/
553 
555  ICOORD start_pt, //start coord
556  ICOORD end_pt, //end coord
557  C_OUTLINE *outline, //source of steps
558  int16_t start_index,
559  int16_t end_index) {
560  start = start_pt;
561  end = end_pt;
562  ycoord = start_pt.y ();
563  stepcount = end_index - start_index;
564  if (stepcount < 0)
565  stepcount += outline->pathlength ();
566  ASSERT_HOST (stepcount > 0);
567  steps = new DIR128[stepcount];
568  if (end_index > start_index) {
569  for (int i = start_index; i < end_index; ++i)
570  steps[i - start_index] = outline->step_dir(i);
571  }
572  else {
573  int len = outline->pathlength();
574  int i = start_index;
575  for (; i < len; ++i)
576  steps[i - start_index] = outline->step_dir(i);
577  if (end_index > 0)
578  for (; i < end_index + len; ++i)
579  steps[i - start_index] = outline->step_dir(i - len);
580  }
581  other_end = nullptr;
582  delete close();
583 }
584 
585 
587  C_OUTLINE_FRAG *head, //other end
588  int16_t tail_y) {
589  ycoord = tail_y;
590  other_end = head;
591  start = head->start;
592  end = head->end;
593  steps = nullptr;
594  stepcount = 0;
595 }
596 
597 
598 /**********************************************************************
599  * add_frag_to_list
600  *
601  * Insert the fragment in the list at the appropriate place to keep
602  * them in ascending ycoord order.
603  **********************************************************************/
604 
605 void add_frag_to_list( //ordered add
606  C_OUTLINE_FRAG *frag, //fragment to add
607  C_OUTLINE_FRAG_LIST *frags //fragment list
608  ) {
609  //output list
610  C_OUTLINE_FRAG_IT frag_it = frags;
611 
612  if (!frags->empty ()) {
613  for (frag_it.mark_cycle_pt (); !frag_it.cycled_list ();
614  frag_it.forward ()) {
615  if (frag_it.data ()->ycoord > frag->ycoord
616  || (frag_it.data ()->ycoord == frag->ycoord
617  && frag->other_end->ycoord < frag->ycoord)) {
618  frag_it.add_before_then_move (frag);
619  return;
620  }
621  }
622  }
623  frag_it.add_to_end (frag);
624 }
625 
626 
627 /**********************************************************************
628  * close_chopped_cfragments
629  *
630  * Clear the given list of fragments joining them up into outlines.
631  * Each outline made soaks up any of the child outlines which it encloses.
632  **********************************************************************/
633 
634 void close_chopped_cfragments( //chop the outline
635  C_OUTLINE_FRAG_LIST *frags, //list to clear
636  C_OUTLINE_LIST *children, //potential children
637  float pitch_error, //allowed shrinkage
638  C_OUTLINE_IT *dest_it //output list
639  ) {
640  //iterator
641  C_OUTLINE_FRAG_IT frag_it = frags;
642  C_OUTLINE_FRAG *bottom_frag; //bottom of cut
643  C_OUTLINE_FRAG *top_frag; //top of cut
644  C_OUTLINE *outline; //new outline
645  C_OUTLINE *child; //current child
646  C_OUTLINE_IT child_it = children;
647  C_OUTLINE_IT olchild_it; //children of outline
648 
649  while (!frag_it.empty()) {
650  frag_it.move_to_first();
651  // get bottom one
652  bottom_frag = frag_it.extract();
653  frag_it.forward();
654  top_frag = frag_it.data(); // look at next
655  if ((bottom_frag->steps == nullptr && top_frag->steps == nullptr)
656  || (bottom_frag->steps != nullptr && top_frag->steps != nullptr)) {
657  if (frag_it.data_relative(1)->ycoord == top_frag->ycoord)
658  frag_it.forward();
659  }
660  top_frag = frag_it.extract();
661  if (top_frag->other_end != bottom_frag) {
662  outline = join_chopped_fragments(bottom_frag, top_frag);
663  ASSERT_HOST(outline == nullptr);
664  } else {
665  outline = join_chopped_fragments(bottom_frag, top_frag);
666  if (outline != nullptr) {
667  olchild_it.set_to_list(outline->child());
668  for (child_it.mark_cycle_pt(); !child_it.cycled_list();
669  child_it.forward()) {
670  child = child_it.data();
671  if (*child < *outline)
672  olchild_it.add_to_end(child_it.extract());
673  }
674  if (outline->bounding_box().width() > pitch_error)
675  dest_it->add_after_then_move(outline);
676  else
677  delete outline; // Make it disappear.
678  }
679  }
680  }
681  while (!child_it.empty ()) {
682  dest_it->add_after_then_move (child_it.extract ());
683  child_it.forward ();
684  }
685 }
686 
687 
688 /**********************************************************************
689  * join_chopped_fragments
690  *
691  * Join the two lists of POLYPTs such that neither OUTLINE_FRAG
692  * operand keeps responsibility for the fragment.
693  **********************************************************************/
694 
696  C_OUTLINE_FRAG *bottom, //bottom of cut
697  C_OUTLINE_FRAG *top //top of cut
698  ) {
699  C_OUTLINE *outline; //closed loop
700 
701  if (bottom->other_end == top) {
702  if (bottom->steps == nullptr)
703  outline = top->close (); //turn to outline
704  else
705  outline = bottom->close ();
706  delete top;
707  delete bottom;
708  return outline;
709  }
710  if (bottom->steps == nullptr) {
711  ASSERT_HOST (top->steps != nullptr);
712  join_segments (bottom->other_end, top);
713  }
714  else {
715  ASSERT_HOST (top->steps == nullptr);
716  join_segments (top->other_end, bottom);
717  }
718  top->other_end->other_end = bottom->other_end;
719  bottom->other_end->other_end = top->other_end;
720  delete bottom;
721  delete top;
722  return nullptr;
723 }
724 
725 /**********************************************************************
726  * join_segments
727  *
728  * Join the two edgestep fragments such that the second comes after
729  * the first and the gap between them is closed.
730  **********************************************************************/
731 
732 void join_segments( //join pieces
733  C_OUTLINE_FRAG *bottom, //bottom of cut
734  C_OUTLINE_FRAG *top //top of cut
735  ) {
736  DIR128 *steps; //new steps
737  int32_t stepcount; //no of steps
738  int16_t fake_count; //fake steps
739  DIR128 fake_step; //step entry
740 
741  ASSERT_HOST (bottom->end.x () == top->start.x ());
742  fake_count = top->start.y () - bottom->end.y ();
743  if (fake_count < 0) {
744  fake_count = -fake_count;
745  fake_step = 32;
746  }
747  else
748  fake_step = 96;
749 
750  stepcount = bottom->stepcount + fake_count + top->stepcount;
751  steps = new DIR128[stepcount];
752  memmove (steps, bottom->steps, bottom->stepcount);
753  memset (steps + bottom->stepcount, fake_step.get_dir(), fake_count);
754  memmove (steps + bottom->stepcount + fake_count, top->steps,
755  top->stepcount);
756  delete [] bottom->steps;
757  bottom->steps = steps;
758  bottom->stepcount = stepcount;
759  bottom->end = top->end;
760  bottom->other_end->end = top->end;
761 }
762 
763 
764 /**********************************************************************
765  * C_OUTLINE_FRAG::close
766  *
767  * Join the ends of this fragment and turn it into an outline.
768  **********************************************************************/
769 
771  DIR128 *new_steps; //new steps
772  int32_t new_stepcount; //no of steps
773  int16_t fake_count; //fake steps
774  DIR128 fake_step; //step entry
775 
776  ASSERT_HOST (start.x () == end.x ());
777  fake_count = start.y () - end.y ();
778  if (fake_count < 0) {
779  fake_count = -fake_count;
780  fake_step = 32;
781  }
782  else
783  fake_step = 96;
784 
785  new_stepcount = stepcount + fake_count;
786  if (new_stepcount > C_OUTLINE::kMaxOutlineLength)
787  return nullptr; // Can't join them
788  new_steps = new DIR128[new_stepcount];
789  memmove(new_steps, steps, stepcount);
790  memset (new_steps + stepcount, fake_step.get_dir(), fake_count);
791  C_OUTLINE* result = new C_OUTLINE (start, new_steps, new_stepcount);
792  delete [] new_steps;
793  return result;
794 }
795 
796 
797 /**********************************************************************
798  * C_OUTLINE_FRAG::operator=
799  *
800  * Copy this fragment.
801  **********************************************************************/
802 
803  //join pieces
805 const C_OUTLINE_FRAG & src //fragment to copy
806 ) {
807  delete [] steps;
808 
809  stepcount = src.stepcount;
810  steps = new DIR128[stepcount];
811  memmove (steps, src.steps, stepcount);
812  start = src.start;
813  end = src.end;
814  ycoord = src.ycoord;
815  return *this;
816 }
C_OUTLINE_FRAG * other_end
Definition: fpchop.h:51
void fixed_chop_cblob(C_BLOB *blob, int16_t chop_coord, float pitch_error, C_OUTLINE_LIST *left_outlines, C_OUTLINE_LIST *right_outlines)
Definition: fpchop.cpp:269
#define TRUE
Definition: capi.h:51
int16_t ycoord
Definition: fpchop.h:52
ICOORD end
Definition: fpchop.h:48
EXTERN bool textord_show_page_cuts
Definition: topitch.cpp:50
#define double_VAR(name, val, comment)
Definition: params.h:285
TBOX bounding_box() const
Definition: werd.cpp:159
ICOORD start
Definition: fpchop.h:47
int16_t y() const
access_function
Definition: points.h:57
void save_chop_cfragment(int16_t head_index, ICOORD head_pos, int16_t tail_index, ICOORD tail_pos, C_OUTLINE *srcline, C_OUTLINE_FRAG_LIST *frags)
Definition: fpchop.cpp:514
void split_to_blob(BLOBNBOX *blob, int16_t chop_coord, float pitch_error, C_OUTLINE_LIST *left_coutlines, C_OUTLINE_LIST *right_coutlines)
Definition: fpchop.cpp:239
Definition: rect.h:34
WERD_LIST * word_list()
Definition: ocrrow.h:55
Definition: werd.h:35
const ICOORD & start_pos() const
Definition: coutln.h:148
EXTERN double textord_fp_chop_snap
Definition: fpchop.cpp:37
void set_flag(WERD_FLAGS mask, bool value)
Definition: werd.h:127
void plot_row_cells(ScrollView *win, ScrollView::Color colour, TO_ROW *row, float xshift, ICOORDELT_LIST *cells)
Definition: drawtord.cpp:396
int16_t width() const
Definition: rect.h:115
C_OUTLINE_FRAG & operator=(const C_OUTLINE_FRAG &src)
Definition: fpchop.cpp:804
int16_t left() const
Definition: rect.h:72
int8_t get_dir() const
Definition: mod128.h:76
integer coordinate
Definition: points.h:32
int16_t x() const
access function
Definition: points.h:53
Definition: mod128.h:29
#define EXTERN
Definition: fpchop.cpp:32
C_OUTLINE * join_chopped_fragments(C_OUTLINE_FRAG *bottom, C_OUTLINE_FRAG *top)
Definition: fpchop.cpp:695
void set_blanks(uint8_t new_blanks)
Definition: werd.h:105
#define ELISTIZE(CLASSNAME)
Definition: elst.h:961
Definition: werd.h:59
int32_t pathlength() const
Definition: coutln.h:135
Definition: ocrrow.h:36
void add_frag_to_list(C_OUTLINE_FRAG *frag, C_OUTLINE_FRAG_LIST *frags)
Definition: fpchop.cpp:605
const TBOX & bounding_box() const
Definition: coutln.h:113
DLLSYM void tprintf(const char *format,...)
Definition: tprintf.cpp:37
Definition: werd.h:34
int32_t stepcount
Definition: fpchop.h:50
C_OUTLINE_LIST * child()
Definition: coutln.h:108
EXTERN ScrollView * to_win
Definition: drawtord.cpp:37
void recalc_bounding_box()
Definition: ocrrow.cpp:101
void fixed_split_coutline(C_OUTLINE *srcline, int16_t chop_coord, float pitch_error, C_OUTLINE_IT *left_it, C_OUTLINE_IT *right_it)
Definition: fpchop.cpp:315
static const int kMaxOutlineLength
Definition: coutln.h:273
WERD * add_repeated_word(WERD_IT *rep_it, int16_t &rep_left, int16_t &prev_chop_coord, uint8_t &blanks, float pitch, WERD_IT *word_it)
Definition: fpchop.cpp:203
void close_chopped_cfragments(C_OUTLINE_FRAG_LIST *frags, C_OUTLINE_LIST *children, float pitch_error, C_OUTLINE_IT *dest_it)
Definition: fpchop.cpp:634
C_OUTLINE_LIST * out_list()
Definition: stepblob.h:70
Definition: points.h:189
C_OUTLINE * close()
Definition: fpchop.cpp:770
int16_t right() const
Definition: rect.h:79
DIR128 step_dir(int index) const
Definition: coutln.h:139
DIR128 * steps
Definition: fpchop.h:49
EXTERN int textord_fp_chop_error
Definition: fpchop.cpp:35
ROW * fixed_pitch_words(TO_ROW *row, FCOORD rotation)
Definition: fpchop.cpp:47
void join_segments(C_OUTLINE_FRAG *bottom, C_OUTLINE_FRAG *top)
Definition: fpchop.cpp:732
class DLLSYM C_OUTLINE
Definition: coutln.h:68
C_OUTLINE_FRAG()
Definition: fpchop.h:28
bool fixed_chop_coutline(C_OUTLINE *srcline, int16_t chop_coord, float pitch_error, C_OUTLINE_FRAG_LIST *left_frags, C_OUTLINE_FRAG_LIST *right_frags)
Definition: fpchop.cpp:397
ICOORD step(int index) const
Definition: coutln.h:144
C_BLOB * cblob() const
Definition: blobbox.h:269
#define INT_VAR(name, val, comment)
Definition: params.h:276
#define ASSERT_HOST(x)
Definition: errcode.h:84