Coverage Report

Created: 2024-02-28 06:46

/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