diff options
Diffstat (limited to 'leptonica/src/rotateorth.c')
-rw-r--r-- | leptonica/src/rotateorth.c | 715 |
1 files changed, 715 insertions, 0 deletions
diff --git a/leptonica/src/rotateorth.c b/leptonica/src/rotateorth.c new file mode 100644 index 00000000..2c83d1e0 --- /dev/null +++ b/leptonica/src/rotateorth.c @@ -0,0 +1,715 @@ +/*====================================================================* + - 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 + * <pre> + * + * 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() + * </pre> + */ + +#ifdef HAVE_CONFIG_H +#include <config_auto.h> +#endif /* HAVE_CONFIG_H */ + +#include <string.h> +#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 + * + * <pre> + * 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); + * </pre> + */ +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 + * + * <pre> + * 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). + * </pre> + */ +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 + * + * <pre> + * 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); + * } + * } + * </pre> + */ +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 + * + * <pre> + * 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. + * </pre> + */ +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; +} |