Coverage Report

Created: 2024-07-27 06:27

/src/leptonica/src/graphics.c
Line
Count
Source (jump to first uncovered line)
1
/*====================================================================*
2
 -  Copyright (C) 2001 Leptonica.  All rights reserved.
3
 -
4
 -  Redistribution and use in source and binary forms, with or without
5
 -  modification, are permitted provided that the following conditions
6
 -  are met:
7
 -  1. Redistributions of source code must retain the above copyright
8
 -     notice, this list of conditions and the following disclaimer.
9
 -  2. Redistributions in binary form must reproduce the above
10
 -     copyright notice, this list of conditions and the following
11
 -     disclaimer in the documentation and/or other materials
12
 -     provided with the distribution.
13
 -
14
 -  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
15
 -  ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
16
 -  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
17
 -  A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL ANY
18
 -  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19
 -  EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20
 -  PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
21
 -  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
22
 -  OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
23
 -  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
24
 -  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25
 *====================================================================*/
26
27
/*!
28
 * \file graphics.c
29
 * <pre>
30
 *
31
 *      Pta generation for arbitrary shapes built with lines
32
 *          PTA        *generatePtaLine()
33
 *          PTA        *generatePtaWideLine()
34
 *          PTA        *generatePtaBox()
35
 *          PTA        *generatePtaBoxa()
36
 *          PTA        *generatePtaHashBox()
37
 *          PTA        *generatePtaHashBoxa()
38
 *          PTAA       *generatePtaaBoxa()
39
 *          PTAA       *generatePtaaHashBoxa()
40
 *          PTA        *generatePtaPolyline()
41
 *          PTA        *generatePtaGrid()
42
 *          PTA        *convertPtaLineTo4cc()
43
 *          PTA        *generatePtaFilledCircle()
44
 *          PTA        *generatePtaFilledSquare()
45
 *          PTA        *generatePtaLineFromPt()
46
 *          l_int32     locatePtRadially()
47
 *
48
 *      Rendering function plots directly on images
49
 *          l_int32     pixRenderPlotFromNuma()
50
 *          l_int32     pixRenderPlotFromNumaGen()
51
 *          PTA        *makePlotPtaFromNuma()
52
 *          PTA        *makePlotPtaFromNumaGen()
53
 *
54
 *      Pta rendering
55
 *          l_int32     pixRenderPta()
56
 *          l_int32     pixRenderPtaArb()
57
 *          l_int32     pixRenderPtaBlend()
58
 *
59
 *      Rendering of arbitrary shapes built with lines
60
 *          l_int32     pixRenderLine()
61
 *          l_int32     pixRenderLineArb()
62
 *          l_int32     pixRenderLineBlend()
63
 *
64
 *          l_int32     pixRenderBox()
65
 *          l_int32     pixRenderBoxArb()
66
 *          l_int32     pixRenderBoxBlend()
67
 *
68
 *          l_int32     pixRenderBoxa()
69
 *          l_int32     pixRenderBoxaArb()
70
 *          l_int32     pixRenderBoxaBlend()
71
 *
72
 *          l_int32     pixRenderHashBox()
73
 *          l_int32     pixRenderHashBoxArb()
74
 *          l_int32     pixRenderHashBoxBlend()
75
 *          l_int32     pixRenderHashMaskArb()
76
 *
77
 *          l_int32     pixRenderHashBoxa()
78
 *          l_int32     pixRenderHashBoxaArb()
79
 *          l_int32     pixRenderHashBoxaBlend()
80
 *
81
 *          l_int32     pixRenderPolyline()
82
 *          l_int32     pixRenderPolylineArb()
83
 *          l_int32     pixRenderPolylineBlend()
84
 *
85
 *          l_int32     pixRenderGrid()
86
 *
87
 *          l_int32     pixRenderRandomCmapPtaa()
88
 *
89
 *      Rendering and filling of polygons
90
 *          PIX        *pixRenderPolygon()
91
 *          PIX        *pixFillPolygon()
92
 *
93
 *      Contour rendering on grayscale images
94
 *          PIX        *pixRenderContours()
95
 *          PIX        *fpixAutoRenderContours()
96
 *          PIX        *fpixRenderContours()
97
 *
98
 *      Boundary pt generation on 1 bpp images
99
 *          PTA        *pixGeneratePtaBoundary()
100
 *
101
 *  The line rendering functions are relatively crude, but they
102
 *  get the job done for most simple situations.  We use the pta
103
 *  (array of points) as an intermediate data structure.  For example,
104
 *  to render a line we first generate a pta.
105
 *
106
 *  Some rendering functions come in sets of three.  For example
107
 *       pixRenderLine() -- render on 1 bpp pix
108
 *       pixRenderLineArb() -- render on 32 bpp pix with arbitrary (r,g,b)
109
 *       pixRenderLineBlend() -- render on 32 bpp pix, blending the
110
 *               (r,g,b) graphic object with the underlying rgb pixels.
111
 *
112
 *  There are also procedures for plotting a function, computed
113
 *  from the row or column pixels, directly on the image.
114
 * </pre>
115
 */
116
117
#ifdef HAVE_CONFIG_H
118
#include <config_auto.h>
119
#endif  /* HAVE_CONFIG_H */
120
121
#include <string.h>
122
#include <math.h>
123
#include "allheaders.h"
124
125
/*------------------------------------------------------------------*
126
 *        Pta generation for arbitrary shapes built with lines      *
127
 *------------------------------------------------------------------*/
128
/*!
129
 * \brief   generatePtaLine()
130
 *
131
 * \param[in]    x1, y1    end point 1
132
 * \param[in]    x2, y2    end point 2
133
 * \return  pta, or NULL on error
134
 *
135
 * <pre>
136
 * Notes:
137
 *      (1) Uses Bresenham line drawing, which results in an 8-connected line.
138
 * </pre>
139
 */
140
PTA  *
141
generatePtaLine(l_int32  x1,
142
                l_int32  y1,
143
                l_int32  x2,
144
                l_int32  y2)
145
0
{
146
0
l_int32    npts, diff, getyofx, sign, i, x, y;
147
0
l_float32  slope;
148
0
PTA       *pta;
149
150
        /* Generate line parameters */
151
0
    if (x1 == x2 && y1 == y2) {  /* same point */
152
0
        getyofx = TRUE;
153
0
        npts = 1;
154
0
    } else if (L_ABS(x2 - x1) >= L_ABS(y2 - y1)) {
155
0
        getyofx = TRUE;
156
0
        npts = L_ABS(x2 - x1) + 1;
157
0
        diff = x2 - x1;
158
0
        sign = L_SIGN(x2 - x1);
159
0
        slope = (l_float32)(sign * (y2 - y1)) / (l_float32)diff;
160
0
    } else {
161
0
        getyofx = FALSE;
162
0
        npts = L_ABS(y2 - y1) + 1;
163
0
        diff = y2 - y1;
164
0
        sign = L_SIGN(y2 - y1);
165
0
        slope = (l_float32)(sign * (x2 - x1)) / (l_float32)diff;
166
0
    }
167
168
0
    if ((pta = ptaCreate(npts)) == NULL)
169
0
        return (PTA *)ERROR_PTR("pta not made", __func__, NULL);
170
171
0
    if (npts == 1) {  /* degenerate case */
172
0
        ptaAddPt(pta, x1, y1);
173
0
        return pta;
174
0
    }
175
176
        /* Generate the set of points */
177
0
    if (getyofx) {  /* y = y(x) */
178
0
        for (i = 0; i < npts; i++) {
179
0
            x = x1 + sign * i;
180
0
            y = (l_int32)(y1 + (l_float32)i * slope + 0.5);
181
0
            ptaAddPt(pta, x, y);
182
0
        }
183
0
    } else {   /* x = x(y) */
184
0
        for (i = 0; i < npts; i++) {
185
0
            x = (l_int32)(x1 + (l_float32)i * slope + 0.5);
186
0
            y = y1 + sign * i;
187
0
            ptaAddPt(pta, x, y);
188
0
        }
189
0
    }
190
191
0
    return pta;
192
0
}
193
194
195
/*!
196
 * \brief   generatePtaWideLine()
197
 *
198
 * \param[in]    x1, y1     end point 1
199
 * \param[in]    x2, y2     end point 2
200
 * \param[in]    width
201
 * \return  ptaj, or NULL on error
202
 */
203
PTA  *
204
generatePtaWideLine(l_int32  x1,
205
                    l_int32  y1,
206
                    l_int32  x2,
207
                    l_int32  y2,
208
                    l_int32  width)
209
0
{
210
0
l_int32  i, x1a, x2a, y1a, y2a;
211
0
PTA     *pta, *ptaj;
212
213
0
    if (width < 1) {
214
0
        L_WARNING("width < 1; setting to 1\n", __func__);
215
0
        width = 1;
216
0
    }
217
218
0
    if ((ptaj = generatePtaLine(x1, y1, x2, y2)) == NULL)
219
0
        return (PTA *)ERROR_PTR("ptaj not made", __func__, NULL);
220
0
    if (width == 1)
221
0
        return ptaj;
222
223
        /* width > 1; estimate line direction & join */
224
0
    if (L_ABS(x1 - x2) > L_ABS(y1 - y2)) {  /* "horizontal" line  */
225
0
        for (i = 1; i < width; i++) {
226
0
            if ((i & 1) == 1) {   /* place above */
227
0
                y1a = y1 - (i + 1) / 2;
228
0
                y2a = y2 - (i + 1) / 2;
229
0
            } else {  /* place below */
230
0
                y1a = y1 + (i + 1) / 2;
231
0
                y2a = y2 + (i + 1) / 2;
232
0
            }
233
0
            if ((pta = generatePtaLine(x1, y1a, x2, y2a)) != NULL) {
234
0
                ptaJoin(ptaj, pta, 0, -1);
235
0
                ptaDestroy(&pta);
236
0
            }
237
0
        }
238
0
    } else  {  /* "vertical" line  */
239
0
        for (i = 1; i < width; i++) {
240
0
            if ((i & 1) == 1) {   /* place to left */
241
0
                x1a = x1 - (i + 1) / 2;
242
0
                x2a = x2 - (i + 1) / 2;
243
0
            } else {  /* place to right */
244
0
                x1a = x1 + (i + 1) / 2;
245
0
                x2a = x2 + (i + 1) / 2;
246
0
            }
247
0
            if ((pta = generatePtaLine(x1a, y1, x2a, y2)) != NULL) {
248
0
                ptaJoin(ptaj, pta, 0, -1);
249
0
                ptaDestroy(&pta);
250
0
            }
251
0
        }
252
0
    }
253
254
0
    return ptaj;
255
0
}
256
257
258
/*!
259
 * \brief   generatePtaBox()
260
 *
261
 * \param[in]    box
262
 * \param[in]    width    of line
263
 * \return  ptad, or NULL on error
264
 *
265
 * <pre>
266
 * Notes:
267
 *      (1) Because the box is constructed so that we don't have any
268
 *          overlapping lines, there is no need to remove duplicates.
269
 * </pre>
270
 */
271
PTA  *
272
generatePtaBox(BOX     *box,
273
               l_int32  width)
274
0
{
275
0
l_int32  x, y, w, h;
276
0
PTA     *ptad, *pta;
277
278
0
    if (!box)
279
0
        return (PTA *)ERROR_PTR("box not defined", __func__, NULL);
280
0
    if (width < 1) {
281
0
        L_WARNING("width < 1; setting to 1\n", __func__);
282
0
        width = 1;
283
0
    }
284
285
        /* Generate line points and add them to the pta. */
286
0
    boxGetGeometry(box, &x, &y, &w, &h);
287
0
    if (w == 0 || h == 0)
288
0
        return (PTA *)ERROR_PTR("box has w = 0 or h = 0", __func__, NULL);
289
0
    ptad = ptaCreate(0);
290
0
    if ((width & 1) == 1) {   /* odd width */
291
0
        pta = generatePtaWideLine(x - width / 2, y,
292
0
                                  x + w - 1 + width / 2, y, width);
293
0
        ptaJoin(ptad, pta, 0, -1);
294
0
        ptaDestroy(&pta);
295
0
        pta = generatePtaWideLine(x + w - 1, y + 1 + width / 2,
296
0
                                  x + w - 1, y + h - 2 - width / 2, width);
297
0
        ptaJoin(ptad, pta, 0, -1);
298
0
        ptaDestroy(&pta);
299
0
        pta = generatePtaWideLine(x + w - 1 + width / 2, y + h - 1,
300
0
                                  x - width / 2, y + h - 1, width);
301
0
        ptaJoin(ptad, pta, 0, -1);
302
0
        ptaDestroy(&pta);
303
0
        pta = generatePtaWideLine(x, y + h - 2 - width / 2,
304
0
                                  x, y + 1 + width / 2, width);
305
0
        ptaJoin(ptad, pta, 0, -1);
306
0
        ptaDestroy(&pta);
307
0
    } else {   /* even width */
308
0
        pta = generatePtaWideLine(x - width / 2, y,
309
0
                                  x + w - 2 + width / 2, y, width);
310
0
        ptaJoin(ptad, pta, 0, -1);
311
0
        ptaDestroy(&pta);
312
0
        pta = generatePtaWideLine(x + w - 1, y + 0 + width / 2,
313
0
                                  x + w - 1, y + h - 2 - width / 2, width);
314
0
        ptaJoin(ptad, pta, 0, -1);
315
0
        ptaDestroy(&pta);
316
0
        pta = generatePtaWideLine(x + w - 2 + width / 2, y + h - 1,
317
0
                                  x - width / 2, y + h - 1, width);
318
0
        ptaJoin(ptad, pta, 0, -1);
319
0
        ptaDestroy(&pta);
320
0
        pta = generatePtaWideLine(x, y + h - 2 - width / 2,
321
0
                                  x, y + 0 + width / 2, width);
322
0
        ptaJoin(ptad, pta, 0, -1);
323
0
        ptaDestroy(&pta);
324
0
    }
325
326
0
    return ptad;
327
0
}
328
329
330
/*!
331
 * \brief   generatePtaBoxa()
332
 *
333
 * \param[in]    boxa
334
 * \param[in]    width
335
 * \param[in]    removedups    1 to remove, 0 to leave
336
 * \return  ptad, or NULL on error
337
 *
338
 * <pre>
339
 * Notes:
340
 *      (1) If %boxa has overlapping boxes, and if blending will
341
 *          be used to give a transparent effect, transparency
342
 *          artifacts at line intersections can be removed using
343
 *          %removedups = 1.
344
 * </pre>
345
 */
346
PTA  *
347
generatePtaBoxa(BOXA    *boxa,
348
                l_int32  width,
349
                l_int32  removedups)
