/*====================================================================*
 -  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 flipdetectdwa.c.notused
 * <pre>
 *
 *                                  NOTE
 *      ==============================================================
 *      This code has been retired from the library.  It is faster than
 *      the rasterop implementation, but not by a large amount.  On a
 *      standard 8 Mpixel page image, the timings are:
 *         Rasterop: 0.37 sec
 *         DWA:      0.21 sec.
 *      It is preserved here to show how the dwa code can be generated
 *      and then used in an application.
 *      ==============================================================
 *
 *      DWA page orientation detection (pure rotation by 90 degree increments):
 *          l_int32      pixOrientDetectDwa()
 *          l_int32      pixUpDownDetectDwa()
 *
 *      DWA mirror detection (flip 180 degrees about line in plane of image):
 *          l_int32      pixMirrorDetectDwa()
 *
 *      Low-level auto-generated DWA implementation of hit-miss transforms.
 *      This code (rearranged) was generated by prog/flipselgen.c.
 *          static PIX  *pixFlipFHMTGen()
 *          --> static l_int32   flipfhmtgen_low()  -- dispatcher
 *              --> static void      fhmt_1_0()
 *              --> static void      fhmt_1_1()
 *              --> static void      fhmt_1_2()
 *              --> static void      fhmt_1_3()
 *
 *      The four Sels used are defined in prog/flipselgen.c.
 *      They are the same as those in flipdetect.c.
 * </pre>
 */

#ifdef HAVE_CONFIG_H
#include <config_auto.h>
#endif  /* HAVE_CONFIG_H */

#include <math.h>
#include <string.h>
#include "allheaders.h"

    /* Parameters for determining orientation */
static const l_int32  DefaultMinUpDownCount = 70;
static const l_float32  DefaultMinUpDownConf = 8.0;

    /* Parameters for determining mirror flip */
static const l_int32  DefaultMinMirrorFlipCount = 100;
static const l_float32  DefaultMinMirrorFlipConf = 5.0;

static l_int32   NUM_SELS_GENERATED = 4;
static char  SEL_NAMES[][10] = {"flipsel1",
                                "flipsel2",
                                "flipsel3",
                                "flipsel4"};

static l_int32 flipfhmtgen_low(l_uint32 *, l_int32, l_int32, l_int32,
                               l_uint32 *, l_int32, l_int32);

static void  fhmt_1_0(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *,
                      l_int32);
static void  fhmt_1_1(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *,
                      l_int32);
static void  fhmt_1_2(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *,
                      l_int32);
static void  fhmt_1_3(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *,
                      l_int32);

/*----------------------------------------------------------------*
 *        High-level interface for orientation detection          *
 *        (four 90 degree angles)                                 * 
 *----------------------------------------------------------------*/
/*!
 * \brief   pixOrientDetectDwa()
 *
 * \param[in]    pixs        1 bpp, deskewed, English text
 * \param[out]   pupconf     [optional] ; may be NULL
 * \param[out]   pleftconf   [optional] ; may be NULL
 * \param[in]    mincount    min number of up + down; use 0 for default
 * \param[in]    debug       1 for debug output; 0 otherwise
 * \return  0 if OK, 1 on error
 *
 * <pre>
 * Notes:
 *      (1) Same interface as for pixOrientDetect().  See notes
 *          there for usage.
 *      (2) Uses auto-gen'd code for the Sels defined at the
 *          top of this file, with some renaming of functions.
 *          The auto-gen'd code is in fliphmtgen.c, and can
 *          be generated by a simple executable; see prog/flipselgen.c.
 *      (3) This runs about 1.5 times faster than the pixOrientDetect().
 * </pre>
 */
l_ok
pixOrientDetectDwa(PIX        *pixs,
                   l_float32  *pupconf,
                   l_float32  *pleftconf,
                   l_int32     mincount,
                   l_int32     debug)
{
PIX  *pix1;

    if (!pixs || pixGetDepth(pixs) != 1)
        return ERROR_INT("pixs not defined or not 1 bpp", __func__, 1);
    if (!pupconf && !pleftconf)
        return ERROR_INT("nothing to do", __func__, 1);
    if (mincount == 0)
        mincount = DefaultMinUpDownCount;

    if (pupconf)
        pixUpDownDetectDwa(pixs, pupconf, mincount, 0, debug);
    if (pleftconf) {
        pix1 = pixRotate90(pixs, 1);
        pixUpDownDetectDwa(pix1, pleftconf, mincount, 0, debug);
        pixDestroy(&pix1);
    }

    return 0;
}


