C++程序  |  587行  |  19.94 KB

/*====================================================================*
 -  Copyright (C) 2001 Leptonica.  All rights reserved.
 -  This software is distributed in the hope that it will be
 -  useful, but with NO WARRANTY OF ANY KIND.
 -  No author or distributor accepts responsibility to anyone for the
 -  consequences of using this software, or for whether it serves any
 -  particular purpose or works at all, unless he or she says so in
 -  writing.  Everyone is granted permission to copy, modify and
 -  redistribute this source code, for commercial or non-commercial
 -  purposes, with the following restrictions: (1) the origin of this
 -  source code must not be misrepresented; (2) modified versions must
 -  be plainly marked as such; and (3) this notice may not be removed
 -  or altered from any source or modified source distribution.
 *====================================================================*/


/*
 *  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()
 *
 *    Static helper
 *      static l_float32  normalizeAngleForShear()
 */

#include <stdio.h>
#include <stdlib.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   MIN_DIFF_FROM_HALF_PI = 0.04;

static l_float32 normalizeAngleForShear(l_float32 radang, l_float32 mindist);


#ifndef  NO_CONSOLE_IO
#define  DEBUG     0
#endif  /* ~NO_CONSOLE_IO */


/*-------------------------------------------------------------*
 *                    About arbitrary lines                    *
 *-------------------------------------------------------------*/
/*!
 *  pixHShear()
 *
 *      Input:  pixd (<optional>, this can be null, equal to pixs,
 *                    or different from pixs)
 *              pixs (no restrictions on depth)
 *              liney  (location of horizontal line, measured from origin)
 *              angle (in radians)
 *              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 = liney
 *          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 liney is equivalent to translating
 *          the result horizontally.
 *      (6) This brings in 'incolor' pixels from outside the image.
 *      (7) For in-place operation, pixs cannot be colormapped,
 *          because the in-place operation only blits in 0 or 1 bits,
 *          not an arbitrary colormap index.
 *      (8) The angle is brought into the range [-pi, -pi].  It is
 *          not permitted to be within MIN_DIFF_FROM_HALF_PI radians
 *          from either -pi/2 or pi/2.
 */
PIX *
pixHShear(PIX       *pixd,
          PIX       *pixs,
          l_int32    liney,
          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) != NULL)
            return (PIX *)ERROR_PTR("pixs is colormapped", procName, pixd);
        pixHShearIP(pixd, liney, radang, incolor);
        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, MIN_DIFF_FROM_HALF_PI);
    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, liney - inityincr, w, 2 * inityincr, PIX_SRC,
                pixs, 0, liney - inityincr);

    for (hshift = 1, y = liney + inityincr; y < h; hshift++) {
        yincr = (l_int32)(invangle * (hshift + 0.5) + 0.5) - (y - liney);
        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
        fprintf(stderr, "y = %d, hshift = %d, yincr = %d\n", y, hshift, yincr);
#endif /* DEBUG */
        y += yincr;
    }

    for (hshift = -1, y = liney - inityincr; y > 0; hshift--) {
        yincr = (y - liney) - (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
        fprintf(stderr, "y = %d, hshift = %d, yincr = %d\n",
                y - yincr, hshift, yincr);
#endif /* DEBUG */
        y -= yincr;
    }

    return pixd;
}
                        

/*!
 *  pixVShear()
 *
 *      Input:  pixd (<optional>, this can be null, equal to pixs,
 *                    or different from pixs)
 *              pixs (no restrictions on depth)
 *              linex  (location of vertical line, measured from origin)
 *              angle (in radians; not too close to +-(pi / 2))
 *              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 = linex
 *          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 linex is equivalent to translating
 *          the result vertically.
 *      (6) This brings in 'incolor' pixels from outside the image.
 *      (7) For in-place operation, pixs cannot be colormapped,
 *          because the in-place operation only blits in 0 or 1 bits,
 *          not an arbitrary colormap index.
 *      (8) The angle is brought into the range [-pi, -pi].  It is
 *          not permitted to be within MIN_DIFF_FROM_HALF_PI radians
 *          from either -pi/2 or pi/2.
 */
PIX *
pixVShear(PIX       *pixd,
          PIX       *pixs,
          l_int32    linex,
          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) != NULL)
            return (PIX *)ERROR_PTR("pixs is colormapped", procName, pixd);
        pixVShearIP(pixd, linex, radang, incolor);
        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, MIN_DIFF_FROM_HALF_PI);
    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, linex - initxincr, 0, 2 * initxincr, h, PIX_SRC,
                pixs, linex - initxincr, 0);

    for (vshift = 1, x = linex + initxincr; x < w; vshift++) {
        xincr = (l_int32)(invangle * (vshift + 0.5) + 0.5) - (x - linex);
        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
        fprintf(stderr, "x = %d, vshift = %d, xincr = %d\n", x, vshift, xincr);
#endif /* DEBUG */
        x += xincr;
    }

    for (vshift = -1, x = linex - initxincr; x > 0; vshift--) {
        xincr = (x - linex) - (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
        fprintf(stderr, "x = %d, vshift = %d, xincr = %d\n",
                x - xincr, vshift, xincr);
#endif /* DEBUG */
        x -= xincr;
    }

    return pixd;
}
                        