350
0
{
351
0
l_int32  i, n;
352
0
BOX     *box;
353
0
PTA     *ptad, *ptat, *pta;
354
355
0
    if (!boxa)
356
0
        return (PTA *)ERROR_PTR("boxa not defined", __func__, NULL);
357
0
    if (width < 1) {
358
0
        L_WARNING("width < 1; setting to 1\n", __func__);
359
0
        width = 1;
360
0
    }
361
362
0
    n = boxaGetCount(boxa);
363
0
    ptat = ptaCreate(0);
364
0
    for (i = 0; i < n; i++) {
365
0
        box = boxaGetBox(boxa, i, L_CLONE);
366
0
        pta = generatePtaBox(box, width);
367
0
        ptaJoin(ptat, pta, 0, -1);
368
0
        ptaDestroy(&pta);
369
0
        boxDestroy(&box);
370
0
    }
371
372
0
    if (removedups)
373
0
        ptaRemoveDupsByAset(ptat, &ptad);
374
0
    else
375
0
        ptad = ptaClone(ptat);
376
377
0
    ptaDestroy(&ptat);
378
0
    return ptad;
379
0
}
380
381
382
/*!
383
 * \brief   generatePtaHashBox()
384
 *
385
 * \param[in]    box
386
 * \param[in]    spacing    spacing between lines; must be > 1
387
 * \param[in]    width      of line
388
 * \param[in]    orient     orientation of lines: L_HORIZONTAL_LINE,
389
 *                             L_POS_SLOPE_LINE, L_VERTICAL_LINE,
390
 *                             L_NEG_SLOPE_LINE
391
 * \param[in]    outline    0 to skip drawing box outline
392
 * \return  ptad, or NULL on error
393
 *
394
 * <pre>
395
 * Notes:
396
 *      (1) The orientation takes on one of 4 orientations (horiz, vertical,
397
 *          slope +1, slope -1).
398
 *      (2) The full outline is also drawn if %outline = 1.
399
 * </pre>
400
 */
401
PTA  *
402
generatePtaHashBox(BOX     *box,
403
                   l_int32  spacing,
404
                   l_int32  width,
405
                   l_int32  orient,
406
                   l_int32  outline)
407
0
{
408
0
l_int32  bx, by, bh, bw, x, y, x1, y1, x2, y2, i, n, npts;
409
0
PTA     *ptad, *pta;
410
411
0
    if (!box)
412
0
        return (PTA *)ERROR_PTR("box not defined", __func__, NULL);
413
0
    if (spacing <= 1)
414
0
        return (PTA *)ERROR_PTR("spacing not > 1", __func__, NULL);
415
0
    if (orient != L_HORIZONTAL_LINE && orient != L_POS_SLOPE_LINE &&
416
0
        orient != L_VERTICAL_LINE && orient != L_NEG_SLOPE_LINE)
417
0
        return (PTA *)ERROR_PTR("invalid line orientation", __func__, NULL);
418
0
    boxGetGeometry(box, &bx, &by, &bw, &bh);
419
0
    if (bw == 0 || bh == 0)
420
0
        return (PTA *)ERROR_PTR("box has bw = 0 or bh = 0", __func__, NULL);
421
0
    if (width < 1) {
422
0
        L_WARNING("width < 1; setting to 1\n", __func__);
423
0
        width = 1;
424
0
    }
425
426
        /* Generate line points and add them to the pta. */
427
0
    ptad = ptaCreate(0);
428
0
    if (outline) {
429
0
        pta = generatePtaBox(box, width);
430
0
        ptaJoin(ptad, pta, 0, -1);
431
0
        ptaDestroy(&pta);
432
0
    }
433
0
    if (orient == L_HORIZONTAL_LINE) {
434
0
        n = 1 + bh / spacing;
435
0
        for (i = 0; i < n; i++) {
436
0
            y = by + (i * (bh - 1)) / (n - 1);
437
0
            pta = generatePtaWideLine(bx, y, bx + bw - 1, y, width);
438
0
            ptaJoin(ptad, pta, 0, -1);
439
0
            ptaDestroy(&pta);
440
0
        }
441
0
    } else if (orient == L_VERTICAL_LINE) {
442
0
        n = 1 + bw / spacing;
443
0
        for (i = 0; i < n; i++) {
444
0
            x = bx + (i * (bw - 1)) / (n - 1);
445
0
            pta = generatePtaWideLine(x, by, x, by + bh - 1, width);
446
0
            ptaJoin(ptad, pta, 0, -1);
447
0
            ptaDestroy(&pta);
448
0
        }
449
0
    } else if (orient == L_POS_SLOPE_LINE) {
450
0
        n = 2 + (l_int32)((bw + bh) / (1.4 * spacing));
451
0
        for (i = 0; i < n; i++) {
452
0
            x = (l_int32)(bx + (i + 0.5) * 1.4 * spacing);
453
0
            boxIntersectByLine(box, x, by - 1, 1.0, &x1, &y1, &x2, &y2, &npts);
454
0
            if (npts == 2) {
455
0
                pta = generatePtaWideLine(x1, y1, x2, y2, width);
456
0
                ptaJoin(ptad, pta, 0, -1);
457
0
                ptaDestroy(&pta);
458
0
            }
459
0
        }
460
0
    } else {  /* orient == L_NEG_SLOPE_LINE */
461
0
        n = 2 + (l_int32)((bw + bh) / (1.4 * spacing));
462
0
        for (i = 0; i < n; i++) {
463
0
            x = (l_int32)(bx - bh + (i + 0.5) * 1.4 * spacing);
464
0
            boxIntersectByLine(box, x, by - 1, -1.0, &x1, &y1, &x2, &y2, &npts);
465
0
            if (npts == 2) {
466
0
                pta = generatePtaWideLine(x1, y1, x2, y2, width);
467
0
                ptaJoin(ptad, pta, 0, -1);
468
0
                ptaDestroy(&pta);
469
0
            }
470
0
        }
471
0
    }
472
473
0
    return ptad;
474
0
}
475
476
477
/*!
478
 * \brief   generatePtaHashBoxa()
479
 *
480
 * \param[in]    boxa
481
 * \param[in]    spacing     spacing between lines; must be > 1
482
 * \param[in]    width       of line
483
 * \param[in]    orient      orientation of lines: L_HORIZONTAL_LINE, ...
484
 * \param[in]    orient      orientation of lines: L_HORIZONTAL_LINE,
485
 *                              L_POS_SLOPE_LINE, L_VERTICAL_LINE,
486
 *                              L_NEG_SLOPE_LINE
487
 * \param[in]    outline     0 to skip drawing box outline
488
 * \param[in]    removedups  1 to remove, 0 to leave
489
 * \return  ptad, or NULL on error
490
 *
491
 * <pre>
492
 * Notes:
493
 *      (1) The orientation takes on one of 4 orientations (horiz, vertical,
494
 *          slope +1, slope -1).
495
 *      (2) The full outline is also drawn if %outline = 1.
496
 *      (3) If the boxa has overlapping boxes, and if blending will
497
 *          be used to give a transparent effect, transparency
498
 *          artifacts at line intersections can be removed using
499
 *          %removedups = 1.
500
 * </pre>
501
 */
502
PTA  *
503
generatePtaHashBoxa(BOXA    *boxa,
504
                    l_int32  spacing,
505
                    l_int32  width,
506
                    l_int32  orient,
507
                    l_int32  outline,
508
                    l_int32  removedups)
509
0
{
510
0
l_int32  i, n;
511
0
BOX     *box;
512
0
PTA     *ptad, *ptat, *pta;
513
514
0
    if (!boxa)
515
0
        return (PTA *)ERROR_PTR("boxa not defined", __func__, NULL);
516
0
    if (spacing <= 1)
517
0
        return (PTA *)ERROR_PTR("spacing not > 1", __func__, NULL);
518
0
    if (width < 1) {
519
0
        L_WARNING("width < 1; setting to 1\n", __func__);
520
0
        width = 1;
521
0
    }
522
0
    if (orient != L_HORIZONTAL_LINE && orient != L_POS_SLOPE_LINE &&
523
0
        orient != L_VERTICAL_LINE && orient != L_NEG_SLOPE_LINE)
524
0
        return (PTA *)ERROR_PTR("invalid line orientation", __func__, NULL);
525
526
0
    n = boxaGetCount(boxa);
527
0
    ptat = ptaCreate(0);
528
0
    for (i = 0; i < n; i++) {
529
0
        box = boxaGetBox(boxa, i, L_CLONE);
530
0
        pta = generatePtaHashBox(box, spacing, width, orient, outline);
531
0
        ptaJoin(ptat, pta, 0, -1);
532
0
        ptaDestroy(&pta);
533
0
        boxDestroy(&box);
534
0
    }
535
536
0
    if (removedups)
537
0
        ptaRemoveDupsByAset(ptat, &ptad);
538
0
    else
539
0
        ptad = ptaClone(ptat);
540
541
0
    ptaDestroy(&ptat);
542
0
    return ptad;
543
0
}
544
545
546
/*!
547
 * \brief   generatePtaaBoxa()
548
 *
549
 * \param[in]    boxa
550
 * \return  ptaa, or NULL on error
551
 *
552
 * <pre>
553
 * Notes:
554
 *      (1) This generates a pta of the four corners for each box in
555
 *          the boxa.
556
 *      (2) Each of these pta can be rendered onto a pix with random colors,
557
 *          by using pixRenderRandomCmapPtaa() with closeflag = 1.
558
 * </pre>
559
 */
560
PTAA  *
561
generatePtaaBoxa(BOXA  *boxa)
562
0
{
563
0
l_int32  i, n, x, y, w, h;
564
0
BOX     *box;
565
0
PTA     *pta;
566
0
PTAA    *ptaa;
567
568
0
    if (!boxa)
569
0
        return (PTAA *)ERROR_PTR("boxa not defined", __func__, NULL);
570
571
0
    n = boxaGetCount(boxa);
572
0
    ptaa = ptaaCreate(n);
573
0
    for (i = 0; i < n; i++) {
574
0
        box = boxaGetBox(boxa, i, L_CLONE);
575
0
        boxGetGeometry(box, &x, &y, &w, &h);
576
0
        pta = ptaCreate(4);
577
0
        ptaAddPt(pta, x, y);
578
0
        ptaAddPt(pta, x + w - 1, y);
579
0
        ptaAddPt(pta, x + w - 1, y + h - 1);
580
0
        ptaAddPt(pta, x, y + h - 1);
581
0
        ptaaAddPta(ptaa, pta, L_INSERT);
582
0
        boxDestroy(&box);
583
0
    }
584
585
0
    return ptaa;
586
0
}
587
588
589
/*!
590
 * \brief   generatePtaaHashBoxa()
591
 *
592
 * \param[in]    boxa
593
 * \param[in]    spacing     spacing between hash lines; must be > 1
594
 * \param[in]    width       hash line width
595
 * \param[in]    orient      orientation of lines: L_HORIZONTAL_LINE,
596
 *                              L_POS_SLOPE_LINE, L_VERTICAL_LINE,
597
 *                              L_NEG_SLOPE_LINE
598
 * \param[in]    outline     0 to skip drawing box outline
599
 * \return  ptaa, or NULL on error
600
 *
601
 * <pre>
602
 * Notes:
603
 *      (1) The orientation takes on one of 4 orientations (horiz, vertical,
604
 *          slope +1, slope -1).
605
 *      (2) The full outline is also drawn if %outline = 1.
606
 *      (3) Each of these pta can be rendered onto a pix with random colors,
607
 *          by using pixRenderRandomCmapPtaa() with closeflag = 1.
608
 *
609
 * </pre>
610
 */
611
PTAA *
612
generatePtaaHashBoxa(BOXA    *boxa,
613
                     l_int32  spacing,
614
                     l_int32  width,
615
                     l_int32  orient,
616
                     l_int32  outline)
617
0
{
618
0
l_int32  i, n;
619
0
BOX     *box;
620
0
PTA     *pta;
621
0
PTAA    *ptaa;
622
623
0
    if (!boxa)
624
0
        return (PTAA *)ERROR_PTR("boxa not defined", __func__, NULL);
625
0
    if (spacing <= 1)
626
0
        return (PTAA *)ERROR_PTR("spacing not > 1", __func__, NULL);
627
0
    if (width < 1) {
628
0
        L_WARNING("width < 1; setting to 1\n", __func__);
629
0
        width = 1;
630
0
    }
631
0
    if (orient != L_HORIZONTAL_LINE && orient != L_POS_SLOPE_LINE &&
632
0
        orient != L_VERTICAL_LINE && orient != L_NEG_SLOPE_LINE)
633
0
        return (PTAA *)ERROR_PTR("invalid line orientation", __func__, NULL);
634
635
0
    n = boxaGetCount(boxa);
636
0
    ptaa = ptaaCreate(n);
637
0
    for (i = 0; i < n; i++) {
638
0
        box = boxaGetBox(boxa, i, L_CLONE);
639
0
        pta = generatePtaHashBox(box, spacing, width, orient, outline);
640
0
        ptaaAddPta(ptaa, pta, L_INSERT);
641
0
        boxDestroy(&box);
642
0
    }
643
644
0
    return ptaa;
645
0
}
646
647
648
/*!
649
 * \brief   generatePtaPolyline()
650
 *
651
 * \param[in]    ptas         vertices of polyline
652
 * \param[in]    width
653
 * \param[in]    closeflag    1 to close the contour; 0 otherwise
654
 * \param[in]    removedups   1 to remove, 0 to leave
655
 * \return  ptad, or NULL on error
656
 */
657
PTA *
658
generatePtaPolyline(PTA     *ptas,
659
                    l_int32  width,
660
                    l_int32  closeflag,
661
                    l_int32  removedups)
662
0
{
663
0
l_int32  i, n, x1, y1, x2, y2;
664
0
PTA     *ptad, *ptat, *pta;
665
666
0
    if (!ptas)
667
0
        return (PTA *)ERROR_PTR("ptas not defined", __func__, NULL);
668
0
    if (width < 1) {
669
0
        L_WARNING("width < 1; setting to 1\n", __func__);
670
0
        width = 1;
671
0
    }
672
673
0
    n = ptaGetCount(ptas);
674
0
    ptat = ptaCreate(0);
675
0
    if (n < 2)  /* nothing to do */
676
0
        return ptat;
677
678
0
    ptaGetIPt(ptas, 0, &x1, &y1);
679
0
    for (i = 1; i < n; i++) {
680
0
        ptaGetIPt(ptas, i, &x2, &y2);
681
0
        pta = generatePtaWideLine(x1, y1, x2, y2, width);
682
0
        ptaJoin(ptat, pta, 0, -1);
683
0
        ptaDestroy(&pta);
684
0
        x1 = x2;
685
0
        y1 = y2;
686
0
    }
687
688
0
    if (closeflag) {
689
0
        ptaGetIPt(ptas, 0, &x2, &y2);
690
0
        pta = generatePtaWideLine(x1, y1, x2, y2, width);
691
0
        ptaJoin(ptat, pta, 0, -1);
692
0
        ptaDestroy(&pta);
693
0
    }
694
695
0
    if (removedups)
696
0
        ptaRemoveDupsByAset(ptat, &ptad);
697
0
    else
698
0
        ptad = ptaClone(ptat);
699
700
0
    ptaDestroy(&ptat);
701
0
    return ptad;
702
0
}
703
704
705
/*!
706
 * \brief   generatePtaGrid()
707
 *
708
 * \param[in]    w, h       of region where grid will be displayed
709
 * \param[in]    nx, ny     number of rectangles in each direction in grid
710
 * \param[in]    width      of rendered lines
711
 * \return  ptad, or NULL on error
712
 */
713
PTA  *
714
generatePtaGrid(l_int32  w,
715
                l_int32  h,
716
                l_int32  nx,
717
                l_int32  ny,
718
                l_int32  width)
