Mercurial > hgrepos > Python2 > PyMuPDF
comparison mupdf-source/thirdparty/tesseract/src/ccstruct/coutln.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: coutln.cpp (Formerly coutline.c) | |
| 3 * Description: Code for the C_OUTLINE class. | |
| 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 "coutln.h" | |
| 25 | |
| 26 #include "arrayaccess.h" // for GET_DATA_BYTE | |
| 27 #include "blobs.h" // for TPOINT | |
| 28 #include "crakedge.h" // for CRACKEDGE | |
| 29 #include "environ.h" // for l_uint32 | |
| 30 #include "errcode.h" // for ASSERT_HOST | |
| 31 #include "normalis.h" // for DENORM | |
| 32 | |
| 33 #include "helpers.h" // for ClipToRange, IntCastRounded, Modulo | |
| 34 | |
| 35 #include <allheaders.h> // for pixSetPixel, pixGetData, pixRasterop, pixGe... | |
| 36 #include "pix.h" // for Pix (ptr only), PIX_DST, PIX_NOT | |
| 37 | |
| 38 #include <algorithm> // for max, min | |
| 39 #include <cmath> // for abs | |
| 40 #include <cstdlib> // for abs | |
| 41 #include <cstring> // for memset, memcpy, memmove | |
| 42 | |
| 43 namespace tesseract { | |
| 44 | |
| 45 ICOORD C_OUTLINE::step_coords[4] = {ICOORD(-1, 0), ICOORD(0, -1), ICOORD(1, 0), ICOORD(0, 1)}; | |
| 46 | |
| 47 /** | |
| 48 * @name C_OUTLINE::C_OUTLINE | |
| 49 * | |
| 50 * Constructor to build a C_OUTLINE from a CRACKEDGE LOOP. | |
| 51 * @param startpt outline to convert | |
| 52 * @param bot_left bounding box | |
| 53 * @param top_right bounding box | |
| 54 * @param length length of loop | |
| 55 */ | |
| 56 | |
| 57 C_OUTLINE::C_OUTLINE(CRACKEDGE *startpt, ICOORD bot_left, ICOORD top_right, int16_t length) | |
| 58 : box(bot_left, top_right), start(startpt->pos), offsets(nullptr) { | |
| 59 int16_t stepindex; // index to step | |
| 60 CRACKEDGE *edgept; // current point | |
| 61 | |
| 62 stepcount = length; // no of steps | |
| 63 if (length == 0) { | |
| 64 return; | |
| 65 } | |
| 66 // get memory | |
| 67 steps.resize(step_mem()); | |
| 68 edgept = startpt; | |
| 69 | |
| 70 for (stepindex = 0; stepindex < length; stepindex++) { | |
| 71 // set compact step | |
| 72 set_step(stepindex, edgept->stepdir); | |
| 73 edgept = edgept->next; | |
| 74 } | |
| 75 } | |
| 76 | |
| 77 /** | |
| 78 * @name C_OUTLINE::C_OUTLINE | |
| 79 * | |
| 80 * Constructor to build a C_OUTLINE from a C_OUTLINE_FRAG. | |
| 81 */ | |
| 82 C_OUTLINE::C_OUTLINE( | |
| 83 // constructor | |
| 84 // steps to copy | |
| 85 ICOORD startpt, DIR128 *new_steps, | |
| 86 int16_t length // length of loop | |
| 87 ) | |
| 88 : start(startpt), offsets(nullptr) { | |
| 89 int8_t dirdiff; // direction difference | |
| 90 DIR128 prevdir; // previous direction | |
| 91 DIR128 dir; // current direction | |
| 92 DIR128 lastdir; // dir of last step | |
| 93 TBOX new_box; // easy bounding | |
| 94 int16_t stepindex; // index to step | |
| 95 int16_t srcindex; // source steps | |
| 96 ICOORD pos; // current position | |
| 97 | |
| 98 pos = startpt; | |
| 99 stepcount = length; // No. of steps. | |
| 100 ASSERT_HOST(length >= 0); | |
| 101 steps.resize(step_mem()); // Get memory. | |
| 102 | |
| 103 lastdir = new_steps[length - 1]; | |
| 104 prevdir = lastdir; | |
| 105 for (stepindex = 0, srcindex = 0; srcindex < length; stepindex++, srcindex++) { | |
| 106 new_box = TBOX(pos, pos); | |
| 107 box += new_box; | |
| 108 // copy steps | |
| 109 dir = new_steps[srcindex]; | |
| 110 set_step(stepindex, dir); | |
| 111 dirdiff = dir - prevdir; | |
| 112 pos += step(stepindex); | |
| 113 if ((dirdiff == 64 || dirdiff == -64) && stepindex > 0) { | |
| 114 stepindex -= 2; // cancel there-and-back | |
| 115 prevdir = stepindex >= 0 ? step_dir(stepindex) : lastdir; | |
| 116 } else { | |
| 117 prevdir = dir; | |
| 118 } | |
| 119 } | |
| 120 ASSERT_HOST(pos.x() == startpt.x() && pos.y() == startpt.y()); | |
| 121 do { | |
| 122 dirdiff = step_dir(stepindex - 1) - step_dir(0); | |
| 123 if (dirdiff == 64 || dirdiff == -64) { | |
| 124 start += step(0); | |
| 125 stepindex -= 2; // cancel there-and-back | |
| 126 for (int i = 0; i < stepindex; ++i) { | |
| 127 set_step(i, step_dir(i + 1)); | |
| 128 } | |
| 129 } | |
| 130 } while (stepindex > 1 && (dirdiff == 64 || dirdiff == -64)); | |
| 131 stepcount = stepindex; | |
| 132 ASSERT_HOST(stepcount >= 4); | |
| 133 } | |
| 134 | |
| 135 /** | |
| 136 * @name C_OUTLINE::C_OUTLINE | |
| 137 * | |
| 138 * Constructor to build a C_OUTLINE from a rotation of a C_OUTLINE. | |
| 139 * @param srcline outline to rotate | |
| 140 * @param rotation rotate to coord | |
| 141 */ | |
| 142 | |
| 143 C_OUTLINE::C_OUTLINE(C_OUTLINE *srcline, FCOORD rotation) : offsets(nullptr) { | |
| 144 TBOX new_box; // easy bounding | |
| 145 int16_t stepindex; // index to step | |
| 146 int16_t dirdiff; // direction change | |
| 147 ICOORD pos; // current position | |
| 148 ICOORD prevpos; // previous dest point | |
| 149 | |
| 150 ICOORD destpos; // destination point | |
| 151 int16_t destindex = INT16_MAX; // index to step | |
| 152 DIR128 dir; // coded direction | |
| 153 uint8_t new_step; | |
| 154 | |
| 155 stepcount = srcline->stepcount * 2; | |
| 156 if (stepcount == 0) { | |
| 157 box = srcline->box; | |
| 158 box.rotate(rotation); | |
| 159 return; | |
| 160 } | |
| 161 // get memory | |
| 162 steps.resize(step_mem()); | |
| 163 | |
| 164 for (int iteration = 0; iteration < 2; ++iteration) { | |
| 165 DIR128 round1 = iteration == 0 ? 32 : 0; | |
| 166 DIR128 round2 = iteration != 0 ? 32 : 0; | |
| 167 pos = srcline->start; | |
| 168 prevpos = pos; | |
| 169 prevpos.rotate(rotation); | |
| 170 start = prevpos; | |
| 171 box = TBOX(start, start); | |
| 172 destindex = 0; | |
| 173 for (stepindex = 0; stepindex < srcline->stepcount; stepindex++) { | |
| 174 pos += srcline->step(stepindex); | |
| 175 destpos = pos; | |
| 176 destpos.rotate(rotation); | |
| 177 // tprintf("%i %i %i %i ", destpos.x(), destpos.y(), pos.x(), pos.y()); | |
| 178 while (destpos.x() != prevpos.x() || destpos.y() != prevpos.y()) { | |
| 179 dir = DIR128(FCOORD(destpos - prevpos)); | |
| 180 dir += 64; // turn to step style | |
| 181 new_step = dir.get_dir(); | |
| 182 // tprintf(" %i\n", new_step); | |
| 183 if (new_step & 31) { | |
| 184 set_step(destindex++, dir + round1); | |
| 185 prevpos += step(destindex - 1); | |
| 186 if (destindex < 2 || | |
| 187 ((dirdiff = step_dir(destindex - 1) - step_dir(destindex - 2)) != -64 && | |
| 188 dirdiff != 64)) { | |
| 189 set_step(destindex++, dir + round2); | |
| 190 prevpos += step(destindex - 1); | |
| 191 } else { | |
| 192 prevpos -= step(destindex - 1); | |
| 193 destindex--; | |
| 194 prevpos -= step(destindex - 1); | |
| 195 set_step(destindex - 1, dir + round2); | |
| 196 prevpos += step(destindex - 1); | |
| 197 } | |
| 198 } else { | |
| 199 set_step(destindex++, dir); | |
| 200 prevpos += step(destindex - 1); | |
| 201 } | |
| 202 while (destindex >= 2 && | |
| 203 ((dirdiff = step_dir(destindex - 1) - step_dir(destindex - 2)) == -64 || | |
| 204 dirdiff == 64)) { | |
| 205 prevpos -= step(destindex - 1); | |
| 206 prevpos -= step(destindex - 2); | |
| 207 destindex -= 2; // Forget u turn | |
| 208 } | |
| 209 // ASSERT_HOST(prevpos.x() == destpos.x() && prevpos.y() == | |
| 210 // destpos.y()); | |
| 211 new_box = TBOX(destpos, destpos); | |
| 212 box += new_box; | |
| 213 } | |
| 214 } | |
| 215 ASSERT_HOST(destpos.x() == start.x() && destpos.y() == start.y()); | |
| 216 while (destindex > 1) { | |
| 217 dirdiff = step_dir(destindex - 1) - step_dir(0); | |
| 218 if (dirdiff != 64 && dirdiff != -64) { | |
| 219 break; | |
| 220 } | |
| 221 start += step(0); | |
| 222 destindex -= 2; | |
| 223 for (int i = 0; i < destindex; ++i) { | |
| 224 set_step(i, step_dir(i + 1)); | |
| 225 } | |
| 226 } | |
| 227 if (destindex >= 4) { | |
| 228 break; | |
| 229 } | |
| 230 } | |
| 231 ASSERT_HOST(destindex <= stepcount); | |
| 232 stepcount = destindex; | |
| 233 destpos = start; | |
| 234 for (stepindex = 0; stepindex < stepcount; stepindex++) { | |
| 235 destpos += step(stepindex); | |
| 236 } | |
| 237 ASSERT_HOST(destpos.x() == start.x() && destpos.y() == start.y()); | |
| 238 } | |
| 239 | |
| 240 // Build a fake outline, given just a bounding box and append to the list. | |
| 241 void C_OUTLINE::FakeOutline(const TBOX &box, C_OUTLINE_LIST *outlines) { | |
| 242 C_OUTLINE_IT ol_it(outlines); | |
| 243 // Make a C_OUTLINE from the bounds. This is a bit of a hack, | |
| 244 // as there is no outline, just a bounding box, but it works nicely. | |
| 245 CRACKEDGE start; | |
| 246 start.pos = box.topleft(); | |
| 247 auto *outline = new C_OUTLINE(&start, box.topleft(), box.botright(), 0); | |
| 248 ol_it.add_to_end(outline); | |
| 249 } | |
| 250 | |
| 251 /** | |
| 252 * @name C_OUTLINE::area | |
| 253 * | |
| 254 * Compute the area of the outline. | |
| 255 */ | |
| 256 | |
| 257 int32_t C_OUTLINE::area() const { | |
| 258 int stepindex; // current step | |
| 259 int32_t total_steps; // steps to do | |
| 260 int32_t total; // total area | |
| 261 ICOORD pos; // position of point | |
| 262 ICOORD next_step; // step to next pix | |
| 263 // We aren't going to modify the list, or its contents, but there is | |
| 264 // no const iterator. | |
| 265 C_OUTLINE_IT it(const_cast<C_OUTLINE_LIST *>(&children)); | |
| 266 | |
| 267 pos = start_pos(); | |
| 268 total_steps = pathlength(); | |
| 269 total = 0; | |
| 270 for (stepindex = 0; stepindex < total_steps; stepindex++) { | |
| 271 // all intersected | |
| 272 next_step = step(stepindex); | |
| 273 if (next_step.x() < 0) { | |
| 274 total += pos.y(); | |
| 275 } else if (next_step.x() > 0) { | |
| 276 total -= pos.y(); | |
| 277 } | |
| 278 pos += next_step; | |
| 279 } | |
| 280 for (it.mark_cycle_pt(); !it.cycled_list(); it.forward()) { | |
| 281 total += it.data()->area(); // add areas of children | |
| 282 } | |
| 283 | |
| 284 return total; | |
| 285 } | |
| 286 | |
| 287 /** | |
| 288 * @name C_OUTLINE::perimeter | |
| 289 * | |
| 290 * Compute the perimeter of the outline and its first level children. | |
| 291 */ | |
| 292 | |
| 293 int32_t C_OUTLINE::perimeter() const { | |
| 294 int32_t total_steps; // Return value. | |
| 295 // We aren't going to modify the list, or its contents, but there is | |
| 296 // no const iterator. | |
| 297 C_OUTLINE_IT it(const_cast<C_OUTLINE_LIST *>(&children)); | |
| 298 | |
| 299 total_steps = pathlength(); | |
| 300 for (it.mark_cycle_pt(); !it.cycled_list(); it.forward()) { | |
| 301 total_steps += it.data()->pathlength(); // Add perimeters of children. | |
| 302 } | |
| 303 | |
| 304 return total_steps; | |
| 305 } | |
| 306 | |
| 307 /** | |
| 308 * @name C_OUTLINE::outer_area | |
| 309 * | |
| 310 * Compute the area of the outline. | |
| 311 */ | |
| 312 | |
| 313 int32_t C_OUTLINE::outer_area() const { | |
| 314 int stepindex; // current step | |
| 315 int32_t total_steps; // steps to do | |
| 316 int32_t total; // total area | |
| 317 ICOORD pos; // position of point | |
| 318 ICOORD next_step; // step to next pix | |
| 319 | |
| 320 pos = start_pos(); | |
| 321 total_steps = pathlength(); | |
| 322 if (total_steps == 0) { | |
| 323 return box.area(); | |
| 324 } | |
| 325 total = 0; | |
| 326 for (stepindex = 0; stepindex < total_steps; stepindex++) { | |
| 327 // all intersected | |
| 328 next_step = step(stepindex); | |
| 329 if (next_step.x() < 0) { | |
| 330 total += pos.y(); | |
| 331 } else if (next_step.x() > 0) { | |
| 332 total -= pos.y(); | |
| 333 } | |
| 334 pos += next_step; | |
| 335 } | |
| 336 | |
| 337 return total; | |
| 338 } | |
| 339 | |
| 340 /** | |
| 341 * @name C_OUTLINE::count_transitions | |
| 342 * | |
| 343 * Compute the number of x and y maxes and mins in the outline. | |
| 344 * @param threshold winding number on size | |
| 345 */ | |
| 346 | |
| 347 int32_t C_OUTLINE::count_transitions(int32_t threshold) { | |
| 348 bool first_was_max_x; // what was first | |
| 349 bool first_was_max_y; | |
| 350 bool looking_for_max_x; // what is next | |
| 351 bool looking_for_min_x; | |
| 352 bool looking_for_max_y; // what is next | |
| 353 bool looking_for_min_y; | |
| 354 int stepindex; // current step | |
| 355 int32_t total_steps; // steps to do | |
| 356 // current limits | |
| 357 int32_t max_x, min_x, max_y, min_y; | |
| 358 int32_t initial_x, initial_y; // initial limits | |
| 359 int32_t total; // total changes | |
| 360 ICOORD pos; // position of point | |
| 361 ICOORD next_step; // step to next pix | |
| 362 | |
| 363 pos = start_pos(); | |
| 364 total_steps = pathlength(); | |
| 365 total = 0; | |
| 366 max_x = min_x = pos.x(); | |
| 367 max_y = min_y = pos.y(); | |
| 368 looking_for_max_x = true; | |
| 369 looking_for_min_x = true; | |
| 370 looking_for_max_y = true; | |
| 371 looking_for_min_y = true; | |
| 372 first_was_max_x = false; | |
| 373 first_was_max_y = false; | |
| 374 initial_x = pos.x(); | |
| 375 initial_y = pos.y(); // stop uninit warning | |
| 376 for (stepindex = 0; stepindex < total_steps; stepindex++) { | |
| 377 // all intersected | |
| 378 next_step = step(stepindex); | |
| 379 pos += next_step; | |
| 380 if (next_step.x() < 0) { | |
| 381 if (looking_for_max_x && pos.x() < min_x) { | |
| 382 min_x = pos.x(); | |
| 383 } | |
| 384 if (looking_for_min_x && max_x - pos.x() > threshold) { | |
| 385 if (looking_for_max_x) { | |
| 386 initial_x = max_x; | |
| 387 first_was_max_x = false; | |
| 388 } | |
| 389 total++; | |
| 390 looking_for_max_x = true; | |
| 391 looking_for_min_x = false; | |
| 392 min_x = pos.x(); // reset min | |
| 393 } | |
| 394 } else if (next_step.x() > 0) { | |
| 395 if (looking_for_min_x && pos.x() > max_x) { | |
| 396 max_x = pos.x(); | |
| 397 } | |
| 398 if (looking_for_max_x && pos.x() - min_x > threshold) { | |
| 399 if (looking_for_min_x) { | |
| 400 initial_x = min_x; // remember first min | |
| 401 first_was_max_x = true; | |
| 402 } | |
| 403 total++; | |
| 404 looking_for_max_x = false; | |
| 405 looking_for_min_x = true; | |
| 406 max_x = pos.x(); | |
| 407 } | |
| 408 } else if (next_step.y() < 0) { | |
| 409 if (looking_for_max_y && pos.y() < min_y) { | |
| 410 min_y = pos.y(); | |
| 411 } | |
| 412 if (looking_for_min_y && max_y - pos.y() > threshold) { | |
| 413 if (looking_for_max_y) { | |
| 414 initial_y = max_y; // remember first max | |
| 415 first_was_max_y = false; | |
| 416 } | |
| 417 total++; | |
| 418 looking_for_max_y = true; | |
| 419 looking_for_min_y = false; | |
| 420 min_y = pos.y(); // reset min | |
| 421 } | |
| 422 } else { | |
| 423 if (looking_for_min_y && pos.y() > max_y) { | |
| 424 max_y = pos.y(); | |
| 425 } | |
| 426 if (looking_for_max_y && pos.y() - min_y > threshold) { | |
| 427 if (looking_for_min_y) { | |
| 428 initial_y = min_y; // remember first min | |
| 429 first_was_max_y = true; | |
| 430 } | |
| 431 total++; | |
| 432 looking_for_max_y = false; | |
| 433 looking_for_min_y = true; | |
| 434 max_y = pos.y(); | |
| 435 } | |
| 436 } | |
| 437 } | |
| 438 if (first_was_max_x && looking_for_min_x) { | |
| 439 if (max_x - initial_x > threshold) { | |
| 440 total++; | |
| 441 } else { | |
| 442 total--; | |
| 443 } | |
| 444 } else if (!first_was_max_x && looking_for_max_x) { | |
| 445 if (initial_x - min_x > threshold) { | |
| 446 total++; | |
| 447 } else { | |
| 448 total--; | |
| 449 } | |
| 450 } | |
| 451 if (first_was_max_y && looking_for_min_y) { | |
| 452 if (max_y - initial_y > threshold) { | |
| 453 total++; | |
| 454 } else { | |
| 455 total--; | |
| 456 } | |
| 457 } else if (!first_was_max_y && looking_for_max_y) { | |
| 458 if (initial_y - min_y > threshold) { | |
| 459 total++; | |
| 460 } else { | |
| 461 total--; | |
| 462 } | |
| 463 } | |
| 464 | |
| 465 return total; | |
| 466 } | |
| 467 | |
| 468 /** | |
| 469 * @name C_OUTLINE::operator< | |
| 470 * | |
| 471 * @return true if the left operand is inside the right one. | |
| 472 * @param other other outline | |
| 473 */ | |
| 474 | |
| 475 bool C_OUTLINE::operator<(const C_OUTLINE &other) const { | |
| 476 int16_t count = 0; // winding count | |
| 477 ICOORD pos; // position of point | |
| 478 int32_t stepindex; // index to cstep | |
| 479 | |
| 480 if (!box.overlap(other.box)) { | |
| 481 return false; // can't be contained | |
| 482 } | |
| 483 if (stepcount == 0) { | |
| 484 return other.box.contains(this->box); | |
| 485 } | |
| 486 | |
| 487 pos = start; | |
| 488 for (stepindex = 0; stepindex < stepcount && (count = other.winding_number(pos)) == INTERSECTING; | |
| 489 stepindex++) { | |
| 490 pos += step(stepindex); // try all points | |
| 491 } | |
| 492 if (count == INTERSECTING) { | |
| 493 // all intersected | |
| 494 pos = other.start; | |
| 495 for (stepindex = 0; | |
| 496 stepindex < other.stepcount && (count = winding_number(pos)) == INTERSECTING; | |
| 497 stepindex++) { | |
| 498 // try other way round | |
| 499 pos += other.step(stepindex); | |
| 500 } | |
| 501 return count == INTERSECTING || count == 0; | |
| 502 } | |
| 503 return count != 0; | |
| 504 } | |
| 505 | |
| 506 /** | |
| 507 * @name C_OUTLINE::winding_number | |
| 508 * | |
| 509 * @return the winding number of the outline around the given point. | |
| 510 * @param point point to wind around | |
| 511 */ | |
| 512 | |
| 513 int16_t C_OUTLINE::winding_number(ICOORD point) const { | |
| 514 int16_t stepindex; // index to cstep | |
| 515 int16_t count; // winding count | |
| 516 ICOORD vec; // to current point | |
| 517 ICOORD stepvec; // step vector | |
| 518 int32_t cross; // cross product | |
| 519 | |
| 520 vec = start - point; // vector to it | |
| 521 count = 0; | |
| 522 for (stepindex = 0; stepindex < stepcount; stepindex++) { | |
| 523 stepvec = step(stepindex); // get the step | |
| 524 // crossing the line | |
| 525 if (vec.y() <= 0 && vec.y() + stepvec.y() > 0) { | |
| 526 cross = vec * stepvec; // cross product | |
| 527 if (cross > 0) { | |
| 528 count++; // crossing right half | |
| 529 } else if (cross == 0) { | |
| 530 return INTERSECTING; // going through point | |
| 531 } | |
| 532 } else if (vec.y() > 0 && vec.y() + stepvec.y() <= 0) { | |
| 533 cross = vec * stepvec; | |
| 534 if (cross < 0) { | |
| 535 count--; // crossing back | |
| 536 } else if (cross == 0) { | |
| 537 return INTERSECTING; // illegal | |
| 538 } | |
| 539 } | |
| 540 vec += stepvec; // sum vectors | |
| 541 } | |
| 542 return count; // winding number | |
| 543 } | |
| 544 | |
| 545 /** | |
| 546 * C_OUTLINE::turn_direction | |
| 547 * | |
| 548 * @return the sum direction delta of the outline. | |
| 549 */ | |
| 550 | |
| 551 int16_t C_OUTLINE::turn_direction() const { // winding number | |
| 552 DIR128 prevdir; // previous direction | |
| 553 DIR128 dir; // current direction | |
| 554 int16_t stepindex; // index to cstep | |
| 555 int8_t dirdiff; // direction difference | |
| 556 int16_t count; // winding count | |
| 557 | |
| 558 if (stepcount == 0) { | |
| 559 return 128; | |
| 560 } | |
| 561 count = 0; | |
| 562 prevdir = step_dir(stepcount - 1); | |
| 563 for (stepindex = 0; stepindex < stepcount; stepindex++) { | |
| 564 dir = step_dir(stepindex); | |
| 565 dirdiff = dir - prevdir; | |
| 566 ASSERT_HOST(dirdiff == 0 || dirdiff == 32 || dirdiff == -32); | |
| 567 count += dirdiff; | |
| 568 prevdir = dir; | |
| 569 } | |
| 570 ASSERT_HOST(count == 128 || count == -128); | |
| 571 return count; // winding number | |
| 572 } | |
| 573 | |
| 574 /** | |
| 575 * @name C_OUTLINE::reverse | |
| 576 * | |
| 577 * Reverse the direction of an outline. | |
| 578 */ | |
| 579 | |
| 580 void C_OUTLINE::reverse() { // reverse drection | |
| 581 DIR128 halfturn = MODULUS / 2; // amount to shift | |
| 582 DIR128 stepdir; // direction of step | |
| 583 int16_t stepindex; // index to cstep | |
| 584 int16_t farindex; // index to other side | |
| 585 int16_t halfsteps; // half of stepcount | |
| 586 | |
| 587 halfsteps = (stepcount + 1) / 2; | |
| 588 for (stepindex = 0; stepindex < halfsteps; stepindex++) { | |
| 589 farindex = stepcount - stepindex - 1; | |
| 590 stepdir = step_dir(stepindex); | |
| 591 set_step(stepindex, step_dir(farindex) + halfturn); | |
| 592 set_step(farindex, stepdir + halfturn); | |
| 593 } | |
| 594 } | |
| 595 | |
| 596 /** | |
| 597 * @name C_OUTLINE::move | |
| 598 * | |
| 599 * Move C_OUTLINE by vector | |
| 600 * @param vec vector to reposition OUTLINE by | |
| 601 */ | |
| 602 | |
| 603 void C_OUTLINE::move(const ICOORD vec) { | |
| 604 C_OUTLINE_IT it(&children); // iterator | |
| 605 | |
| 606 box.move(vec); | |
| 607 start += vec; | |
| 608 | |
| 609 for (it.mark_cycle_pt(); !it.cycled_list(); it.forward()) { | |
| 610 it.data()->move(vec); // move child outlines | |
| 611 } | |
| 612 } | |
| 613 | |
| 614 /** | |
| 615 * Returns true if *this and its children are legally nested. | |
| 616 * The outer area of a child should have the opposite sign to the | |
| 617 * parent. If not, it means we have discarded an outline in between | |
| 618 * (probably due to excessive length). | |
| 619 */ | |
| 620 bool C_OUTLINE::IsLegallyNested() const { | |
| 621 if (stepcount == 0) { | |
| 622 return true; | |
| 623 } | |
| 624 int64_t parent_area = outer_area(); | |
| 625 // We aren't going to modify the list, or its contents, but there is | |
| 626 // no const iterator. | |
| 627 C_OUTLINE_IT child_it(const_cast<C_OUTLINE_LIST *>(&children)); | |
| 628 for (child_it.mark_cycle_pt(); !child_it.cycled_list(); child_it.forward()) { | |
| 629 const C_OUTLINE *child = child_it.data(); | |
| 630 if (child->outer_area() * parent_area > 0 || !child->IsLegallyNested()) { | |
| 631 return false; | |
| 632 } | |
| 633 } | |
| 634 return true; | |
| 635 } | |
| 636 | |
| 637 /** | |
| 638 * If this outline is smaller than the given min_size, delete this and | |
| 639 * remove from its list, via *it, after checking that *it points to this. | |
| 640 * Otherwise, if any children of this are too small, delete them. | |
| 641 * On entry, *it must be an iterator pointing to this. If this gets deleted | |
| 642 * then this is extracted from *it, so an iteration can continue. | |
| 643 * @param min_size minimum size for outline | |
| 644 * @param it outline iterator | |
| 645 */ | |
| 646 void C_OUTLINE::RemoveSmallRecursive(int min_size, C_OUTLINE_IT *it) { | |
| 647 if (box.width() < min_size || box.height() < min_size) { | |
| 648 ASSERT_HOST(this == it->data()); | |
| 649 delete it->extract(); // Too small so get rid of it and any children. | |
| 650 } else if (!children.empty()) { | |
| 651 // Search the children of this, deleting any that are too small. | |
| 652 C_OUTLINE_IT child_it(&children); | |
| 653 for (child_it.mark_cycle_pt(); !child_it.cycled_list(); child_it.forward()) { | |
| 654 C_OUTLINE *child = child_it.data(); | |
| 655 child->RemoveSmallRecursive(min_size, &child_it); | |
| 656 } | |
| 657 } | |
| 658 } | |
| 659 | |
| 660 // Factored out helpers below are used only by ComputeEdgeOffsets to operate | |
| 661 // on data from an 8-bit Pix, and assume that any input x and/or y are already | |
| 662 // constrained to be legal Pix coordinates. | |
| 663 | |
| 664 /** | |
| 665 * Helper computes the local 2-D gradient (dx, dy) from the 2x2 cell centered | |
| 666 * on the given (x,y). If the cell would go outside the image, it is padded | |
| 667 * with white. | |
| 668 */ | |
| 669 static void ComputeGradient(const l_uint32 *data, int wpl, int x, int y, int width, int height, | |
| 670 ICOORD *gradient) { | |
| 671 const l_uint32 *line = data + y * wpl; | |
| 672 int pix_x_y = x < width && y < height ? GET_DATA_BYTE(line, x) : 255; | |
| 673 int pix_x_prevy = x < width && y > 0 ? GET_DATA_BYTE(line - wpl, x) : 255; | |
| 674 int pix_prevx_prevy = x > 0 && y > 0 ? GET_DATA_BYTE(line - wpl, x - 1) : 255; | |
| 675 int pix_prevx_y = x > 0 && y < height ? GET_DATA_BYTE(line, x - 1) : 255; | |
| 676 gradient->set_x(pix_x_y + pix_x_prevy - (pix_prevx_y + pix_prevx_prevy)); | |
| 677 gradient->set_y(pix_x_prevy + pix_prevx_prevy - (pix_x_y + pix_prevx_y)); | |
| 678 } | |
| 679 | |
| 680 /** | |
| 681 * Helper evaluates a vertical difference, (x,y) - (x,y-1), returning true if | |
| 682 * the difference, matches diff_sign and updating the best_diff, best_sum, | |
| 683 * best_y if a new max. | |
| 684 */ | |
| 685 static bool EvaluateVerticalDiff(const l_uint32 *data, int wpl, int diff_sign, int x, int y, | |
| 686 int height, int *best_diff, int *best_sum, int *best_y) { | |
| 687 if (y <= 0 || y >= height) { | |
| 688 return false; | |
| 689 } | |
| 690 const l_uint32 *line = data + y * wpl; | |
| 691 int pixel1 = GET_DATA_BYTE(line - wpl, x); | |
| 692 int pixel2 = GET_DATA_BYTE(line, x); | |
| 693 int diff = (pixel2 - pixel1) * diff_sign; | |
| 694 if (diff > *best_diff) { | |
| 695 *best_diff = diff; | |
| 696 *best_sum = pixel1 + pixel2; | |
| 697 *best_y = y; | |
| 698 } | |
| 699 return diff > 0; | |
| 700 } | |
| 701 | |
| 702 /** | |
| 703 * Helper evaluates a horizontal difference, (x,y) - (x-1,y), where y is implied | |
| 704 * by the input image line, returning true if the difference matches diff_sign | |
| 705 * and updating the best_diff, best_sum, best_x if a new max. | |
| 706 */ | |
| 707 static bool EvaluateHorizontalDiff(const l_uint32 *line, int diff_sign, int x, int width, | |
| 708 int *best_diff, int *best_sum, int *best_x) { | |
| 709 if (x <= 0 || x >= width) { | |
| 710 return false; | |
| 711 } | |
| 712 int pixel1 = GET_DATA_BYTE(line, x - 1); | |
| 713 int pixel2 = GET_DATA_BYTE(line, x); | |
| 714 int diff = (pixel2 - pixel1) * diff_sign; | |
| 715 if (diff > *best_diff) { | |
| 716 *best_diff = diff; | |
| 717 *best_sum = pixel1 + pixel2; | |
| 718 *best_x = x; | |
| 719 } | |
| 720 return diff > 0; | |
| 721 } | |
| 722 | |
| 723 /** | |
| 724 * Adds sub-pixel resolution EdgeOffsets for the outline if the supplied | |
| 725 * pix is 8-bit. Does nothing otherwise. | |
| 726 * Operation: Consider the following near-horizontal line: | |
| 727 * @verbatim | |
| 728 * _________ | |
| 729 * |________ | |
| 730 * |________ | |
| 731 * @endverbatim | |
| 732 * At *every* position along this line, the gradient direction will be close | |
| 733 * to vertical. Extrapoaltion/interpolation of the position of the threshold | |
| 734 * that was used to binarize the image gives a more precise vertical position | |
| 735 * for each horizontal step, and the conflict in step direction and gradient | |
| 736 * direction can be used to ignore the vertical steps. | |
| 737 */ | |
| 738 void C_OUTLINE::ComputeEdgeOffsets(int threshold, Image pix) { | |
| 739 if (pixGetDepth(pix) != 8) { | |
| 740 return; | |
| 741 } | |
| 742 const l_uint32 *data = pixGetData(pix); | |
| 743 int wpl = pixGetWpl(pix); | |
| 744 int width = pixGetWidth(pix); | |
| 745 int height = pixGetHeight(pix); | |
| 746 bool negative = flag(COUT_INVERSE); | |
| 747 delete[] offsets; | |
| 748 offsets = new EdgeOffset[stepcount]; | |
| 749 ICOORD pos = start; | |
| 750 ICOORD prev_gradient; | |
| 751 ComputeGradient(data, wpl, pos.x(), height - pos.y(), width, height, &prev_gradient); | |
| 752 for (int s = 0; s < stepcount; ++s) { | |
| 753 ICOORD step_vec = step(s); | |
| 754 TPOINT pt1(pos); | |
| 755 pos += step_vec; | |
| 756 TPOINT pt2(pos); | |
| 757 ICOORD next_gradient; | |
| 758 ComputeGradient(data, wpl, pos.x(), height - pos.y(), width, height, &next_gradient); | |
| 759 // Use the sum of the prev and next as the working gradient. | |
| 760 ICOORD gradient = prev_gradient + next_gradient; | |
| 761 // best_diff will be manipulated to be always positive. | |
| 762 int best_diff = 0; | |
| 763 // offset will be the extrapolation of the location of the greyscale | |
| 764 // threshold from the edge with the largest difference, relative to the | |
| 765 // location of the binary edge. | |
| 766 int offset = 0; | |
| 767 if (pt1.y == pt2.y && abs(gradient.y()) * 2 >= abs(gradient.x())) { | |
| 768 // Horizontal step. diff_sign == 1 indicates black above. | |
| 769 int diff_sign = (pt1.x > pt2.x) == negative ? 1 : -1; | |
| 770 int x = std::min(pt1.x, pt2.x); | |
| 771 int y = height - pt1.y; | |
| 772 int best_sum = 0; | |
| 773 int best_y = y; | |
| 774 EvaluateVerticalDiff(data, wpl, diff_sign, x, y, height, &best_diff, &best_sum, &best_y); | |
| 775 // Find the strongest edge. | |
| 776 int test_y = y; | |
| 777 do { | |
| 778 ++test_y; | |
| 779 } while (EvaluateVerticalDiff(data, wpl, diff_sign, x, test_y, height, &best_diff, &best_sum, | |
| 780 &best_y)); | |
| 781 test_y = y; | |
| 782 do { | |
| 783 --test_y; | |
| 784 } while (EvaluateVerticalDiff(data, wpl, diff_sign, x, test_y, height, &best_diff, &best_sum, | |
| 785 &best_y)); | |
| 786 offset = diff_sign * (best_sum / 2 - threshold) + (y - best_y) * best_diff; | |
| 787 } else if (pt1.x == pt2.x && abs(gradient.x()) * 2 >= abs(gradient.y())) { | |
| 788 // Vertical step. diff_sign == 1 indicates black on the left. | |
| 789 int diff_sign = (pt1.y > pt2.y) == negative ? 1 : -1; | |
| 790 int x = pt1.x; | |
| 791 int y = height - std::max(pt1.y, pt2.y); | |
| 792 const l_uint32 *line = pixGetData(pix) + y * wpl; | |
| 793 int best_sum = 0; | |
| 794 int best_x = x; | |
| 795 EvaluateHorizontalDiff(line, diff_sign, x, width, &best_diff, &best_sum, &best_x); | |
| 796 // Find the strongest edge. | |
| 797 int test_x = x; | |
| 798 do { | |
| 799 ++test_x; | |
| 800 } while ( | |
| 801 EvaluateHorizontalDiff(line, diff_sign, test_x, width, &best_diff, &best_sum, &best_x)); | |
| 802 test_x = x; | |
| 803 do { | |
| 804 --test_x; | |
| 805 } while ( | |
| 806 EvaluateHorizontalDiff(line, diff_sign, test_x, width, &best_diff, &best_sum, &best_x)); | |
| 807 offset = diff_sign * (threshold - best_sum / 2) + (best_x - x) * best_diff; | |
| 808 } | |
| 809 offsets[s].offset_numerator = ClipToRange<int>(offset, -INT8_MAX, INT8_MAX); | |
| 810 offsets[s].pixel_diff = ClipToRange<int>(best_diff, 0, UINT8_MAX); | |
| 811 if (negative) { | |
| 812 gradient = -gradient; | |
| 813 } | |
| 814 // Compute gradient angle quantized to 256 directions, rotated by 64 (pi/2) | |
| 815 // to convert from gradient direction to edge direction. | |
| 816 offsets[s].direction = Modulo(FCOORD::binary_angle_plus_pi(gradient.angle()) + 64, 256); | |
| 817 prev_gradient = next_gradient; | |
| 818 } | |
| 819 } | |
| 820 | |
| 821 /** | |
| 822 * Adds sub-pixel resolution EdgeOffsets for the outline using only | |
| 823 * a binary image source. | |
| 824 * | |
| 825 * Runs a sliding window of 5 edge steps over the outline, maintaining a count | |
| 826 * of the number of steps in each of the 4 directions in the window, and a | |
| 827 * sum of the x or y position of each step (as appropriate to its direction.) | |
| 828 * Ignores single-count steps EXCEPT the sharp U-turn and smoothes out the | |
| 829 * perpendicular direction. Eg | |
| 830 * @verbatim | |
| 831 * ___ ___ Chain code from the left: | |
| 832 * |___ ___ ___| 222122212223221232223000 | |
| 833 * |___| |_| Corresponding counts of each direction: | |
| 834 * 0 00000000000000000123 | |
| 835 * 1 11121111001111100000 | |
| 836 * 2 44434443443333343321 | |
| 837 * 3 00000001111111112111 | |
| 838 * Count of direction at center 41434143413313143313 | |
| 839 * Step gets used? YNYYYNYYYNYYNYNYYYyY (y= U-turn exception) | |
| 840 * Path redrawn showing only the used points: | |
| 841 * ___ ___ | |
| 842 * ___ ___ ___| | |
| 843 * ___ _ | |
| 844 * @endverbatim | |
| 845 * Sub-pixel edge position cannot be shown well with ASCII-art, but each | |
| 846 * horizontal step's y position is the mean of the y positions of the steps | |
| 847 * in the same direction in the sliding window, which makes a much smoother | |
| 848 * outline, without losing important detail. | |
| 849 */ | |
| 850 void C_OUTLINE::ComputeBinaryOffsets() { | |
| 851 delete[] offsets; | |
| 852 offsets = new EdgeOffset[stepcount]; | |
| 853 // Count of the number of steps in each direction in the sliding window. | |
| 854 int dir_counts[4]; | |
| 855 // Sum of the positions (y for a horizontal step, x for vertical) in each | |
| 856 // direction in the sliding window. | |
| 857 int pos_totals[4]; | |
| 858 memset(dir_counts, 0, sizeof(dir_counts)); | |
| 859 memset(pos_totals, 0, sizeof(pos_totals)); | |
| 860 ICOORD pos = start; | |
| 861 ICOORD tail_pos = pos; | |
| 862 // tail_pos is the trailing position, with the next point to be lost from | |
| 863 // the window. | |
| 864 tail_pos -= step(stepcount - 1); | |
| 865 tail_pos -= step(stepcount - 2); | |
| 866 // head_pos is the leading position, with the next point to be added to the | |
| 867 // window. | |
| 868 ICOORD head_pos = tail_pos; | |
| 869 // Set up the initial window with 4 points in [-2, 2) | |
| 870 for (int s = -2; s < 2; ++s) { | |
| 871 increment_step(s, 1, &head_pos, dir_counts, pos_totals); | |
| 872 } | |
| 873 for (int s = 0; s < stepcount; pos += step(s++)) { | |
| 874 // At step s, s in the middle of [s-2, s+2]. | |
| 875 increment_step(s + 2, 1, &head_pos, dir_counts, pos_totals); | |
| 876 int dir_index = chain_code(s); | |
| 877 ICOORD step_vec = step(s); | |
| 878 int best_diff = 0; | |
| 879 int offset = 0; | |
| 880 // Use only steps that have a count of >=2 OR the strong U-turn with a | |
| 881 // single d and 2 at d-1 and 2 at d+1 (mod 4). | |
| 882 if (dir_counts[dir_index] >= 2 || | |
| 883 (dir_counts[dir_index] == 1 && dir_counts[Modulo(dir_index - 1, 4)] == 2 && | |
| 884 dir_counts[Modulo(dir_index + 1, 4)] == 2)) { | |
| 885 // Valid step direction. | |
| 886 best_diff = dir_counts[dir_index]; | |
| 887 int edge_pos = step_vec.x() == 0 ? pos.x() : pos.y(); | |
| 888 // The offset proposes that the actual step should be positioned at | |
| 889 // the mean position of the steps in the window of the same direction. | |
| 890 // See ASCII art above. | |
| 891 offset = pos_totals[dir_index] - best_diff * edge_pos; | |
| 892 } | |
| 893 offsets[s].offset_numerator = ClipToRange<int>(offset, -INT8_MAX, INT8_MAX); | |
| 894 offsets[s].pixel_diff = ClipToRange<int>(best_diff, 0, UINT8_MAX); | |
| 895 // The direction is just the vector from start to end of the window. | |
| 896 FCOORD direction(head_pos.x() - tail_pos.x(), head_pos.y() - tail_pos.y()); | |
| 897 offsets[s].direction = direction.to_direction(); | |
| 898 increment_step(s - 2, -1, &tail_pos, dir_counts, pos_totals); | |
| 899 } | |
| 900 } | |
| 901 | |
| 902 /** | |
| 903 * Renders the outline to the given pix, with left and top being | |
| 904 * the coords of the upper-left corner of the pix. | |
| 905 */ | |
| 906 void C_OUTLINE::render(int left, int top, Image pix) const { | |
| 907 ICOORD pos = start; | |
| 908 for (int stepindex = 0; stepindex < stepcount; ++stepindex) { | |
| 909 ICOORD next_step = step(stepindex); | |
| 910 if (next_step.y() < 0) { | |
| 911 pixRasterop(pix, 0, top - pos.y(), pos.x() - left, 1, PIX_NOT(PIX_DST), nullptr, 0, 0); | |
| 912 } else if (next_step.y() > 0) { | |
| 913 pixRasterop(pix, 0, top - pos.y() - 1, pos.x() - left, 1, PIX_NOT(PIX_DST), nullptr, 0, 0); | |
| 914 } | |
| 915 pos += next_step; | |
| 916 } | |
| 917 } | |
| 918 | |
| 919 /** | |
| 920 * Renders just the outline to the given pix (no fill), with left and top | |
| 921 * being the coords of the upper-left corner of the pix. | |
| 922 * @param left coord | |
| 923 * @param top coord | |
| 924 * @param pix the pix to outline | |
| 925 */ | |
| 926 void C_OUTLINE::render_outline(int left, int top, Image pix) const { | |
| 927 ICOORD pos = start; | |
| 928 for (int stepindex = 0; stepindex < stepcount; ++stepindex) { | |
| 929 ICOORD next_step = step(stepindex); | |
| 930 if (next_step.y() < 0) { | |
| 931 pixSetPixel(pix, pos.x() - left, top - pos.y(), 1); | |
| 932 } else if (next_step.y() > 0) { | |
| 933 pixSetPixel(pix, pos.x() - left - 1, top - pos.y() - 1, 1); | |
| 934 } else if (next_step.x() < 0) { | |
| 935 pixSetPixel(pix, pos.x() - left - 1, top - pos.y(), 1); | |
| 936 } else if (next_step.x() > 0) { | |
| 937 pixSetPixel(pix, pos.x() - left, top - pos.y() - 1, 1); | |
| 938 } | |
| 939 pos += next_step; | |
| 940 } | |
| 941 } | |
| 942 | |
| 943 /** | |
| 944 * @name C_OUTLINE::plot | |
| 945 * | |
| 946 * Draw the outline in the given colour. | |
| 947 * @param window window to draw in | |
| 948 * @param colour colour to draw in | |
| 949 */ | |
| 950 | |
| 951 #ifndef GRAPHICS_DISABLED | |
| 952 void C_OUTLINE::plot(ScrollView *window, ScrollView::Color colour) const { | |
| 953 int16_t stepindex; // index to cstep | |
| 954 ICOORD pos; // current position | |
| 955 DIR128 stepdir; // direction of step | |
| 956 | |
| 957 pos = start; // current position | |
| 958 window->Pen(colour); | |
| 959 if (stepcount == 0) { | |
| 960 window->Rectangle(box.left(), box.top(), box.right(), box.bottom()); | |
| 961 return; | |
| 962 } | |
| 963 window->SetCursor(pos.x(), pos.y()); | |
| 964 | |
| 965 stepindex = 0; | |
| 966 while (stepindex < stepcount) { | |
| 967 pos += step(stepindex); // step to next | |
| 968 stepdir = step_dir(stepindex); | |
| 969 stepindex++; // count steps | |
| 970 // merge straight lines | |
| 971 while (stepindex < stepcount && stepdir.get_dir() == step_dir(stepindex).get_dir()) { | |
| 972 pos += step(stepindex); | |
| 973 stepindex++; | |
| 974 } | |
| 975 window->DrawTo(pos.x(), pos.y()); | |
| 976 } | |
| 977 } | |
| 978 | |
| 979 /** | |
| 980 * Draws the outline in the given colour, normalized using the given denorm, | |
| 981 * making use of sub-pixel accurate information if available. | |
| 982 */ | |
| 983 void C_OUTLINE::plot_normed(const DENORM &denorm, ScrollView::Color colour, | |
| 984 ScrollView *window) const { | |
| 985 window->Pen(colour); | |
| 986 if (stepcount == 0) { | |
| 987 window->Rectangle(box.left(), box.top(), box.right(), box.bottom()); | |
| 988 return; | |
| 989 } | |
| 990 const DENORM *root_denorm = denorm.RootDenorm(); | |
| 991 ICOORD pos = start; // current position | |
| 992 FCOORD f_pos = sub_pixel_pos_at_index(pos, 0); | |
| 993 FCOORD pos_normed; | |
| 994 denorm.NormTransform(root_denorm, f_pos, &pos_normed); | |
| 995 window->SetCursor(IntCastRounded(pos_normed.x()), IntCastRounded(pos_normed.y())); | |
| 996 for (int s = 0; s < stepcount; pos += step(s++)) { | |
| 997 int edge_weight = edge_strength_at_index(s); | |
| 998 if (edge_weight == 0) { | |
| 999 // This point has conflicting gradient and step direction, so ignore it. | |
| 1000 continue; | |
| 1001 } | |
| 1002 FCOORD f_pos = sub_pixel_pos_at_index(pos, s); | |
| 1003 FCOORD pos_normed; | |
| 1004 denorm.NormTransform(root_denorm, f_pos, &pos_normed); | |
| 1005 window->DrawTo(IntCastRounded(pos_normed.x()), IntCastRounded(pos_normed.y())); | |
| 1006 } | |
| 1007 } | |
| 1008 #endif | |
| 1009 | |
| 1010 /** | |
| 1011 * @name C_OUTLINE::operator= | |
| 1012 * | |
| 1013 * Assignment - deep copy data | |
| 1014 * @param source assign from this | |
| 1015 */ | |
| 1016 | |
| 1017 C_OUTLINE &C_OUTLINE::operator=(const C_OUTLINE &source) { | |
| 1018 box = source.box; | |
| 1019 start = source.start; | |
| 1020 if (!children.empty()) { | |
| 1021 children.clear(); | |
| 1022 } | |
| 1023 children.deep_copy(&source.children, &deep_copy); | |
| 1024 delete[] offsets; | |
| 1025 offsets = nullptr; | |
| 1026 stepcount = source.stepcount; | |
| 1027 if (stepcount > 0) { | |
| 1028 steps.resize(step_mem()); | |
| 1029 memmove(&steps[0], &source.steps[0], step_mem()); | |
| 1030 if (source.offsets != nullptr) { | |
| 1031 offsets = new EdgeOffset[stepcount]; | |
| 1032 memcpy(offsets, source.offsets, stepcount * sizeof(*offsets)); | |
| 1033 } | |
| 1034 } | |
| 1035 return *this; | |
| 1036 } | |
| 1037 | |
| 1038 /** | |
| 1039 * Helper for ComputeBinaryOffsets. Increments pos, dir_counts, pos_totals | |
| 1040 * by the step, increment, and vertical step ? x : y position * increment | |
| 1041 * at step s Mod stepcount respectively. Used to add or subtract the | |
| 1042 * direction and position to/from accumulators of a small neighbourhood. | |
| 1043 */ | |
| 1044 void C_OUTLINE::increment_step(int s, int increment, ICOORD *pos, int *dir_counts, | |
| 1045 int *pos_totals) const { | |
| 1046 int step_index = Modulo(s, stepcount); | |
| 1047 int dir_index = chain_code(step_index); | |
| 1048 dir_counts[dir_index] += increment; | |
| 1049 ICOORD step_vec = step(step_index); | |
| 1050 if (step_vec.x() == 0) { | |
| 1051 pos_totals[dir_index] += pos->x() * increment; | |
| 1052 } else { | |
| 1053 pos_totals[dir_index] += pos->y() * increment; | |
| 1054 } | |
| 1055 *pos += step_vec; | |
| 1056 } | |
| 1057 | |
| 1058 ICOORD C_OUTLINE::chain_step(int chaindir) { | |
| 1059 return step_coords[chaindir % 4]; | |
| 1060 } | |
| 1061 | |
| 1062 } // namespace tesseract |
