/*====================================================================* - 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 shear.c *
 *
 *    About arbitrary lines
 *           PIX      *pixHShear()
 *           PIX      *pixVShear()
 *
 *    About special 'points': UL corner and center
 *           PIX      *pixHShearCorner()
 *           PIX      *pixVShearCorner()
 *           PIX      *pixHShearCenter()
 *           PIX      *pixVShearCenter()
 *
 *    In place about arbitrary lines
 *           l_int32   pixHShearIP()
 *           l_int32   pixVShearIP()
 *
 *    Linear interpolated shear about arbitrary lines
 *           PIX      *pixHShearLI()
 *           PIX      *pixVShearLI()
 *
 *    Static helper
 *      static l_float32  normalizeAngleForShear()
 * 
*/ #ifdef HAVE_CONFIG_H #include #endif /* HAVE_CONFIG_H */ #include #include #include "allheaders.h" /* Shear angle must not get too close to -pi/2 or pi/2 */ static const l_float32 MinDiffFromHalfPi = 0.04f; static l_float32 normalizeAngleForShear(l_float32 radang, l_float32 mindif); #ifndef NO_CONSOLE_IO #define DEBUG 0 #endif /* ~NO_CONSOLE_IO */ /*-------------------------------------------------------------* * About arbitrary lines * *-------------------------------------------------------------*/ /*! * \brief pixHShear() * * \param[in] pixd [optional] this can be null, equal to pixs, * or different from pixs * \param[in] pixs any depth; cmap ok * \param[in] yloc location of horizontal line, measured from origin * \param[in] radang angle in radians * \param[in] incolor L_BRING_IN_WHITE, L_BRING_IN_BLACK; * \return pixd, always * *
 * Notes:
 *      (1) There are 3 cases:
 *            (a) pixd == null (make a new pixd)
 *            (b) pixd == pixs (in-place)
 *            (c) pixd != pixs
 *      (2) For these three cases, use these patterns, respectively:
 *              pixd = pixHShear(NULL, pixs, ...);
 *              pixHShear(pixs, pixs, ...);
 *              pixHShear(pixd, pixs, ...);
 *      (3) This shear leaves the horizontal line of pixels at y = yloc
 *          invariant.  For a positive shear angle, pixels above this
 *          line are shoved to the right, and pixels below this line
 *          move to the left.
 *      (4) With positive shear angle, this can be used, along with
 *          pixVShear(), to perform a cw rotation, either with 2 shears
 *          (for small angles) or in the general case with 3 shears.
 *      (5) Changing the value of yloc is equivalent to translating
 *          the result horizontally.
 *      (6) This brings in %incolor pixels from outside the image.
 *      (7) In-place shears do not work on cmapped pix, because the
 *          in-place operation cannot initialize to the requested %incolor,
 *          so we shear from a copy.
 *      (8) The angle is brought into the range [-pi, -pi].  It is
 *          not permitted to be within MinDiffFromHalfPi radians
 *          from either -pi/2 or pi/2.
 * 