719
0
{
720
0
l_int32  i, j, bx, by, x1, x2, y1, y2;
721
0
BOX     *box;
722
0
BOXA    *boxa;
723
0
PTA     *pta;
724
725
0
    if (nx < 1 || ny < 1)
726
0
        return (PTA *)ERROR_PTR("nx and ny must be > 0", __func__, NULL);
727
0
    if (w < 2 * nx || h < 2 * ny)
728
0
        return (PTA *)ERROR_PTR("w and/or h too small", __func__, NULL);
729
0
    if (width < 1) {
730
0
        L_WARNING("width < 1; setting to 1\n", __func__);
731
0
        width = 1;
732
0
    }
733
734
0
    boxa = boxaCreate(nx * ny);
735
0
    bx = (w + nx - 1) / nx;
736
0
    by = (h + ny - 1) / ny;
737
0
    for (i = 0; i < ny; i++) {
738
0
        y1 = by * i;
739
0
        y2 = L_MIN(y1 + by, h - 1);
740
0
        for (j = 0; j < nx; j++) {
741
0
            x1 = bx * j;
742
0
            x2 = L_MIN(x1 + bx, w - 1);
743
0
            box = boxCreate(x1, y1, x2 - x1 + 1, y2 - y1 + 1);
744
0
            boxaAddBox(boxa, box, L_INSERT);
745
0
        }
746
0
    }
747
748
0
    pta = generatePtaBoxa(boxa, width, 1);
749
0
    boxaDestroy(&boxa);
750
0
    return pta;
751
0
}
752
753
754
/*!
755
 * \brief   convertPtaLineTo4cc()
756
 *
757
 * \param[in]    ptas    8-connected line of points
758
 * \return  ptad 4-connected line, or NULL on error
759
 *
760
 * <pre>
761
 * Notes:
762
 *      (1) When a polyline is generated with width = 1, the resulting
763
 *          line is not 4-connected in general.  This function adds
764
 *          points as necessary to convert the line to 4-cconnected.
765
 *          It is useful when rendering 1 bpp on a pix.
766
 *      (2) Do not use this for lines generated with width > 1.
767
 * </pre>
768
 */
769
PTA *
770
convertPtaLineTo4cc(PTA  *ptas)
771
0
{
772
0
l_int32  i, n, x, y, xp, yp;
773
0
PTA     *ptad;
774
775
0
    if (!ptas)
776
0
        return (PTA *)ERROR_PTR("ptas not defined", __func__, NULL);
777
778
0
    n = ptaGetCount(ptas);
779
0
    ptad = ptaCreate(n);
780
0
    ptaGetIPt(ptas, 0, &xp, &yp);
781
0
    ptaAddPt(ptad, xp, yp);
782
0
    for (i = 1; i < n; i++) {
783
0
        ptaGetIPt(ptas, i, &x, &y);
784
0
        if (x != xp && y != yp)  /* diagonal */
785
0
            ptaAddPt(ptad, x, yp);
786
0
        ptaAddPt(ptad, x, y);
787
0
        xp = x;
788
0
        yp = y;
789
0
    }
790
791
0
    return ptad;
792
0
}
793
794
795
/*!
796
 * \brief   generatePtaFilledCircle()
797
 *
798
 * \param[in]    radius
799
 * \return  pta, or NULL on error
800
 *
801
 * <pre>
802
 * Notes:
803
 *      (1) The circle is has diameter = 2 * radius + 1.
804
 *      (2) It is located with the center of the circle at the
805
 *          point (%radius, %radius).
806
 *      (3) Consequently, it typically must be translated if
807
 *          it is to represent a set of pixels in an image.
808
 * </pre>
809
 */
810
PTA *
811
generatePtaFilledCircle(l_int32  radius)
812
0
{
813
0
l_int32    x, y;
814
0
l_float32  radthresh, sqdist;
815
0
PTA       *pta;
816
817
0
    if (radius < 1)
818
0
        return (PTA *)ERROR_PTR("radius must be >= 1", __func__, NULL);
819
820
0
    pta = ptaCreate(0);
821
0
    radthresh = (radius + 0.5) * (radius + 0.5);
822
0
    for (y = 0; y <= 2 * radius; y++) {
823
0
        for (x = 0; x <= 2 * radius; x++) {
824
0
            sqdist = (l_float32)((y - radius) * (y - radius) +
825
0
                                 (x - radius) * (x - radius));
826
0
            if (sqdist <= radthresh)
827
0
                ptaAddPt(pta, x, y);
828
0
        }
829
0
    }
830
831
0
    return pta;
832
0
}
833
834
835
/*!
836
 * \brief   generatePtaFilledSquare()
837
 *
838
 * \param[in]    side
839
 * \return  pta, or NULL on error
840
 *
841
 * <pre>
842
 * Notes:
843
 *      (1) The center of the square can be chosen to be at
844
 *          (side / 2, side / 2).  It must be translated by this amount
845
 *          when used for replication.
846
 * </pre>
847
 */
848
PTA *
849
generatePtaFilledSquare(l_int32  side)
850
0
{
851
0
l_int32  x, y;
852
0
PTA     *pta;
853
854
0
    if (side < 1)
855
0
        return (PTA *)ERROR_PTR("side must be > 0", __func__, NULL);
856
857
0
    pta = ptaCreate(0);
858
0
    for (y = 0; y < side; y++)
859
0
        for (x = 0; x < side; x++)
860
0
            ptaAddPt(pta, x, y);
861
862
0
    return pta;
863
0
}
864
865
866
/*!
867
 * \brief   generatePtaLineFromPt()
868
 *
869
 * \param[in]    x, y      point of origination
870
 * \param[in]    length    of line, including starting point
871
 * \param[in]    radang    angle in radians, CW from horizontal
872
 * \return  pta, or NULL on error
873
 *
874
 * <pre>
875
 * Notes:
876
 *      (1) %length of the line is 1 greater than the distance
877
 *          used in locatePtRadially().  Example: a distance of 1
878
 *          gives rise to a length of 2.
879
 * </pre>
880
 */
881
PTA *
882
generatePtaLineFromPt(l_int32    x,
883
                      l_int32    y,
884
                      l_float64  length,
885
                      l_float64  radang)
886
0
{
887
0
l_int32  x2, y2;  /* the point at the other end of the line */
888
889
0
    x2 = x + (l_int32)((length - 1.0) * cos(radang));
890
0
    y2 = y + (l_int32)((length - 1.0) * sin(radang));
891
0
    return generatePtaLine(x, y, x2, y2);
892
0
}
893
894
895
/*!
896
 * \brief   locatePtRadially()
897
 *
898
 * \param[in]    xr, yr    reference point
899
 * \param[in]    radang    angle in radians, CW from horizontal
900
 * \param[in]    dist      distance of point from reference point along
901
 *                         line given by the specified angle
902
 * \param[out]   px, py    location of point
903
 * \return  0 if OK, 1 on error
904
 */
905
l_ok
906
locatePtRadially(l_int32     xr,
907
                 l_int32     yr,
908
                 l_float64   dist,
909
                 l_float64   radang,
910
                 l_float64  *px,
911
                 l_float64  *py)
912
0
{
913
0
    if (!px || !py)
914
0
        return ERROR_INT("&x and &y not both defined", __func__, 1);
915
916
0
    *px = xr + dist * cos(radang);
917
0
    *py = yr + dist * sin(radang);
918
0
    return 0;
919
0
}
920
921
922
/*------------------------------------------------------------------*
923
 *            Rendering function plots directly on images           *
924
 *------------------------------------------------------------------*/
925
/*!
926
 * \brief   pixRenderPlotFromNuma()
927
 *
928
 * \param[in,out]  ppix        any type; replaced if not 32 bpp rgb
929
 * \param[in]      na          to be plotted
930
 * \param[in]      plotloc     location of plot: L_PLOT_AT_TOP, etc
931
 * \param[in]      linewidth   width of "line" that is drawn; between 1 and 7
932
 * \param[in]      max         maximum excursion in pixels from baseline
933
 * \param[in]      color plot color: 0xrrggbb00
934
 * \return  0 if OK, 1 on error
935
 *
936
 * <pre>
937
 * Notes:
938
 *      (1) Simplified interface for plotting row or column aligned data
939
 *          on a pix.
940
 *      (2) This replaces %pix with a 32 bpp rgb version if it is not
941
 *          already 32 bpp.  It then draws the plot on the pix.
942
 *      (3) See makePlotPtaFromNumaGen() for more details.
943
 * </pre>
944
 */
945
l_ok
946
pixRenderPlotFromNuma(PIX     **ppix,
947
                      NUMA     *na,
948
                      l_int32   plotloc,
949
                      l_int32   linewidth,
950
                      l_int32   max,
951
                      l_uint32  color)
952
0
{
953
0
l_int32  w, h, size, rval, gval, bval;
954
0
PIX     *pix1;
955
0
PTA     *pta;
956
957
0
    if (!ppix)
958
0
        return ERROR_INT("&pix not defined", __func__, 1);
959
0
    if (*ppix == NULL)
960
0
        return ERROR_INT("pix not defined", __func__, 1);
961
962
0
    pixGetDimensions(*ppix, &w, &h, NULL);
963
0
    size = (plotloc == L_PLOT_AT_TOP || plotloc == L_PLOT_AT_MID_HORIZ ||
964
0
            plotloc == L_PLOT_AT_BOT) ? h : w;
965
0
    pta = makePlotPtaFromNuma(na, size, plotloc, linewidth, max);
966
0
    if (!pta)
967
0
        return ERROR_INT("pta not made", __func__, 1);
968
969
0
    if (pixGetDepth(*ppix) != 32) {
970
0
        pix1 = pixConvertTo32(*ppix);
971
0
        pixDestroy(ppix);
972
0
        *ppix = pix1;
973
0
    }
974
0
    extractRGBValues(color, &rval, &gval, &bval);
975
0
    pixRenderPtaArb(*ppix, pta, rval, gval, bval);
976
0
    ptaDestroy(&pta);
977
0
    return 0;
978
0
}
979
980
981
/*!
982
 * \brief   makePlotPtaFromNuma()
983
 *
984
 * \param[in]    na
985
 * \param[in]    size        pix height for horizontal plot; pix width
986
 *                           for vertical plot
987
 * \param[in]    plotloc     location of plot: L_PLOT_AT_TOP, etc
988
 * \param[in]    linewidth   width of "line" that is drawn; between 1 and 7
989
 * \param[in]    max         maximum excursion in pixels from baseline
990
 * \return  ptad, or NULL on error
991
 *
992
 * <pre>
993
 * Notes:
994
 *      (1) This generates points from %numa representing y(x) or x(y)
995
 *          with respect to a pix.  A horizontal plot y(x) is drawn for
996
 *          a function of column position, and a vertical plot is drawn
997
 *          for a function x(y) of row position.  The baseline is located
998
 *          so that all plot points will fit in the pix.
999
 *      (2) See makePlotPtaFromNumaGen() for more details.
1000
 * </pre>
1001
 */
1002
PTA *
1003
makePlotPtaFromNuma(NUMA    *na,
1004
                    l_int32  size,
1005
                    l_int32  plotloc,
1006
                    l_int32  linewidth,
1007
                    l_int32  max)
1008
0
{
1009
0
l_int32  orient, refpos;
1010
1011
0
    if (!na)
1012
0
        return (PTA *)ERROR_PTR("na not defined", __func__, NULL);
1013
0
    if (plotloc == L_PLOT_AT_TOP || plotloc == L_PLOT_AT_MID_HORIZ ||
1014
0
        plotloc == L_PLOT_AT_BOT) {
1015
0
        orient = L_HORIZONTAL_LINE;
1016
0
    } else if (plotloc == L_PLOT_AT_LEFT || plotloc == L_PLOT_AT_MID_VERT ||
1017
0
               plotloc == L_PLOT_AT_RIGHT) {
1018
0
        orient = L_VERTICAL_LINE;
1019
0
    } else {
1020
0
        return (PTA *)ERROR_PTR("invalid plotloc", __func__, NULL);
1021
0
    }
1022
1023
0
    if (plotloc == L_PLOT_AT_LEFT || plotloc == L_PLOT_AT_TOP)
1024
0
        refpos = max;
1025
0
    else if (plotloc == L_PLOT_AT_MID_VERT || plotloc == L_PLOT_AT_MID_HORIZ)
1026
0
        refpos = size / 2;
1027
0
    else  /* L_PLOT_AT_RIGHT || L_PLOT_AT_BOT */
1028
0
        refpos = size - max - 1;
1029
1030
0
    return makePlotPtaFromNumaGen(na, orient, linewidth, refpos, max, 1);
1031
0
}
1032
1033
1034
/*!
1035
 * \brief   pixRenderPlotFromNumaGen()
1036
 *
1037
 * \param[in,out]  ppix         any type; replaced if not 32 bpp rgb
1038
 * \param[in]      na           to be plotted
1039
 * \param[in]      orient       L_HORIZONTAL_LINE, L_VERTICAL_LINE
1040
 * \param[in]      linewidth    width of "line" that is drawn; between 1 and 7
1041
 * \param[in]      refpos       reference position: y for horizontal;
1042
 *                              x for vertical
1043
 * \param[in]      max          maximum excursion in pixels from baseline
1044
 * \param[in]      drawref      1 to draw the reference line and its normal
1045
 * \param[in]      color        plot color: 0xrrggbb00
1046
 * \return  0 if OK, 1 on error
1047
 *
1048
 * <pre>
1049
 * Notes:
1050
 *      (1) General interface for plotting row or column aligned data
1051
 *          on a pix.
1052
 *      (2) This replaces %pix with a 32 bpp rgb version if it is not
1053
 *          already 32 bpp.  It then draws the plot on the pix.
1054
 *      (3) See makePlotPtaFromNumaGen() for other input parameters.
1055
 * </pre>
1056
 */
1057
l_ok
1058
pixRenderPlotFromNumaGen(PIX     **ppix,
1059
                         NUMA     *na,
1060
                         l_int32   orient,
1061
                         l_int32   linewidth,
1062
                         l_int32   refpos,
1063
                         l_int32   max,
1064
                         l_int32   drawref,
1065
                         l_uint32  color)
