tesseract  5.0.0
pdblock.cpp
Go to the documentation of this file.
1 /**********************************************************************
2  * File: pdblock.cpp
3  * Description: PDBLK member functions and iterator functions.
4  * Author: Ray Smith
5  *
6  * (C) Copyright 1991, Hewlett-Packard Ltd.
7  ** Licensed under the Apache License, Version 2.0 (the "License");
8  ** you may not use this file except in compliance with the License.
9  ** You may obtain a copy of the License at
10  ** http://www.apache.org/licenses/LICENSE-2.0
11  ** Unless required by applicable law or agreed to in writing, software
12  ** distributed under the License is distributed on an "AS IS" BASIS,
13  ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  ** See the License for the specific language governing permissions and
15  ** limitations under the License.
16  *
17  **********************************************************************/
18 
19 // Include automatically generated configuration file if running autoconf.
20 #ifdef HAVE_CONFIG_H
21 # include "config_auto.h"
22 #endif
23 
24 #include "pdblock.h"
25 
26 #include <allheaders.h>
27 
28 #include <cinttypes> // for PRId32
29 #include <cstdlib>
30 #include <memory> // std::unique_ptr
31 
32 namespace tesseract {
33 
34 #define BLOCK_LABEL_HEIGHT 150 // char height of block id
35 
36 constexpr ERRCODE BADBLOCKLINE("Y coordinate in block out of bounds");
37 constexpr ERRCODE LOSTBLOCKLINE("Can't find rectangle for line");
38 
39 /**********************************************************************
40  * PDBLK::PDBLK
41  *
42  * Constructor for a simple rectangular block.
43  **********************************************************************/
44 PDBLK::PDBLK( // rectangular block
45  TDimension xmin, // bottom left
46  TDimension ymin,
47  TDimension xmax, // top right
48  TDimension ymax)
49  : box(ICOORD(xmin, ymin), ICOORD(xmax, ymax)) {
50  // boundaries
51  ICOORDELT_IT left_it = &leftside;
52  ICOORDELT_IT right_it = &rightside;
53 
54  hand_poly = nullptr;
55  left_it.set_to_list(&leftside);
56  right_it.set_to_list(&rightside);
57  // make default box
58  left_it.add_to_end(new ICOORDELT(xmin, ymin));
59  left_it.add_to_end(new ICOORDELT(xmin, ymax));
60  right_it.add_to_end(new ICOORDELT(xmax, ymin));
61  right_it.add_to_end(new ICOORDELT(xmax, ymax));
62  index_ = 0;
63 }
64 
65 /**********************************************************************
66  * PDBLK::set_sides
67  *
68  * Sets left and right vertex lists
69  **********************************************************************/
70 
71 void PDBLK::set_sides( // set vertex lists
72  ICOORDELT_LIST *left, // left vertices
73  ICOORDELT_LIST *right // right vertices
74 ) {
75  // boundaries
76  ICOORDELT_IT left_it = &leftside;
77  ICOORDELT_IT right_it = &rightside;
78 
79  leftside.clear();
80  left_it.move_to_first();
81  left_it.add_list_before(left);
82  rightside.clear();
83  right_it.move_to_first();
84  right_it.add_list_before(right);
85 }
86 
87 /**********************************************************************
88  * PDBLK::contains
89  *
90  * Return true if the given point is within the block.
91  **********************************************************************/
92 
93 bool PDBLK::contains( // test containment
94  ICOORD pt // point to test
95 ) {
96  BLOCK_RECT_IT it = this; // rectangle iterator
97  ICOORD bleft, tright; // corners of rectangle
98 
99  for (it.start_block(); !it.cycled_rects(); it.forward()) {
100  // get rectangle
101  it.bounding_box(bleft, tright);
102  // inside rect
103  if (pt.x() >= bleft.x() && pt.x() <= tright.x() && pt.y() >= bleft.y() &&
104  pt.y() <= tright.y()) {
105  return true; // is inside
106  }
107  }
108  return false; // not inside
109 }
110 
111 /**********************************************************************
112  * PDBLK::move
113  *
114  * Reposition block
115  **********************************************************************/
116 
117 void PDBLK::move( // reposition block
118  const ICOORD vec // by vector
119 ) {
120  ICOORDELT_IT it(&leftside);
121 
122  for (it.mark_cycle_pt(); !it.cycled_list(); it.forward()) {
123  *(it.data()) += vec;
124  }
125 
126  it.set_to_list(&rightside);
127 
128  for (it.mark_cycle_pt(); !it.cycled_list(); it.forward()) {
129  *(it.data()) += vec;
130  }
131 
132  box.move(vec);
133 }
134 
135 // Returns a binary Pix mask with a 1 pixel for every pixel within the
136 // block. Rotates the coordinate system by rerotation prior to rendering.
137 Image PDBLK::render_mask(const FCOORD &rerotation, TBOX *mask_box) {
138  TBOX rotated_box(box);
139  rotated_box.rotate(rerotation);
140  Image pix = pixCreate(rotated_box.width(), rotated_box.height(), 1);
141  if (hand_poly != nullptr) {
142  // We are going to rotate, so get a deep copy of the points and
143  // make a new POLY_BLOCK with it.
144  ICOORDELT_LIST polygon;
145  polygon.deep_copy(hand_poly->points(), ICOORDELT::deep_copy);
146  POLY_BLOCK image_block(&polygon, hand_poly->isA());
147  image_block.rotate(rerotation);
148  // Block outline is a polygon, so use a PB_LINE_IT to get the
149  // rasterized interior. (Runs of interior pixels on a line.)
150  auto *lines = new PB_LINE_IT(&image_block);
151  for (int y = box.bottom(); y < box.top(); ++y) {
152  const std::unique_ptr</*non-const*/ ICOORDELT_LIST> segments(lines->get_line(y));
153  if (!segments->empty()) {
154  ICOORDELT_IT s_it(segments.get());
155  // Each element of segments is a start x and x size of the
156  // run of interior pixels.
157  for (s_it.mark_cycle_pt(); !s_it.cycled_list(); s_it.forward()) {
158  int start = s_it.data()->x();
159  int xext = s_it.data()->y();
160  // Set the run of pixels to 1.
161  pixRasterop(pix, start - rotated_box.left(),
162  rotated_box.height() - 1 - (y - rotated_box.bottom()), xext, 1, PIX_SET,
163  nullptr, 0, 0);
164  }
165  }
166  }
167  delete lines;
168  } else {
169  // Just fill the whole block as there is only a bounding box.
170  pixRasterop(pix, 0, 0, rotated_box.width(), rotated_box.height(), PIX_SET, nullptr, 0, 0);
171  }
172  if (mask_box != nullptr) {
173  *mask_box = rotated_box;
174  }
175  return pix;
176 }
177 
178 /**********************************************************************
179  * PDBLK::plot
180  *
181  * Plot the outline of a block in the given colour.
182  **********************************************************************/
183 
184 #ifndef GRAPHICS_DISABLED
185 void PDBLK::plot( // draw outline
186  ScrollView *window, // window to draw in
187  int32_t serial, // serial number
188  ScrollView::Color colour // colour to draw in
189 ) {
190  ICOORD startpt; // start of outline
191  ICOORD endpt; // end of outline
192  ICOORD prevpt; // previous point
193  ICOORDELT_IT it = &leftside; // iterator
194 
195  // set the colour
196  window->Pen(colour);
197  window->TextAttributes("Times", BLOCK_LABEL_HEIGHT, false, false, false);
198 
199  if (hand_poly != nullptr) {
200  hand_poly->plot(window, serial);
201  } else if (!leftside.empty()) {
202  startpt = *(it.data()); // bottom left corner
203  // tprintf("Block %d bottom left is (%d,%d)\n",
204  // serial,startpt.x(),startpt.y());
205  char temp_buff[34];
206 # if !defined(_WIN32) || defined(__MINGW32__)
207  snprintf(temp_buff, sizeof(temp_buff), "%" PRId32, serial);
208 # else
209  _ultoa(serial, temp_buff, 10);
210 # endif
211  window->Text(startpt.x(), startpt.y(), temp_buff);
212 
213  window->SetCursor(startpt.x(), startpt.y());
214  do {
215  prevpt = *(it.data()); // previous point
216  it.forward(); // move to next point
217  // draw round corner
218  window->DrawTo(prevpt.x(), it.data()->y());
219  window->DrawTo(it.data()->x(), it.data()->y());
220  } while (!it.at_last()); // until end of list
221  endpt = *(it.data()); // end point
222 
223  // other side of boundary
224  window->SetCursor(startpt.x(), startpt.y());
225  it.set_to_list(&rightside);
226  prevpt = startpt;
227  for (it.mark_cycle_pt(); !it.cycled_list(); it.forward()) {
228  // draw round corner
229  window->DrawTo(prevpt.x(), it.data()->y());
230  window->DrawTo(it.data()->x(), it.data()->y());
231  prevpt = *(it.data()); // previous point
232  }
233  // close boundary
234  window->DrawTo(endpt.x(), endpt.y());
235  }
236 }
237 #endif
238 
239 /**********************************************************************
240  * PDBLK::operator=
241  *
242  * Assignment - duplicate the block structure, but with an EMPTY row list.
243  **********************************************************************/
244 
245 PDBLK &PDBLK::operator=( // assignment
246  const PDBLK &source // from this
247 ) {
248  // this->ELIST_LINK::operator=(source);
249  if (!leftside.empty()) {
250  leftside.clear();
251  }
252  if (!rightside.empty()) {
253  rightside.clear();
254  }
255  leftside.deep_copy(&source.leftside, &ICOORDELT::deep_copy);
256  rightside.deep_copy(&source.rightside, &ICOORDELT::deep_copy);
257  box = source.box;
258  return *this;
259 }
260 
261 /**********************************************************************
262  * BLOCK_RECT_IT::BLOCK_RECT_IT
263  *
264  * Construct a block rectangle iterator.
265  **********************************************************************/
266 
268  // iterate rectangles
269  PDBLK *blkptr // from block
270  )
271  : left_it(&blkptr->leftside), right_it(&blkptr->rightside) {
272  block = blkptr; // remember block
273  // non empty list
274  if (!blkptr->leftside.empty()) {
275  start_block(); // ready for iteration
276  }
277 }
278 
279 /**********************************************************************
280  * BLOCK_RECT_IT::set_to_block
281  *
282  * Start a new block.
283  **********************************************************************/
284 
285 void BLOCK_RECT_IT::set_to_block( // start (new) block
286  PDBLK *blkptr) { // block to start
287  block = blkptr; // remember block
288  // set iterators
289  left_it.set_to_list(&blkptr->leftside);
290  right_it.set_to_list(&blkptr->rightside);
291  if (!blkptr->leftside.empty()) {
292  start_block(); // ready for iteration
293  }
294 }
295 
296 /**********************************************************************
297  * BLOCK_RECT_IT::start_block
298  *
299  * Restart a block.
300  **********************************************************************/
301 
302 void BLOCK_RECT_IT::start_block() { // start (new) block
303  left_it.move_to_first();
304  right_it.move_to_first();
305  left_it.mark_cycle_pt();
306  right_it.mark_cycle_pt();
307  ymin = left_it.data()->y(); // bottom of first box
308  ymax = left_it.data_relative(1)->y();
309  if (right_it.data_relative(1)->y() < ymax) {
310  // smallest step
311  ymax = right_it.data_relative(1)->y();
312  }
313 }
314 
315 /**********************************************************************
316  * BLOCK_RECT_IT::forward
317  *
318  * Move to the next rectangle in the block.
319  **********************************************************************/
320 
321 void BLOCK_RECT_IT::forward() { // next rectangle
322  if (!left_it.empty()) { // non-empty list
323  if (left_it.data_relative(1)->y() == ymax) {
324  left_it.forward(); // move to meet top
325  }
326  if (right_it.data_relative(1)->y() == ymax) {
327  right_it.forward();
328  }
329  // last is special
330  if (left_it.at_last() || right_it.at_last()) {
331  left_it.move_to_first(); // restart
332  right_it.move_to_first();
333  // now at bottom
334  ymin = left_it.data()->y();
335  } else {
336  ymin = ymax; // new bottom
337  }
338  // next point
339  ymax = left_it.data_relative(1)->y();
340  if (right_it.data_relative(1)->y() < ymax) {
341  // least step forward
342  ymax = right_it.data_relative(1)->y();
343  }
344  }
345 }
346 
347 /**********************************************************************
348  * BLOCK_LINE_IT::get_line
349  *
350  * Get the the start and width of a line in the block.
351  **********************************************************************/
352 
354  TDimension y, // line to get
355  TDimension &xext // output extent
356 ) {
357  ICOORD bleft; // bounding box
358  ICOORD tright; // of block & rect
359 
360  // get block box
361  block->bounding_box(bleft, tright);
362  if (y < bleft.y() || y >= tright.y()) {
363  // block->print(stderr,false);
364  BADBLOCKLINE.error("BLOCK_LINE_IT::get_line", ABORT, "Y=%d", y);
365  }
366 
367  // get rectangle box
368  rect_it.bounding_box(bleft, tright);
369  // inside rectangle
370  if (y >= bleft.y() && y < tright.y()) {
371  // width of line
372  xext = tright.x() - bleft.x();
373  return bleft.x(); // start of line
374  }
375  for (rect_it.start_block(); !rect_it.cycled_rects(); rect_it.forward()) {
376  // get rectangle box
377  rect_it.bounding_box(bleft, tright);
378  // inside rectangle
379  if (y >= bleft.y() && y < tright.y()) {
380  // width of line
381  xext = tright.x() - bleft.x();
382  return bleft.x(); // start of line
383  }
384  }
385  LOSTBLOCKLINE.error("BLOCK_LINE_IT::get_line", ABORT, "Y=%d", y);
386  return 0; // dummy to stop warning
387 }
388 
389 } // namespace tesseract
#define BLOCK_LABEL_HEIGHT
Definition: pdblock.cpp:34
constexpr ERRCODE BADBLOCKLINE("Y coordinate in block out of bounds")
constexpr ERRCODE LOSTBLOCKLINE("Can't find rectangle for line")
int16_t TDimension
Definition: tesstypes.h:32
@ ABORT
Definition: errcode.h:31
page block
Definition: pdblock.h:33
bool contains(ICOORD pt)
is pt inside block
Definition: pdblock.cpp:93
PDBLK & operator=(const PDBLK &source)
Definition: pdblock.cpp:245
PDBLK()
empty constructor
Definition: pdblock.h:39
Image render_mask(const FCOORD &rerotation, TBOX *mask_box)
Definition: pdblock.cpp:137
ICOORDELT_LIST rightside
right side vertices
Definition: pdblock.h:111
TBOX box
bounding box
Definition: pdblock.h:112
int index_
Serial number of this block.
Definition: pdblock.h:113
void move(const ICOORD vec)
reposition block
Definition: pdblock.cpp:117
void plot(ScrollView *window, int32_t serial, ScrollView::Color colour)
Definition: pdblock.cpp:185
POLY_BLOCK * hand_poly
weird as well
Definition: pdblock.h:109
ICOORDELT_LIST leftside
left side vertices
Definition: pdblock.h:110
void set_sides(ICOORDELT_LIST *left, ICOORDELT_LIST *right)
Definition: pdblock.cpp:71
void bounding_box(ICOORD &bottom_left, ICOORD &top_right) const
get box
Definition: pdblock.h:67
void forward()
next rectangle
Definition: pdblock.cpp:321
void start_block()
start iteration
Definition: pdblock.cpp:302
bool cycled_rects() const
test end
Definition: pdblock.h:133
void set_to_block(PDBLK *blkptr)
start (new) block
Definition: pdblock.cpp:285
BLOCK_RECT_IT(PDBLK *blkptr)
Definition: pdblock.cpp:267
void bounding_box(ICOORD &bleft, ICOORD &tright)
Definition: pdblock.h:140
TDimension get_line(TDimension y, TDimension &xext)
Definition: pdblock.cpp:353
integer coordinate
Definition: points.h:36
TDimension y() const
access_function
Definition: points.h:62
TDimension x() const
access function
Definition: points.h:58
static ICOORDELT * deep_copy(const ICOORDELT *src)
Definition: points.h:180
PolyBlockType isA() const
Definition: polyblk.h:48
void rotate(FCOORD rotation)
Definition: polyblk.cpp:191
ICOORDELT_LIST * points()
Definition: polyblk.h:42
void plot(ScrollView *window, int32_t num)
Definition: polyblk.cpp:246
TDimension left() const
Definition: rect.h:82
TDimension height() const
Definition: rect.h:118
TDimension width() const
Definition: rect.h:126
void move(const ICOORD vec)
Definition: rect.h:170
void rotate(const FCOORD &vec)
Definition: rect.h:210
TDimension top() const
Definition: rect.h:68
TDimension bottom() const
Definition: rect.h:75
void error(const char *caller, TessErrorLogCode action, const char *format,...) const __attribute__((format(printf
Definition: errcode.cpp:38
void TextAttributes(const char *font, int pixel_size, bool bold, bool italic, bool underlined)
Definition: scrollview.cpp:623
void Text(int x, int y, const char *mystring)
Definition: scrollview.cpp:648
void Pen(Color color)
Definition: scrollview.cpp:723
void SetCursor(int x, int y)
Definition: scrollview.cpp:498
void DrawTo(int x, int y)
Definition: scrollview.cpp:504