Coverage Report

Created: 2025-11-16 06:50

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/tesseract/src/ccstruct/quspline.cpp
Line
Count
Source
1
/**********************************************************************
2
 * File:        quspline.cpp  (Formerly qspline.c)
3
 * Description: Code for the QSPLINE 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 "quspline.h"
25
26
#include "points.h"   // for ICOORD
27
#include "quadlsq.h"  // for QLSQ
28
#include "quadratc.h" // for QUAD_COEFFS
29
30
#include <allheaders.h> // for pixRenderPolyline, pixGetDepth, pixGetHeight
31
#include "pix.h"        // for L_CLEAR_PIXELS, L_SET_PIXELS, Pix (ptr only)
32
33
namespace tesseract {
34
35
0
#define QSPLINE_PRECISION 16 // no of steps to draw
36
37
/**********************************************************************
38
 * QSPLINE::QSPLINE
39
 *
40
 * Constructor to build a QSPLINE given the components used in the old code.
41
 **********************************************************************/
42
43
QSPLINE::QSPLINE(     // constructor
44
    int32_t count,    // no of segments
45
    int32_t *xstarts, // start coords
46
    double *coeffs    // coefficients
47
319k
) {
48
319k
  int32_t index; // segment index
49
50
  // get memory
51
319k
  xcoords = new int32_t[count + 1];
52
319k
  quadratics = new QUAD_COEFFS[count];
53
319k
  segments = count;
54
915k
  for (index = 0; index < segments; index++) {
55
    // copy them
56
595k
    xcoords[index] = xstarts[index];
57
595k
    quadratics[index] =
58
595k
        QUAD_COEFFS(coeffs[index * 3], coeffs[index * 3 + 1], coeffs[index * 3 + 2]);
59
595k
  }
60
  // right edge
61
319k
  xcoords[index] = xstarts[index];
62
319k
}
63
64
/**********************************************************************
65
 * QSPLINE::QSPLINE
66
 *
67
 * Constructor to build a QSPLINE by appproximation of points.
68
 **********************************************************************/
69
70
QSPLINE::QSPLINE(               // constructor
71
    int xstarts[],              // spline boundaries
72
    int segcount,               // no of segments
73
    int xpts[],                 // points to fit
74
    int ypts[], int pointcount, // no of pts
75
    int degree                  // fit required
76
138k
) {
77
138k
  int pointindex;    /*no along text line */
78
138k
  int segment;       /*segment no */
79
138k
  int32_t *ptcounts; // no in each segment
80
138k
  QLSQ qlsq;         /*accumulator */
81
82
138k
  segments = segcount;
83
138k
  xcoords = new int32_t[segcount + 1];
84
138k
  ptcounts = new int32_t[segcount + 1];
85
138k
  quadratics = new QUAD_COEFFS[segcount];
86
138k
  memmove(xcoords, xstarts, (segcount + 1) * sizeof(int32_t));
87
138k
  ptcounts[0] = 0; /*none in any yet */
88
1.27M
  for (segment = 0, pointindex = 0; pointindex < pointcount; pointindex++) {
89
1.39M
    while (segment < segcount && xpts[pointindex] >= xstarts[segment]) {
90
265k
      segment++; /*try next segment */
91
                 /*cumulative counts */
92
265k
      ptcounts[segment] = ptcounts[segment - 1];
93
265k
    }
94
1.13M
    ptcounts[segment]++; /*no in previous partition */
95
1.13M
  }
96
138k
  while (segment < segcount) {
97
0
    segment++;
98
    /*zero the rest */
99
0
    ptcounts[segment] = ptcounts[segment - 1];
100
0
  }
101
102
403k
  for (segment = 0; segment < segcount; segment++) {
103
265k
    qlsq.clear();
104
    /*first blob */
105
265k
    pointindex = ptcounts[segment];
106
265k
    if (pointindex > 0 && xpts[pointindex] != xpts[pointindex - 1] &&
107
127k
        xpts[pointindex] != xstarts[segment]) {
108
111k
      qlsq.add(xstarts[segment],
109
111k
               ypts[pointindex - 1] + (ypts[pointindex] - ypts[pointindex - 1]) *
110
111k
                                          (xstarts[segment] - xpts[pointindex - 1]) /
111
111k
                                          (xpts[pointindex] - xpts[pointindex - 1]));
112
111k
    }
113
1.39M
    for (; pointindex < ptcounts[segment + 1]; pointindex++) {
114
1.13M
      qlsq.add(xpts[pointindex], ypts[pointindex]);
115
1.13M
    }
116
265k
    if (pointindex > 0 && pointindex < pointcount && xpts[pointindex] != xstarts[segment + 1]) {
117
111k
      qlsq.add(xstarts[segment + 1],
118
111k
               ypts[pointindex - 1] + (ypts[pointindex] - ypts[pointindex - 1]) *
119
111k
                                          (xstarts[segment + 1] - xpts[pointindex - 1]) /
120
111k
                                          (xpts[pointindex] - xpts[pointindex - 1]));
121
111k
    }
122
265k
    qlsq.fit(degree);
123
265k
    quadratics[segment].a = qlsq.get_a();
124
265k
    quadratics[segment].b = qlsq.get_b();
125
265k
    quadratics[segment].c = qlsq.get_c();
126
265k
  }
127
138k
  delete[] ptcounts;
128
138k
}
129
130
/**********************************************************************
131
 * QSPLINE::QSPLINE
132
 *
133
 * Constructor to build a QSPLINE from another.
134
 **********************************************************************/
