tesseract v5.3.3.20231005
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
32namespace tesseract {
33
34#define BLOCK_LABEL_HEIGHT 150 // char height of block id
35
36constexpr ERRCODE BADBLOCKLINE("Y coordinate in block out of bounds");
37constexpr ERRCODE LOSTBLOCKLINE("Can't find rectangle for line");
38
39/**********************************************************************
40 * PDBLK::PDBLK
41 *
42 * Constructor for a simple rectangular block.
43 **********************************************************************/
44PDBLK::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
71void 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
93bool 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
117void 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.
137Image 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
185void 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
245PDBLK &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
285void 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
302void 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
321void 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 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
const double y
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
ICOORDELT_LIST * points()
Definition: polyblk.h:42
PolyBlockType isA() const
Definition: polyblk.h:48
void rotate(FCOORD rotation)
Definition: polyblk.cpp:191
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(gnu_printf
Definition: errcode.cpp:40
void TextAttributes(const char *font, int pixel_size, bool bold, bool italic, bool underlined)
Definition: scrollview.cpp:610
void Text(int x, int y, const char *mystring)
Definition: scrollview.cpp:635
void Pen(Color color)
Definition: scrollview.cpp:710
void SetCursor(int x, int y)
Definition: scrollview.cpp:485
void DrawTo(int x, int y)
Definition: scrollview.cpp:491