/*====================================================================* - 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 rotateorth.c *
 *
 *      Top-level rotation by multiples of 90 degrees
 *            PIX             *pixRotateOrth()
 *
 *      180-degree rotation
 *            PIX             *pixRotate180()
 *
 *      90-degree rotation (both directions)
 *            PIX             *pixRotate90()
 *
 *      Left-right flip
 *            PIX             *pixFlipLR()
 *
 *      Top-bottom flip
 *            PIX             *pixFlipTB()
 *
 *      Byte reverse tables
 *            static l_uint8  *makeReverseByteTab1()
 *            static l_uint8  *makeReverseByteTab2()
 *            static l_uint8  *makeReverseByteTab4()
 * 
*/ #ifdef HAVE_CONFIG_H #include #endif /* HAVE_CONFIG_H */ #include #include "allheaders.h" static l_uint8 *makeReverseByteTab1(void); static l_uint8 *makeReverseByteTab2(void); static l_uint8 *makeReverseByteTab4(void); /*------------------------------------------------------------------* * Top-level rotation by multiples of 90 degrees * *------------------------------------------------------------------*/ /*! * \brief pixRotateOrth() * * \param[in] pixs all depths * \param[in] quads 0-3; number of 90 degree cw rotations * \return pixd, or NULL on error */ PIX * pixRotateOrth(PIX *pixs, l_int32 quads) { PROCNAME("pixRotateOrth"); if (!pixs) return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); if (quads < 0 || quads > 3) return (PIX *)ERROR_PTR("quads not in {0,1,2,3}", procName, NULL); if (quads == 0) return pixCopy(NULL, pixs); else if (quads == 1) return pixRotate90(pixs, 1); else if (quads == 2) return pixRotate180(NULL, pixs); else /* quads == 3 */ return pixRotate90(pixs, -1); } /*------------------------------------------------------------------* * 180 degree rotation * *------------------------------------------------------------------*/ /*! * \brief pixRotate180() * * \param[in] pixd [optional]; can be null, equal to pixs, * or different from pixs * \param[in] pixs all depths * \return pixd, or NULL on error * *
 * Notes:
 *      (1) This does a 180 rotation of the image about the center,
 *          which is equivalent to a left-right flip about a vertical
 *          line through the image center, followed by a top-bottom
 *          flip about a horizontal line through the image center.
 *      (2) There are 3 cases for input:
 *          (a) pixd == null (creates a new pixd)
 *          (b) pixd == pixs (in-place operation)
 *          (c) pixd != pixs (existing pixd)
 *      (3) For clarity, use these three patterns, respectively:
 *          (a) pixd = pixRotate180(NULL, pixs);
 *          (b) pixRotate180(pixs, pixs);
 *          (c) pixRotate180(pixd, pixs);
 * 