1066
0
{
1067
0
l_int32  rval, gval, bval;
1068
0
PIX     *pix1;
1069
0
PTA     *pta;
1070
1071
0
    if (!ppix)
1072
0
        return ERROR_INT("&pix not defined", __func__, 1);
1073
0
    if (*ppix == NULL)
1074
0
        return ERROR_INT("pix not defined", __func__, 1);
1075
1076
0
    pta = makePlotPtaFromNumaGen(na, orient, linewidth, refpos, max, drawref);
1077
0
    if (!pta)
1078
0
        return ERROR_INT("pta not made", __func__, 1);
1079
1080
0
    if (pixGetDepth(*ppix) != 32) {
1081
0
        pix1 = pixConvertTo32(*ppix);
1082
0
        pixDestroy(ppix);
1083
0
        *ppix = pix1;
1084
0
    }
1085
0
    extractRGBValues(color, &rval, &gval, &bval);
1086
0
    pixRenderPtaArb(*ppix, pta, rval, gval, bval);
1087
0
    ptaDestroy(&pta);
1088
0
    return 0;
1089
0
}
1090
1091
1092
/*!
1093
 * \brief   makePlotPtaFromNumaGen()
1094
 *
1095
 * \param[in]    na
1096
 * \param[in]    orient       L_HORIZONTAL_LINE, L_VERTICAL_LINE
1097
 * \param[in]    linewidth    width of "line" that is drawn; between 1 and 7
1098
 * \param[in]    refpos       reference position: y for horizontal;
1099
 *                            x for vertical
1100
 * \param[in]    max          maximum excursion in pixels from baseline
1101
 * \param[in]    drawref      1 to draw the reference line and its normal
1102
 * \return  ptad, or NULL on error
1103
 *
1104
 * <pre>
1105
 * Notes:
1106
 *      (1) This generates points from %numa representing y(x) or x(y)
1107
 *          with respect to a pix.  For y(x), we draw a horizontal line
1108
 *          at the reference position and a vertical line at the edge; then
1109
 *          we draw the values of %numa, scaled so that the maximum
1110
 *          excursion from the reference position is %max pixels.
1111
 *      (2) The start and delx parameters of %numa are used to refer
1112
 *          its values to the raster lines (L_VERTICAL_LINE) or columns
1113
 *          (L_HORIZONTAL_LINE).
1114
 *      (3) The linewidth is chosen in the interval [1 ... 7].
1115
 *      (4) %refpos should be chosen so the plot is entirely within the pix
1116
 *          that it will be painted onto.
1117
 *      (5) This would typically be used to plot, in place, a function
1118
 *          computed along pixel rows or columns.
1119
 * </pre>
1120
 */
1121
PTA *
1122
makePlotPtaFromNumaGen(NUMA    *na,
1123
                       l_int32  orient,
1124
                       l_int32  linewidth,
1125
                       l_int32  refpos,
1126
                       l_int32  max,
1127
                       l_int32  drawref)
1128
0
{
1129
0
l_int32    i, n, maxw, maxh;
1130
0
l_float32  minval, maxval, absval, val, scale, start, del;
1131
0
PTA       *pta1, *pta2, *ptad;
1132
1133
0
    if (!na)
1134
0
        return (PTA *)ERROR_PTR("na not defined", __func__, NULL);
1135
0
    if (orient != L_HORIZONTAL_LINE && orient != L_VERTICAL_LINE)
1136
0
        return (PTA *)ERROR_PTR("invalid orient", __func__, NULL);
1137
0
    if (linewidth < 1) {
1138
0
        L_WARNING("linewidth < 1; setting to 1\n", __func__);
1139
0
        linewidth = 1;
1140
0
    }
1141
0
    if (linewidth > 7) {
1142
0
        L_WARNING("linewidth > 7; setting to 7\n", __func__);
1143
0
        linewidth = 7;
1144
0
    }
1145
1146
0
    numaGetMin(na, &minval, NULL);
1147
0
    numaGetMax(na, &maxval, NULL);
1148
0
    absval = L_MAX(L_ABS(minval), L_ABS(maxval));
1149
0
    scale = (l_float32)max / (l_float32)absval;
1150
0
    n = numaGetCount(na);
1151
0
    numaGetParameters(na, &start, &del);
1152
1153
        /* Generate the plot points */
1154
0
    pta1 = ptaCreate(n);
1155
0
    maxw = maxh = 0;       
1156
0
    for (i = 0; i < n; i++) {
1157
0
        numaGetFValue(na, i, &val);
1158
0
        if (orient == L_HORIZONTAL_LINE) {
1159
0
            ptaAddPt(pta1, start + i * del, refpos + scale * val);
1160
0
            maxw = (del >= 0) ? start + n * del + linewidth
1161
0
                              : start + linewidth;
1162
0
            maxh = refpos + max + linewidth;
1163
0
        } else {  /* vertical line */
1164
0
            ptaAddPt(pta1, refpos + scale * val, start + i * del);
1165
0
            maxw = refpos + max + linewidth;
1166
0
            maxh = (del >= 0) ? start + n * del + linewidth
1167
0
                              : start + linewidth;
1168
0
        }
1169
0
    }
1170
1171
        /* Optionally, widen the plot */
1172
0
    if (linewidth > 1) {
1173
0
        if (linewidth % 2 == 0)  /* even linewidth; use side of a square */
1174
0
            pta2 = generatePtaFilledSquare(linewidth);
1175
0
        else  /* odd linewidth; use radius of a circle */
1176
0
            pta2 = generatePtaFilledCircle(linewidth / 2);
1177
0
        ptad = ptaReplicatePattern(pta1, NULL, pta2, linewidth / 2,
1178
0
                                   linewidth / 2, maxw, maxh);
1179
0
        ptaDestroy(&pta2);
1180
0
    } else {
1181
0
        ptad = ptaClone(pta1);
1182
0
    }
1183
0
    ptaDestroy(&pta1);
1184
1185
        /* Optionally, add the reference lines */
1186
0
    if (drawref) {
1187
0
        if (orient == L_HORIZONTAL_LINE) {
1188
0
            pta1 = generatePtaLine(start, refpos, start + n * del, refpos);
1189
0
            ptaJoin(ptad, pta1, 0, -1);
1190
0
            ptaDestroy(&pta1);
1191
0
            pta1 = generatePtaLine(start, refpos - max,
1192
0
                                   start, refpos + max);
1193
0
            ptaJoin(ptad, pta1, 0, -1);
1194
0
        } else {  /* vertical line */
1195
0
            pta1 = generatePtaLine(refpos, start, refpos, start + n * del);
1196
0
            ptaJoin(ptad, pta1, 0, -1);
1197
0
            ptaDestroy(&pta1);
1198
0
            pta1 = generatePtaLine(refpos - max, start,
1199
0
                                   refpos + max, start);
1200
0
            ptaJoin(ptad, pta1, 0, -1);
1201
0
        }
1202
0
        ptaDestroy(&pta1);
1203
0
    }
1204
1205
0
    return ptad;
1206
0
}
1207
1208
1209
/*------------------------------------------------------------------*
1210
 *        Pta generation for arbitrary shapes built with lines      *
1211
 *------------------------------------------------------------------*/
1212
/*!
1213
 * \brief   pixRenderPta()
1214
 *
1215
 * \param[in]    pix   any depth, not cmapped
1216
 * \param[in]    pta   arbitrary set of points
1217
 * \param[in]    op    one of L_SET_PIXELS, L_CLEAR_PIXELS, L_FLIP_PIXELS
1218
 * \return  0 if OK, 1 on error
1219
 *
1220
 * <pre>
1221
 * Notes:
1222
 *      (1) L_SET_PIXELS puts all image bits in each pixel to 1
1223
 *          (black for 1 bpp; white for depth > 1)
1224
 *      (2) L_CLEAR_PIXELS puts all image bits in each pixel to 0
1225
 *          (white for 1 bpp; black for depth > 1)
1226
 *      (3) L_FLIP_PIXELS reverses all image bits in each pixel
1227
 *      (4) This function clips the rendering to the pix.  It performs
1228
 *          clipping for functions such as pixRenderLine(),
1229
 *          pixRenderBox() and pixRenderBoxa(), that call pixRenderPta().
1230
 * </pre>
1231
 */
1232
l_ok
1233
pixRenderPta(PIX     *pix,
1234
             PTA     *pta,
1235
             l_int32  op)
1236
0
{
1237
0
l_int32  i, n, x, y, w, h, d, maxval;
1238
1239
0
    if (!pix)
1240
0
        return ERROR_INT("pix not defined", __func__, 1);
1241
0
    if (pixGetColormap(pix))
1242
0
        return ERROR_INT("pix is colormapped", __func__, 1);
1243
0
    if (!pta)
1244
0
        return ERROR_INT("pta not defined", __func__, 1);
1245
0
    if (op != L_SET_PIXELS && op != L_CLEAR_PIXELS && op != L_FLIP_PIXELS)
1246
0
        return ERROR_INT("invalid op", __func__, 1);
1247
1248
0
    pixGetDimensions(pix, &w, &h, &d);
1249
0
    maxval = 1;
1250
0
    if (op == L_SET_PIXELS) {
1251
0
        switch (d)
1252
0
        {
1253
0
        case 2:
1254
0
            maxval = 0x3;
1255
0
            break;
1256
0
        case 4:
1257
0
            maxval = 0xf;
1258
0
            break;
1259
0
        case 8:
1260
0
            maxval = 0xff;
1261
0
            break;
1262
0
        case 16:
1263
0
            maxval = 0xffff;
1264
0
            break;
1265
0
        case 32:
1266
0
            maxval = 0xffffffff;
1267
0
            break;
1268
0
        }
1269
0
    }
1270
1271
0
    n = ptaGetCount(pta);
1272
0
    for (i = 0; i < n; i++) {
1273
0
        ptaGetIPt(pta, i, &x, &y);
1274
0
        if (x < 0 || x >= w)
1275
0
            continue;
1276
0
        if (y < 0 || y >= h)
1277
0
            continue;
1278
0
        switch (op)
1279
0
        {
1280
0
        case L_SET_PIXELS:
1281
0
            pixSetPixel(pix, x, y, maxval);
1282
0
            break;
1283
0
        case L_CLEAR_PIXELS:
1284
0
            pixClearPixel(pix, x, y);
1285
0
            break;
1286
0
        case L_FLIP_PIXELS:
1287
0
            pixFlipPixel(pix, x, y);
1288
0
            break;
1289
0
        default:
1290
0
            break;
1291
0
        }
1292
0
    }
1293
1294
0
    return 0;
1295
0
}
1296
1297
1298
/*!
1299
 * \brief   pixRenderPtaArb()
1300
 *
1301
 * \param[in]    pix      any depth, cmapped ok
1302
 * \param[in]    pta      arbitrary set of points
1303
 * \param[in]    rval, gval, bval
1304
 * \return  0 if OK, 1 on error
1305
 *
1306
 * <pre>
1307
 * Notes:
1308
 *      (1) If %pix is colormapped, render this color (or the nearest
1309
 *          color if the cmap is full) on each pixel.
1310
 *      (2) The rgb components have the standard dynamic range [0 ... 255]
1311
 *      (3) If pix is not colormapped, do the best job you can using
1312
 *          the input colors:
1313
 *          ~ d = 1: set the pixels
1314
 *          ~ d = 2, 4, 8: average the input rgb value
1315
 *          ~ d = 32: use the input rgb value
1316
 *      (4) This function clips the rendering to %pix.
1317
 * </pre>
1318
 */
1319
l_ok
1320
pixRenderPtaArb(PIX     *pix,
1321
                PTA     *pta,
1322
                l_uint8  rval,
1323
                l_uint8  gval,
1324
                l_uint8  bval)
1325
0
{
1326
0
l_int32   i, n, x, y, w, h, d, index;
1327
0
l_uint8   val;
1328
0
l_uint32  val32;
1329
0
PIXCMAP  *cmap;
1330
1331
0
    if (!pix)
1332
0
        return ERROR_INT("pix not defined", __func__, 1);
1333
0
    if (!pta)
1334
0
        return ERROR_INT("pta not defined", __func__, 1);
1335
0
    d = pixGetDepth(pix);
1336
0
    if (d != 1 && d != 2 && d != 4 && d != 8 && d != 32)
1337
0
        return ERROR_INT("depth not in {1,2,4,8,32}", __func__, 1);
1338
1339
0
    if (d == 1) {
1340
0
        pixRenderPta(pix, pta, L_SET_PIXELS);
1341
0
        return 0;
1342
0
    }
1343
1344
0
    cmap = pixGetColormap(pix);
1345
0
    pixGetDimensions(pix, &w, &h, &d);
1346
0
    if (cmap) {
1347
0
        pixcmapAddNearestColor(cmap, rval, gval, bval, &index);
1348
0
    } else {
1349
0
        if (d == 2)
1350
0
            val = (rval + gval + bval) / (3 * 64);
1351
0
        else if (d == 4)
1352
0
            val = (rval + gval + bval) / (3 * 16);
1353
0
        else if (d == 8)
1354
0
            val = (rval + gval + bval) / 3;
1355
0
        else  /* d == 32 */
1356
0
            composeRGBPixel(rval, gval, bval, &val32);
1357
0
    }
1358
1359
0
    n = ptaGetCount(pta);
1360
0
    for (i = 0; i < n; i++) {
1361
0
        ptaGetIPt(pta, i, &x, &y);
1362
0
        if (x < 0 || x >= w)
1363
0
            continue;
1364
0
        if (y < 0 || y >= h)
1365
0
            continue;
1366
0
        if (cmap)
1367
0
            pixSetPixel(pix, x, y, index);
1368
0
        else if (d == 32)
1369
0
            pixSetPixel(pix, x, y, val32);
1370
0
        else
1371
0
            pixSetPixel(pix, x, y, val);
1372
0
    }
1373
1374
0
    return 0;
1375
0
}
1376
1377
1378
/*!
1379
 * \brief   pixRenderPtaBlend()
1380
 *
1381
 * \param[in]    pix      32 bpp rgb
1382
 * \param[in]    pta      arbitrary set of points
1383
 * \param[in]    rval, gval, bval
1384
 * \param[in]    fract
1385
 * \return  0 if OK, 1 on error
1386
 *
1387
 * <pre>
1388
 * Notes:
1389
 *      (1) This function clips the rendering to %pix.
1390
 * </pre>
1391
 */
1392
l_ok
1393
pixRenderPtaBlend(PIX     *pix,
1394
                  PTA     *pta,
1395
                  l_uint8  rval,
1396
                  l_uint8  gval,
1397
                  l_uint8  bval,
1398
                  l_float32 fract)
1399
0
{
1400
0
l_int32    i, n, x, y, w, h;
1401
0
l_uint8    nrval, ngval, nbval;
1402
0
l_uint32   val32;
1403
0
l_float32  frval, fgval, fbval;
1404
1405
0
    if (!pix)
1406
0
        return ERROR_INT("pix not defined", __func__, 1);
1407
0
    if (!pta)
1408
0
        return ERROR_INT("pta not defined", __func__, 1);
1409
0
    if (pixGetDepth(pix) != 32)
1410
0
        return ERROR_INT("depth not 32 bpp", __func__, 1);
1411
0
    if (fract < 0.0 || fract > 1.0) {
1412
0
        L_WARNING("fract must be in [0.0, 1.0]; setting to 0.5\n", __func__);
1413
0
        fract = 0.5;
1414
0
    }
1415
1416
0
    pixGetDimensions(pix, &w, &h, NULL);
1417
0
    n = ptaGetCount(pta);
1418
0
    frval = fract * rval;
1419
0
    fgval = fract * gval;
1420
0
    fbval = fract * bval;
1421
0
    for (i = 0; i < n; i++) {
1422
0
        ptaGetIPt(pta, i, &x, &y);
1423
0
        if (x < 0 || x >= w)
1424
0
            continue;
1425
0
        if (y < 0 || y >= h)
1426
0
            continue;
1427
0
        pixGetPixel(pix, x, y, &val32);
1428
0
        nrval = GET_DATA_BYTE(&val32, COLOR_RED);
1429
0
        nrval = (l_uint8)((1. - fract) * nrval + frval);
1430
0
        ngval = GET_DATA_BYTE(&val32, COLOR_GREEN);
1431
0
        ngval = (l_uint8)((1. - fract) * ngval + fgval);
1432
0
        nbval = GET_DATA_BYTE(&val32, COLOR_BLUE);
1433
0
        nbval = (l_uint8)((1. - fract) * nbval + fbval);
1434
0
        composeRGBPixel(nrval, ngval, nbval, &val32);
1435
0
        pixSetPixel(pix, x, y, val32);
1436
0
    }
1437
1438
0
    return 0;
1439
0
}
1440
1441
1442
/*------------------------------------------------------------------*
1443
 *           Rendering of arbitrary shapes built with lines         *
1444
 *------------------------------------------------------------------*/
