/*====================================================================* - 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 *
 *
 *                                  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.
 * 
*/ #ifdef HAVE_CONFIG_H #include #endif /* HAVE_CONFIG_H */ #include #include #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 * *
 * 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().
 * 
*/ l_ok pixOrientDetectDwa(PIX *pixs, l_float32 *pupconf, l_float32 *pleftconf, l_int32 mincount, l_int32 debug) { PIX *pix1; PROCNAME("pixOrientDetectDwa"); if (!pixs || pixGetDepth(pixs) != 1) return ERROR_INT("pixs not defined or not 1 bpp", procName, 1); if (!pupconf && !pleftconf) return ERROR_INT("nothing to do", procName, 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 * *
 * 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.
 * 
*/ 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; PROCNAME("pixUpDownDetectDwa"); if (!pconf) return ERROR_INT("&conf not defined", procName, 1); *pconf = 0.0; if (!pixs || pixGetDepth(pixs) != 1) return ERROR_INT("pixs not defined or not 1 bpp", procName, 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 * *
 * Notes:
 *      (1) We assume the text is horizontally oriented, with
 *          ascenders going up.
 *      (2) See notes in pixMirrorDetect().
 * 
*/ 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; PROCNAME("pixMirrorDetectDwa"); if (!pconf) return ERROR_INT("&conf not defined", procName, 1); *pconf = 0.0; if (!pixs || pixGetDepth(pixs) != 1) return ERROR_INT("pixs not defined or not 1 bpp", procName, 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; PROCNAME("pixFlipFHMTGen"); if (!pixs) return (PIX *)ERROR_PTR("pixs not defined", procName, pixd); if (pixGetDepth(pixs) != 1) return (PIX *)ERROR_PTR("pixs must be 1 bpp", procName, 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", procName, pixd); if (pixd) { if (!pixSizesEqual(pixs, pixd)) return (PIX *)ERROR_PTR("sizes not equal", procName, pixd); } else { if ((pixd = pixCreateTemplate(pixs)) == NULL) return (PIX *)ERROR_PTR("pixd not made", procName, 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", procName, 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)); } } }