*/ PIX * pixRotate180(PIX *pixd, PIX *pixs) { l_int32 d; PROCNAME("pixRotate180"); if (!pixs) return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); d = pixGetDepth(pixs); if (d != 1 && d != 2 && d != 4 && d != 8 && d != 16 && d != 32) return (PIX *)ERROR_PTR("pixs not in {1,2,4,8,16,32} bpp", procName, NULL); /* Prepare pixd for in-place operation */ if ((pixd = pixCopy(pixd, pixs)) == NULL) return (PIX *)ERROR_PTR("pixd not made", procName, NULL); pixFlipLR(pixd, pixd); pixFlipTB(pixd, pixd); return pixd; } /*------------------------------------------------------------------* * 90 degree rotation * *------------------------------------------------------------------*/ /*! * \brief pixRotate90() * * \param[in] pixs all depths * \param[in] direction clockwise = 1, counterclockwise = -1 * \return pixd, or NULL on error * *
 * Notes:
 *      (1) This does a 90 degree rotation of the image about the center,
 *          either cw or ccw, returning a new pix.
 *      (2) The direction must be either 1 (cw) or -1 (ccw).
 * 
*/ PIX * pixRotate90(PIX *pixs, l_int32 direction) { l_int32 wd, hd, d, wpls, wpld; l_int32 i, j, k, m, iend, nswords; l_uint32 val, word; l_uint32 *lines, *datas, *lined, *datad; PIX *pixd; PROCNAME("pixRotate90"); if (!pixs) return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); pixGetDimensions(pixs, &hd, &wd, &d); /* note: reversed */ if (d != 1 && d != 2 && d != 4 && d != 8 && d != 16 && d != 32) return (PIX *)ERROR_PTR("pixs not in {1,2,4,8,16,32} bpp", procName, NULL); if (direction != 1 && direction != -1) return (PIX *)ERROR_PTR("invalid direction", procName, NULL); if ((pixd = pixCreate(wd, hd, d)) == NULL) return (PIX *)ERROR_PTR("pixd not made", procName, NULL); pixCopyColormap(pixd, pixs); pixCopyResolution(pixd, pixs); pixCopyInputFormat(pixd, pixs); pixCopySpp(pixd, pixs); datas = pixGetData(pixs); wpls = pixGetWpl(pixs); datad = pixGetData(pixd); wpld = pixGetWpl(pixd); if (direction == 1) { /* clockwise */ switch (d) { case 32: for (i = 0; i < hd; i++) { lined = datad + i * wpld; lines = datas + (wd - 1) * wpls; for (j = 0; j < wd; j++) { lined[j] = lines[i]; lines -= wpls; } } break; case 16: for (i = 0; i < hd; i++) { lined = datad + i * wpld; lines = datas + (wd - 1) * wpls; for (j = 0; j < wd; j++) { if ((val = GET_DATA_TWO_BYTES(lines, i))) SET_DATA_TWO_BYTES(lined, j, val); lines -= wpls; } } break; case 8: for (i = 0; i < hd; i++) { lined = datad + i * wpld; lines = datas + (wd - 1) * wpls; for (j = 0; j < wd; j++) { if ((val = GET_DATA_BYTE(lines, i))) SET_DATA_BYTE(lined, j, val); lines -= wpls; } } break; case 4: for (i = 0; i < hd; i++) { lined = datad + i * wpld; lines = datas + (wd - 1) * wpls; for (j = 0; j < wd; j++) { if ((val = GET_DATA_QBIT(lines, i))) SET_DATA_QBIT(lined, j, val); lines -= wpls; } } break; case 2: for (i = 0; i < hd; i++) { lined = datad + i * wpld; lines = datas + (wd - 1) * wpls; for (j = 0; j < wd; j++) { if ((val = GET_DATA_DIBIT(lines, i))) SET_DATA_DIBIT(lined, j, val); lines -= wpls; } } break; case 1: nswords = hd / 32; for (j = 0; j < wd; j++) { lined = datad; lines = datas + (wd - 1 - j) * wpls; for (k = 0; k < nswords; k++) { word = lines[k]; if (!word) { lined += 32 * wpld; continue; } else { iend = 32 * (k + 1); for (m = 0, i = 32 * k; i < iend; i++, m++) { if ((word << m) & 0x80000000) SET_DATA_BIT(lined, j); lined += wpld; } } } for (i = 32 * nswords; i < hd; i++) { if (GET_DATA_BIT(lines, i)) SET_DATA_BIT(lined, j); lined += wpld; } } break; default: pixDestroy(&pixd); L_ERROR("illegal depth: %d\n", procName, d); break; } } else { /* direction counter-clockwise */ switch (d) { case 32: for (i = 0; i < hd; i++) { lined = datad + i * wpld; lines = datas; for (j = 0; j < wd; j++) { lined[j] = lines[hd - 1 - i]; lines += wpls; } } break; case 16: for (i = 0; i < hd; i++) { lined = datad + i * wpld; lines = datas; for (j = 0; j < wd; j++) { if ((val = GET_DATA_TWO_BYTES(lines, hd - 1 - i))) SET_DATA_TWO_BYTES(lined, j, val); lines += wpls; } } break; case 8: for (i = 0; i < hd; i++) { lined = datad + i * wpld; lines = datas; for (j = 0; j < wd; j++) { if ((val = GET_DATA_BYTE(lines, hd - 1 - i))) SET_DATA_BYTE(lined, j, val); lines += wpls; } } break; case 4: for (i = 0; i < hd; i++) { lined = datad + i * wpld; lines = datas; for (j = 0; j < wd; j++) { if ((val = GET_DATA_QBIT(lines, hd - 1 - i))) SET_DATA_QBIT(lined, j, val); lines += wpls; } } break; case 2: for (i = 0; i < hd; i++) { lined = datad + i * wpld; lines = datas; for (j = 0; j < wd; j++) { if ((val = GET_DATA_DIBIT(lines, hd - 1 - i))) SET_DATA_DIBIT(lined, j, val); lines += wpls; } } break; case 1: nswords = hd / 32; for (j = 0; j < wd; j++) { lined = datad + (hd - 1) * wpld; lines = datas + (wd - 1 - j) * wpls; for (k = 0; k < nswords; k++) { word = lines[k]; if (!word) { lined -= 32 * wpld; continue; } else { iend = 32 * (k + 1); for (m = 0, i = 32 * k; i < iend; i++, m++) { if ((word << m) & 0x80000000) SET_DATA_BIT(lined, wd - 1 - j); lined -= wpld; } } } for (i = 32 * nswords; i < hd; i++) { if (GET_DATA_BIT(lines, i)) SET_DATA_BIT(lined, wd - 1 - j); lined -= wpld; } } break; default: pixDestroy(&pixd); L_ERROR("illegal depth: %d\n", procName, d); break; } } return pixd; } /*------------------------------------------------------------------* * Left-right flip * *------------------------------------------------------------------*/ /*! * \brief pixFlipLR() * * \param[in] pixd [optional]; can be null, equal to pixs, * or different from pixs * \param[in] pixs all depths * \return pixd, or NULL on error * *
 * Notes:
 *      (1) This does a left-right flip of the image, which is
 *          equivalent to a rotation out of the plane about a
 *          vertical line through the image center.
 *      (2) There are 3 cases for input:
 *          (a) pixd == null (creates a new pixd)
 *          (b) pixd == pixs (in-place operation)
 *          (c) pixd != pixs (existing pixd)
 *      (3) For clarity, use these three patterns, respectively:
 *          (a) pixd = pixFlipLR(NULL, pixs);
 *          (b) pixFlipLR(pixs, pixs);
 *          (c) pixFlipLR(pixd, pixs);
 *      (4) If an existing pixd is not the same size as pixs, the
 *          image data will be reallocated.
 *      (5) The pixel access routines allow a trivial implementation.
 *          However, for d < 8, it is more efficient to right-justify
 *          each line to a 32-bit boundary and then extract bytes and
 *          do pixel reversing.   In those cases, as in the 180 degree
 *          rotation, we right-shift the data (if necessary) to
 *          right-justify on the 32 bit boundary, and then read the
 *          bytes off each raster line in reverse order, reversing
 *          the pixels in each byte using a table.  These functions
 *          for 1, 2 and 4 bpp were tested against the "trivial"
 *          version (shown here for 4 bpp):
 *              for (i = 0; i < h; i++) {
 *                  line = data + i * wpl;
 *                  memcpy(buffer, line, bpl);
 *                    for (j = 0; j < w; j++) {
 *                      val = GET_DATA_QBIT(buffer, w - 1 - j);
 *                        SET_DATA_QBIT(line, j, val);
 *                  }
 *              }
 * 
*/ PIX * pixFlipLR(PIX *pixd, PIX *pixs) { l_uint8 *tab; l_int32 w, h, d, wpl; l_int32 extra, shift, databpl, bpl, i, j; l_uint32 val; l_uint32 *line, *data, *buffer; PROCNAME("pixFlipLR"); if (!pixs) return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); pixGetDimensions(pixs, &w, &h, &d); if (d != 1 && d != 2 && d != 4 && d != 8 && d != 16 && d != 32) return (PIX *)ERROR_PTR("pixs not in {1,2,4,8,16,32} bpp", procName, NULL); /* Prepare pixd for in-place operation */ if ((pixd = pixCopy(pixd, pixs)) == NULL) return (PIX *)ERROR_PTR("pixd not made", procName, NULL); data = pixGetData(pixd); wpl = pixGetWpl(pixd); switch (d) { case 1: tab = makeReverseByteTab1(); break; case 2: tab = makeReverseByteTab2(); break; case 4: tab = makeReverseByteTab4(); break; default: tab = NULL; break; } /* Possibly inplace assigning return val, so on failure return pixd */ if ((buffer = (l_uint32 *)LEPT_CALLOC(wpl, sizeof(l_uint32))) == NULL) { if (tab) LEPT_FREE(tab); return (PIX *)ERROR_PTR("buffer not made", procName, pixd); } bpl = 4 * wpl; switch (d) { case 32: for (i = 0; i < h; i++) { line = data + i * wpl; memcpy(buffer, line, bpl); for (j = 0; j < w; j++) line[j] = buffer[w - 1 - j]; } break; case 16: for (i = 0; i < h; i++) { line = data + i * wpl; memcpy(buffer, line, bpl); for (j = 0; j < w; j++) { val = GET_DATA_TWO_BYTES(buffer, w - 1 - j); SET_DATA_TWO_BYTES(line, j, val); } } break; case 8: for (i = 0; i < h; i++) { line = data + i * wpl; memcpy(buffer, line, bpl); for (j = 0; j < w; j++) { val = GET_DATA_BYTE(buffer, w - 1 - j); SET_DATA_BYTE(line, j, val); } } break; case 4: extra = (w * d) & 31; if (extra) shift = 8 - extra / 4; else shift = 0; if (shift) rasteropHipLow(data, h, d, wpl, 0, h, shift); databpl = (w + 1) / 2; for (i = 0; i < h; i++) { line = data + i * wpl; memcpy(buffer, line, bpl); for (j = 0; j < databpl; j++) { val = GET_DATA_BYTE(buffer, bpl - 1 - j); SET_DATA_BYTE(line, j, tab[val]); } } break; case 2: extra = (w * d) & 31; if (extra) shift = 16 - extra / 2; else shift = 0; if (shift) rasteropHipLow(data, h, d, wpl, 0, h, shift); databpl = (w + 3) / 4; for (i = 0; i < h; i++) { line = data + i * wpl; memcpy(buffer, line, bpl); for (j = 0; j < databpl; j++) { val = GET_DATA_BYTE(buffer, bpl - 1 - j); SET_DATA_BYTE(line, j, tab[val]); } } break; case 1: extra = (w * d) & 31; if (extra) shift = 32 - extra; else shift = 0; if (shift) rasteropHipLow(data, h, d, wpl, 0, h, shift); databpl = (w + 7) / 8; for (i = 0; i < h; i++) { line = data + i * wpl; memcpy(buffer, line, bpl); for (j = 0; j < databpl; j++) { val = GET_DATA_BYTE(buffer, bpl - 1 - j); SET_DATA_BYTE(line, j, tab[val]); } } break; default: pixDestroy(&pixd); L_ERROR("illegal depth: %d\n", procName, d); break; } LEPT_FREE(buffer); if (tab) LEPT_FREE(tab); return pixd; } /*------------------------------------------------------------------* * Top-bottom flip * *------------------------------------------------------------------*/ /*! * \brief pixFlipTB() * * \param[in] pixd [optional]; can be null, equal to pixs, * or different from pixs * \param[in] pixs all depths * \return pixd, or NULL on error * *
 * Notes:
 *      (1) This does a top-bottom flip of the image, which is
 *          equivalent to a rotation out of the plane about a
 *          horizontal line through the image center.
 *      (2) There are 3 cases for input:
 *          (a) pixd == null (creates a new pixd)
 *          (b) pixd == pixs (in-place operation)
 *          (c) pixd != pixs (existing pixd)
 *      (3) For clarity, use these three patterns, respectively:
 *          (a) pixd = pixFlipTB(NULL, pixs);
 *          (b) pixFlipTB(pixs, pixs);
 *          (c) pixFlipTB(pixd, pixs);
 *      (4) If an existing pixd is not the same size as pixs, the
 *          image data will be reallocated.
 *      (5) This is simple and fast.  We use the memcpy function
 *          to do all the work on aligned data, regardless of pixel
 *          depth.
 * 
*/ PIX * pixFlipTB(PIX *pixd, PIX *pixs) { l_int32 h, d, wpl, i, k, h2, bpl; l_uint32 *linet, *lineb; l_uint32 *data, *buffer; PROCNAME("pixFlipTB"); if (!pixs) return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); pixGetDimensions(pixs, NULL, &h, &d); if (d != 1 && d != 2 && d != 4 && d != 8 && d != 16 && d != 32) return (PIX *)ERROR_PTR("pixs not in {1,2,4,8,16,32} bpp", procName, NULL); /* Prepare pixd for in-place operation */ if ((pixd = pixCopy(pixd, pixs)) == NULL) return (PIX *)ERROR_PTR("pixd not made", procName, NULL); data = pixGetData(pixd); wpl = pixGetWpl(pixd); if ((buffer = (l_uint32 *)LEPT_CALLOC(wpl, sizeof(l_uint32))) == NULL) return (PIX *)ERROR_PTR("buffer not made", procName, pixd); h2 = h / 2; bpl = 4 * wpl; for (i = 0, k = h - 1; i < h2; i++, k--) { linet = data + i * wpl; lineb = data + k * wpl; memcpy(buffer, linet, bpl); memcpy(linet, lineb, bpl); memcpy(lineb, buffer, bpl); } LEPT_FREE(buffer); return pixd; } /*------------------------------------------------------------------* * Static byte reverse tables * *------------------------------------------------------------------*/ /*! * \brief makeReverseByteTab1() * * Notes: * (1) This generates an 8 bit lookup table for reversing * the order of eight 1-bit pixels. */ static l_uint8 * makeReverseByteTab1(void) { l_int32 i; l_uint8 *tab; tab = (l_uint8 *)LEPT_CALLOC(256, sizeof(l_uint8)); for (i = 0; i < 256; i++) tab[i] = ((0x80 & i) >> 7) | ((0x40 & i) >> 5) | ((0x20 & i) >> 3) | ((0x10 & i) >> 1) | ((0x08 & i) << 1) | ((0x04 & i) << 3) | ((0x02 & i) << 5) | ((0x01 & i) << 7); return tab; } /*! * \brief makeReverseByteTab2() * * Notes: * (1) This generates an 8 bit lookup table for reversing * the order of four 2-bit pixels. */ static l_uint8 * makeReverseByteTab2(void) { l_int32 i; l_uint8 *tab; tab = (l_uint8 *)LEPT_CALLOC(256, sizeof(l_uint8)); for (i = 0; i < 256; i++) tab[i] = ((0xc0 & i) >> 6) | ((0x30 & i) >> 2) | ((0x0c & i) << 2) | ((0x03 & i) << 6); return tab; } /*! * \brief makeReverseByteTab4() * * Notes: * (1) This generates an 8 bit lookup table for reversing * the order of two 4-bit pixels. */ static l_uint8 * makeReverseByteTab4(void) { l_int32 i; l_uint8 *tab; tab = (l_uint8 *)LEPT_CALLOC(256, sizeof(l_uint8)); for (i = 0; i < 256; i++) tab[i] = ((0xf0 & i) >> 4) | ((0x0f & i) << 4); return tab; }