1445
/*!
1446
 * \brief   pixRenderLine()
1447
 *
1448
 * \param[in]    pix      any depth, not cmapped
1449
 * \param[in]    x1, y1
1450
 * \param[in]    x2, y2
1451
 * \param[in]    width    thickness of line
1452
 * \param[in]    op       one of L_SET_PIXELS, L_CLEAR_PIXELS, L_FLIP_PIXELS
1453
 * \return  0 if OK, 1 on error
1454
 */
1455
l_ok
1456
pixRenderLine(PIX     *pix,
1457
              l_int32  x1,
1458
              l_int32  y1,
1459
              l_int32  x2,
1460
              l_int32  y2,
1461
              l_int32  width,
1462
              l_int32  op)
1463
0
{
1464
0
PTA  *pta;
1465
1466
0
    if (!pix)
1467
0
        return ERROR_INT("pix not defined", __func__, 1);
1468
0
    if (width < 1) {
1469
0
        L_WARNING("width must be > 0; setting to 1\n", __func__);
1470
0
        width = 1;
1471
0
    }
1472
0
    if (op != L_SET_PIXELS && op != L_CLEAR_PIXELS && op != L_FLIP_PIXELS)
1473
0
        return ERROR_INT("invalid op", __func__, 1);
1474
1475
0
    if ((pta = generatePtaWideLine(x1, y1, x2, y2, width)) == NULL)
1476
0
        return ERROR_INT("pta not made", __func__, 1);
1477
0
    pixRenderPta(pix, pta, op);
1478
0
    ptaDestroy(&pta);
1479
0
    return 0;
1480
0
}
1481
1482
1483
/*!
1484
 * \brief   pixRenderLineArb()
1485
 *
1486
 * \param[in]    pix       any depth, cmapped ok
1487
 * \param[in]    x1, y1
1488
 * \param[in]    x2, y2
1489
 * \param[in]    width     thickness of line
1490
 * \param[in]    rval, gval, bval
1491
 * \return  0 if OK, 1 on error
1492
 */
1493
l_ok
1494
pixRenderLineArb(PIX     *pix,
1495
                 l_int32  x1,
1496
                 l_int32  y1,
1497
                 l_int32  x2,
1498
                 l_int32  y2,
1499
                 l_int32  width,
1500
                 l_uint8  rval,
1501
                 l_uint8  gval,
1502
                 l_uint8  bval)
1503
0
{
1504
0
PTA  *pta;
1505
1506
0
    if (!pix)
1507
0
        return ERROR_INT("pix not defined", __func__, 1);
1508
0
    if (width < 1) {
1509
0
        L_WARNING("width must be > 0; setting to 1\n", __func__);
1510
0
        width = 1;
1511
0
    }
1512
1513
0
    if ((pta = generatePtaWideLine(x1, y1, x2, y2, width)) == NULL)
1514
0
        return ERROR_INT("pta not made", __func__, 1);
1515
0
    pixRenderPtaArb(pix, pta, rval, gval, bval);
1516
0
    ptaDestroy(&pta);
1517
0
    return 0;
1518
0
}
1519
1520
1521
/*!
1522
 * \brief   pixRenderLineBlend()
1523
 *
1524
 * \param[in]    pix      32 bpp rgb
1525
 * \param[in]    x1, y1
1526
 * \param[in]    x2, y2
1527
 * \param[in]    width    thickness of line
1528
 * \param[in]    rval, gval, bval
1529
 * \param[in]    fract
1530
 * \return  0 if OK, 1 on error
1531
 */
1532
l_ok
1533
pixRenderLineBlend(PIX       *pix,
1534
                   l_int32    x1,
1535
                   l_int32    y1,
1536
                   l_int32    x2,
1537
                   l_int32    y2,
1538
                   l_int32    width,
1539
                   l_uint8    rval,
1540
                   l_uint8    gval,
1541
                   l_uint8    bval,
1542
                   l_float32  fract)
1543
0
{
1544
0
PTA  *pta;
1545
1546
0
    if (!pix)
1547
0
        return ERROR_INT("pix not defined", __func__, 1);
1548
0
    if (width < 1) {
1549
0
        L_WARNING("width must be > 0; setting to 1\n", __func__);
1550
0
        width = 1;
1551
0
    }
1552
1553
0
    if ((pta = generatePtaWideLine(x1, y1, x2, y2, width)) == NULL)
1554
0
        return ERROR_INT("pta not made", __func__, 1);
1555
0
    pixRenderPtaBlend(pix, pta, rval, gval, bval, fract);
1556
0
    ptaDestroy(&pta);
1557
0
    return 0;
1558
0
}
1559
1560
1561
/*!
1562
 * \brief   pixRenderBox()
1563
 *
1564
 * \param[in]    pix     any depth, not cmapped
1565
 * \param[in]    box
1566
 * \param[in]    width   thickness of box lines
1567
 * \param[in]    op      one of L_SET_PIXELS, L_CLEAR_PIXELS, L_FLIP_PIXELS
1568
 * \return  0 if OK, 1 on error
1569
 */
1570
l_ok
1571
pixRenderBox(PIX     *pix,
1572
             BOX     *box,
1573
             l_int32  width,
1574
             l_int32  op)
1575
0
{
1576
0
PTA  *pta;
1577
1578
0
    if (!pix)
1579
0
        return ERROR_INT("pix not defined", __func__, 1);
1580
0
    if (!box)
1581
0
        return ERROR_INT("box not defined", __func__, 1);
1582
0
    if (width < 1) {
1583
0
        L_WARNING("width < 1; setting to 1\n", __func__);
1584
0
        width = 1;
1585
0
    }
1586
0
    if (op != L_SET_PIXELS && op != L_CLEAR_PIXELS && op != L_FLIP_PIXELS)
1587
0
        return ERROR_INT("invalid op", __func__, 1);
1588
1589
0
    if ((pta = generatePtaBox(box, width)) == NULL)
1590
0
        return ERROR_INT("pta not made", __func__, 1);
1591
0
    pixRenderPta(pix, pta, op);
1592
0
    ptaDestroy(&pta);
1593
0
    return 0;
1594
0
}
1595
1596
1597
/*!
1598
 * \brief   pixRenderBoxArb()
1599
 *
1600
 * \param[in]    pix       any depth, cmapped ok
1601
 * \param[in]    box
1602
 * \param[in]    width     thickness of box lines
1603
 * \param[in]    rval, gval, bval
1604
 * \return  0 if OK, 1 on error
1605
 */
1606
l_ok
1607
pixRenderBoxArb(PIX     *pix,
1608
                BOX     *box,
1609
                l_int32  width,
1610
                l_uint8  rval,
1611
                l_uint8  gval,
1612
                l_uint8  bval)
1613
0
{
1614
0
PTA  *pta;
1615
1616
0
    if (!pix)
1617
0
        return ERROR_INT("pix not defined", __func__, 1);
1618
0
    if (!box)
1619
0
        return ERROR_INT("box not defined", __func__, 1);
1620
0
    if (width < 1) {
1621
0
        L_WARNING("width < 1; setting to 1\n", __func__);
1622
0
        width = 1;
1623
0
    }
1624
1625
0
    if ((pta = generatePtaBox(box, width)) == NULL)
1626
0
        return ERROR_INT("pta not made", __func__, 1);
1627
0
    pixRenderPtaArb(pix, pta, rval, gval, bval);
1628
0
    ptaDestroy(&pta);
1629
0
    return 0;
1630
0
}
1631
1632
1633
/*!
1634
 * \brief   pixRenderBoxBlend()
1635
 *
1636
 * \param[in]    pix       32 bpp rgb
1637
 * \param[in]    box
1638
 * \param[in]    width     thickness of box lines
1639
 * \param[in]    rval, gval, bval
1640
 * \param[in]    fract     in [0.0 - 1.0]: 1.0 is no transparency;
1641
 *                         0.0 is complete transparency (no effect)
1642
 * \return  0 if OK, 1 on error
1643
 */
1644
l_ok
1645
pixRenderBoxBlend(PIX       *pix,
1646
                  BOX       *box,
1647
                  l_int32    width,
1648
                  l_uint8    rval,
1649
                  l_uint8    gval,
1650
                  l_uint8    bval,
1651
                  l_float32  fract)
1652
0
{
1653
0
PTA  *pta;
1654
1655
0
    if (!pix)
1656
0
        return ERROR_INT("pix not defined", __func__, 1);
1657
0
    if (!box)
1658
0
        return ERROR_INT("box not defined", __func__, 1);
1659
0
    if (width < 1) {
1660
0
        L_WARNING("width < 1; setting to 1\n", __func__);
1661
0
        width = 1;
1662
0
    }
1663
1664
0
    if ((pta = generatePtaBox(box, width)) == NULL)
1665
0
        return ERROR_INT("pta not made", __func__, 1);
1666
0
    pixRenderPtaBlend(pix, pta, rval, gval, bval, fract);
1667
0
    ptaDestroy(&pta);
1668
0
    return 0;
1669
0
}
1670
1671
1672
/*!
1673
 * \brief   pixRenderBoxa()
1674
 *
1675
 * \param[in]    pix      any depth, not cmapped
1676
 * \param[in]    boxa
1677
 * \param[in]    width    thickness of line
1678
 * \param[in]    op       one of L_SET_PIXELS, L_CLEAR_PIXELS, L_FLIP_PIXELS
1679
 * \return  0 if OK, 1 on error
1680
 */
1681
l_ok
1682
pixRenderBoxa(PIX     *pix,
1683
              BOXA    *boxa,
1684
              l_int32  width,
1685
              l_int32  op)
1686
0
{
1687
0
PTA  *pta;
1688
1689
0
    if (!pix)
1690
0
        return ERROR_INT("pix not defined", __func__, 1);
1691
0
    if (!boxa)
1692
0
        return ERROR_INT("boxa not defined", __func__, 1);
1693
0
    if (width < 1) {
1694
0
        L_WARNING("width < 1; setting to 1\n", __func__);
1695
0
        width = 1;
1696
0
    }
1697
0
    if (op != L_SET_PIXELS && op != L_CLEAR_PIXELS && op != L_FLIP_PIXELS)
1698
0
        return ERROR_INT("invalid op", __func__, 1);
1699
1700
0
    if ((pta = generatePtaBoxa(boxa, width, 0)) == NULL)
1701
0
        return ERROR_INT("pta not made", __func__, 1);
1702
0
    pixRenderPta(pix, pta, op);
1703
0
    ptaDestroy(&pta);
1704
0
    return 0;
1705
0
}
1706
1707
1708
/*!
1709
 * \brief   pixRenderBoxaArb()
1710
 *
1711
 * \param[in]    pix       any depth; colormapped is ok
1712
 * \param[in]    boxa
1713
 * \param[in]    width     thickness of line
1714
 * \param[in]    rval, gval, bval
1715
 * \return  0 if OK, 1 on error
1716
 */
1717
l_ok
1718
pixRenderBoxaArb(PIX     *pix,
1719
                 BOXA    *boxa,
1720
                 l_int32  width,
1721
                 l_uint8  rval,
1722
                 l_uint8  gval,
1723
                 l_uint8  bval)
1724
0
{
1725
0
PTA  *pta;
1726
1727
0
    if (!pix)
1728
0
        return ERROR_INT("pix not defined", __func__, 1);
1729
0
    if (!boxa)
1730
0
        return ERROR_INT("boxa not defined", __func__, 1);
1731
0
    if (width < 1) {
1732
0
        L_WARNING("width < 1; setting to 1\n", __func__);
1733
0
        width = 1;
1734
0
    }
1735
1736
0
    if ((pta = generatePtaBoxa(boxa, width, 0)) == NULL)
1737
0
        return ERROR_INT("pta not made", __func__, 1);
1738
0
    pixRenderPtaArb(pix, pta, rval, gval, bval);
1739
0
    ptaDestroy(&pta);
1740
0
    return 0;
1741
0
}
1742
1743
1744
/*!
1745
 * \brief   pixRenderBoxaBlend()
1746
 *
1747
 * \param[in]    pix          32 bpp rgb
1748
 * \param[in]    boxa
1749
 * \param[in]    width        thickness of line
1750
 * \param[in]    rval, gval, bval
1751
 * \param[in]    fract        in [0.0 - 1.0]: 1.0 is no transparency;
1752
 *                            0.0 is complete transparency (no effect)
1753
 * \param[in]    removedups   1 to remove; 0 otherwise
1754
 * \return  0 if OK, 1 on error
1755
 */
1756
l_ok
1757
pixRenderBoxaBlend(PIX       *pix,
1758
                   BOXA      *boxa,
1759
                   l_int32    width,
1760
                   l_uint8    rval,
1761
                   l_uint8    gval,
1762
                   l_uint8    bval,
1763
                   l_float32  fract,
1764
                   l_int32    removedups)
1765
0
{
1766
0
PTA  *pta;
1767
1768
0
    if (!pix)
1769
0
        return ERROR_INT("pix not defined", __func__, 1);
1770
0
    if (!boxa)
1771
0
        return ERROR_INT("boxa not defined", __func__, 1);
1772
0
    if (width < 1) {
1773
0
        L_WARNING("width < 1; setting to 1\n", __func__);
1774
0
        width = 1;
1775
0
    }
1776
1777
0
    if ((pta = generatePtaBoxa(boxa, width, removedups)) == NULL)
1778
0
        return ERROR_INT("pta not made", __func__, 1);
1779
0
    pixRenderPtaBlend(pix, pta, rval, gval, bval, fract);
1780
0
    ptaDestroy(&pta);
1781
0
    return 0;
1782
0
}
1783
1784
1785
/*!
1786
 * \brief   pixRenderHashBox()
1787
 *
1788
 * \param[in]    pix       any depth, not cmapped
1789
 * \param[in]    box
1790
 * \param[in]    spacing    spacing between lines; must be > 1
1791
 * \param[in]    width      thickness of box and hash lines
1792
 * \param[in]    orient     orientation of lines: L_HORIZONTAL_LINE, ...
1793
 * \param[in]    outline    0 to skip drawing box outline
1794
 * \param[in]    op         one of L_SET_PIXELS, L_CLEAR_PIXELS, L_FLIP_PIXELS
1795
 * \return  0 if OK, 1 on error
1796
 */
1797
l_ok
1798
pixRenderHashBox(PIX     *pix,
1799
                 BOX     *box,
1800
                 l_int32  spacing,
1801
                 l_int32  width,
1802
                 l_int32  orient,
1803
                 l_int32  outline,
1804
                 l_int32  op)
