/*
 * Copyright (c) 1999-2000 Image Power, Inc. and the University of
 *   British Columbia.
 * Copyright (c) 2001-2002 Michael David Adams.
 * All rights reserved.
 */

/* __START_OF_JASPER_LICENSE__
 *
 * JasPer Software License
 *
 * IMAGE POWER JPEG-2000 PUBLIC LICENSE
 * ************************************
 *
 * GRANT:
 *
 * Permission is hereby granted, free of charge, to any person (the "User")
 * obtaining a copy of this software and associated documentation, to deal
 * in the JasPer Software without restriction, including without limitation
 * the right to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the JasPer Software (in source and binary forms),
 * and to permit persons to whom the JasPer Software is furnished to do so,
 * provided further that the License Conditions below are met.
 *
 * License Conditions
 * ******************
 *
 * A.  Redistributions of source code must retain the above copyright notice,
 * and this list of conditions, and the following disclaimer.
 *
 * B.  Redistributions in binary form must reproduce the above copyright
 * notice, and this list of conditions, and the following disclaimer in
 * the documentation and/or other materials provided with the distribution.
 *
 * C.  Neither the name of Image Power, Inc. nor any other contributor
 * (including, but not limited to, the University of British Columbia and
 * Michael David Adams) may be used to endorse or promote products derived
 * from this software without specific prior written permission.
 *
 * D.  User agrees that it shall not commence any action against Image Power,
 * Inc., the University of British Columbia, Michael David Adams, or any
 * other contributors (collectively "Licensors") for infringement of any
 * intellectual property rights ("IPR") held by the User in respect of any
 * technology that User owns or has a right to license or sublicense and
 * which is an element required in order to claim compliance with ISO/IEC
 * 15444-1 (i.e., JPEG-2000 Part 1).  "IPR" means all intellectual property
 * rights worldwide arising under statutory or common law, and whether
 * or not perfected, including, without limitation, all (i) patents and
 * patent applications owned or licensable by User; (ii) rights associated
 * with works of authorship including copyrights, copyright applications,
 * copyright registrations, mask work rights, mask work applications,
 * mask work registrations; (iii) rights relating to the protection of
 * trade secrets and confidential information; (iv) any right analogous
 * to those set forth in subsections (i), (ii), or (iii) and any other
 * proprietary rights relating to intangible property (other than trademark,
 * trade dress, or service mark rights); and (v) divisions, continuations,
 * renewals, reissues and extensions of the foregoing (as and to the extent
 * applicable) now existing, hereafter filed, issued or acquired.
 *
 * E.  If User commences an infringement action against any Licensor(s) then
 * such Licensor(s) shall have the right to terminate User's license and
 * all sublicenses that have been granted hereunder by User to other parties.
 *
 * F.  This software is for use only in hardware or software products that
 * are compliant with ISO/IEC 15444-1 (i.e., JPEG-2000 Part 1).  No license
 * or right to this Software is granted for products that do not comply
 * with ISO/IEC 15444-1.  The JPEG-2000 Part 1 standard can be purchased
 * from the ISO.
 *
 * THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE.
 * NO USE OF THE JASPER SOFTWARE IS AUTHORIZED HEREUNDER EXCEPT UNDER
 * THIS DISCLAIMER.  THE JASPER SOFTWARE IS PROVIDED BY THE LICENSORS AND
 * CONTRIBUTORS UNDER THIS LICENSE ON AN ``AS-IS'' BASIS, WITHOUT WARRANTY
 * OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION,
 * WARRANTIES THAT THE JASPER SOFTWARE IS FREE OF DEFECTS, IS MERCHANTABLE,
 * IS FIT FOR A PARTICULAR PURPOSE OR IS NON-INFRINGING.  THOSE INTENDING
 * TO USE THE JASPER SOFTWARE OR MODIFICATIONS THEREOF FOR USE IN HARDWARE
 * OR SOFTWARE PRODUCTS ARE ADVISED THAT THEIR USE MAY INFRINGE EXISTING
 * PATENTS, COPYRIGHTS, TRADEMARKS, OR OTHER INTELLECTUAL PROPERTY RIGHTS.
 * THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE JASPER SOFTWARE
 * IS WITH THE USER.  SHOULD ANY PART OF THE JASPER SOFTWARE PROVE DEFECTIVE
 * IN ANY RESPECT, THE USER (AND NOT THE INITIAL DEVELOPERS, THE UNIVERSITY
 * OF BRITISH COLUMBIA, IMAGE POWER, INC., MICHAEL DAVID ADAMS, OR ANY
 * OTHER CONTRIBUTOR) SHALL ASSUME THE COST OF ANY NECESSARY SERVICING,
 * REPAIR OR CORRECTION.  UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY,
 * WHETHER TORT (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL THE
 * INITIAL DEVELOPER, THE UNIVERSITY OF BRITISH COLUMBIA, IMAGE POWER, INC.,
 * MICHAEL DAVID ADAMS, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF THE
 * JASPER SOFTWARE, OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO
 * THE USER OR ANY OTHER PERSON FOR ANY INDIRECT, SPECIAL, INCIDENTAL, OR
 * CONSEQUENTIAL DAMAGES OF ANY CHARACTER INCLUDING, WITHOUT LIMITATION,
 * DAMAGES FOR LOSS OF GOODWILL, WORK STOPPAGE, COMPUTER FAILURE OR
 * MALFUNCTION, OR ANY AND ALL OTHER COMMERCIAL DAMAGES OR LOSSES, EVEN IF
 * SUCH PARTY HAD BEEN INFORMED, OR OUGHT TO HAVE KNOWN, OF THE POSSIBILITY
 * OF SUCH DAMAGES.  THE JASPER SOFTWARE AND UNDERLYING TECHNOLOGY ARE NOT
 * FAULT-TOLERANT AND ARE NOT DESIGNED, MANUFACTURED OR INTENDED FOR USE OR
 * RESALE AS ON-LINE CONTROL EQUIPMENT IN HAZARDOUS ENVIRONMENTS REQUIRING
 * FAIL-SAFE PERFORMANCE, SUCH AS IN THE OPERATION OF NUCLEAR FACILITIES,
 * AIRCRAFT NAVIGATION OR COMMUNICATION SYSTEMS, AIR TRAFFIC CONTROL, DIRECT
 * LIFE SUPPORT MACHINES, OR WEAPONS SYSTEMS, IN WHICH THE FAILURE OF THE
 * JASPER SOFTWARE OR UNDERLYING TECHNOLOGY OR PRODUCT COULD LEAD DIRECTLY
 * TO DEATH, PERSONAL INJURY, OR SEVERE PHYSICAL OR ENVIRONMENTAL DAMAGE
 * ("HIGH RISK ACTIVITIES").  LICENSOR SPECIFICALLY DISCLAIMS ANY EXPRESS
 * OR IMPLIED WARRANTY OF FITNESS FOR HIGH RISK ACTIVITIES.  USER WILL NOT
 * KNOWINGLY USE, DISTRIBUTE OR RESELL THE JASPER SOFTWARE OR UNDERLYING
 * TECHNOLOGY OR PRODUCTS FOR HIGH RISK ACTIVITIES AND WILL ENSURE THAT ITS
 * CUSTOMERS AND END-USERS OF ITS PRODUCTS ARE PROVIDED WITH A COPY OF THE
 * NOTICE SPECIFIED IN THIS SECTION.
 *
 * __END_OF_JASPER_LICENSE__
 */