/*!
 * \brief   pixUpDownDetectDwa()
 *
 * \param[in]    pixs       1 bpp, deskewed, English text, 150 - 300 ppi
 * \param[out]   pconf      confidence that text is rightside-up
 * \param[in]    mincount   min number of up + down; use 0 for default
 * \param[in]    npixels    number of pixels removed from each side of word box
 * \param[in]    debug      1 for debug output; 0 otherwise
 * \return  0 if OK, 1 on error
 *
 * <pre>
 * Notes:
 *      (1) Faster (DWA) version of pixUpDownDetect().
 *      (2) If npixels == 0, the pixels identified through the HMT
 *          (hit-miss transform) are not clipped by a truncated word
 *          mask pixm.  See pixUpDownDetect() for usage and other details.
 *      (3) The returned confidence is the normalized difference
 *          between the number of detected up and down ascenders,
 *          assuming that the text is either rightside-up or upside-down
 *          and not rotated at a 90 degree angle.
 * </pre>
 */
l_ok
pixUpDownDetectDwa(PIX        *pixs,
                   l_float32  *pconf,
                   l_int32     mincount,
                   l_int32     npixels,
                   l_int32     debug)
{
char       flipsel1[] = "flipsel1";
char       flipsel2[] = "flipsel2";
char       flipsel3[] = "flipsel3";
char       flipsel4[] = "flipsel4";
l_int32    countup, countdown, nmax;
l_float32  nup, ndown;
PIX       *pixt, *pix0, *pix1, *pix2, *pix3, *pixm;

    if (!pconf)
        return ERROR_INT("&conf not defined", __func__, 1);
    *pconf = 0.0;
    if (!pixs || pixGetDepth(pixs) != 1)
        return ERROR_INT("pixs not defined or not 1 bpp", __func__, 1);
    if (mincount == 0)
        mincount = DefaultMinUpDownCount;
    if (npixels < 0)
        npixels = 0;

        /* One of many reasonable pre-filtering sequences: (1, 8) and (30, 1).
         * This closes holes in x-height characters and joins them at
         * the x-height.  There is more noise in the descender detection
         * from this, but it works fairly well. */
    pixt = pixMorphSequenceDwa(pixs, "c1.8 + c30.1", 0);

        /* Be sure to add the border before the flip DWA operations! */
    pix0 = pixAddBorderGeneral(pixt, ADDED_BORDER, ADDED_BORDER,
                                ADDED_BORDER, ADDED_BORDER, 0);
    pixDestroy(&pixt);

        /* Optionally, make a mask of the word bounding boxes, shortening
         * each of them by a fixed amount at each end. */
    pixm = NULL;
    if (npixels > 0) {
        l_int32  i, nbox, x, y, w, h;
        BOX   *box;
        BOXA  *boxa;
        pix1 = pixMorphSequenceDwa(pix0, "o10.1", 0);
        boxa = pixConnComp(pix1, NULL, 8);
        pixm = pixCreateTemplate(pix1);
        pixDestroy(&pix1);
        nbox = boxaGetCount(boxa);
        for (i = 0; i < nbox; i++) {
            box = boxaGetBox(boxa, i, L_CLONE);
            boxGetGeometry(box, &x, &y, &w, &h);
            if (w > 2 * npixels)
                pixRasterop(pixm, x + npixels, y - 6, w - 2 * npixels, h + 13,
                            PIX_SET, NULL, 0, 0);
            boxDestroy(&box);
        }
        boxaDestroy(&boxa);
    }

        /* Find the ascenders and optionally filter with pixm.
         * For an explanation of the procedure used for counting the result
         * of the HMT, see comments in pixUpDownDetect().  */
    pix1 = pixFlipFHMTGen(NULL, pix0, flipsel1);
    pix2 = pixFlipFHMTGen(NULL, pix0, flipsel2);
    pixOr(pix1, pix1, pix2);
    if (pixm)
        pixAnd(pix1, pix1, pixm);
    pix3 = pixReduceRankBinaryCascade(pix1, 1, 1, 0, 0);
    pixCountPixels(pix3, &countup, NULL);
    pixDestroy(&pix1);
    pixDestroy(&pix2);
    pixDestroy(&pix3);

        /* Find the ascenders and optionally filter with pixm. */
    pix1 = pixFlipFHMTGen(NULL, pix0, flipsel3);
    pix2 = pixFlipFHMTGen(NULL, pix0, flipsel4);
    pixOr(pix1, pix1, pix2);
    if (pixm)
        pixAnd(pix1, pix1, pixm);
    pix3 = pixReduceRankBinaryCascade(pix1, 1, 1, 0, 0);
    pixCountPixels(pix3, &countdown, NULL);
    pixDestroy(&pix1);
    pixDestroy(&pix2);
    pixDestroy(&pix3);

        /* Evaluate statistically, generating a confidence that is
         * related to the probability with a gaussian distribution. */
    nup = (l_float32)(countup);
    ndown = (l_float32)(countdown);
    nmax = L_MAX(countup, countdown);
    if (nmax > mincount)
        *pconf = 2. * ((nup - ndown) / sqrt(nup + ndown));

    if (debug) {
        if (pixm) {
            lept_mkdir("lept/orient");
            pixWriteDebug("/tmp/lept/orient/pixm2.png", pixm, IFF_PNG);
        }
        lept_stderr("nup = %7.3f, ndown = %7.3f, conf = %7.3f\n",
                nup, ndown, *pconf);
        if (*pconf > DefaultMinUpDownConf)
            lept_stderr("Text is rightside-up\n");
        if (*pconf < -DefaultMinUpDownConf)
            lept_stderr("Text is upside-down\n");
    }

    pixDestroy(&pix0);
    pixDestroy(&pixm);
    return 0;
}