1805
0
{
1806
0
PTA  *pta;
1807
1808
0
    if (!pix)
1809
0
        return ERROR_INT("pix not defined", __func__, 1);
1810
0
    if (!box)
1811
0
        return ERROR_INT("box not defined", __func__, 1);
1812
0
    if (spacing <= 1)
1813
0
        return ERROR_INT("spacing not > 1", __func__, 1);
1814
0
    if (width < 1) {
1815
0
        L_WARNING("width < 1; setting to 1\n", __func__);
1816
0
        width = 1;
1817
0
    }
1818
0
    if (orient != L_HORIZONTAL_LINE && orient != L_POS_SLOPE_LINE &&
1819
0
        orient != L_VERTICAL_LINE && orient != L_NEG_SLOPE_LINE)
1820
0
        return ERROR_INT("invalid line orientation", __func__, 1);
1821
0
    if (op != L_SET_PIXELS && op != L_CLEAR_PIXELS && op != L_FLIP_PIXELS)
1822
0
        return ERROR_INT("invalid op", __func__, 1);
1823
1824
0
    pta = generatePtaHashBox(box, spacing, width, orient, outline);
1825
0
    if (!pta)
1826
0
        return ERROR_INT("pta not made", __func__, 1);
1827
0
    pixRenderPta(pix, pta, op);
1828
0
    ptaDestroy(&pta);
1829
0
    return 0;
1830
0
}
1831
1832
1833
/*!
1834
 * \brief   pixRenderHashBoxArb()
1835
 *
1836
 * \param[in]    pix         any depth; cmapped ok
1837
 * \param[in]    box
1838
 * \param[in]    spacing     spacing between lines; must be > 1
1839
 * \param[in]    width       thickness of box and hash lines
1840
 * \param[in]    orient      orientation of lines: L_HORIZONTAL_LINE, ...
1841
 * \param[in]    outline     0 to skip drawing box outline
1842
 * \param[in]    rval, gval, bval
1843
 * \return  0 if OK, 1 on error
1844
 */
1845
l_ok
1846
pixRenderHashBoxArb(PIX     *pix,
1847
                    BOX     *box,
1848
                    l_int32  spacing,
1849
                    l_int32  width,
1850
                    l_int32  orient,
1851
                    l_int32  outline,
1852
                    l_int32  rval,
1853
                    l_int32  gval,
1854
                    l_int32  bval)
1855
0
{
1856
0
PTA  *pta;
1857
1858
0
    if (!pix)
1859
0
        return ERROR_INT("pix not defined", __func__, 1);
1860
0
    if (!box)
1861
0
        return ERROR_INT("box not defined", __func__, 1);
1862
0
    if (spacing <= 1)
1863
0
        return ERROR_INT("spacing not > 1", __func__, 1);
1864
0
    if (width < 1) {
1865
0
        L_WARNING("width < 1; setting to 1\n", __func__);
1866
0
        width = 1;
1867
0
    }
1868
0
    if (orient != L_HORIZONTAL_LINE && orient != L_POS_SLOPE_LINE &&
1869
0
        orient != L_VERTICAL_LINE && orient != L_NEG_SLOPE_LINE)
1870
0
        return ERROR_INT("invalid line orientation", __func__, 1);
1871
1872
0
    pta = generatePtaHashBox(box, spacing, width, orient, outline);
1873
0
    if (!pta)
1874
0
        return ERROR_INT("pta not made", __func__, 1);
1875
0
    pixRenderPtaArb(pix, pta, rval, gval, bval);
1876
0
    ptaDestroy(&pta);
1877
0
    return 0;
1878
0
}
1879
1880
1881
/*!
1882
 * \brief   pixRenderHashBoxBlend()
1883
 *
1884
 * \param[in]    pix        32 bpp
1885
 * \param[in]    box
1886
 * \param[in]    spacing    spacing between lines; must be > 1
1887
 * \param[in]    width      thickness of box and hash lines
1888
 * \param[in]    orient     orientation of lines: L_HORIZONTAL_LINE, ...
1889
 * \param[in]    outline    0 to skip drawing box outline
1890
 * \param[in]    rval, gval, bval
1891
 * \param[in]    fract      in [0.0 - 1.0]: 1.0 is no transparency;
1892
 *                          0.0 is complete transparency (no effect)
1893
 * \return  0 if OK, 1 on error
1894
 */
1895
l_ok
1896
pixRenderHashBoxBlend(PIX       *pix,
1897
                      BOX       *box,
1898
                      l_int32    spacing,
1899
                      l_int32    width,
1900
                      l_int32    orient,
1901
                      l_int32    outline,
1902
                      l_int32    rval,
1903
                      l_int32    gval,
1904
                      l_int32    bval,
1905
                      l_float32  fract)
1906
0
{
1907
0
PTA  *pta;
1908
1909
0
    if (!pix)
1910
0
        return ERROR_INT("pix not defined", __func__, 1);
1911
0
    if (!box)
1912
0
        return ERROR_INT("box not defined", __func__, 1);
1913
0
    if (spacing <= 1)
1914
0
        return ERROR_INT("spacing not > 1", __func__, 1);
1915
0
    if (width < 1) {
1916
0
        L_WARNING("width < 1; setting to 1\n", __func__);
1917
0
        width = 1;
1918
0
    }
1919
0
    if (orient != L_HORIZONTAL_LINE && orient != L_POS_SLOPE_LINE &&
1920
0
        orient != L_VERTICAL_LINE && orient != L_NEG_SLOPE_LINE)
1921
0
        return ERROR_INT("invalid line orientation", __func__, 1);
1922
1923
0
    pta = generatePtaHashBox(box, spacing, width, orient, outline);
1924
0
    if (!pta)
1925
0
        return ERROR_INT("pta not made", __func__, 1);
1926
0
    pixRenderPtaBlend(pix, pta, rval, gval, bval, fract);
1927
0
    ptaDestroy(&pta);
1928
0
    return 0;
1929
0
}
1930
1931
1932
/*!
1933
 * \brief   pixRenderHashMaskArb()
1934
 *
1935
 * \param[in]    pix       any depth; cmapped ok
1936
 * \param[in]    pixm      1 bpp clipping mask for hash marks
1937
 * \param[in]    x,y       UL corner of %pixm with respect to %pix
1938
 * \param[in]    spacing   spacing between lines; must be > 1
1939
 * \param[in]    width     thickness of box and hash lines
1940
 * \param[in]    orient    orientation of lines: L_HORIZONTAL_LINE,
1941
 *                            L_POS_SLOPE_LINE, L_VERTICAL_LINE,
1942
 *                            L_NEG_SLOPE_LINE
1943
 * \param[in]    outline   0 to skip drawing box outline
1944
 * \param[in]    rval, gval, bval
1945
 * \return  0 if OK, 1 on error
1946
 * <pre>
1947
 * Notes:
1948
 *      (1) This is an in-place operation that renders hash lines
1949
 *          through a mask %pixm onto %pix.  The mask origin is
1950
 *          translated by (%x,%y) relative to the origin of %pix.
1951
 * </pre>
1952
 */
1953
l_ok
1954
pixRenderHashMaskArb(PIX     *pix,
1955
                     PIX     *pixm,
1956
                     l_int32  x,
1957
                     l_int32  y,
1958
                     l_int32  spacing,
1959
                     l_int32  width,
1960
                     l_int32  orient,
1961
                     l_int32  outline,
1962
                     l_int32  rval,
1963
                     l_int32  gval,
1964
                     l_int32  bval)
1965
0
{
1966
0
l_int32  w, h;
1967
0
BOX     *box1, *box2;
1968
0
PIX     *pix1;
1969
0
PTA     *pta1, *pta2;
1970
1971
0
    if (!pix)
1972
0
        return ERROR_INT("pix not defined", __func__, 1);
1973
0
    if (!pixm || pixGetDepth(pixm) != 1)
1974
0
        return ERROR_INT("pixm not defined or not 1 bpp", __func__, 1);
1975
0
    if (spacing <= 1)
1976
0
        return ERROR_INT("spacing not > 1", __func__, 1);
1977
0
    if (width < 1) {
1978
0
        L_WARNING("width < 1; setting to 1\n", __func__);
1979
0
        width = 1;
1980
0
    }
1981
0
    if (orient != L_HORIZONTAL_LINE && orient != L_POS_SLOPE_LINE &&
1982
0
        orient != L_VERTICAL_LINE && orient != L_NEG_SLOPE_LINE)
1983
0
        return ERROR_INT("invalid line orientation", __func__, 1);
1984
1985
        /* Get the points for masked hash lines */
1986
0
    pixGetDimensions(pixm, &w, &h, NULL);
1987
0
    box1 = boxCreate(0, 0, w, h);
1988
0
    pta1 = generatePtaHashBox(box1, spacing, width, orient, outline);
1989
0
    pta2 = ptaCropToMask(pta1, pixm);
1990
0
    boxDestroy(&box1);
1991
0
    ptaDestroy(&pta1);
1992
1993
        /* Clip out the region and apply the hash lines */
1994
0
    box2 = boxCreate(x, y, w, h);
1995
0
    pix1 = pixClipRectangle(pix, box2, NULL);
1996
0
    pixRenderPtaArb(pix1, pta2, rval, gval, bval);
1997
0
    ptaDestroy(&pta2);
1998
0
    boxDestroy(&box2);
1999
2000
        /* Rasterop the altered rectangle back in place */
2001
0
    pixRasterop(pix, x, y, w, h, PIX_SRC, pix1, 0, 0);
2002
0
    pixDestroy(&pix1);
2003
0
    return 0;
2004
0
}
2005
2006
2007
/*!
2008
 * \brief   pixRenderHashBoxa()
2009
 *
2010
 * \param[in]    pix       any depth, not cmapped
2011
 * \param[in]    boxa
2012
 * \param[in]    spacing   spacing between lines; must be > 1
2013
 * \param[in]    width     thickness of box and hash lines
2014
 * \param[in]    orient    orientation of lines: L_HORIZONTAL_LINE,
2015
 *                            L_POS_SLOPE_LINE, L_VERTICAL_LINE,
2016
 *                            L_NEG_SLOPE_LINE
2017
 * \param[in]    outline   0 to skip drawing box outline
2018
 * \param[in]    op        one of L_SET_PIXELS, L_CLEAR_PIXELS, L_FLIP_PIXELS
2019
 * \return  0 if OK, 1 on error
2020
 */
2021
l_ok
2022
pixRenderHashBoxa(PIX     *pix,
2023
                  BOXA    *boxa,
2024
                  l_int32  spacing,
2025
                  l_int32  width,
2026
                  l_int32  orient,
2027
                  l_int32  outline,
2028
                  l_int32  op)
2029
0
 {
2030
0
PTA  *pta;
2031
2032
0
    if (!pix)
2033
0
        return ERROR_INT("pix not defined", __func__, 1);
2034
0
    if (!boxa)
2035
0
        return ERROR_INT("boxa not defined", __func__, 1);
2036
0
    if (spacing <= 1)
2037
0
        return ERROR_INT("spacing not > 1", __func__, 1);
2038
0
    if (width < 1) {
2039
0
        L_WARNING("width < 1; setting to 1\n", __func__);
2040
0
        width = 1;
2041
0
    }
2042
0
    if (orient != L_HORIZONTAL_LINE && orient != L_POS_SLOPE_LINE &&
2043
0
        orient != L_VERTICAL_LINE && orient != L_NEG_SLOPE_LINE)
2044
0
        return ERROR_INT("invalid line orientation", __func__, 1);
2045
0
    if (op != L_SET_PIXELS && op != L_CLEAR_PIXELS && op != L_FLIP_PIXELS)
2046
0
        return ERROR_INT("invalid op", __func__, 1);
2047
2048
0
    pta = generatePtaHashBoxa(boxa, spacing, width, orient, outline, 1);
2049
0
    if (!pta)
2050
0
        return ERROR_INT("pta not made", __func__, 1);
2051
0
    pixRenderPta(pix, pta, op);
2052
0
    ptaDestroy(&pta);
2053
0
    return 0;
2054
0
}
2055
2056
2057
/*!
2058
 * \brief   pixRenderHashBoxaArb()
2059
 *
2060
 * \param[in]    pix         any depth; cmapped ok
2061
 * \param[in]    box
2062
 * \param[in]    spacing     spacing between lines; must be > 1
2063
 * \param[in]    width       thickness of box and hash lines
2064
 * \param[in]    orient      orientation of lines: L_HORIZONTAL_LINE,
2065
 *                              L_POS_SLOPE_LINE, L_VERTICAL_LINE,
2066
 *                              L_NEG_SLOPE_LINE
2067
 * \param[in]    outline     0 to skip drawing box outline
2068
 * \param[in]    rval, gval, bval
2069
 * \return  0 if OK, 1 on error
2070
 */
2071
l_ok
2072
pixRenderHashBoxaArb(PIX     *pix,
2073
                     BOXA    *boxa,
2074
                     l_int32  spacing,
2075
                     l_int32  width,
2076
                     l_int32  orient,
2077
                     l_int32  outline,
2078
                     l_int32  rval,
2079
                     l_int32  gval,
2080
                     l_int32  bval)
2081
0
{
2082
0
PTA  *pta;
2083
2084
0
    if (!pix)
2085
0
        return ERROR_INT("pix not defined", __func__, 1);
2086
0
    if (!boxa)
2087
0
        return ERROR_INT("boxa not defined", __func__, 1);
2088
0
    if (spacing <= 1)
2089
0
        return ERROR_INT("spacing not > 1", __func__, 1);
2090
0
    if (width < 1) {
2091
0
        L_WARNING("width < 1; setting to 1\n", __func__);
2092
0
        width = 1;
2093
0
    }
2094
0
    if (orient != L_HORIZONTAL_LINE && orient != L_POS_SLOPE_LINE &&
2095
0
        orient != L_VERTICAL_LINE && orient != L_NEG_SLOPE_LINE)
2096
0
        return ERROR_INT("invalid line orientation", __func__, 1);
2097
2098
0
    pta = generatePtaHashBoxa(boxa, spacing, width, orient, outline, 1);
2099
0
    if (!pta)
2100
0
        return ERROR_INT("pta not made", __func__, 1);
2101
0
    pixRenderPtaArb(pix, pta, rval, gval, bval);
2102
0
    ptaDestroy(&pta);
2103
0
    return 0;
2104
0
}
2105
2106
2107
/*!
2108
 * \brief   pixRenderHashBoxaBlend()
2109
 *
2110
 * \param[in]    pix         32 bpp rgb
2111
 * \param[in]    boxa
2112
 * \param[in]    spacing     spacing between lines; must be > 1
2113
 * \param[in]    width       thickness of box and hash lines
2114
 * \param[in]    orient      orientation of lines: L_HORIZONTAL_LINE,
2115
 *                              L_POS_SLOPE_LINE, L_VERTICAL_LINE,
2116
 *                              L_NEG_SLOPE_LINE
2117
 * \param[in]    outline     0 to skip drawing box outline
2118
 * \param[in]    rval, gval, bval
2119
 * \param[in]    fract       in [0.0 - 1.0]: 1.0 is no transparency;
2120
 *                           0.0 is complete transparency (no effect)
2121
 * \return  0 if OK, 1 on error
2122
 */
2123
l_ok
2124
pixRenderHashBoxaBlend(PIX       *pix,
2125
                       BOXA      *boxa,
2126
                       l_int32    spacing,
2127
                       l_int32    width,
2128
                       l_int32    orient,
2129
                       l_int32    outline,
2130
                       l_int32    rval,
2131
                       l_int32    gval,
2132
                       l_int32    bval,
2133
                       l_float32  fract)