135
136
QSPLINE::QSPLINE( // constructor
137
0
    const QSPLINE &src) {
138
0
  segments = 0;
139
0
  xcoords = nullptr;
140
0
  quadratics = nullptr;
141
0
  *this = src;
142
0
}
143
144
/**********************************************************************
145
 * QSPLINE::~QSPLINE
146
 *
147
 * Destroy a QSPLINE.
148
 **********************************************************************/
149
150
915k
QSPLINE::~QSPLINE() {
151
915k
  delete[] xcoords;
152
915k
  delete[] quadratics;
153
915k
}
154
155
/**********************************************************************
156
 * QSPLINE::operator=
157
 *
158
 * Copy a QSPLINE
159
 **********************************************************************/
160
161
QSPLINE &QSPLINE::operator=( // assignment
162
639k
    const QSPLINE &source) {
163
639k
  delete[] xcoords;
164
639k
  delete[] quadratics;
165
166
639k
  segments = source.segments;
167
639k
  xcoords = new int32_t[segments + 1];
168
639k
  quadratics = new QUAD_COEFFS[segments];
169
639k
  memmove(xcoords, source.xcoords, (segments + 1) * sizeof(int32_t));
170
639k
  memmove(quadratics, source.quadratics, segments * sizeof(QUAD_COEFFS));
171
639k
  return *this;
172
639k
}
173
174
/**********************************************************************
175
 * QSPLINE::step
176
 *
177
 * Return the total of the step functions between the given coords.
178
 **********************************************************************/
179
180
double QSPLINE::step( // find step functions
181
    double x1,        // between coords
182
1.61M
    double x2) {
183
1.61M
  int index1, index2; // indices of coords
184
1.61M
  double total;       /*total steps */
185
186
1.61M
  index1 = spline_index(x1);
187
1.61M
  index2 = spline_index(x2);
188
1.61M
  total = 0;
189
1.72M
  while (index1 < index2) {
190
109k
    total += static_cast<double>(quadratics[index1 + 1].y(static_cast<float>(xcoords[index1 + 1])));
191
109k
    total -= static_cast<double>(quadratics[index1].y(static_cast<float>(xcoords[index1 + 1])));
192
109k
    index1++; /*next segment */
193
109k
  }
194
1.61M
  return total; /*total steps */
195
1.61M
}
196
197
/**********************************************************************
198
 * QSPLINE::y
199
 *
200
 * Return the y value at the given x value.
201
 **********************************************************************/
