Mercurial > hgrepos > Python2 > PyMuPDF
comparison mupdf-source/thirdparty/leptonica/src/graphics.c @ 2:b50eed0cc0ef upstream
ADD: MuPDF v1.26.7: the MuPDF source as downloaded by a default build of PyMuPDF 1.26.4.
The directory name has changed: no version number in the expanded directory now.
| author | Franz Glasner <fzglas.hg@dom66.de> |
|---|---|
| date | Mon, 15 Sep 2025 11:43:07 +0200 |
| parents | |
| children |
comparison
equal
deleted
inserted
replaced
| 1:1d09e1dec1d9 | 2:b50eed0cc0ef |
|---|---|
| 1 /*====================================================================* | |
| 2 - 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 { | |
| 146 l_int32 npts, diff, getyofx, sign, i, x, y; | |
| 147 l_float32 slope; | |
| 148 PTA *pta; | |
| 149 | |
| 150 /* Generate line parameters */ | |
| 151 if (x1 == x2 && y1 == y2) { /* same point */ | |
| 152 getyofx = TRUE; | |
| 153 npts = 1; | |
| 154 } else if (L_ABS(x2 - x1) >= L_ABS(y2 - y1)) { | |
| 155 getyofx = TRUE; | |
| 156 npts = L_ABS(x2 - x1) + 1; | |
| 157 diff = x2 - x1; | |
| 158 sign = L_SIGN(x2 - x1); | |
| 159 slope = (l_float32)(sign * (y2 - y1)) / (l_float32)diff; | |
| 160 } else { | |
| 161 getyofx = FALSE; | |
| 162 npts = L_ABS(y2 - y1) + 1; | |
| 163 diff = y2 - y1; | |
| 164 sign = L_SIGN(y2 - y1); | |
| 165 slope = (l_float32)(sign * (x2 - x1)) / (l_float32)diff; | |
| 166 } | |
| 167 | |
| 168 if ((pta = ptaCreate(npts)) == NULL) | |
| 169 return (PTA *)ERROR_PTR("pta not made", __func__, NULL); | |
| 170 | |
| 171 if (npts == 1) { /* degenerate case */ | |
| 172 ptaAddPt(pta, x1, y1); | |
| 173 return pta; | |
| 174 } | |
| 175 | |
| 176 /* Generate the set of points */ | |
| 177 if (getyofx) { /* y = y(x) */ | |
| 178 for (i = 0; i < npts; i++) { | |
| 179 x = x1 + sign * i; | |
| 180 y = (l_int32)(y1 + (l_float32)i * slope + 0.5); | |
| 181 ptaAddPt(pta, x, y); | |
| 182 } | |
| 183 } else { /* x = x(y) */ | |
| 184 for (i = 0; i < npts; i++) { | |
| 185 x = (l_int32)(x1 + (l_float32)i * slope + 0.5); | |
| 186 y = y1 + sign * i; | |
| 187 ptaAddPt(pta, x, y); | |
| 188 } | |
| 189 } | |
| 190 | |
| 191 return pta; | |
| 192 } | |
| 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 { | |
| 210 l_int32 i, x1a, x2a, y1a, y2a; | |
| 211 PTA *pta, *ptaj; | |
| 212 | |
| 213 if (width < 1) { | |
| 214 L_WARNING("width < 1; setting to 1\n", __func__); | |
| 215 width = 1; | |
| 216 } | |
| 217 | |
| 218 if ((ptaj = generatePtaLine(x1, y1, x2, y2)) == NULL) | |
| 219 return (PTA *)ERROR_PTR("ptaj not made", __func__, NULL); | |
| 220 if (width == 1) | |
| 221 return ptaj; | |
| 222 | |
| 223 /* width > 1; estimate line direction & join */ | |
| 224 if (L_ABS(x1 - x2) > L_ABS(y1 - y2)) { /* "horizontal" line */ | |
| 225 for (i = 1; i < width; i++) { | |
| 226 if ((i & 1) == 1) { /* place above */ | |
| 227 y1a = y1 - (i + 1) / 2; | |
| 228 y2a = y2 - (i + 1) / 2; | |
| 229 } else { /* place below */ | |
| 230 y1a = y1 + (i + 1) / 2; | |
| 231 y2a = y2 + (i + 1) / 2; | |
| 232 } | |
| 233 if ((pta = generatePtaLine(x1, y1a, x2, y2a)) != NULL) { | |
| 234 ptaJoin(ptaj, pta, 0, -1); | |
| 235 ptaDestroy(&pta); | |
| 236 } | |
| 237 } | |
| 238 } else { /* "vertical" line */ | |
| 239 for (i = 1; i < width; i++) { | |
| 240 if ((i & 1) == 1) { /* place to left */ | |
| 241 x1a = x1 - (i + 1) / 2; | |
| 242 x2a = x2 - (i + 1) / 2; | |
| 243 } else { /* place to right */ | |
| 244 x1a = x1 + (i + 1) / 2; | |
| 245 x2a = x2 + (i + 1) / 2; | |
| 246 } | |
| 247 if ((pta = generatePtaLine(x1a, y1, x2a, y2)) != NULL) { | |
| 248 ptaJoin(ptaj, pta, 0, -1); | |
| 249 ptaDestroy(&pta); | |
| 250 } | |
| 251 } | |
| 252 } | |
| 253 | |
| 254 return ptaj; | |
| 255 } | |
| 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 { | |
| 275 l_int32 x, y, w, h; | |
| 276 PTA *ptad, *pta; | |
| 277 | |
| 278 if (!box) | |
| 279 return (PTA *)ERROR_PTR("box not defined", __func__, NULL); | |
| 280 if (width < 1) { | |
| 281 L_WARNING("width < 1; setting to 1\n", __func__); | |
| 282 width = 1; | |
| 283 } | |
| 284 | |
| 285 /* Generate line points and add them to the pta. */ | |
| 286 boxGetGeometry(box, &x, &y, &w, &h); | |
| 287 if (w == 0 || h == 0) | |
| 288 return (PTA *)ERROR_PTR("box has w = 0 or h = 0", __func__, NULL); | |
| 289 ptad = ptaCreate(0); | |
| 290 if ((width & 1) == 1) { /* odd width */ | |
| 291 pta = generatePtaWideLine(x - width / 2, y, | |
| 292 x + w - 1 + width / 2, y, width); | |
| 293 ptaJoin(ptad, pta, 0, -1); | |
| 294 ptaDestroy(&pta); | |
| 295 pta = generatePtaWideLine(x + w - 1, y + 1 + width / 2, | |
| 296 x + w - 1, y + h - 2 - width / 2, width); | |
| 297 ptaJoin(ptad, pta, 0, -1); | |
| 298 ptaDestroy(&pta); | |
| 299 pta = generatePtaWideLine(x + w - 1 + width / 2, y + h - 1, | |
| 300 x - width / 2, y + h - 1, width); | |
| 301 ptaJoin(ptad, pta, 0, -1); | |
| 302 ptaDestroy(&pta); | |
| 303 pta = generatePtaWideLine(x, y + h - 2 - width / 2, | |
| 304 x, y + 1 + width / 2, width); | |
| 305 ptaJoin(ptad, pta, 0, -1); | |
| 306 ptaDestroy(&pta); | |
| 307 } else { /* even width */ | |
| 308 pta = generatePtaWideLine(x - width / 2, y, | |
| 309 x + w - 2 + width / 2, y, width); | |
| 310 ptaJoin(ptad, pta, 0, -1); | |
| 311 ptaDestroy(&pta); | |
| 312 pta = generatePtaWideLine(x + w - 1, y + 0 + width / 2, | |
| 313 x + w - 1, y + h - 2 - width / 2, width); | |
| 314 ptaJoin(ptad, pta, 0, -1); | |
| 315 ptaDestroy(&pta); | |
| 316 pta = generatePtaWideLine(x + w - 2 + width / 2, y + h - 1, | |
| 317 x - width / 2, y + h - 1, width); | |
| 318 ptaJoin(ptad, pta, 0, -1); | |
| 319 ptaDestroy(&pta); | |
| 320 pta = generatePtaWideLine(x, y + h - 2 - width / 2, | |
| 321 x, y + 0 + width / 2, width); | |
| 322 ptaJoin(ptad, pta, 0, -1); | |
| 323 ptaDestroy(&pta); | |
| 324 } | |
| 325 | |
| 326 return ptad; | |
| 327 } | |
| 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 { | |
| 351 l_int32 i, n; | |
| 352 BOX *box; | |
| 353 PTA *ptad, *ptat, *pta; | |
| 354 | |
| 355 if (!boxa) | |
| 356 return (PTA *)ERROR_PTR("boxa not defined", __func__, NULL); | |
| 357 if (width < 1) { | |
| 358 L_WARNING("width < 1; setting to 1\n", __func__); | |
| 359 width = 1; | |
| 360 } | |
| 361 | |
| 362 n = boxaGetCount(boxa); | |
| 363 ptat = ptaCreate(0); | |
| 364 for (i = 0; i < n; i++) { | |
| 365 box = boxaGetBox(boxa, i, L_CLONE); | |
| 366 pta = generatePtaBox(box, width); | |
| 367 ptaJoin(ptat, pta, 0, -1); | |
| 368 ptaDestroy(&pta); | |
| 369 boxDestroy(&box); | |
| 370 } | |
| 371 | |
| 372 if (removedups) | |
| 373 ptaRemoveDupsByAset(ptat, &ptad); | |
| 374 else | |
| 375 ptad = ptaClone(ptat); | |
| 376 | |
| 377 ptaDestroy(&ptat); | |
| 378 return ptad; | |
| 379 } | |
| 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 { | |
| 408 l_int32 bx, by, bh, bw, x, y, x1, y1, x2, y2, i, n, npts; | |
| 409 PTA *ptad, *pta; | |
| 410 | |
| 411 if (!box) | |
| 412 return (PTA *)ERROR_PTR("box not defined", __func__, NULL); | |
| 413 if (spacing <= 1) | |
| 414 return (PTA *)ERROR_PTR("spacing not > 1", __func__, NULL); | |
| 415 if (orient != L_HORIZONTAL_LINE && orient != L_POS_SLOPE_LINE && | |
| 416 orient != L_VERTICAL_LINE && orient != L_NEG_SLOPE_LINE) | |
| 417 return (PTA *)ERROR_PTR("invalid line orientation", __func__, NULL); | |
| 418 boxGetGeometry(box, &bx, &by, &bw, &bh); | |
| 419 if (bw == 0 || bh == 0) | |
| 420 return (PTA *)ERROR_PTR("box has bw = 0 or bh = 0", __func__, NULL); | |
| 421 if (width < 1) { | |
| 422 L_WARNING("width < 1; setting to 1\n", __func__); | |
| 423 width = 1; | |
| 424 } | |
| 425 | |
| 426 /* Generate line points and add them to the pta. */ | |
| 427 ptad = ptaCreate(0); | |
| 428 if (outline) { | |
| 429 pta = generatePtaBox(box, width); | |
| 430 ptaJoin(ptad, pta, 0, -1); | |
| 431 ptaDestroy(&pta); | |
| 432 } | |
| 433 if (orient == L_HORIZONTAL_LINE) { | |
| 434 n = 1 + bh / spacing; | |
| 435 for (i = 0; i < n; i++) { | |
| 436 y = by + (i * (bh - 1)) / (n - 1); | |
| 437 pta = generatePtaWideLine(bx, y, bx + bw - 1, y, width); | |
| 438 ptaJoin(ptad, pta, 0, -1); | |
| 439 ptaDestroy(&pta); | |
| 440 } | |
| 441 } else if (orient == L_VERTICAL_LINE) { | |
| 442 n = 1 + bw / spacing; | |
| 443 for (i = 0; i < n; i++) { | |
| 444 x = bx + (i * (bw - 1)) / (n - 1); | |
| 445 pta = generatePtaWideLine(x, by, x, by + bh - 1, width); | |
| 446 ptaJoin(ptad, pta, 0, -1); | |
| 447 ptaDestroy(&pta); | |
| 448 } | |
| 449 } else if (orient == L_POS_SLOPE_LINE) { | |
| 450 n = 2 + (l_int32)((bw + bh) / (1.4 * spacing)); | |
| 451 for (i = 0; i < n; i++) { | |
| 452 x = (l_int32)(bx + (i + 0.5) * 1.4 * spacing); | |
| 453 boxIntersectByLine(box, x, by - 1, 1.0, &x1, &y1, &x2, &y2, &npts); | |
| 454 if (npts == 2) { | |
| 455 pta = generatePtaWideLine(x1, y1, x2, y2, width); | |
| 456 ptaJoin(ptad, pta, 0, -1); | |
| 457 ptaDestroy(&pta); | |
| 458 } | |
| 459 } | |
| 460 } else { /* orient == L_NEG_SLOPE_LINE */ | |
| 461 n = 2 + (l_int32)((bw + bh) / (1.4 * spacing)); | |
| 462 for (i = 0; i < n; i++) { | |
| 463 x = (l_int32)(bx - bh + (i + 0.5) * 1.4 * spacing); | |
| 464 boxIntersectByLine(box, x, by - 1, -1.0, &x1, &y1, &x2, &y2, &npts); | |
| 465 if (npts == 2) { | |
| 466 pta = generatePtaWideLine(x1, y1, x2, y2, width); | |
| 467 ptaJoin(ptad, pta, 0, -1); | |
| 468 ptaDestroy(&pta); | |
| 469 } | |
| 470 } | |
| 471 } | |
| 472 | |
| 473 return ptad; | |
| 474 } | |
| 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 { | |
| 510 l_int32 i, n; | |
| 511 BOX *box; | |
| 512 PTA *ptad, *ptat, *pta; | |
| 513 | |
| 514 if (!boxa) | |
| 515 return (PTA *)ERROR_PTR("boxa not defined", __func__, NULL); | |
| 516 if (spacing <= 1) | |
| 517 return (PTA *)ERROR_PTR("spacing not > 1", __func__, NULL); | |
| 518 if (width < 1) { | |
| 519 L_WARNING("width < 1; setting to 1\n", __func__); | |
| 520 width = 1; | |
| 521 } | |
| 522 if (orient != L_HORIZONTAL_LINE && orient != L_POS_SLOPE_LINE && | |
| 523 orient != L_VERTICAL_LINE && orient != L_NEG_SLOPE_LINE) | |
| 524 return (PTA *)ERROR_PTR("invalid line orientation", __func__, NULL); | |
| 525 | |
| 526 n = boxaGetCount(boxa); | |
| 527 ptat = ptaCreate(0); | |
| 528 for (i = 0; i < n; i++) { | |
| 529 box = boxaGetBox(boxa, i, L_CLONE); | |
| 530 pta = generatePtaHashBox(box, spacing, width, orient, outline); | |
| 531 ptaJoin(ptat, pta, 0, -1); | |
| 532 ptaDestroy(&pta); | |
| 533 boxDestroy(&box); | |
| 534 } | |
| 535 | |
| 536 if (removedups) | |
| 537 ptaRemoveDupsByAset(ptat, &ptad); | |
| 538 else | |
| 539 ptad = ptaClone(ptat); | |
| 540 | |
| 541 ptaDestroy(&ptat); | |
| 542 return ptad; | |
| 543 } | |
| 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 { | |
| 563 l_int32 i, n, x, y, w, h; | |
| 564 BOX *box; | |
| 565 PTA *pta; | |
| 566 PTAA *ptaa; | |
| 567 | |
| 568 if (!boxa) | |
| 569 return (PTAA *)ERROR_PTR("boxa not defined", __func__, NULL); | |
| 570 | |
| 571 n = boxaGetCount(boxa); | |
| 572 ptaa = ptaaCreate(n); | |
| 573 for (i = 0; i < n; i++) { | |
| 574 box = boxaGetBox(boxa, i, L_CLONE); | |
| 575 boxGetGeometry(box, &x, &y, &w, &h); | |
| 576 pta = ptaCreate(4); | |
| 577 ptaAddPt(pta, x, y); | |
| 578 ptaAddPt(pta, x + w - 1, y); | |
| 579 ptaAddPt(pta, x + w - 1, y + h - 1); | |
| 580 ptaAddPt(pta, x, y + h - 1); | |
| 581 ptaaAddPta(ptaa, pta, L_INSERT); | |
| 582 boxDestroy(&box); | |
| 583 } | |
| 584 | |
| 585 return ptaa; | |
| 586 } | |
| 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 { | |
| 618 l_int32 i, n; | |
| 619 BOX *box; | |
| 620 PTA *pta; | |
| 621 PTAA *ptaa; | |
| 622 | |
| 623 if (!boxa) | |
| 624 return (PTAA *)ERROR_PTR("boxa not defined", __func__, NULL); | |
| 625 if (spacing <= 1) | |
| 626 return (PTAA *)ERROR_PTR("spacing not > 1", __func__, NULL); | |
| 627 if (width < 1) { | |
| 628 L_WARNING("width < 1; setting to 1\n", __func__); | |
| 629 width = 1; | |
| 630 } | |
| 631 if (orient != L_HORIZONTAL_LINE && orient != L_POS_SLOPE_LINE && | |
| 632 orient != L_VERTICAL_LINE && orient != L_NEG_SLOPE_LINE) | |
| 633 return (PTAA *)ERROR_PTR("invalid line orientation", __func__, NULL); | |
| 634 | |
| 635 n = boxaGetCount(boxa); | |
| 636 ptaa = ptaaCreate(n); | |
| 637 for (i = 0; i < n; i++) { | |
| 638 box = boxaGetBox(boxa, i, L_CLONE); | |
| 639 pta = generatePtaHashBox(box, spacing, width, orient, outline); | |
| 640 ptaaAddPta(ptaa, pta, L_INSERT); | |
| 641 boxDestroy(&box); | |
| 642 } | |
| 643 | |
| 644 return ptaa; | |
| 645 } | |
| 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 { | |
| 663 l_int32 i, n, x1, y1, x2, y2; | |
| 664 PTA *ptad, *ptat, *pta; | |
| 665 | |
| 666 if (!ptas) | |
| 667 return (PTA *)ERROR_PTR("ptas not defined", __func__, NULL); | |
| 668 if (width < 1) { | |
| 669 L_WARNING("width < 1; setting to 1\n", __func__); | |
| 670 width = 1; | |
| 671 } | |
| 672 | |
| 673 n = ptaGetCount(ptas); | |
| 674 ptat = ptaCreate(0); | |
| 675 if (n < 2) /* nothing to do */ | |
| 676 return ptat; | |
| 677 | |
| 678 ptaGetIPt(ptas, 0, &x1, &y1); | |
| 679 for (i = 1; i < n; i++) { | |
| 680 ptaGetIPt(ptas, i, &x2, &y2); | |
| 681 pta = generatePtaWideLine(x1, y1, x2, y2, width); | |
| 682 ptaJoin(ptat, pta, 0, -1); | |
| 683 ptaDestroy(&pta); | |
| 684 x1 = x2; | |
| 685 y1 = y2; | |
| 686 } | |
| 687 | |
| 688 if (closeflag) { | |
| 689 ptaGetIPt(ptas, 0, &x2, &y2); | |
| 690 pta = generatePtaWideLine(x1, y1, x2, y2, width); | |
| 691 ptaJoin(ptat, pta, 0, -1); | |
| 692 ptaDestroy(&pta); | |
| 693 } | |
| 694 | |
| 695 if (removedups) | |
| 696 ptaRemoveDupsByAset(ptat, &ptad); | |
| 697 else | |
| 698 ptad = ptaClone(ptat); | |
| 699 | |
| 700 ptaDestroy(&ptat); | |
| 701 return ptad; | |
| 702 } | |
| 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 { | |
| 720 l_int32 i, j, bx, by, x1, x2, y1, y2; | |
| 721 BOX *box; | |
| 722 BOXA *boxa; | |
| 723 PTA *pta; | |
| 724 | |
| 725 if (nx < 1 || ny < 1) | |
| 726 return (PTA *)ERROR_PTR("nx and ny must be > 0", __func__, NULL); | |
| 727 if (w < 2 * nx || h < 2 * ny) | |
| 728 return (PTA *)ERROR_PTR("w and/or h too small", __func__, NULL); | |
| 729 if (width < 1) { | |
| 730 L_WARNING("width < 1; setting to 1\n", __func__); | |
| 731 width = 1; | |
| 732 } | |
| 733 | |
| 734 boxa = boxaCreate(nx * ny); | |
| 735 bx = (w + nx - 1) / nx; | |
| 736 by = (h + ny - 1) / ny; | |
| 737 for (i = 0; i < ny; i++) { | |
| 738 y1 = by * i; | |
| 739 y2 = L_MIN(y1 + by, h - 1); | |
| 740 for (j = 0; j < nx; j++) { | |
| 741 x1 = bx * j; | |
| 742 x2 = L_MIN(x1 + bx, w - 1); | |
| 743 box = boxCreate(x1, y1, x2 - x1 + 1, y2 - y1 + 1); | |
| 744 boxaAddBox(boxa, box, L_INSERT); | |
| 745 } | |
| 746 } | |
| 747 | |
| 748 pta = generatePtaBoxa(boxa, width, 1); | |
| 749 boxaDestroy(&boxa); | |
| 750 return pta; | |
| 751 } | |
| 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 { | |
| 772 l_int32 i, n, x, y, xp, yp; | |
| 773 PTA *ptad; | |
| 774 | |
| 775 if (!ptas) | |
| 776 return (PTA *)ERROR_PTR("ptas not defined", __func__, NULL); | |
| 777 | |
| 778 n = ptaGetCount(ptas); | |
| 779 ptad = ptaCreate(n); | |
| 780 ptaGetIPt(ptas, 0, &xp, &yp); | |
| 781 ptaAddPt(ptad, xp, yp); | |
| 782 for (i = 1; i < n; i++) { | |
| 783 ptaGetIPt(ptas, i, &x, &y); | |
| 784 if (x != xp && y != yp) /* diagonal */ | |
| 785 ptaAddPt(ptad, x, yp); | |
| 786 ptaAddPt(ptad, x, y); | |
| 787 xp = x; | |
| 788 yp = y; | |
| 789 } | |
| 790 | |
| 791 return ptad; | |
| 792 } | |
| 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 { | |
| 813 l_int32 x, y; | |
| 814 l_float32 radthresh, sqdist; | |
| 815 PTA *pta; | |
| 816 | |
| 817 if (radius < 1) | |
| 818 return (PTA *)ERROR_PTR("radius must be >= 1", __func__, NULL); | |
| 819 | |
| 820 pta = ptaCreate(0); | |
| 821 radthresh = (radius + 0.5) * (radius + 0.5); | |
| 822 for (y = 0; y <= 2 * radius; y++) { | |
| 823 for (x = 0; x <= 2 * radius; x++) { | |
| 824 sqdist = (l_float32)((y - radius) * (y - radius) + | |
| 825 (x - radius) * (x - radius)); | |
| 826 if (sqdist <= radthresh) | |
| 827 ptaAddPt(pta, x, y); | |
| 828 } | |
| 829 } | |
| 830 | |
| 831 return pta; | |
| 832 } | |
| 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 { | |
| 851 l_int32 x, y; | |
| 852 PTA *pta; | |
| 853 | |
| 854 if (side < 1) | |
| 855 return (PTA *)ERROR_PTR("side must be > 0", __func__, NULL); | |
| 856 | |
| 857 pta = ptaCreate(0); | |
| 858 for (y = 0; y < side; y++) | |
| 859 for (x = 0; x < side; x++) | |
| 860 ptaAddPt(pta, x, y); | |
| 861 | |
| 862 return pta; | |
| 863 } | |
| 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 { | |
| 887 l_int32 x2, y2; /* the point at the other end of the line */ | |
| 888 | |
| 889 x2 = x + (l_int32)((length - 1.0) * cos(radang)); | |
| 890 y2 = y + (l_int32)((length - 1.0) * sin(radang)); | |
| 891 return generatePtaLine(x, y, x2, y2); | |
| 892 } | |
| 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 { | |
| 913 if (!px || !py) | |
| 914 return ERROR_INT("&x and &y not both defined", __func__, 1); | |
| 915 | |
| 916 *px = xr + dist * cos(radang); | |
| 917 *py = yr + dist * sin(radang); | |
| 918 return 0; | |
| 919 } | |
| 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 { | |
| 953 l_int32 w, h, size, rval, gval, bval; | |
| 954 PIX *pix1; | |
| 955 PTA *pta; | |
| 956 | |
| 957 if (!ppix) | |
| 958 return ERROR_INT("&pix not defined", __func__, 1); | |
| 959 if (*ppix == NULL) | |
| 960 return ERROR_INT("pix not defined", __func__, 1); | |
| 961 | |
| 962 pixGetDimensions(*ppix, &w, &h, NULL); | |
| 963 size = (plotloc == L_PLOT_AT_TOP || plotloc == L_PLOT_AT_MID_HORIZ || | |
| 964 plotloc == L_PLOT_AT_BOT) ? h : w; | |
| 965 pta = makePlotPtaFromNuma(na, size, plotloc, linewidth, max); | |
| 966 if (!pta) | |
| 967 return ERROR_INT("pta not made", __func__, 1); | |
| 968 | |
| 969 if (pixGetDepth(*ppix) != 32) { | |
| 970 pix1 = pixConvertTo32(*ppix); | |
| 971 pixDestroy(ppix); | |
| 972 *ppix = pix1; | |
| 973 } | |
| 974 extractRGBValues(color, &rval, &gval, &bval); | |
| 975 pixRenderPtaArb(*ppix, pta, rval, gval, bval); | |
| 976 ptaDestroy(&pta); | |
| 977 return 0; | |
| 978 } | |
| 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 { | |
| 1009 l_int32 orient, refpos; | |
| 1010 | |
| 1011 if (!na) | |
| 1012 return (PTA *)ERROR_PTR("na not defined", __func__, NULL); | |
| 1013 if (plotloc == L_PLOT_AT_TOP || plotloc == L_PLOT_AT_MID_HORIZ || | |
| 1014 plotloc == L_PLOT_AT_BOT) { | |
| 1015 orient = L_HORIZONTAL_LINE; | |
| 1016 } else if (plotloc == L_PLOT_AT_LEFT || plotloc == L_PLOT_AT_MID_VERT || | |
| 1017 plotloc == L_PLOT_AT_RIGHT) { | |
| 1018 orient = L_VERTICAL_LINE; | |
| 1019 } else { | |
| 1020 return (PTA *)ERROR_PTR("invalid plotloc", __func__, NULL); | |
| 1021 } | |
| 1022 | |
| 1023 if (plotloc == L_PLOT_AT_LEFT || plotloc == L_PLOT_AT_TOP) | |
| 1024 refpos = max; | |
| 1025 else if (plotloc == L_PLOT_AT_MID_VERT || plotloc == L_PLOT_AT_MID_HORIZ) | |
| 1026 refpos = size / 2; | |
| 1027 else /* L_PLOT_AT_RIGHT || L_PLOT_AT_BOT */ | |
| 1028 refpos = size - max - 1; | |
| 1029 | |
| 1030 return makePlotPtaFromNumaGen(na, orient, linewidth, refpos, max, 1); | |
| 1031 } | |
| 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 { | |
| 1067 l_int32 rval, gval, bval; | |
| 1068 PIX *pix1; | |
| 1069 PTA *pta; | |
| 1070 | |
| 1071 if (!ppix) | |
| 1072 return ERROR_INT("&pix not defined", __func__, 1); | |
| 1073 if (*ppix == NULL) | |
| 1074 return ERROR_INT("pix not defined", __func__, 1); | |
| 1075 | |
| 1076 pta = makePlotPtaFromNumaGen(na, orient, linewidth, refpos, max, drawref); | |
| 1077 if (!pta) | |
| 1078 return ERROR_INT("pta not made", __func__, 1); | |
| 1079 | |
| 1080 if (pixGetDepth(*ppix) != 32) { | |
| 1081 pix1 = pixConvertTo32(*ppix); | |
| 1082 pixDestroy(ppix); | |
| 1083 *ppix = pix1; | |
| 1084 } | |
| 1085 extractRGBValues(color, &rval, &gval, &bval); | |
| 1086 pixRenderPtaArb(*ppix, pta, rval, gval, bval); | |
| 1087 ptaDestroy(&pta); | |
| 1088 return 0; | |
| 1089 } | |
| 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 { | |
| 1129 l_int32 i, n, maxw, maxh; | |
| 1130 l_float32 minval, maxval, absval, val, scale, start, del; | |
| 1131 PTA *pta1, *pta2, *ptad; | |
| 1132 | |
| 1133 if (!na) | |
| 1134 return (PTA *)ERROR_PTR("na not defined", __func__, NULL); | |
| 1135 if (orient != L_HORIZONTAL_LINE && orient != L_VERTICAL_LINE) | |
| 1136 return (PTA *)ERROR_PTR("invalid orient", __func__, NULL); | |
| 1137 if (linewidth < 1) { | |
| 1138 L_WARNING("linewidth < 1; setting to 1\n", __func__); | |
| 1139 linewidth = 1; | |
| 1140 } | |
| 1141 if (linewidth > 7) { | |
| 1142 L_WARNING("linewidth > 7; setting to 7\n", __func__); | |
| 1143 linewidth = 7; | |
| 1144 } | |
| 1145 | |
| 1146 numaGetMin(na, &minval, NULL); | |
| 1147 numaGetMax(na, &maxval, NULL); | |
| 1148 absval = L_MAX(L_ABS(minval), L_ABS(maxval)); | |
| 1149 scale = (l_float32)max / (l_float32)absval; | |
| 1150 n = numaGetCount(na); | |
| 1151 numaGetParameters(na, &start, &del); | |
| 1152 | |
| 1153 /* Generate the plot points */ | |
| 1154 pta1 = ptaCreate(n); | |
| 1155 maxw = maxh = 0; | |
| 1156 for (i = 0; i < n; i++) { | |
| 1157 numaGetFValue(na, i, &val); | |
| 1158 if (orient == L_HORIZONTAL_LINE) { | |
| 1159 ptaAddPt(pta1, start + i * del, refpos + scale * val); | |
| 1160 maxw = (del >= 0) ? start + n * del + linewidth | |
| 1161 : start + linewidth; | |
| 1162 maxh = refpos + max + linewidth; | |
| 1163 } else { /* vertical line */ | |
| 1164 ptaAddPt(pta1, refpos + scale * val, start + i * del); | |
| 1165 maxw = refpos + max + linewidth; | |
| 1166 maxh = (del >= 0) ? start + n * del + linewidth | |
| 1167 : start + linewidth; | |
| 1168 } | |
| 1169 } | |
| 1170 | |
| 1171 /* Optionally, widen the plot */ | |
| 1172 if (linewidth > 1) { | |
| 1173 if (linewidth % 2 == 0) /* even linewidth; use side of a square */ | |
| 1174 pta2 = generatePtaFilledSquare(linewidth); | |
| 1175 else /* odd linewidth; use radius of a circle */ | |
| 1176 pta2 = generatePtaFilledCircle(linewidth / 2); | |
| 1177 ptad = ptaReplicatePattern(pta1, NULL, pta2, linewidth / 2, | |
| 1178 linewidth / 2, maxw, maxh); | |
| 1179 ptaDestroy(&pta2); | |
| 1180 } else { | |
| 1181 ptad = ptaClone(pta1); | |
| 1182 } | |
| 1183 ptaDestroy(&pta1); | |
| 1184 | |
| 1185 /* Optionally, add the reference lines */ | |
| 1186 if (drawref) { | |
| 1187 if (orient == L_HORIZONTAL_LINE) { | |
| 1188 pta1 = generatePtaLine(start, refpos, start + n * del, refpos); | |
| 1189 ptaJoin(ptad, pta1, 0, -1); | |
| 1190 ptaDestroy(&pta1); | |
| 1191 pta1 = generatePtaLine(start, refpos - max, | |
| 1192 start, refpos + max); | |
| 1193 ptaJoin(ptad, pta1, 0, -1); | |
| 1194 } else { /* vertical line */ | |
| 1195 pta1 = generatePtaLine(refpos, start, refpos, start + n * del); | |
| 1196 ptaJoin(ptad, pta1, 0, -1); | |
| 1197 ptaDestroy(&pta1); | |
| 1198 pta1 = generatePtaLine(refpos - max, start, | |
| 1199 refpos + max, start); | |
| 1200 ptaJoin(ptad, pta1, 0, -1); | |
| 1201 } | |
| 1202 ptaDestroy(&pta1); | |
| 1203 } | |
| 1204 | |
| 1205 return ptad; | |
| 1206 } | |
| 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 { | |
| 1237 l_int32 i, n, x, y, w, h, d, maxval; | |
| 1238 | |
| 1239 if (!pix) | |
| 1240 return ERROR_INT("pix not defined", __func__, 1); | |
| 1241 if (pixGetColormap(pix)) | |
| 1242 return ERROR_INT("pix is colormapped", __func__, 1); | |
| 1243 if (!pta) | |
| 1244 return ERROR_INT("pta not defined", __func__, 1); | |
| 1245 if (op != L_SET_PIXELS && op != L_CLEAR_PIXELS && op != L_FLIP_PIXELS) | |
| 1246 return ERROR_INT("invalid op", __func__, 1); | |
| 1247 | |
| 1248 pixGetDimensions(pix, &w, &h, &d); | |
| 1249 maxval = 1; | |
| 1250 if (op == L_SET_PIXELS) { | |
| 1251 switch (d) | |
| 1252 { | |
| 1253 case 2: | |
| 1254 maxval = 0x3; | |
| 1255 break; | |
| 1256 case 4: | |
| 1257 maxval = 0xf; | |
| 1258 break; | |
| 1259 case 8: | |
| 1260 maxval = 0xff; | |
| 1261 break; | |
| 1262 case 16: | |
| 1263 maxval = 0xffff; | |
| 1264 break; | |
| 1265 case 32: | |
| 1266 maxval = 0xffffffff; | |
| 1267 break; | |
| 1268 } | |
| 1269 } | |
| 1270 | |
| 1271 n = ptaGetCount(pta); | |
| 1272 for (i = 0; i < n; i++) { | |
| 1273 ptaGetIPt(pta, i, &x, &y); | |
| 1274 if (x < 0 || x >= w) | |
| 1275 continue; | |
| 1276 if (y < 0 || y >= h) | |
| 1277 continue; | |
| 1278 switch (op) | |
| 1279 { | |
| 1280 case L_SET_PIXELS: | |
| 1281 pixSetPixel(pix, x, y, maxval); | |
| 1282 break; | |
| 1283 case L_CLEAR_PIXELS: | |
| 1284 pixClearPixel(pix, x, y); | |
| 1285 break; | |
| 1286 case L_FLIP_PIXELS: | |
| 1287 pixFlipPixel(pix, x, y); | |
| 1288 break; | |
| 1289 default: | |
| 1290 break; | |
| 1291 } | |
| 1292 } | |
| 1293 | |
| 1294 return 0; | |
| 1295 } | |
| 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 { | |
| 1326 l_int32 i, n, x, y, w, h, d, index; | |
| 1327 l_uint8 val; | |
| 1328 l_uint32 val32; | |
| 1329 PIXCMAP *cmap; | |
| 1330 | |
| 1331 if (!pix) | |
| 1332 return ERROR_INT("pix not defined", __func__, 1); | |
| 1333 if (!pta) | |
| 1334 return ERROR_INT("pta not defined", __func__, 1); | |
| 1335 d = pixGetDepth(pix); | |
| 1336 if (d != 1 && d != 2 && d != 4 && d != 8 && d != 32) | |
| 1337 return ERROR_INT("depth not in {1,2,4,8,32}", __func__, 1); | |
| 1338 | |
| 1339 if (d == 1) { | |
| 1340 pixRenderPta(pix, pta, L_SET_PIXELS); | |
| 1341 return 0; | |
| 1342 } | |
| 1343 | |
| 1344 cmap = pixGetColormap(pix); | |
| 1345 pixGetDimensions(pix, &w, &h, &d); | |
| 1346 if (cmap) { | |
| 1347 pixcmapAddNearestColor(cmap, rval, gval, bval, &index); | |
| 1348 } else { | |
| 1349 if (d == 2) | |
| 1350 val = (rval + gval + bval) / (3 * 64); | |
| 1351 else if (d == 4) | |
| 1352 val = (rval + gval + bval) / (3 * 16); | |
| 1353 else if (d == 8) | |
| 1354 val = (rval + gval + bval) / 3; | |
| 1355 else /* d == 32 */ | |
| 1356 composeRGBPixel(rval, gval, bval, &val32); | |
| 1357 } | |
| 1358 | |
| 1359 n = ptaGetCount(pta); | |
| 1360 for (i = 0; i < n; i++) { | |
| 1361 ptaGetIPt(pta, i, &x, &y); | |
| 1362 if (x < 0 || x >= w) | |
| 1363 continue; | |
| 1364 if (y < 0 || y >= h) | |
| 1365 continue; | |
| 1366 if (cmap) | |
| 1367 pixSetPixel(pix, x, y, index); | |
| 1368 else if (d == 32) | |
| 1369 pixSetPixel(pix, x, y, val32); | |
| 1370 else | |
| 1371 pixSetPixel(pix, x, y, val); | |
| 1372 } | |
| 1373 | |
| 1374 return 0; | |
| 1375 } | |
| 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 { | |
| 1400 l_int32 i, n, x, y, w, h; | |
| 1401 l_uint8 nrval, ngval, nbval; | |
| 1402 l_uint32 val32; | |
| 1403 l_float32 frval, fgval, fbval; | |
| 1404 | |
| 1405 if (!pix) | |
| 1406 return ERROR_INT("pix not defined", __func__, 1); | |
| 1407 if (!pta) | |
| 1408 return ERROR_INT("pta not defined", __func__, 1); | |
| 1409 if (pixGetDepth(pix) != 32) | |
| 1410 return ERROR_INT("depth not 32 bpp", __func__, 1); | |
| 1411 if (fract < 0.0 || fract > 1.0) { | |
| 1412 L_WARNING("fract must be in [0.0, 1.0]; setting to 0.5\n", __func__); | |
| 1413 fract = 0.5; | |
| 1414 } | |
| 1415 | |
| 1416 pixGetDimensions(pix, &w, &h, NULL); | |
| 1417 n = ptaGetCount(pta); | |
| 1418 frval = fract * rval; | |
| 1419 fgval = fract * gval; | |
| 1420 fbval = fract * bval; | |
| 1421 for (i = 0; i < n; i++) { | |
| 1422 ptaGetIPt(pta, i, &x, &y); | |
| 1423 if (x < 0 || x >= w) | |
| 1424 continue; | |
| 1425 if (y < 0 || y >= h) | |
| 1426 continue; | |
| 1427 pixGetPixel(pix, x, y, &val32); | |
| 1428 nrval = GET_DATA_BYTE(&val32, COLOR_RED); | |
| 1429 nrval = (l_uint8)((1. - fract) * nrval + frval); | |
| 1430 ngval = GET_DATA_BYTE(&val32, COLOR_GREEN); | |
| 1431 ngval = (l_uint8)((1. - fract) * ngval + fgval); | |
| 1432 nbval = GET_DATA_BYTE(&val32, COLOR_BLUE); | |
| 1433 nbval = (l_uint8)((1. - fract) * nbval + fbval); | |
| 1434 composeRGBPixel(nrval, ngval, nbval, &val32); | |
| 1435 pixSetPixel(pix, x, y, val32); | |
| 1436 } | |
| 1437 | |
| 1438 return 0; | |
| 1439 } | |
| 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 { | |
| 1464 PTA *pta; | |
| 1465 | |
| 1466 if (!pix) | |
| 1467 return ERROR_INT("pix not defined", __func__, 1); | |
| 1468 if (width < 1) { | |
| 1469 L_WARNING("width must be > 0; setting to 1\n", __func__); | |
| 1470 width = 1; | |
| 1471 } | |
| 1472 if (op != L_SET_PIXELS && op != L_CLEAR_PIXELS && op != L_FLIP_PIXELS) | |
| 1473 return ERROR_INT("invalid op", __func__, 1); | |
| 1474 | |
| 1475 if ((pta = generatePtaWideLine(x1, y1, x2, y2, width)) == NULL) | |
| 1476 return ERROR_INT("pta not made", __func__, 1); | |
| 1477 pixRenderPta(pix, pta, op); | |
| 1478 ptaDestroy(&pta); | |
| 1479 return 0; | |
| 1480 } | |
| 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 { | |
| 1504 PTA *pta; | |
| 1505 | |
| 1506 if (!pix) | |
| 1507 return ERROR_INT("pix not defined", __func__, 1); | |
| 1508 if (width < 1) { | |
| 1509 L_WARNING("width must be > 0; setting to 1\n", __func__); | |
| 1510 width = 1; | |
| 1511 } | |
| 1512 | |
| 1513 if ((pta = generatePtaWideLine(x1, y1, x2, y2, width)) == NULL) | |
| 1514 return ERROR_INT("pta not made", __func__, 1); | |
| 1515 pixRenderPtaArb(pix, pta, rval, gval, bval); | |
| 1516 ptaDestroy(&pta); | |
| 1517 return 0; | |
| 1518 } | |
| 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 { | |
| 1544 PTA *pta; | |
| 1545 | |
| 1546 if (!pix) | |
| 1547 return ERROR_INT("pix not defined", __func__, 1); | |
| 1548 if (width < 1) { | |
| 1549 L_WARNING("width must be > 0; setting to 1\n", __func__); | |
| 1550 width = 1; | |
| 1551 } | |
| 1552 | |
| 1553 if ((pta = generatePtaWideLine(x1, y1, x2, y2, width)) == NULL) | |
| 1554 return ERROR_INT("pta not made", __func__, 1); | |
| 1555 pixRenderPtaBlend(pix, pta, rval, gval, bval, fract); | |
| 1556 ptaDestroy(&pta); | |
| 1557 return 0; | |
| 1558 } | |
| 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 { | |
| 1576 PTA *pta; | |
| 1577 | |
| 1578 if (!pix) | |
| 1579 return ERROR_INT("pix not defined", __func__, 1); | |
| 1580 if (!box) | |
| 1581 return ERROR_INT("box not defined", __func__, 1); | |
| 1582 if (width < 1) { | |
| 1583 L_WARNING("width < 1; setting to 1\n", __func__); | |
| 1584 width = 1; | |
| 1585 } | |
| 1586 if (op != L_SET_PIXELS && op != L_CLEAR_PIXELS && op != L_FLIP_PIXELS) | |
| 1587 return ERROR_INT("invalid op", __func__, 1); | |
| 1588 | |
| 1589 if ((pta = generatePtaBox(box, width)) == NULL) | |
| 1590 return ERROR_INT("pta not made", __func__, 1); | |
| 1591 pixRenderPta(pix, pta, op); | |
| 1592 ptaDestroy(&pta); | |
| 1593 return 0; | |
| 1594 } | |
| 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 { | |
| 1614 PTA *pta; | |
| 1615 | |
| 1616 if (!pix) | |
| 1617 return ERROR_INT("pix not defined", __func__, 1); | |
| 1618 if (!box) | |
| 1619 return ERROR_INT("box not defined", __func__, 1); | |
| 1620 if (width < 1) { | |
| 1621 L_WARNING("width < 1; setting to 1\n", __func__); | |
| 1622 width = 1; | |
| 1623 } | |
| 1624 | |
| 1625 if ((pta = generatePtaBox(box, width)) == NULL) | |
| 1626 return ERROR_INT("pta not made", __func__, 1); | |
| 1627 pixRenderPtaArb(pix, pta, rval, gval, bval); | |
| 1628 ptaDestroy(&pta); | |
| 1629 return 0; | |
| 1630 } | |
| 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 { | |
| 1653 PTA *pta; | |
| 1654 | |
| 1655 if (!pix) | |
| 1656 return ERROR_INT("pix not defined", __func__, 1); | |
| 1657 if (!box) | |
| 1658 return ERROR_INT("box not defined", __func__, 1); | |
| 1659 if (width < 1) { | |
| 1660 L_WARNING("width < 1; setting to 1\n", __func__); | |
| 1661 width = 1; | |
| 1662 } | |
| 1663 | |
| 1664 if ((pta = generatePtaBox(box, width)) == NULL) | |
| 1665 return ERROR_INT("pta not made", __func__, 1); | |
| 1666 pixRenderPtaBlend(pix, pta, rval, gval, bval, fract); | |
| 1667 ptaDestroy(&pta); | |
| 1668 return 0; | |
| 1669 } | |
| 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 { | |
| 1687 PTA *pta; | |
| 1688 | |
| 1689 if (!pix) | |
| 1690 return ERROR_INT("pix not defined", __func__, 1); | |
| 1691 if (!boxa) | |
| 1692 return ERROR_INT("boxa not defined", __func__, 1); | |
| 1693 if (width < 1) { | |
| 1694 L_WARNING("width < 1; setting to 1\n", __func__); | |
| 1695 width = 1; | |
| 1696 } | |
| 1697 if (op != L_SET_PIXELS && op != L_CLEAR_PIXELS && op != L_FLIP_PIXELS) | |
| 1698 return ERROR_INT("invalid op", __func__, 1); | |
| 1699 | |
| 1700 if ((pta = generatePtaBoxa(boxa, width, 0)) == NULL) | |
| 1701 return ERROR_INT("pta not made", __func__, 1); | |
| 1702 pixRenderPta(pix, pta, op); | |
| 1703 ptaDestroy(&pta); | |
| 1704 return 0; | |
| 1705 } | |
| 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 { | |
| 1725 PTA *pta; | |
| 1726 | |
| 1727 if (!pix) | |
| 1728 return ERROR_INT("pix not defined", __func__, 1); | |
| 1729 if (!boxa) | |
| 1730 return ERROR_INT("boxa not defined", __func__, 1); | |
| 1731 if (width < 1) { | |
| 1732 L_WARNING("width < 1; setting to 1\n", __func__); | |
| 1733 width = 1; | |
| 1734 } | |
| 1735 | |
| 1736 if ((pta = generatePtaBoxa(boxa, width, 0)) == NULL) | |
| 1737 return ERROR_INT("pta not made", __func__, 1); | |
| 1738 pixRenderPtaArb(pix, pta, rval, gval, bval); | |
| 1739 ptaDestroy(&pta); | |
| 1740 return 0; | |
| 1741 } | |
| 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 { | |
| 1766 PTA *pta; | |
| 1767 | |
| 1768 if (!pix) | |
| 1769 return ERROR_INT("pix not defined", __func__, 1); | |
| 1770 if (!boxa) | |
| 1771 return ERROR_INT("boxa not defined", __func__, 1); | |
| 1772 if (width < 1) { | |
| 1773 L_WARNING("width < 1; setting to 1\n", __func__); | |
| 1774 width = 1; | |
| 1775 } | |
| 1776 | |
| 1777 if ((pta = generatePtaBoxa(boxa, width, removedups)) == NULL) | |
| 1778 return ERROR_INT("pta not made", __func__, 1); | |
| 1779 pixRenderPtaBlend(pix, pta, rval, gval, bval, fract); | |
| 1780 ptaDestroy(&pta); | |
| 1781 return 0; | |
| 1782 } | |
| 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 { | |
| 1806 PTA *pta; | |
| 1807 | |
| 1808 if (!pix) | |
| 1809 return ERROR_INT("pix not defined", __func__, 1); | |
| 1810 if (!box) | |
| 1811 return ERROR_INT("box not defined", __func__, 1); | |
| 1812 if (spacing <= 1) | |
| 1813 return ERROR_INT("spacing not > 1", __func__, 1); | |
| 1814 if (width < 1) { | |
| 1815 L_WARNING("width < 1; setting to 1\n", __func__); | |
| 1816 width = 1; | |
| 1817 } | |
| 1818 if (orient != L_HORIZONTAL_LINE && orient != L_POS_SLOPE_LINE && | |
| 1819 orient != L_VERTICAL_LINE && orient != L_NEG_SLOPE_LINE) | |
| 1820 return ERROR_INT("invalid line orientation", __func__, 1); | |
| 1821 if (op != L_SET_PIXELS && op != L_CLEAR_PIXELS && op != L_FLIP_PIXELS) | |
| 1822 return ERROR_INT("invalid op", __func__, 1); | |
| 1823 | |
| 1824 pta = generatePtaHashBox(box, spacing, width, orient, outline); | |
| 1825 if (!pta) | |
| 1826 return ERROR_INT("pta not made", __func__, 1); | |
| 1827 pixRenderPta(pix, pta, op); | |
| 1828 ptaDestroy(&pta); | |
| 1829 return 0; | |
| 1830 } | |
| 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 { | |
| 1856 PTA *pta; | |
| 1857 | |
| 1858 if (!pix) | |
| 1859 return ERROR_INT("pix not defined", __func__, 1); | |
| 1860 if (!box) | |
| 1861 return ERROR_INT("box not defined", __func__, 1); | |
| 1862 if (spacing <= 1) | |
| 1863 return ERROR_INT("spacing not > 1", __func__, 1); | |
| 1864 if (width < 1) { | |
| 1865 L_WARNING("width < 1; setting to 1\n", __func__); | |
| 1866 width = 1; | |
| 1867 } | |
| 1868 if (orient != L_HORIZONTAL_LINE && orient != L_POS_SLOPE_LINE && | |
| 1869 orient != L_VERTICAL_LINE && orient != L_NEG_SLOPE_LINE) | |
| 1870 return ERROR_INT("invalid line orientation", __func__, 1); | |
| 1871 | |
| 1872 pta = generatePtaHashBox(box, spacing, width, orient, outline); | |
| 1873 if (!pta) | |
| 1874 return ERROR_INT("pta not made", __func__, 1); | |
| 1875 pixRenderPtaArb(pix, pta, rval, gval, bval); | |
| 1876 ptaDestroy(&pta); | |
| 1877 return 0; | |
| 1878 } | |
| 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 { | |
| 1907 PTA *pta; | |
| 1908 | |
| 1909 if (!pix) | |
| 1910 return ERROR_INT("pix not defined", __func__, 1); | |
| 1911 if (!box) | |
| 1912 return ERROR_INT("box not defined", __func__, 1); | |
| 1913 if (spacing <= 1) | |
| 1914 return ERROR_INT("spacing not > 1", __func__, 1); | |
| 1915 if (width < 1) { | |
| 1916 L_WARNING("width < 1; setting to 1\n", __func__); | |
| 1917 width = 1; | |
| 1918 } | |
| 1919 if (orient != L_HORIZONTAL_LINE && orient != L_POS_SLOPE_LINE && | |
| 1920 orient != L_VERTICAL_LINE && orient != L_NEG_SLOPE_LINE) | |
| 1921 return ERROR_INT("invalid line orientation", __func__, 1); | |
| 1922 | |
| 1923 pta = generatePtaHashBox(box, spacing, width, orient, outline); | |
| 1924 if (!pta) | |
| 1925 return ERROR_INT("pta not made", __func__, 1); | |
| 1926 pixRenderPtaBlend(pix, pta, rval, gval, bval, fract); | |
| 1927 ptaDestroy(&pta); | |
| 1928 return 0; | |
| 1929 } | |
| 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 { | |
| 1966 l_int32 w, h; | |
| 1967 BOX *box1, *box2; | |
| 1968 PIX *pix1; | |
| 1969 PTA *pta1, *pta2; | |
| 1970 | |
| 1971 if (!pix) | |
| 1972 return ERROR_INT("pix not defined", __func__, 1); | |
| 1973 if (!pixm || pixGetDepth(pixm) != 1) | |
| 1974 return ERROR_INT("pixm not defined or not 1 bpp", __func__, 1); | |
| 1975 if (spacing <= 1) | |
| 1976 return ERROR_INT("spacing not > 1", __func__, 1); | |
| 1977 if (width < 1) { | |
| 1978 L_WARNING("width < 1; setting to 1\n", __func__); | |
| 1979 width = 1; | |
| 1980 } | |
| 1981 if (orient != L_HORIZONTAL_LINE && orient != L_POS_SLOPE_LINE && | |
| 1982 orient != L_VERTICAL_LINE && orient != L_NEG_SLOPE_LINE) | |
| 1983 return ERROR_INT("invalid line orientation", __func__, 1); | |
| 1984 | |
| 1985 /* Get the points for masked hash lines */ | |
| 1986 pixGetDimensions(pixm, &w, &h, NULL); | |
| 1987 box1 = boxCreate(0, 0, w, h); | |
| 1988 pta1 = generatePtaHashBox(box1, spacing, width, orient, outline); | |
| 1989 pta2 = ptaCropToMask(pta1, pixm); | |
| 1990 boxDestroy(&box1); | |
| 1991 ptaDestroy(&pta1); | |
| 1992 | |
| 1993 /* Clip out the region and apply the hash lines */ | |
| 1994 box2 = boxCreate(x, y, w, h); | |
| 1995 pix1 = pixClipRectangle(pix, box2, NULL); | |
| 1996 pixRenderPtaArb(pix1, pta2, rval, gval, bval); | |
| 1997 ptaDestroy(&pta2); | |
| 1998 boxDestroy(&box2); | |
| 1999 | |
| 2000 /* Rasterop the altered rectangle back in place */ | |
| 2001 pixRasterop(pix, x, y, w, h, PIX_SRC, pix1, 0, 0); | |
| 2002 pixDestroy(&pix1); | |
| 2003 return 0; | |
| 2004 } | |
| 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 { | |
| 2030 PTA *pta; | |
| 2031 | |
| 2032 if (!pix) | |
| 2033 return ERROR_INT("pix not defined", __func__, 1); | |
| 2034 if (!boxa) | |
| 2035 return ERROR_INT("boxa not defined", __func__, 1); | |
| 2036 if (spacing <= 1) | |
| 2037 return ERROR_INT("spacing not > 1", __func__, 1); | |
| 2038 if (width < 1) { | |
| 2039 L_WARNING("width < 1; setting to 1\n", __func__); | |
| 2040 width = 1; | |
| 2041 } | |
| 2042 if (orient != L_HORIZONTAL_LINE && orient != L_POS_SLOPE_LINE && | |
| 2043 orient != L_VERTICAL_LINE && orient != L_NEG_SLOPE_LINE) | |
| 2044 return ERROR_INT("invalid line orientation", __func__, 1); | |
| 2045 if (op != L_SET_PIXELS && op != L_CLEAR_PIXELS && op != L_FLIP_PIXELS) | |
| 2046 return ERROR_INT("invalid op", __func__, 1); | |
| 2047 | |
| 2048 pta = generatePtaHashBoxa(boxa, spacing, width, orient, outline, 1); | |
| 2049 if (!pta) | |
| 2050 return ERROR_INT("pta not made", __func__, 1); | |
| 2051 pixRenderPta(pix, pta, op); | |
| 2052 ptaDestroy(&pta); | |
| 2053 return 0; | |
| 2054 } | |
| 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 { | |
| 2082 PTA *pta; | |
| 2083 | |
| 2084 if (!pix) | |
| 2085 return ERROR_INT("pix not defined", __func__, 1); | |
| 2086 if (!boxa) | |
| 2087 return ERROR_INT("boxa not defined", __func__, 1); | |
| 2088 if (spacing <= 1) | |
| 2089 return ERROR_INT("spacing not > 1", __func__, 1); | |
| 2090 if (width < 1) { | |
| 2091 L_WARNING("width < 1; setting to 1\n", __func__); | |
| 2092 width = 1; | |
| 2093 } | |
| 2094 if (orient != L_HORIZONTAL_LINE && orient != L_POS_SLOPE_LINE && | |
| 2095 orient != L_VERTICAL_LINE && orient != L_NEG_SLOPE_LINE) | |
| 2096 return ERROR_INT("invalid line orientation", __func__, 1); | |
| 2097 | |
| 2098 pta = generatePtaHashBoxa(boxa, spacing, width, orient, outline, 1); | |
| 2099 if (!pta) | |
| 2100 return ERROR_INT("pta not made", __func__, 1); | |
| 2101 pixRenderPtaArb(pix, pta, rval, gval, bval); | |
| 2102 ptaDestroy(&pta); | |
| 2103 return 0; | |
| 2104 } | |
| 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 { | |
| 2135 PTA *pta; | |
| 2136 | |
| 2137 if (!pix) | |
| 2138 return ERROR_INT("pix not defined", __func__, 1); | |
| 2139 if (!boxa) | |
| 2140 return ERROR_INT("boxa not defined", __func__, 1); | |
| 2141 if (spacing <= 1) | |
| 2142 return ERROR_INT("spacing not > 1", __func__, 1); | |
| 2143 if (width < 1) { | |
| 2144 L_WARNING("width < 1; setting to 1\n", __func__); | |
| 2145 width = 1; | |
| 2146 } | |
| 2147 if (orient != L_HORIZONTAL_LINE && orient != L_POS_SLOPE_LINE && | |
| 2148 orient != L_VERTICAL_LINE && orient != L_NEG_SLOPE_LINE) | |
| 2149 return ERROR_INT("invalid line orientation", __func__, 1); | |
| 2150 | |
| 2151 pta = generatePtaHashBoxa(boxa, spacing, width, orient, outline, 1); | |
| 2152 if (!pta) | |
| 2153 return ERROR_INT("pta not made", __func__, 1); | |
| 2154 pixRenderPtaBlend(pix, pta, rval, gval, bval, fract); | |
| 2155 ptaDestroy(&pta); | |
| 2156 return 0; | |
| 2157 } | |
| 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 { | |
| 2182 PTA *pta; | |
| 2183 | |
| 2184 if (!pix) | |
| 2185 return ERROR_INT("pix not defined", __func__, 1); | |
| 2186 if (!ptas) | |
| 2187 return ERROR_INT("ptas not defined", __func__, 1); | |
| 2188 if (width < 1) { | |
| 2189 L_WARNING("width < 1; setting to 1\n", __func__); | |
| 2190 width = 1; | |
| 2191 } | |
| 2192 if (op != L_SET_PIXELS && op != L_CLEAR_PIXELS && op != L_FLIP_PIXELS) | |
| 2193 return ERROR_INT("invalid op", __func__, 1); | |
| 2194 | |
| 2195 if ((pta = generatePtaPolyline(ptas, width, closeflag, 0)) == NULL) | |
| 2196 return ERROR_INT("pta not made", __func__, 1); | |
| 2197 pixRenderPta(pix, pta, op); | |
| 2198 ptaDestroy(&pta); | |
| 2199 return 0; | |
| 2200 } | |
| 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 { | |
| 2227 PTA *pta; | |
| 2228 | |
| 2229 if (!pix) | |
| 2230 return ERROR_INT("pix not defined", __func__, 1); | |
| 2231 if (!ptas) | |
| 2232 return ERROR_INT("ptas not defined", __func__, 1); | |
| 2233 if (width < 1) { | |
| 2234 L_WARNING("width < 1; setting to 1\n", __func__); | |
| 2235 width = 1; | |
| 2236 } | |
| 2237 | |
| 2238 if ((pta = generatePtaPolyline(ptas, width, closeflag, 0)) == NULL) | |
| 2239 return ERROR_INT("pta not made", __func__, 1); | |
| 2240 pixRenderPtaArb(pix, pta, rval, gval, bval); | |
| 2241 ptaDestroy(&pta); | |
| 2242 return 0; | |
| 2243 } | |
| 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 { | |
| 2270 PTA *pta; | |
| 2271 | |
| 2272 if (!pix) | |
| 2273 return ERROR_INT("pix not defined", __func__, 1); | |
| 2274 if (!ptas) | |
| 2275 return ERROR_INT("ptas not defined", __func__, 1); | |
| 2276 if (width < 1) { | |
| 2277 L_WARNING("width < 1; setting to 1\n", __func__); | |
| 2278 width = 1; | |
| 2279 } | |
| 2280 | |
| 2281 if ((pta = generatePtaPolyline(ptas, width, closeflag, removedups)) == NULL) | |
| 2282 return ERROR_INT("pta not made", __func__, 1); | |
| 2283 pixRenderPtaBlend(pix, pta, rval, gval, bval, fract); | |
| 2284 ptaDestroy(&pta); | |
| 2285 return 0; | |
| 2286 } | |
| 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 { | |
| 2307 l_int32 w, h; | |
| 2308 PTA *pta; | |
| 2309 | |
| 2310 if (!pix) | |
| 2311 return ERROR_INT("pix not defined", __func__, 1); | |
| 2312 if (nx < 1 || ny < 1) | |
| 2313 return ERROR_INT("nx, ny must be > 0", __func__, 1); | |
| 2314 if (width < 1) { | |
| 2315 L_WARNING("width < 1; setting to 1\n", __func__); | |
| 2316 width = 1; | |
| 2317 } | |
| 2318 | |
| 2319 pixGetDimensions(pix, &w, &h, NULL); | |
| 2320 if ((pta = generatePtaGrid(w, h, nx, ny, width)) == NULL) | |
| 2321 return ERROR_INT("pta not made", __func__, 1); | |
| 2322 pixRenderPtaArb(pix, pta, rval, gval, bval); | |
| 2323 ptaDestroy(&pta); | |
| 2324 return 0; | |
| 2325 } | |
| 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 { | |
| 2363 l_int32 i, n, index, rval, gval, bval; | |
| 2364 PIXCMAP *cmap; | |
| 2365 PTA *pta, *ptat; | |
| 2366 PIX *pixd; | |
| 2367 | |
| 2368 if (!pix) | |
| 2369 return (PIX *)ERROR_PTR("pix not defined", __func__, NULL); | |
| 2370 if (!ptaa) | |
| 2371 return (PIX *)ERROR_PTR("ptaa not defined", __func__, NULL); | |
| 2372 if (polyflag != 0 && width < 1) { | |
| 2373 L_WARNING("width < 1; setting to 1\n", __func__); | |
| 2374 width = 1; | |
| 2375 } | |
| 2376 | |
| 2377 pixd = pixConvertTo8(pix, FALSE); | |
| 2378 cmap = pixcmapCreateRandom(8, 1, 1); | |
| 2379 pixSetColormap(pixd, cmap); | |
| 2380 | |
| 2381 if ((n = ptaaGetCount(ptaa)) == 0) | |
| 2382 return pixd; | |
| 2383 | |
| 2384 for (i = 0; i < n; i++) { | |
| 2385 index = 1 + (i % 254); | |
| 2386 pixcmapGetColor(cmap, index, &rval, &gval, &bval); | |
| 2387 pta = ptaaGetPta(ptaa, i, L_CLONE); | |
| 2388 if (polyflag) | |
| 2389 ptat = generatePtaPolyline(pta, width, closeflag, 0); | |
| 2390 else | |
| 2391 ptat = ptaClone(pta); | |
| 2392 pixRenderPtaArb(pixd, ptat, rval, gval, bval); | |
| 2393 ptaDestroy(&pta); | |
| 2394 ptaDestroy(&ptat); | |
| 2395 } | |
| 2396 | |
| 2397 return pixd; | |
| 2398 } | |
| 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 { | |
| 2429 l_float32 fxmin, fxmax, fymin, fymax; | |
| 2430 PIX *pixd; | |
| 2431 PTA *pta1, *pta2; | |
| 2432 | |
| 2433 if (pxmin) *pxmin = 0; | |
| 2434 if (pymin) *pymin = 0; | |
| 2435 if (!ptas) | |
| 2436 return (PIX *)ERROR_PTR("ptas not defined", __func__, NULL); | |
| 2437 | |
| 2438 /* Generate a 4-connected polygon line */ | |
| 2439 if ((pta1 = generatePtaPolyline(ptas, width, 1, 0)) == NULL) | |
| 2440 return (PIX *)ERROR_PTR("pta1 not made", __func__, NULL); | |
| 2441 if (width < 2) | |
| 2442 pta2 = convertPtaLineTo4cc(pta1); | |
| 2443 else | |
| 2444 pta2 = ptaClone(pta1); | |
| 2445 | |
| 2446 /* Render onto a minimum-sized pix */ | |
| 2447 ptaGetRange(pta2, &fxmin, &fxmax, &fymin, &fymax); | |
| 2448 if (pxmin) *pxmin = (l_int32)(fxmin + 0.5); | |
| 2449 if (pymin) *pymin = (l_int32)(fymin + 0.5); | |
| 2450 pixd = pixCreate((l_int32)(fxmax + 0.5) + 1, (l_int32)(fymax + 0.5) + 1, 1); | |
| 2451 pixRenderPolyline(pixd, pta2, width, L_SET_PIXELS, 1); | |
| 2452 ptaDestroy(&pta1); | |
| 2453 ptaDestroy(&pta2); | |
| 2454 return pixd; | |
| 2455 } | |
| 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 { | |
| 2482 l_int32 w, h, i, n, inside, found; | |
| 2483 l_int32 *xstart, *xend; | |
| 2484 PIX *pixi, *pixd; | |
| 2485 | |
| 2486 if (!pixs || (pixGetDepth(pixs) != 1)) | |
| 2487 return (PIX *)ERROR_PTR("pixs undefined or not 1 bpp", __func__, NULL); | |
| 2488 if (!pta) | |
| 2489 return (PIX *)ERROR_PTR("pta not defined", __func__, NULL); | |
| 2490 if (ptaGetCount(pta) < 2) | |
| 2491 return (PIX *)ERROR_PTR("pta has < 2 pts", __func__, NULL); | |
| 2492 | |
| 2493 pixGetDimensions(pixs, &w, &h, NULL); | |
| 2494 xstart = (l_int32 *)LEPT_CALLOC(L_MAX(1, w / 2), sizeof(l_int32)); | |
| 2495 xend = (l_int32 *)LEPT_CALLOC(L_MAX(1, w / 2), sizeof(l_int32)); | |
| 2496 if (!xstart || !xend) { | |
| 2497 LEPT_FREE(xstart); | |
| 2498 LEPT_FREE(xend); | |
| 2499 return (PIX *)ERROR_PTR("xstart and xend not made", __func__, NULL); | |
| 2500 } | |
| 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 found = FALSE; | |
| 2506 for (i = ymin + 1; i < h; i++) { | |
| 2507 pixFindHorizontalRuns(pixs, i, xstart, xend, &n); | |
| 2508 if (n > 1) { | |
| 2509 ptaPtInsidePolygon(pta, xend[0] + 1, i, &inside); | |
| 2510 if (inside) { | |
| 2511 found = TRUE; | |
| 2512 break; | |
| 2513 } | |
| 2514 } | |
| 2515 } | |
| 2516 if (!found) { | |
| 2517 L_WARNING("nothing found to fill\n", __func__); | |
| 2518 LEPT_FREE(xstart); | |
| 2519 LEPT_FREE(xend); | |
| 2520 return 0; | |
| 2521 } | |
| 2522 | |
| 2523 /* Place the seed pixel in the output image */ | |
| 2524 pixd = pixCreateTemplate(pixs); | |
| 2525 pixSetPixel(pixd, xend[0] + 1, i, 1); | |
| 2526 | |
| 2527 /* Invert pixs to make a filling mask, and fill from the seed */ | |
| 2528 pixi = pixInvert(NULL, pixs); | |
| 2529 pixSeedfillBinary(pixd, pixd, pixi, 4); | |
| 2530 | |
| 2531 /* Add the pixels of the original polygon outline */ | |
| 2532 pixOr(pixd, pixd, pixs); | |
| 2533 | |
| 2534 pixDestroy(&pixi); | |
| 2535 LEPT_FREE(xstart); | |
| 2536 LEPT_FREE(xend); | |
| 2537 return pixd; | |
| 2538 } | |
| 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 { | |
| 2566 l_int32 w, h, d, maxval, wpls, wpld, i, j, val, test; | |
| 2567 l_uint32 *datas, *datad, *lines, *lined; | |
| 2568 PIX *pixd; | |
| 2569 | |
| 2570 if (!pixs) | |
| 2571 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); | |
| 2572 if (pixGetColormap(pixs)) | |
| 2573 return (PIX *)ERROR_PTR("pixs has colormap", __func__, NULL); | |
| 2574 pixGetDimensions(pixs, &w, &h, &d); | |
| 2575 if (d != 8 && d != 16) | |
| 2576 return (PIX *)ERROR_PTR("pixs not 8 or 16 bpp", __func__, NULL); | |
| 2577 if (outdepth != 1 && outdepth != d) { | |
| 2578 L_WARNING("invalid outdepth; setting to 1\n", __func__); | |
| 2579 outdepth = 1; | |
| 2580 } | |
| 2581 maxval = (1 << d) - 1; | |
| 2582 if (startval < 0 || startval > maxval) | |
| 2583 return (PIX *)ERROR_PTR("startval not in [0 ... maxval]", | |
| 2584 __func__, NULL); | |
| 2585 if (incr < 1) | |
| 2586 return (PIX *)ERROR_PTR("incr < 1", __func__, NULL); | |
| 2587 | |
| 2588 if (outdepth == d) | |
| 2589 pixd = pixCopy(NULL, pixs); | |
| 2590 else | |
| 2591 pixd = pixCreate(w, h, 1); | |
| 2592 | |
| 2593 pixCopyResolution(pixd, pixs); | |
| 2594 datad = pixGetData(pixd); | |
| 2595 wpld = pixGetWpl(pixd); | |
| 2596 datas = pixGetData(pixs); | |
| 2597 wpls = pixGetWpl(pixs); | |
| 2598 | |
| 2599 switch (d) | |
| 2600 { | |
| 2601 case 8: | |
| 2602 if (outdepth == 1) { | |
| 2603 for (i = 0; i < h; i++) { | |
| 2604 lines = datas + i * wpls; | |
| 2605 lined = datad + i * wpld; | |
| 2606 for (j = 0; j < w; j++) { | |
| 2607 val = GET_DATA_BYTE(lines, j); | |
| 2608 if (val < startval) | |
| 2609 continue; | |
| 2610 test = (val - startval) % incr; | |
| 2611 if (!test) | |
| 2612 SET_DATA_BIT(lined, j); | |
| 2613 } | |
| 2614 } | |
| 2615 } else { /* outdepth == d */ | |
| 2616 for (i = 0; i < h; i++) { | |
| 2617 lines = datas + i * wpls; | |
| 2618 lined = datad + i * wpld; | |
| 2619 for (j = 0; j < w; j++) { | |
| 2620 val = GET_DATA_BYTE(lines, j); | |
| 2621 if (val < startval) | |
| 2622 continue; | |
| 2623 test = (val - startval) % incr; | |
| 2624 if (!test) | |
| 2625 SET_DATA_BYTE(lined, j, 0); | |
| 2626 } | |
| 2627 } | |
| 2628 } | |
| 2629 break; | |
| 2630 | |
| 2631 case 16: | |
| 2632 if (outdepth == 1) { | |
| 2633 for (i = 0; i < h; i++) { | |
| 2634 lines = datas + i * wpls; | |
| 2635 lined = datad + i * wpld; | |
| 2636 for (j = 0; j < w; j++) { | |
| 2637 val = GET_DATA_TWO_BYTES(lines, j); | |
| 2638 if (val < startval) | |
| 2639 continue; | |
| 2640 test = (val - startval) % incr; | |
| 2641 if (!test) | |
| 2642 SET_DATA_BIT(lined, j); | |
| 2643 } | |
| 2644 } | |
| 2645 } else { /* outdepth == d */ | |
| 2646 for (i = 0; i < h; i++) { | |
| 2647 lines = datas + i * wpls; | |
| 2648 lined = datad + i * wpld; | |
| 2649 for (j = 0; j < w; j++) { | |
| 2650 val = GET_DATA_TWO_BYTES(lines, j); | |
| 2651 if (val < startval) | |
| 2652 continue; | |
| 2653 test = (val - startval) % incr; | |
| 2654 if (!test) | |
| 2655 SET_DATA_TWO_BYTES(lined, j, 0); | |
| 2656 } | |
| 2657 } | |
| 2658 } | |
| 2659 break; | |
| 2660 | |
| 2661 default: | |
| 2662 return (PIX *)ERROR_PTR("pixs not 8 or 16 bpp", __func__, NULL); | |
| 2663 } | |
| 2664 | |
| 2665 return pixd; | |
| 2666 } | |
| 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 { | |
| 2688 l_float32 minval, maxval, incr; | |
| 2689 | |
| 2690 if (!fpix) | |
| 2691 return (PIX *)ERROR_PTR("fpix not defined", __func__, NULL); | |
| 2692 if (ncontours < 2 || ncontours > 500) | |
| 2693 return (PIX *)ERROR_PTR("ncontours < 2 or > 500", __func__, NULL); | |
| 2694 | |
| 2695 fpixGetMin(fpix, &minval, NULL, NULL); | |
| 2696 fpixGetMax(fpix, &maxval, NULL, NULL); | |
| 2697 if (minval == maxval) | |
| 2698 return (PIX *)ERROR_PTR("all values in fpix are equal", __func__, NULL); | |
| 2699 incr = (maxval - minval) / ((l_float32)ncontours - 1); | |
| 2700 return fpixRenderContours(fpix, incr, 0.15f); | |
| 2701 } | |
| 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 { | |
| 2725 l_int32 i, j, w, h, wpls, wpld; | |
| 2726 l_float32 val, invincr, finter, above, below, diff; | |
| 2727 l_uint32 *datad, *lined; | |
| 2728 l_float32 *datas, *lines; | |
| 2729 PIX *pixd; | |
| 2730 PIXCMAP *cmap; | |
| 2731 | |
| 2732 if (!fpixs) | |
| 2733 return (PIX *)ERROR_PTR("fpixs not defined", __func__, NULL); | |
| 2734 if (incr <= 0.0) | |
| 2735 return (PIX *)ERROR_PTR("incr <= 0.0", __func__, NULL); | |
| 2736 if (proxim <= 0.0) | |
| 2737 proxim = 0.15f; /* default */ | |
| 2738 | |
| 2739 fpixGetDimensions(fpixs, &w, &h); | |
| 2740 if ((pixd = pixCreate(w, h, 8)) == NULL) | |
| 2741 return (PIX *)ERROR_PTR("pixd not made", __func__, NULL); | |
| 2742 cmap = pixcmapCreate(8); | |
| 2743 pixSetColormap(pixd, cmap); | |
| 2744 pixcmapAddColor(cmap, 255, 255, 255); /* white */ | |
| 2745 pixcmapAddColor(cmap, 0, 0, 0); /* black */ | |
| 2746 pixcmapAddColor(cmap, 255, 0, 0); /* red */ | |
| 2747 | |
| 2748 datas = fpixGetData(fpixs); | |
| 2749 wpls = fpixGetWpl(fpixs); | |
| 2750 datad = pixGetData(pixd); | |
| 2751 wpld = pixGetWpl(pixd); | |
| 2752 invincr = 1.0 / incr; | |
| 2753 for (i = 0; i < h; i++) { | |
| 2754 lines = datas + i * wpls; | |
| 2755 lined = datad + i * wpld; | |
| 2756 for (j = 0; j < w; j++) { | |
| 2757 val = lines[j]; | |
| 2758 finter = invincr * val; | |
| 2759 above = finter - floorf(finter); | |
| 2760 below = ceilf(finter) - finter; | |
| 2761 diff = L_MIN(above, below); | |
| 2762 if (diff <= proxim) { | |
| 2763 if (val < 0.0) | |
| 2764 SET_DATA_BYTE(lined, j, 2); | |
| 2765 else | |
| 2766 SET_DATA_BYTE(lined, j, 1); | |
| 2767 } | |
| 2768 } | |
| 2769 } | |
| 2770 | |
| 2771 return pixd; | |
| 2772 } | |
| 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 { | |
| 2801 PIX *pix1; | |
| 2802 PTA *pta; | |
| 2803 | |
| 2804 if (!pixs || pixGetDepth(pixs) != 1) | |
| 2805 return (PTA *)ERROR_PTR("pixs undefined or not 1 bpp", __func__, NULL); | |
| 2806 if (width < 1) { | |
| 2807 L_WARNING("width < 1; setting to 1\n", __func__); | |
| 2808 width = 1; | |
| 2809 } | |
| 2810 | |
| 2811 pix1 = pixErodeBrick(NULL, pixs, 2 * width + 1, 2 * width + 1); | |
| 2812 pixXor(pix1, pix1, pixs); | |
| 2813 pta = ptaGetPixelsFromPix(pix1, NULL); | |
| 2814 pixDestroy(&pix1); | |
| 2815 return pta; | |
| 2816 } |
