diff mupdf-source/thirdparty/tesseract/src/ccstruct/polyblk.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
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mupdf-source/thirdparty/tesseract/src/ccstruct/polyblk.cpp	Mon Sep 15 11:43:07 2025 +0200
@@ -0,0 +1,417 @@
+/**********************************************************************
+ * File:        polyblk.cpp  (Formerly poly_block.c)
+ * Description: Polygonal blocks
+ *
+ * (C) Copyright 1993, Hewlett-Packard Ltd.
+ ** Licensed under the Apache License, Version 2.0 (the "License");
+ ** you may not use this file except in compliance with the License.
+ ** You may obtain a copy of the License at
+ ** http://www.apache.org/licenses/LICENSE-2.0
+ ** Unless required by applicable law or agreed to in writing, software
+ ** distributed under the License is distributed on an "AS IS" BASIS,
+ ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ** See the License for the specific language governing permissions and
+ ** limitations under the License.
+ *
+ **********************************************************************/
+
+// Include automatically generated configuration file if running autoconf.
+#ifdef HAVE_CONFIG_H
+#  include "config_auto.h"
+#endif
+
+#include "polyblk.h"
+
+#include "elst.h"
+
+#include <cctype>
+#include <cinttypes> // PRId32
+#include <cmath>
+#include <cstdio>
+#include <memory> // std::unique_ptr
+
+namespace tesseract {
+
+#define INTERSECTING INT16_MAX
+
+int lessthan(const void *first, const void *second);
+
+POLY_BLOCK::POLY_BLOCK(ICOORDELT_LIST *points, PolyBlockType t) {
+  ICOORDELT_IT v = &vertices;
+
+  vertices.clear();
+  v.move_to_first();
+  v.add_list_before(points);
+  compute_bb();
+  type = t;
+}
+
+// Initialize from box coordinates.
+POLY_BLOCK::POLY_BLOCK(const TBOX &tbox, PolyBlockType t) {
+  vertices.clear();
+  ICOORDELT_IT v = &vertices;
+  v.move_to_first();
+  v.add_to_end(new ICOORDELT(tbox.left(), tbox.top()));
+  v.add_to_end(new ICOORDELT(tbox.left(), tbox.bottom()));
+  v.add_to_end(new ICOORDELT(tbox.right(), tbox.bottom()));
+  v.add_to_end(new ICOORDELT(tbox.right(), tbox.top()));
+  compute_bb();
+  type = t;
+}
+
+/**
+ * @name POLY_BLOCK::compute_bb
+ *
+ * Compute the bounding box from the outline points.
+ */
+
+void POLY_BLOCK::compute_bb() { // constructor
+  ICOORD ibl, itr;              // integer bb
+  ICOORD botleft;               // bounding box
+  ICOORD topright;
+  ICOORD pos;                   // current pos;
+  ICOORDELT_IT pts = &vertices; // iterator
+
+  botleft = *pts.data();
+  topright = botleft;
+  do {
+    pos = *pts.data();
+    if (pos.x() < botleft.x()) {
+      // get bounding box
+      botleft = ICOORD(pos.x(), botleft.y());
+    }
+    if (pos.y() < botleft.y()) {
+      botleft = ICOORD(botleft.x(), pos.y());
+    }
+    if (pos.x() > topright.x()) {
+      topright = ICOORD(pos.x(), topright.y());
+    }
+    if (pos.y() > topright.y()) {
+      topright = ICOORD(topright.x(), pos.y());
+    }
+    pts.forward();
+  } while (!pts.at_first());
+  ibl = ICOORD(botleft.x(), botleft.y());
+  itr = ICOORD(topright.x(), topright.y());
+  box = TBOX(ibl, itr);
+}
+
+/**
+ * @name POLY_BLOCK::winding_number
+ *
+ * Return the winding number of the outline around the given point.
+ * @param point point to wind around
+ */
+
+int16_t POLY_BLOCK::winding_number(const ICOORD &point) {
+  int16_t count;               // winding count
+  ICOORD pt;                   // current point
+  ICOORD vec;                  // point to current point
+  ICOORD vvec;                 // current point to next point
+  int32_t cross;               // cross product
+  ICOORDELT_IT it = &vertices; // iterator
+
+  count = 0;
+  do {
+    pt = *it.data();
+    vec = pt - point;
+    vvec = *it.data_relative(1) - pt;
+    // crossing the line
+    if (vec.y() <= 0 && vec.y() + vvec.y() > 0) {
+      cross = vec * vvec; // cross product
+      if (cross > 0) {
+        count++; // crossing right half
+      } else if (cross == 0) {
+        return INTERSECTING; // going through point
+      }
+    } else if (vec.y() > 0 && vec.y() + vvec.y() <= 0) {
+      cross = vec * vvec;
+      if (cross < 0) {
+        count--; // crossing back
+      } else if (cross == 0) {
+        return INTERSECTING; // illegal
+      }
+    } else if (vec.y() == 0 && vec.x() == 0) {
+      return INTERSECTING;
+    }
+    it.forward();
+  } while (!it.at_first());
+  return count; // winding number
+}
+
+/// @return true if other is inside this.
+bool POLY_BLOCK::contains(POLY_BLOCK *other) {
+  int16_t count;               // winding count
+  ICOORDELT_IT it = &vertices; // iterator
+  ICOORD vertex;
+
+  if (!box.overlap(*(other->bounding_box()))) {
+    return false; // can't be contained
+  }
+
+  /* check that no vertex of this is inside other */
+
+  do {
+    vertex = *it.data();
+    // get winding number
+    count = other->winding_number(vertex);
+    if (count != INTERSECTING) {
+      if (count != 0) {
+        return false;
+      }
+    }
+    it.forward();
+  } while (!it.at_first());
+
+  /* check that all vertices of other are inside this */
+
+  // switch lists
+  it.set_to_list(other->points());
+  do {
+    vertex = *it.data();
+    // try other way round
+    count = winding_number(vertex);
+    if (count != INTERSECTING) {
+      if (count == 0) {
+        return false;
+      }
+    }
+    it.forward();
+  } while (!it.at_first());
+  return true;
+}
+
+/**
+ * @name POLY_BLOCK::rotate
+ *
+ * Rotate the POLY_BLOCK.
+ * @param rotation cos, sin of angle
+ */
+
+void POLY_BLOCK::rotate(FCOORD rotation) {
+  FCOORD pos;                   // current pos;
+  ICOORDELT *pt;                // current point
+  ICOORDELT_IT pts = &vertices; // iterator
+
+  do {
+    pt = pts.data();
+    pos.set_x(pt->x());
+    pos.set_y(pt->y());
+    pos.rotate(rotation);
+    pt->set_x(static_cast<TDimension>(floor(pos.x() + 0.5)));
+    pt->set_y(static_cast<TDimension>(floor(pos.y() + 0.5)));
+    pts.forward();
+  } while (!pts.at_first());
+  compute_bb();
+}
+
+/**
+ * @name POLY_BLOCK::reflect_in_y_axis
+ *
+ * Reflect the coords of the polygon in the y-axis. (Flip the sign of x.)
+ */
+
+void POLY_BLOCK::reflect_in_y_axis() {
+  ICOORDELT *pt;                // current point
+  ICOORDELT_IT pts = &vertices; // Iterator.
+
+  do {
+    pt = pts.data();
+    pt->set_x(-pt->x());
+    pts.forward();
+  } while (!pts.at_first());
+  compute_bb();
+}
+
+/**
+ * POLY_BLOCK::move
+ *
+ * Move the POLY_BLOCK.
+ * @param shift x,y translation vector
+ */
+
+void POLY_BLOCK::move(ICOORD shift) {
+  ICOORDELT *pt;                // current point
+  ICOORDELT_IT pts = &vertices; // iterator
+
+  do {
+    pt = pts.data();
+    *pt += shift;
+    pts.forward();
+  } while (!pts.at_first());
+  compute_bb();
+}
+
+#ifndef GRAPHICS_DISABLED
+void POLY_BLOCK::plot(ScrollView *window, int32_t num) {
+  ICOORDELT_IT v = &vertices;
+
+  window->Pen(ColorForPolyBlockType(type));
+
+  v.move_to_first();
+
+  if (num > 0) {
+    window->TextAttributes("Times", 80, false, false, false);
+    char temp_buff[34];
+#  if !defined(_WIN32) || defined(__MINGW32__)
+    snprintf(temp_buff, sizeof(temp_buff), "%" PRId32, num);
+#  else
+    _ltoa(num, temp_buff, 10);
+#  endif
+    window->Text(v.data()->x(), v.data()->y(), temp_buff);
+  }
+
+  window->SetCursor(v.data()->x(), v.data()->y());
+  for (v.mark_cycle_pt(); !v.cycled_list(); v.forward()) {
+    window->DrawTo(v.data()->x(), v.data()->y());
+  }
+  v.move_to_first();
+  window->DrawTo(v.data()->x(), v.data()->y());
+}
+
+void POLY_BLOCK::fill(ScrollView *window, ScrollView::Color colour) {
+  ICOORDELT_IT s_it;
+
+  std::unique_ptr<PB_LINE_IT> lines(new PB_LINE_IT(this));
+  window->Pen(colour);
+
+  for (auto y = this->bounding_box()->bottom(); y <= this->bounding_box()->top(); y++) {
+    const std::unique_ptr</*non-const*/ ICOORDELT_LIST> segments(lines->get_line(y));
+    if (!segments->empty()) {
+      s_it.set_to_list(segments.get());
+      for (s_it.mark_cycle_pt(); !s_it.cycled_list(); s_it.forward()) {
+        // Note different use of ICOORDELT, x coord is x coord of pixel
+        // at the start of line segment, y coord is length of line segment
+        // Last pixel is start pixel + length.
+        auto width = s_it.data()->y();
+        window->SetCursor(s_it.data()->x(), y);
+        window->DrawTo(s_it.data()->x() + static_cast<float>(width), y);
+      }
+    }
+  }
+}
+#endif
+
+/// @return true if the polygons of other and this overlap.
+bool POLY_BLOCK::overlap(POLY_BLOCK *other) {
+  int16_t count;               // winding count
+  ICOORDELT_IT it = &vertices; // iterator
+  ICOORD vertex;
+
+  if (!box.overlap(*(other->bounding_box()))) {
+    return false; // can't be any overlap.
+  }
+
+  /* see if a vertex of this is inside other */
+
+  do {
+    vertex = *it.data();
+    // get winding number
+    count = other->winding_number(vertex);
+    if (count != INTERSECTING) {
+      if (count != 0) {
+        return true;
+      }
+    }
+    it.forward();
+  } while (!it.at_first());
+
+  /* see if a vertex of other is inside this */
+
+  // switch lists
+  it.set_to_list(other->points());
+  do {
+    vertex = *it.data();
+    // try other way round
+    count = winding_number(vertex);
+    if (count != INTERSECTING) {
+      if (count != 0) {
+        return true;
+      }
+    }
+    it.forward();
+  } while (!it.at_first());
+  return false;
+}
+
+ICOORDELT_LIST *PB_LINE_IT::get_line(TDimension y) {
+  ICOORDELT_IT v, r;
+  ICOORDELT_LIST *result;
+  ICOORDELT *x, *current, *previous;
+  float fy = y + 0.5f;
+  result = new ICOORDELT_LIST();
+  r.set_to_list(result);
+  v.set_to_list(block->points());
+
+  for (v.mark_cycle_pt(); !v.cycled_list(); v.forward()) {
+    if (((v.data_relative(-1)->y() > y) && (v.data()->y() <= y)) ||
+        ((v.data_relative(-1)->y() <= y) && (v.data()->y() > y))) {
+      previous = v.data_relative(-1);
+      current = v.data();
+      float fx =
+          0.5f + previous->x() +
+          (current->x() - previous->x()) * (fy - previous->y()) / (current->y() - previous->y());
+      x = new ICOORDELT(static_cast<TDimension>(fx), 0);
+      r.add_to_end(x);
+    }
+  }
+
+  if (!r.empty()) {
+    r.sort(lessthan);
+    for (r.mark_cycle_pt(); !r.cycled_list(); r.forward()) {
+      x = r.data();
+    }
+    for (r.mark_cycle_pt(); !r.cycled_list(); r.forward()) {
+      r.data()->set_y(r.data_relative(1)->x() - r.data()->x());
+      r.forward();
+      delete (r.extract());
+    }
+  }
+
+  return result;
+}
+
+int lessthan(const void *first, const void *second) {
+  const ICOORDELT *p1 = *reinterpret_cast<const ICOORDELT *const *>(first);
+  const ICOORDELT *p2 = *reinterpret_cast<const ICOORDELT *const *>(second);
+
+  if (p1->x() < p2->x()) {
+    return (-1);
+  } else if (p1->x() > p2->x()) {
+    return (1);
+  } else {
+    return (0);
+  }
+}
+
+#ifndef GRAPHICS_DISABLED
+/// Returns a color to draw the given type.
+ScrollView::Color POLY_BLOCK::ColorForPolyBlockType(PolyBlockType type) {
+  // Keep kPBColors in sync with PolyBlockType.
+  const ScrollView::Color kPBColors[PT_COUNT] = {
+      ScrollView::WHITE,       // Type is not yet known. Keep as the 1st element.
+      ScrollView::BLUE,        // Text that lives inside a column.
+      ScrollView::CYAN,        // Text that spans more than one column.
+      ScrollView::MEDIUM_BLUE, // Text that is in a cross-column pull-out
+                               // region.
+      ScrollView::AQUAMARINE,  // Partition belonging to an equation region.
+      ScrollView::SKY_BLUE,    // Partition belonging to an inline equation
+                               // region.
+      ScrollView::MAGENTA,     // Partition belonging to a table region.
+      ScrollView::GREEN,       // Text-line runs vertically.
+      ScrollView::LIGHT_BLUE,  // Text that belongs to an image.
+      ScrollView::RED,         // Image that lives inside a column.
+      ScrollView::YELLOW,      // Image that spans more than one column.
+      ScrollView::ORANGE,      // Image in a cross-column pull-out region.
+      ScrollView::BROWN,       // Horizontal Line.
+      ScrollView::DARK_GREEN,  // Vertical Line.
+      ScrollView::GREY         // Lies outside of any column.
+  };
+  if (type < PT_COUNT) {
+    return kPBColors[type];
+  }
+  return ScrollView::WHITE;
+}
+#endif // !GRAPHICS_DISABLED
+
+} // namespace tesseract