/*----------------------------------------------------------------*
 *                     Left-right mirror detection                *
 *                          DWA implementation                    *
 *----------------------------------------------------------------*/
/*!
 * \brief   pixMirrorDetectDwa()
 *
 * \param[in]    pixs       1 bpp, deskewed, English text
 * \param[out]   pconf      confidence that text is not LR mirror reversed
 * \param[in]    mincount   min number of left + right; use 0 for default
 * \param[in]    debug      1 for debug output; 0 otherwise
 * \return  0 if OK, 1 on error
 *
 * <pre>
 * Notes:
 *      (1) We assume the text is horizontally oriented, with
 *          ascenders going up.
 *      (2) See notes in pixMirrorDetect().
 * </pre>
 */
l_ok
pixMirrorDetectDwa(PIX        *pixs,
                   l_float32  *pconf,
                   l_int32     mincount,
                   l_int32     debug)
{
char       flipsel1[] = "flipsel1";
char       flipsel2[] = "flipsel2";
l_int32    count1, count2, nmax;
l_float32  nleft, nright;
PIX       *pix0, *pix1, *pix2, *pix3;

    if (!pconf)
        return ERROR_INT("&conf not defined", __func__, 1);
    *pconf = 0.0;
    if (!pixs || pixGetDepth(pixs) != 1)
        return ERROR_INT("pixs not defined or not 1 bpp", __func__, 1);
    if (mincount == 0)
        mincount = DefaultMinMirrorFlipCount;

        /* Fill x-height characters but not space between them, sort of. */
    pix3 = pixMorphSequenceDwa(pixs, "d1.30", 0);
    pixXor(pix3, pix3, pixs);
    pix0 = pixMorphSequenceDwa(pixs, "c15.1", 0);
    pixXor(pix0, pix0, pixs);
    pixAnd(pix0, pix0, pix3);
    pixOr(pix3, pix0, pixs);
    pixDestroy(&pix0);
    pix0 = pixAddBorderGeneral(pix3, ADDED_BORDER, ADDED_BORDER,
                                ADDED_BORDER, ADDED_BORDER, 0);
    pixDestroy(&pix3);

        /* Filter the right-facing characters. */
    pix1 = pixFlipFHMTGen(NULL, pix0, flipsel1);
    pix3 = pixReduceRankBinaryCascade(pix1, 1, 1, 0, 0);
    pixCountPixels(pix3, &count1, NULL);
    pixDestroy(&pix1);
    pixDestroy(&pix3);

        /* Filter the left-facing characters. */
    pix2 = pixFlipFHMTGen(NULL, pix0, flipsel2);
    pix3 = pixReduceRankBinaryCascade(pix2, 1, 1, 0, 0);
    pixCountPixels(pix3, &count2, NULL);
    pixDestroy(&pix2);
    pixDestroy(&pix3);

    pixDestroy(&pix0);
    nright = (l_float32)count1;
    nleft = (l_float32)count2;
    nmax = L_MAX(count1, count2);

    if (nmax > mincount)
        *pconf = 2. * ((nright - nleft) / sqrt(nright + nleft));

    if (debug) {
        lept_stderr("nright = %f, nleft = %f\n", nright, nleft);
        if (*pconf > DefaultMinMirrorFlipConf)
            lept_stderr("Text is not mirror reversed\n");
        if (*pconf < -DefaultMinMirrorFlipConf)
            lept_stderr("Text is mirror reversed\n");
    }

    return 0;
}