/*
 * Tier 1 Decoder
 *
 * $Id$
 */

/******************************************************************************\
* Includes.
\******************************************************************************/

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

#include "netpbm/nstring.h"

#include "jasper/jas_stream.h"
#include "jasper/jas_math.h"

#include "jpc_bs.h"
#include "jpc_mqdec.h"
#include "jpc_t1dec.h"
#include "jpc_t1cod.h"
#include "jpc_dec.h"

/******************************************************************************\
*
\******************************************************************************/

#if defined(DEBUG)
static long t1dec_cnt = 0;
#endif

#if !defined(DEBUG)
#define JPC_T1D_GETBIT(mqdec, v, passtypename, symtypename) \
    ((v) = jpc_mqdec_getbit(mqdec))
#else
#define JPC_T1D_GETBIT(mqdec, v, passtypename, symtypename) \
{ \
    (v) = jpc_mqdec_getbit(mqdec); \
    if (jas_getdbglevel() >= 100) { \
        fprintf(stderr, "index = %ld; passtype = %s; symtype = %s; sym = %d\n", t1dec_cnt, passtypename, symtypename, v); \
        ++t1dec_cnt; \
    } \
}
#endif
#define JPC_T1D_GETBITNOSKEW(mqdec, v, passtypename, symtypename) \
    JPC_T1D_GETBIT(mqdec, v, passtypename, symtypename)

#if !defined(DEBUG)
#define JPC_T1D_RAWGETBIT(bitstream, v, passtypename, symtypename) \
    ((v) = jpc_bitstream_getbit(bitstream))
#else
#define JPC_T1D_RAWGETBIT(bitstream, v, passtypename, symtypename) \
{ \
    (v) = jpc_bitstream_getbit(bitstream); \
    if (jas_getdbglevel() >= 100) { \
        fprintf(stderr, "index = %ld; passtype = %s; symtype = %s; sym = %d\n", t1dec_cnt, passtypename, symtypename, v); \
        ++t1dec_cnt; \
    } \
}
#endif



