Mercurial > hgrepos > Python2 > PyMuPDF
diff mupdf-source/thirdparty/leptonica/src/shear.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 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mupdf-source/thirdparty/leptonica/src/shear.c Mon Sep 15 11:43:07 2025 +0200 @@ -0,0 +1,832 @@ +/*====================================================================* + - 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 + * <pre> + * + * 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() + * </pre> + */ + +#ifdef HAVE_CONFIG_H +#include <config_auto.h> +#endif /* HAVE_CONFIG_H */ + +#include <string.h> +#include <math.h> +#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 + * + * <pre> + * 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. + * </pre> + */ +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; + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", __func__, pixd); + if (incolor != L_BRING_IN_WHITE && incolor != L_BRING_IN_BLACK) + return (PIX *)ERROR_PTR("invalid incolor value", __func__, 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", __func__, 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 + * + * <pre> + * 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. + * </pre> + */ +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; + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); + if (incolor != L_BRING_IN_WHITE && incolor != L_BRING_IN_BLACK) + return (PIX *)ERROR_PTR("invalid incolor value", __func__, 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", __func__, 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. + * + * <pre> + * 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. + * </pre> + */ +PIX * +pixHShearCorner(PIX *pixd, + PIX *pixs, + l_float32 radang, + l_int32 incolor) +{ + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", __func__, 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. + * + * <pre> + * 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. + * </pre> + */ +PIX * +pixVShearCorner(PIX *pixd, + PIX *pixs, + l_float32 radang, + l_int32 incolor) +{ + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", __func__, 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. + * + * <pre> + * Notes: + * (1) See pixHShear() for usage. + * (2) This does a horizontal shear about the center, with (+) shear + * pushing increasingly leftward (-x) with increasing y. + * </pre> + */ +PIX * +pixHShearCenter(PIX *pixd, + PIX *pixs, + l_float32 radang, + l_int32 incolor) +{ + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", __func__, 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. + * + * <pre> + * Notes: + * (1) See pixVShear() for usage. + * (2) This does a vertical shear about the center, with (+) shear + * pushing increasingly downward (+y) with increasing x. + * </pre> + */ +PIX * +pixVShearCenter(PIX *pixd, + PIX *pixs, + l_float32 radang, + l_int32 incolor) +{ + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", __func__, 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 + * + * <pre> + * 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. + * </pre> + */ +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; + + if (!pixs) + return ERROR_INT("pixs not defined", __func__, 1); + if (incolor != L_BRING_IN_WHITE && incolor != L_BRING_IN_BLACK) + return ERROR_INT("invalid incolor value", __func__, 1); + if (pixGetColormap(pixs)) + return ERROR_INT("pixs is colormapped", __func__, 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 + * + * <pre> + * 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. + * </pre> + */ +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; + + if (!pixs) + return ERROR_INT("pixs not defined", __func__, 1); + if (incolor != L_BRING_IN_WHITE && incolor != L_BRING_IN_BLACK) + return ERROR_INT("invalid incolor value", __func__, 1); + if (pixGetColormap(pixs)) + return ERROR_INT("pixs is colormapped", __func__, 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 + * + * <pre> + * 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. + * </pre> + */ +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; + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); + pixGetDimensions(pixs, &w, &h, &d); + if (d != 8 && d != 32 && !pixGetColormap(pixs)) + return (PIX *)ERROR_PTR("pixs not 8, 32 bpp, or cmap", __func__, NULL); + if (incolor != L_BRING_IN_WHITE && incolor != L_BRING_IN_BLACK) + return (PIX *)ERROR_PTR("invalid incolor value", __func__, NULL); + if (yloc < 0 || yloc >= h) + return (PIX *)ERROR_PTR("yloc not in [0 ... h-1]", __func__, 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 + * + * <pre> + * 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. + * </pre> + */ +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; + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); + pixGetDimensions(pixs, &w, &h, &d); + if (d != 8 && d != 32 && !pixGetColormap(pixs)) + return (PIX *)ERROR_PTR("pixs not 8, 32 bpp, or cmap", __func__, NULL); + if (incolor != L_BRING_IN_WHITE && incolor != L_BRING_IN_BLACK) + return (PIX *)ERROR_PTR("invalid incolor value", __func__, NULL); + if (xloc < 0 || xloc >= w) + return (PIX *)ERROR_PTR("xloc not in [0 ... w-1]", __func__, 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; + + /* 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", __func__); + radang = pi2 - mindif; + } else if (radang < -pi2 + mindif) { + L_WARNING("angle close to -pi/2; shifting away\n", __func__); + radang = -pi2 + mindif; + } + + return radang; +}