/*---------------------------------------------------------------------*
 *                         Auto-generated hmt code                     *
 *---------------------------------------------------------------------*/
/*
 *  pixFlipFHMTGen()
 *
 *      Input:  pixd (usual 3 choices: null, == pixs, != pixs)
 *              pixs
 *              sel name (one of four defined in SEL_NAMES[])
 *      Return: pixd
 *
 *  Notes:
 *     Action: hit-miss transform on pixs by the sel
 *     N.B.: the sel must have at least one hit, and it
 *           can have any number of misses.
 */
PIX *
pixFlipFHMTGen(PIX         *pixd,
               PIX         *pixs,
               const char  *selname)
{
l_int32    i, index, found, w, h, wpls, wpld;
l_uint32  *datad, *datas, *datat;
PIX       *pixt;

    if (!pixs)
        return (PIX *)ERROR_PTR("pixs not defined", __func__, pixd);
    if (pixGetDepth(pixs) != 1)
        return (PIX *)ERROR_PTR("pixs must be 1 bpp", __func__, pixd);

    found = FALSE;
    for (i = 0; i < NUM_SELS_GENERATED; i++) {
        if (strcmp(selname, SEL_NAMES[i]) == 0) {
            found = TRUE;
            index = i;
            break;
        }
    }
    if (found == FALSE)
        return (PIX *)ERROR_PTR("sel index not found", __func__, pixd);

    if (pixd) {
        if (!pixSizesEqual(pixs, pixd))
            return (PIX *)ERROR_PTR("sizes not equal", __func__, pixd);
    } else {
        if ((pixd = pixCreateTemplate(pixs)) == NULL)
            return (PIX *)ERROR_PTR("pixd not made", __func__, NULL);
    }

    wpls = pixGetWpl(pixs);
    wpld = pixGetWpl(pixd);

        /*  The images must be surrounded with ADDED_BORDER white pixels,
         *  that we'll read from.  We fabricate a "proper"
         *  image as the subimage within the border, having the
         *  following parameters:  */
    w = pixGetWidth(pixs) - 2 * ADDED_BORDER;
    h = pixGetHeight(pixs) - 2 * ADDED_BORDER;
    datas = pixGetData(pixs) + ADDED_BORDER * wpls + ADDED_BORDER / 32;
    datad = pixGetData(pixd) + ADDED_BORDER * wpld + ADDED_BORDER / 32;

    if (pixd == pixs) {  /* need temp image if in-place */
        if ((pixt = pixCopy(NULL, pixs)) == NULL)
            return (PIX *)ERROR_PTR("pixt not made", __func__, pixd);
        datat = pixGetData(pixt) + ADDED_BORDER * wpls + ADDED_BORDER / 32;
        flipfhmtgen_low(datad, w, h, wpld, datat, wpls, index);
        pixDestroy(&pixt);
    } else {  /* simple and not in-place */
        flipfhmtgen_low(datad, w, h, wpld, datas, wpls, index);
    }

    return pixd;
}


/*---------------------------------------------------------------------*
 *                           Fast hmt dispatcher                       *
 *---------------------------------------------------------------------*/
/*
 *  flipfhmtgen_low()
 *
 *       A dispatcher to appropriate low-level code for flip hmt ops
 */
static l_int32
flipfhmtgen_low(l_uint32  *datad,
                l_int32    w,
                l_int32    h,
                l_int32    wpld,
                l_uint32  *datas,
                l_int32    wpls,
                l_int32    index)
{

    switch (index)
    {
    case 0:
        fhmt_1_0(datad, w, h, wpld, datas, wpls);
        break;
    case 1:
        fhmt_1_1(datad, w, h, wpld, datas, wpls);
        break;
    case 2:
        fhmt_1_2(datad, w, h, wpld, datas, wpls);
        break;
    case 3:
        fhmt_1_3(datad, w, h, wpld, datas, wpls);
        break;
    }

    return 0;
}