/******************************************************************************\
* Code for significance pass.
\******************************************************************************/

#define jpc_sigpass_step(fp, frowstep, dp, bitpos, oneplushalf, orient, mqdec, vcausalflag) \
{ \
    int f; \
    int v; \
    f = *(fp); \
    if ((f & JPC_OTHSIGMSK) && !(f & (JPC_SIG | JPC_VISIT))) { \
        jpc_mqdec_setcurctx((mqdec), JPC_GETZCCTXNO(f, (orient))); \
        JPC_T1D_GETBIT((mqdec), v, "SIG", "ZC"); \
        if (v) { \
            jpc_mqdec_setcurctx((mqdec), JPC_GETSCCTXNO(f)); \
            JPC_T1D_GETBIT((mqdec), v, "SIG", "SC"); \
            v ^= JPC_GETSPB(f); \
            JPC_UPDATEFLAGS4((fp), (frowstep), v, (vcausalflag)); \
            *(fp) |= JPC_SIG; \
            *(dp) = (v) ? (-(oneplushalf)) : (oneplushalf); \
        } \
        *(fp) |= JPC_VISIT; \
    } \
}

#define jpc_rawsigpass_step(fp, frowstep, dp, oneplushalf, in, vcausalflag) \
{ \
    jpc_fix_t f = *(fp); \
    jpc_fix_t v; \
    if ((f & JPC_OTHSIGMSK) && !(f & (JPC_SIG | JPC_VISIT))) { \
        JPC_T1D_RAWGETBIT(in, v, "SIG", "ZC"); \
        if (v < 0) { \
            return -1; \
        } \
        if (v) { \
            JPC_T1D_RAWGETBIT(in, v, "SIG", "SC"); \
            if (v < 0) { \
                return -1; \
            } \
            JPC_UPDATEFLAGS4((fp), (frowstep), v, (vcausalflag)); \
            *(fp) |= JPC_SIG; \
            *(dp) = v ? (-oneplushalf) : (oneplushalf); \
        } \
        *(fp) |= JPC_VISIT; \
    } \
}



static int
dec_sigpass(jpc_dec_t *dec, register jpc_mqdec_t *mqdec, int bitpos,
            int orient,
            int vcausalflag, jas_matrix_t *flags, jas_matrix_t *data)
{
    int i;
    int j;
    int one;
    int half;
    int oneplushalf;
    int vscanlen;
    int width;
    int height;
    jpc_fix_t *fp;
    int frowstep;
    int fstripestep;
    jpc_fix_t *fstripestart;
    jpc_fix_t *fvscanstart;
    jpc_fix_t *dp;
    int drowstep;
    int dstripestep;
    jpc_fix_t *dstripestart;
    jpc_fix_t *dvscanstart;
    int k;

    width = jas_matrix_numcols(data);
    height = jas_matrix_numrows(data);
    frowstep = jas_matrix_rowstep(flags);
    drowstep = jas_matrix_rowstep(data);
    fstripestep = frowstep << 2;
    dstripestep = drowstep << 2;

    one = 1 << bitpos;
    half = one >> 1;
    oneplushalf = one | half;

    fstripestart = jas_matrix_getref(flags, 1, 1);
    dstripestart = jas_matrix_getref(data, 0, 0);
    for (i = height; i > 0; i -= 4, fstripestart += fstripestep,
      dstripestart += dstripestep) {
        fvscanstart = fstripestart;
        dvscanstart = dstripestart;
        vscanlen = JAS_MIN(i, 4);
        for (j = width; j > 0; --j, ++fvscanstart, ++dvscanstart) {
            fp = fvscanstart;
            dp = dvscanstart;
            k = vscanlen;

            /* Process first sample in vertical scan. */
            jpc_sigpass_step(fp, frowstep, dp, bitpos, oneplushalf,
              orient, mqdec, vcausalflag);
            if (--k <= 0) {
                continue;
            }
            fp += frowstep;
            dp += drowstep;

            /* Process second sample in vertical scan. */
            jpc_sigpass_step(fp, frowstep, dp, bitpos, oneplushalf,
              orient, mqdec, 0);
            if (--k <= 0) {
                continue;
            }
            fp += frowstep;
            dp += drowstep;

            /* Process third sample in vertical scan. */
            jpc_sigpass_step(fp, frowstep, dp, bitpos, oneplushalf,
              orient, mqdec, 0);
            if (--k <= 0) {
                continue;
            }
            fp += frowstep;
            dp += drowstep;

            /* Process fourth sample in vertical scan. */
            jpc_sigpass_step(fp, frowstep, dp, bitpos, oneplushalf,
              orient, mqdec, 0);
        }
    }
    return 0;
}



