• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*****************************************************************************
2  *
3  * File:        blkocc.cpp  (Formerly blockocc.c)
4  * Description:  Block Occupancy routines
5  * Author:       Chris Newton
6  * Created:      Fri Nov 8
7  * Modified:
8  * Language:     C++
9  * Package:      N/A
10  * Status:       Experimental (Do Not Distribute)
11  *
12  * (c) Copyright 1991, Hewlett-Packard Company.
13  ** Licensed under the Apache License, Version 2.0 (the "License");
14  ** you may not use this file except in compliance with the License.
15  ** You may obtain a copy of the License at
16  ** http://www.apache.org/licenses/LICENSE-2.0
17  ** Unless required by applicable law or agreed to in writing, software
18  ** distributed under the License is distributed on an "AS IS" BASIS,
19  ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20  ** See the License for the specific language governing permissions and
21  ** limitations under the License.
22  *
23  ******************************************************************************/
24 
25 /*
26 ----------------------------------------------------------------------
27               I n c l u d e s
28 ----------------------------------------------------------------------
29 */
30 
31 #include "mfcpch.h"
32 #include          <ctype.h>
33 #include          <math.h>
34 #include          "errcode.h"
35 #include          "drawtord.h"
36 #include          "blkocc.h"
37 #include          "notdll.h"
38 
39 const ERRCODE BLOCKOCC = "BlockOcc";
40 
41 ELISTIZE (REGION_OCC)
42 #define EXTERN
43 EXTERN BOOL_VAR (blockocc_show_result, FALSE,
44 "Show intermediate results");
45 
46 /* The values given here should reflect the values of bln_x_height and
47  * bln_baseline_offset. These are defined as part of the word class
48  * definition                                                             */
49 
50 EXTERN INT_VAR (blockocc_desc_height, 0,
51 "Descender height after normalisation");
52 EXTERN INT_VAR (blockocc_asc_height, 255,
53 "Ascender height after normalisation");
54 
55 EXTERN INT_VAR (blockocc_band_count, 4, "Number of bands used");
56 
57 EXTERN double_VAR (textord_underline_threshold, 0.5,
58 "Fraction of width occupied");
59 
60 /* ********************************************************************
61 A note on transitions.
62 
63 We want to record occupancy in various bands. In general we need to consider
64 7 situations:
65 
66 (1)     (2)  (3)             (4)
67 \       /   \           /   \           /
68 __\_____/_____\_________/_____\_________/______ Upper Limit
69   \   /       \       /       \       /
70   /   \        \-->--/         \--<--/     /-----\
71 v     ^                                  /       \(7)
72 \      \                                 \       /
73   \      \      /--<--\      /-->--\       \-----/
74 ____\______\____/_______\____/_______\_________ Lower Limit
75   \      \  /         \  /         \
76           (5)          (6)
77 We know that following "next" pointers around an outline keeps the black area
78 on the LEFT. We only need be concerned with situations 1,2,3,5 and 7.
79 4 and 6 can be ignored as they represent small incursions into a large black
80 region which will be recorded elsewhere.  Situations 3 and 5 define encloseed
81 areas bounded by the upper and lower limits respectively.  Situation 1 is open
82 to the right, awaiting a closure by a situation 2 which is open to the right.
83 Situation 7 is entirely enclosed within the band.
84 
85 The situations are refered to as region types and are determined by
86 find_region_type.
87 
88 An empty region type is used to denote entry to an adjacent band and return
89 to the original band at the same x location.
90 ***********************************************************************/
91 
92 #define REGION_TYPE_EMPTY 0
93 #define REGION_TYPE_OPEN_RIGHT 1
94 #define REGION_TYPE_OPEN_LEFT 2
95 #define REGION_TYPE_UPPER_BOUND 3
96 #define REGION_TYPE_UPPER_UNBOUND 4
97 #define REGION_TYPE_LOWER_BOUND 5
98 #define REGION_TYPE_LOWER_UNBOUND 6
99 #define REGION_TYPE_ENCLOSED 7
100 
101 BAND bands[MAX_NUM_BANDS + 1];   // band defns
102 
103 /**********************************************************************
104  * test_underline
105  *
106  * Check to see if the blob is an underline.
107  * Return TRUE if it is.
108  **********************************************************************/
109 
test_underline(BOOL8 testing_on,PBLOB * blob,float baseline,float xheight)110 BOOL8 test_underline(                   //look for underlines
111                      BOOL8 testing_on,  //drawing blob
112                      PBLOB *blob,       //blob to test
113                      float baseline,    //coords of baseline
114                      float xheight      //height of line
115                     ) {
116   inT16 occ;
117   inT16 blob_width;              //width of blob
118   TBOX blob_box;                  //bounding box
119   float occs[MAX_NUM_BANDS + 1]; //total occupancy
120 
121   blob_box = blob->bounding_box ();
122   set_bands(baseline, xheight);  //setup block occ
123   blob_width = blob->bounding_box ().width ();
124   if (testing_on) {
125     //              blob->plot(to_win,GOLDENROD,GOLDENROD);
126     //              line_color_index(to_win,GOLDENROD);
127     //              move2d(to_win,blob_box.left(),baseline);
128     //              draw2d(to_win,blob_box.right(),baseline);
129     //              move2d(to_win,blob_box.left(),baseline+xheight);
130     //              draw2d(to_win,blob_box.right(),baseline+xheight);
131     tprintf
132       ("Testing underline on blob at (%d,%d)->(%d,%d), base=%g\nOccs:",
133       blob->bounding_box ().left (), blob->bounding_box ().bottom (),
134       blob->bounding_box ().right (), blob->bounding_box ().top (),
135       baseline);
136   }
137   block_occ(blob, occs);
138   if (testing_on) {
139     for (occ = 0; occ <= MAX_NUM_BANDS; occ++)
140       tprintf ("%g ", occs[occ]);
141     tprintf ("\n");
142   }
143   if (occs[1] > occs[2] + occs[2] && occs[1] > occs[3] + occs[3]
144     && occs[1] > blob_width * textord_underline_threshold)
145     return TRUE;                 //real underline
146   if (occs[4] > occs[2] + occs[2]
147     && occs[4] > blob_width * textord_underline_threshold)
148     return TRUE;                 //overline
149   return FALSE;                  //neither
150 }
151 
152 
153 /**********************************************************************
154  * test_underline
155  *
156  * Check to see if the blob is an underline.
157  * Return TRUE if it is.
158  **********************************************************************/
159 
test_underline(BOOL8 testing_on,C_BLOB * blob,inT16 baseline,inT16 xheight)160 BOOL8 test_underline(                   //look for underlines
161                      BOOL8 testing_on,  //drawing blob
162                      C_BLOB *blob,      //blob to test
163                      inT16 baseline,    //coords of baseline
164                      inT16 xheight      //height of line
165                     ) {
166   inT16 occ;
167   inT16 blob_width;              //width of blob
168   TBOX blob_box;                  //bounding box
169   inT32 desc_occ;
170   inT32 x_occ;
171   inT32 asc_occ;
172   STATS projection;
173 
174   blob_box = blob->bounding_box ();
175   blob_width = blob->bounding_box ().width ();
176   projection.set_range (blob_box.bottom (), blob_box.top () + 1);
177   if (testing_on) {
178     //              blob->plot(to_win,GOLDENROD,GOLDENROD);
179     //              line_color_index(to_win,GOLDENROD);
180     //              move2d(to_win,blob_box.left(),baseline);
181     //              draw2d(to_win,blob_box.right(),baseline);
182     //              move2d(to_win,blob_box.left(),baseline+xheight);
183     //              draw2d(to_win,blob_box.right(),baseline+xheight);
184     tprintf
185       ("Testing underline on blob at (%d,%d)->(%d,%d), base=%d\nOccs:",
186       blob->bounding_box ().left (), blob->bounding_box ().bottom (),
187       blob->bounding_box ().right (), blob->bounding_box ().top (),
188       baseline);
189   }
190   horizontal_cblob_projection(blob, &projection);
191   desc_occ = 0;
192   for (occ = blob_box.bottom (); occ < baseline; occ++)
193     if (occ <= blob_box.top () && projection.pile_count (occ) > desc_occ)
194                                  //max in region
195       desc_occ = projection.pile_count (occ);
196   x_occ = 0;
197   for (occ = baseline; occ <= baseline + xheight; occ++)
198     if (occ >= blob_box.bottom () && occ <= blob_box.top ()
199     && projection.pile_count (occ) > x_occ)
200                                  //max in region
201       x_occ = projection.pile_count (occ);
202   asc_occ = 0;
203   for (occ = baseline + xheight + 1; occ <= blob_box.top (); occ++)
204     if (occ >= blob_box.bottom () && projection.pile_count (occ) > asc_occ)
205       asc_occ = projection.pile_count (occ);
206   if (testing_on) {
207     tprintf ("%d %d %d\n", desc_occ, x_occ, asc_occ);
208   }
209   if (desc_occ == 0 && x_occ == 0 && asc_occ == 0) {
210     tprintf ("Bottom=%d, top=%d, base=%d, x=%d\n",
211       blob_box.bottom (), blob_box.top (), baseline, xheight);
212     projection.print (stdout, TRUE);
213   }
214   if (desc_occ > x_occ + x_occ
215     && desc_occ > blob_width * textord_underline_threshold)
216     return TRUE;                 //real underline
217   if (asc_occ > x_occ + x_occ
218     && asc_occ > blob_width * textord_underline_threshold)
219     return TRUE;                 //overline
220   return FALSE;                  //neither
221 }
222 
223 
224 /**********************************************************************
225  * horizontal_cblob_projection
226  *
227  * Compute the horizontal projection of a cblob from its outlines
228  * and add to the given STATS.
229  **********************************************************************/
230 
horizontal_cblob_projection(C_BLOB * blob,STATS * stats)231 void horizontal_cblob_projection(               //project outlines
232                                  C_BLOB *blob,  //blob to project
233                                  STATS *stats   //output
234                                 ) {
235                                  //outlines of blob
236   C_OUTLINE_IT out_it = blob->out_list ();
237 
238   for (out_it.mark_cycle_pt (); !out_it.cycled_list (); out_it.forward ()) {
239     horizontal_coutline_projection (out_it.data (), stats);
240   }
241 }
242 
243 
244 /**********************************************************************
245  * horizontal_coutline_projection
246  *
247  * Compute the horizontal projection of a outline from its outlines
248  * and add to the given STATS.
249  **********************************************************************/
250 
horizontal_coutline_projection(C_OUTLINE * outline,STATS * stats)251 void horizontal_coutline_projection(                     //project outlines
252                                     C_OUTLINE *outline,  //outline to project
253                                     STATS *stats         //output
254                                    ) {
255   ICOORD pos;                    //current point
256   ICOORD step;                   //edge step
257   inT32 length;                  //of outline
258   inT16 stepindex;               //current step
259   C_OUTLINE_IT out_it = outline->child ();
260 
261   pos = outline->start_pos ();
262   length = outline->pathlength ();
263   for (stepindex = 0; stepindex < length; stepindex++) {
264     step = outline->step (stepindex);
265     if (step.y () > 0) {
266       stats->add (pos.y (), pos.x ());
267     }
268     else if (step.y () < 0) {
269       stats->add (pos.y () - 1, -pos.x ());
270     }
271     pos += step;
272   }
273 
274   for (out_it.mark_cycle_pt (); !out_it.cycled_list (); out_it.forward ()) {
275     horizontal_coutline_projection (out_it.data (), stats);
276   }
277 }
278 
279 
set_bands(float baseline,float xheight)280 void set_bands(                 //init from varibles
281                float baseline,  //top of bottom band
282                float xheight    //height of split band
283               ) {
284   inT16 int_bl, int_xh;          //for band.set
285 
286   bands[DOT_BAND].set (0, 0, 0, 0, 0, 0);
287 
288   int_bl = (inT16) baseline;
289   int_xh = (inT16) xheight;
290   bands[1].set (int_bl, int_bl, int_bl,
291     NO_LOWER_LIMIT, NO_LOWER_LIMIT, NO_LOWER_LIMIT);
292 
293   bands[2].set (int_bl + int_xh / 2, int_bl + int_xh / 2, int_bl + int_xh / 2,
294     int_bl, int_bl, int_bl);
295 
296   bands[3].set (int_bl + int_xh, int_bl + int_xh, int_bl + int_xh,
297     int_bl + int_xh / 2, int_bl + int_xh / 2,
298     int_bl + int_xh / 2);
299 
300   bands[4].set (NO_UPPER_LIMIT, NO_UPPER_LIMIT, NO_UPPER_LIMIT,
301     int_bl + int_xh, int_bl + int_xh, int_bl + int_xh);
302 }
303 
304 
305 void
block_occ(PBLOB * blob,float occs[])306 block_occ (PBLOB * blob,         //blob to do
307 float occs[]                     //output histogram
308 ) {
309   int band_index;                //current band
310   REGION_OCC *region;            //current segment
311   REGION_OCC_LIST region_occ_list[MAX_NUM_BANDS + 1];
312   REGION_OCC_IT region_it;       //region iterator
313 
314   find_transitions(blob, region_occ_list);
315   compress_region_list(region_occ_list);
316   for (band_index = 0; band_index <= MAX_NUM_BANDS; band_index++) {
317     occs[band_index] = 0.0f;
318     region_it.set_to_list (&region_occ_list[band_index]);
319     for (region_it.mark_cycle_pt (); !region_it.cycled_list ();
320     region_it.forward ()) {
321       region = region_it.data ();
322       occs[band_index] += region->max_x - region->min_x;
323     }
324   }
325 }
326 
327 
find_transitions(PBLOB * blob,REGION_OCC_LIST * region_occ_list)328 void find_transitions(PBLOB *blob,  //blob to do
329                       REGION_OCC_LIST *region_occ_list) {
330   OUTLINE_IT outline_it;
331   TBOX box;
332   POLYPT_IT pt_it;
333   FCOORD point1;
334   FCOORD point2;
335   FCOORD *entry_pt = &point1;
336   FCOORD *exit_pt = &point2;
337   FCOORD *temp_pt;
338   inT16 increment;
339   inT16 prev_band;
340   inT16 band;
341   inT16 next_band;
342   float min_x;
343   float max_x;
344   float min_y;
345   float max_y;
346   BOOL8 doubly_contained;
347 
348   outline_it = blob->out_list ();
349   for (outline_it.mark_cycle_pt (); !outline_it.cycled_list ();
350   outline_it.forward ()) {
351     find_fbox(&outline_it, &min_x, &min_y, &max_x, &max_y);
352 
353     if (bands[DOT_BAND].range_in_nominal (max_y, min_y)) {
354       record_region(DOT_BAND,
355                     min_x,
356                     max_x,
357                     REGION_TYPE_ENCLOSED,
358                     region_occ_list);
359     }
360     else {
361       band = find_containing_maximal_band (max_y, min_y,
362         &doubly_contained);
363       if (band != UNDEFINED_BAND) {
364                                  //No transitions
365         if (!doubly_contained)
366           record_region(band,
367                         min_x,
368                         max_x,
369                         REGION_TYPE_ENCLOSED,
370                         region_occ_list);
371         else {
372           //                                      if (wordocc_debug_on && blockocc_show_result)
373           //                                      {
374           //                                              fprintf( db_win,
375           //                                                "Ignoring doubly contained outline (%d, %d) (%d, %d)\n",
376           //                                                box.left(), box.top(),
377           //                                                box.right(), box.bottom());
378           //                                              fprintf( db_win, "\tContained in bands %d and %d\n",
379           //                                                                      band, band + 1 );
380           //                                      }
381         }
382       }
383       else {
384                                  //There are transitns
385         /*
386         Determining a good start point for recognising transitions between bands
387         is complicated by error limits on bands.  We need to find a line which
388         significantly occupies a band.
389 
390         Having found such a point, we need to find a significant transition out of
391         its band and start the walk around the outline from there.
392 
393         Note that we are relying on having recognised and dealt with elsewhere,
394         outlines which do not significantly occupy more than one region. A
395         particularly nasty case of this are outlines which do not significantly
396         occupy ANY band. I.e. they lie entirely within the error limits.
397         Given this condition, all remaining outlines must contain at least one
398         significant line.  */
399 
400         pt_it = outline_it.data ()->polypts ();
401 
402         find_significant_line(pt_it, &band);
403         *entry_pt = pt_it.data ()->pos;
404         next_region(&pt_it,
405                     band,
406                     &next_band,
407                     &min_x,
408                     &max_x,
409                     &increment,
410                     exit_pt);
411         pt_it.mark_cycle_pt ();
412 
413         // Found the first real transition, so start walking the outline from here.
414 
415         do {
416           prev_band = band;
417           band = band + increment;
418 
419           while (band != next_band) {
420             temp_pt = entry_pt;
421             entry_pt = exit_pt;
422             exit_pt = temp_pt;
423             min_x = max_x = entry_pt->x ();
424 
425             find_trans_point (&pt_it, band, band + increment,
426               exit_pt);
427             maintain_limits (&min_x, &max_x, exit_pt->x ());
428 
429             record_region (band,
430               min_x,
431               max_x,
432               find_region_type (prev_band,
433               band,
434               band + increment,
435               entry_pt->x (),
436               exit_pt->x ()),
437               region_occ_list);
438             prev_band = band;
439             band = band + increment;
440           }
441 
442           temp_pt = entry_pt;
443           entry_pt = exit_pt;
444           exit_pt = temp_pt;
445           min_x = max_x = entry_pt->x ();
446           next_region(&pt_it,
447                       band,
448                       &next_band,
449                       &min_x,
450                       &max_x,
451                       &increment,
452                       exit_pt);
453 
454           record_region (band,
455             min_x,
456             max_x,
457             find_region_type (prev_band,
458             band,
459             band + increment,
460             entry_pt->x (),
461             exit_pt->x ()),
462             region_occ_list);
463         }
464         while (!pt_it.cycled_list ());
465       }
466     }
467   }
468 }
469 
470 
record_region(inT16 band,float new_min,float new_max,inT16 region_type,REGION_OCC_LIST * region_occ_list)471 void record_region(  //add region on list
472                    inT16 band,
473                    float new_min,
474                    float new_max,
475                    inT16 region_type,
476                    REGION_OCC_LIST *region_occ_list) {
477   REGION_OCC_IT it (&(region_occ_list[band]));
478 
479   //   if (wordocc_debug_on && blockocc_show_result)
480   //         fprintf( db_win, "\nBand %d, region type %d, from %f to %f",
481   //                                 band, region_type, new_min, new_max );
482 
483   if ((region_type == REGION_TYPE_UPPER_UNBOUND) ||
484     (region_type == REGION_TYPE_LOWER_UNBOUND) ||
485     (region_type == REGION_TYPE_EMPTY))
486     return;
487 
488   if (it.empty ()) {
489     it.add_after_stay_put (new REGION_OCC (new_min, new_max, region_type));
490   }
491   else {
492 
493     /* Insert in sorted order of average limit */
494 
495     while ((new_min + new_max > it.data ()->min_x + it.data ()->max_x) &&
496       (!it.at_last ()))
497       it.forward ();
498 
499     if ((it.at_last ()) &&       //at the end
500       (new_min + new_max > it.data ()->min_x + it.data ()->max_x))
501       //new range > current
502       it.add_after_stay_put (new REGION_OCC (new_min,
503         new_max, region_type));
504     else {
505       it.add_before_stay_put (new REGION_OCC (new_min,
506         new_max, region_type));
507     }
508   }
509 }
510 
511 
find_containing_maximal_band(float y1,float y2,BOOL8 * doubly_contained)512 inT16 find_containing_maximal_band(  //find range's band
513                                    float y1,
514                                    float y2,
515                                    BOOL8 *doubly_contained) {
516   inT16 band;
517 
518   *doubly_contained = FALSE;
519 
520   for (band = 1; band <= blockocc_band_count; band++) {
521     if (bands[band].range_in_maximal (y1, y2)) {
522       if ((band < blockocc_band_count) &&
523         (bands[band + 1].range_in_maximal (y1, y2)))
524         *doubly_contained = TRUE;
525       return band;
526     }
527   }
528   return UNDEFINED_BAND;
529 }
530 
531 
find_significant_line(POLYPT_IT it,inT16 * band)532 void find_significant_line(POLYPT_IT it, inT16 *band) {
533 
534   /* Look for a line which significantly occupies at least one band. I.e. part
535   of the line is in the non-margin part of the band. */
536 
537   *band = find_overlapping_minimal_band (it.data ()->pos.y (),
538     it.data ()->pos.y () +
539     it.data ()->vec.y ());
540 
541   while (*band == UNDEFINED_BAND) {
542     it.forward ();
543     *band = find_overlapping_minimal_band (it.data ()->pos.y (),
544       it.data ()->pos.y () +
545       it.data ()->vec.y ());
546   }
547 }
548 
549 
find_overlapping_minimal_band(float y1,float y2)550 inT16 find_overlapping_minimal_band(  //find range's band
551                                     float y1,
552                                     float y2) {
553   inT16 band;
554 
555   for (band = 1; band <= blockocc_band_count; band++) {
556     if (bands[band].range_overlaps_minimal (y1, y2))
557       return band;
558   }
559   return UNDEFINED_BAND;
560 }
561 
562 
find_region_type(inT16 entry_band,inT16 current_band,inT16 exit_band,float entry_x,float exit_x)563 inT16 find_region_type(inT16 entry_band,
564                        inT16 current_band,
565                        inT16 exit_band,
566                        float entry_x,
567                        float exit_x) {
568   if (entry_band > exit_band)
569     return REGION_TYPE_OPEN_RIGHT;
570   if (entry_band < exit_band)
571     return REGION_TYPE_OPEN_LEFT;
572   if (entry_x == exit_x)
573     return REGION_TYPE_EMPTY;
574   if (entry_band > current_band) {
575     if (entry_x < exit_x)
576       return REGION_TYPE_UPPER_BOUND;
577     else
578       return REGION_TYPE_UPPER_UNBOUND;
579   }
580   else {
581     if (entry_x > exit_x)
582       return REGION_TYPE_LOWER_BOUND;
583     else
584       return REGION_TYPE_LOWER_UNBOUND;
585   }
586 }
587 
588 
find_trans_point(POLYPT_IT * pt_it,inT16 current_band,inT16 next_band,FCOORD * transition_pt)589 void find_trans_point(POLYPT_IT *pt_it,
590                       inT16 current_band,
591                       inT16 next_band,
592                       FCOORD *transition_pt) {
593   float x1, x2, y1, y2;          // points of edge
594   float gradient;                // m in y = mx + c
595   float offset;                  // c in y = mx + c
596 
597   if (current_band < next_band)
598     transition_pt->set_y (bands[current_band].max);
599   //going up
600   else
601     transition_pt->set_y (bands[current_band].min);
602   //going down
603 
604   x1 = pt_it->data ()->pos.x ();
605   x2 = x1 + pt_it->data ()->vec.x ();
606   y1 = pt_it->data ()->pos.y ();
607   y2 = y1 + pt_it->data ()->vec.y ();
608 
609   if (x1 == x2)
610     transition_pt->set_x (x1);   //avoid div by 0
611   else {
612     if (y1 == y2)                //avoid div by 0
613       transition_pt->set_x ((x1 + x2) / 2.0);
614     else {
615       gradient = (y1 - y2) / (float) (x1 - x2);
616       offset = y1 - x1 * gradient;
617       transition_pt->set_x ((transition_pt->y () - offset) / gradient);
618     }
619   }
620 }
621 
622 
next_region(POLYPT_IT * start_pt_it,inT16 start_band,inT16 * to_band,float * min_x,float * max_x,inT16 * increment,FCOORD * exit_pt)623 void next_region(POLYPT_IT *start_pt_it,
624                  inT16 start_band,
625                  inT16 *to_band,
626                  float *min_x,
627                  float *max_x,
628                  inT16 *increment,
629                  FCOORD *exit_pt) {
630   /*
631   Given an edge and a band which the edge significantly occupies, find the
632   significant end of the region containing the band. I.e. find an edge which
633   points to another band such that the outline subsequetly moves significantly
634   out of the starting band.
635 
636   Note that we can assume that we are significantly inside the current band to
637   start with because the edges passed will be from previous calls to this
638   routine apart from the first - the result of which is only used to establish
639   the start of the first region.
640   */
641 
642   inT16 band;                    //band of current edge
643   inT16 prev_band = start_band;  //band of prev edge
644                                  //edge crossing out
645   POLYPT_IT last_transition_out_it;
646                                  //band it pts to
647   inT16 last_trans_out_to_band = 0;
648   float ext_min_x = 0.0f;
649   float ext_max_x = 0.0f;
650 
651   start_pt_it->forward ();
652   band = find_band (start_pt_it->data ()->pos.y ());
653 
654   while ((band == start_band) ||
655   bands[start_band].in_maximal (start_pt_it->data ()->pos.y ())) {
656     if (band == start_band) {
657                                  //Return to start band
658       if (prev_band != start_band) {
659         *min_x = ext_min_x;
660         *max_x = ext_max_x;
661       }
662       maintain_limits (min_x, max_x, start_pt_it->data ()->pos.x ());
663     }
664     else {
665       if (prev_band == start_band) {
666                                  //Exit from start band
667                                  //so remember edge
668         last_transition_out_it = *start_pt_it;
669                                  //before we left
670         last_transition_out_it.backward ();
671                                  //and band it pts to
672         last_trans_out_to_band = band;
673         ext_min_x = *min_x;
674         ext_max_x = *max_x;
675       }
676       maintain_limits (&ext_min_x, &ext_max_x,
677         start_pt_it->data ()->pos.x ());
678     }
679     prev_band = band;
680     start_pt_it->forward ();
681     band = find_band (start_pt_it->data ()->pos.y ());
682   }
683 
684   if (prev_band == start_band) { //exit from start band
685     *to_band = band;
686                                  //so remember edge
687     last_transition_out_it = *start_pt_it;
688                                  //before we left
689     last_transition_out_it.backward ();
690   }
691   else {
692     *to_band = last_trans_out_to_band;
693   }
694 
695   if (*to_band > start_band)
696     *increment = 1;
697   else
698     *increment = -1;
699 
700   find_trans_point (&last_transition_out_it, start_band,
701     start_band + *increment, exit_pt);
702   maintain_limits (min_x, max_x, exit_pt->x ());
703   *start_pt_it = last_transition_out_it;
704 }
705 
706 
find_band(float y)707 inT16 find_band(  // find POINT's band
708                 float y) {
709   inT16 band;
710 
711   for (band = 1; band <= blockocc_band_count; band++) {
712     if (bands[band].in_nominal (y))
713       return band;
714   }
715   BLOCKOCC.error ("find_band", ABORT, "Cant find band for %d", y);
716   return 0;
717 }
718 
719 
compress_region_list(REGION_OCC_LIST * region_occ_list)720 void compress_region_list(  // join open regions
721                           REGION_OCC_LIST *region_occ_list) {
722   REGION_OCC_IT it (&(region_occ_list[0]));
723   REGION_OCC *open_right = NULL;
724 
725   inT16 i = 0;
726 
727   for (i = 0; i <= blockocc_band_count; i++) {
728     it.set_to_list (&(region_occ_list[i]));
729     if (!it.empty ()) {
730       /* First check for left right pairs. Merge them into the open right and delete
731       the open left. */
732       open_right = NULL;
733       for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) {
734         switch (it.data ()->region_type) {
735           case REGION_TYPE_OPEN_RIGHT:
736           {
737             if (open_right != NULL)
738               BLOCKOCC.error ("compress_region_list", ABORT,
739                 "unmatched right");
740             else
741               open_right = it.data ();
742             break;
743           }
744           case REGION_TYPE_OPEN_LEFT:
745           {
746             if (open_right == NULL)
747               BLOCKOCC.error ("compress_region_list", ABORT,
748                 "unmatched left");
749             else {
750               open_right->max_x = it.data ()->max_x;
751               open_right = NULL;
752               delete it.extract ();
753             }
754             break;
755           }
756           default:
757             break;
758         }
759       }
760       if (open_right != NULL)
761         BLOCKOCC.error ("compress_region_list", ABORT,
762           "unmatched right remaining");
763 
764       /* Now cycle the list again, merging and deleting any redundant regions */
765       it.move_to_first ();
766       open_right = it.data ();
767       while (!it.at_last ()) {
768         it.forward ();
769         if (it.data ()->min_x <= open_right->max_x) {
770         // Overlaps
771           if (it.data ()->max_x > open_right->max_x)
772             open_right->max_x = it.data ()->max_x;
773           // Extend
774           delete it.extract ();
775         }
776         else
777           open_right = it.data ();
778       }
779     }
780   }
781 }
782 
783 
find_fbox(OUTLINE_IT * out_it,float * min_x,float * min_y,float * max_x,float * max_y)784 void find_fbox(OUTLINE_IT *out_it,
785                float *min_x,
786                float *min_y,
787                float *max_x,
788                float *max_y) {
789   POLYPT_IT pt_it = out_it->data ()->polypts ();
790   FCOORD pt;
791   *min_x = 9999.0f;
792   *min_y = 9999.0f;
793   *max_x = 0.0f;
794   *max_y = 0.0f;
795 
796   for (pt_it.mark_cycle_pt (); !pt_it.cycled_list (); pt_it.forward ()) {
797     pt = pt_it.data ()->pos;
798     maintain_limits (min_x, max_x, pt.x ());
799     maintain_limits (min_y, max_y, pt.y ());
800   }
801 }
802 
803 
maintain_limits(float * min_x,float * max_x,float x)804 void maintain_limits(float *min_x, float *max_x, float x) {
805   if (x > *max_x)
806     *max_x = x;
807   if (x < *min_x)
808     *min_x = x;
809 }
810