Mercurial > hgrepos > Python2 > PyMuPDF
comparison mupdf-source/thirdparty/leptonica/src/rotateshear.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 rotateshear.c | |
| 29 * <pre> | |
| 30 * | |
| 31 * Shear rotation about arbitrary point using 2 and 3 shears | |
| 32 * | |
| 33 * PIX *pixRotateShear() | |
| 34 * PIX *pixRotate2Shear() | |
| 35 * PIX *pixRotate3Shear() | |
| 36 * | |
| 37 * Shear rotation in-place about arbitrary point using 3 shears | |
| 38 * l_int32 pixRotateShearIP() | |
| 39 * | |
| 40 * Shear rotation around the image center | |
| 41 * PIX *pixRotateShearCenter() (2 or 3 shears) | |
| 42 * l_int32 pixRotateShearCenterIP() (3 shears) | |
| 43 * | |
| 44 * Rotation is measured in radians; clockwise rotations are positive. | |
| 45 * | |
| 46 * Rotation by shear works on images of any depth, | |
| 47 * including 8 bpp color paletted images and 32 bpp | |
| 48 * rgb images. It works by translating each src pixel | |
| 49 * value to the appropriate pixel in the rotated dest. | |
| 50 * For 8 bpp grayscale images, it is about 10-15x faster | |
| 51 * than rotation by area-mapping. | |
| 52 * | |
| 53 * This speed and flexibility comes at the following cost, | |
| 54 * relative to area-mapped rotation: | |
| 55 * | |
| 56 * ~ Jaggies are created on edges of straight lines | |
| 57 * | |
| 58 * ~ For large angles, where you must use 3 shears, | |
| 59 * there is some extra clipping from the shears. | |
| 60 * | |
| 61 * For small angles, typically less than 0.05 radians, | |
| 62 * rotation can be done with 2 orthogonal shears. | |
| 63 * Two such continuous shears (as opposed to the discrete | |
| 64 * shears on a pixel lattice that we have here) give | |
| 65 * a rotated image that has a distortion in the lengths | |
| 66 * of the two rotated and still-perpendicular axes. The | |
| 67 * length/width ratio changes by a fraction | |
| 68 * | |
| 69 * 0.5 * (angle)**2 | |
| 70 * | |
| 71 * For an angle of 0.05 radians, this is about 1 part in | |
| 72 * a thousand. This distortion is absent when you use | |
| 73 * 3 continuous shears with the correct angles (see below). | |
| 74 * | |
| 75 * Of course, the image is on a discrete pixel lattice. | |
| 76 * Rotation by shear gives an approximation to a continuous | |
| 77 * rotation, leaving pixel jaggies at sharp boundaries. | |
| 78 * For very small rotations, rotating from a corner gives | |
| 79 * better sensitivity than rotating from the image center. | |
| 80 * Here's why. Define the shear "center" to be the line such | |
| 81 * that the image is sheared in opposite directions on | |
| 82 * each side of and parallel to the line. For small | |
| 83 * rotations there is a "dead space" on each side of the | |
| 84 * shear center of width equal to half the shear angle, | |
| 85 * in radians. Thus, when the image is sheared about the center, | |
| 86 * the dead space width equals the shear angle, but when | |
| 87 * the image is sheared from a corner, the dead space | |
| 88 * width is only half the shear angle. | |
| 89 * | |
| 90 * All horizontal and vertical shears are implemented by | |
| 91 * rasterop. The in-place rotation uses special in-place | |
| 92 * shears that copy rows sideways or columns vertically | |
| 93 * without buffering, and then rewrite old pixels that are | |
| 94 * no longer covered by sheared pixels. For that rewriting, | |
| 95 * you have the choice of using white or black pixels. | |
| 96 * When not in-place, the new pix is initialized with white or black | |
| 97 * pixels by pixSetBlackOrWhite(), which also works for cmapped pix. | |
| 98 * But for in-place, this initialization is not possible, so | |
| 99 * in-place shear operations on cmapped pix are not allowed. | |
| 100 * | |
| 101 * Rotation by shear is fast and depth-independent. However, it | |
| 102 * does not work well for large rotation angles. In fact, for | |
| 103 * rotation angles greater than about 7 degrees, more pixels are | |
| 104 * lost at the edges than when using pixRotationBySampling(), which | |
| 105 * only loses pixels because they are rotated out of the image. | |
| 106 * For larger rotations, use pixRotationBySampling() or, for | |
| 107 * more accuracy when d > 1 bpp, pixRotateAM(). | |
| 108 * | |
| 109 * For small angles, when comparing the quality of rotation by | |
| 110 * sampling and by shear, you can see that rotation by sampling | |
| 111 * is slightly more accurate. However, the difference in | |
| 112 * accuracy of rotation by sampling when compared to 3-shear and | |
| 113 * (for angles less than 2 degrees, when compared to 2-shear) is | |
| 114 * less than 1 pixel at any point. For very small angles, rotation by | |
| 115 * sampling is much slower than rotation by shear. The speed difference | |
| 116 * depends on the pixel depth and the rotation angle. Rotation | |
| 117 * by shear is very fast for small angles and for small depth (esp. 1 bpp). | |
| 118 * Rotation by sampling speed is independent of angle and relatively | |
| 119 * more efficient for 8 and 32 bpp images. Here are some timings | |
| 120 * for the ratio of rotation times: (time for sampling)/ (time for shear) | |
| 121 * | |
| 122 * depth (bpp) ratio (2 deg) ratio (10 deg) | |
| 123 * ----------------------------------------------------- | |
| 124 * 1 25 6 | |
| 125 * 8 5 2.6 | |
| 126 * 32 1.6 1.0 | |
| 127 * | |
| 128 * In summary: | |
| 129 * * For d == 1 and small angles, use rotation by shear. By default | |
| 130 * this will use 2-shear rotations, because 3-shears cause more | |
| 131 * visible artifacts in straight lines and, for small angles, the | |
| 132 * distortion in asperity ratio is small. | |
| 133 * * For d > 1, shear is faster than sampling, which is faster than | |
| 134 * area mapping. However, area mapping gives the best results. | |
| 135 * These results are used in selecting the rotation methods in | |
| 136 * pixRotateShear(). | |
| 137 * | |
| 138 * There has been some work on what is called a "quasishear | |
| 139 * rotation" ("The Quasi-Shear Rotation, Eric Andres, | |
| 140 * DGCI 1996, pp. 307-314). I believe they use a 3-shear | |
| 141 * approximation to the continuous rotation, exactly as | |
| 142 * we do here. The approximation is due to being on | |
| 143 * a square pixel lattice. They also use integers to specify | |
| 144 * the rotation angle and center offset, but that makes | |
| 145 * little sense on a machine where you have a few GFLOPS | |
| 146 * and only a few hundred floating point operations to do (!) | |
| 147 * They also allow subpixel specification of the center of | |
| 148 * rotation, which I haven't bothered with, and claim that | |
| 149 * better results are possible if each of the 4 quadrants is | |
| 150 * handled separately. | |
| 151 * | |
| 152 * But the bottom line is that you are going to see shear lines when | |
| 153 * you rotate 1 bpp images. Although the 3-shear rotation is | |
| 154 * mathematically exact in the limit of infinitesimal pixels, artifacts | |
| 155 * will be evident in real images. One might imagine using dithering | |
| 156 * to break up the horizontal and vertical shear lines, but this | |
| 157 * is hard with block shears, where you need to dither on the block | |
| 158 * boundaries. Dithering (by accumulation of 'error') with sampling | |
| 159 * makes more sense, but I haven't tried to do this. There is only | |
| 160 * so much you can do with 1 bpp images! | |
| 161 * </pre> | |
| 162 */ | |
| 163 | |
| 164 #ifdef HAVE_CONFIG_H | |
| 165 #include <config_auto.h> | |
| 166 #endif /* HAVE_CONFIG_H */ | |
| 167 | |
| 168 #include <math.h> | |
| 169 #include <string.h> | |
| 170 #include "allheaders.h" | |
| 171 | |
| 172 /* Angle limits: | |
| 173 * angle < MinAngleToRotate ==> clone | |
| 174 * angle > MaxTwoShearAngle ==> warning for 2-angle shears | |
| 175 * angle > MaxThreeShearAngle ==> warning for 3-angle shears | |
| 176 * angle > MaxShearAngle ==> error | |
| 177 */ | |
| 178 static const l_float32 MinAngleToRotate = 0.001f; /* radians; ~0.06 deg */ | |
| 179 static const l_float32 MaxTwoShearAngle = 0.06f; /* radians; ~3 deg */ | |
| 180 static const l_float32 MaxThreeShearAngle = 0.35f; /* radians; ~20 deg */ | |
| 181 static const l_float32 MaxShearAngle = 0.50f; /* radians; ~29 deg */ | |
| 182 | |
| 183 /*------------------------------------------------------------------* | |
| 184 * Rotations about an arbitrary point * | |
| 185 *------------------------------------------------------------------*/ | |
| 186 /*! | |
| 187 * \brief pixRotateShear() | |
| 188 * | |
| 189 * \param[in] pixs any depth; cmap ok | |
| 190 * \param[in] xcen x value for which there is no horizontal shear | |
| 191 * \param[in] ycen y value for which there is no vertical shear | |
| 192 * \param[in] angle radians | |
| 193 * \param[in] incolor L_BRING_IN_WHITE, L_BRING_IN_BLACK; | |
| 194 * \return pixd, or NULL on error. | |
| 195 * | |
| 196 * <pre> | |
| 197 * Notes: | |
| 198 * (1) This rotates an image about the given point, using | |
| 199 * either 2 or 3 shears. | |
| 200 * (2) A positive angle gives a clockwise rotation. | |
| 201 * (3) This brings in 'incolor' pixels from outside the image. | |
| 202 * (4) For rotation angles larger than about 0.35 radians, we issue | |
| 203 * a warning because you should probably be using another method | |
| 204 * (either sampling or area mapping) | |
| 205 * </pre> | |
| 206 */ | |
| 207 PIX * | |
| 208 pixRotateShear(PIX *pixs, | |
| 209 l_int32 xcen, | |
| 210 l_int32 ycen, | |
| 211 l_float32 angle, | |
| 212 l_int32 incolor) | |
| 213 { | |
| 214 if (!pixs) | |
| 215 return (PIX *)(PIX *)ERROR_PTR("pixs not defined", __func__, NULL); | |
| 216 if (incolor != L_BRING_IN_WHITE && incolor != L_BRING_IN_BLACK) | |
| 217 return (PIX *)(PIX *)ERROR_PTR("invalid incolor value", __func__, NULL); | |
| 218 | |
| 219 if (L_ABS(angle) > MaxShearAngle) { | |
| 220 L_ERROR("%6.2f radians; too large for shear rotation\n", __func__, | |
| 221 L_ABS(angle)); | |
| 222 return NULL; | |
| 223 } | |
| 224 if (L_ABS(angle) < MinAngleToRotate) | |
| 225 return pixClone(pixs); | |
| 226 | |
| 227 if (L_ABS(angle) <= MaxTwoShearAngle) | |
| 228 return pixRotate2Shear(pixs, xcen, ycen, angle, incolor); | |
| 229 else | |
| 230 return pixRotate3Shear(pixs, xcen, ycen, angle, incolor); | |
| 231 } | |
| 232 | |
| 233 | |
| 234 /*! | |
| 235 * \brief pixRotate2Shear() | |
| 236 * | |
| 237 * \param[in] pixs any depth; cmap ok | |
| 238 * \param[in] xcen, ycen center of rotation | |
| 239 * \param[in] angle radians | |
| 240 * \param[in] incolor L_BRING_IN_WHITE, L_BRING_IN_BLACK; | |
| 241 * \return pixd, or NULL on error. | |
| 242 * | |
| 243 * <pre> | |
| 244 * Notes: | |
| 245 * (1) This rotates the image about the given point, using the 2-shear | |
| 246 * method. It should only be used for angles no larger than | |
| 247 * MaxTwoShearAngle. For larger angles, a warning is issued. | |
| 248 * (2) A positive angle gives a clockwise rotation. | |
| 249 * (3) 2-shear rotation by a specified angle is equivalent | |
| 250 * to the sequential transformations | |
| 251 * x' = x + tan(angle) * (y - ycen) for x-shear | |
| 252 * y' = y + tan(angle) * (x - xcen) for y-shear | |
| 253 * (4) Computation of tan(angle) is performed within the shear operation. | |
| 254 * (5) This brings in 'incolor' pixels from outside the image. | |
| 255 * (6) If the image has an alpha layer, it is rotated separately by | |
| 256 * two shears. | |
| 257 * </pre> | |
| 258 */ | |
| 259 PIX * | |
| 260 pixRotate2Shear(PIX *pixs, | |
| 261 l_int32 xcen, | |
| 262 l_int32 ycen, | |
| 263 l_float32 angle, | |
| 264 l_int32 incolor) | |
| 265 { | |
| 266 PIX *pix1, *pix2, *pixd; | |
| 267 | |
| 268 if (!pixs) | |
| 269 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); | |
| 270 if (incolor != L_BRING_IN_WHITE && incolor != L_BRING_IN_BLACK) | |
| 271 return (PIX *)(PIX *)ERROR_PTR("invalid incolor value", __func__, NULL); | |
| 272 | |
| 273 if (L_ABS(angle) > MaxShearAngle) { | |
| 274 L_ERROR("%6.2f radians; too large for shear rotation\n", __func__, | |
| 275 L_ABS(angle)); | |
| 276 return NULL; | |
| 277 } | |
| 278 if (L_ABS(angle) < MinAngleToRotate) | |
| 279 return pixClone(pixs); | |
| 280 if (L_ABS(angle) > MaxTwoShearAngle) | |
| 281 L_WARNING("%6.2f radians; large angle for 2-shear rotation\n", | |
| 282 __func__, L_ABS(angle)); | |
| 283 | |
| 284 if ((pix1 = pixHShear(NULL, pixs, ycen, angle, incolor)) == NULL) | |
| 285 return (PIX *)ERROR_PTR("pix1 not made", __func__, NULL); | |
| 286 pixd = pixVShear(NULL, pix1, xcen, angle, incolor); | |
| 287 pixDestroy(&pix1); | |
| 288 if (!pixd) | |
| 289 return (PIX *)ERROR_PTR("pixd not made", __func__, NULL); | |
| 290 | |
| 291 if (pixGetDepth(pixs) == 32 && pixGetSpp(pixs) == 4) { | |
| 292 pix1 = pixGetRGBComponent(pixs, L_ALPHA_CHANNEL); | |
| 293 /* L_BRING_IN_WHITE brings in opaque for the alpha component */ | |
| 294 pix2 = pixRotate2Shear(pix1, xcen, ycen, angle, L_BRING_IN_WHITE); | |
| 295 pixSetRGBComponent(pixd, pix2, L_ALPHA_CHANNEL); | |
| 296 pixDestroy(&pix1); | |
| 297 pixDestroy(&pix2); | |
| 298 } | |
| 299 return pixd; | |
| 300 } | |
| 301 | |
| 302 | |
| 303 /*! | |
| 304 * \brief pixRotate3Shear() | |
| 305 * | |
| 306 * \param[in] pixs any depth; cmap ok | |
| 307 * \param[in] xcen, ycen center of rotation | |
| 308 * \param[in] angle radians | |
| 309 * \param[in] incolor L_BRING_IN_WHITE, L_BRING_IN_BLACK; | |
| 310 * \return pixd, or NULL on error. | |
| 311 * | |
| 312 * <pre> | |
| 313 * Notes: | |
| 314 * (1) This rotates the image about the given point, using the 3-shear | |
| 315 * method. It should only be used for angles smaller than | |
| 316 * MaxThreeShearAngle. For larger angles, a warning is issued. | |
| 317 * (2) A positive angle gives a clockwise rotation. | |
| 318 * (3) 3-shear rotation by a specified angle is equivalent | |
| 319 * to the sequential transformations | |
| 320 * y' = y + tan(angle/2) * (x - xcen) for first y-shear | |
| 321 * x' = x + sin(angle) * (y - ycen) for x-shear | |
| 322 * y' = y + tan(angle/2) * (x - xcen) for second y-shear | |
| 323 * (4) Computation of tan(angle) is performed in the shear operations. | |
| 324 * (5) This brings in 'incolor' pixels from outside the image. | |
| 325 * (6) If the image has an alpha layer, it is rotated separately by | |
| 326 * two shears. | |
| 327 * (7) The algorithm was published by Alan Paeth: "A Fast Algorithm | |
| 328 * for General Raster Rotation," Graphics Interface '86, | |
| 329 * pp. 77-81, May 1986. A description of the method, along with | |
| 330 * an implementation, can be found in Graphics Gems, p. 179, | |
| 331 * edited by Andrew Glassner, published by Academic Press, 1990. | |
| 332 * </pre> | |
| 333 */ | |
| 334 PIX * | |
| 335 pixRotate3Shear(PIX *pixs, | |
| 336 l_int32 xcen, | |
| 337 l_int32 ycen, | |
| 338 l_float32 angle, | |
| 339 l_int32 incolor) | |
| 340 { | |
| 341 l_float32 hangle; | |
| 342 PIX *pix1, *pix2, *pixd; | |
| 343 | |
| 344 if (!pixs) | |
| 345 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); | |
| 346 if (incolor != L_BRING_IN_WHITE && incolor != L_BRING_IN_BLACK) | |
| 347 return (PIX *)(PIX *)ERROR_PTR("invalid incolor value", __func__, NULL); | |
| 348 | |
| 349 if (L_ABS(angle) > MaxShearAngle) { | |
| 350 L_ERROR("%6.2f radians; too large for shear rotation\n", __func__, | |
| 351 L_ABS(angle)); | |
| 352 return NULL; | |
| 353 } | |
| 354 if (L_ABS(angle) < MinAngleToRotate) | |
| 355 return pixClone(pixs); | |
| 356 if (L_ABS(angle) > MaxThreeShearAngle) { | |
| 357 L_WARNING("%6.2f radians; large angle for 3-shear rotation\n", | |
| 358 __func__, L_ABS(angle)); | |
| 359 } | |
| 360 | |
| 361 hangle = atan(sin(angle)); | |
| 362 if ((pixd = pixVShear(NULL, pixs, xcen, angle / 2.f, incolor)) == NULL) | |
| 363 return (PIX *)ERROR_PTR("pixd not made", __func__, NULL); | |
| 364 if ((pix1 = pixHShear(NULL, pixd, ycen, hangle, incolor)) == NULL) { | |
| 365 pixDestroy(&pixd); | |
| 366 return (PIX *)ERROR_PTR("pix1 not made", __func__, NULL); | |
| 367 } | |
| 368 pixVShear(pixd, pix1, xcen, angle / 2.f, incolor); | |
| 369 pixDestroy(&pix1); | |
| 370 | |
| 371 if (pixGetDepth(pixs) == 32 && pixGetSpp(pixs) == 4) { | |
| 372 pix1 = pixGetRGBComponent(pixs, L_ALPHA_CHANNEL); | |
| 373 /* L_BRING_IN_WHITE brings in opaque for the alpha component */ | |
| 374 pix2 = pixRotate3Shear(pix1, xcen, ycen, angle, L_BRING_IN_WHITE); | |
| 375 pixSetRGBComponent(pixd, pix2, L_ALPHA_CHANNEL); | |
| 376 pixDestroy(&pix1); | |
| 377 pixDestroy(&pix2); | |
| 378 } | |
| 379 return pixd; | |
| 380 } | |
| 381 | |
| 382 | |
| 383 /*------------------------------------------------------------------* | |
| 384 * Rotations in-place about an arbitrary point * | |
| 385 *------------------------------------------------------------------*/ | |
| 386 /*! | |
| 387 * \brief pixRotateShearIP() | |
| 388 * | |
| 389 * \param[in] pixs any depth; no cmap | |
| 390 * \param[in] xcen, ycen center of rotation | |
| 391 * \param[in] angle radians | |
| 392 * \param[in] incolor L_BRING_IN_WHITE, L_BRING_IN_BLACK | |
| 393 * \return 0 if OK; 1 on error | |
| 394 * | |
| 395 * <pre> | |
| 396 * Notes: | |
| 397 * (1) This does an in-place rotation of the image about the | |
| 398 * specified point, using the 3-shear method. It should only | |
| 399 * be used for angles smaller than MaxThreeShearAngle. | |
| 400 * For larger angles, a warning is issued. | |
| 401 * (2) A positive angle gives a clockwise rotation. | |
| 402 * (3) 3-shear rotation by a specified angle is equivalent | |
| 403 * to the sequential transformations | |
| 404 * y' = y + tan(angle/2) * (x - xcen) for first y-shear | |
| 405 * x' = x + sin(angle) * (y - ycen) for x-shear | |
| 406 * y' = y + tan(angle/2) * (x - xcen) for second y-shear | |
| 407 * (4) Computation of tan(angle) is performed in the shear operations. | |
| 408 * (5) This brings in 'incolor' pixels from outside the image. | |
| 409 * (6) The pix cannot be colormapped, because the in-place operation | |
| 410 * only blits in 0 or 1 bits, not an arbitrary colormap index. | |
| 411 * </pre> | |
| 412 */ | |
| 413 l_ok | |
| 414 pixRotateShearIP(PIX *pixs, | |
| 415 l_int32 xcen, | |
| 416 l_int32 ycen, | |
| 417 l_float32 angle, | |
| 418 l_int32 incolor) | |
| 419 { | |
| 420 l_float32 hangle; | |
| 421 | |
| 422 if (!pixs) | |
| 423 return ERROR_INT("pixs not defined", __func__, 1); | |
| 424 if (incolor != L_BRING_IN_WHITE && incolor != L_BRING_IN_BLACK) | |
| 425 return ERROR_INT("invalid value for incolor", __func__, 1); | |
| 426 if (pixGetColormap(pixs) != NULL) | |
| 427 return ERROR_INT("pixs is colormapped", __func__, 1); | |
| 428 | |
| 429 if (angle == 0.0) | |
| 430 return 0; | |
| 431 if (L_ABS(angle) > MaxThreeShearAngle) { | |
| 432 L_WARNING("%6.2f radians; large angle for in-place 3-shear rotation\n", | |
| 433 __func__, L_ABS(angle)); | |
| 434 } | |
| 435 | |
| 436 hangle = atan(sin(angle)); | |
| 437 pixHShearIP(pixs, ycen, angle / 2.f, incolor); | |
| 438 pixVShearIP(pixs, xcen, hangle, incolor); | |
| 439 pixHShearIP(pixs, ycen, angle / 2.f, incolor); | |
| 440 return 0; | |
| 441 } | |
| 442 | |
| 443 | |
| 444 /*------------------------------------------------------------------* | |
| 445 * Rotations about the image center * | |
| 446 *------------------------------------------------------------------*/ | |
| 447 /*! | |
| 448 * \brief pixRotateShearCenter() | |
| 449 * | |
| 450 * \param[in] pixs any depth; cmap ok | |
| 451 * \param[in] angle radians | |
| 452 * \param[in] incolor L_BRING_IN_WHITE, L_BRING_IN_BLACK | |
| 453 * \return pixd, or NULL on error | |
| 454 */ | |
| 455 PIX * | |
| 456 pixRotateShearCenter(PIX *pixs, | |
| 457 l_float32 angle, | |
| 458 l_int32 incolor) | |
| 459 { | |
| 460 if (!pixs) | |
| 461 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); | |
| 462 | |
| 463 return pixRotateShear(pixs, pixGetWidth(pixs) / 2, | |
| 464 pixGetHeight(pixs) / 2, angle, incolor); | |
| 465 } | |
| 466 | |
| 467 | |
| 468 /*! | |
| 469 * \brief pixRotateShearCenterIP() | |
| 470 * | |
| 471 * \param[in] pixs any depth; no cmap | |
| 472 * \param[in] angle radians | |
| 473 * \param[in] incolor L_BRING_IN_WHITE, L_BRING_IN_BLACK | |
| 474 * \return 0 if OK, 1 on error | |
| 475 */ | |
| 476 l_ok | |
| 477 pixRotateShearCenterIP(PIX *pixs, | |
| 478 l_float32 angle, | |
| 479 l_int32 incolor) | |
| 480 { | |
| 481 if (!pixs) | |
| 482 return ERROR_INT("pixs not defined", __func__, 1); | |
| 483 | |
| 484 return pixRotateShearIP(pixs, pixGetWidth(pixs) / 2, | |
| 485 pixGetHeight(pixs) / 2, angle, incolor); | |
| 486 } |