2134
0
{
2135
0
PTA  *pta;
2136
2137
0
    if (!pix)
2138
0
        return ERROR_INT("pix not defined", __func__, 1);
2139
0
    if (!boxa)
2140
0
        return ERROR_INT("boxa not defined", __func__, 1);
2141
0
    if (spacing <= 1)
2142
0
        return ERROR_INT("spacing not > 1", __func__, 1);
2143
0
    if (width < 1) {
2144
0
        L_WARNING("width < 1; setting to 1\n", __func__);
2145
0
        width = 1;
2146
0
    }
2147
0
    if (orient != L_HORIZONTAL_LINE && orient != L_POS_SLOPE_LINE &&
2148
0
        orient != L_VERTICAL_LINE && orient != L_NEG_SLOPE_LINE)
2149
0
        return ERROR_INT("invalid line orientation", __func__, 1);
2150
2151
0
    pta = generatePtaHashBoxa(boxa, spacing, width, orient, outline, 1);
2152
0
    if (!pta)
2153
0
        return ERROR_INT("pta not made", __func__, 1);
2154
0
    pixRenderPtaBlend(pix, pta, rval, gval, bval, fract);
2155
0
    ptaDestroy(&pta);
2156
0
    return 0;
2157
0
}
2158
2159
2160
/*!
2161
 * \brief   pixRenderPolyline()
2162
 *
2163
 * \param[in]    pix         any depth, not cmapped
2164
 * \param[in]    ptas
2165
 * \param[in]    width       thickness of line
2166
 * \param[in]    op          one of L_SET_PIXELS, L_CLEAR_PIXELS, L_FLIP_PIXELS
2167
 * \param[in]    closeflag   1 to close the contour; 0 otherwise
2168
 * \return  0 if OK, 1 on error
2169
 *
2170
 * <pre>
2171
 * Notes:
2172
 *      This renders a closed contour.
2173
 * </pre>
2174
 */
2175
l_ok
2176
pixRenderPolyline(PIX     *pix,
2177
                  PTA     *ptas,
2178
                  l_int32  width,
2179
                  l_int32  op,
2180
                  l_int32  closeflag)
2181
0
{
2182
0
PTA  *pta;
2183
2184
0
    if (!pix)
2185
0
        return ERROR_INT("pix not defined", __func__, 1);
2186
0
    if (!ptas)
2187
0
        return ERROR_INT("ptas not defined", __func__, 1);
2188
0
    if (width < 1) {
2189
0
        L_WARNING("width < 1; setting to 1\n", __func__);
2190
0
        width = 1;
2191
0
    }
2192
0
    if (op != L_SET_PIXELS && op != L_CLEAR_PIXELS && op != L_FLIP_PIXELS)
2193
0
        return ERROR_INT("invalid op", __func__, 1);
2194
2195
0
    if ((pta = generatePtaPolyline(ptas, width, closeflag, 0)) == NULL)
2196
0
        return ERROR_INT("pta not made", __func__, 1);
2197
0
    pixRenderPta(pix, pta, op);
2198
0
    ptaDestroy(&pta);
2199
0
    return 0;
2200
0
}
2201
2202
2203
/*!
2204
 * \brief   pixRenderPolylineArb()
2205
 *
2206
 * \param[in]    pix         any depth; cmapped ok
2207
 * \param[in]    ptas
2208
 * \param[in]    width       thickness of line
2209
 * \param[in]    rval, gval, bval
2210
 * \param[in]    closeflag   1 to close the contour; 0 otherwise
2211
 * \return  0 if OK, 1 on error
2212
 *
2213
 * <pre>
2214
 * Notes:
2215
 *      This renders a closed contour.
2216
 * </pre>
2217
 */
2218
l_ok
2219
pixRenderPolylineArb(PIX     *pix,
2220
                     PTA     *ptas,
2221
                     l_int32  width,
2222
                     l_uint8  rval,
2223
                     l_uint8  gval,
2224
                     l_uint8  bval,
2225
                     l_int32  closeflag)
2226
0
{
2227
0
PTA  *pta;
2228
2229
0
    if (!pix)
2230
0
        return ERROR_INT("pix not defined", __func__, 1);
2231
0
    if (!ptas)
2232
0
        return ERROR_INT("ptas not defined", __func__, 1);
2233
0
    if (width < 1) {
2234
0
        L_WARNING("width < 1; setting to 1\n", __func__);
2235
0
        width = 1;
2236
0
    }
2237
2238
0
    if ((pta = generatePtaPolyline(ptas, width, closeflag, 0)) == NULL)
2239
0
        return ERROR_INT("pta not made", __func__, 1);
2240
0
    pixRenderPtaArb(pix, pta, rval, gval, bval);
2241
0
    ptaDestroy(&pta);
2242
0
    return 0;
2243
0
}
2244
2245
2246
/*!
2247
 * \brief   pixRenderPolylineBlend()
2248
 *
2249
 * \param[in]    pix          32 bpp rgb
2250
 * \param[in]    ptas
2251
 * \param[in]    width        thickness of line
2252
 * \param[in]    rval, gval, bval
2253
 * \param[in]    fract        in [0.0 - 1.0]: 1.0 is no transparency;
2254
 *                            0.0 is complete transparency (no effect)
2255
 * \param[in]    closeflag    1 to close the contour; 0 otherwise
2256
 * \param[in]    removedups   1 to remove; 0 otherwise
2257
 * \return  0 if OK, 1 on error
2258
 */
2259
l_ok
2260
pixRenderPolylineBlend(PIX       *pix,
2261
                       PTA       *ptas,
2262
                       l_int32    width,
2263
                       l_uint8    rval,
2264
                       l_uint8    gval,
2265
                       l_uint8    bval,
2266
                       l_float32  fract,
2267
                       l_int32    closeflag,
2268
                       l_int32    removedups)
2269
0
{
2270
0
PTA  *pta;
2271
2272
0
    if (!pix)
2273
0
        return ERROR_INT("pix not defined", __func__, 1);
2274
0
    if (!ptas)
2275
0
        return ERROR_INT("ptas not defined", __func__, 1);
2276
0
    if (width < 1) {
2277
0
        L_WARNING("width < 1; setting to 1\n", __func__);
2278
0
        width = 1;
2279
0
    }
2280
2281
0
    if ((pta = generatePtaPolyline(ptas, width, closeflag, removedups)) == NULL)
2282
0
        return ERROR_INT("pta not made", __func__, 1);
2283
0
    pixRenderPtaBlend(pix, pta, rval, gval, bval, fract);
2284
0
    ptaDestroy(&pta);
2285
0
    return 0;
2286
0
}
2287
2288
2289
/*!
2290
 * \brief   pixRenderGridArb()
2291
 *
2292
 * \param[in]    pix        any depth, cmapped ok
2293
 * \param[in]    nx, ny     number of rectangles in each direction
2294
 * \param[in]    width      thickness of grid lines
2295
 * \param[in]    rval, gval, bval
2296
 * \return  0 if OK, 1 on error
2297
 */
2298
l_ok
2299
pixRenderGridArb(PIX     *pix,
2300
                 l_int32  nx,
2301
                 l_int32  ny,
2302
                 l_int32  width,
2303
                 l_uint8  rval,
2304
                 l_uint8  gval,
2305
                 l_uint8  bval)
2306
0
{
2307
0
l_int32  w, h;
2308
0
PTA     *pta;
2309
2310
0
    if (!pix)
2311
0
        return ERROR_INT("pix not defined", __func__, 1);
2312
0
    if (nx < 1 || ny < 1)
2313
0
        return ERROR_INT("nx, ny must be > 0", __func__, 1);
2314
0
    if (width < 1) {
2315
0
        L_WARNING("width < 1; setting to 1\n", __func__);
2316
0
        width = 1;
2317
0
    }
2318
2319
0
    pixGetDimensions(pix, &w, &h, NULL);
2320
0
    if ((pta = generatePtaGrid(w, h, nx, ny, width)) == NULL)
2321
0
        return ERROR_INT("pta not made", __func__, 1);
2322
0
    pixRenderPtaArb(pix, pta, rval, gval, bval);
2323
0
    ptaDestroy(&pta);
2324
0
    return 0;
2325
0
}
2326
2327
2328
/*!
2329
 * \brief   pixRenderRandomCmapPtaa()
2330
 *
2331
 * \param[in]    pix          1, 2, 4, 8, 16, 32 bpp
2332
 * \param[in]    ptaa
2333
 * \param[in]    polyflag     1 to interpret each Pta as a polyline;
2334
 *                            0 to simply render the Pta as a set of pixels
2335
 * \param[in]    width        thickness of line; use only for polyline
2336
 * \param[in]    closeflag    1 to close the contour; 0 otherwise;
2337
 *                            use only for polyline mode
2338
 * \return  pixd cmapped, 8 bpp or NULL on error
2339
 *
2340
 * <pre>
2341
 * Notes:
2342
 *      (1) This is a debugging routine, that displays a set of
2343
 *          pixels, selected by the set of Ptas in a Ptaa,
2344
 *          in a random color in a pix.
2345
 *      (2) If %polyflag == 1, each Pta is considered to be a polyline,
2346
 *          and is rendered using %width and %closeflag.  Each polyline
2347
 *          is rendered in a random color.
2348
 *      (3) If %polyflag == 0, all points in each Pta are rendered in a
2349
 *          random color.  The %width and %closeflag parameters are ignored.
2350
 *      (4) The output pix is 8 bpp and colormapped.  Up to 254
2351
 *          different, randomly selected colors, can be used.
2352
 *      (5) The rendered pixels replace the input pixels.  They will
2353
 *          be clipped silently to the input pix.
2354
 * </pre>
2355
 */
2356
PIX  *
2357
pixRenderRandomCmapPtaa(PIX     *pix,
2358
                        PTAA    *ptaa,
2359
                        l_int32  polyflag,
2360
                        l_int32  width,
2361
                        l_int32  closeflag)
2362
0
{
2363
0
l_int32   i, n, index, rval, gval, bval;
2364
0
PIXCMAP  *cmap;
2365
0
PTA      *pta, *ptat;
2366
0
PIX      *pixd;
2367
2368
0
    if (!pix)
2369
0
        return (PIX *)ERROR_PTR("pix not defined", __func__, NULL);
2370
0
    if (!ptaa)
2371
0
        return (PIX *)ERROR_PTR("ptaa not defined", __func__, NULL);
2372
0
    if (polyflag != 0 && width < 1) {
2373
0
        L_WARNING("width < 1; setting to 1\n", __func__);
2374
0
        width = 1;
2375
0
    }
2376
2377
0
    pixd = pixConvertTo8(pix, FALSE);
2378
0
    cmap = pixcmapCreateRandom(8, 1, 1);
2379
0
    pixSetColormap(pixd, cmap);
2380
2381
0
    if ((n = ptaaGetCount(ptaa)) == 0)
2382
0
        return pixd;
2383
2384
0
    for (i = 0; i < n; i++) {
2385
0
        index = 1 + (i % 254);
2386
0
        pixcmapGetColor(cmap, index, &rval, &gval, &bval);
2387
0
        pta = ptaaGetPta(ptaa, i, L_CLONE);
2388
0
        if (polyflag)
2389
0
            ptat = generatePtaPolyline(pta, width, closeflag, 0);
2390
0
        else
2391
0
            ptat = ptaClone(pta);
2392
0
        pixRenderPtaArb(pixd, ptat, rval, gval, bval);
2393
0
        ptaDestroy(&pta);
2394
0
        ptaDestroy(&ptat);
2395
0
    }
2396
2397
0
    return pixd;
2398
0
}
2399
2400
2401
2402
/*------------------------------------------------------------------*
2403
 *                Rendering and filling of polygons                 *
2404
 *------------------------------------------------------------------*/
2405
/*!
2406
 * \brief   pixRenderPolygon()
2407
 *
2408
 * \param[in]    ptas     of vertices, none repeated
2409
 * \param[in]    width    of polygon outline
2410
 * \param[out]   pxmin    [optional] min x value of input pts
2411
 * \param[out]   pymin    [optional] min y value of input pts
2412
 * \return  pix 1 bpp, with outline generated, or NULL on error
2413
 *
2414
 * <pre>
2415
 * Notes:
2416
 *      (1) The pix is the minimum size required to contain the origin
2417
 *          and the polygon.  For example, the max x value of the input
2418
 *          points is w - 1, where w is the pix width.
2419
 *      (2) The rendered line is 4-connected, so that an interior or
2420
 *          exterior 8-c.c. flood fill operation works properly.
2421
 * </pre>
2422
 */
2423
PIX *
2424
pixRenderPolygon(PTA      *ptas,
2425
                 l_int32   width,
2426
                 l_int32  *pxmin,
2427
                 l_int32  *pymin)
2428
0
{
2429
0
l_float32  fxmin, fxmax, fymin, fymax;
2430
0
PIX       *pixd;
2431
0
PTA       *pta1, *pta2;
2432
2433
0
    if (pxmin) *pxmin = 0;
2434
0
    if (pymin) *pymin = 0;
2435
0
    if (!ptas)
2436
0
        return (PIX *)ERROR_PTR("ptas not defined", __func__, NULL);
2437
2438
        /* Generate a 4-connected polygon line */
2439
0
    if ((pta1 = generatePtaPolyline(ptas, width, 1, 0)) == NULL)
2440
0
        return (PIX *)ERROR_PTR("pta1 not made", __func__, NULL);
2441
0
    if (width < 2)
2442
0
        pta2 = convertPtaLineTo4cc(pta1);
2443
0
    else
2444
0
        pta2 = ptaClone(pta1);
2445
2446
        /* Render onto a minimum-sized pix */
2447
0
    ptaGetRange(pta2, &fxmin, &fxmax, &fymin, &fymax);
2448
0
    if (pxmin) *pxmin = (l_int32)(fxmin + 0.5);
2449
0
    if (pymin) *pymin = (l_int32)(fymin + 0.5);
2450
0
    pixd = pixCreate((l_int32)(fxmax + 0.5) + 1, (l_int32)(fymax + 0.5) + 1, 1);
2451
0
    pixRenderPolyline(pixd, pta2, width, L_SET_PIXELS, 1);
2452
0
    ptaDestroy(&pta1);
2453
0
    ptaDestroy(&pta2);
2454
0
    return pixd;
2455
0
}
2456
2457
2458
/*!
2459
 * \brief   pixFillPolygon()
2460
 *
2461
 * \param[in]    pixs          1 bpp, with 4-connected polygon outline
2462
 * \param[in]    pta           vertices of the polygon
2463
 * \param[in]    xmin, ymin    min values of vertices of polygon
2464
 * \return  pixd with outline filled, or NULL on error
2465
 *
2466
 * <pre>
2467
 * Notes:
2468
 *      (1) This fills the interior of the polygon, returning a
2469
 *          new pix.  It works for both convex and non-convex polygons.
2470
 *      (2) To generate a filled polygon from %pta:
2471
 *            PIX *pixt = pixRenderPolygon(pta, 1, &xmin, &ymin);
2472
 *            PIX *pixd = pixFillPolygon(pixt, pta, xmin, ymin);
2473
 *            pixDestroy(&pixt);
2474
 * </pre>
2475
 */