202
203
double QSPLINE::y( // evaluate
204
    double x       // coord to evaluate at
205
22.3M
    ) const {
206
22.3M
  int32_t index; // segment index
207
208
22.3M
  index = spline_index(x);
209
22.3M
  return quadratics[index].y(x); // in correct segment
210
22.3M
}
211
212
/**********************************************************************
213
 * QSPLINE::spline_index
214
 *
215
 * Return the index to the largest xcoord not greater than x.
216
 **********************************************************************/
217
218
int32_t QSPLINE::spline_index( // evaluate
219
    double x                   // coord to evaluate at
220
25.6M
    ) const {
221
25.6M
  int32_t index;  // segment index
222
25.6M
  int32_t bottom; // bottom of range
223
25.6M
  int32_t top;    // top of range
224
225
25.6M
  bottom = 0;
226
25.6M
  top = segments;
227
67.7M
  while (top - bottom > 1) {
228
42.1M
    index = (top + bottom) / 2; // centre of range
229
42.1M
    if (x >= xcoords[index]) {
230
22.0M
      bottom = index; // new min
231
22.0M
    } else {
232
20.0M
      top = index; // new max
233
20.0M
    }
234
42.1M
  }
235
25.6M
  return bottom;
236
25.6M
}
237
238
/**********************************************************************
239
 * QSPLINE::move
240
 *
241
 * Reposition spline by vector
242
 **********************************************************************/
243
244
void QSPLINE::move( // reposition spline
245
    ICOORD vec      // by vector
246
3.09k
) {
247
3.09k
  int32_t segment; // index of segment
248
3.09k
  int16_t x_shift = vec.x();
249
250
18.4k
  for (segment = 0; segment < segments; segment++) {
251
15.3k
    xcoords[segment] += x_shift;
252
15.3k
    quadratics[segment].move(vec);
253
15.3k
  }
254
3.09k
  xcoords[segment] += x_shift;
255
3.09k
}
256
257
/**********************************************************************
258
 * QSPLINE::overlap
259
 *
260
 * Return true if spline2 overlaps this by no more than fraction less
261
 * than the bounds of this.
262
 **********************************************************************/
263
264
bool QSPLINE::overlap( // test overlap
265
    QSPLINE *spline2,  // 2 cannot be smaller
266
    double fraction    // by more than this
267
0
) {
268
0
  int leftlimit = xcoords[1];             /*common left limit */
269
0
  int rightlimit = xcoords[segments - 1]; /*common right limit */
270
                                          /*or too non-overlap */
271
0
  return !(spline2->segments < 3 ||
272
0
           spline2->xcoords[1] > leftlimit + fraction * (rightlimit - leftlimit) ||
273
0
           spline2->xcoords[spline2->segments - 1] <
274
0
               rightlimit - fraction * (rightlimit - leftlimit));
275
0
}
276
277
/**********************************************************************
278
 * extrapolate_spline
279
 *
280
 * Extrapolates the spline linearly using the same gradient as the
281
 * quadratic has at either end.
282
 **********************************************************************/