*/ PIX * pixHShear(PIX *pixd, PIX *pixs, l_int32 yloc, l_float32 radang, l_int32 incolor) { l_int32 sign, w, h; l_int32 y, yincr, inityincr, hshift; l_float32 tanangle, invangle; PROCNAME("pixHShear"); if (!pixs) return (PIX *)ERROR_PTR("pixs not defined", procName, pixd); if (incolor != L_BRING_IN_WHITE && incolor != L_BRING_IN_BLACK) return (PIX *)ERROR_PTR("invalid incolor value", procName, pixd); if (pixd == pixs) { /* in place */ if (!pixGetColormap(pixs)) { pixHShearIP(pixd, yloc, radang, incolor); } else { /* can't do in-place with a colormap */ PIX *pix1 = pixCopy(NULL, pixs); pixHShear(pixd, pix1, yloc, radang, incolor); pixDestroy(&pix1); } return pixd; } /* Make sure pixd exists and is same size as pixs */ if (!pixd) { if ((pixd = pixCreateTemplate(pixs)) == NULL) return (PIX *)ERROR_PTR("pixd not made", procName, NULL); } else { /* pixd != pixs */ pixResizeImageData(pixd, pixs); } /* Normalize angle. If no rotation, return a copy */ radang = normalizeAngleForShear(radang, MinDiffFromHalfPi); if (radang == 0.0 || tan(radang) == 0.0) return pixCopy(pixd, pixs); /* Initialize to value of incoming pixels */ pixSetBlackOrWhite(pixd, incolor); pixGetDimensions(pixs, &w, &h, NULL); sign = L_SIGN(radang); tanangle = tan(radang); invangle = L_ABS(1. / tanangle); inityincr = (l_int32)(invangle / 2.); yincr = (l_int32)invangle; pixRasterop(pixd, 0, yloc - inityincr, w, 2 * inityincr, PIX_SRC, pixs, 0, yloc - inityincr); for (hshift = 1, y = yloc + inityincr; y < h; hshift++) { yincr = (l_int32)(invangle * (hshift + 0.5) + 0.5) - (y - yloc); if (h - y < yincr) /* reduce for last one if req'd */ yincr = h - y; pixRasterop(pixd, -sign*hshift, y, w, yincr, PIX_SRC, pixs, 0, y); #if DEBUG lept_stderr("y = %d, hshift = %d, yincr = %d\n", y, hshift, yincr); #endif /* DEBUG */ y += yincr; } for (hshift = -1, y = yloc - inityincr; y > 0; hshift--) { yincr = (y - yloc) - (l_int32)(invangle * (hshift - 0.5) + 0.5); if (y < yincr) /* reduce for last one if req'd */ yincr = y; pixRasterop(pixd, -sign*hshift, y - yincr, w, yincr, PIX_SRC, pixs, 0, y - yincr); #if DEBUG lept_stderr("y = %d, hshift = %d, yincr = %d\n", y - yincr, hshift, yincr); #endif /* DEBUG */ y -= yincr; } return pixd; } /*! * \brief pixVShear() * * \param[in] pixd [optional], this can be null, equal to pixs, * or different from pixs * \param[in] pixs any depth; cmap ok * \param[in] xloc location of vertical line, measured from origin * \param[in] radang angle in radians; not too close to +-(pi / 2) * \param[in] incolor L_BRING_IN_WHITE, L_BRING_IN_BLACK; * \return pixd, or NULL on error * *
 * Notes:
 *      (1) There are 3 cases:
 *            (a) pixd == null (make a new pixd)
 *            (b) pixd == pixs (in-place)
 *            (c) pixd != pixs
 *      (2) For these three cases, use these patterns, respectively:
 *              pixd = pixVShear(NULL, pixs, ...);
 *              pixVShear(pixs, pixs, ...);
 *              pixVShear(pixd, pixs, ...);
 *      (3) This shear leaves the vertical line of pixels at x = xloc
 *          invariant.  For a positive shear angle, pixels to the right
 *          of this line are shoved downward, and pixels to the left
 *          of the line move upward.
 *      (4) With positive shear angle, this can be used, along with
 *          pixHShear(), to perform a cw rotation, either with 2 shears
 *          (for small angles) or in the general case with 3 shears.
 *      (5) Changing the value of xloc is equivalent to translating
 *          the result vertically.
 *      (6) This brings in %incolor pixels from outside the image.
 *      (7) In-place shears do not work on cmapped pix, because the
 *          in-place operation cannot initialize to the requested %incolor,
 *          so we shear from a copy.
 *      (8) The angle is brought into the range [-pi, -pi].  It is
 *          not permitted to be within MinDiffFromHalfPi radians
 *          from either -pi/2 or pi/2.
 * 
*/ PIX * pixVShear(PIX *pixd, PIX *pixs, l_int32 xloc, l_float32 radang, l_int32 incolor) { l_int32 sign, w, h; l_int32 x, xincr, initxincr, vshift; l_float32 tanangle, invangle; PROCNAME("pixVShear"); if (!pixs) return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); if (incolor != L_BRING_IN_WHITE && incolor != L_BRING_IN_BLACK) return (PIX *)ERROR_PTR("invalid incolor value", procName, NULL); if (pixd == pixs) { /* in place */ if (!pixGetColormap(pixs)) { pixVShearIP(pixd, xloc, radang, incolor); } else { /* can't do in-place with a colormap */ PIX *pix1 = pixCopy(NULL, pixs); pixVShear(pixd, pix1, xloc, radang, incolor); pixDestroy(&pix1); } return pixd; } /* Make sure pixd exists and is same size as pixs */ if (!pixd) { if ((pixd = pixCreateTemplate(pixs)) == NULL) return (PIX *)ERROR_PTR("pixd not made", procName, NULL); } else { /* pixd != pixs */ pixResizeImageData(pixd, pixs); } /* Normalize angle. If no rotation, return a copy */ radang = normalizeAngleForShear(radang, MinDiffFromHalfPi); if (radang == 0.0 || tan(radang) == 0.0) return pixCopy(pixd, pixs); /* Initialize to value of incoming pixels */ pixSetBlackOrWhite(pixd, incolor); pixGetDimensions(pixs, &w, &h, NULL); sign = L_SIGN(radang); tanangle = tan(radang); invangle = L_ABS(1. / tanangle); initxincr = (l_int32)(invangle / 2.); xincr = (l_int32)invangle; pixRasterop(pixd, xloc - initxincr, 0, 2 * initxincr, h, PIX_SRC, pixs, xloc - initxincr, 0); for (vshift = 1, x = xloc + initxincr; x < w; vshift++) { xincr = (l_int32)(invangle * (vshift + 0.5) + 0.5) - (x - xloc); if (w - x < xincr) /* reduce for last one if req'd */ xincr = w - x; pixRasterop(pixd, x, sign*vshift, xincr, h, PIX_SRC, pixs, x, 0); #if DEBUG lept_stderr("x = %d, vshift = %d, xincr = %d\n", x, vshift, xincr); #endif /* DEBUG */ x += xincr; } for (vshift = -1, x = xloc - initxincr; x > 0; vshift--) { xincr = (x - xloc) - (l_int32)(invangle * (vshift - 0.5) + 0.5); if (x < xincr) /* reduce for last one if req'd */ xincr = x; pixRasterop(pixd, x - xincr, sign*vshift, xincr, h, PIX_SRC, pixs, x - xincr, 0); #if DEBUG lept_stderr("x = %d, vshift = %d, xincr = %d\n", x - xincr, vshift, xincr); #endif /* DEBUG */ x -= xincr; } return pixd; } /*-------------------------------------------------------------* * Shears about UL corner and center * *-------------------------------------------------------------*/ /*! * \brief pixHShearCorner() * * \param[in] pixd [optional], if not null, must be equal to pixs * \param[in] pixs any depth * \param[in] radang angle in radians * \param[in] incolor L_BRING_IN_WHITE, L_BRING_IN_BLACK; * \return pixd, or NULL on error. * *
 * Notes:
 *      (1) See pixHShear() for usage.
 *      (2) This does a horizontal shear about the UL corner, with (+) shear
 *          pushing increasingly leftward (-x) with increasing y.
 * 
*/ PIX * pixHShearCorner(PIX *pixd, PIX *pixs, l_float32 radang, l_int32 incolor) { PROCNAME("pixHShearCorner"); if (!pixs) return (PIX *)ERROR_PTR("pixs not defined", procName, pixd); return pixHShear(pixd, pixs, 0, radang, incolor); } /*! * \brief pixVShearCorner() * * \param[in] pixd [optional], if not null, must be equal to pixs * \param[in] pixs any depth * \param[in] radang angle in radians * \param[in] incolor L_BRING_IN_WHITE, L_BRING_IN_BLACK; * \return pixd, or NULL on error. * *
 * Notes:
 *      (1) See pixVShear() for usage.
 *      (2) This does a vertical shear about the UL corner, with (+) shear
 *          pushing increasingly downward (+y) with increasing x.
 * 
*/ PIX * pixVShearCorner(PIX *pixd, PIX *pixs, l_float32 radang, l_int32 incolor) { PROCNAME("pixVShearCorner"); if (!pixs) return (PIX *)ERROR_PTR("pixs not defined", procName, pixd); return pixVShear(pixd, pixs, 0, radang, incolor); } /*! * \brief pixHShearCenter() * * \param[in] pixd [optional] if not null, must be equal to pixs * \param[in] pixs any depth * \param[in] radang angle in radians * \param[in] incolor L_BRING_IN_WHITE, L_BRING_IN_BLACK; * \return pixd, or NULL on error. * *
 * Notes:
 *      (1) See pixHShear() for usage.
 *      (2) This does a horizontal shear about the center, with (+) shear
 *          pushing increasingly leftward (-x) with increasing y.
 * 
*/ PIX * pixHShearCenter(PIX *pixd, PIX *pixs, l_float32 radang, l_int32 incolor) { PROCNAME("pixHShearCenter"); if (!pixs) return (PIX *)ERROR_PTR("pixs not defined", procName, pixd); return pixHShear(pixd, pixs, pixGetHeight(pixs) / 2, radang, incolor); } /*! * \brief pixVShearCenter() * * \param[in] pixd [optional] if not null, must be equal to pixs * \param[in] pixs any depth * \param[in] radang angle in radians * \param[in] incolor L_BRING_IN_WHITE, L_BRING_IN_BLACK; * \return pixd, or NULL on error. * *
 * Notes:
 *      (1) See pixVShear() for usage.
 *      (2) This does a vertical shear about the center, with (+) shear
 *          pushing increasingly downward (+y) with increasing x.
 * 
*/ PIX * pixVShearCenter(PIX *pixd, PIX *pixs, l_float32 radang, l_int32 incolor) { PROCNAME("pixVShearCenter"); if (!pixs) return (PIX *)ERROR_PTR("pixs not defined", procName, pixd); return pixVShear(pixd, pixs, pixGetWidth(pixs) / 2, radang, incolor); } /*--------------------------------------------------------------------------* * In place about arbitrary lines * *--------------------------------------------------------------------------*/ /*! * \brief pixHShearIP() * * \param[in] pixs any depth; no cmap * \param[in] yloc location of horizontal line, measured from origin * \param[in] radang angle in radians * \param[in] incolor L_BRING_IN_WHITE, L_BRING_IN_BLACK; * \return 0 if OK; 1 on error * *
 * Notes:
 *      (1) This is an in-place version of pixHShear(); see comments there.
 *      (2) This brings in 'incolor' pixels from outside the image.
 *      (3) pixs cannot be colormapped, because the in-place operation
 *          only blits in 0 or 1 bits, not an arbitrary colormap index.
 *      (4) Does a horizontal full-band shear about the line with (+) shear
 *          pushing increasingly leftward (-x) with increasing y.
 * 
*/ l_ok pixHShearIP(PIX *pixs, l_int32 yloc, l_float32 radang, l_int32 incolor) { l_int32 sign, w, h; l_int32 y, yincr, inityincr, hshift; l_float32 tanangle, invangle; PROCNAME("pixHShearIP"); 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 incolor value", procName, 1); if (pixGetColormap(pixs)) return ERROR_INT("pixs is colormapped", procName, 1); /* Normalize angle */ radang = normalizeAngleForShear(radang, MinDiffFromHalfPi); if (radang == 0.0 || tan(radang) == 0.0) return 0; sign = L_SIGN(radang); pixGetDimensions(pixs, &w, &h, NULL); tanangle = tan(radang); invangle = L_ABS(1. / tanangle); inityincr = (l_int32)(invangle / 2.); yincr = (l_int32)invangle; if (inityincr > 0) pixRasteropHip(pixs, yloc - inityincr, 2 * inityincr, 0, incolor); for (hshift = 1, y = yloc + inityincr; y < h; hshift++) { yincr = (l_int32)(invangle * (hshift + 0.5) + 0.5) - (y - yloc); if (yincr == 0) continue; if (h - y < yincr) /* reduce for last one if req'd */ yincr = h - y; pixRasteropHip(pixs, y, yincr, -sign*hshift, incolor); y += yincr; } for (hshift = -1, y = yloc - inityincr; y > 0; hshift--) { yincr = (y - yloc) - (l_int32)(invangle * (hshift - 0.5) + 0.5); if (yincr == 0) continue; if (y < yincr) /* reduce for last one if req'd */ yincr = y; pixRasteropHip(pixs, y - yincr, yincr, -sign*hshift, incolor); y -= yincr; } return 0; } /*! * \brief pixVShearIP() * * \param[in] pixs any depth; no cmap * \param[in] xloc location of vertical line, measured from origin * \param[in] radang angle in radians * \param[in] incolor L_BRING_IN_WHITE, L_BRING_IN_BLACK; * \return 0 if OK; 1 on error * *
 * Notes:
 *      (1) This is an in-place version of pixVShear(); see comments there.
 *      (2) This brings in 'incolor' pixels from outside the image.
 *      (3) pixs cannot be colormapped, because the in-place operation
 *          only blits in 0 or 1 bits, not an arbitrary colormap index.
 *      (4) Does a vertical full-band shear about the line with (+) shear
 *          pushing increasingly downward (+y) with increasing x.
 * 
*/ l_ok pixVShearIP(PIX *pixs, l_int32 xloc, l_float32 radang, l_int32 incolor) { l_int32 sign, w, h; l_int32 x, xincr, initxincr, vshift; l_float32 tanangle, invangle; PROCNAME("pixVShearIP"); 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 incolor value", procName, 1); if (pixGetColormap(pixs)) return ERROR_INT("pixs is colormapped", procName, 1); /* Normalize angle */ radang = normalizeAngleForShear(radang, MinDiffFromHalfPi); if (radang == 0.0 || tan(radang) == 0.0) return 0; sign = L_SIGN(radang); pixGetDimensions(pixs, &w, &h, NULL); tanangle = tan(radang); invangle = L_ABS(1. / tanangle); initxincr = (l_int32)(invangle / 2.); xincr = (l_int32)invangle; if (initxincr > 0) pixRasteropVip(pixs, xloc - initxincr, 2 * initxincr, 0, incolor); for (vshift = 1, x = xloc + initxincr; x < w; vshift++) { xincr = (l_int32)(invangle * (vshift + 0.5) + 0.5) - (x - xloc); if (xincr == 0) continue; if (w - x < xincr) /* reduce for last one if req'd */ xincr = w - x; pixRasteropVip(pixs, x, xincr, sign*vshift, incolor); x += xincr; } for (vshift = -1, x = xloc - initxincr; x > 0; vshift--) { xincr = (x - xloc) - (l_int32)(invangle * (vshift - 0.5) + 0.5); if (xincr == 0) continue; if (x < xincr) /* reduce for last one if req'd */ xincr = x; pixRasteropVip(pixs, x - xincr, xincr, sign*vshift, incolor); x -= xincr; } return 0; } /*-------------------------------------------------------------------------* * Linear interpolated shear about arbitrary lines * *-------------------------------------------------------------------------*/ /*! * \brief pixHShearLI() * * \param[in] pixs 8 bpp or 32 bpp, or colormapped * \param[in] yloc location of horizontal line, measured from origin * \param[in] radang angle in radians, in range (-pi/2 ... pi/2) * \param[in] incolor L_BRING_IN_WHITE, L_BRING_IN_BLACK; * \return pixd sheared, or NULL on error * *
 * Notes:
 *      (1) This does horizontal shear with linear interpolation for
 *          accurate results on 8 bpp gray, 32 bpp rgb, or cmapped images.
 *          It is relatively slow compared to the sampled version
 *          implemented by rasterop, but the result is much smoother.
 *      (2) This shear leaves the horizontal line of pixels at y = yloc
 *          invariant.  For a positive shear angle, pixels above this
 *          line are shoved to the right, and pixels below this line
 *          move to the left.
 *      (3) Any colormap is removed.
 *      (4) The angle is brought into the range [-pi/2 + del, pi/2 - del],
 *          where del == MinDiffFromHalfPi.
 * 
*/ PIX * pixHShearLI(PIX *pixs, l_int32 yloc, l_float32 radang, l_int32 incolor) { l_int32 i, jd, x, xp, xf, w, h, d, wm, wpls, wpld, val, rval, gval, bval; l_uint32 word0, word1; l_uint32 *datas, *datad, *lines, *lined; l_float32 tanangle, xshift; PIX *pix, *pixd; PROCNAME("pixHShearLI"); if (!pixs) return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); pixGetDimensions(pixs, &w, &h, &d); if (d != 8 && d != 32 && !pixGetColormap(pixs)) return (PIX *)ERROR_PTR("pixs not 8, 32 bpp, or cmap", procName, NULL); if (incolor != L_BRING_IN_WHITE && incolor != L_BRING_IN_BLACK) return (PIX *)ERROR_PTR("invalid incolor value", procName, NULL); if (yloc < 0 || yloc >= h) return (PIX *)ERROR_PTR("yloc not in [0 ... h-1]", procName, NULL); if (pixGetColormap(pixs)) pix = pixRemoveColormap(pixs, REMOVE_CMAP_BASED_ON_SRC); else pix = pixClone(pixs); /* Normalize angle. If no rotation, return a copy */ radang = normalizeAngleForShear(radang, MinDiffFromHalfPi); if (radang == 0.0 || tan(radang) == 0.0) { pixDestroy(&pix); return pixCopy(NULL, pixs); } /* Initialize to value of incoming pixels */ pixd = pixCreateTemplate(pix); pixSetBlackOrWhite(pixd, incolor); /* Standard linear interp: subdivide each pixel into 64 parts */ d = pixGetDepth(pixd); /* 8 or 32 */ datas = pixGetData(pix); datad = pixGetData(pixd); wpls = pixGetWpl(pix); wpld = pixGetWpl(pixd); tanangle = tan(radang); for (i = 0; i < h; i++) { lines = datas + i * wpls; lined = datad + i * wpld; xshift = (yloc - i) * tanangle; for (jd = 0; jd < w; jd++) { x = (l_int32)(64.0 * (-xshift + jd) + 0.5); xp = x / 64; xf = x & 63; wm = w - 1; if (xp < 0 || xp > wm) continue; if (d == 8) { if (xp < wm) { val = ((63 - xf) * GET_DATA_BYTE(lines, xp) + xf * GET_DATA_BYTE(lines, xp + 1) + 31) / 63; } else { /* xp == wm */ val = GET_DATA_BYTE(lines, xp); } SET_DATA_BYTE(lined, jd, val); } else { /* d == 32 */ if (xp < wm) { word0 = *(lines + xp); word1 = *(lines + xp + 1); rval = ((63 - xf) * ((word0 >> L_RED_SHIFT) & 0xff) + xf * ((word1 >> L_RED_SHIFT) & 0xff) + 31) / 63; gval = ((63 - xf) * ((word0 >> L_GREEN_SHIFT) & 0xff) + xf * ((word1 >> L_GREEN_SHIFT) & 0xff) + 31) / 63; bval = ((63 - xf) * ((word0 >> L_BLUE_SHIFT) & 0xff) + xf * ((word1 >> L_BLUE_SHIFT) & 0xff) + 31) / 63; composeRGBPixel(rval, gval, bval, lined + jd); } else { /* xp == wm */ lined[jd] = lines[xp]; } } } } pixDestroy(&pix); return pixd; } /*! * \brief pixVShearLI() * * \param[in] pixs 8 bpp or 32 bpp, or colormapped * \param[in] xloc location of vertical line, measured from origin * \param[in] radang angle in radians, in range (-pi/2 ... pi/2) * \param[in] incolor L_BRING_IN_WHITE, L_BRING_IN_BLACK; * \return pixd sheared, or NULL on error * *
 * Notes:
 *      (1) This does vertical shear with linear interpolation for
 *          accurate results on 8 bpp gray, 32 bpp rgb, or cmapped images.
 *          It is relatively slow compared to the sampled version
 *          implemented by rasterop, but the result is much smoother.
 *      (2) This shear leaves the vertical line of pixels at x = xloc
 *          invariant.  For a positive shear angle, pixels to the right
 *          of this line are shoved downward, and pixels to the left
 *          of the line move upward.
 *      (3) Any colormap is removed.
 *      (4) The angle is brought into the range [-pi/2 + del, pi/2 - del],
 *          where del == MinDiffFromHalfPi.
 * 
*/ PIX * pixVShearLI(PIX *pixs, l_int32 xloc, l_float32 radang, l_int32 incolor) { l_int32 id, y, yp, yf, j, w, h, d, hm, wpls, wpld, val, rval, gval, bval; l_uint32 word0, word1; l_uint32 *datas, *datad, *lines, *lined; l_float32 tanangle, yshift; PIX *pix, *pixd; PROCNAME("pixVShearLI"); if (!pixs) return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); pixGetDimensions(pixs, &w, &h, &d); if (d != 8 && d != 32 && !pixGetColormap(pixs)) return (PIX *)ERROR_PTR("pixs not 8, 32 bpp, or cmap", procName, NULL); if (incolor != L_BRING_IN_WHITE && incolor != L_BRING_IN_BLACK) return (PIX *)ERROR_PTR("invalid incolor value", procName, NULL); if (xloc < 0 || xloc >= w) return (PIX *)ERROR_PTR("xloc not in [0 ... w-1]", procName, NULL); if (pixGetColormap(pixs)) pix = pixRemoveColormap(pixs, REMOVE_CMAP_BASED_ON_SRC); else pix = pixClone(pixs); /* Normalize angle. If no rotation, return a copy */ radang = normalizeAngleForShear(radang, MinDiffFromHalfPi); if (radang == 0.0 || tan(radang) == 0.0) { pixDestroy(&pix); return pixCopy(NULL, pixs); } /* Initialize to value of incoming pixels */ pixd = pixCreateTemplate(pix); pixSetBlackOrWhite(pixd, incolor); /* Standard linear interp: subdivide each pixel into 64 parts */ d = pixGetDepth(pixd); /* 8 or 32 */ datas = pixGetData(pix); datad = pixGetData(pixd); wpls = pixGetWpl(pix); wpld = pixGetWpl(pixd); tanangle = tan(radang); for (j = 0; j < w; j++) { yshift = (j - xloc) * tanangle; for (id = 0; id < h; id++) { y = (l_int32)(64.0 * (-yshift + id) + 0.5); yp = y / 64; yf = y & 63; hm = h - 1; if (yp < 0 || yp > hm) continue; lines = datas + yp * wpls; lined = datad + id * wpld; if (d == 8) { if (yp < hm) { val = ((63 - yf) * GET_DATA_BYTE(lines, j) + yf * GET_DATA_BYTE(lines + wpls, j) + 31) / 63; } else { /* yp == hm */ val = GET_DATA_BYTE(lines, j); } SET_DATA_BYTE(lined, j, val); } else { /* d == 32 */ if (yp < hm) { word0 = *(lines + j); word1 = *(lines + wpls + j); rval = ((63 - yf) * ((word0 >> L_RED_SHIFT) & 0xff) + yf * ((word1 >> L_RED_SHIFT) & 0xff) + 31) / 63; gval = ((63 - yf) * ((word0 >> L_GREEN_SHIFT) & 0xff) + yf * ((word1 >> L_GREEN_SHIFT) & 0xff) + 31) / 63; bval = ((63 - yf) * ((word0 >> L_BLUE_SHIFT) & 0xff) + yf * ((word1 >> L_BLUE_SHIFT) & 0xff) + 31) / 63; composeRGBPixel(rval, gval, bval, lined + j); } else { /* yp == hm */ lined[j] = lines[j]; } } } } pixDestroy(&pix); return pixd; } /*-------------------------------------------------------------------------* * Angle normalization * *-------------------------------------------------------------------------*/ static l_float32 normalizeAngleForShear(l_float32 radang, l_float32 mindif) { l_float32 pi2; PROCNAME("normalizeAngleForShear"); /* Bring angle into range [-pi/2, pi/2] */ pi2 = 3.14159265f / 2.0f; if (radang < -pi2 || radang > pi2) radang = radang - (l_int32)(radang / pi2) * pi2; /* If angle is too close to pi/2 or -pi/2, move it */ if (radang > pi2 - mindif) { L_WARNING("angle close to pi/2; shifting away\n", procName); radang = pi2 - mindif; } else if (radang < -pi2 + mindif) { L_WARNING("angle close to -pi/2; shifting away\n", procName); radang = -pi2 + mindif; } return radang; }