| /********************************************************************** |
| * File: blobbox.cpp (Formerly blobnbox.c) |
| * Description: Code for the textord blob class. |
| * Author: Ray Smith |
| * Created: Thu Jul 30 09:08:51 BST 1992 |
| * |
| * (C) Copyright 1992, Hewlett-Packard Ltd. |
| ** Licensed under the Apache License, Version 2.0 (the "License"); |
| ** you may not use this file except in compliance with the License. |
| ** You may obtain a copy of the License at |
| ** http://www.apache.org/licenses/LICENSE-2.0 |
| ** Unless required by applicable law or agreed to in writing, software |
| ** distributed under the License is distributed on an "AS IS" BASIS, |
| ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| ** See the License for the specific language governing permissions and |
| ** limitations under the License. |
| * |
| **********************************************************************/ |
| |
| #include "mfcpch.h" |
| #include "blobbox.h" |
| |
| #define PROJECTION_MARGIN 10 //arbitrary |
| #define EXTERN |
| |
| EXTERN double_VAR (textord_error_weight, 3, |
| "Weighting for error in believability"); |
| EXTERN BOOL_VAR (pitsync_projection_fix, TRUE, |
| "Fix bug in projection profile"); |
| |
| ELISTIZE (BLOBNBOX) ELIST2IZE (TO_ROW) ELISTIZE (TO_BLOCK) |
| /********************************************************************** |
| * BLOBNBOX::merge |
| * |
| * Merge this blob with the given blob, which should be after this. |
| **********************************************************************/ |
| void BLOBNBOX::merge( //merge blobs |
| BLOBNBOX *nextblob //blob to join with |
| ) { |
| box += nextblob->box; //merge boxes |
| nextblob->joined = TRUE; |
| } |
| |
| |
| /********************************************************************** |
| * BLOBNBOX::chop |
| * |
| * Chop this blob into equal sized pieces using the x height as a guide. |
| * The blob is not actually chopped. Instead, fake blobs are inserted |
| * with the relevant bounding boxes. |
| **********************************************************************/ |
| |
| void BLOBNBOX::chop( //chop blobs |
| BLOBNBOX_IT *start_it, //location of this |
| BLOBNBOX_IT *end_it, //iterator |
| FCOORD rotation, //for landscape |
| float xheight //of line |
| ) { |
| inT16 blobcount; //no of blobs |
| BLOBNBOX *newblob; //fake blob |
| BLOBNBOX *blob; //current blob |
| inT16 blobindex; //number of chop |
| inT16 leftx; //left edge of blob |
| float blobwidth; //width of each |
| float rightx; //right edge to scan |
| float ymin, ymax; //limits of new blob |
| float test_ymin, test_ymax; //limits of part blob |
| ICOORD bl, tr; //corners of box |
| BLOBNBOX_IT blob_it; //blob iterator |
| |
| //get no of chops |
| blobcount = (inT16) floor (box.width () / xheight); |
| if (blobcount > 1 && (blob_ptr != NULL || cblob_ptr != NULL)) { |
| //width of each |
| blobwidth = (float) (box.width () + 1) / blobcount; |
| for (blobindex = blobcount - 1, rightx = box.right (); |
| blobindex >= 0; blobindex--, rightx -= blobwidth) { |
| ymin = (float) MAX_INT32; |
| ymax = (float) -MAX_INT32; |
| blob_it = *start_it; |
| do { |
| blob = blob_it.data (); |
| if (blob->blob_ptr != NULL) |
| find_blob_limits (blob->blob_ptr, rightx - blobwidth, rightx, |
| rotation, test_ymin, test_ymax); |
| else |
| find_cblob_vlimits (blob->cblob_ptr, rightx - blobwidth, |
| rightx, |
| /*rotation, */ test_ymin, test_ymax); |
| blob_it.forward (); |
| if (test_ymin < ymin) |
| ymin = test_ymin; |
| if (test_ymax > ymax) |
| ymax = test_ymax; |
| } |
| while (blob != end_it->data ()); |
| if (ymin < ymax) { |
| leftx = (inT16) floor (rightx - blobwidth); |
| if (leftx < box.left ()) |
| leftx = box.left (); //clip to real box |
| bl = ICOORD (leftx, (inT16) floor (ymin)); |
| tr = ICOORD ((inT16) ceil (rightx), (inT16) ceil (ymax)); |
| if (blobindex == 0) |
| box = TBOX (bl, tr); //change box |
| else { |
| newblob = new BLOBNBOX; |
| //box is all it has |
| newblob->box = TBOX (bl, tr); |
| //stay on current |
| end_it->add_after_stay_put (newblob); |
| } |
| } |
| } |
| } |
| } |
| |
| |
| /********************************************************************** |
| * find_blob_limits |
| * |
| * Scan the outlines of the blob to locate the y min and max |
| * between the given x limits. |
| **********************************************************************/ |
| |
| void find_blob_limits( //get y limits |
| PBLOB *blob, //blob to search |
| float leftx, //x limits |
| float rightx, |
| FCOORD rotation, //for landscape |
| float &ymin, //output y limits |
| float &ymax) { |
| float testy; //y intercept |
| FCOORD pos; //rotated |
| FCOORD vec; |
| POLYPT *polypt; //current point |
| //outlines |
| OUTLINE_IT out_it = blob->out_list (); |
| POLYPT_IT poly_it; //outline pts |
| |
| ymin = (float) MAX_INT32; |
| ymax = (float) -MAX_INT32; |
| for (out_it.mark_cycle_pt (); !out_it.cycled_list (); out_it.forward ()) { |
| //get points |
| poly_it.set_to_list (out_it.data ()->polypts ()); |
| for (poly_it.mark_cycle_pt (); !poly_it.cycled_list (); |
| poly_it.forward ()) { |
| polypt = poly_it.data (); |
| pos = polypt->pos; |
| pos.rotate (rotation); |
| vec = polypt->vec; |
| vec.rotate (rotation); |
| if ((pos.x () < leftx && pos.x () + vec.x () > leftx) |
| || (pos.x () > leftx && pos.x () + vec.x () < leftx)) { |
| testy = pos.y () + vec.y () * (leftx - pos.x ()) / vec.x (); |
| //intercept of boundary |
| if (testy < ymin) |
| ymin = testy; |
| if (testy > ymax) |
| ymax = testy; |
| } |
| if (pos.x () >= leftx && pos.x () <= rightx) { |
| if (pos.y () > ymax) |
| ymax = pos.y (); |
| if (pos.y () < ymin) |
| ymin = pos.y (); |
| } |
| if ((pos.x () > rightx && pos.x () + vec.x () < rightx) |
| || (pos.x () < rightx && pos.x () + vec.x () > rightx)) { |
| testy = pos.y () + vec.y () * (rightx - pos.x ()) / vec.x (); |
| //intercept of boundary |
| if (testy < ymin) |
| ymin = testy; |
| if (testy > ymax) |
| ymax = testy; |
| } |
| } |
| } |
| } |
| |
| |
| /********************************************************************** |
| * find_cblob_limits |
| * |
| * Scan the outlines of the cblob to locate the y min and max |
| * between the given x limits. |
| **********************************************************************/ |
| |
| void find_cblob_limits( //get y limits |
| C_BLOB *blob, //blob to search |
| float leftx, //x limits |
| float rightx, |
| FCOORD rotation, //for landscape |
| float &ymin, //output y limits |
| float &ymax) { |
| inT16 stepindex; //current point |
| ICOORD pos; //current coords |
| ICOORD vec; //rotated step |
| C_OUTLINE *outline; //current outline |
| //outlines |
| C_OUTLINE_IT out_it = blob->out_list (); |
| |
| ymin = (float) MAX_INT32; |
| ymax = (float) -MAX_INT32; |
| for (out_it.mark_cycle_pt (); !out_it.cycled_list (); out_it.forward ()) { |
| outline = out_it.data (); |
| pos = outline->start_pos (); //get coords |
| pos.rotate (rotation); |
| for (stepindex = 0; stepindex < outline->pathlength (); stepindex++) { |
| //inside |
| if (pos.x () >= leftx && pos.x () <= rightx) { |
| if (pos.y () > ymax) |
| ymax = pos.y (); |
| if (pos.y () < ymin) |
| ymin = pos.y (); |
| } |
| vec = outline->step (stepindex); |
| vec.rotate (rotation); |
| pos += vec; //move to next |
| } |
| } |
| } |
| |
| |
| /********************************************************************** |
| * find_cblob_vlimits |
| * |
| * Scan the outlines of the cblob to locate the y min and max |
| * between the given x limits. |
| **********************************************************************/ |
| |
| void find_cblob_vlimits( //get y limits |
| C_BLOB *blob, //blob to search |
| float leftx, //x limits |
| float rightx, |
| float &ymin, //output y limits |
| float &ymax) { |
| inT16 stepindex; //current point |
| ICOORD pos; //current coords |
| ICOORD vec; //rotated step |
| C_OUTLINE *outline; //current outline |
| //outlines |
| C_OUTLINE_IT out_it = blob->out_list (); |
| |
| ymin = (float) MAX_INT32; |
| ymax = (float) -MAX_INT32; |
| for (out_it.mark_cycle_pt (); !out_it.cycled_list (); out_it.forward ()) { |
| outline = out_it.data (); |
| pos = outline->start_pos (); //get coords |
| for (stepindex = 0; stepindex < outline->pathlength (); stepindex++) { |
| //inside |
| if (pos.x () >= leftx && pos.x () <= rightx) { |
| if (pos.y () > ymax) |
| ymax = pos.y (); |
| if (pos.y () < ymin) |
| ymin = pos.y (); |
| } |
| vec = outline->step (stepindex); |
| pos += vec; //move to next |
| } |
| } |
| } |
| |
| |
| /********************************************************************** |
| * find_cblob_hlimits |
| * |
| * Scan the outlines of the cblob to locate the x min and max |
| * between the given y limits. |
| **********************************************************************/ |
| |
| void find_cblob_hlimits( //get x limits |
| C_BLOB *blob, //blob to search |
| float bottomy, //y limits |
| float topy, |
| float &xmin, //output x limits |
| float &xmax) { |
| inT16 stepindex; //current point |
| ICOORD pos; //current coords |
| ICOORD vec; //rotated step |
| C_OUTLINE *outline; //current outline |
| //outlines |
| C_OUTLINE_IT out_it = blob->out_list (); |
| |
| xmin = (float) MAX_INT32; |
| xmax = (float) -MAX_INT32; |
| for (out_it.mark_cycle_pt (); !out_it.cycled_list (); out_it.forward ()) { |
| outline = out_it.data (); |
| pos = outline->start_pos (); //get coords |
| for (stepindex = 0; stepindex < outline->pathlength (); stepindex++) { |
| //inside |
| if (pos.y () >= bottomy && pos.y () <= topy) { |
| if (pos.x () > xmax) |
| xmax = pos.x (); |
| if (pos.x () < xmin) |
| xmin = pos.x (); |
| } |
| vec = outline->step (stepindex); |
| pos += vec; //move to next |
| } |
| } |
| } |
| |
| |
| /********************************************************************** |
| * rotate_blob |
| * |
| * Poly copy the blob and rotate the copy by the given vector. |
| **********************************************************************/ |
| |
| PBLOB *rotate_blob( //get y limits |
| PBLOB *blob, //blob to search |
| FCOORD rotation //vector to rotate by |
| ) { |
| PBLOB *copy; //copy of blob |
| POLYPT *polypt; //current point |
| OUTLINE_IT out_it; |
| POLYPT_IT poly_it; //outline pts |
| |
| copy = new PBLOB; |
| *copy = *blob; //deep copy |
| out_it.set_to_list (copy->out_list ()); |
| for (out_it.mark_cycle_pt (); !out_it.cycled_list (); out_it.forward ()) { |
| //get points |
| poly_it.set_to_list (out_it.data ()->polypts ()); |
| for (poly_it.mark_cycle_pt (); !poly_it.cycled_list (); |
| poly_it.forward ()) { |
| polypt = poly_it.data (); |
| //rotate it |
| polypt->pos.rotate (rotation); |
| polypt->vec.rotate (rotation); |
| } |
| out_it.data ()->compute_bb (); |
| } |
| return copy; |
| } |
| |
| |
| /********************************************************************** |
| * rotate_cblob |
| * |
| * Poly copy the blob and rotate the copy by the given vector. |
| **********************************************************************/ |
| |
| PBLOB *rotate_cblob( //rotate it |
| C_BLOB *blob, //blob to search |
| float xheight, //for poly approx |
| FCOORD rotation //for landscape |
| ) { |
| PBLOB *copy; //copy of blob |
| POLYPT *polypt; //current point |
| OUTLINE_IT out_it; |
| POLYPT_IT poly_it; //outline pts |
| |
| copy = new PBLOB (blob, xheight); |
| out_it.set_to_list (copy->out_list ()); |
| for (out_it.mark_cycle_pt (); !out_it.cycled_list (); out_it.forward ()) { |
| //get points |
| poly_it.set_to_list (out_it.data ()->polypts ()); |
| for (poly_it.mark_cycle_pt (); !poly_it.cycled_list (); |
| poly_it.forward ()) { |
| polypt = poly_it.data (); |
| //rotate it |
| polypt->pos.rotate (rotation); |
| polypt->vec.rotate (rotation); |
| } |
| out_it.data ()->compute_bb (); |
| } |
| return copy; |
| } |
| |
| |
| /********************************************************************** |
| * crotate_cblob |
| * |
| * Rotate the copy by the given vector and return a C_BLOB. |
| **********************************************************************/ |
| |
| C_BLOB *crotate_cblob( //rotate it |
| C_BLOB *blob, //blob to search |
| FCOORD rotation //for landscape |
| ) { |
| C_OUTLINE_LIST out_list; //output outlines |
| //input outlines |
| C_OUTLINE_IT in_it = blob->out_list (); |
| //output outlines |
| C_OUTLINE_IT out_it = &out_list; |
| |
| for (in_it.mark_cycle_pt (); !in_it.cycled_list (); in_it.forward ()) { |
| out_it.add_after_then_move (new C_OUTLINE (in_it.data (), rotation)); |
| } |
| return new C_BLOB (&out_list); |
| } |
| |
| |
| /********************************************************************** |
| * box_next |
| * |
| * Compute the bounding box of this blob with merging of x overlaps |
| * but no pre-chopping. |
| * Then move the iterator on to the start of the next blob. |
| **********************************************************************/ |
| |
| TBOX box_next( //get bounding box |
| BLOBNBOX_IT *it //iterator to blobds |
| ) { |
| BLOBNBOX *blob; //current blob |
| TBOX result; //total box |
| |
| blob = it->data (); |
| result = blob->bounding_box (); |
| do { |
| it->forward (); |
| blob = it->data (); |
| if (blob->blob () == NULL && blob->cblob () == NULL) |
| //was pre-chopped |
| result += blob->bounding_box (); |
| } |
| //until next real blob |
| while ((blob->blob () == NULL && blob->cblob () == NULL) || blob->joined_to_prev ()); |
| return result; |
| } |
| |
| |
| /********************************************************************** |
| * box_next_pre_chopped |
| * |
| * Compute the bounding box of this blob with merging of x overlaps |
| * but WITH pre-chopping. |
| * Then move the iterator on to the start of the next pre-chopped blob. |
| **********************************************************************/ |
| |
| TBOX box_next_pre_chopped( //get bounding box |
| BLOBNBOX_IT *it //iterator to blobds |
| ) { |
| BLOBNBOX *blob; //current blob |
| TBOX result; //total box |
| |
| blob = it->data (); |
| result = blob->bounding_box (); |
| do { |
| it->forward (); |
| blob = it->data (); |
| } |
| //until next real blob |
| while (blob->joined_to_prev ()); |
| return result; |
| } |
| |
| |
| /********************************************************************** |
| * TO_ROW::TO_ROW |
| * |
| * Constructor to make a row from a blob. |
| **********************************************************************/ |
| |
| TO_ROW::TO_ROW ( //constructor |
| BLOBNBOX * blob, //first blob |
| float top, //corrected top |
| float bottom, //of row |
| float row_size //ideal |
| ):y_min (bottom), y_max (top), initial_y_min (bottom) { |
| float diff; //in size |
| BLOBNBOX_IT it = &blobs; //list of blobs |
| |
| it.add_to_end (blob); |
| diff = top - bottom - row_size; |
| if (diff > 0) { |
| y_max -= diff / 2; |
| y_min += diff / 2; |
| } |
| //very small object |
| else if ((top - bottom) * 3 < row_size) { |
| diff = row_size / 3 + bottom - top; |
| y_max += diff / 2; |
| y_min -= diff / 2; |
| } |
| } |
| |
| |
| /********************************************************************** |
| * TO_ROW:add_blob |
| * |
| * Add the blob to the end of the row. |
| **********************************************************************/ |
| |
| void TO_ROW::add_blob( //constructor |
| BLOBNBOX *blob, //first blob |
| float top, //corrected top |
| float bottom, //of row |
| float row_size //ideal |
| ) { |
| float allowed; //allowed expansion |
| float available; //expansion |
| BLOBNBOX_IT it = &blobs; //list of blobs |
| |
| it.add_to_end (blob); |
| allowed = row_size + y_min - y_max; |
| if (allowed > 0) { |
| available = top > y_max ? top - y_max : 0; |
| if (bottom < y_min) |
| //total available |
| available += y_min - bottom; |
| if (available > 0) { |
| available += available; //do it gradually |
| if (available < allowed) |
| available = allowed; |
| if (bottom < y_min) |
| y_min -= (y_min - bottom) * allowed / available; |
| if (top > y_max) |
| y_max += (top - y_max) * allowed / available; |
| } |
| } |
| } |
| |
| |
| /********************************************************************** |
| * TO_ROW:insert_blob |
| * |
| * Add the blob to the row in the correct position. |
| **********************************************************************/ |
| |
| void TO_ROW::insert_blob( //constructor |
| BLOBNBOX *blob //first blob |
| ) { |
| BLOBNBOX_IT it = &blobs; //list of blobs |
| |
| if (it.empty ()) |
| it.add_before_then_move (blob); |
| else { |
| it.mark_cycle_pt (); |
| while (!it.cycled_list () |
| && it.data ()->bounding_box ().left () <= |
| blob->bounding_box ().left ()) |
| it.forward (); |
| if (it.cycled_list ()) |
| it.add_to_end (blob); |
| else |
| it.add_before_stay_put (blob); |
| } |
| } |
| |
| |
| /********************************************************************** |
| * TO_ROW::compute_vertical_projection |
| * |
| * Compute the vertical projection of a TO_ROW from its blobs. |
| **********************************************************************/ |
| |
| void TO_ROW::compute_vertical_projection() { //project whole row |
| TBOX row_box; //bound of row |
| BLOBNBOX *blob; //current blob |
| TBOX blob_box; //bounding box |
| BLOBNBOX_IT blob_it = blob_list (); |
| |
| if (blob_it.empty ()) |
| return; |
| row_box = blob_it.data ()->bounding_box (); |
| for (blob_it.mark_cycle_pt (); !blob_it.cycled_list (); blob_it.forward ()) |
| row_box += blob_it.data ()->bounding_box (); |
| |
| projection.set_range (row_box.left () - PROJECTION_MARGIN, |
| row_box.right () + PROJECTION_MARGIN); |
| projection_left = row_box.left () - PROJECTION_MARGIN; |
| projection_right = row_box.right () + PROJECTION_MARGIN; |
| for (blob_it.mark_cycle_pt (); !blob_it.cycled_list (); blob_it.forward ()) { |
| blob = blob_it.data (); |
| if (blob->blob () != NULL) |
| vertical_blob_projection (blob->blob (), &projection); |
| else if (blob->cblob () != NULL) |
| vertical_cblob_projection (blob->cblob (), &projection); |
| } |
| } |
| |
| |
| /********************************************************************** |
| * vertical_blob_projection |
| * |
| * Compute the vertical projection of a blob from its outlines |
| * and add to the given STATS. |
| **********************************************************************/ |
| |
| void vertical_blob_projection( //project outlines |
| PBLOB *blob, //blob to project |
| STATS *stats //output |
| ) { |
| //outlines of blob |
| OUTLINE_IT out_it = blob->out_list (); |
| |
| for (out_it.mark_cycle_pt (); !out_it.cycled_list (); out_it.forward ()) { |
| vertical_outline_projection (out_it.data (), stats); |
| } |
| } |
| |
| |
| /********************************************************************** |
| * vertical_outline_projection |
| * |
| * Compute the vertical projection of a outline from its outlines |
| * and add to the given STATS. |
| **********************************************************************/ |
| |
| void vertical_outline_projection( //project outlines |
| OUTLINE *outline, //outline to project |
| STATS *stats //output |
| ) { |
| POLYPT *polypt; //current point |
| inT32 xcoord; //current pixel coord |
| float end_x; //end of vec |
| POLYPT_IT poly_it = outline->polypts (); |
| OUTLINE_IT out_it = outline->child (); |
| float ymean; //amount to add |
| float width; //amount of x |
| |
| for (poly_it.mark_cycle_pt (); !poly_it.cycled_list (); poly_it.forward ()) { |
| polypt = poly_it.data (); |
| end_x = polypt->pos.x () + polypt->vec.x (); |
| if (polypt->vec.x () > 0) { |
| for (xcoord = (inT32) floor (polypt->pos.x ()); |
| xcoord < end_x; xcoord++) { |
| if (polypt->pos.x () < xcoord) { |
| width = (float) xcoord; |
| ymean = |
| polypt->vec.y () * (xcoord - |
| polypt->pos.x ()) / polypt->vec.x () + |
| polypt->pos.y (); |
| } |
| else { |
| width = polypt->pos.x (); |
| ymean = polypt->pos.y (); |
| } |
| if (end_x > xcoord + 1) { |
| width -= xcoord + 1; |
| ymean += |
| polypt->vec.y () * (xcoord + 1 - |
| polypt->pos.x ()) / polypt->vec.x () + |
| polypt->pos.y (); |
| } |
| else { |
| width -= end_x; |
| ymean += polypt->pos.y () + polypt->vec.y (); |
| } |
| ymean = ymean * width / 2; |
| stats->add (xcoord, (inT32) floor (ymean + 0.5)); |
| } |
| } |
| else if (polypt->vec.x () < 0) { |
| for (xcoord = (inT32) floor (end_x); |
| xcoord < polypt->pos.x (); xcoord++) { |
| if (polypt->pos.x () > xcoord + 1) { |
| width = xcoord + 1.0f; |
| ymean = |
| polypt->vec.y () * (xcoord + 1 - |
| polypt->pos.x ()) / polypt->vec.x () + |
| polypt->pos.y (); |
| } |
| else { |
| width = polypt->pos.x (); |
| ymean = polypt->pos.y (); |
| } |
| if (end_x < xcoord) { |
| width -= xcoord; |
| ymean += |
| polypt->vec.y () * (xcoord - |
| polypt->pos.x ()) / polypt->vec.x () + |
| polypt->pos.y (); |
| } |
| else { |
| width -= end_x; |
| ymean += polypt->pos.y () + polypt->vec.y (); |
| } |
| ymean = ymean * width / 2; |
| stats->add (xcoord, (inT32) floor (ymean + 0.5)); |
| } |
| } |
| } |
| |
| for (out_it.mark_cycle_pt (); !out_it.cycled_list (); out_it.forward ()) { |
| vertical_outline_projection (out_it.data (), stats); |
| } |
| } |
| |
| |
| /********************************************************************** |
| * vertical_cblob_projection |
| * |
| * Compute the vertical projection of a cblob from its outlines |
| * and add to the given STATS. |
| **********************************************************************/ |
| |
| void vertical_cblob_projection( //project outlines |
| C_BLOB *blob, //blob to project |
| STATS *stats //output |
| ) { |
| //outlines of blob |
| C_OUTLINE_IT out_it = blob->out_list (); |
| |
| for (out_it.mark_cycle_pt (); !out_it.cycled_list (); out_it.forward ()) { |
| vertical_coutline_projection (out_it.data (), stats); |
| } |
| } |
| |
| |
| /********************************************************************** |
| * vertical_coutline_projection |
| * |
| * Compute the vertical projection of a outline from its outlines |
| * and add to the given STATS. |
| **********************************************************************/ |
| |
| void vertical_coutline_projection( //project outlines |
| C_OUTLINE *outline, //outline to project |
| STATS *stats //output |
| ) { |
| ICOORD pos; //current point |
| ICOORD step; //edge step |
| inT32 length; //of outline |
| inT16 stepindex; //current step |
| C_OUTLINE_IT out_it = outline->child (); |
| |
| pos = outline->start_pos (); |
| length = outline->pathlength (); |
| for (stepindex = 0; stepindex < length; stepindex++) { |
| step = outline->step (stepindex); |
| if (step.x () > 0) { |
| if (pitsync_projection_fix) |
| stats->add (pos.x (), -pos.y ()); |
| else |
| stats->add (pos.x (), pos.y ()); |
| } |
| else if (step.x () < 0) { |
| if (pitsync_projection_fix) |
| stats->add (pos.x () - 1, pos.y ()); |
| else |
| stats->add (pos.x () - 1, -pos.y ()); |
| } |
| pos += step; |
| } |
| |
| for (out_it.mark_cycle_pt (); !out_it.cycled_list (); out_it.forward ()) { |
| vertical_coutline_projection (out_it.data (), stats); |
| } |
| } |
| |
| |
| /********************************************************************** |
| * TO_BLOCK::TO_BLOCK |
| * |
| * Constructor to make a TO_BLOCK from a real block. |
| **********************************************************************/ |
| |
| TO_BLOCK::TO_BLOCK( //make a block |
| BLOCK *src_block //real block |
| ) { |
| block = src_block; |
| } |
| |
| static void clear_blobnboxes(BLOBNBOX_LIST* boxes) { |
| BLOBNBOX_IT it = boxes; |
| // A BLOBNBOX generally doesn't own its blobs, so if they do, you |
| // have to delete them explicitly. |
| for (it.mark_cycle_pt(); !it.cycled_list(); it.forward()) { |
| BLOBNBOX* box = it.data(); |
| if (box->blob() != NULL) |
| delete box->blob(); |
| if (box->cblob() != NULL) |
| delete box->cblob(); |
| } |
| } |
| |
| TO_BLOCK::~TO_BLOCK() { |
| // Any residual BLOBNBOXes at this stage own their blobs, so delete them. |
| clear_blobnboxes(&blobs); |
| clear_blobnboxes(&underlines); |
| clear_blobnboxes(&noise_blobs); |
| clear_blobnboxes(&small_blobs); |
| clear_blobnboxes(&large_blobs); |
| } |
| |