283
284
void QSPLINE::extrapolate( // linear extrapolation
285
    double gradient,       // gradient to use
286
    int xmin,              // new left edge
287
    int xmax               // new right edge
288
195k
) {
289
195k
  int segment;        /*current segment of spline */
290
195k
  int dest_segment;   // dest index
291
195k
  int32_t *xstarts;   // new boundaries
292
195k
  QUAD_COEFFS *quads; // new ones
293
195k
  int increment;      // in size
294
295
195k
  increment = xmin < xcoords[0] ? 1 : 0;
296
195k
  if (xmax > xcoords[segments]) {
297
189k
    increment++;
298
189k
  }
299
195k
  if (increment == 0) {
300
5.97k
    return;
301
5.97k
  }
302
190k
  xstarts = new int32_t[segments + 1 + increment];
303
190k
  quads = new QUAD_COEFFS[segments + increment];
304
190k
  if (xmin < xcoords[0]) {
305
117k
    xstarts[0] = xmin;
306
117k
    quads[0].a = 0;
307
117k
    quads[0].b = gradient;
308
117k
    quads[0].c = y(xcoords[0]) - quads[0].b * xcoords[0];
309
117k
    dest_segment = 1;
310
117k
  } else {
311
72.3k
    dest_segment = 0;
312
72.3k
  }
313
501k
  for (segment = 0; segment < segments; segment++) {
314
311k
    xstarts[dest_segment] = xcoords[segment];
315
311k
    quads[dest_segment] = quadratics[segment];
316
311k
    dest_segment++;
317
311k
  }
318
190k
  xstarts[dest_segment] = xcoords[segment];
319
190k
  if (xmax > xcoords[segments]) {
320
189k
    quads[dest_segment].a = 0;
321
189k
    quads[dest_segment].b = gradient;
322
189k
    quads[dest_segment].c = y(xcoords[segments]) - quads[dest_segment].b * xcoords[segments];
323
189k
    dest_segment++;
324
189k
    xstarts[dest_segment] = xmax + 1;
325
189k
  }
326
190k
  segments = dest_segment;
327
190k
  delete[] xcoords;
328
190k
  delete[] quadratics;
329
190k
  xcoords = xstarts;
330
190k
  quadratics = quads;
331
190k
}
332
333
/**********************************************************************
334
 * QSPLINE::plot
335
 *
336
 * Draw the QSPLINE in the given colour.
337
 **********************************************************************/
338
339
#ifndef GRAPHICS_DISABLED
340
void QSPLINE::plot(          // draw it
341
    ScrollView *window,      // window to draw in
342
    ScrollView::Color colour // colour to draw in
343
    ) const {
344
  int32_t segment;  // index of segment
345
  int16_t step;     // index of poly piece
346
  double increment; // x increment
347
  double x;         // x coord
348
349
  window->Pen(colour);
350
  for (segment = 0; segment < segments; segment++) {
351
    increment = static_cast<double>(xcoords[segment + 1] - xcoords[segment]) / QSPLINE_PRECISION;
352
    x = xcoords[segment];
353
    for (step = 0; step <= QSPLINE_PRECISION; step++) {
354
      if (segment == 0 && step == 0) {
355
        window->SetCursor(x, quadratics[segment].y(x));
356
      } else {
357
        window->DrawTo(x, quadratics[segment].y(x));
358
      }
359
      x += increment;
360
    }
361
  }
362
}
363
#endif
364
365
0
void QSPLINE::plot(Image pix) const {
366
0
  if (pix == nullptr) {
367
0
    return;
368
0
  }
369
370
0
  int32_t segment;  // Index of segment
371
0
  int16_t step;     // Index of poly piece
372
0
  double increment; // x increment
373
0
  double x;         // x coord
374
0
  auto height = static_cast<double>(pixGetHeight(pix));
375
0
  Pta *points = ptaCreate(QSPLINE_PRECISION * segments);
376
0
  const int kLineWidth = 5;
377
378
0
  for (segment = 0; segment < segments; segment++) {
379
0
    increment = static_cast<double>((xcoords[segment + 1] - xcoords[segment])) / QSPLINE_PRECISION;
380
0
    x = xcoords[segment];
381
0
    for (step = 0; step <= QSPLINE_PRECISION; step++) {
382
0
      double y = height - quadratics[segment].y(x);
383
0
      ptaAddPt(points, x, y);
384
0
      x += increment;
385
0
    }
386
0
  }
387
388
0
  switch (pixGetDepth(pix)) {
389
0
    case 1:
390
0
      pixRenderPolyline(pix, points, kLineWidth, L_SET_PIXELS, 1);
391
0
      break;
392
0
    case 32:
393
0
      pixRenderPolylineArb(pix, points, kLineWidth, 255, 0, 0, 1);
394
0
      break;
395
0
    default:
396
0
      pixRenderPolyline(pix, points, kLineWidth, L_CLEAR_PIXELS, 1);
397
0
      break;
398
0
  }
399
0
  ptaDestroy(&points);
400
0
}
401
402
} // namespace tesseract