static int
dec_rawsigpass(jpc_dec_t *dec, jpc_bitstream_t *in, int bitpos,
               int vcausalflag,
               jas_matrix_t *flags, jas_matrix_t *data)
{
    int i;
    int j;
    int k;
    int one;
    int half;
    int oneplushalf;
    int vscanlen;
    int width;
    int height;
    jpc_fix_t *fp;
    int frowstep;
    int fstripestep;
    jpc_fix_t *fstripestart;
    jpc_fix_t *fvscanstart;
    jpc_fix_t *dp;
    int drowstep;
    int dstripestep;
    jpc_fix_t *dstripestart;
    jpc_fix_t *dvscanstart;

    width = jas_matrix_numcols(data);
    height = jas_matrix_numrows(data);
    frowstep = jas_matrix_rowstep(flags);
    drowstep = jas_matrix_rowstep(data);
    fstripestep = frowstep << 2;
    dstripestep = drowstep << 2;

    one = 1 << bitpos;
    half = one >> 1;
    oneplushalf = one | half;

    fstripestart = jas_matrix_getref(flags, 1, 1);
    dstripestart = jas_matrix_getref(data, 0, 0);
    for (i = height; i > 0; i -= 4, fstripestart += fstripestep,
      dstripestart += dstripestep) {
        fvscanstart = fstripestart;
        dvscanstart = dstripestart;
        vscanlen = JAS_MIN(i, 4);
        for (j = width; j > 0; --j, ++fvscanstart, ++dvscanstart) {
            fp = fvscanstart;
            dp = dvscanstart;
            k = vscanlen;

            /* Process first sample in vertical scan. */
            jpc_rawsigpass_step(fp, frowstep, dp, oneplushalf,
              in, vcausalflag);
            if (--k <= 0) {
                continue;
            }
            fp += frowstep;
            dp += drowstep;

            /* Process second sample in vertical scan. */
            jpc_rawsigpass_step(fp, frowstep, dp, oneplushalf,
              in, 0);
            if (--k <= 0) {
                continue;
            }
            fp += frowstep;
            dp += drowstep;

            /* Process third sample in vertical scan. */
            jpc_rawsigpass_step(fp, frowstep, dp, oneplushalf,
              in, 0);
            if (--k <= 0) {
                continue;
            }
            fp += frowstep;
            dp += drowstep;

            /* Process fourth sample in vertical scan. */
            jpc_rawsigpass_step(fp, frowstep, dp, oneplushalf,
              in, 0);

        }
    }
    return 0;
}



/******************************************************************************\
* Code for refinement pass.
\******************************************************************************/

#define jpc_refpass_step(fp, dp, poshalf, neghalf, mqdec, vcausalflag) \
{ \
    int v; \
    int t; \
    if (((*(fp)) & (JPC_SIG | JPC_VISIT)) == JPC_SIG) { \
        jpc_mqdec_setcurctx((mqdec), JPC_GETMAGCTXNO(*(fp))); \
        JPC_T1D_GETBITNOSKEW((mqdec), v, "REF", "MR"); \
        t = (v ? (poshalf) : (neghalf)); \
        *(dp) += (*(dp) < 0) ? (-t) : t; \
        *(fp) |= JPC_REFINE; \
    } \
}



static int
dec_refpass(jpc_dec_t *dec, register jpc_mqdec_t *mqdec, int bitpos,
            int vcausalflag, jas_matrix_t *flags, jas_matrix_t *data)
{
    int i;
    int j;
    int vscanlen;
    int width;
    int height;
    int one;
    int poshalf;
    int neghalf;
    jpc_fix_t *fp;
    int frowstep;
    int fstripestep;
    jpc_fix_t *fstripestart;
    jpc_fix_t *fvscanstart;
    jpc_fix_t *dp;
    int drowstep;
    int dstripestep;
    jpc_fix_t *dstripestart;
    jpc_fix_t *dvscanstart;
    int k;

    width = jas_matrix_numcols(data);
    height = jas_matrix_numrows(data);
    frowstep = jas_matrix_rowstep(flags);
    drowstep = jas_matrix_rowstep(data);
    fstripestep = frowstep << 2;
    dstripestep = drowstep << 2;

    one = 1 << bitpos;
    poshalf = one >> 1;
    neghalf = (bitpos > 0) ? (-poshalf) : (-1);

    fstripestart = jas_matrix_getref(flags, 1, 1);
    dstripestart = jas_matrix_getref(data, 0, 0);
    for (i = height; i > 0; i -= 4, fstripestart += fstripestep,
      dstripestart += dstripestep) {
        fvscanstart = fstripestart;
        dvscanstart = dstripestart;
        vscanlen = JAS_MIN(i, 4);
        for (j = width; j > 0; --j, ++fvscanstart, ++dvscanstart) {
            fp = fvscanstart;
            dp = dvscanstart;
            k = vscanlen;

            /* Process first sample in vertical scan. */
            jpc_refpass_step(fp, dp, poshalf, neghalf, mqdec,
              vcausalflag);
            if (--k <= 0) {
                continue;
            }
            fp += frowstep;
            dp += drowstep;

            /* Process second sample in vertical scan. */
            jpc_refpass_step(fp, dp, poshalf, neghalf, mqdec, 0);
            if (--k <= 0) {
                continue;
            }
            fp += frowstep;
            dp += drowstep;

            /* Process third sample in vertical scan. */
            jpc_refpass_step(fp, dp, poshalf, neghalf, mqdec, 0);
            if (--k <= 0) {
                continue;
            }
            fp += frowstep;
            dp += drowstep;

            /* Process fourth sample in vertical scan. */
            jpc_refpass_step(fp, dp, poshalf, neghalf, mqdec, 0);
        }
    }

    return 0;
}

