Mercurial > hgrepos > Python2 > PyMuPDF
comparison mupdf-source/thirdparty/tesseract/src/ccstruct/pdblock.cpp @ 2:b50eed0cc0ef upstream
ADD: MuPDF v1.26.7: the MuPDF source as downloaded by a default build of PyMuPDF 1.26.4.
The directory name has changed: no version number in the expanded directory now.
| author | Franz Glasner <fzglas.hg@dom66.de> |
|---|---|
| date | Mon, 15 Sep 2025 11:43:07 +0200 |
| parents | |
| children |
comparison
equal
deleted
inserted
replaced
| 1:1d09e1dec1d9 | 2:b50eed0cc0ef |
|---|---|
| 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 | |
| 267 BLOCK_RECT_IT::BLOCK_RECT_IT( | |
| 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 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 ) { | |
| 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 |
