/*====================================================================*
 -  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.
 *====================================================================*/


/*
 *  rotateorthlow.c
 *
 *      90-degree rotation (cw)
 *            void      rotate90Low()
 *
 *      LR-rotation
 *            void      rotateLRLow()
 *
 *      TB-rotation
 *            void      rotateTBLow()
 *
 *      Byte reverse tables
 *            l_uint8  *makeReverseByteTab1()
 *            l_uint8  *makeReverseByteTab2()
 *            l_uint8  *makeReverseByteTab4()
 */

#include <stdio.h>
#include <stdlib.h>

#include "allheaders.h"



/*------------------------------------------------------------------*
 *                           90 degree rotation                     *
 *------------------------------------------------------------------*/
/*!
 *  rotate90Low()
 *
 *       direction:  1 for cw rotation
 *                  -1 for ccw rotation
 *
 *       Note: The dest must be cleared in advance because not
 *             all source pixels are written to the destination.
 */
void
rotate90Low(l_uint32  *datad,
            l_int32    wd,
            l_int32    hd,
            l_int32    d,
            l_int32    wpld,
            l_uint32  *datas, 
            l_int32    wpls,
	    l_int32    direction)
{
l_int32    i, j, k, m, iend, nswords;
l_uint32   val, word;
l_uint32  *lines, *lined;

    PROCNAME("rotate90Low");

    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:
		ERROR_VOID("illegal depth", procName);
	}
    }
    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:
		ERROR_VOID("illegal depth", procName);
	}
    }

    return;
}


/*------------------------------------------------------------------*
 *                               LR rotation                        *
 *------------------------------------------------------------------*/
/*!
 *  rotateLRLow()
 *
 *     Action: 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);
 *                       }
 *                   }
 *
 *      Note: This operation is in-place.
 */
void
rotateLRLow(l_uint32  *data,
            l_int32    w,
            l_int32    h,
            l_int32    d,
            l_int32    wpl,
	    l_uint8   *tab,
	    l_uint32  *buffer)
{
l_int32    extra, shift, databpl, bpl, i, j;
l_uint32   val;
l_uint32  *line;

    PROCNAME("rotateLRLow");

    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:
	    ERROR_VOID("depth not permitted for LR rot", procName);
	    return;
    }

    return;
}


/*------------------------------------------------------------------*
 *                               TB rotation                        *
 *------------------------------------------------------------------*/
/*!
 *  rotateTBLow()
 *
 *     Action: This is simple & fast.  We use the memcpy function
 *             to do all the work on aligned data, regardless
 *             of pixel depth.  The operation is in-place.
 */
void
rotateTBLow(l_uint32  *data,
            l_int32    h,
            l_int32    wpl,
	    l_uint32  *buffer)
{
l_int32    i, k, h2, bpl;
l_uint32  *linet, *lineb;

    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);
    }

    return;
}


/*------------------------------------------------------------------*
 *                          Byte reverse tables                     *
 *------------------------------------------------------------------*/
/*!
 *  makeReverseByteTab1()
 *
 *     Table for reversing the order of eight 1-bit pixels
 */
l_uint8 *
makeReverseByteTab1(void)
{
l_int32   i;
l_uint8  *tab;

    PROCNAME("makeReverseByteTab1");

    if ((tab = (l_uint8 *)CALLOC(256, sizeof(l_uint8))) == NULL)
	return (l_uint8 *)ERROR_PTR("calloc fail for tab", procName, NULL);

    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;
}


/*!
 *  makeReverseByteTab2()
 *
 *     Table for reversing the order of four 2-bit pixels
 */
l_uint8 *
makeReverseByteTab2(void)
{
l_int32   i;
l_uint8  *tab;

    PROCNAME("makeReverseByteTab2");

    if ((tab = (l_uint8 *)CALLOC(256, sizeof(l_uint8))) == NULL)
	return (l_uint8 *)ERROR_PTR("calloc fail for tab", procName, NULL);

    for (i = 0; i < 256; i++)
	tab[i] = ((0xc0 & i) >> 6) |
	         ((0x30 & i) >> 2) |
	         ((0x0c & i) << 2) |
	         ((0x03 & i) << 6);
    return tab;
}


/*!
 *  makeReverseByteTab4()
 *
 *     Table for reversing the order of two 4-bit pixels
 */
l_uint8 *
makeReverseByteTab4(void)
{
l_int32   i;
l_uint8  *tab;

    PROCNAME("makeReverseByteTab4");

    if ((tab = (l_uint8 *)CALLOC(256, sizeof(l_uint8))) == NULL)
	return (l_uint8 *)ERROR_PTR("calloc fail for tab", procName, NULL);

    for (i = 0; i < 256; i++)
	tab[i] = ((0xf0 & i) >> 4) | ((0x0f & i) << 4);
    return tab;
}