#define jpc_rawrefpass_step(fp, dp, poshalf, neghalf, in, vcausalflag) \
{ \
    jpc_fix_t v; \
    jpc_fix_t t; \
    if (((*(fp)) & (JPC_SIG | JPC_VISIT)) == JPC_SIG) { \
        JPC_T1D_RAWGETBIT(in, v, "REF", "MAGREF"); \
        if (v < 0) { \
            return -1; \
        } \
        t = (v ? poshalf : neghalf); \
        *(dp) += (*(dp) < 0) ? (-t) : t; \
        *(fp) |= JPC_REFINE; \
    } \
}



static int
dec_rawrefpass(jpc_dec_t *dec, jpc_bitstream_t *in, int bitpos,
               int vcausalflag,
               jas_matrix_t *flags, jas_matrix_t *data)
{
    int i;
    int j;
    int k;
    int vscanlen;
    int width;
    int height;
    int one;
    int poshalf;
    int neghalf;
    jpc_fix_t *fp;
    int frowstep;
    int fstripestep;
    jpc_fix_t *fstripestart;
    jpc_fix_t *fvscanstart;
    jpc_fix_t *dp;
    int drowstep;
    int dstripestep;
    jpc_fix_t *dstripestart;
    jpc_fix_t *dvscanstart;

    width = jas_matrix_numcols(data);
    height = jas_matrix_numrows(data);
    frowstep = jas_matrix_rowstep(flags);
    drowstep = jas_matrix_rowstep(data);
    fstripestep = frowstep << 2;
    dstripestep = drowstep << 2;

    one = 1 << bitpos;
    poshalf = one >> 1;
    neghalf = (bitpos > 0) ? (-poshalf) : (-1);

    fstripestart = jas_matrix_getref(flags, 1, 1);
    dstripestart = jas_matrix_getref(data, 0, 0);
    for (i = height; i > 0; i -= 4, fstripestart += fstripestep,
      dstripestart += dstripestep) {
        fvscanstart = fstripestart;
        dvscanstart = dstripestart;
        vscanlen = JAS_MIN(i, 4);
        for (j = width; j > 0; --j, ++fvscanstart, ++dvscanstart) {
            fp = fvscanstart;
            dp = dvscanstart;
            k = vscanlen;

            /* Process first sample in vertical scan. */
            jpc_rawrefpass_step(fp, dp, poshalf, neghalf, in,
              vcausalflag);
            if (--k <= 0) {
                continue;
            }
            fp += frowstep;
            dp += drowstep;

            /* Process second sample in vertical scan. */
            jpc_rawrefpass_step(fp, dp, poshalf, neghalf, in, 0);
            if (--k <= 0) {
                continue;
            }
            fp += frowstep;
            dp += drowstep;

            /* Process third sample in vertical scan. */
            jpc_rawrefpass_step(fp, dp, poshalf, neghalf, in, 0);
            if (--k <= 0) {
                continue;
            }
            fp += frowstep;
            dp += drowstep;

            /* Process fourth sample in vertical scan. */
            jpc_rawrefpass_step(fp, dp, poshalf, neghalf, in, 0);
        }
    }
    return 0;
}



/******************************************************************************\
* Code for cleanup pass.
\******************************************************************************/

#define jpc_clnpass_step(f, fp, frowstep, dp, oneplushalf, orient, mqdec, flabel, plabel, vcausalflag) \
{ \
    int v; \
flabel \
    if (!((f) & (JPC_SIG | JPC_VISIT))) { \
        jpc_mqdec_setcurctx((mqdec), JPC_GETZCCTXNO((f), (orient))); \
        JPC_T1D_GETBIT((mqdec), v, "CLN", "ZC"); \
        if (v) { \
plabel \
            /* Coefficient is significant. */ \
            jpc_mqdec_setcurctx((mqdec), JPC_GETSCCTXNO(f)); \
            JPC_T1D_GETBIT((mqdec), v, "CLN", "SC"); \
            v ^= JPC_GETSPB(f); \
            *(dp) = (v) ? (-(oneplushalf)) : (oneplushalf); \
            JPC_UPDATEFLAGS4((fp), (frowstep), v, (vcausalflag)); \
            *(fp) |= JPC_SIG; \
        } \
    } \
    /* XXX - Is this correct?  Can aggregation cause some VISIT bits not to be reset?  Check. */ \
    *(fp) &= ~JPC_VISIT; \
}

