/* $Revision: 1.6 $ */
/* Copyright 1984-2001 The MathWorks, Inc.  */

/*
 * PCXDRLE.MEX
 *
 * [X,IDX] = PCXDRLE(BUFFER, HEIGHT, WIDTH) decompresses the RLE-encoded
 * byte-stream contained in the uint8 vector BUFFER.  X is a uint8 array
 * containing the decompressed result.  Note that the dimensions of
 * X will be WIDTH-by-HEIGHT.  This is not a mistake; this reflects the
 * way pixels are ordered in a PCX file.  IDX is the 1-based index 
 * of the last BUFFER element processed.  The reason that all the
 * elements of BUFFER might not be processed is that BUFFER may contain
 * colormap data at the end.
 *
 * Reference:  Murray and vanRyper, Encyclopedia of Graphics File Formats,
 * 2nd ed, O'Reilly, 1996.
 */


static char rcsid[] = "$Id: pcxdrle.c,v 1.6 2001/09/28 12:20:22 marc Exp $";

#include "mex.h"

#define CHK_IN_BUFFER \
if (nIn >= nInMax) { \
    mexWarnMsgTxt("RLE decoding failure; input appears to be truncated"); \
    nIn--; \
    break; \
}
    
#define CHK_OUT_BUFFER \
if (nOut >= nOutMax) { \
    mexWarnMsgTxt("buffer overflow in RLE decoder; input may be corrupt"); \
    break; \
}
    
void mexFunction(int nlhs, 
                 mxArray *plhs[], 
                 int nrhs, 
                 const mxArray *prhs[] )
{
    uint8_T *prIn, *prOut;
    int     nXMax, nYMax, nOutMax;
    int     nnn, nIn, nOut;
    int     nInMax;
    int     outputSize[2];
    uint8_T nCount, nValue, nByte;
    uint8_T upper2 = 192;
    uint8_T lower6 = 63;
    int k;
    
    if (nrhs < 3)
    {
        mexErrMsgTxt("Too few input arguments");
    }
    if (nrhs > 3)
    {
        mexErrMsgTxt("Too many input arguments");
    }
    
    if (!mxIsUint8(prhs[0]))
    {
        mexErrMsgTxt("First input argument must be uint8");
    }
    for (k = 1; k < nrhs; k++)
    {
        if (mxIsEmpty(prhs[k]))
        {
            mexErrMsgTxt("All inputs except the first must be nonempty");
        }
        if (!mxIsDouble(prhs[k]))
        {
            mexErrMsgTxt("All inputs except the first must be double");
        }
    }
    
    prIn = (uint8_T *) mxGetData(prhs[0]);
    nInMax = mxGetM(prhs[0]) * mxGetN(prhs[0]);
    nYMax = (int) mxGetScalar(prhs[1]);
    nXMax = (int) mxGetScalar(prhs[2]);
    nOutMax = nYMax*nXMax;
    
    outputSize[0] = nXMax;
    outputSize[1] = nYMax;
    plhs[0] = mxCreateNumericArray(2, outputSize, mxUINT8_CLASS, mxREAL);
    prOut = (uint8_T *) mxGetData(plhs[0]);
    
    nIn=0;
    nOut=0;
    while (nOut < nOutMax)
    {
        CHK_IN_BUFFER;
        nByte = prIn[nIn++];
        if ((nByte & upper2) == upper2)
        {
            nCount=nByte & lower6;
            CHK_IN_BUFFER;
            nValue=prIn[nIn++];
        } 
        else 
        {
            nCount=1;
            nValue=nByte;
        }
        for (nnn = 0; nnn < (int) nCount; nnn++)
        {
            CHK_OUT_BUFFER;
            prOut[nOut++] = nValue;
        }
        
    }
    
    if (nlhs > 1)
    {
        /* ending input buffer index (one-based) */
        plhs[1] = mxCreateDoubleScalar(nIn);
    }
    
}
