/src/tesseract/src/ccstruct/pdblock.cpp
Line | Count | Source (jump to first uncovered line) |
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 | 16.6k | : box(ICOORD(xmin, ymin), ICOORD(xmax, ymax)) { |
50 | | // boundaries |
51 | 16.6k | ICOORDELT_IT left_it = &leftside; |
52 | 16.6k | ICOORDELT_IT right_it = &rightside; |
53 | | |
54 | 16.6k | hand_poly = nullptr; |
55 | 16.6k | left_it.set_to_list(&leftside); |
56 | 16.6k | right_it.set_to_list(&rightside); |
57 | | // make default box |
58 | 16.6k | left_it.add_to_end(new ICOORDELT(xmin, ymin)); |
59 | 16.6k | left_it.add_to_end(new ICOORDELT(xmin, ymax)); |
60 | 16.6k | right_it.add_to_end(new ICOORDELT(xmax, ymin)); |
61 | 16.6k | right_it.add_to_end(new ICOORDELT(xmax, ymax)); |
62 | 16.6k | index_ = 0; |
63 | 16.6k | } |
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 | 0 | ) { |
75 | | // boundaries |
76 | 0 | ICOORDELT_IT left_it = &leftside; |
77 | 0 | ICOORDELT_IT right_it = &rightside; |
78 | |
|
79 | 0 | leftside.clear(); |
80 | 0 | left_it.move_to_first(); |
81 | 0 | left_it.add_list_before(left); |
82 | 0 | rightside.clear(); |
83 | 0 | right_it.move_to_first(); |
84 | 0 | right_it.add_list_before(right); |
85 | 0 | } |
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 | 0 | ) { |
96 | 0 | BLOCK_RECT_IT it = this; // rectangle iterator |
97 | 0 | ICOORD bleft, tright; // corners of rectangle |
98 | |
|
99 | 0 | for (it.start_block(); !it.cycled_rects(); it.forward()) { |
100 | | // get rectangle |
101 | 0 | it.bounding_box(bleft, tright); |
102 | | // inside rect |
103 | 0 | if (pt.x() >= bleft.x() && pt.x() <= tright.x() && pt.y() >= bleft.y() && |
104 | 0 | pt.y() <= tright.y()) { |
105 | 0 | return true; // is inside |
106 | 0 | } |
107 | 0 | } |
108 | 0 | return false; // not inside |
109 | 0 | } |
110 | | |
111 | | /********************************************************************** |
112 | | * PDBLK::move |
113 | | * |
114 | | * Reposition block |
115 | | **********************************************************************/ |
116 | | |
117 | | void PDBLK::move( // reposition block |
118 | | const ICOORD vec // by vector |
119 | 0 | ) { |
120 | 0 | ICOORDELT_IT it(&leftside); |
121 | |
|
122 | 0 | for (it.mark_cycle_pt(); !it.cycled_list(); it.forward()) { |
123 | 0 | *(it.data()) += vec; |
124 | 0 | } |
125 | |
|
126 | 0 | it.set_to_list(&rightside); |
127 | |
|
128 | 0 | for (it.mark_cycle_pt(); !it.cycled_list(); it.forward()) { |
129 | 0 | *(it.data()) += vec; |
130 | 0 | } |
131 | |
|
132 | 0 | box.move(vec); |
133 | 0 | } |
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 | 0 | Image PDBLK::render_mask(const FCOORD &rerotation, TBOX *mask_box) { |
138 | 0 | TBOX rotated_box(box); |
139 | 0 | rotated_box.rotate(rerotation); |
140 | 0 | Image pix = pixCreate(rotated_box.width(), rotated_box.height(), 1); |
141 | 0 | 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 | 0 | ICOORDELT_LIST polygon; |
145 | 0 | polygon.deep_copy(hand_poly->points(), ICOORDELT::deep_copy); |
146 | 0 | POLY_BLOCK image_block(&polygon, hand_poly->isA()); |
147 | 0 | 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 | 0 | auto *lines = new PB_LINE_IT(&image_block); |
151 | 0 | for (int y = box.bottom(); y < box.top(); ++y) { |
152 | 0 | const std::unique_ptr</*non-const*/ ICOORDELT_LIST> segments(lines->get_line(y)); |
153 | 0 | if (!segments->empty()) { |
154 | 0 | 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 | 0 | for (s_it.mark_cycle_pt(); !s_it.cycled_list(); s_it.forward()) { |
158 | 0 | int start = s_it.data()->x(); |
159 | 0 | int xext = s_it.data()->y(); |
160 | | // Set the run of pixels to 1. |
161 | 0 | pixRasterop(pix, start - rotated_box.left(), |
162 | 0 | rotated_box.height() - 1 - (y - rotated_box.bottom()), xext, 1, PIX_SET, |
163 | 0 | nullptr, 0, 0); |
164 | 0 | } |
165 | 0 | } |
166 | 0 | } |
167 | 0 | delete lines; |
168 | 0 | } else { |
169 | | // Just fill the whole block as there is only a bounding box. |
170 | 0 | pixRasterop(pix, 0, 0, rotated_box.width(), rotated_box.height(), PIX_SET, nullptr, 0, 0); |
171 | 0 | } |
172 | 0 | if (mask_box != nullptr) { |
173 | 0 | *mask_box = rotated_box; |
174 | 0 | } |
175 | 0 | return pix; |
176 | 0 | } |
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 | 0 | ) { |
248 | | // this->ELIST_LINK::operator=(source); |
249 | 0 | if (!leftside.empty()) { |
250 | 0 | leftside.clear(); |
251 | 0 | } |
252 | 0 | if (!rightside.empty()) { |
253 | 0 | rightside.clear(); |
254 | 0 | } |
255 | 0 | leftside.deep_copy(&source.leftside, &ICOORDELT::deep_copy); |
256 | 0 | rightside.deep_copy(&source.rightside, &ICOORDELT::deep_copy); |
257 | 0 | box = source.box; |
258 | 0 | return *this; |
259 | 0 | } |
260 | | |
261 | | /********************************************************************** |
262 | | * BLOCK_RECT_IT::BLOCK_RECT_IT |
263 | | * |
264 | | * Construct a block rectangle iterator. |
265 | | **********************************************************************/ |
266 | | |
267 | | BLOCK_RECT_IT::BLOCK_RECT_IT( |
268 | | // iterate rectangles |
269 | | PDBLK *blkptr // from block |
270 | | ) |
271 | 16.6k | : left_it(&blkptr->leftside), right_it(&blkptr->rightside) { |
272 | 16.6k | block = blkptr; // remember block |
273 | | // non empty list |
274 | 16.6k | if (!blkptr->leftside.empty()) { |
275 | 16.6k | start_block(); // ready for iteration |
276 | 16.6k | } |
277 | 16.6k | } |
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 | 0 | PDBLK *blkptr) { // block to start |
287 | 0 | block = blkptr; // remember block |
288 | | // set iterators |
289 | 0 | left_it.set_to_list(&blkptr->leftside); |
290 | 0 | right_it.set_to_list(&blkptr->rightside); |
291 | 0 | if (!blkptr->leftside.empty()) { |
292 | 0 | start_block(); // ready for iteration |
293 | 0 | } |
294 | 0 | } |
295 | | |
296 | | /********************************************************************** |
297 | | * BLOCK_RECT_IT::start_block |
298 | | * |
299 | | * Restart a block. |
300 | | **********************************************************************/ |
301 | | |
302 | 16.6k | void BLOCK_RECT_IT::start_block() { // start (new) block |
303 | 16.6k | left_it.move_to_first(); |
304 | 16.6k | right_it.move_to_first(); |
305 | 16.6k | left_it.mark_cycle_pt(); |
306 | 16.6k | right_it.mark_cycle_pt(); |
307 | 16.6k | ymin = left_it.data()->y(); // bottom of first box |
308 | 16.6k | ymax = left_it.data_relative(1)->y(); |
309 | 16.6k | if (right_it.data_relative(1)->y() < ymax) { |
310 | | // smallest step |
311 | 0 | ymax = right_it.data_relative(1)->y(); |
312 | 0 | } |
313 | 16.6k | } |
314 | | |
315 | | /********************************************************************** |
316 | | * BLOCK_RECT_IT::forward |
317 | | * |
318 | | * Move to the next rectangle in the block. |
319 | | **********************************************************************/ |
320 | | |
321 | 0 | void BLOCK_RECT_IT::forward() { // next rectangle |
322 | 0 | if (!left_it.empty()) { // non-empty list |
323 | 0 | if (left_it.data_relative(1)->y() == ymax) { |
324 | 0 | left_it.forward(); // move to meet top |
325 | 0 | } |
326 | 0 | if (right_it.data_relative(1)->y() == ymax) { |
327 | 0 | right_it.forward(); |
328 | 0 | } |
329 | | // last is special |
330 | 0 | if (left_it.at_last() || right_it.at_last()) { |
331 | 0 | left_it.move_to_first(); // restart |
332 | 0 | right_it.move_to_first(); |
333 | | // now at bottom |
334 | 0 | ymin = left_it.data()->y(); |
335 | 0 | } else { |
336 | 0 | ymin = ymax; // new bottom |
337 | 0 | } |
338 | | // next point |
339 | 0 | ymax = left_it.data_relative(1)->y(); |
340 | 0 | if (right_it.data_relative(1)->y() < ymax) { |
341 | | // least step forward |
342 | 0 | ymax = right_it.data_relative(1)->y(); |
343 | 0 | } |
344 | 0 | } |
345 | 0 | } |
346 | | |
347 | | /********************************************************************** |
348 | | * BLOCK_LINE_IT::get_line |
349 | | * |
350 | | * Get the start and width of a line in the block. |
351 | | **********************************************************************/ |
352 | | |
353 | | TDimension BLOCK_LINE_IT::get_line( // get a line |
354 | | TDimension y, // line to get |
355 | | TDimension &xext // output extent |
356 | 3.01M | ) { |
357 | 3.01M | ICOORD bleft; // bounding box |
358 | 3.01M | ICOORD tright; // of block & rect |
359 | | |
360 | | // get block box |
361 | 3.01M | block->bounding_box(bleft, tright); |
362 | 3.01M | if (y < bleft.y() || y >= tright.y()) { |
363 | | // block->print(stderr,false); |
364 | 0 | BADBLOCKLINE.error("BLOCK_LINE_IT::get_line", ABORT, "Y=%d", y); |
365 | 0 | } |
366 | | |
367 | | // get rectangle box |
368 | 3.01M | rect_it.bounding_box(bleft, tright); |
369 | | // inside rectangle |
370 | 3.01M | if (y >= bleft.y() && y < tright.y()) { |
371 | | // width of line |
372 | 3.01M | xext = tright.x() - bleft.x(); |
373 | 3.01M | return bleft.x(); // start of line |
374 | 3.01M | } |
375 | 0 | for (rect_it.start_block(); !rect_it.cycled_rects(); rect_it.forward()) { |
376 | | // get rectangle box |
377 | 0 | rect_it.bounding_box(bleft, tright); |
378 | | // inside rectangle |
379 | 0 | if (y >= bleft.y() && y < tright.y()) { |
380 | | // width of line |
381 | 0 | xext = tright.x() - bleft.x(); |
382 | 0 | return bleft.x(); // start of line |
383 | 0 | } |
384 | 0 | } |
385 | 0 | LOSTBLOCKLINE.error("BLOCK_LINE_IT::get_line", ABORT, "Y=%d", y); |
386 | 0 | return 0; // dummy to stop warning |
387 | 0 | } |
388 | | |
389 | | } // namespace tesseract |