static int
dec_clnpass(jpc_dec_t *dec, register jpc_mqdec_t *mqdec, int bitpos,
            int orient, int vcausalflag, int segsymflag, jas_matrix_t *flags,
            jas_matrix_t *data)
{
    int i;
    int j;
    int k;
    int vscanlen;
    int v;
    int half;
    int runlen;
    int f;
    int width;
    int height;
    int one;
    int oneplushalf;

    jpc_fix_t *fp;
    int frowstep;
    int fstripestep;
    jpc_fix_t *fstripestart;
    jpc_fix_t *fvscanstart;

    jpc_fix_t *dp;
    int drowstep;
    int dstripestep;
    jpc_fix_t *dstripestart;
    jpc_fix_t *dvscanstart;

    one = 1 << bitpos;
    half = one >> 1;
    oneplushalf = one | half;

    width = jas_matrix_numcols(data);
    height = jas_matrix_numrows(data);

    frowstep = jas_matrix_rowstep(flags);
    drowstep = jas_matrix_rowstep(data);
    fstripestep = frowstep << 2;
    dstripestep = drowstep << 2;

    fstripestart = jas_matrix_getref(flags, 1, 1);
    dstripestart = jas_matrix_getref(data, 0, 0);
    for (i = 0; i < height; i += 4, fstripestart += fstripestep,
      dstripestart += dstripestep) {
        fvscanstart = fstripestart;
        dvscanstart = dstripestart;
        vscanlen = JAS_MIN(4, height - i);
        for (j = width; j > 0; --j, ++fvscanstart, ++dvscanstart) {
            fp = fvscanstart;
            if (vscanlen >= 4 && (!((*fp) & (JPC_SIG | JPC_VISIT |
              JPC_OTHSIGMSK))) && (fp += frowstep, !((*fp) & (JPC_SIG |
              JPC_VISIT | JPC_OTHSIGMSK))) && (fp += frowstep, !((*fp) &
              (JPC_SIG | JPC_VISIT | JPC_OTHSIGMSK))) && (fp += frowstep,
              !((*fp) & (JPC_SIG | JPC_VISIT | JPC_OTHSIGMSK)))) {

                jpc_mqdec_setcurctx(mqdec, JPC_AGGCTXNO);
                JPC_T1D_GETBIT(mqdec, v, "CLN", "AGG");
                if (!v) {
                    continue;
                }
                jpc_mqdec_setcurctx(mqdec, JPC_UCTXNO);
                JPC_T1D_GETBITNOSKEW(mqdec, v, "CLN", "RL");
                runlen = v;
                JPC_T1D_GETBITNOSKEW(mqdec, v, "CLN", "RL");
                runlen = (runlen << 1) | v;
                f = *(fp = fvscanstart + frowstep * runlen);
                dp = dvscanstart + drowstep * runlen;
                k = vscanlen - runlen;
                switch (runlen) {
                case 0:
                    goto clnpass_partial0;
                    break;
                case 1:
                    goto clnpass_partial1;
                    break;
                case 2:
                    goto clnpass_partial2;
                    break;
                case 3:
                    goto clnpass_partial3;
                    break;
                }
            } else {
                f = *(fp = fvscanstart);
                dp = dvscanstart;
                k = vscanlen;
                goto clnpass_full0;
            }

            /* Process first sample in vertical scan. */
            jpc_clnpass_step(f, fp, frowstep, dp, oneplushalf, orient,
              mqdec, clnpass_full0:, clnpass_partial0:,
              vcausalflag);
            if (--k <= 0) {
                continue;
            }
            fp += frowstep;
            dp += drowstep;

            /* Process second sample in vertical scan. */
            f = *fp;
            jpc_clnpass_step(f, fp, frowstep, dp, oneplushalf, orient,
                mqdec, ;, clnpass_partial1:, 0);
            if (--k <= 0) {
                continue;
            }
            fp += frowstep;
            dp += drowstep;

            /* Process third sample in vertical scan. */
            f = *fp;
            jpc_clnpass_step(f, fp, frowstep, dp, oneplushalf, orient,
                mqdec, ;, clnpass_partial2:, 0);
            if (--k <= 0) {
                continue;
            }
            fp += frowstep;
            dp += drowstep;

            /* Process fourth sample in vertical scan. */
            f = *fp;
            jpc_clnpass_step(f, fp, frowstep, dp, oneplushalf, orient,
                mqdec, ;, clnpass_partial3:, 0);
        }
    }

    if (segsymflag) {
        int segsymval;
        segsymval = 0;
        jpc_mqdec_setcurctx(mqdec, JPC_UCTXNO);
        JPC_T1D_GETBITNOSKEW(mqdec, v, "CLN", "SEGSYM");
        segsymval = (segsymval << 1) | (v & 1);
        JPC_T1D_GETBITNOSKEW(mqdec, v, "CLN", "SEGSYM");
        segsymval = (segsymval << 1) | (v & 1);
        JPC_T1D_GETBITNOSKEW(mqdec, v, "CLN", "SEGSYM");
        segsymval = (segsymval << 1) | (v & 1);
        JPC_T1D_GETBITNOSKEW(mqdec, v, "CLN", "SEGSYM");
        segsymval = (segsymval << 1) | (v & 1);
        if (segsymval != 0xa) {
            fprintf(stderr, "warning: bad segmentation symbol\n");
        }
    }

    return 0;
}



