mirror of http://192.168.1.51:8099/lmh188/twain3.0
495 lines
20 KiB
C
495 lines
20 KiB
C
|
/*====================================================================*
|
||
|
- Copyright (C) 2001 Leptonica. All rights reserved.
|
||
|
-
|
||
|
- Redistribution and use in source and binary forms, with or without
|
||
|
- modification, are permitted provided that the following conditions
|
||
|
- are met:
|
||
|
- 1. Redistributions of source code must retain the above copyright
|
||
|
- notice, this list of conditions and the following disclaimer.
|
||
|
- 2. Redistributions in binary form must reproduce the above
|
||
|
- copyright notice, this list of conditions and the following
|
||
|
- disclaimer in the documentation and/or other materials
|
||
|
- provided with the distribution.
|
||
|
-
|
||
|
- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||
|
- ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||
|
- LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||
|
- A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ANY
|
||
|
- CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||
|
- EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||
|
- PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||
|
- PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
|
||
|
- OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||
|
- NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||
|
- SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||
|
*====================================================================*/
|
||
|
|
||
|
/*!
|
||
|
* \file rotateshear.c
|
||
|
* <pre>
|
||
|
*
|
||
|
* Shear rotation about arbitrary point using 2 and 3 shears
|
||
|
*
|
||
|
* PIX *pixRotateShear()
|
||
|
* PIX *pixRotate2Shear()
|
||
|
* PIX *pixRotate3Shear()
|
||
|
*
|
||
|
* Shear rotation in-place about arbitrary point using 3 shears
|
||
|
* l_int32 pixRotateShearIP()
|
||
|
*
|
||
|
* Shear rotation around the image center
|
||
|
* PIX *pixRotateShearCenter() (2 or 3 shears)
|
||
|
* l_int32 pixRotateShearCenterIP() (3 shears)
|
||
|
*
|
||
|
* Rotation is measured in radians; clockwise rotations are positive.
|
||
|
*
|
||
|
* Rotation by shear works on images of any depth,
|
||
|
* including 8 bpp color paletted images and 32 bpp
|
||
|
* rgb images. It works by translating each src pixel
|
||
|
* value to the appropriate pixel in the rotated dest.
|
||
|
* For 8 bpp grayscale images, it is about 10-15x faster
|
||
|
* than rotation by area-mapping.
|
||
|
*
|
||
|
* This speed and flexibility comes at the following cost,
|
||
|
* relative to area-mapped rotation:
|
||
|
*
|
||
|
* ~ Jaggies are created on edges of straight lines
|
||
|
*
|
||
|
* ~ For large angles, where you must use 3 shears,
|
||
|
* there is some extra clipping from the shears.
|
||
|
*
|
||
|
* For small angles, typically less than 0.05 radians,
|
||
|
* rotation can be done with 2 orthogonal shears.
|
||
|
* Two such continuous shears (as opposed to the discrete
|
||
|
* shears on a pixel lattice that we have here) give
|
||
|
* a rotated image that has a distortion in the lengths
|
||
|
* of the two rotated and still-perpendicular axes. The
|
||
|
* length/width ratio changes by a fraction
|
||
|
*
|
||
|
* 0.5 * (angle)**2
|
||
|
*
|
||
|
* For an angle of 0.05 radians, this is about 1 part in
|
||
|
* a thousand. This distortion is absent when you use
|
||
|
* 3 continuous shears with the correct angles (see below).
|
||
|
*
|
||
|
* Of course, the image is on a discrete pixel lattice.
|
||
|
* Rotation by shear gives an approximation to a continuous
|
||
|
* rotation, leaving pixel jaggies at sharp boundaries.
|
||
|
* For very small rotations, rotating from a corner gives
|
||
|
* better sensitivity than rotating from the image center.
|
||
|
* Here's why. Define the shear "center" to be the line such
|
||
|
* that the image is sheared in opposite directions on
|
||
|
* each side of and parallel to the line. For small
|
||
|
* rotations there is a "dead space" on each side of the
|
||
|
* shear center of width equal to half the shear angle,
|
||
|
* in radians. Thus, when the image is sheared about the center,
|
||
|
* the dead space width equals the shear angle, but when
|
||
|
* the image is sheared from a corner, the dead space
|
||
|
* width is only half the shear angle.
|
||
|
*
|
||
|
* All horizontal and vertical shears are implemented by
|
||
|
* rasterop. The in-place rotation uses special in-place
|
||
|
* shears that copy rows sideways or columns vertically
|
||
|
* without buffering, and then rewrite old pixels that are
|
||
|
* no longer covered by sheared pixels. For that rewriting,
|
||
|
* you have the choice of using white or black pixels.
|
||
|
* When not in-place, the new pix is initialized with white or black
|
||
|
* pixels by pixSetBlackOrWhite(), which also works for cmapped pix.
|
||
|
* But for in-place, this initialization is not possible, so
|
||
|
* in-place shear operations on cmapped pix are not allowed.
|
||
|
*
|
||
|
* Rotation by shear is fast and depth-independent. However, it
|
||
|
* does not work well for large rotation angles. In fact, for
|
||
|
* rotation angles greater than about 7 degrees, more pixels are
|
||
|
* lost at the edges than when using pixRotationBySampling(), which
|
||
|
* only loses pixels because they are rotated out of the image.
|
||
|
* For larger rotations, use pixRotationBySampling() or, for
|
||
|
* more accuracy when d > 1 bpp, pixRotateAM().
|
||
|
*
|
||
|
* For small angles, when comparing the quality of rotation by
|
||
|
* sampling and by shear, you can see that rotation by sampling
|
||
|
* is slightly more accurate. However, the difference in
|
||
|
* accuracy of rotation by sampling when compared to 3-shear and
|
||
|
* (for angles less than 2 degrees, when compared to 2-shear) is
|
||
|
* less than 1 pixel at any point. For very small angles, rotation by
|
||
|
* sampling is much slower than rotation by shear. The speed difference
|
||
|
* depends on the pixel depth and the rotation angle. Rotation
|
||
|
* by shear is very fast for small angles and for small depth (esp. 1 bpp).
|
||
|
* Rotation by sampling speed is independent of angle and relatively
|
||
|
* more efficient for 8 and 32 bpp images. Here are some timings
|
||
|
* for the ratio of rotation times: (time for sampling)/ (time for shear)
|
||
|
*
|
||
|
* depth (bpp) ratio (2 deg) ratio (10 deg)
|
||
|
* -----------------------------------------------------
|
||
|
* 1 25 6
|
||
|
* 8 5 2.6
|
||
|
* 32 1.6 1.0
|
||
|
*
|
||
|
* In summary:
|
||
|
* * For d == 1 and small angles, use rotation by shear. By default
|
||
|
* this will use 2-shear rotations, because 3-shears cause more
|
||
|
* visible artifacts in straight lines and, for small angles, the
|
||
|
* distortion in asperity ratio is small.
|
||
|
* * For d > 1, shear is faster than sampling, which is faster than
|
||
|
* area mapping. However, area mapping gives the best results.
|
||
|
* These results are used in selecting the rotation methods in
|
||
|
* pixRotateShear().
|
||
|
*
|
||
|
* There has been some work on what is called a "quasishear
|
||
|
* rotation" ("The Quasi-Shear Rotation, Eric Andres,
|
||
|
* DGCI 1996, pp. 307-314). I believe they use a 3-shear
|
||
|
* approximation to the continuous rotation, exactly as
|
||
|
* we do here. The approximation is due to being on
|
||
|
* a square pixel lattice. They also use integers to specify
|
||
|
* the rotation angle and center offset, but that makes
|
||
|
* little sense on a machine where you have a few GFLOPS
|
||
|
* and only a few hundred floating point operations to do (!)
|
||
|
* They also allow subpixel specification of the center of
|
||
|
* rotation, which I haven't bothered with, and claim that
|
||
|
* better results are possible if each of the 4 quadrants is
|
||
|
* handled separately.
|
||
|
*
|
||
|
* But the bottom line is that you are going to see shear lines when
|
||
|
* you rotate 1 bpp images. Although the 3-shear rotation is
|
||
|
* mathematically exact in the limit of infinitesimal pixels, artifacts
|
||
|
* will be evident in real images. One might imagine using dithering
|
||
|
* to break up the horizontal and vertical shear lines, but this
|
||
|
* is hard with block shears, where you need to dither on the block
|
||
|
* boundaries. Dithering (by accumulation of 'error') with sampling
|
||
|
* makes more sense, but I haven't tried to do this. There is only
|
||
|
* so much you can do with 1 bpp images!
|
||
|
* </pre>
|
||
|
*/
|
||
|
|
||
|
#include <math.h>
|
||
|
#include <string.h>
|
||
|
#include "allheaders.h"
|
||
|
|
||
|
/* Angle limits:
|
||
|
* angle < MinAngleToRotate ==> clone
|
||
|
* angle > MaxTwoShearAngle ==> warning for 2-angle shears
|
||
|
* angle > MaxThreeShearAngle ==> warning for 3-angle shears
|
||
|
* angle > MaxShearAngle ==> error
|
||
|
*/
|
||
|
static const l_float32 MinAngleToRotate = 0.001; /* radians; ~0.06 deg */
|
||
|
static const l_float32 MaxTwoShearAngle = 0.06; /* radians; ~3 deg */
|
||
|
static const l_float32 MaxThreeShearAngle = 0.35; /* radians; ~20 deg */
|
||
|
static const l_float32 MaxShearAngle = 0.50; /* radians; ~29 deg */
|
||
|
|
||
|
/*------------------------------------------------------------------*
|
||
|
* Rotations about an arbitrary point *
|
||
|
*------------------------------------------------------------------*/
|
||
|
/*!
|
||
|
* \brief pixRotateShear()
|
||
|
*
|
||
|
* \param[in] pixs any depth; cmap ok
|
||
|
* \param[in] xcen x value for which there is no horizontal shear
|
||
|
* \param[in] ycen y value for which there is no vertical shear
|
||
|
* \param[in] angle radians
|
||
|
* \param[in] incolor L_BRING_IN_WHITE, L_BRING_IN_BLACK;
|
||
|
* \return pixd, or NULL on error.
|
||
|
*
|
||
|
* <pre>
|
||
|
* Notes:
|
||
|
* (1) This rotates an image about the given point, using
|
||
|
* either 2 or 3 shears.
|
||
|
* (2) A positive angle gives a clockwise rotation.
|
||
|
* (3) This brings in 'incolor' pixels from outside the image.
|
||
|
* (4) For rotation angles larger than about 0.35 radians, we issue
|
||
|
* a warning because you should probably be using another method
|
||
|
* (either sampling or area mapping)
|
||
|
* </pre>
|
||
|
*/
|
||
|
PIX *
|
||
|
pixRotateShear(PIX *pixs,
|
||
|
l_int32 xcen,
|
||
|
l_int32 ycen,
|
||
|
l_float32 angle,
|
||
|
l_int32 incolor)
|
||
|
{
|
||
|
PROCNAME("pixRotateShear");
|
||
|
|
||
|
if (!pixs)
|
||
|
return (PIX *)(PIX *)ERROR_PTR("pixs not defined", procName, NULL);
|
||
|
if (incolor != L_BRING_IN_WHITE && incolor != L_BRING_IN_BLACK)
|
||
|
return (PIX *)(PIX *)ERROR_PTR("invalid incolor value", procName, NULL);
|
||
|
|
||
|
if (L_ABS(angle) > MaxShearAngle) {
|
||
|
L_ERROR("%6.2f radians; too large for shear rotation\n", procName,
|
||
|
L_ABS(angle));
|
||
|
return NULL;
|
||
|
}
|
||
|
if (L_ABS(angle) < MinAngleToRotate)
|
||
|
return pixClone(pixs);
|
||
|
|
||
|
if (L_ABS(angle) <= MaxTwoShearAngle)
|
||
|
return pixRotate2Shear(pixs, xcen, ycen, angle, incolor);
|
||
|
else
|
||
|
return pixRotate3Shear(pixs, xcen, ycen, angle, incolor);
|
||
|
}
|
||
|
|
||
|
|
||
|
/*!
|
||
|
* \brief pixRotate2Shear()
|
||
|
*
|
||
|
* \param[in] pixs any depth; cmap ok
|
||
|
* \param[in] xcen, ycen center of rotation
|
||
|
* \param[in] angle radians
|
||
|
* \param[in] incolor L_BRING_IN_WHITE, L_BRING_IN_BLACK;
|
||
|
* \return pixd, or NULL on error.
|
||
|
*
|
||
|
* <pre>
|
||
|
* Notes:
|
||
|
* (1) This rotates the image about the given point, using the 2-shear
|
||
|
* method. It should only be used for angles no larger than
|
||
|
* MaxTwoShearAngle. For larger angles, a warning is issued.
|
||
|
* (2) A positive angle gives a clockwise rotation.
|
||
|
* (3) 2-shear rotation by a specified angle is equivalent
|
||
|
* to the sequential transformations
|
||
|
* x' = x + tan(angle) * (y - ycen) for x-shear
|
||
|
* y' = y + tan(angle) * (x - xcen) for y-shear
|
||
|
* (4) Computation of tan(angle) is performed within the shear operation.
|
||
|
* (5) This brings in 'incolor' pixels from outside the image.
|
||
|
* (6) If the image has an alpha layer, it is rotated separately by
|
||
|
* two shears.
|
||
|
* </pre>
|
||
|
*/
|
||
|
PIX *
|
||
|
pixRotate2Shear(PIX *pixs,
|
||
|
l_int32 xcen,
|
||
|
l_int32 ycen,
|
||
|
l_float32 angle,
|
||
|
l_int32 incolor)
|
||
|
{
|
||
|
PIX *pix1, *pix2, *pixd;
|
||
|
|
||
|
PROCNAME("pixRotate2Shear");
|
||
|
|
||
|
if (!pixs)
|
||
|
return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
|
||
|
if (incolor != L_BRING_IN_WHITE && incolor != L_BRING_IN_BLACK)
|
||
|
return (PIX *)(PIX *)ERROR_PTR("invalid incolor value", procName, NULL);
|
||
|
|
||
|
if (L_ABS(angle) > MaxShearAngle) {
|
||
|
L_ERROR("%6.2f radians; too large for shear rotation\n", procName,
|
||
|
L_ABS(angle));
|
||
|
return NULL;
|
||
|
}
|
||
|
if (L_ABS(angle) < MinAngleToRotate)
|
||
|
return pixClone(pixs);
|
||
|
if (L_ABS(angle) > MaxTwoShearAngle)
|
||
|
L_WARNING("%6.2f radians; large angle for 2-shear rotation\n",
|
||
|
procName, L_ABS(angle));
|
||
|
|
||
|
if ((pix1 = pixHShear(NULL, pixs, ycen, angle, incolor)) == NULL)
|
||
|
return (PIX *)ERROR_PTR("pix1 not made", procName, NULL);
|
||
|
pixd = pixVShear(NULL, pix1, xcen, angle, incolor);
|
||
|
pixDestroy(&pix1);
|
||
|
if (!pixd)
|
||
|
return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
|
||
|
|
||
|
if (pixGetDepth(pixs) == 32 && pixGetSpp(pixs) == 4) {
|
||
|
pix1 = pixGetRGBComponent(pixs, L_ALPHA_CHANNEL);
|
||
|
/* L_BRING_IN_WHITE brings in opaque for the alpha component */
|
||
|
pix2 = pixRotate2Shear(pix1, xcen, ycen, angle, L_BRING_IN_WHITE);
|
||
|
pixSetRGBComponent(pixd, pix2, L_ALPHA_CHANNEL);
|
||
|
pixDestroy(&pix1);
|
||
|
pixDestroy(&pix2);
|
||
|
}
|
||
|
return pixd;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*!
|
||
|
* \brief pixRotate3Shear()
|
||
|
*
|
||
|
* \param[in] pixs any depth; cmap ok
|
||
|
* \param[in] xcen, ycen center of rotation
|
||
|
* \param[in] angle radians
|
||
|
* \param[in] incolor L_BRING_IN_WHITE, L_BRING_IN_BLACK;
|
||
|
* \return pixd, or NULL on error.
|
||
|
*
|
||
|
* <pre>
|
||
|
* Notes:
|
||
|
* (1) This rotates the image about the given point, using the 3-shear
|
||
|
* method. It should only be used for angles smaller than
|
||
|
* MaxThreeShearAngle. For larger angles, a warning is issued.
|
||
|
* (2) A positive angle gives a clockwise rotation.
|
||
|
* (3) 3-shear rotation by a specified angle is equivalent
|
||
|
* to the sequential transformations
|
||
|
* y' = y + tan(angle/2) * (x - xcen) for first y-shear
|
||
|
* x' = x + sin(angle) * (y - ycen) for x-shear
|
||
|
* y' = y + tan(angle/2) * (x - xcen) for second y-shear
|
||
|
* (4) Computation of tan(angle) is performed in the shear operations.
|
||
|
* (5) This brings in 'incolor' pixels from outside the image.
|
||
|
* (6) If the image has an alpha layer, it is rotated separately by
|
||
|
* two shears.
|
||
|
* (7) The algorithm was published by Alan Paeth: "A Fast Algorithm
|
||
|
* for General Raster Rotation," Graphics Interface '86,
|
||
|
* pp. 77-81, May 1986. A description of the method, along with
|
||
|
* an implementation, can be found in Graphics Gems, p. 179,
|
||
|
* edited by Andrew Glassner, published by Academic Press, 1990.
|
||
|
* </pre>
|
||
|
*/
|
||
|
PIX *
|
||
|
pixRotate3Shear(PIX *pixs,
|
||
|
l_int32 xcen,
|
||
|
l_int32 ycen,
|
||
|
l_float32 angle,
|
||
|
l_int32 incolor)
|
||
|
{
|
||
|
l_float32 hangle;
|
||
|
PIX *pix1, *pix2, *pixd;
|
||
|
|
||
|
PROCNAME("pixRotate3Shear");
|
||
|
|
||
|
if (!pixs)
|
||
|
return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
|
||
|
if (incolor != L_BRING_IN_WHITE && incolor != L_BRING_IN_BLACK)
|
||
|
return (PIX *)(PIX *)ERROR_PTR("invalid incolor value", procName, NULL);
|
||
|
|
||
|
if (L_ABS(angle) > MaxShearAngle) {
|
||
|
L_ERROR("%6.2f radians; too large for shear rotation\n", procName,
|
||
|
L_ABS(angle));
|
||
|
return NULL;
|
||
|
}
|
||
|
if (L_ABS(angle) < MinAngleToRotate)
|
||
|
return pixClone(pixs);
|
||
|
if (L_ABS(angle) > MaxThreeShearAngle) {
|
||
|
L_WARNING("%6.2f radians; large angle for 3-shear rotation\n",
|
||
|
procName, L_ABS(angle));
|
||
|
}
|
||
|
|
||
|
hangle = atan(sin(angle));
|
||
|
if ((pixd = pixVShear(NULL, pixs, xcen, angle / 2., incolor)) == NULL)
|
||
|
return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
|
||
|
if ((pix1 = pixHShear(NULL, pixd, ycen, hangle, incolor)) == NULL) {
|
||
|
pixDestroy(&pixd);
|
||
|
return (PIX *)ERROR_PTR("pix1 not made", procName, NULL);
|
||
|
}
|
||
|
pixVShear(pixd, pix1, xcen, angle / 2., incolor);
|
||
|
pixDestroy(&pix1);
|
||
|
|
||
|
if (pixGetDepth(pixs) == 32 && pixGetSpp(pixs) == 4) {
|
||
|
pix1 = pixGetRGBComponent(pixs, L_ALPHA_CHANNEL);
|
||
|
/* L_BRING_IN_WHITE brings in opaque for the alpha component */
|
||
|
pix2 = pixRotate3Shear(pix1, xcen, ycen, angle, L_BRING_IN_WHITE);
|
||
|
pixSetRGBComponent(pixd, pix2, L_ALPHA_CHANNEL);
|
||
|
pixDestroy(&pix1);
|
||
|
pixDestroy(&pix2);
|
||
|
}
|
||
|
return pixd;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*------------------------------------------------------------------*
|
||
|
* Rotations in-place about an arbitrary point *
|
||
|
*------------------------------------------------------------------*/
|
||
|
/*!
|
||
|
* \brief pixRotateShearIP()
|
||
|
*
|
||
|
* \param[in] pixs any depth; no cmap
|
||
|
* \param[in] xcen, ycen center of rotation
|
||
|
* \param[in] angle radians
|
||
|
* \param[in] incolor L_BRING_IN_WHITE, L_BRING_IN_BLACK
|
||
|
* \return 0 if OK; 1 on error
|
||
|
*
|
||
|
* <pre>
|
||
|
* Notes:
|
||
|
* (1) This does an in-place rotation of the image about the
|
||
|
* specified point, using the 3-shear method. It should only
|
||
|
* be used for angles smaller than MaxThreeShearAngle.
|
||
|
* For larger angles, a warning is issued.
|
||
|
* (2) A positive angle gives a clockwise rotation.
|
||
|
* (3) 3-shear rotation by a specified angle is equivalent
|
||
|
* to the sequential transformations
|
||
|
* y' = y + tan(angle/2) * (x - xcen) for first y-shear
|
||
|
* x' = x + sin(angle) * (y - ycen) for x-shear
|
||
|
* y' = y + tan(angle/2) * (x - xcen) for second y-shear
|
||
|
* (4) Computation of tan(angle) is performed in the shear operations.
|
||
|
* (5) This brings in 'incolor' pixels from outside the image.
|
||
|
* (6) The pix cannot be colormapped, because the in-place operation
|
||
|
* only blits in 0 or 1 bits, not an arbitrary colormap index.
|
||
|
* </pre>
|
||
|
*/
|
||
|
l_ok
|
||
|
pixRotateShearIP(PIX *pixs,
|
||
|
l_int32 xcen,
|
||
|
l_int32 ycen,
|
||
|
l_float32 angle,
|
||
|
l_int32 incolor)
|
||
|
{
|
||
|
l_float32 hangle;
|
||
|
|
||
|
PROCNAME("pixRotateShearIP");
|
||
|
|
||
|
if (!pixs)
|
||
|
return ERROR_INT("pixs not defined", procName, 1);
|
||
|
if (incolor != L_BRING_IN_WHITE && incolor != L_BRING_IN_BLACK)
|
||
|
return ERROR_INT("invalid value for incolor", procName, 1);
|
||
|
if (pixGetColormap(pixs) != NULL)
|
||
|
return ERROR_INT("pixs is colormapped", procName, 1);
|
||
|
|
||
|
if (angle == 0.0)
|
||
|
return 0;
|
||
|
if (L_ABS(angle) > MaxThreeShearAngle) {
|
||
|
L_WARNING("%6.2f radians; large angle for in-place 3-shear rotation\n",
|
||
|
procName, L_ABS(angle));
|
||
|
}
|
||
|
|
||
|
hangle = atan(sin(angle));
|
||
|
pixHShearIP(pixs, ycen, angle / 2., incolor);
|
||
|
pixVShearIP(pixs, xcen, hangle, incolor);
|
||
|
pixHShearIP(pixs, ycen, angle / 2., incolor);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*------------------------------------------------------------------*
|
||
|
* Rotations about the image center *
|
||
|
*------------------------------------------------------------------*/
|
||
|
/*!
|
||
|
* \brief pixRotateShearCenter()
|
||
|
*
|
||
|
* \param[in] pixs any depth; cmap ok
|
||
|
* \param[in] angle radians
|
||
|
* \param[in] incolor L_BRING_IN_WHITE, L_BRING_IN_BLACK
|
||
|
* \return pixd, or NULL on error
|
||
|
*/
|
||
|
PIX *
|
||
|
pixRotateShearCenter(PIX *pixs,
|
||
|
l_float32 angle,
|
||
|
l_int32 incolor)
|
||
|
{
|
||
|
PROCNAME("pixRotateShearCenter");
|
||
|
|
||
|
if (!pixs)
|
||
|
return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
|
||
|
|
||
|
return pixRotateShear(pixs, pixGetWidth(pixs) / 2,
|
||
|
pixGetHeight(pixs) / 2, angle, incolor);
|
||
|
}
|
||
|
|
||
|
|
||
|
/*!
|
||
|
* \brief pixRotateShearCenterIP()
|
||
|
*
|
||
|
* \param[in] pixs any depth; no cmap
|
||
|
* \param[in] angle radians
|
||
|
* \param[in] incolor L_BRING_IN_WHITE, L_BRING_IN_BLACK
|
||
|
* \return 0 if OK, 1 on error
|
||
|
*/
|
||
|
l_ok
|
||
|
pixRotateShearCenterIP(PIX *pixs,
|
||
|
l_float32 angle,
|
||
|
l_int32 incolor)
|
||
|
{
|
||
|
PROCNAME("pixRotateShearCenterIP");
|
||
|
|
||
|
if (!pixs)
|
||
|
return ERROR_INT("pixs not defined", procName, 1);
|
||
|
|
||
|
return pixRotateShearIP(pixs, pixGetWidth(pixs) / 2,
|
||
|
pixGetHeight(pixs) / 2, angle, incolor);
|
||
|
}
|