/////////////////////////////////////////////////////////////////////// // File: bbgrid.h // Description: Class to hold BLOBNBOXs in a grid for fast access // to neighbours. // Author: Ray Smith // Created: Wed Jun 06 17:22:01 PDT 2007 // // (C) Copyright 2007, Google Inc. // 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. // /////////////////////////////////////////////////////////////////////// #ifndef TESSERACT_TEXTORD_BBGRID_H__ #define TESSERACT_TEXTORD_BBGRID_H__ #include "clst.h" #include "coutln.h" #include "rect.h" #include "scrollview.h" // Some code is dependent upon leptonica. If you don't have it, // you don't get this functionality. #ifdef HAVE_CONFIG_H #include "config_auto.h" #endif #ifdef HAVE_LIBLEPT #include "allheaders.h" #endif class BLOCK; namespace tesseract { #ifdef HAVE_LIBLEPT // Helper function to return a scaled Pix with one pixel per grid cell, // set (black) where the given outline enters the corresponding grid cell, // and clear where the outline does not touch the grid cell. // Also returns the grid coords of the bottom-left of the Pix, in *left // and *bottom, which corresponds to (0, 0) on the Pix. // Note that the Pix is used upside-down, with (0, 0) being the bottom-left. Pix* TraceOutlineOnReducedPix(C_OUTLINE* outline, int gridsize, ICOORD bleft, int* left, int* bottom); // As TraceOutlineOnReducedPix above, but on a BLOCK instead of a C_OUTLINE. Pix* TraceBlockOnReducedPix(BLOCK* block, int gridsize, ICOORD bleft, int* left, int* bottom); #endif template class GridSearch; // The BBGrid class holds C_LISTs of template classes BBC (bounding box class) // in a grid for fast neighbour access. // The BBC class must have a member const TBOX& bounding_box() const. // The BBC class must have been CLISTIZEH'ed elsewhere to make the // list class BBC_CLIST and the iterator BBC_C_IT. // Use of C_LISTs enables BBCs to exist in multiple cells simultaneously. // As a consequence, ownership of BBCs is assumed to be elsewhere and // persistent for at least the life of the BBGrid, or at least until Clear is // called which removes all references to inserted objects without actually // deleting them. // Most uses derive a class from a specific instantiation of BBGrid, // thereby making most of the ugly template notation go away. // The friend class GridSearch, with the same template arguments, is // used to search a grid efficiently in one of several search patterns. template class BBGrid { friend class GridSearch; public: BBGrid(); BBGrid(int gridsize, const ICOORD& bleft, const ICOORD& tright); virtual ~BBGrid(); // (Re)Initialize the grid. The gridsize is the size in pixels of each cell, // and bleft, tright are the bounding box of everything to go in it. void Init(int gridsize, const ICOORD& bleft, const ICOORD& tright); // Empty all the lists but leave the grid itself intact. void Clear(); // Deallocate the data in the lists but otherwise leave the lists and the grid // intact. void ClearGridData(void (*free_method)(BBC*)); // Simple accessors. int gridsize() const { return gridsize_; } int gridwidth() const { return gridwidth_; } int gridheight() const { return gridheight_; } ICOORD bleft() const { return bleft_; } ICOORD tright() const { return tright_; } // Insert a bbox into the appropriate place in the grid. // If h_spread, then all cells covered horizontally by the box are // used, otherwise, just the bottom-left. Similarly for v_spread. // WARNING: InsertBBox may invalidate an active GridSearch. Call // RepositionIterator() on any GridSearches that are active on this grid. void InsertBBox(bool h_spread, bool v_spread, BBC* bbox); #ifdef HAVE_LIBLEPT // Using a pix from TraceOutlineOnReducedPix or TraceBlockOnReducedPix, in // which each pixel corresponds to a grid cell, insert a bbox into every // place in the grid where the corresponding pixel is 1. The Pix is handled // upside-down to match the Tesseract coordinate system. (As created by // TraceOutlineOnReducedPix or TraceBlockOnReducedPix.) // (0, 0) in the pix corresponds to (left, bottom) in the // grid (in grid coords), and the pix works up the grid from there. // WARNING: InsertPixPtBBox may invalidate an active GridSearch. Call // RepositionIterator() on any GridSearches that are active on this grid. void InsertPixPtBBox(int left, int bottom, Pix* pix, BBC* bbox); #endif // Remove the bbox from the grid. // WARNING: Any GridSearch operating on this grid could be invalidated! // If a GridSearch is operating, call GridSearch::RemoveBBox() instead. void RemoveBBox(BBC* bbox); // Compute the given grid coordinates from image coords. void GridCoords(int x, int y, int* grid_x, int* grid_y); // Clip the given grid coordinates to fit within the grid. void ClipGridCoords(int* x, int* y); // Make a window of an appropriate size to display things in the grid. ScrollView* MakeWindow(int x, int y, const char* window_name); // Display the bounding boxes of the BLOBNBOXes in this grid. // Use of this function requires an additional member of the BBC class: // ScrollView::Color BBC::BoxColor() const. void DisplayBoxes(ScrollView* window); // ASSERT_HOST that every cell contains no more than one copy of each entry. void AssertNoDuplicates(); // Handle a click event in a display window. virtual void HandleClick(int x, int y); protected: int gridsize_; // Pixel size of each grid cell. int gridwidth_; // Size of the grid in cells. int gridheight_; int gridbuckets_; // Total cells in grid. ICOORD bleft_; // Pixel coords of bottom-left of grid. ICOORD tright_; // Pixel coords of top-right of grid. BBC_CLIST* grid_; // 2-d array of CLISTS of BBC elements. private: }; // The GridSearch class enables neighbourhood searching on a BBGrid. template class GridSearch { public: GridSearch(BBGrid* grid) : grid_(grid), previous_return_(NULL), next_return_(NULL) { } // Get the grid x, y coords of the most recently returned BBC. int GridX() const { return x_; } int GridY() const { return y_; } // Apart from full search, all other searches return a box several // times if the box is inserted with h_spread or v_spread. // This method will return true for only one occurrance of each box // that was inserted with both h_spread and v_spread as true. // It will usually return false for boxes that were not inserted with // both h_spread=true and v_spread=true bool ReturnedSeedElement() const { TBOX box = previous_return_->bounding_box(); int x_center = (box.left()+box.right())/2; int y_center = (box.top()+box.bottom())/2; int grid_x, grid_y; grid_->GridCoords(x_center, y_center, &grid_x, &grid_y); return (x_ == grid_x) && (y_ == grid_y); } // Various searching iterations... Note that these iterations // all share data members, so you can't run more than one iteration // in parallel in a single GridSearch instance, but multiple instances // can search the same BBGrid in parallel. // Note that all the searches can return blobs that may not exactly // match the search conditions, since they return everything in the // covered grid cells. It is up to the caller to check for // appropriateness. // Start a new full search. Will iterate all stored blobs, from the top. // If the blobs have been inserted using InsertBBox, (not InsertPixPtBBox) // then the full search guarantees to return each blob in the grid once. // Other searches may return a blob more than once if they have been // inserted using h_spread or v_spread. void StartFullSearch(); // Return the next bbox in the search or NULL if done. BBC* NextFullSearch(); // Start a new radius search. Will search in a spiral upto a // given maximum radius in grid cells from the given center in pixels. void StartRadSearch(int x, int y, int max_radius); // Return the next bbox in the radius search or NULL if the // maximum radius has been reached. BBC* NextRadSearch(); // Start a new left or right-looking search. Will search to the side // for a box that vertically overlaps the given vertical line segment. // CAVEAT: This search returns all blobs from the cells to the side // of the start, and somewhat below, since there is no guarantee // that there may not be a taller object in a lower cell. The // blobs returned will include all those that vertically overlap and // are no more than twice as high, but may also include some that do // not overlap and some that are more than twice as high. void StartSideSearch(int x, int ymin, int ymax); // Return the next bbox in the side search or NULL if the // edge has been reached. Searches left to right or right to left // according to the flag. BBC* NextSideSearch(bool right_to_left); // Start a vertical-looking search. Will search up or down // for a box that horizontally overlaps the given line segment. void StartVerticalSearch(int xmin, int xmax, int y); // Return the next bbox in the vertical search or NULL if the // edge has been reached. Searches top to bottom or bottom to top // according to the flag. BBC* NextVerticalSearch(bool top_to_bottom); // Start a rectangular search. Will search for a box that overlaps the // given rectangle. void StartRectSearch(const TBOX& rect); // Return the next bbox in the rectangular search or NULL if complete. BBC* NextRectSearch(); // Remove the last returned BBC. Will not invalidate this. May invalidate // any other concurrent GridSearch on the same grid. If any others are // in use, call RepositionIterator on those, to continue without harm. void RemoveBBox(); void RepositionIterator(); private: // Factored out helper to start a search. void CommonStart(int x, int y); // Factored out helper to complete a next search. BBC* CommonNext(); // Factored out final return when search is exhausted. BBC* CommonEnd(); // Factored out function to set the iterator to the current x_, y_ // grid coords and mark the cycle pt. void SetIterator(); private: // The grid we are searching. BBGrid* grid_; // For executing a search. The different search algorithms use these in // different ways, but most use x_origin_ and y_origin_ as the start position. int x_origin_; int y_origin_; int max_radius_; int radius_; int rad_index_; int rad_dir_; TBOX rect_; int x_; // The current location in grid coords, of the current search. int y_; BBC* previous_return_; // Previous return from Next*. BBC* next_return_; // Current value of it_.data() used for repositioning. // An iterator over the list at (x_, y_) in the grid_. BBC_C_IT it_; }; // Sort function to sort a BBC by bounding_box().left(). template int SortByBoxLeft(const void* void1, const void* void2) { // The void*s are actually doubly indirected, so get rid of one level. const BBC* p1 = *reinterpret_cast(void1); const BBC* p2 = *reinterpret_cast(void2); return p1->bounding_box().left() - p2->bounding_box().left(); } /////////////////////////////////////////////////////////////////////// // BBGrid IMPLEMENTATION. /////////////////////////////////////////////////////////////////////// template BBGrid::BBGrid() : grid_(NULL) { } template BBGrid::BBGrid( int gridsize, const ICOORD& bleft, const ICOORD& tright) : grid_(NULL) { Init(gridsize, bleft, tright); } template BBGrid::~BBGrid() { if (grid_ != NULL) delete [] grid_; } // (Re)Initialize the grid. The gridsize is the size in pixels of each cell, // and bleft, tright are the bounding box of everything to go in it. template void BBGrid::Init(int gridsize, const ICOORD& bleft, const ICOORD& tright) { gridsize_ = gridsize; bleft_ = bleft; tright_ = tright; if (grid_ != NULL) delete [] grid_; if (gridsize_ == 0) gridsize_ = 1; gridwidth_ = (tright.x() - bleft.x() + gridsize_ - 1) / gridsize_; gridheight_ = (tright.y() - bleft.y() + gridsize_ - 1) / gridsize_; gridbuckets_ = gridwidth_ * gridheight_; grid_ = new BBC_CLIST[gridbuckets_]; } // Clear all lists, but leave the array of lists present. template void BBGrid::Clear() { for (int i = 0; i < gridbuckets_; ++i) { grid_[i].shallow_clear(); } } // Deallocate the data in the lists but otherwise leave the lists and the grid // intact. template void BBGrid::ClearGridData( void (*free_method)(BBC*)) { if (grid_ == NULL) return; GridSearch search(this); search.StartFullSearch(); BBC* bb; BBC_CLIST bb_list; BBC_C_IT it(&bb_list); while ((bb = search.NextFullSearch()) != NULL) { it.add_after_then_move(bb); } for (it.mark_cycle_pt(); !it.cycled_list(); it.forward()) { free_method(it.data()); } } // Insert a bbox into the appropriate place in the grid. // If h_spread, then all cells covered horizontally by the box are // used, otherwise, just the bottom-left. Similarly for v_spread. // WARNING: InsertBBox may invalidate an active GridSearch. Call // RepositionIterator() on any GridSearches that are active on this grid. template void BBGrid::InsertBBox(bool h_spread, bool v_spread, BBC* bbox) { TBOX box = bbox->bounding_box(); int start_x, start_y, end_x, end_y; GridCoords(box.left(), box.bottom(), &start_x, &start_y); GridCoords(box.right(), box.top(), &end_x, &end_y); if (!h_spread) end_x = start_x; if (!v_spread) end_y = start_y; int grid_index = start_y * gridwidth_; for (int y = start_y; y <= end_y; ++y, grid_index += gridwidth_) { for (int x = start_x; x <= end_x; ++x) { grid_[grid_index + x].add_sorted(SortByBoxLeft, true, bbox); } } } #ifdef HAVE_LIBLEPT // Using a pix from TraceOutlineOnReducedPix or TraceBlockOnReducedPix, in // which each pixel corresponds to a grid cell, insert a bbox into every // place in the grid where the corresponding pixel is 1. The Pix is handled // upside-down to match the Tesseract coordinate system. (As created by // TraceOutlineOnReducedPix or TraceBlockOnReducedPix.) // (0, 0) in the pix corresponds to (left, bottom) in the // grid (in grid coords), and the pix works up the grid from there. // WARNING: InsertPixPtBBox may invalidate an active GridSearch. Call // RepositionIterator() on any GridSearches that are active on this grid. template void BBGrid::InsertPixPtBBox(int left, int bottom, Pix* pix, BBC* bbox) { int width = pixGetWidth(pix); int height = pixGetHeight(pix); for (int y = 0; y < height; ++y) { l_uint32* data = pixGetData(pix) + y * pixGetWpl(pix); for (int x = 0; x < width; ++x) { if (GET_DATA_BIT(data, x)) { grid_[(bottom + y) * gridwidth_ + x + left]. add_sorted(SortByBoxLeft, true, bbox); } } } } #endif // Remove the bbox from the grid. // WARNING: Any GridSearch operating on this grid could be invalidated! // If a GridSearch is operating, call GridSearch::RemoveBBox() instead. template void BBGrid::RemoveBBox(BBC* bbox) { TBOX box = bbox->bounding_box(); int start_x, start_y, end_x, end_y; GridCoords(box.left(), box.bottom(), &start_x, &start_y); GridCoords(box.right(), box.top(), &end_x, &end_y); int grid_index = start_y * gridwidth_; for (int y = start_y; y <= end_y; ++y, grid_index += gridwidth_) { for (int x = start_x; x <= end_x; ++x) { BBC_C_IT it(&grid_[grid_index + x]); for (it.mark_cycle_pt(); !it.cycled_list(); it.forward()) { if (it.data() == bbox) it.extract(); } } } } // Compute the given grid coordinates from image coords. template void BBGrid::GridCoords(int x, int y, int* grid_x, int* grid_y) { *grid_x = (x - bleft_.x()) / gridsize_; *grid_y = (y - bleft_.y()) / gridsize_; ClipGridCoords(grid_x, grid_y); } // Clip the given grid coordinates to fit within the grid. template void BBGrid::ClipGridCoords(int* x, int* y) { if (*x < 0) *x = 0; if (*x >= gridwidth_) *x = gridwidth_ - 1; if (*y < 0) *y = 0; if (*y >= gridheight_) *y = gridheight_ - 1; } template class TabEventHandler : public SVEventHandler { public: explicit TabEventHandler(G* grid) : grid_(grid) { } void Notify(const SVEvent* sv_event) { if (sv_event->type == SVET_CLICK) { grid_->HandleClick(sv_event->x, sv_event->y); } } private: G* grid_; }; // Make a window of an appropriate size to display things in the grid. // Position the window at the given x,y. template ScrollView* BBGrid::MakeWindow( int x, int y, const char* window_name) { ScrollView* tab_win = NULL; #ifndef GRAPHICS_DISABLED tab_win = new ScrollView(window_name, x, y, tright_.x() - bleft_.x(), tright_.y() - bleft_.y(), tright_.x() - bleft_.x(), tright_.y() - bleft_.y(), true); TabEventHandler >* handler = new TabEventHandler >(this); tab_win->AddEventHandler(handler); tab_win->Pen(ScrollView::GREY); tab_win->Rectangle(0, 0, tright_.x(), tright_.y()); #endif return tab_win; } // Create a window at (x,y) and display the bounding boxes of the // BLOBNBOXes in this grid. // Use of this function requires an additional member of the BBC class: // ScrollView::Color BBC::BoxColor() const. template void BBGrid::DisplayBoxes(ScrollView* tab_win) { #ifndef GRAPHICS_DISABLED tab_win->Pen(ScrollView::BLUE); tab_win->Brush(ScrollView::NONE); // For every bbox in the grid, display it. GridSearch gsearch(this); gsearch.StartFullSearch(); BBC* bbox; while ((bbox = gsearch.NextFullSearch()) != NULL) { TBOX box = bbox->bounding_box(); int left_x = box.left(); int right_x = box.right(); int top_y = box.top(); int bottom_y = box.bottom(); ScrollView::Color box_color = bbox->BoxColor(); tab_win->Pen(box_color); tab_win->Rectangle(left_x, bottom_y, right_x, top_y); } tab_win->Update(); #endif } // ASSERT_HOST that every cell contains no more than one copy of each entry. template void BBGrid::AssertNoDuplicates() { // Process all grid cells. for (int i = gridwidth_ * gridheight_ - 1; i >= 0; --i) { // Iterate over all elements excent the last. for (BBC_C_IT it(&grid_[i]); !it.at_last(); it.forward()) { BBC* ptr = it.data(); BBC_C_IT it2(it); // None of the rest of the elements in the list should equal ptr. for (it2.forward(); !it2.at_first(); it2.forward()) { ASSERT_HOST(it2.data() != ptr); } } } } // Handle a click event in a display window. template void BBGrid::HandleClick(int x, int y) { tprintf("Click at (%d, %d)\n", x, y); } /////////////////////////////////////////////////////////////////////// // GridSearch IMPLEMENTATION. /////////////////////////////////////////////////////////////////////// // Start a new full search. Will iterate all stored blobs. template void GridSearch::StartFullSearch() { // Full search uses x_ and y_ as the current grid // cell being searched. CommonStart(grid_->bleft_.x(), grid_->tright_.y()); } // Return the next bbox in the search or NULL if done. // The other searches will return a box that overlaps the grid cell // thereby duplicating boxes, but NextFullSearch only returns each box once. template BBC* GridSearch::NextFullSearch() { int x; int y; do { while (it_.cycled_list()) { ++x_; if (x_ >= grid_->gridwidth_) { --y_; if (y_ < 0) return CommonEnd(); x_ = 0; } SetIterator(); } CommonNext(); TBOX box = previous_return_->bounding_box(); grid_->GridCoords(box.left(), box.bottom(), &x, &y); } while (x != x_ || y != y_); return previous_return_; } // Start a new radius search. template void GridSearch::StartRadSearch(int x, int y, int max_radius) { // Rad search uses x_origin_ and y_origin_ as the center of the circle. // The radius_ is the radius of the (diamond-shaped) circle and // rad_index/rad_dir_ combine to determine the position around it. max_radius_ = max_radius; radius_ = 0; rad_index_ = 0; rad_dir_ = 3; CommonStart(x, y); } // Return the next bbox in the radius search or NULL if the // maximum radius has been reached. template BBC* GridSearch::NextRadSearch() { while (it_.cycled_list()) { ++rad_index_; if (rad_index_ >= radius_) { ++rad_dir_; rad_index_ = 0; if (rad_dir_ >= 4) { ++radius_; if (radius_ > max_radius_) return CommonEnd(); rad_dir_ = 0; } } ICOORD offset = C_OUTLINE::chain_step(rad_dir_); offset *= radius_ - rad_index_; offset += C_OUTLINE::chain_step(rad_dir_ + 1) * rad_index_; x_ = x_origin_ + offset.x(); y_ = y_origin_ + offset.y(); if (x_ >= 0 && x_ < grid_->gridwidth_ && y_ >= 0 && y_ < grid_->gridheight_) SetIterator(); } return CommonNext(); } // Start a new left or right-looking search. Will search to the side // for a box that vertically overlaps the given vertical line segment. template void GridSearch::StartSideSearch(int x, int ymin, int ymax) { // Right search records the x in x_origin_, the ymax in y_origin_ // and the size of the vertical strip to search in radius_. // To guarantee finding overlapping objects of upto twice the // given size, double the height. radius_ = ((ymax - ymin) * 2 + grid_->gridsize_ - 1) / grid_->gridsize_; rad_index_ = 0; CommonStart(x, ymax); } // Return the next bbox in the side search or NULL if the // edge has been reached. Searches left to right or right to left // according to the flag. template BBC* GridSearch::NextSideSearch(bool right_to_left) { while (it_.cycled_list()) { ++rad_index_; if (rad_index_ > radius_) { if (right_to_left) --x_; else ++x_; rad_index_ = 0; if (x_ < 0 || x_ >= grid_->gridwidth_) return CommonEnd(); } y_ = y_origin_ - rad_index_; if (y_ >= 0 && y_ < grid_->gridheight_) SetIterator(); } return CommonNext(); } // Start a vertical-looking search. Will search up or down // for a box that horizontally overlaps the given line segment. template void GridSearch::StartVerticalSearch(int xmin, int xmax, int y) { // Right search records the xmin in x_origin_, the y in y_origin_ // and the size of the horizontal strip to search in radius_. radius_ = (xmax - xmin + grid_->gridsize_ - 1) / grid_->gridsize_; rad_index_ = 0; CommonStart(xmin, y); } // Return the next bbox in the vertical search or NULL if the // edge has been reached. Searches top to bottom or bottom to top // according to the flag. template BBC* GridSearch::NextVerticalSearch( bool top_to_bottom) { while (it_.cycled_list()) { ++rad_index_; if (rad_index_ > radius_) { if (top_to_bottom) --y_; else ++y_; rad_index_ = 0; if (y_ < 0 || y_ >= grid_->gridheight_) return CommonEnd(); } x_ = x_origin_ + rad_index_; if (x_ >= 0 && x_ < grid_->gridwidth_) SetIterator(); } return CommonNext(); } // Start a rectangular search. Will search for a box that overlaps the // given rectangle. template void GridSearch::StartRectSearch(const TBOX& rect) { // Rect search records the xmin in x_origin_, the ymin in y_origin_ // and the xmax in max_radius_. // The search proceeds left to right, top to bottom. rect_ = rect; CommonStart(rect.left(), rect.top()); grid_->GridCoords(rect.right(), rect.bottom(), // - rect.height(), &max_radius_, &y_origin_); } // Return the next bbox in the rectangular search or NULL if complete. template BBC* GridSearch::NextRectSearch() { while (it_.cycled_list()) { ++x_; if (x_ > max_radius_) { --y_; x_ = x_origin_; if (y_ < y_origin_) return CommonEnd(); } SetIterator(); } return CommonNext(); } // Remove the last returned BBC. Will not invalidate this. May invalidate // any other concurrent GridSearch on the same grid. If any others are // in use, call RepositionIterator on those, to continue without harm. template void GridSearch::RemoveBBox() { if (previous_return_ != NULL) { // Remove all instances of previous_return_ from the list, so the iterator // remains valid after removal from the rest of the grid cells. // if previous_return_ is not on the list, then it has been removed already. BBC* prev_data = NULL; BBC* new_previous_return = NULL; it_.move_to_first(); for (it_.mark_cycle_pt(); !it_.cycled_list();) { if (it_.data() == previous_return_) { new_previous_return = prev_data; it_.extract(); it_.forward(); next_return_ = it_.cycled_list() ? NULL : it_.data(); } else { prev_data = it_.data(); it_.forward(); } } grid_->RemoveBBox(previous_return_); previous_return_ = new_previous_return; RepositionIterator(); } } template void GridSearch::RepositionIterator() { // Reset the iterator back to one past the previous return. // If the previous_return_ is no longer in the list, then // next_return_ serves as a backup. it_.move_to_first(); for (it_.mark_cycle_pt(); !it_.cycled_list(); it_.forward()) { if (it_.data() == previous_return_ || it_.data_relative(1) == next_return_) { CommonNext(); return; } } // We ran off the end of the list. Move to a new cell next time. previous_return_ = NULL; next_return_ = NULL; } // Factored out helper to start a search. template void GridSearch::CommonStart(int x, int y) { grid_->GridCoords(x, y, &x_origin_, &y_origin_); x_ = x_origin_; y_ = y_origin_; SetIterator(); previous_return_ = NULL; next_return_ = it_.empty() ? NULL : it_.data(); } // Factored out helper to complete a next search. template BBC* GridSearch::CommonNext() { previous_return_ = it_.data(); it_.forward(); next_return_ = it_.cycled_list() ? NULL : it_.data(); return previous_return_; } // Factored out final return when search is exhausted. template BBC* GridSearch::CommonEnd() { previous_return_ = NULL; next_return_ = NULL; return NULL; } // Factored out function to set the iterator to the current x_, y_ // grid coords and mark the cycle pt. template void GridSearch::SetIterator() { it_= &(grid_->grid_[y_ * grid_->gridwidth_ + x_]); it_.mark_cycle_pt(); } } // namespace tesseract. #endif // TESSERACT_TEXTORD_BBGRID_H__