static void
jpc_dec_decodecblk(jpc_dec_t *       const dec,
                   jpc_dec_tile_t *  const tile,
                   jpc_dec_tcomp_t * const tcomp,
                   jpc_dec_band_t *  const band,
                   jpc_dec_cblk_t *  const cblk,
                   int               const dopartial,
                   int               const maxlyrs,
                   const char **     const errorP) {

    jpc_dec_seg_t *seg;
    int i;
    int bpno;
    int passtype;
    int ret;
    int compno;
    int filldata;
    int fillmask;
    jpc_dec_ccp_t *ccp;

    compno = tcomp - tile->tcomps;

    if (!cblk->flags) {
        /* Note: matrix is assumed to be zeroed */
        unsigned int const nrow = jas_matrix_numrows(cblk->data) + 2;
        unsigned int const ncol = jas_matrix_numcols(cblk->data) + 2;
        cblk->flags = jas_matrix_create(nrow, ncol);
        if (!cblk->flags) {
            pm_asprintf(errorP, "Out of memory allocating a flags matrix of "
                        "%u rows by %u columns", nrow, ncol);
            return;
        }
    }

    seg = cblk->segs.head;
    while (seg && (seg != cblk->curseg || dopartial) && (maxlyrs < 0 ||
      seg->lyrno < maxlyrs)) {
        assert(seg->numpasses >= seg->maxpasses || dopartial);
        assert(seg->stream);
        jas_stream_rewind(seg->stream);
        jas_stream_setrwcount(seg->stream, 0);
        if (seg->type == JPC_SEG_MQ) {
            if (!cblk->mqdec) {
                if (!(cblk->mqdec = jpc_mqdec_create(JPC_NUMCTXS, 0))) {
                    pm_asprintf(errorP, "jpc_mqdec_create failed");
                    return;
                }
                jpc_mqdec_setctxs(cblk->mqdec, JPC_NUMCTXS, jpc_mqctxs);
            }
            jpc_mqdec_setinput(cblk->mqdec, seg->stream);
            jpc_mqdec_init(cblk->mqdec);
        } else {
            assert(seg->type == JPC_SEG_RAW);
            if (!cblk->nulldec) {
                if (!(cblk->nulldec = jpc_bitstream_sopen(seg->stream, "r"))) {
                    assert(0);
                }
            }
        }


        for (i = 0; i < seg->numpasses; ++i) {
            if (cblk->numimsbs > band->numbps) {
                ccp = &tile->cp->ccps[compno];
                if (ccp->roishift <= 0) {
                    fprintf(stderr, "warning: corrupt code stream\n");
                } else {
                    if (cblk->numimsbs < ccp->roishift - band->numbps) {
                        fprintf(stderr, "warning: corrupt code stream\n");
                    }
                }
            }
            bpno = band->roishift + band->numbps - 1 - (cblk->numimsbs +
              (seg->passno + i - cblk->firstpassno + 2) / 3);
            if (bpno < 0) {
                goto exit;
            }
#if 1
            passtype = (seg->passno + i + 2) % 3;
#else
            passtype = JPC_PASSTYPE(seg->passno + i + 2);
#endif
            assert(bpno >= 0 && bpno < 31);
            switch (passtype) {
            case JPC_SIGPASS:
                ret = (seg->type == JPC_SEG_MQ) ? dec_sigpass(dec,
                  cblk->mqdec, bpno, band->orient,
                  (tile->cp->ccps[compno].cblkctx & JPC_COX_VSC) != 0,
                  cblk->flags, cblk->data) :
                  dec_rawsigpass(dec, cblk->nulldec, bpno,
                  (tile->cp->ccps[compno].cblkctx & JPC_COX_VSC) != 0,
                  cblk->flags, cblk->data);
                break;
            case JPC_REFPASS:
                ret = (seg->type == JPC_SEG_MQ) ?
                  dec_refpass(dec, cblk->mqdec, bpno,
                  (tile->cp->ccps[compno].cblkctx & JPC_COX_VSC) != 0,
                  cblk->flags, cblk->data) :
                  dec_rawrefpass(dec, cblk->nulldec, bpno,
                  (tile->cp->ccps[compno].cblkctx & JPC_COX_VSC) != 0,
                  cblk->flags, cblk->data);
                break;
            case JPC_CLNPASS:
                assert(seg->type == JPC_SEG_MQ);
                ret = dec_clnpass(dec, cblk->mqdec, bpno,
                  band->orient, (tile->cp->ccps[compno].cblkctx &
                  JPC_COX_VSC) != 0, (tile->cp->ccps[compno].cblkctx &
                  JPC_COX_SEGSYM) != 0, cblk->flags,
                  cblk->data);
                break;
            default:
                ret = -1;
                break;
            }
            /* Do we need to reset after each coding pass? */
            if (tile->cp->ccps[compno].cblkctx & JPC_COX_RESET) {
                jpc_mqdec_setctxs(cblk->mqdec, JPC_NUMCTXS, jpc_mqctxs);
            }

            if (ret) {
                pm_asprintf(errorP,
                            "coding pass failed passtype=%d segtype=%d",
                            passtype, seg->type);
                return;
            }

        }

        if (seg->type == JPC_SEG_MQ) {
            /* Note: dont destroy mq decoder because context info will be
               lost
            */
        } else {
            assert(seg->type == JPC_SEG_RAW);
            if (tile->cp->ccps[compno].cblkctx & JPC_COX_PTERM) {
                fillmask = 0x7f;
                filldata = 0x2a;
            } else {
                fillmask = 0;
                filldata = 0;
            }
            if ((ret = jpc_bitstream_inalign(cblk->nulldec, fillmask,
              filldata)) < 0) {
                pm_asprintf(errorP, "jpc_bitstream_inalign failed");
                return;
            } else if (ret > 0) {
                pm_message("warning: bad termination pattern detected");
            }
            jpc_bitstream_close(cblk->nulldec);
            cblk->nulldec = 0;
        }

        cblk->curseg = seg->next;
        jpc_seglist_remove(&cblk->segs, seg);
        jpc_seg_destroy(seg);
        seg = cblk->curseg;
    }

    assert(dopartial ? (!cblk->curseg) : 1);

exit:
    *errorP = NULL;
}