2476
PIX *
2477
pixFillPolygon(PIX     *pixs,
2478
               PTA     *pta,
2479
               l_int32  xmin,
2480
               l_int32  ymin)
2481
0
{
2482
0
l_int32   w, h, i, n, inside, found;
2483
0
l_int32  *xstart, *xend;
2484
0
PIX      *pixi, *pixd;
2485
2486
0
    if (!pixs || (pixGetDepth(pixs) != 1))
2487
0
        return (PIX *)ERROR_PTR("pixs undefined or not 1 bpp", __func__, NULL);
2488
0
    if (!pta)
2489
0
        return (PIX *)ERROR_PTR("pta not defined", __func__, NULL);
2490
0
    if (ptaGetCount(pta) < 2)
2491
0
        return (PIX *)ERROR_PTR("pta has < 2 pts", __func__, NULL);
2492
2493
0
    pixGetDimensions(pixs, &w, &h, NULL);
2494
0
    xstart = (l_int32 *)LEPT_CALLOC(L_MAX(1, w / 2), sizeof(l_int32));
2495
0
    xend = (l_int32 *)LEPT_CALLOC(L_MAX(1, w / 2), sizeof(l_int32));
2496
0
    if (!xstart || !xend) {
2497
0
        LEPT_FREE(xstart);
2498
0
        LEPT_FREE(xend);
2499
0
        return (PIX *)ERROR_PTR("xstart and xend not made", __func__, NULL);
2500
0
    }
2501
2502
        /* Find a raster with 2 or more black runs.  The first background
2503
         * pixel after the end of the first run is likely to be inside
2504
         * the polygon, and can be used as a seed pixel. */
2505
0
    found = FALSE;
2506
0
    for (i = ymin + 1; i < h; i++) {
2507
0
        pixFindHorizontalRuns(pixs, i, xstart, xend, &n);
2508
0
        if (n > 1) {
2509
0
            ptaPtInsidePolygon(pta, xend[0] + 1, i, &inside);
2510
0
            if (inside) {
2511
0
                found = TRUE;
2512
0
                break;
2513
0
            }
2514
0
        }
2515
0
    }
2516
0
    if (!found) {
2517
0
        L_WARNING("nothing found to fill\n", __func__);
2518
0
        LEPT_FREE(xstart);
2519
0
        LEPT_FREE(xend);
2520
0
        return 0;
2521
0
    }
2522
2523
        /* Place the seed pixel in the output image */
2524
0
    pixd = pixCreateTemplate(pixs);
2525
0
    pixSetPixel(pixd, xend[0] + 1, i, 1);
2526
2527
        /* Invert pixs to make a filling mask, and fill from the seed */
2528
0
    pixi = pixInvert(NULL, pixs);
2529
0
    pixSeedfillBinary(pixd, pixd, pixi, 4);
2530
2531
        /* Add the pixels of the original polygon outline */
2532
0
    pixOr(pixd, pixd, pixs);
2533
2534
0
    pixDestroy(&pixi);
2535
0
    LEPT_FREE(xstart);
2536
0
    LEPT_FREE(xend);
2537
0
    return pixd;
2538
0
}
2539
2540
2541
/*------------------------------------------------------------------*
2542
 *             Contour rendering on grayscale images                *
2543
 *------------------------------------------------------------------*/
2544
/*!
2545
 * \brief   pixRenderContours()
2546
 *
2547
 * \param[in]    pixs        8 or 16 bpp; no colormap
2548
 * \param[in]    startval    value of lowest contour; must be in [0 ... maxval]
2549
 * \param[in]    incr        increment to next contour; must be > 0
2550
 * \param[in]    outdepth    either 1 or depth of pixs
2551
 * \return  pixd, or NULL on error
2552
 *
2553
 * <pre>
2554
 * Notes:
2555
 *      (1) The output can be either 1 bpp, showing just the contour
2556
 *          lines, or a copy of the input pixs with the contour lines
2557
 *          superposed.
2558
 * </pre>
2559
 */
2560
PIX *
2561
pixRenderContours(PIX     *pixs,
2562
                  l_int32  startval,
2563
                  l_int32  incr,
2564
                  l_int32  outdepth)
2565
0
{
2566
0
l_int32    w, h, d, maxval, wpls, wpld, i, j, val, test;
2567
0
l_uint32  *datas, *datad, *lines, *lined;
2568
0
PIX       *pixd;
2569
2570
0
    if (!pixs)
2571
0
        return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL);
2572
0
    if (pixGetColormap(pixs))
2573
0
        return (PIX *)ERROR_PTR("pixs has colormap", __func__, NULL);
2574
0
    pixGetDimensions(pixs, &w, &h, &d);
2575
0
    if (d != 8 && d != 16)
2576
0
        return (PIX *)ERROR_PTR("pixs not 8 or 16 bpp", __func__, NULL);
2577
0
    if (outdepth != 1 && outdepth != d) {
2578
0
        L_WARNING("invalid outdepth; setting to 1\n", __func__);
2579
0
        outdepth = 1;
2580
0
    }
2581
0
    maxval = (1 << d) - 1;
2582
0
    if (startval < 0 || startval > maxval)
2583
0
        return (PIX *)ERROR_PTR("startval not in [0 ... maxval]",
2584
0
               __func__, NULL);
2585
0
    if (incr < 1)
2586
0
        return (PIX *)ERROR_PTR("incr < 1", __func__, NULL);
2587
2588
0
    if (outdepth == d)
2589
0
        pixd = pixCopy(NULL, pixs);
2590
0
    else
2591
0
        pixd = pixCreate(w, h, 1);
2592
2593
0
    pixCopyResolution(pixd, pixs);
2594
0
    datad = pixGetData(pixd);
2595
0
    wpld = pixGetWpl(pixd);
2596
0
    datas = pixGetData(pixs);
2597
0
    wpls = pixGetWpl(pixs);
2598
2599
0
    switch (d)
2600
0
    {
2601
0
    case 8:
2602
0
        if (outdepth == 1) {
2603
0
            for (i = 0; i < h; i++) {
2604
0
                lines = datas + i * wpls;
2605
0
                lined = datad + i * wpld;
2606
0
                for (j = 0; j < w; j++) {
2607
0
                    val = GET_DATA_BYTE(lines, j);
2608
0
                    if (val < startval)
2609
0
                        continue;
2610
0
                    test = (val - startval) % incr;
2611
0
                    if (!test)
2612
0
                        SET_DATA_BIT(lined, j);
2613
0
                }
2614
0
            }
2615
0
        } else {  /* outdepth == d */
2616
0
            for (i = 0; i < h; i++) {
2617
0
                lines = datas + i * wpls;
2618
0
                lined = datad + i * wpld;
2619
0
                for (j = 0; j < w; j++) {
2620
0
                    val = GET_DATA_BYTE(lines, j);
2621
0
                    if (val < startval)
2622
0
                        continue;
2623
0
                    test = (val - startval) % incr;
2624
0
                    if (!test)
2625
0
                        SET_DATA_BYTE(lined, j, 0);
2626
0
                }
2627
0
            }
2628
0
        }
2629
0
        break;
2630
2631
0
    case 16:
2632
0
        if (outdepth == 1) {
2633
0
            for (i = 0; i < h; i++) {
2634
0
                lines = datas + i * wpls;
2635
0
                lined = datad + i * wpld;
2636
0
                for (j = 0; j < w; j++) {
2637
0
                    val = GET_DATA_TWO_BYTES(lines, j);
2638
0
                    if (val < startval)
2639
0
                        continue;
2640
0
                    test = (val - startval) % incr;
2641
0
                    if (!test)
2642
0
                        SET_DATA_BIT(lined, j);
2643
0
                }
2644
0
            }
2645
0
        } else {  /* outdepth == d */
2646
0
            for (i = 0; i < h; i++) {
2647
0
                lines = datas + i * wpls;
2648
0
                lined = datad + i * wpld;
2649
0
                for (j = 0; j < w; j++) {
2650
0
                    val = GET_DATA_TWO_BYTES(lines, j);
2651
0
                    if (val < startval)
2652
0
                        continue;
2653
0
                    test = (val - startval) % incr;
2654
0
                    if (!test)
2655
0
                        SET_DATA_TWO_BYTES(lined, j, 0);
2656
0
                }
2657
0
            }
2658
0
        }
2659
0
        break;
2660
2661
0
    default:
2662
0
        return (PIX *)ERROR_PTR("pixs not 8 or 16 bpp", __func__, NULL);
2663
0
    }
2664
2665
0
    return pixd;
2666
0
}
2667
2668
2669
/*!
2670
 * \brief   fpixAutoRenderContours()
2671
 *
2672
 * \param[in]    fpix
2673
 * \param[in]    ncontours   in [2 ... 500]; typically about 50
2674
 * \return  pixd 8 bpp, or NULL on error
2675
 *
2676
 * <pre>
2677
 * Notes:
2678
 *      (1) The increment is set to get approximately %ncontours.
2679
 *      (2) The proximity to the target value for contour display
2680
 *          is set to 0.15.
2681
 *      (3) Negative values are rendered in red; positive values as black.
2682
 * </pre>
2683
 */
2684
PIX *
2685
fpixAutoRenderContours(FPIX    *fpix,
2686
                       l_int32  ncontours)
2687
0
{
2688
0
l_float32  minval, maxval, incr;
2689
2690
0
    if (!fpix)
2691
0
        return (PIX *)ERROR_PTR("fpix not defined", __func__, NULL);
2692
0
    if (ncontours < 2 || ncontours > 500)
2693
0
        return (PIX *)ERROR_PTR("ncontours < 2 or > 500", __func__, NULL);
2694
2695
0
    fpixGetMin(fpix, &minval, NULL, NULL);
2696
0
    fpixGetMax(fpix, &maxval, NULL, NULL);
2697
0
    if (minval == maxval)
2698
0
        return (PIX *)ERROR_PTR("all values in fpix are equal", __func__, NULL);
2699
0
    incr = (maxval - minval) / ((l_float32)ncontours - 1);
2700
0
    return fpixRenderContours(fpix, incr, 0.15f);
2701
0
}
2702
2703
2704
/*!
2705
 * \brief   fpixRenderContours()
2706
 *
2707
 * \param[in]    fpixs
2708
 * \param[in]    incr      increment between contours; must be > 0.0
2709
 * \param[in]    proxim    required proximity to target value; default 0.15
2710
 * \return  pixd 8 bpp, or NULL on error
2711
 *
2712
 * <pre>
2713
 * Notes:
2714
 *      (1) Values are displayed when val/incr is within +-proxim
2715
 *          to an integer.  The default value is 0.15; smaller values
2716
 *          result in thinner contour lines.
2717
 *      (2) Negative values are rendered in red; positive values as black.
2718
 * </pre>
2719
 */
2720
PIX *
2721
fpixRenderContours(FPIX      *fpixs,
2722
                   l_float32  incr,
2723
                   l_float32  proxim)
2724
0
{
2725
0
l_int32     i, j, w, h, wpls, wpld;
2726
0
l_float32   val, invincr, finter, above, below, diff;
2727
0
l_uint32   *datad, *lined;
2728
0
l_float32  *datas, *lines;
2729
0
PIX        *pixd;
2730
0
PIXCMAP    *cmap;
2731
2732
0
    if (!fpixs)
2733
0
        return (PIX *)ERROR_PTR("fpixs not defined", __func__, NULL);
2734
0
    if (incr <= 0.0)
2735
0
        return (PIX *)ERROR_PTR("incr <= 0.0", __func__, NULL);
2736
0
    if (proxim <= 0.0)
2737
0
        proxim = 0.15f;  /* default */
2738
2739
0
    fpixGetDimensions(fpixs, &w, &h);
2740
0
    if ((pixd = pixCreate(w, h, 8)) == NULL)
2741
0
        return (PIX *)ERROR_PTR("pixd not made", __func__, NULL);
2742
0
    cmap = pixcmapCreate(8);
2743
0
    pixSetColormap(pixd, cmap);
2744
0
    pixcmapAddColor(cmap, 255, 255, 255);  /* white */
2745
0
    pixcmapAddColor(cmap, 0, 0, 0);  /* black */
2746
0
    pixcmapAddColor(cmap, 255, 0, 0);  /* red */
2747
2748
0
    datas = fpixGetData(fpixs);
2749
0
    wpls = fpixGetWpl(fpixs);
2750
0
    datad = pixGetData(pixd);
2751
0
    wpld = pixGetWpl(pixd);
2752
0
    invincr = 1.0 / incr;
2753
0
    for (i = 0; i < h; i++) {
2754
0
        lines = datas + i * wpls;
2755
0
        lined = datad + i * wpld;
2756
0
        for (j = 0; j < w; j++) {
2757
0
            val = lines[j];
2758
0
            finter = invincr * val;
2759
0
            above = finter - floorf(finter);
2760
0
            below = ceilf(finter) - finter;
2761
0
            diff = L_MIN(above, below);
2762
0
            if (diff <= proxim) {
2763
0
                if (val < 0.0)
2764
0
                    SET_DATA_BYTE(lined, j, 2);
2765
0
                else
2766
0
                    SET_DATA_BYTE(lined, j, 1);
2767
0
            }
2768
0
        }
2769
0
    }
2770
2771
0
    return pixd;
2772
0
}
2773
2774
2775
/*------------------------------------------------------------------*
2776
 *             Boundary pt generation on 1 bpp images               *
2777
 *------------------------------------------------------------------*/
2778
/*!
2779
 * \brief   pixGeneratePtaBoundary()
2780
 *
2781
 * \param[in]    pixs     1 bpp
2782
 * \param[in]    width    of boundary line
2783
 * \return  pta, or NULL on error
2784
 *
2785
 * <pre>
2786
 * Notes:
2787
 *      (1) Similar to ptaGetBoundaryPixels(), except here:
2788
 *          * we only get pixels in the foreground
2789
 *          * we can have a "line" width greater than 1 pixel.
2790
 *      (2) Once generated, this can be applied to a random 1 bpp image
2791
 *          to add a color boundary as follows:
2792
 *             Pta *pta = pixGeneratePtaBoundary(pixs, width);
2793
 *             Pix *pix1 = pixConvert1To8Cmap(pixs);
2794
 *             pixRenderPtaArb(pix1, pta, rval, gval, bval);
2795
 * </pre>
2796
 */
2797
PTA  *
2798
pixGeneratePtaBoundary(PIX     *pixs,
2799
                       l_int32  width)
2800
0
{
2801
0
PIX  *pix1;
2802
0
PTA  *pta;
2803
2804
0
    if (!pixs || pixGetDepth(pixs) != 1)
2805
0
        return (PTA *)ERROR_PTR("pixs undefined or not 1 bpp", __func__, NULL);
2806
0
    if (width < 1) {
2807
0
        L_WARNING("width < 1; setting to 1\n", __func__);
2808
0
        width = 1;
2809
0
    }
2810
2811
0
    pix1 = pixErodeBrick(NULL, pixs, 2 * width + 1, 2 * width + 1);
2812
0
    pixXor(pix1, pix1, pixs);
2813
0
    pta = ptaGetPixelsFromPix(pix1, NULL);
2814
0
    pixDestroy(&pix1);
2815
0
    return pta;
2816
0
}