/*--------------------------------------------------------------------------*
 *                  Low-level auto-generated hmt routines                   *
 *--------------------------------------------------------------------------*/
/*
 *  N.B.  in all the low-level routines, the part of the image
 *        that is accessed has been clipped by ADDED_BORDER pixels
 *        on all four sides.  This is done in the higher level
 *        code by redefining w and h smaller and by moving the
 *        start-of-image pointers up to the beginning of this
 *        interior rectangle.
 */

static void
fhmt_1_0(l_uint32  *datad,
         l_int32    w,
         l_int32    h,
         l_int32    wpld,
         l_uint32  *datas,
         l_int32    wpls)
{
l_int32    i;
l_int32    j, pwpls;
l_uint32  *sptr, *dptr;
l_int32    wpls2, wpls3;

    wpls2 = 2 * wpls;
    wpls3 = 3 * wpls;
    pwpls = (l_uint32)(w + 31) / 32;  /* proper wpl of src */

    for (i = 0; i < h; i++) {
        sptr = datas + i * wpls;
        dptr = datad + i * wpld;
        for (j = 0; j < pwpls; j++, sptr++, dptr++) {
            *dptr = ((*(sptr - wpls) >> 3) | (*(sptr - wpls - 1) << 29)) &
                    (~*(sptr - wpls)) &
                    ((~*(sptr - wpls) << 1) | (~*(sptr - wpls + 1) >> 31)) &
                    ((*(sptr) >> 3) | (*(sptr - 1) << 29)) &
                    ((~*(sptr) >> 1) | (~*(sptr - 1) << 31)) &
                    (~*sptr) &
                    ((~*(sptr) << 1) | (~*(sptr + 1) >> 31)) &
                    ((*(sptr + wpls) >> 3) | (*(sptr + wpls - 1) << 29)) &
                    (~*(sptr + wpls)) &
                    ((*(sptr + wpls2) >> 3) | (*(sptr + wpls2 - 1) << 29)) &
                    ((*(sptr + wpls3) >> 3) | (*(sptr + wpls3 - 1) << 29)) &
                    ((*(sptr + wpls3) >> 2) | (*(sptr + wpls3 - 1) << 30)) &
                    ((*(sptr + wpls3) >> 1) | (*(sptr + wpls3 - 1) << 31)) &
                    (*(sptr + wpls3)) &
                    ((*(sptr + wpls3) << 1) | (*(sptr + wpls3 + 1) >> 31)) &
                    ((*(sptr + wpls3) << 2) | (*(sptr + wpls3 + 1) >> 30));
        }
    }
}


static void
fhmt_1_1(l_uint32  *datad,
         l_int32    w,
         l_int32    h,
         l_int32    wpld,
         l_uint32  *datas,
         l_int32    wpls)
{
l_int32    i;
l_int32    j, pwpls;
l_uint32  *sptr, *dptr;
l_int32    wpls2, wpls3;

    wpls2 = 2 * wpls;
    wpls3 = 3 * wpls;
    pwpls = (l_uint32)(w + 31) / 32;  /* proper wpl of src */

    for (i = 0; i < h; i++) {
        sptr = datas + i * wpls;
        dptr = datad + i * wpld;
        for (j = 0; j < pwpls; j++, sptr++, dptr++) {
            *dptr = ((~*(sptr - wpls) >> 1) | (~*(sptr - wpls - 1) << 31)) &
                    (~*(sptr - wpls)) &
                    ((*(sptr - wpls) << 3) | (*(sptr - wpls + 1) >> 29)) &
                    ((~*(sptr) >> 1) | (~*(sptr - 1) << 31)) &
                    (~*sptr) &
                    ((~*(sptr) << 1) | (~*(sptr + 1) >> 31)) &
                    ((*(sptr) << 3) | (*(sptr + 1) >> 29)) &
                    (~*(sptr + wpls)) &
                    ((*(sptr + wpls) << 3) | (*(sptr + wpls + 1) >> 29)) &
                    ((*(sptr + wpls2) << 3) | (*(sptr + wpls2 + 1) >> 29)) &
                    ((*(sptr + wpls3) >> 2) | (*(sptr + wpls3 - 1) << 30)) &
                    ((*(sptr + wpls3) >> 1) | (*(sptr + wpls3 - 1) << 31)) &
                    (*(sptr + wpls3)) &
                    ((*(sptr + wpls3) << 1) | (*(sptr + wpls3 + 1) >> 31)) &
                    ((*(sptr + wpls3) << 2) | (*(sptr + wpls3 + 1) >> 30)) &
                    ((*(sptr + wpls3) << 3) | (*(sptr + wpls3 + 1) >> 29));
        }
    }
}