void
jpc_dec_decodecblks(jpc_dec_t *      const decP,
                    jpc_dec_tile_t * const tileP,
                    const char **    const errorP) {
/*----------------------------------------------------------------------------
  Decode all of the code blocks for a particular tile
-----------------------------------------------------------------------------*/
    unsigned int compcnt;
    jpc_dec_tcomp_t * tcompP;

    for (compcnt = 0, tcompP = tileP->tcomps;
         compcnt < decP->numcomps;
         ++compcnt, ++tcompP) {

        unsigned int rlvlcnt;
        jpc_dec_rlvl_t * rlvlP;

        for (rlvlcnt = 0, rlvlP = tcompP->rlvls;
             rlvlcnt < tcompP->numrlvls;
             ++rlvlcnt, ++rlvlP) {

            if (rlvlP->bands) {
                unsigned int bandcnt;
                jpc_dec_band_t * bandP;

                for (bandcnt = 0, bandP = rlvlP->bands;
                     bandcnt < rlvlP->numbands;
                     ++bandcnt, ++bandP) {

                    if (bandP->data) {
                        unsigned int prccnt;
                        jpc_dec_prc_t * prcP;

                        for (prccnt = 0, prcP = bandP->prcs;
                             prccnt < rlvlP->numprcs;
                             ++prccnt, ++prcP) {

                            if (prcP->cblks) {
                                unsigned int cblkcnt;
                                jpc_dec_cblk_t * cblkP;

                                for (cblkcnt = 0, cblkP = prcP->cblks;
                                     cblkcnt < prcP->numcblks;
                                     ++cblkcnt, ++cblkP) {

                                    const char * error;

                                    jpc_dec_decodecblk(decP, tileP, tcompP,
                                                       bandP, cblkP, 1,
                                                       JPC_MAXLYRS, &error);
                                    if (error) {
                                        pm_asprintf(errorP,
                                                    "jpc_dec_decodecblk "
                                                    "failed on component %u, "
                                                    "resolution level %u, "
                                                    "band %u, prc %u, "
                                                    "code block %u.  %s",
                                                    compcnt, rlvlcnt, bandcnt,
                                                    prccnt, cblkcnt, error);
                                        pm_strfree(error);
                                        return;
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
    *errorP = NULL;
}

