• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**********************************************************************
2  * File:        blobbox.cpp  (Formerly blobnbox.c)
3  * Description: Code for the textord blob class.
4  * Author:					Ray Smith
5  * Created:					Thu Jul 30 09:08:51 BST 1992
6  *
7  * (C) Copyright 1992, 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 "mfcpch.h"
21 #include          "blobbox.h"
22 
23 #define PROJECTION_MARGIN 10     //arbitrary
24 #define EXTERN
25 
26 EXTERN double_VAR (textord_error_weight, 3,
27 "Weighting for error in believability");
28 EXTERN BOOL_VAR (pitsync_projection_fix, TRUE,
29 "Fix bug in projection profile");
30 
ELIST2IZE(TO_ROW)31 ELISTIZE (BLOBNBOX) ELIST2IZE (TO_ROW) ELISTIZE (TO_BLOCK)
32 /**********************************************************************
33  * BLOBNBOX::merge
34  *
35  * Merge this blob with the given blob, which should be after this.
36  **********************************************************************/
37 void BLOBNBOX::merge(                    //merge blobs
38                      BLOBNBOX *nextblob  //blob to join with
39                     ) {
40   box += nextblob->box;          //merge boxes
41   nextblob->joined = TRUE;
42 }
43 
44 
45 /**********************************************************************
46  * BLOBNBOX::chop
47  *
48  * Chop this blob into equal sized pieces using the x height as a guide.
49  * The blob is not actually chopped. Instead, fake blobs are inserted
50  * with the relevant bounding boxes.
51  **********************************************************************/
52 
chop(BLOBNBOX_IT * start_it,BLOBNBOX_IT * end_it,FCOORD rotation,float xheight)53 void BLOBNBOX::chop(                        //chop blobs
54                     BLOBNBOX_IT *start_it,  //location of this
55                     BLOBNBOX_IT *end_it,    //iterator
56                     FCOORD rotation,        //for landscape
57                     float xheight           //of line
58                    ) {
59   inT16 blobcount;               //no of blobs
60   BLOBNBOX *newblob;             //fake blob
61   BLOBNBOX *blob;                //current blob
62   inT16 blobindex;               //number of chop
63   inT16 leftx;                   //left edge of blob
64   float blobwidth;               //width of each
65   float rightx;                  //right edge to scan
66   float ymin, ymax;              //limits of new blob
67   float test_ymin, test_ymax;    //limits of part blob
68   ICOORD bl, tr;                 //corners of box
69   BLOBNBOX_IT blob_it;           //blob iterator
70 
71                                  //get no of chops
72   blobcount = (inT16) floor (box.width () / xheight);
73   if (blobcount > 1 && (blob_ptr != NULL || cblob_ptr != NULL)) {
74                                  //width of each
75     blobwidth = (float) (box.width () + 1) / blobcount;
76     for (blobindex = blobcount - 1, rightx = box.right ();
77     blobindex >= 0; blobindex--, rightx -= blobwidth) {
78       ymin = (float) MAX_INT32;
79       ymax = (float) -MAX_INT32;
80       blob_it = *start_it;
81       do {
82         blob = blob_it.data ();
83         if (blob->blob_ptr != NULL)
84           find_blob_limits (blob->blob_ptr, rightx - blobwidth, rightx,
85             rotation, test_ymin, test_ymax);
86         else
87           find_cblob_vlimits (blob->cblob_ptr, rightx - blobwidth,
88             rightx,
89             /*rotation, */ test_ymin, test_ymax);
90         blob_it.forward ();
91         if (test_ymin < ymin)
92           ymin = test_ymin;
93         if (test_ymax > ymax)
94           ymax = test_ymax;
95       }
96       while (blob != end_it->data ());
97       if (ymin < ymax) {
98         leftx = (inT16) floor (rightx - blobwidth);
99         if (leftx < box.left ())
100           leftx = box.left ();   //clip to real box
101         bl = ICOORD (leftx, (inT16) floor (ymin));
102         tr = ICOORD ((inT16) ceil (rightx), (inT16) ceil (ymax));
103         if (blobindex == 0)
104           box = TBOX (bl, tr);    //change box
105         else {
106           newblob = new BLOBNBOX;
107                                  //box is all it has
108           newblob->box = TBOX (bl, tr);
109                                  //stay on current
110           end_it->add_after_stay_put (newblob);
111         }
112       }
113     }
114   }
115 }
116 
117 
118 /**********************************************************************
119  * find_blob_limits
120  *
121  * Scan the outlines of the blob to locate the y min and max
122  * between the given x limits.
123  **********************************************************************/
124 
find_blob_limits(PBLOB * blob,float leftx,float rightx,FCOORD rotation,float & ymin,float & ymax)125 void find_blob_limits(                  //get y limits
126                       PBLOB *blob,      //blob to search
127                       float leftx,      //x limits
128                       float rightx,
129                       FCOORD rotation,  //for landscape
130                       float &ymin,      //output y limits
131                       float &ymax) {
132   float testy;                   //y intercept
133   FCOORD pos;                    //rotated
134   FCOORD vec;
135   POLYPT *polypt;                //current point
136                                  //outlines
137   OUTLINE_IT out_it = blob->out_list ();
138   POLYPT_IT poly_it;             //outline pts
139 
140   ymin = (float) MAX_INT32;
141   ymax = (float) -MAX_INT32;
142   for (out_it.mark_cycle_pt (); !out_it.cycled_list (); out_it.forward ()) {
143                                  //get points
144     poly_it.set_to_list (out_it.data ()->polypts ());
145     for (poly_it.mark_cycle_pt (); !poly_it.cycled_list ();
146     poly_it.forward ()) {
147       polypt = poly_it.data ();
148       pos = polypt->pos;
149       pos.rotate (rotation);
150       vec = polypt->vec;
151       vec.rotate (rotation);
152       if ((pos.x () < leftx && pos.x () + vec.x () > leftx)
153       || (pos.x () > leftx && pos.x () + vec.x () < leftx)) {
154         testy = pos.y () + vec.y () * (leftx - pos.x ()) / vec.x ();
155         //intercept of boundary
156         if (testy < ymin)
157           ymin = testy;
158         if (testy > ymax)
159           ymax = testy;
160       }
161       if (pos.x () >= leftx && pos.x () <= rightx) {
162         if (pos.y () > ymax)
163           ymax = pos.y ();
164         if (pos.y () < ymin)
165           ymin = pos.y ();
166       }
167       if ((pos.x () > rightx && pos.x () + vec.x () < rightx)
168       || (pos.x () < rightx && pos.x () + vec.x () > rightx)) {
169         testy = pos.y () + vec.y () * (rightx - pos.x ()) / vec.x ();
170         //intercept of boundary
171         if (testy < ymin)
172           ymin = testy;
173         if (testy > ymax)
174           ymax = testy;
175       }
176     }
177   }
178 }
179 
180 
181 /**********************************************************************
182  * find_cblob_limits
183  *
184  * Scan the outlines of the cblob to locate the y min and max
185  * between the given x limits.
186  **********************************************************************/
187 
find_cblob_limits(C_BLOB * blob,float leftx,float rightx,FCOORD rotation,float & ymin,float & ymax)188 void find_cblob_limits(                  //get y limits
189                        C_BLOB *blob,     //blob to search
190                        float leftx,      //x limits
191                        float rightx,
192                        FCOORD rotation,  //for landscape
193                        float &ymin,      //output y limits
194                        float &ymax) {
195   inT16 stepindex;               //current point
196   ICOORD pos;                    //current coords
197   ICOORD vec;                    //rotated step
198   C_OUTLINE *outline;            //current outline
199                                  //outlines
200   C_OUTLINE_IT out_it = blob->out_list ();
201 
202   ymin = (float) MAX_INT32;
203   ymax = (float) -MAX_INT32;
204   for (out_it.mark_cycle_pt (); !out_it.cycled_list (); out_it.forward ()) {
205     outline = out_it.data ();
206     pos = outline->start_pos (); //get coords
207     pos.rotate (rotation);
208     for (stepindex = 0; stepindex < outline->pathlength (); stepindex++) {
209                                  //inside
210       if (pos.x () >= leftx && pos.x () <= rightx) {
211         if (pos.y () > ymax)
212           ymax = pos.y ();
213         if (pos.y () < ymin)
214           ymin = pos.y ();
215       }
216       vec = outline->step (stepindex);
217       vec.rotate (rotation);
218       pos += vec;                //move to next
219     }
220   }
221 }
222 
223 
224 /**********************************************************************
225  * find_cblob_vlimits
226  *
227  * Scan the outlines of the cblob to locate the y min and max
228  * between the given x limits.
229  **********************************************************************/
230 
find_cblob_vlimits(C_BLOB * blob,float leftx,float rightx,float & ymin,float & ymax)231 void find_cblob_vlimits(               //get y limits
232                         C_BLOB *blob,  //blob to search
233                         float leftx,   //x limits
234                         float rightx,
235                         float &ymin,   //output y limits
236                         float &ymax) {
237   inT16 stepindex;               //current point
238   ICOORD pos;                    //current coords
239   ICOORD vec;                    //rotated step
240   C_OUTLINE *outline;            //current outline
241                                  //outlines
242   C_OUTLINE_IT out_it = blob->out_list ();
243 
244   ymin = (float) MAX_INT32;
245   ymax = (float) -MAX_INT32;
246   for (out_it.mark_cycle_pt (); !out_it.cycled_list (); out_it.forward ()) {
247     outline = out_it.data ();
248     pos = outline->start_pos (); //get coords
249     for (stepindex = 0; stepindex < outline->pathlength (); stepindex++) {
250                                  //inside
251       if (pos.x () >= leftx && pos.x () <= rightx) {
252         if (pos.y () > ymax)
253           ymax = pos.y ();
254         if (pos.y () < ymin)
255           ymin = pos.y ();
256       }
257       vec = outline->step (stepindex);
258       pos += vec;                //move to next
259     }
260   }
261 }
262 
263 
264 /**********************************************************************
265  * find_cblob_hlimits
266  *
267  * Scan the outlines of the cblob to locate the x min and max
268  * between the given y limits.
269  **********************************************************************/
270 
find_cblob_hlimits(C_BLOB * blob,float bottomy,float topy,float & xmin,float & xmax)271 void find_cblob_hlimits(                //get x limits
272                         C_BLOB *blob,   //blob to search
273                         float bottomy,  //y limits
274                         float topy,
275                         float &xmin,    //output x limits
276                         float &xmax) {
277   inT16 stepindex;               //current point
278   ICOORD pos;                    //current coords
279   ICOORD vec;                    //rotated step
280   C_OUTLINE *outline;            //current outline
281                                  //outlines
282   C_OUTLINE_IT out_it = blob->out_list ();
283 
284   xmin = (float) MAX_INT32;
285   xmax = (float) -MAX_INT32;
286   for (out_it.mark_cycle_pt (); !out_it.cycled_list (); out_it.forward ()) {
287     outline = out_it.data ();
288     pos = outline->start_pos (); //get coords
289     for (stepindex = 0; stepindex < outline->pathlength (); stepindex++) {
290                                  //inside
291       if (pos.y () >= bottomy && pos.y () <= topy) {
292         if (pos.x () > xmax)
293           xmax = pos.x ();
294         if (pos.x () < xmin)
295           xmin = pos.x ();
296       }
297       vec = outline->step (stepindex);
298       pos += vec;                //move to next
299     }
300   }
301 }
302 
303 
304 /**********************************************************************
305  * rotate_blob
306  *
307  * Poly copy the blob and rotate the copy by the given vector.
308  **********************************************************************/
309 
rotate_blob(PBLOB * blob,FCOORD rotation)310 PBLOB *rotate_blob(                 //get y limits
311                    PBLOB *blob,     //blob to search
312                    FCOORD rotation  //vector to rotate by
313                   ) {
314   PBLOB *copy;                   //copy of blob
315   POLYPT *polypt;                //current point
316   OUTLINE_IT out_it;
317   POLYPT_IT poly_it;             //outline pts
318 
319   copy = new PBLOB;
320   *copy = *blob;                 //deep copy
321   out_it.set_to_list (copy->out_list ());
322   for (out_it.mark_cycle_pt (); !out_it.cycled_list (); out_it.forward ()) {
323                                  //get points
324     poly_it.set_to_list (out_it.data ()->polypts ());
325     for (poly_it.mark_cycle_pt (); !poly_it.cycled_list ();
326     poly_it.forward ()) {
327       polypt = poly_it.data ();
328                                  //rotate it
329       polypt->pos.rotate (rotation);
330       polypt->vec.rotate (rotation);
331     }
332     out_it.data ()->compute_bb ();
333   }
334   return copy;
335 }
336 
337 
338 /**********************************************************************
339  * rotate_cblob
340  *
341  * Poly copy the blob and rotate the copy by the given vector.
342  **********************************************************************/
343 
rotate_cblob(C_BLOB * blob,float xheight,FCOORD rotation)344 PBLOB *rotate_cblob(                 //rotate it
345                     C_BLOB *blob,    //blob to search
346                     float xheight,   //for poly approx
347                     FCOORD rotation  //for landscape
348                    ) {
349   PBLOB *copy;                   //copy of blob
350   POLYPT *polypt;                //current point
351   OUTLINE_IT out_it;
352   POLYPT_IT poly_it;             //outline pts
353 
354   copy = new PBLOB (blob, xheight);
355   out_it.set_to_list (copy->out_list ());
356   for (out_it.mark_cycle_pt (); !out_it.cycled_list (); out_it.forward ()) {
357                                  //get points
358     poly_it.set_to_list (out_it.data ()->polypts ());
359     for (poly_it.mark_cycle_pt (); !poly_it.cycled_list ();
360     poly_it.forward ()) {
361       polypt = poly_it.data ();
362                                  //rotate it
363       polypt->pos.rotate (rotation);
364       polypt->vec.rotate (rotation);
365     }
366     out_it.data ()->compute_bb ();
367   }
368   return copy;
369 }
370 
371 
372 /**********************************************************************
373  * crotate_cblob
374  *
375  * Rotate the copy by the given vector and return a C_BLOB.
376  **********************************************************************/
377 
crotate_cblob(C_BLOB * blob,FCOORD rotation)378 C_BLOB *crotate_cblob(                 //rotate it
379                       C_BLOB *blob,    //blob to search
380                       FCOORD rotation  //for landscape
381                      ) {
382   C_OUTLINE_LIST out_list;       //output outlines
383                                  //input outlines
384   C_OUTLINE_IT in_it = blob->out_list ();
385                                  //output outlines
386   C_OUTLINE_IT out_it = &out_list;
387 
388   for (in_it.mark_cycle_pt (); !in_it.cycled_list (); in_it.forward ()) {
389     out_it.add_after_then_move (new C_OUTLINE (in_it.data (), rotation));
390   }
391   return new C_BLOB (&out_list);
392 }
393 
394 
395 /**********************************************************************
396  * box_next
397  *
398  * Compute the bounding box of this blob with merging of x overlaps
399  * but no pre-chopping.
400  * Then move the iterator on to the start of the next blob.
401  **********************************************************************/
402 
box_next(BLOBNBOX_IT * it)403 TBOX box_next(                 //get bounding box
404              BLOBNBOX_IT *it  //iterator to blobds
405             ) {
406   BLOBNBOX *blob;                //current blob
407   TBOX result;                    //total box
408 
409   blob = it->data ();
410   result = blob->bounding_box ();
411   do {
412     it->forward ();
413     blob = it->data ();
414     if (blob->blob () == NULL && blob->cblob () == NULL)
415                                  //was pre-chopped
416       result += blob->bounding_box ();
417   }
418                                  //until next real blob
419   while ((blob->blob () == NULL && blob->cblob () == NULL) || blob->joined_to_prev ());
420   return result;
421 }
422 
423 
424 /**********************************************************************
425  * box_next_pre_chopped
426  *
427  * Compute the bounding box of this blob with merging of x overlaps
428  * but WITH pre-chopping.
429  * Then move the iterator on to the start of the next pre-chopped blob.
430  **********************************************************************/
431 
box_next_pre_chopped(BLOBNBOX_IT * it)432 TBOX box_next_pre_chopped(                 //get bounding box
433                          BLOBNBOX_IT *it  //iterator to blobds
434                         ) {
435   BLOBNBOX *blob;                //current blob
436   TBOX result;                    //total box
437 
438   blob = it->data ();
439   result = blob->bounding_box ();
440   do {
441     it->forward ();
442     blob = it->data ();
443   }
444                                  //until next real blob
445   while (blob->joined_to_prev ());
446   return result;
447 }
448 
449 
450 /**********************************************************************
451  * TO_ROW::TO_ROW
452  *
453  * Constructor to make a row from a blob.
454  **********************************************************************/
455 
TO_ROW(BLOBNBOX * blob,float top,float bottom,float row_size)456 TO_ROW::TO_ROW (                 //constructor
457 BLOBNBOX * blob,                 //first blob
458 float top,                       //corrected top
459 float bottom,                    //of row
460 float row_size                   //ideal
461 ): y_min(bottom), y_max(top), initial_y_min(bottom), num_repeated_sets_(-1) {
462   float diff;                    //in size
463   BLOBNBOX_IT it = &blobs;       //list of blobs
464 
465   it.add_to_end (blob);
466   diff = top - bottom - row_size;
467   if (diff > 0) {
468     y_max -= diff / 2;
469     y_min += diff / 2;
470   }
471                                  //very small object
472   else if ((top - bottom) * 3 < row_size) {
473     diff = row_size / 3 + bottom - top;
474     y_max += diff / 2;
475     y_min -= diff / 2;
476   }
477 }
478 
479 
480 /**********************************************************************
481  * TO_ROW:add_blob
482  *
483  * Add the blob to the end of the row.
484  **********************************************************************/
485 
add_blob(BLOBNBOX * blob,float top,float bottom,float row_size)486 void TO_ROW::add_blob(                 //constructor
487                       BLOBNBOX *blob,  //first blob
488                       float top,       //corrected top
489                       float bottom,    //of row
490                       float row_size   //ideal
491                      ) {
492   float allowed;                 //allowed expansion
493   float available;               //expansion
494   BLOBNBOX_IT it = &blobs;       //list of blobs
495 
496   it.add_to_end (blob);
497   allowed = row_size + y_min - y_max;
498   if (allowed > 0) {
499     available = top > y_max ? top - y_max : 0;
500     if (bottom < y_min)
501                                  //total available
502         available += y_min - bottom;
503     if (available > 0) {
504       available += available;    //do it gradually
505       if (available < allowed)
506         available = allowed;
507       if (bottom < y_min)
508         y_min -= (y_min - bottom) * allowed / available;
509       if (top > y_max)
510         y_max += (top - y_max) * allowed / available;
511     }
512   }
513 }
514 
515 
516 /**********************************************************************
517  * TO_ROW:insert_blob
518  *
519  * Add the blob to the row in the correct position.
520  **********************************************************************/
521 
insert_blob(BLOBNBOX * blob)522 void TO_ROW::insert_blob(                //constructor
523                          BLOBNBOX *blob  //first blob
524                         ) {
525   BLOBNBOX_IT it = &blobs;       //list of blobs
526 
527   if (it.empty ())
528     it.add_before_then_move (blob);
529   else {
530     it.mark_cycle_pt ();
531     while (!it.cycled_list ()
532       && it.data ()->bounding_box ().left () <=
533       blob->bounding_box ().left ())
534       it.forward ();
535     if (it.cycled_list ())
536       it.add_to_end (blob);
537     else
538       it.add_before_stay_put (blob);
539   }
540 }
541 
542 
543 /**********************************************************************
544  * TO_ROW::compute_vertical_projection
545  *
546  * Compute the vertical projection of a TO_ROW from its blobs.
547  **********************************************************************/
548 
compute_vertical_projection()549 void TO_ROW::compute_vertical_projection() {  //project whole row
550   TBOX row_box;                   //bound of row
551   BLOBNBOX *blob;                //current blob
552   TBOX blob_box;                  //bounding box
553   BLOBNBOX_IT blob_it = blob_list ();
554 
555   if (blob_it.empty ())
556     return;
557   row_box = blob_it.data ()->bounding_box ();
558   for (blob_it.mark_cycle_pt (); !blob_it.cycled_list (); blob_it.forward ())
559     row_box += blob_it.data ()->bounding_box ();
560 
561   projection.set_range (row_box.left () - PROJECTION_MARGIN,
562     row_box.right () + PROJECTION_MARGIN);
563   projection_left = row_box.left () - PROJECTION_MARGIN;
564   projection_right = row_box.right () + PROJECTION_MARGIN;
565   for (blob_it.mark_cycle_pt (); !blob_it.cycled_list (); blob_it.forward ()) {
566     blob = blob_it.data ();
567     if (blob->blob () != NULL)
568       vertical_blob_projection (blob->blob (), &projection);
569     else if (blob->cblob () != NULL)
570       vertical_cblob_projection (blob->cblob (), &projection);
571   }
572 }
573 
574 
575 /**********************************************************************
576  * vertical_blob_projection
577  *
578  * Compute the vertical projection of a blob from its outlines
579  * and add to the given STATS.
580  **********************************************************************/
581 
vertical_blob_projection(PBLOB * blob,STATS * stats)582 void vertical_blob_projection(              //project outlines
583                               PBLOB *blob,  //blob to project
584                               STATS *stats  //output
585                              ) {
586                                  //outlines of blob
587   OUTLINE_IT out_it = blob->out_list ();
588 
589   for (out_it.mark_cycle_pt (); !out_it.cycled_list (); out_it.forward ()) {
590     vertical_outline_projection (out_it.data (), stats);
591   }
592 }
593 
594 
595 /**********************************************************************
596  * vertical_outline_projection
597  *
598  * Compute the vertical projection of a outline from its outlines
599  * and add to the given STATS.
600  **********************************************************************/
601 
vertical_outline_projection(OUTLINE * outline,STATS * stats)602 void vertical_outline_projection(                   //project outlines
603                                  OUTLINE *outline,  //outline to project
604                                  STATS *stats       //output
605                                 ) {
606   POLYPT *polypt;                //current point
607   inT32 xcoord;                  //current pixel coord
608   float end_x;                   //end of vec
609   POLYPT_IT poly_it = outline->polypts ();
610   OUTLINE_IT out_it = outline->child ();
611   float ymean;                   //amount to add
612   float width;                   //amount of x
613 
614   for (poly_it.mark_cycle_pt (); !poly_it.cycled_list (); poly_it.forward ()) {
615     polypt = poly_it.data ();
616     end_x = polypt->pos.x () + polypt->vec.x ();
617     if (polypt->vec.x () > 0) {
618       for (xcoord = (inT32) floor (polypt->pos.x ());
619       xcoord < end_x; xcoord++) {
620         if (polypt->pos.x () < xcoord) {
621           width = (float) xcoord;
622           ymean =
623             polypt->vec.y () * (xcoord -
624             polypt->pos.x ()) / polypt->vec.x () +
625             polypt->pos.y ();
626         }
627         else {
628           width = polypt->pos.x ();
629           ymean = polypt->pos.y ();
630         }
631         if (end_x > xcoord + 1) {
632           width -= xcoord + 1;
633           ymean +=
634             polypt->vec.y () * (xcoord + 1 -
635             polypt->pos.x ()) / polypt->vec.x () +
636             polypt->pos.y ();
637         }
638         else {
639           width -= end_x;
640           ymean += polypt->pos.y () + polypt->vec.y ();
641         }
642         ymean = ymean * width / 2;
643         stats->add (xcoord, (inT32) floor (ymean + 0.5));
644       }
645     }
646     else if (polypt->vec.x () < 0) {
647       for (xcoord = (inT32) floor (end_x);
648       xcoord < polypt->pos.x (); xcoord++) {
649         if (polypt->pos.x () > xcoord + 1) {
650           width = xcoord + 1.0f;
651           ymean =
652             polypt->vec.y () * (xcoord + 1 -
653             polypt->pos.x ()) / polypt->vec.x () +
654             polypt->pos.y ();
655         }
656         else {
657           width = polypt->pos.x ();
658           ymean = polypt->pos.y ();
659         }
660         if (end_x < xcoord) {
661           width -= xcoord;
662           ymean +=
663             polypt->vec.y () * (xcoord -
664             polypt->pos.x ()) / polypt->vec.x () +
665             polypt->pos.y ();
666         }
667         else {
668           width -= end_x;
669           ymean += polypt->pos.y () + polypt->vec.y ();
670         }
671         ymean = ymean * width / 2;
672         stats->add (xcoord, (inT32) floor (ymean + 0.5));
673       }
674     }
675   }
676 
677   for (out_it.mark_cycle_pt (); !out_it.cycled_list (); out_it.forward ()) {
678     vertical_outline_projection (out_it.data (), stats);
679   }
680 }
681 
682 
683 /**********************************************************************
684  * vertical_cblob_projection
685  *
686  * Compute the vertical projection of a cblob from its outlines
687  * and add to the given STATS.
688  **********************************************************************/
689 
vertical_cblob_projection(C_BLOB * blob,STATS * stats)690 void vertical_cblob_projection(               //project outlines
691                                C_BLOB *blob,  //blob to project
692                                STATS *stats   //output
693                               ) {
694                                  //outlines of blob
695   C_OUTLINE_IT out_it = blob->out_list ();
696 
697   for (out_it.mark_cycle_pt (); !out_it.cycled_list (); out_it.forward ()) {
698     vertical_coutline_projection (out_it.data (), stats);
699   }
700 }
701 
702 
703 /**********************************************************************
704  * vertical_coutline_projection
705  *
706  * Compute the vertical projection of a outline from its outlines
707  * and add to the given STATS.
708  **********************************************************************/
709 
vertical_coutline_projection(C_OUTLINE * outline,STATS * stats)710 void vertical_coutline_projection(                     //project outlines
711                                   C_OUTLINE *outline,  //outline to project
712                                   STATS *stats         //output
713                                  ) {
714   ICOORD pos;                    //current point
715   ICOORD step;                   //edge step
716   inT32 length;                  //of outline
717   inT16 stepindex;               //current step
718   C_OUTLINE_IT out_it = outline->child ();
719 
720   pos = outline->start_pos ();
721   length = outline->pathlength ();
722   for (stepindex = 0; stepindex < length; stepindex++) {
723     step = outline->step (stepindex);
724     if (step.x () > 0) {
725       if (pitsync_projection_fix)
726         stats->add (pos.x (), -pos.y ());
727       else
728         stats->add (pos.x (), pos.y ());
729     }
730     else if (step.x () < 0) {
731       if (pitsync_projection_fix)
732         stats->add (pos.x () - 1, pos.y ());
733       else
734         stats->add (pos.x () - 1, -pos.y ());
735     }
736     pos += step;
737   }
738 
739   for (out_it.mark_cycle_pt (); !out_it.cycled_list (); out_it.forward ()) {
740     vertical_coutline_projection (out_it.data (), stats);
741   }
742 }
743 
744 
745 /**********************************************************************
746  * TO_BLOCK::TO_BLOCK
747  *
748  * Constructor to make a TO_BLOCK from a real block.
749  **********************************************************************/
750 
TO_BLOCK(BLOCK * src_block)751 TO_BLOCK::TO_BLOCK(                  //make a block
752                    BLOCK *src_block  //real block
753                   ) {
754   block = src_block;
755 }
756 
clear_blobnboxes(BLOBNBOX_LIST * boxes)757 static void clear_blobnboxes(BLOBNBOX_LIST* boxes) {
758   BLOBNBOX_IT it = boxes;
759   // A BLOBNBOX generally doesn't own its blobs, so if they do, you
760   // have to delete them explicitly.
761   for (it.mark_cycle_pt(); !it.cycled_list(); it.forward()) {
762     BLOBNBOX* box = it.data();
763     if (box->blob() != NULL)
764       delete box->blob();
765     if (box->cblob() != NULL)
766       delete box->cblob();
767   }
768 }
769 
~TO_BLOCK()770 TO_BLOCK::~TO_BLOCK() {
771   // Any residual BLOBNBOXes at this stage own their blobs, so delete them.
772   clear_blobnboxes(&blobs);
773   clear_blobnboxes(&underlines);
774   clear_blobnboxes(&noise_blobs);
775   clear_blobnboxes(&small_blobs);
776   clear_blobnboxes(&large_blobs);
777 }
778 
779 // Draw the blobs on the various lists in the block in different colors.
780 #ifndef GRAPHICS_DISABLED
plot_graded_blobs(ScrollView * to_win)781 void TO_BLOCK::plot_graded_blobs(ScrollView* to_win) {
782   plot_blob_list(to_win, &noise_blobs, ScrollView::CORAL, ScrollView::BLUE);
783   plot_blob_list(to_win, &small_blobs,
784                  ScrollView::GOLDENROD, ScrollView::YELLOW);
785   plot_blob_list(to_win, &large_blobs,
786                  ScrollView::DARK_GREEN, ScrollView::YELLOW);
787   plot_blob_list(to_win, &blobs, ScrollView::WHITE, ScrollView::BROWN);
788 }
789 #endif
790 
791 /**********************************************************************
792  * plot_blob_list
793  *
794  * Draw a list of blobs.
795  **********************************************************************/
796 
797 #ifndef GRAPHICS_DISABLED
plot_blob_list(ScrollView * win,BLOBNBOX_LIST * list,ScrollView::Color body_colour,ScrollView::Color child_colour)798 void plot_blob_list(ScrollView* win,                   // window to draw in
799                     BLOBNBOX_LIST *list,               // blob list
800                     ScrollView::Color body_colour,     // colour to draw
801                     ScrollView::Color child_colour) {  // colour of child
802   BLOBNBOX_IT it = list;
803   for (it.mark_cycle_pt(); !it.cycled_list(); it.forward()) {
804     it.data()->plot(win, body_colour, child_colour);
805   }
806 }
807 #endif
808 
809 
810