static void
fhmt_1_2(l_uint32  *datad,
         l_int32    w,
         l_int32    h,
         l_int32    wpld,
         l_uint32  *datas,
         l_int32    wpls)
{
l_int32    i;
l_int32    j, pwpls;
l_uint32  *sptr, *dptr;
l_int32    wpls2, wpls3;

    wpls2 = 2 * wpls;
    wpls3 = 3 * wpls;
    pwpls = (l_uint32)(w + 31) / 32;  /* proper wpl of src */

    for (i = 0; i < h; i++) {
        sptr = datas + i * wpls;
        dptr = datad + i * wpld;
        for (j = 0; j < pwpls; j++, sptr++, dptr++) {
            *dptr = ((*(sptr - wpls3) >> 3) | (*(sptr - wpls3 - 1) << 29)) &
                    ((*(sptr - wpls3) >> 2) | (*(sptr - wpls3 - 1) << 30)) &
                    ((*(sptr - wpls3) >> 1) | (*(sptr - wpls3 - 1) << 31)) &
                    (*(sptr - wpls3)) &
                    ((*(sptr - wpls3) << 1) | (*(sptr - wpls3 + 1) >> 31)) &
                    ((*(sptr - wpls3) << 2) | (*(sptr - wpls3 + 1) >> 30)) &
                    ((*(sptr - wpls2) >> 3) | (*(sptr - wpls2 - 1) << 29)) &
                    ((*(sptr - wpls) >> 3) | (*(sptr - wpls - 1) << 29)) &
                    (~*(sptr - wpls)) &
                    ((*(sptr) >> 3) | (*(sptr - 1) << 29)) &
                    ((~*(sptr) >> 1) | (~*(sptr - 1) << 31)) &
                    (~*sptr) &
                    ((~*(sptr) << 1) | (~*(sptr + 1) >> 31)) &
                    ((*(sptr + wpls) >> 3) | (*(sptr + wpls - 1) << 29)) &
                    (~*(sptr + wpls)) &
                    ((~*(sptr + wpls) << 1) | (~*(sptr + wpls + 1) >> 31));
        }
    }
}


static void
fhmt_1_3(l_uint32  *datad,
         l_int32    w,
         l_int32    h,
         l_int32    wpld,
         l_uint32  *datas,
         l_int32    wpls)
{
l_int32    i;
l_int32    j, pwpls;
l_uint32  *sptr, *dptr;
l_int32    wpls2, wpls3;

    wpls2 = 2 * wpls;
    wpls3 = 3 * wpls;
    pwpls = (l_uint32)(w + 31) / 32;  /* proper wpl of src */

    for (i = 0; i < h; i++) {
        sptr = datas + i * wpls;
        dptr = datad + i * wpld;
        for (j = 0; j < pwpls; j++, sptr++, dptr++) {
            *dptr = ((*(sptr - wpls3) >> 2) | (*(sptr - wpls3 - 1) << 30)) &
                    ((*(sptr - wpls3) >> 1) | (*(sptr - wpls3 - 1) << 31)) &
                    (*(sptr - wpls3)) &
                    ((*(sptr - wpls3) << 1) | (*(sptr - wpls3 + 1) >> 31)) &
                    ((*(sptr - wpls3) << 2) | (*(sptr - wpls3 + 1) >> 30)) &
                    ((*(sptr - wpls3) << 3) | (*(sptr - wpls3 + 1) >> 29)) &
                    ((*(sptr - wpls2) << 3) | (*(sptr - wpls2 + 1) >> 29)) &
                    (~*(sptr - wpls)) &
                    ((*(sptr - wpls) << 3) | (*(sptr - wpls + 1) >> 29)) &
                    ((~*(sptr) >> 1) | (~*(sptr - 1) << 31)) &
                    (~*sptr) &
                    ((~*(sptr) << 1) | (~*(sptr + 1) >> 31)) &
                    ((*(sptr) << 3) | (*(sptr + 1) >> 29)) &
                    ((~*(sptr + wpls) >> 1) | (~*(sptr + wpls - 1) << 31)) &
                    (~*(sptr + wpls)) &
                    ((*(sptr + wpls) << 3) | (*(sptr + wpls + 1) >> 29));
        }
    }
}