/*-------------------------------------------------------------*
 *             Shears about UL corner and center               *
 *-------------------------------------------------------------*/
/*!
 *  pixHShearCorner()
 *
 *      Input:  pixd (<optional>, if not null, must be equal to pixs)
 *              pixs
 *              angle (in radians)
 *              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);
}


/*!
 *  pixVShearCorner()
 *
 *      Input:  pixd (<optional>, if not null, must be equal to pixs)
 *              pixs
 *              angle (in radians)
 *              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);
}
                        

/*!
 *  pixHShearCenter()
 *
 *      Input:  pixd (<optional>, if not null, must be equal to pixs)
 *              pixs
 *              angle (in radians)
 *              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);
}


/*!
 *  pixVShearCenter()
 *
 *      Input:  pixd (<optional>, if not null, must be equal to pixs)
 *              pixs
 *              angle (in radians)
 *              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                     *
 *--------------------------------------------------------------------------*/
/*!
 *  pixHShearIP()
 *
 *      Input:  pixs
 *              liney  (location of horizontal line, measured from origin)
 *              angle (in radians)
 *              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_int32
pixHShearIP(PIX       *pixs,
            l_int32    liney,
            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) != NULL)
        return ERROR_INT("pixs is colormapped", procName, 1);

        /* Normalize angle */
    radang = normalizeAngleForShear(radang, MIN_DIFF_FROM_HALF_PI);
    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;

    pixRasteropHip(pixs, liney - inityincr, 2 * inityincr, 0, incolor);

    for (hshift = 1, y = liney + inityincr; y < h; hshift++) {
        yincr = (l_int32)(invangle * (hshift + 0.5) + 0.5) - (y - liney);
        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 = liney - inityincr; y > 0; hshift--) {
        yincr = (y - liney) - (l_int32)(invangle * (hshift - 0.5) + 0.5);
        if (y < yincr)  /* reduce for last one if req'd */
            yincr = y;
        pixRasteropHip(pixs, y - yincr, yincr, -sign*hshift, incolor);
        y -= yincr;
    }

    return 0;
}
                        

/*!
 *  pixVShearIP()
 *
 *      Input:  pixs (all depths; not colormapped)
 *              linex  (location of vertical line, measured from origin)
 *              angle (in radians)
 *              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_int32
pixVShearIP(PIX       *pixs,
            l_int32    linex,
            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) != NULL)
        return ERROR_INT("pixs is colormapped", procName, 1);

        /* Normalize angle */
    radang = normalizeAngleForShear(radang, MIN_DIFF_FROM_HALF_PI);
    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;

    pixRasteropVip(pixs, linex - initxincr, 2 * initxincr, 0, incolor);

    for (vshift = 1, x = linex + initxincr; x < w; vshift++) {
        xincr = (l_int32)(invangle * (vshift + 0.5) + 0.5) - (x - linex);
        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 = linex - initxincr; x > 0; vshift--) {
        xincr = (x - linex) - (l_int32)(invangle * (vshift - 0.5) + 0.5);
        if (x < xincr)  /* reduce for last one if req'd */
            xincr = x;
        pixRasteropVip(pixs, x - xincr, xincr, sign*vshift, incolor);
        x -= xincr;
    }

    return 0;
}


/*-------------------------------------------------------------------------*
 *                           Angle normalization                           *
 *-------------------------------------------------------------------------*/
static l_float32
normalizeAngleForShear(l_float32  radang,
                       l_float32  mindist)
{
l_float32 pi, diff90;

    PROCNAME("normalizeAngleForShear");

       /* Bring angle into range from [-pi, pi] */
    pi = 3.14159265;
    if (radang < -pi || radang > pi)
        radang = radang - (l_int32)(radang / pi) * pi;

       /* If angle is too close to pi/2 or -pi/2, move away and issue warning */
    diff90 = radang - pi / 2.0;
    if (L_ABS(diff90) < mindist)
        L_WARNING("angle close to pi/2; shifting away", procName);
    if (diff90 > -mindist && diff90 < 0.0)
        radang = pi / 2.0 - mindist;
    else if (diff90 >= 0.0 && diff90 < mindist)
        radang = pi / 2.0 + mindist;
    diff90 = radang + pi / 2.0;
    if (L_ABS(diff90) < mindist)
        L_WARNING("angle close to -pi/2; shifting away", procName);
    if (diff90 > -mindist && diff90 < 0.0)
        radang = -pi / 2.0 - mindist;
    else if (diff90 >= 0.0 && diff90 < mindist)
        radang = -pi / 2.0 + mindist;

    return radang;
}