/* 
 * video.c --
 *
 *      This file contains C code that implements the video decoder model.
 *
 */

/*
 * Copyright (c) 1995 The Regents of the University of California.
 * All rights reserved.  
 *
 * Permission to use, copy, modify, and distribute this software and its
 * documentation for any purpose, without fee, and without written agreement is
 * hereby granted, provided that the above copyright notice and the following
 * two paragraphs appear in all copies of this software.
 *
 * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
 * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF
 * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
 * AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
 * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO
 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
 */

/*
 * Portions of this software Copyright (c) 1995 Brown University.
 * All rights reserved.
 * 
 * Permission to use, copy, modify, and distribute this software and its
 * documentation for any purpose, without fee, and without written agreement
 * is hereby granted, provided that the above copyright notice and the
 * following two paragraphs appear in all copies of this software.
 * 
 * IN NO EVENT SHALL BROWN UNIVERSITY BE LIABLE TO ANY PARTY FOR
 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
 * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF BROWN
 * UNIVERSITY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 * 
 * BROWN UNIVERSITY SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
 * PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS"
 * BASIS, AND BROWN UNIVERSITY HAS NO OBLIGATION TO PROVIDE MAINTENANCE,
 * SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
 */

/* We use FULL_COLOR_DITHER code for SMPEG */
#define DISABLE_DITHER

/* Correct bad motion information */
#define LOOSE_MPEG

/* If hardware accelerated, prevent use of dither code */
#ifdef USE_ATI
#ifndef DISABLE_DITHER
#define DISABLE_DITHER
#endif
#endif

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

#include "decoders.h"
#include "video.h"
#include "util.h"
#include "proto.h"

#ifdef USE_ATI
#include "vhar128.h"
#endif

/* Declarations of functions. */
#ifndef USE_ATI
static void ReconIMBlock( VidStream*, int bnum );
static void ReconPMBlock( VidStream*, int bnum,
        int recon_right_for, int recon_down_for, int zflag );
static void ReconBMBlock( VidStream*, int bnum,
        int recon_right_back, int recon_down_back, int zflag );
static void ReconBiMBlock( VidStream*, int bnum,
        int recon_right_for, int recon_down_for, int recon_right_back,
        int recon_down_back, int zflag );
static void ReconSkippedBlock( unsigned char *source, unsigned char *dest,
      int row, int col, int row_size, int right, int down,
      int right_half, int down_half, int width );
#endif /* USE_ATI */
static void DoPictureDisplay( VidStream* );
static int ParseSeqHead( VidStream* );
static int ParseGOP( VidStream* );
static int ParsePicture( VidStream*, TimeStamp );
static int ParseSlice( VidStream* );
static int ParseMacroBlock( VidStream* );
static void ProcessSkippedPFrameMBlocks( VidStream* );
static void ProcessSkippedBFrameMBlocks( VidStream* );

/*
   Changes to make the code reentrant:
     de-globalized: totNumFrames, realTimeStart, matched_depth, ditherType,
       curBits, ReconPMBlock statics, first, [lc]max[xy], ditherFlags,
       vid_stream, Parse_done, seekValue, ReadPack static, sys_layer,
       bitOffset, bitLength, bitBuffer, curVidStream,
     X globals to xinfo (window, et al)
     use vid_stream->film_has_ended instead of FilmState
     lookup tables only initialized once, global as possible
        (default_intra_matrix, zigzag, zigzag_direct, scan)
     get rid of setjmp, long jmp
   Additional changes:
     if DISABLE_DITHER defined then do not compile dithering code
   -lsh@cs.brown.edu (Loring Holden)
 */

/* Macro for returning 1 if num is positive, -1 if negative, 0 if 0. */

#define Sign(num) ((num > 0) ? 1 : ((num == 0) ? 0 : -1))

/* Set up array for fast conversion from zig zag order to row/column
   coordinates.
*/
const int zigzag[64][2] =
{
    { 0, 0 }, { 1, 0 }, { 0, 1 }, { 0, 2 }, { 1, 1 },
    { 2, 0 }, { 3, 0 }, { 2, 1 }, { 1, 2 }, { 0, 3 },
    { 0, 4 }, { 1, 3 }, { 2, 2 }, { 3, 1 }, { 4, 0 },
    { 5, 0 }, { 4, 1 }, { 3, 2 }, { 2, 3 }, { 1, 4 },
    { 0, 5 }, { 0, 6 }, { 1, 5 }, { 2, 4 }, { 3, 3 },
    { 4, 2 }, { 5, 1 }, { 6, 0 }, { 7, 0 }, { 6, 1 },
    { 5, 2 }, { 4, 3 }, { 3, 4 }, { 2, 5 }, { 1, 6 },
    { 0, 7 }, { 1, 7 }, { 2, 6 }, { 3, 5 }, { 4, 4 },
    { 5, 3 }, { 6, 2 }, { 7, 1 }, { 7, 2 }, { 6, 3 },
    { 5, 4 }, { 4, 5 }, { 3, 6 }, { 2, 7 }, { 3, 7 },
    { 4, 6 }, { 5, 5 }, { 6, 4 }, { 7, 3 }, { 7, 4 },
    { 6, 5 }, { 5, 6 }, { 4, 7 }, { 5, 7 }, { 6, 6 },
    { 7, 5 }, { 7, 6 }, { 6, 7 }, { 7, 7 }
};

/* Set up array for fast conversion from row/column coordinates to
   zig zag order.
*/
const int scan[8][8] =
{
  { 0, 1, 5, 6, 14, 15, 27, 28 },
  { 2, 4, 7, 13, 16, 26, 29, 42 },
  { 3, 8, 12, 17, 25, 30, 41, 43 },
  { 9, 11, 18, 24, 31, 40, 44, 53 },
  { 10, 19, 23, 32, 39, 45, 52, 54 },
  { 20, 22, 33, 38, 46, 51, 55, 60 },
  { 21, 34, 37, 47, 50, 56, 59, 61 },
  { 35, 36, 48, 49, 57, 58, 62, 63 }
};

/* Max lum, chrom indices for illegal block checking. */


#ifdef USE_CROP_TABLE
/*
 * We use a lookup table to make sure values stay in the 0..255 range.
 * Since this is cropping (ie, x = (x < 0)?0:(x>255)?255:x; ), wee call this
 * table the "crop table".
 * MAX_NEG_CROP is the maximum neg/pos value we can handle.
 */

#define MAX_NEG_CROP 2048
#define NUM_CROP_ENTRIES (2048+2*MAX_NEG_CROP)
static unsigned char cropTbl[NUM_CROP_ENTRIES];

#define crop(x) cm[x]
#else
static inline unsigned char crop(int x)
{
  if(x<=0)
    return 0;
  if(x>=255)
    return 255;
  return x;
}
#endif /* USE_CROP_TABLE */

/*
  The following accounts for time and size  spent in various parsing acitivites
  if ANALYSIS has been defined.
*/

#ifdef ANALYSIS


unsigned int bitCount = 0;

/* #define SHOWMB_FLAG */
/* #define SHOWEACHFLAG */

typedef struct {
  int frametype;
  unsigned int totsize;
  unsigned int number;
  unsigned int i_mbsize;
  unsigned int p_mbsize;
  unsigned int b_mbsize;
  unsigned int bi_mbsize;
  unsigned int i_mbnum;
  unsigned int p_mbnum;
  unsigned int b_mbnum;
  unsigned int bi_mbnum;
  unsigned int i_mbcbp[64];
  unsigned int p_mbcbp[64];
  unsigned int b_mbcbp[64];
  unsigned int bi_mbcbp[64];
  unsigned int i_mbcoeff[64];
  unsigned int p_mbcoeff[64];
  unsigned int b_mbcoeff[64];
  unsigned int bi_mbcoeff[64];
  double tottime;
} Statval;

Statval stat_a[4];
unsigned int pictureSizeCount;
unsigned int mbSizeCount;
unsigned int *mbCBPPtr, *mbCoeffPtr, *mbSizePtr;
unsigned int cacheHit[8][8];
unsigned int cacheMiss[8][8];

static void
init_stat_struct(astat)
  Statval *astat;
{
  int j;

  astat->frametype = 0;
  astat->totsize = 0;
  astat->number = 0;
  astat->i_mbsize = 0;
  astat->p_mbsize = 0;
  astat->b_mbsize = 0;
  astat->bi_mbsize = 0;
  astat->i_mbnum = 0;
  astat->p_mbnum = 0;
  astat->b_mbnum = 0;
  astat->bi_mbnum = 0;

  for (j = 0; j < 64; j++) {

    astat->i_mbcbp[j] = 0;
    astat->p_mbcbp[j] = 0;
    astat->b_mbcbp[j] = 0;
    astat->bi_mbcbp[j] = 0;
    astat->i_mbcoeff[j] = 0;
    astat->p_mbcoeff[j] = 0;
    astat->b_mbcoeff[j] = 0;
    astat->bi_mbcoeff[j] = 0;
  }
  astat->tottime = 0.0;
}

void
init_stats()
{
  int i, j;

  for (i = 0; i < 4; i++) {
    init_stat_struct(&(stat_a[i]));
    stat_a[i].frametype = i;
  }

  for (i = 0; i < 8; i++) {
    for (j = 0; j < 8; j++) {
      cacheHit[i][j] = 0;
      cacheMiss[i][j] = 0;
    }
  }

  bitCount = 0;
}

static void
PrintOneStat()
{
  int i;

  printf("\n");
  switch (stat_a[0].frametype) {
  case I_TYPE:
    printf("I FRAME\n");
    break;
  case P_TYPE:
    printf("P FRAME\n");
    break;
  case B_TYPE:
    printf("B FRAME\n");
    break;
  }

  printf("Size: %d bytes + %d bits\n", stat_a[0].totsize / 8, stat_a[0].totsize % 8);
  if (stat_a[0].i_mbnum > 0) {
    printf("\tI Macro Block Stats:\n");
    printf("\t%d I Macroblocks\n", stat_a[0].i_mbnum);
    printf("\tAvg. Size: %d bytes + %d bits\n",
           stat_a[0].i_mbsize / (8 * stat_a[0].i_mbnum),
           (stat_a[0].i_mbsize * stat_a[0].i_mbnum) % 8);
    printf("\t\tCoded Block Pattern Histogram:\n");
    for (i = 0; i < 64; i += 8) {
      printf("\t%.6d %.6d %.6d %.6d %.6d %.6d %.6d %.6d\n", stat_a[0].i_mbcbp[i],
             stat_a[0].i_mbcbp[i + 1], stat_a[0].i_mbcbp[i + 2], stat_a[0].i_mbcbp[i + 3],
             stat_a[0].i_mbcbp[i + 4], stat_a[0].i_mbcbp[i + 5], stat_a[0].i_mbcbp[i + 6],
             stat_a[0].i_mbcbp[i + 7]);
    }
    printf("\n\t\tNumber of Coefficients/Block Histogram:\n");
    for (i = 0; i < 64; i += 8) {
      printf("\t%.6d %.6d %.6d %.6d %.6d %.6d %.6d %.6d\n", stat_a[0].i_mbcoeff[i],
             stat_a[0].i_mbcoeff[i + 1], stat_a[0].i_mbcoeff[i + 2],
             stat_a[0].i_mbcoeff[i + 3], stat_a[0].i_mbcoeff[i + 4],
             stat_a[0].i_mbcoeff[i + 5], stat_a[0].i_mbcoeff[i + 6],
             stat_a[0].i_mbcoeff[i + 7]);
    }
  }
  if (stat_a[0].p_mbnum > 0) {
    printf("\tP Macro Block Stats:\n");
    printf("\t%d P Macroblocks\n", stat_a[0].p_mbnum);
    printf("\tAvg. Size: %d bytes + %d bits\n",
           stat_a[0].p_mbsize / (8 * stat_a[0].p_mbnum),
           (stat_a[0].p_mbsize / stat_a[0].p_mbnum) % 8);
    printf("\t\tCoded Block Pattern Histogram:\n");
    for (i = 0; i < 64; i += 8) {
      printf("\t%.6d %.6d %.6d %.6d %.6d %.6d %.6d %.6d\n", stat_a[0].p_mbcbp[i],
             stat_a[0].p_mbcbp[i + 1], stat_a[0].p_mbcbp[i + 2], stat_a[0].p_mbcbp[i + 3],
             stat_a[0].p_mbcbp[i + 4], stat_a[0].p_mbcbp[i + 5], stat_a[0].p_mbcbp[i + 6],
             stat_a[0].p_mbcbp[i + 7]);
    }
    printf("\n\t\tNumber of Coefficients/Block Histogram:\n");
    for (i = 0; i < 64; i += 8) {
      printf("\t%.6d %.6d %.6d %.6d %.6d %.6d %.6d %.6d\n", stat_a[0].p_mbcoeff[i],
             stat_a[0].p_mbcoeff[i + 1], stat_a[0].p_mbcoeff[i + 2],
             stat_a[0].p_mbcoeff[i + 3], stat_a[0].p_mbcoeff[i + 4],
             stat_a[0].p_mbcoeff[i + 5], stat_a[0].p_mbcoeff[i + 6],
             stat_a[0].p_mbcoeff[i + 7]);
    }
  }
  if (stat_a[0].b_mbnum > 0) {
    printf("\tB Macro Block Stats:\n");
    printf("\t%d B Macroblocks\n", stat_a[0].b_mbnum);
    printf("\tAvg. Size: %d bytes + %d bits\n",
           stat_a[0].b_mbsize / (8 * stat_a[0].b_mbnum),
           (stat_a[0].b_mbsize / stat_a[0].b_mbnum) % 8);
    printf("\t\tCoded Block Pattern Histogram:\n");
    for (i = 0; i < 64; i += 8) {
      printf("\t%.6d %.6d %.6d %.6d %.6d %.6d %.6d %.6d\n", stat_a[0].b_mbcbp[i],
             stat_a[0].b_mbcbp[i + 1], stat_a[0].b_mbcbp[i + 2], stat_a[0].b_mbcbp[i + 3],
             stat_a[0].b_mbcbp[i + 4], stat_a[0].b_mbcbp[i + 5], stat_a[0].b_mbcbp[i + 6],
             stat_a[0].b_mbcbp[i + 7]);
    }
    printf("\n\t\tNumber of Coefficients/Block Histogram:\n");
    for (i = 0; i < 64; i += 8) {
      printf("\t%.6d %.6d %.6d %.6d %.6d %.6d %.6d %.6d\n", stat_a[0].b_mbcoeff[i],
             stat_a[0].b_mbcoeff[i + 1], stat_a[0].b_mbcoeff[i + 2],
             stat_a[0].b_mbcoeff[i + 3], stat_a[0].b_mbcoeff[i + 4],
             stat_a[0].b_mbcoeff[i + 5], stat_a[0].b_mbcoeff[i + 6],
             stat_a[0].b_mbcoeff[i + 7]);
    }
  }
  if (stat_a[0].bi_mbnum > 0) {
    printf("\tBi Macro Block Stats:\n");
    printf("\t%d Bi Macroblocks\n", stat_a[0].bi_mbnum);
    printf("\tAvg. Size: %d bytes + %d bits\n",
           stat_a[0].bi_mbsize / (8 * stat_a[0].bi_mbnum),
           (stat_a[0].bi_mbsize * stat_a[0].bi_mbnum) % 8);
    printf("\t\tCoded Block Pattern Histogram:\n");
    for (i = 0; i < 64; i += 8) {
      printf("\t%.6d %.6d %.6d %.6d %.6d %.6d %.6d %.6d\n", stat_a[0].bi_mbcbp[i],
             stat_a[0].bi_mbcbp[i + 1], stat_a[0].bi_mbcbp[i + 2], stat_a[0].bi_mbcbp[i + 3],
             stat_a[0].bi_mbcbp[i + 4], stat_a[0].bi_mbcbp[i + 5], stat_a[0].bi_mbcbp[i + 6],
             stat_a[0].bi_mbcbp[i + 7]);
    }
    printf("\n\t\tNumber of Coefficients/Block Histogram:\n");
    for (i = 0; i < 64; i += 8) {
      printf("\t%.6d %.6d %.6d %.6d %.6d %.6d %.6d %.6d\n", stat_a[0].bi_mbcoeff[i],
             stat_a[0].bi_mbcoeff[i + 1], stat_a[0].bi_mbcoeff[i + 2],
             stat_a[0].bi_mbcoeff[i + 3], stat_a[0].bi_mbcoeff[i + 4],
             stat_a[0].bi_mbcoeff[i + 5], stat_a[0].bi_mbcoeff[i + 6],
             stat_a[0].bi_mbcoeff[i + 7]);
    }
  }
  printf("\nTime to Decode: %g secs.\n", stat_a[0].tottime);
  printf("****************\n");
}

void
PrintAllStats(vid_stream)
VidStream *vid_stream;
{
  int i, j;
  unsigned int supertot, supernum;
  double supertime;

  printf("\n");
  printf("General Info: \n");
  printf("Width: %d\nHeight: %d\n", vid_stream->mb_width * 16, vid_stream->mb_height * 16);

  for (i = 1; i < 4; i++) {

    if (stat_a[i].number == 0)
      continue;

    switch (i) {
    case 1:
      printf("I FRAMES\n");
      break;
    case 2:
      printf("P FRAMES\n");
      break;
    case 3:
      printf("B FRAMES\n");
      break;
    }

    printf("Number: %d\n", stat_a[i].number);
    printf("Avg. Size: %d bytes + %d bits\n",
           stat_a[i].totsize / (8 * stat_a[i].number), (stat_a[i].totsize / stat_a[i].number) % 8);
    if (stat_a[i].i_mbnum > 0) {
      printf("\tI Macro Block Stats:\n");
      printf("\t%d I Macroblocks\n", stat_a[i].i_mbnum);
      printf("\tAvg. Size: %d bytes + %d bits\n",
             stat_a[i].i_mbsize / (8 * stat_a[i].i_mbnum),
             (stat_a[i].i_mbsize / stat_a[i].i_mbnum) % 8);
      printf("\t\tCoded Block Pattern Histogram:\n");
      for (j = 0; j < 64; j += 8) {
        printf("\t%.6d %.6d %.6d %.6d %.6d %.6d %.6d %.6d\n", stat_a[i].i_mbcbp[j],
               stat_a[i].i_mbcbp[j + 1], stat_a[i].i_mbcbp[j + 2], stat_a[i].i_mbcbp[j + 3],
               stat_a[i].i_mbcbp[j + 4], stat_a[i].i_mbcbp[j + 5], stat_a[i].i_mbcbp[j + 6],
               stat_a[i].i_mbcbp[j + 7]);
      }
      printf("\n\t\tNumber of Coefficients/Block Histogram:\n");
      for (j = 0; j < 64; j += 8) {
        printf("\t%.6d %.6d %.6d %.6d %.6d %.6d %.6d %.6d\n", stat_a[i].i_mbcoeff[j],
               stat_a[i].i_mbcoeff[j + 1], stat_a[i].i_mbcoeff[j + 2],
               stat_a[i].i_mbcoeff[j + 3], stat_a[i].i_mbcoeff[j + 4],
               stat_a[i].i_mbcoeff[j + 5], stat_a[i].i_mbcoeff[j + 6],
               stat_a[i].i_mbcoeff[j + 7]);
      }
    }
    if (stat_a[i].p_mbnum > 0) {
      printf("\tP Macro Block Stats:\n");
      printf("\t%d P Macroblocks\n", stat_a[i].p_mbnum);
      printf("\tAvg. Size: %d bytes + %d bits\n",
             stat_a[i].p_mbsize / (8 * stat_a[i].p_mbnum),
             (stat_a[i].p_mbsize / stat_a[i].p_mbnum) % 8);
      printf("\t\tCoded Block Pattern Histogram:\n");
      for (j = 0; j < 64; j += 8) {
        printf("\t%.6d %.6d %.6d %.6d %.6d %.6d %.6d %.6d\n", stat_a[i].p_mbcbp[j],
               stat_a[i].p_mbcbp[j + 1], stat_a[i].p_mbcbp[j + 2], stat_a[i].p_mbcbp[j + 3],
               stat_a[i].p_mbcbp[j + 4], stat_a[i].p_mbcbp[j + 5], stat_a[i].p_mbcbp[j + 6],
               stat_a[i].p_mbcbp[j + 7]);
      }
      printf("\n\t\tNumber of Coefficients/Block Histogram:\n");
      for (j = 0; j < 64; j += 8) {
        printf("\t%.6d %.6d %.6d %.6d %.6d %.6d %.6d %.6d\n", stat_a[i].p_mbcoeff[j],
               stat_a[i].p_mbcoeff[j + 1], stat_a[i].p_mbcoeff[j + 2],
               stat_a[i].p_mbcoeff[j + 3], stat_a[i].p_mbcoeff[j + 4],
               stat_a[i].p_mbcoeff[j + 5], stat_a[i].p_mbcoeff[j + 6],
               stat_a[i].p_mbcoeff[j + 7]);
      }
    }
    if (stat_a[i].b_mbnum > 0) {
      printf("\tB Macro Block Stats:\n");
      printf("\t%d B Macroblocks\n", stat_a[i].b_mbnum);
      printf("\tAvg. Size: %d bytes + %d bits\n",
             stat_a[i].b_mbsize / (8 * stat_a[i].b_mbnum),
             (stat_a[i].b_mbsize * stat_a[i].b_mbnum) % 8);
      printf("\t\tCoded Block Pattern Histogram:\n");
      for (j = 0; j < 64; j += 8) {
        printf("\t%.6d %.6d %.6d %.6d %.6d %.6d %.6d %.6d\n", stat_a[i].b_mbcbp[j],
               stat_a[i].b_mbcbp[j + 1], stat_a[i].b_mbcbp[j + 2], stat_a[i].b_mbcbp[j + 3],
               stat_a[i].b_mbcbp[j + 4], stat_a[i].b_mbcbp[j + 5], stat_a[i].b_mbcbp[j + 6],
               stat_a[i].b_mbcbp[j + 7]);
      }
      printf("\n\t\tNumber of Coefficients/Block Histogram:\n");
      for (j = 0; j < 64; j += 8) {
        printf("\t%.6d %.6d %.6d %.6d %.6d %.6d %.6d %.6d\n", stat_a[i].b_mbcoeff[j],
               stat_a[i].b_mbcoeff[j + 1], stat_a[i].b_mbcoeff[j + 2],
               stat_a[i].b_mbcoeff[j + 3], stat_a[i].b_mbcoeff[j + 4],
               stat_a[i].b_mbcoeff[j + 5], stat_a[i].b_mbcoeff[j + 6],
               stat_a[i].b_mbcoeff[j + 7]);
      }
    }
    if (stat_a[i].bi_mbnum > 0) {
      printf("\tBi Macro Block Stats:\n");
      printf("\t%d Bi Macroblocks\n", stat_a[i].bi_mbnum);
      printf("\tAvg. Size: %d bytes + %d bits\n",
             stat_a[i].bi_mbsize / (8 * stat_a[i].bi_mbnum),
             (stat_a[i].bi_mbsize * stat_a[i].bi_mbnum) % 8);
      printf("\t\tCoded Block Pattern Histogram:\n");
      for (j = 0; j < 64; j += 8) {
        printf("\t%.6d %.6d %.6d %.6d %.6d %.6d %.6d %.6d\n", stat_a[i].bi_mbcbp[j],
               stat_a[i].bi_mbcbp[j + 1], stat_a[i].bi_mbcbp[j + 2], stat_a[i].bi_mbcbp[j + 3],
               stat_a[i].bi_mbcbp[j + 4], stat_a[i].bi_mbcbp[j + 5], stat_a[i].bi_mbcbp[j + 6],
               stat_a[i].bi_mbcbp[j + 7]);
      }
      printf("\n\t\tNumber of Coefficients/Block Histogram:\n");
      for (j = 0; j < 64; j += 8) {
        printf("\t%.6d %.6d %.6d %.6d %.6d %.6d %.6d %.6d\n", stat_a[i].bi_mbcoeff[j],
               stat_a[i].bi_mbcoeff[j + 1], stat_a[i].bi_mbcoeff[j + 2],
               stat_a[i].bi_mbcoeff[j + 3], stat_a[i].bi_mbcoeff[j + 4],
               stat_a[i].bi_mbcoeff[j + 5], stat_a[i].bi_mbcoeff[j + 6],
               stat_a[i].bi_mbcoeff[j + 7]);
      }
    }
    printf("\nAvg. Time to Decode: %f secs.\n",
           (stat_a[i].tottime / ((double) stat_a[i].number)));
    printf("\n");
    printf("*************************\n\n");
  }

  supertot = stat_a[1].totsize + stat_a[2].totsize + stat_a[3].totsize;
  supernum = stat_a[1].number + stat_a[2].number + stat_a[3].number;
  supertime = stat_a[1].tottime + stat_a[2].tottime + stat_a[3].tottime;

  printf("Total Number of Frames: %d\n", supernum);
  printf("Avg Frame Size: %d bytes %d bits\n",
         supertot / (8 * supernum), (supertot / supernum) % 8);
  printf("Total Time Decoding: %g secs.\n", supertime);
  printf("Avg Decoding Time/Frame: %g secs.\n", supertime / ((double) supernum));
  printf("Avg Decoding Frames/Sec: %g secs.\n", ((double) supernum) / supertime);
  printf("\n");

  printf("Cache Hits/Miss\n");
  for (i = 0; i < 8; i++) {
    printf("%.6d/%.6d\t%.6d/%.6d\t%.6d/%.6d\t%.6d/%.6d\n",
           cacheHit[i][0], cacheMiss[i][0], cacheHit[i][1], cacheMiss[i][1],
           cacheHit[i][2], cacheMiss[i][2], cacheHit[i][3], cacheMiss[i][3]);
    printf("%.6d/%.6d\t%.6d/%.6d\t%.6d/%.6d\t%.6d/%.6d\n",
           cacheHit[i][4], cacheMiss[i][4], cacheHit[i][5], cacheMiss[i][5],
           cacheHit[i][6], cacheMiss[i][6], cacheHit[i][7], cacheMiss[i][7]);
  }

}

static void
CollectStats()
{
  int i, j;

  i = stat_a[0].frametype;

  stat_a[i].totsize += stat_a[0].totsize;
  stat_a[i].number += stat_a[0].number;
  stat_a[i].i_mbsize += stat_a[0].i_mbsize;
  stat_a[i].p_mbsize += stat_a[0].p_mbsize;
  stat_a[i].b_mbsize += stat_a[0].b_mbsize;
  stat_a[i].bi_mbsize += stat_a[0].bi_mbsize;
  stat_a[i].i_mbnum += stat_a[0].i_mbnum;
  stat_a[i].p_mbnum += stat_a[0].p_mbnum;
  stat_a[i].b_mbnum += stat_a[0].b_mbnum;
  stat_a[i].bi_mbnum += stat_a[0].bi_mbnum;

  for (j = 0; j < 64; j++) {

    stat_a[i].i_mbcbp[j] += stat_a[0].i_mbcbp[j];
    stat_a[i].p_mbcbp[j] += stat_a[0].p_mbcbp[j];
    stat_a[i].b_mbcbp[j] += stat_a[0].b_mbcbp[j];
    stat_a[i].bi_mbcbp[j] += stat_a[0].bi_mbcbp[j];
    stat_a[i].i_mbcoeff[j] += stat_a[0].i_mbcoeff[j];
    stat_a[i].p_mbcoeff[j] += stat_a[0].p_mbcoeff[j];
    stat_a[i].b_mbcoeff[j] += stat_a[0].b_mbcoeff[j];
    stat_a[i].bi_mbcoeff[j] += stat_a[0].bi_mbcoeff[j];
  }

  stat_a[i].tottime += stat_a[0].tottime;

  init_stat_struct(&(stat_a[0]));
}

static unsigned int
bitCountRead()
{
  return bitCount;
}

static void
StartTime()
{
  stat_a[0].tottime = ReadSysClock();
}

static void
EndTime()
{
  stat_a[0].tottime = ReadSysClock() - stat_a[0].tottime;
}
#endif


/*
 *--------------------------------------------------------------
 *
 * ReadSysClock --
 *
 *  Computes the current time according to the system clock.
 *
 * Results:
 *  The current time according to the system clock.
 *
 * Side effects:
 *  None.
 *
 *--------------------------------------------------------------
 */

double
ReadSysClock()
{
    return(SDL_GetTicks()/1000.0);
}

/*
 *--------------------------------------------------------------
 *
 * InitCrop --
 *
 *  Initializes cropTbl - this was taken from newVidStream so
 *  that it wasn't done for each new video stream
 *
 * Results:
 *    None
 *
 * Side effects:
 *    cropTbl will be initialized
 *
 *--------------------------------------------------------------
 */
void
InitCrop()
{
#ifdef USE_CROP_TABLE
  int i;

  /* Initialize crop table. */
  for (i = (-MAX_NEG_CROP); i < NUM_CROP_ENTRIES - MAX_NEG_CROP; i++) {
    if (i <= 0) {
      cropTbl[i + MAX_NEG_CROP] = 0;
#ifdef TWELVE_BITS
        } else if (i >= 2047) {
      cropTbl[i + MAX_NEG_CROP] = 2047;
#endif
    } else if (i >= 255) {
      cropTbl[i + MAX_NEG_CROP] = 255;
    } else {
      cropTbl[i + MAX_NEG_CROP] = i;
    }
  }
#endif /* USE_CROP_TABLE */
}



/*
 *--------------------------------------------------------------
 *
 * NewVidStream --
 *
 *    Allocates and initializes a VidStream structure. Takes
 *      as parameter requested size for buffer length.
 *
 * Results:
 *    A pointer to the new VidStream structure.
 *
 * Side effects:
 *    None.
 *
 *--------------------------------------------------------------
 */

VidStream* NewVidStream( unsigned int buffer_len )
{
    int i, j;
    VidStream* vs;
    static const unsigned char default_intra_matrix[64] =
    {
        8, 16, 19, 22, 26, 27, 29, 34,
        16, 16, 22, 24, 27, 29, 34, 37,
        19, 22, 26, 27, 29, 34, 34, 38,
        22, 22, 26, 27, 29, 34, 37, 40,
        22, 26, 27, 29, 32, 35, 40, 48,
        26, 27, 29, 32, 35, 40, 48, 58,
        26, 27, 29, 34, 38, 46, 56, 69,
        27, 29, 35, 38, 46, 56, 69, 83
    };

    /* Check for legal buffer length. */

    if( buffer_len < 4 )
        return NULL;

    /* Make buffer length multiple of 4. */

    buffer_len = (buffer_len + 3) >> 2;

    /* Allocate memory for vs structure. */

    vs = (VidStream *) malloc(sizeof(VidStream));
    memset( vs, 0, (sizeof *vs) );

    /* Initialize pointers to extension and user data. */

    vs->group.ext_data = vs->group.user_data =
    vs->picture.extra_info = vs->picture.user_data =
    vs->picture.ext_data = vs->slice.extra_info =
    vs->ext_data = vs->user_data = NULL;

    /* Copy default intra matrix. */

    for( i = 0; i < 8; i++ )
    {
        for( j = 0; j < 8; j++ )
        {
            vs->intra_quant_matrix[i][j] = default_intra_matrix[i * 8 + j];
        }
    }

    /* Initialize non intra quantization matrix. */

    for( i = 0; i < 8; i++ )
    {
        for( j = 0; j < 8; j++ )
        {
            vs->non_intra_quant_matrix[i][j] = 16;
        }
    }
    
    /* Initialize noise base matrix */
    for( i = 0; i < 8; i++ )
        for( j = 0; j < 8; j++ )
            vs->noise_base_matrix[i][j] = (short) vs->non_intra_quant_matrix[i][j];

    j_rev_dct((short *) vs->noise_base_matrix);

    for( i = 0; i < 8; i++ )
        for( j = 0; j < 8; j++ )
	  vs->noise_base_matrix[i][j] *= vs->noise_base_matrix[i][j];

    /* Initialize pointers to image spaces. */

    vs->current = vs->past = vs->future = NULL;
    for( i = 0; i < RING_BUF_SIZE; i++ )
    {
        vs->ring[i] = NULL;
    }

    /* Create buffer. */

    vs->buf_start = (unsigned int *) malloc(buffer_len * 4);

    /*
    * Set max_buf_length to one less than actual length to deal with messy
    * data without proper seq. end codes.
    */

    vs->max_buf_length = buffer_len - 1;

    /* Initialize fields that used to be global */
    vs->ditherFlags = NULL;
    vs->rate_deal = -1;

    /* Reset everything for start of display */
    ResetVidStream(vs);

#ifdef USE_ATI
    /* Initialize Rage128 hardware */
    vs->ati_handle = vhar128_new();
#endif

    /* Return structure. */

    return vs;
}


/*
 *--------------------------------------------------------------
 *
 * ResetVidStream --
 *
 *    Re-initializes a VidStream structure. Takes
 *      as parameter a pointer to the VidStream to reset.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    None.
 *
 *--------------------------------------------------------------
 */

void ResetVidStream( VidStream* vid )
{
  int i;

  /* Initialize pointers to image spaces. */
  vid->current = vid->past = vid->future = NULL;

  /* Initialize rings */
  for (i = 0; i < RING_BUF_SIZE; i++) {
    if ( vid->ring[i] )
      vid->ring[i]->locked = 0;  /* Unlock */
  }

  /* Initialize bitstream i/o fields. */
  vid->bit_offset = 0;
  vid->buf_length = 0;
  vid->buffer = vid->buf_start;
  vid->curBits = 0;

  /* We are at the beginning of the film, so film has not ended */
  vid->film_has_ended = FALSE;

  /* Reset number of frames to zero */
  vid->totNumFrames=0;
#ifdef CALCULATE_FPS
  for ( i=0; i<FPS_WINDOW; ++i )
    vid->frame_time[i] = 0.0;
  vid->timestamp_index = 0;
#endif

  /* Fields added by KR for synchronization */
  vid->_skipFrame = 0;
  vid->_skipCount = 0;
  vid->_jumpFrame = -1;
  vid->realTimeStart = 0;

  /* Reset EOF_flag to 0 */
  vid->EOF_flag = FALSE;

  vid->current_frame=0;
  vid->need_frameadjust=false;
}


/*
 *--------------------------------------------------------------
 *
 * DestroyVidStream --
 *
 *    Deallocates a VidStream structure.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    None.
 *
 *--------------------------------------------------------------
 */

void DestroyVidStream( VidStream* astream )
{
    int i;

    if( astream->ext_data != NULL )
        free(astream->ext_data);

    if( astream->user_data != NULL )
        free(astream->user_data);

    if( astream->group.ext_data != NULL )
        free(astream->group.ext_data);

    if( astream->group.user_data != NULL )
        free(astream->group.user_data);

    if( astream->picture.extra_info != NULL )
        free(astream->picture.extra_info);

    if( astream->picture.ext_data != NULL )
        free(astream->picture.ext_data);

    if( astream->picture.user_data != NULL )
        free(astream->picture.user_data);

    if( astream->slice.extra_info != NULL )
        free(astream->slice.extra_info);

    if( astream->buf_start != NULL )
        free(astream->buf_start);

    for( i = 0; i < RING_BUF_SIZE; i++ )
    {
        if( astream->ring[i] != NULL )
        {
            DestroyPictImage( astream, astream->ring[i] );
            astream->ring[i] = NULL;
        }
    }

    if( astream->ditherFlags != NULL )
        free( astream->ditherFlags );

#ifdef USE_ATI
    vhar128_close(astream->ati_handle);
#endif

#ifdef USE_ATI
    vhar128_delete();
#endif

    free( (char*) astream );
}



/*
 *--------------------------------------------------------------
 *
 * NewPictImage --
 *
 *    Allocates and initializes a PictImage structure.
 *      The width and height of the image space are passed in
 *      as parameters.
 *
 * Results:
 *    A pointer to the new PictImage structure.
 *
 * Side effects:
 *    None.
 *
 *--------------------------------------------------------------
 */

PictImage* NewPictImage( VidStream* vid_stream, int w, int h, SDL_Surface *dst )
{
    PictImage* pi;

    /* Allocate memory space for pi structure. */

    pi = (PictImage *) malloc(sizeof(PictImage));

    /* Create a YV12 image (Y + V + U) */
#ifdef USE_ATI
    pi->image = vhar128_newimage(vid_stream->ati_handle, w, h);
#else
    pi->image = (unsigned char *) malloc(w*h*12/8);
    pi->luminance = (unsigned char *)pi->image;
    pi->Cr = pi->luminance + (w*h);
    pi->Cb = pi->luminance + (w*h) + (w*h)/4;
#endif
  
    /* Alloc space for filter info */
    pi->mb_qscale = (unsigned short int *) malloc(vid_stream->mb_width * vid_stream->mb_height * sizeof(unsigned int));

    /* Reset locked flag. */
  
    pi->locked = 0;

    /* Return pointer to pi structure. */

    return pi;
}

bool InitPictImages( VidStream* vid_stream, int w, int h, SDL_Surface* dst )
{
    int i;

    vid_stream->current = vid_stream->past = vid_stream->future = NULL;
    for (i = 0; i < RING_BUF_SIZE; i++) {
        if ( vid_stream->ring[i] ) {
            DestroyPictImage(vid_stream, vid_stream->ring[i]);
        }
        vid_stream->ring[i] = NewPictImage( vid_stream, w, h, dst );
        if ( ! vid_stream->ring[i] )
            return false;
    }

#ifdef USE_ATI
    struct vhar128_image * ring[RING_BUF_SIZE];

    for (i = 0; i < RING_BUF_SIZE; i++) {
      ring[i] = vid_stream->ring[i]->image;
    }

    vhar128_init(vid_stream->ati_handle, w, h, ring, RING_BUF_SIZE);
#endif

    return true;
}

/*
 *--------------------------------------------------------------
 *
 * DestroyPictImage --
 *
 *    Deallocates a PictImage structure.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    None.
 *
 *--------------------------------------------------------------
 */
void DestroyPictImage( VidStream* vid_stream, PictImage* apictimage )
{
#ifdef USE_ATI
  vhar128_destroyimage(vid_stream->ati_handle, apictimage->image);
#else
  if (apictimage->image != NULL) free(apictimage->image);
#endif

  free(apictimage->mb_qscale);
  free(apictimage);
}


/*
 *--------------------------------------------------------------
 *
 * mpegVidRsrc --
 *
 *      Parses bit stream until MB_QUANTUM number of
 *      macroblocks have been decoded or current slice or
 *      picture ends, whichever comes first. If the start
 *      of a frame is encountered, the frame is time stamped
 *      with the value passed in time_stamp. If the value
 *      passed in buffer is not null, the video stream buffer
 *      is set to buffer and the length of the buffer is
 *      expected in value passed in through length.
 *
 * Results:
 *      A pointer to the video stream structure used.
 *
 * Side effects:
 *      Bit stream is irreversibly parsed. If a picture is completed,
 *      a function is called to display the frame at the correct time.
 *
 *--------------------------------------------------------------
 */

VidStream* mpegVidRsrc( TimeStamp time_stamp, VidStream* vid_stream, int first )
{
    unsigned int data;
    int i, status;

    /*
    * If called for the first time, find start code, make sure it is a
    * sequence start code.
    */

    if( first )
    {
        vid_stream->num_left       = 0;
        vid_stream->leftover_bytes = 0;
        vid_stream->Parse_done     = FALSE;

        next_start_code(vid_stream);  /* sets curBits */
        show_bits32(data);
        if( data != SEQ_START_CODE )
        {
            vid_stream->_smpeg->SetError("Invalid sequence in video stream");
            /* Let whoever called NewVidStream call DestroyVidStream - KR
            DestroyVidStream( vid_stream );
            */
            return 0;
        }
    }
    else
    {
#ifdef UTIL2
        vid_stream->curBits = *vid_stream->buffer << vid_stream->bit_offset;
#else
        vid_stream->curBits = *vid_stream->buffer;
#endif
    }

    /* Get next 32 bits (size of start codes). */

    show_bits32(data);

    /* Check for end of file */
    if(vid_stream->EOF_flag)
    {
        /* Set ended flag first so that ExecuteDisplay may check it. */

        vid_stream->film_has_ended = TRUE;

        if( vid_stream->future != NULL )
        {
            vid_stream->current = vid_stream->future;
            vid_stream->_smpeg->ExecuteDisplay( vid_stream );
        }

#ifdef ANALYSIS
        PrintAllStats(vid_stream);
#endif
        goto done;
    }

    /*
    * Process according to start code (or parse macroblock if not a start code
    * at all).
    */

    switch( data )
    {
    case SEQ_END_CODE:
    case 0x000001b9:   /*  handle ISO_11172_END_CODE too */

#ifdef VERBOSE_DEBUG
        printf("SEQ_END_CODE\n");
#endif
        flush_bits32;
        goto done;
        break;

    case SEQ_START_CODE:

        /* Sequence start code. Parse sequence header. */

#ifdef VERBOSE_DEBUG
        printf("SEQ_START_CODE\n");
#endif
        if( ParseSeqHead( vid_stream ) != PARSE_OK )
        {
            fprintf( stderr, "mpegVidRsrc ParseSeqHead\n" );
            goto error;
        }

        /*
        * Return after sequence start code so that application above can use
        * info in header.
        */
        goto done;


    case GOP_START_CODE:

        /* Group of Pictures start code. Parse gop header. */

#ifdef VERBOSE_DEBUG
        printf("GOP_START_CODE\n");
#endif
        if( ParseGOP(vid_stream) != PARSE_OK )
        {
            fprintf( stderr, "mpegVidRsrc ParseGOP\n" );
            goto error;
        }
	/* need adjust current_frame (after Seek) */
	if (vid_stream->need_frameadjust) {
		int prev;
		prev = vid_stream->totNumFrames;
		vid_stream->current_frame = (int)
		(
		vid_stream->group.tc_hours * 3600 * vid_stream->rate_deal +
		vid_stream->group.tc_minutes * 60 * vid_stream->rate_deal +
		vid_stream->group.tc_seconds * vid_stream->rate_deal +
		vid_stream->group.tc_pictures);
		vid_stream->need_frameadjust=false;
		vid_stream->totNumFrames=vid_stream->current_frame;
#if 0
	printf("Adjusted Frame %d -> %d\n",prev,vid_stream->current_frame);
#endif
	}
        goto done;


    case PICTURE_START_CODE:

        /* Picture start code. Parse picture header and first slice header. */

#ifdef VERBOSE_DEBUG
        printf("PICTURE_START_CODE\n");
#endif
	if (vid_stream->timestamp_mark < vid_stream->buffer
	    && !vid_stream->timestamp_used){
	  vid_stream->timestamp_used = true;
	  status = ParsePicture( vid_stream, vid_stream->timestamp );
	} else
	  status = ParsePicture( vid_stream, -1);

        if((vid_stream->picture.code_type == B_TYPE) &&
            vid_stream->_skipFrame && (vid_stream->_jumpFrame < 0))
        {
            status = SKIP_PICTURE;
        }
	
	if( !vid_stream->current )
	    status = SKIP_PICTURE;

        if( status == SKIP_PICTURE )
        {
            //fprintf( stderr, "%d\r", vid_stream->totNumFrames );

            next_start_code( vid_stream );

            while( ! next_bits( 32, PICTURE_START_CODE, vid_stream ) )
            {
                if( next_bits( 32, GOP_START_CODE, vid_stream) )
                    break;
                else if( next_bits( 32, SEQ_END_CODE, vid_stream ) )
                    break;

                flush_bits( 24 );
                next_start_code( vid_stream );
            }

            vid_stream->_smpeg->timeSync( vid_stream );
            goto done;
        }
        else if( status != PARSE_OK )
        {
            fprintf( stderr, "mpegVidRsrc ParsePicture\n" );
            goto error;
        }


        if( ParseSlice(vid_stream) != PARSE_OK )
        {
            fprintf( stderr, "mpegVidRsrc ParseSlice\n" );
            goto error;
        }
        break;


    case SEQUENCE_ERROR_CODE:

#ifdef VERBOSE_DEBUG
        printf("SEQUENCE_ERROR_CODE\n");
#endif
        flush_bits32;
        next_start_code(vid_stream);
        goto done;


    default:

	/* No base picture for decoding */
	if( !vid_stream->current )
	{
	    flush_bits32;
	    next_start_code(vid_stream);
#ifdef VERBOSE_DEBUG
            printf("No base picture, flushing to next start code\n");
#endif
	    goto done;
	}

        /* Check for slice start code. */

        if( (data >= SLICE_MIN_START_CODE) && (data <= SLICE_MAX_START_CODE) )
        {
            /* Slice start code. Parse slice header. */
            if( ParseSlice(vid_stream) != PARSE_OK )
            {
                fprintf( stderr, "mpegVidRsrc ParseSlice\n" );
                goto error;
            }
        }
#ifdef VERBOSE_DEBUG
        else
            fprintf(stderr, "Unknown data [%x] - not slice start code!\n", data);
#endif
        break;
    }


    /* Parse next MB_QUANTUM macroblocks. */

    for( i = 0; i < MB_QUANTUM; i++ )
    {
        /* Check to see if actually a startcode and not a macroblock. */

        if( ! next_bits( 23, 0x00000000, vid_stream ) &&
            ! vid_stream->film_has_ended )
        {
            /* Not start code. Parse Macroblock. */

            if (ParseMacroBlock(vid_stream) != PARSE_OK)
            {
#ifdef VERBOSE_WARNINGS
                fprintf( stderr, "mpegVidRsrc ParseMacroBlock\n" );
#endif
                goto error;
            }
        }
        else
        {
            /* Not macroblock, actually start code. Get start code. */

            next_start_code(vid_stream);
            show_bits32(data);

            /*
            * If start code is outside range of slice start codes, frame is
            * complete, display frame.
            */

            if( ((data<SLICE_MIN_START_CODE) || (data>SLICE_MAX_START_CODE)) &&
                (data != SEQUENCE_ERROR_CODE) )
            {
                DoPictureDisplay( vid_stream );
            }
            goto done;
        }
    }


    /* Check if we just finished a picture on the MB_QUANTUMth macroblock */

    if( next_bits( 23, 0x00000000, vid_stream ) )
    {
        next_start_code(vid_stream);
        show_bits32(data);
        if( (data < SLICE_MIN_START_CODE) || (data > SLICE_MAX_START_CODE) )
        {
            DoPictureDisplay( vid_stream );
        }
    }

    goto done;

error:

    next_start_code( vid_stream );
    return vid_stream;

done:

    return vid_stream;
}


/*
 *--------------------------------------------------------------
 *
 * ParseSeqHead --
 *
 *      Assumes bit stream is at the begining of the sequence
 *      header start code. Parses off the sequence header.
 *
 * Results:
 *      Fills the vid_stream structure with values derived and
 *      decoded from the sequence header. Allocates the pict image
 *      structures based on the dimensions of the image space
 *      found in the sequence header.
 *
 * Side effects:
 *      Bit stream irreversibly parsed off.
 *
 *--------------------------------------------------------------
 */
static int ParseSeqHead( VidStream* vid_stream )
{
  unsigned int data;
  int i, j;
#ifndef DISABLE_DITHER
  int ditherType=vid_stream->ditherType;
#endif

  /* Flush off sequence start code. */

  flush_bits32;

  /* Get horizontal size of image space. */

  get_bits12(data);
  vid_stream->h_size = (data + 15) & ~15;

  /* Get vertical size of image space. */

  get_bits12(data);
  vid_stream->v_size = (data + 15) & ~15;

  /* Calculate macroblock width and height of image space. */

  vid_stream->mb_width = (vid_stream->h_size + 15) / 16;
  vid_stream->mb_height = (vid_stream->v_size + 15) / 16;

#ifndef DISABLE_DITHER
  /* If dither type is MBORDERED allocate ditherFlags. */
  if (ditherType == MBORDERED_DITHER) {
    vid_stream->ditherFlags =
         (char *) malloc(vid_stream->mb_width*vid_stream->mb_height);
  }
#endif

  /* Parse of aspect ratio code. */

  get_bits4(data);
  vid_stream->aspect_ratio = (unsigned char) data;

  /* Parse off picture rate code. */

  get_bits4(data);
  vid_stream->picture_rate = (unsigned char) data;

  /* Parse off bit rate. */

  get_bits18(data);
  vid_stream->bit_rate = data;

  /* Flush marker bit. */

  flush_bits(1);

  /* Parse off vbv buffer size. */

  get_bits10(data);
  vid_stream->vbv_buffer_size = data;

#ifdef not_def
  /* Lets not bother with this.  Only increases memory image */
  if (data*1024>vid_stream->max_buf_length) {
    unsigned int *newbuf;
    int sz=1024*data+1;
    /* If they actually want a bigger buffer than we default to,
       let them have it! (if we can) */
    newbuf = (unsigned int *) realloc(vid_stream->buf_start, (unsigned int) 4*sz);
    if (newbuf!=(unsigned int *)NULL) {
      vid_stream->max_buf_length=sz;
      vid_stream->buffer=
          (vid_stream->buffer-vid_stream->buf_start)+newbuf;
      vid_stream->buf_start=newbuf;
    }}
#endif

  /* Parse off contrained parameter flag. */

  get_bits1(data);
  if (data) {
    vid_stream->const_param_flag = TRUE;
  } else
    vid_stream->const_param_flag = FALSE;

  /*
   * If intra_quant_matrix_flag set, parse off intra quant matrix values.
   */

  get_bits1(data);
  if (data) {
    for (i = 0; i < 64; i++) {
      get_bits8(data);

      vid_stream->intra_quant_matrix[zigzag[i][1]][zigzag[i][0]] =
        (unsigned char) data;
    }
  }
  /*
   * If non intra quant matrix flag set, parse off non intra quant matrix
   * values.
   */

  get_bits1(data);
  if (data) {
    for (i = 0; i < 64; i++) {
      get_bits8(data);

      vid_stream->non_intra_quant_matrix[zigzag[i][1]][zigzag[i][0]] =
        (unsigned char) data;
    }
  }

  /* Adjust noise base matrix according to non_intra matrix */
  for( i = 0; i < 8; i++ )
    for( j = 0; j < 8; j++ )
      vid_stream->noise_base_matrix[i][j] = (short) vid_stream->non_intra_quant_matrix[i][j];
  
  j_rev_dct((short *) vid_stream->noise_base_matrix);
  
  for( i = 0; i < 8; i++ )
    for( j = 0; j < 8; j++ )
      vid_stream->noise_base_matrix[i][j] *= vid_stream->noise_base_matrix[i][j];

  /* Go to next start code. */

  next_start_code(vid_stream);

  /*
   * If next start code is extension start code, parse off extension data.
   */

  if (next_bits(32, EXT_START_CODE, vid_stream)) {
    flush_bits32;
    if (vid_stream->ext_data != NULL) {
      free(vid_stream->ext_data);
      vid_stream->ext_data = NULL;
    }
    vid_stream->ext_data = get_ext_data(vid_stream);
  }
  /* If next start code is user start code, parse off user data. */

  if (next_bits(32, USER_START_CODE, vid_stream)) {
    flush_bits32;
    if (vid_stream->user_data != NULL) {
      free(vid_stream->user_data);
      vid_stream->user_data = NULL;
    }
    vid_stream->user_data = get_ext_data(vid_stream);
  }
  return PARSE_OK;
}


/*
 *--------------------------------------------------------------
 *
 * ParseGOP --
 *
 *      Parses of group of pictures header from bit stream
 *      associated with vid_stream.
 *
 * Results:
 *      Values in gop header placed into video stream structure.
 *
 * Side effects:
 *      Bit stream irreversibly parsed.
 *
 *--------------------------------------------------------------
 */

static int ParseGOP( VidStream* vid_stream )
{
  unsigned int data;

  /* Flush group of pictures start code. */

  flush_bits32;

  /* Parse off drop frame flag. */

  get_bits1(data);
  if (data) {
    vid_stream->group.drop_flag = TRUE;
  } else
    vid_stream->group.drop_flag = FALSE;

  /* Parse off hour component of time code. */

  get_bits5(data);
  vid_stream->group.tc_hours = data;

  /* Parse off minute component of time code. */

  get_bits6(data);
  vid_stream->group.tc_minutes = data;

  /* Flush marker bit. */

  flush_bits(1);

  /* Parse off second component of time code. */

  get_bits6(data);
  vid_stream->group.tc_seconds = data;

  /* Parse off picture count component of time code. */

  get_bits6(data);
  vid_stream->group.tc_pictures = data;

  /* Parse off closed gop and broken link flags. */

  get_bits2(data);
  if (data > 1) {
    vid_stream->group.closed_gop = TRUE;
    if (data > 2) {
      vid_stream->group.broken_link = TRUE;
    } else
      vid_stream->group.broken_link = FALSE;
  } else {
    vid_stream->group.closed_gop = FALSE;
    if (data) {
      vid_stream->group.broken_link = TRUE;
    } else
      vid_stream->group.broken_link = FALSE;
  }

  /* Goto next start code. */

  next_start_code(vid_stream);

  /* If next start code is extension data, parse off extension data. */

  if (next_bits(32, EXT_START_CODE, vid_stream)) {
    flush_bits32;
    if (vid_stream->group.ext_data != NULL) {
      free(vid_stream->group.ext_data);
      vid_stream->group.ext_data = NULL;
    }
    vid_stream->group.ext_data = get_ext_data(vid_stream);
  }
  /* If next start code is user data, parse off user data. */

  if (next_bits(32, USER_START_CODE,vid_stream)) {
    flush_bits32;
    if (vid_stream->group.user_data != NULL) {
      free(vid_stream->group.user_data);
      vid_stream->group.user_data = NULL;
    }
    vid_stream->group.user_data = get_ext_data(vid_stream);
  }
  return PARSE_OK;
}


/*
 *--------------------------------------------------------------
 *
 * ParsePicture --
 *
 *      Parses picture header. Marks picture to be presented
 *      at particular time given a time stamp.
 *
 * Results:
 *      Values from picture header put into video stream structure.
 *
 * Side effects:
 *      Bit stream irreversibly parsed.
 *
 *--------------------------------------------------------------
 */

static int ParsePicture( VidStream* vid_stream, TimeStamp time_stamp )
{
  unsigned int data;
  int i;

  /* Flush header start code. */
  flush_bits32;

  /* This happens if there is a picture code before a sequence start */
  if (vid_stream->ring[0] == NULL) {
    printf("Warning: picture block before sequence header block\n");
    return SKIP_PICTURE;
  }

  /* Parse off temporal reference. */
  get_bits10(data);
  vid_stream->picture.temp_ref = data;

  /* Parse of picture type. */
  get_bits3(data);
  vid_stream->picture.code_type = data;

  if ((vid_stream->picture.code_type == B_TYPE) &&
      ((vid_stream->future == NULL) ||
       ((vid_stream->past == NULL) && !(vid_stream->group.closed_gop))))
    /* According to 2-D.5.1 (p D-18) this is ok, if the refereneces are OK */
    return SKIP_PICTURE;

  if ((vid_stream->picture.code_type == P_TYPE) && (vid_stream->future == NULL))
    return SKIP_PICTURE;

#ifdef ANALYSIS
  StartTime();
  stat_a[0].frametype = vid_stream->picture.code_type;
  stat_a[0].number = 1;
  stat_a[0].totsize = 45;
  pictureSizeCount = bitCountRead();
#endif

  /* Parse off vbv buffer delay value. */
  get_bits16(data);
  vid_stream->picture.vbv_delay = data;

  /* If P or B type frame... */

  if ((vid_stream->picture.code_type == P_TYPE) || 
      (vid_stream->picture.code_type == B_TYPE)) {

    /* Parse off forward vector full pixel flag. */
    get_bits1(data);
    if (data) {
      vid_stream->picture.full_pel_forw_vector = TRUE;
    } else {
      vid_stream->picture.full_pel_forw_vector = FALSE;
    }

    /* Parse of forw_r_code. */
    get_bits3(data);

    /* Decode forw_r_code into forw_r_size and forw_f. */

    vid_stream->picture.forw_r_size = data - 1;
    vid_stream->picture.forw_f = (1 << vid_stream->picture.forw_r_size);
  }
  /* If B type frame... */

  if (vid_stream->picture.code_type == B_TYPE) {

    /* Parse off back vector full pixel flag. */
    get_bits1(data);
    if (data)
      vid_stream->picture.full_pel_back_vector = TRUE;
    else
      vid_stream->picture.full_pel_back_vector = FALSE;

    /* Parse off back_r_code. */
    get_bits3(data);

    /* Decode back_r_code into back_r_size and back_f. */

    vid_stream->picture.back_r_size = data - 1;
    vid_stream->picture.back_f = (1 << vid_stream->picture.back_r_size);
  }
  /* Get extra bit picture info. */

  if (vid_stream->picture.extra_info != NULL) {
    free(vid_stream->picture.extra_info);
    vid_stream->picture.extra_info = NULL;
  }
  vid_stream->picture.extra_info = get_extra_bit_info(vid_stream);

  /* Goto next start code. */
  next_start_code(vid_stream);

  /* If start code is extension start code, parse off extension data. */

  if (next_bits(32, EXT_START_CODE, vid_stream)) {
    flush_bits32;

    if (vid_stream->picture.ext_data != NULL) {
      free(vid_stream->picture.ext_data);
      vid_stream->picture.ext_data = NULL;
    }
    vid_stream->picture.ext_data = get_ext_data(vid_stream);
  }
  /* If start code is user start code, parse off user data. */

  if (next_bits(32, USER_START_CODE, vid_stream)) {
    flush_bits32;

    if (vid_stream->picture.user_data != NULL) {
      free(vid_stream->picture.user_data);
      vid_stream->picture.user_data = NULL;
    }
    vid_stream->picture.user_data = get_ext_data(vid_stream);
  }

  /* Find a pict image structure in ring buffer not currently locked. */

  i = 0;

  while (vid_stream->ring[i]->locked != 0) {
    if (++i >= RING_BUF_SIZE) {
      perror("Fatal error. Ring buffer full.");
      exit(1);
    }
  }

  /* Set current pict image structure to the one just found in ring. */

  vid_stream->current = vid_stream->ring[i];

  /* Set time stamp. */

  vid_stream->current->show_time = time_stamp;

  /* Reset past macroblock address field. */

  vid_stream->mblock.past_mb_addr = -1;

#ifdef USE_ATI
  int back, forw, current;

  back = forw = -1;
  current = i;

  /* Look for indexes of future and past frames */
  for(i = 0; i < RING_BUF_SIZE; i++)
  {
    if(vid_stream->future == vid_stream->ring[i]) forw = i;
    if(vid_stream->past == vid_stream->ring[i]) back = i;
  }

  /* Start decoding a new frame */
  switch(vid_stream->picture.code_type)
  {
    case B_TYPE:
      vhar128_newdecode(vid_stream->ati_handle, forw, back, current);
    break;
    case P_TYPE:
      vhar128_newdecode(vid_stream->ati_handle, -1, forw, current);
    break;
    case I_TYPE:
      vhar128_newdecode(vid_stream->ati_handle, -1, -1, current);
    break;
  }
#endif

  return PARSE_OK;
}


/*
 *--------------------------------------------------------------
 *
 * ParseSlice --
 *
 *      Parses off slice header.
 *
 * Results:
 *      Values found in slice header put into video stream structure.
 *
 * Side effects:
 *      Bit stream irreversibly parsed.
 *
 *--------------------------------------------------------------
 */

static int ParseSlice( VidStream* vid_stream )
{
  unsigned int data;

  /* Flush slice start code. */

  flush_bits(24);

  /* Parse off slice vertical position. */

  get_bits8(data);
  vid_stream->slice.vert_pos = data;

  /* Parse off quantization scale. */

  get_bits5(data);
  vid_stream->slice.quant_scale = data;

  /* Parse off extra bit slice info. */

  if (vid_stream->slice.extra_info != NULL) {
    free(vid_stream->slice.extra_info);
    vid_stream->slice.extra_info = NULL;
  }
  vid_stream->slice.extra_info = get_extra_bit_info(vid_stream);

  /* Reset past intrablock address. */

  vid_stream->mblock.past_intra_addr = -2;

  /* Reset previous recon motion vectors. */

  vid_stream->mblock.recon_right_for_prev = 0;
  vid_stream->mblock.recon_down_for_prev = 0;
  vid_stream->mblock.recon_right_back_prev = 0;
  vid_stream->mblock.recon_down_back_prev = 0;

  /* Reset macroblock address. */

  vid_stream->mblock.mb_address = ((vid_stream->slice.vert_pos - 1) *
                                   vid_stream->mb_width) - 1;

  /* Reset past dct dc y, cr, and cb values. */

#ifdef USE_ATI
  vid_stream->block.dct_dc_y_past = 0;
  vid_stream->block.dct_dc_cr_past = 0;
  vid_stream->block.dct_dc_cb_past = 0;
#else
  vid_stream->block.dct_dc_y_past = 1024 << 3;
  vid_stream->block.dct_dc_cr_past = 1024 << 3;
  vid_stream->block.dct_dc_cb_past = 1024 << 3;
#endif

  return PARSE_OK;
}


/*
 *--------------------------------------------------------------
 *
 * ParseMacroBlock --
 *
 *      Parseoff macroblock. Reconstructs DCT values. Applies
 *      inverse DCT, reconstructs motion vectors, calculates and
 *      set pixel values for macroblock in current pict image
 *      structure.
 *
 * Results:
 *      Here's where everything really happens. Welcome to the
 *      heart of darkness.
 *
 * Side effects:
 *      Bit stream irreversibly parsed off.
 *
 *--------------------------------------------------------------
 */

static int ParseMacroBlock( VidStream* vid_stream )
{
  int addr_incr;
  unsigned int data;
  int mask, i, recon_right_for, recon_down_for, recon_right_back,
      recon_down_back;
  int zero_block_flag;
  BOOLEAN mb_quant = 0, mb_motion_forw = 0, mb_motion_back = 0, 
      mb_pattern = 0;
#ifndef DISABLE_DITHER
  int no_dith_flag = 0;
  int ditherType = vid_stream->ditherType;
#endif

#ifdef ANALYSIS
  mbSizeCount = bitCountRead();
#endif

#ifdef USE_ATI
  /* Empty macroblock */
  vid_stream->block.dct_recon[0][0] = 0xFFFFFFFF;
  vid_stream->block.dct_recon[1][0] = 0xFFFFFFFF;
  vid_stream->block.dct_recon[2][0] = 0xFFFFFFFF;
  vid_stream->block.dct_recon[3][0] = 0xFFFFFFFF;
  vid_stream->block.dct_recon[4][0] = 0xFFFFFFFF;
  vid_stream->block.dct_recon[5][0] = 0xFFFFFFFF;
#endif

  /*
   * Parse off macroblock address increment and add to macroblock address.
   */
  do
  {
    unsigned int ind;

    show_bits11(ind);
    DecodeMBAddrInc(addr_incr);

    if (mb_addr_inc[ind].num_bits==0)
    {
      addr_incr = 1;
    }
    if (addr_incr == MB_ESCAPE)
    {
      vid_stream->mblock.mb_address += 33;
      addr_incr = MB_STUFFING;
    }
  } while (addr_incr == MB_STUFFING);
  vid_stream->mblock.mb_address += addr_incr;

  if( vid_stream->mblock.mb_address > (int) (vid_stream->mb_height *
                                       vid_stream->mb_width - 1) )
    return SKIP_TO_START_CODE;
  if( vid_stream->mblock.mb_address < 0 )
    return SKIP_TO_START_CODE;

  /*
   * If macroblocks have been skipped, process skipped macroblocks.
   */

  if (vid_stream->mblock.mb_address - vid_stream->mblock.past_mb_addr > 1) {
    if (vid_stream->picture.code_type == P_TYPE)
      ProcessSkippedPFrameMBlocks(vid_stream);
    else if (vid_stream->picture.code_type == B_TYPE)
      ProcessSkippedBFrameMBlocks(vid_stream);
  }
  /* Set past macroblock address to current macroblock address. */
  vid_stream->mblock.past_mb_addr = vid_stream->mblock.mb_address;

  /* Based on picture type decode macroblock type. */
  switch (vid_stream->picture.code_type) {
  case I_TYPE:
    DecodeMBTypeI(mb_quant, mb_motion_forw, mb_motion_back, mb_pattern,
                  vid_stream->mblock.mb_intra);
    break;

  case P_TYPE:
    DecodeMBTypeP(mb_quant, mb_motion_forw, mb_motion_back, mb_pattern,
                  vid_stream->mblock.mb_intra);
    break;

  case B_TYPE:
    DecodeMBTypeB(mb_quant, mb_motion_forw, mb_motion_back, mb_pattern,
                  vid_stream->mblock.mb_intra);
    break;
  case D_TYPE:
    fprintf(stderr, "ERROR:  MPEG-1 Streams with D-frames are not supported\n");
    exit(1);
  }

  /* If quantization flag set, parse off new quantization scale. */

  if (mb_quant == TRUE) {
    get_bits5(data);
    vid_stream->slice.quant_scale = data;
  }
  /* If forward motion vectors exist... */
  if (mb_motion_forw == TRUE) {

    /* Parse off and decode horizontal forward motion vector. */
    DecodeMotionVectors(vid_stream->mblock.motion_h_forw_code);

    /* If horiz. forward r data exists, parse off. */

    if ((vid_stream->picture.forw_f != 1) &&
        (vid_stream->mblock.motion_h_forw_code != 0)) {
      get_bitsn(vid_stream->picture.forw_r_size, data);
      vid_stream->mblock.motion_h_forw_r = data;
    }
    /* Parse off and decode vertical forward motion vector. */
    DecodeMotionVectors(vid_stream->mblock.motion_v_forw_code);

    /* If vert. forw. r data exists, parse off. */

    if ((vid_stream->picture.forw_f != 1) &&
        (vid_stream->mblock.motion_v_forw_code != 0)) {
      get_bitsn(vid_stream->picture.forw_r_size, data);
      vid_stream->mblock.motion_v_forw_r = data;
    }
  }
  /* If back motion vectors exist... */
  if (mb_motion_back == TRUE) {

    /* Parse off and decode horiz. back motion vector. */
    DecodeMotionVectors(vid_stream->mblock.motion_h_back_code);

    /* If horiz. back r data exists, parse off. */

    if ((vid_stream->picture.back_f != 1) &&
        (vid_stream->mblock.motion_h_back_code != 0)) {
      get_bitsn(vid_stream->picture.back_r_size, data);
      vid_stream->mblock.motion_h_back_r = data;
    }
    /* Parse off and decode vert. back motion vector. */
    DecodeMotionVectors(vid_stream->mblock.motion_v_back_code);

    /* If vert. back r data exists, parse off. */

    if ((vid_stream->picture.back_f != 1) &&
        (vid_stream->mblock.motion_v_back_code != 0)) {
      get_bitsn(vid_stream->picture.back_r_size, data);
      vid_stream->mblock.motion_v_back_r = data;
    }
  }
#ifdef ANALYSIS
  if (vid_stream->mblock.mb_intra) {
    stat_a[0].i_mbnum++;
    mbCBPPtr = stat_a[0].i_mbcbp;
    mbCoeffPtr = stat_a[0].i_mbcoeff;
    mbSizePtr = &(stat_a[0].i_mbsize);
  } else if (mb_motion_back && mb_motion_forw) {
    stat_a[0].bi_mbnum++;
    mbCBPPtr = stat_a[0].bi_mbcbp;
    mbCoeffPtr = stat_a[0].bi_mbcoeff;
    mbSizePtr = &(stat_a[0].bi_mbsize);
  } else if (mb_motion_back) {
    stat_a[0].b_mbnum++;
    mbCBPPtr = stat_a[0].b_mbcbp;
    mbCoeffPtr = stat_a[0].b_mbcoeff;
    mbSizePtr = &(stat_a[0].b_mbsize);
  } else {
    stat_a[0].p_mbnum++;
    mbCBPPtr = stat_a[0].p_mbcbp;
    mbCoeffPtr = stat_a[0].p_mbcoeff;
    mbSizePtr = &(stat_a[0].p_mbsize);
  }
#endif

  /* If mblock pattern flag set, parse and decode CBP (code block pattern). */
  if (mb_pattern == TRUE) {
    DecodeCBP(vid_stream->mblock.cbp);
  }
  /* Otherwise, set CBP to zero. */
  else
    vid_stream->mblock.cbp = 0;


#ifdef ANALYSIS
  mbCBPPtr[vid_stream->mblock.cbp]++;
#endif

  /* Reconstruct motion vectors depending on picture type. */
  if (vid_stream->picture.code_type == P_TYPE) {

    /*
     * If no forw motion vectors, reset previous and current vectors to 0.
     */

    if (!mb_motion_forw) {
      recon_right_for = 0;
      recon_down_for = 0;
      vid_stream->mblock.recon_right_for_prev = 0;
      vid_stream->mblock.recon_down_for_prev = 0;
    }
    /*
     * Otherwise, compute new forw motion vectors. Reset previous vectors to
     * current vectors.
     */

    else {
      ComputeForwVector(&recon_right_for, &recon_down_for, vid_stream);
    }
  }
  if (vid_stream->picture.code_type == B_TYPE) {

    /* Reset prev. and current vectors to zero if mblock is intracoded. */

    if (vid_stream->mblock.mb_intra) {
      vid_stream->mblock.recon_right_for_prev = 0;
      vid_stream->mblock.recon_down_for_prev = 0;
      vid_stream->mblock.recon_right_back_prev = 0;
      vid_stream->mblock.recon_down_back_prev = 0;
    } else {
      
      /* If no forw vectors, current vectors equal prev. vectors. */
      
      if (!mb_motion_forw) {
        recon_right_for = vid_stream->mblock.recon_right_for_prev;
        recon_down_for = vid_stream->mblock.recon_down_for_prev;
      }
      /*
       * Otherwise compute forw. vectors. Reset prev vectors to new values.
       */
      
      else {
        ComputeForwVector(&recon_right_for, &recon_down_for, vid_stream);
      }
      
      /* If no back vectors, set back vectors to prev back vectors. */
      
      if (!mb_motion_back) {
        recon_right_back = vid_stream->mblock.recon_right_back_prev;
        recon_down_back = vid_stream->mblock.recon_down_back_prev;
      }
      /* Otherwise compute new vectors and reset prev. back vectors. */

      else {
        ComputeBackVector(&recon_right_back, &recon_down_back, vid_stream);
      }

      /*
       * Store vector existence flags in structure for possible skipped
       * macroblocks to follow.
       */

      vid_stream->mblock.bpict_past_forw = mb_motion_forw;
      vid_stream->mblock.bpict_past_back = mb_motion_back;
    }
  }

#ifndef DISABLE_DITHER
  /* For each possible block in macroblock. */
  if (ditherType == GRAY_DITHER ||
      ditherType == GRAY2_DITHER ||
      ditherType == GRAY256_DITHER ||
      ditherType == GRAY2562_DITHER ||
      ditherType == MONO_DITHER ||
      ditherType == MONO_THRESHOLD) {
    for (mask = 32, i = 0; i < 4; mask >>= 1, i++) {

      /* If block exists... */
      if ((vid_stream->mblock.mb_intra) || (vid_stream->mblock.cbp & mask)) {
        zero_block_flag = 0;
        ParseReconBlock(i, vid_stream);
      } else {
        zero_block_flag = 1;
      }

      /* If macroblock is intra coded... */
      if (vid_stream->mblock.mb_intra) {
        ReconIMBlock(vid_stream, i);
      } else if (mb_motion_forw && mb_motion_back) {
        ReconBiMBlock(vid_stream, i, recon_right_for, recon_down_for,
                    recon_right_back, recon_down_back, zero_block_flag);
      } else if (mb_motion_forw || (vid_stream->picture.code_type == P_TYPE)) {
        ReconPMBlock(vid_stream, i, recon_right_for, recon_down_for,
                    zero_block_flag);
      } else if (mb_motion_back) {
        ReconBMBlock(vid_stream, i, recon_right_back, recon_down_back,
                    zero_block_flag);
      }
    }
    /* Kill the Chrominance blocks... */
    if ((vid_stream->mblock.mb_intra) || (vid_stream->mblock.cbp & 0x2)) {
        ParseAwayBlock(4, vid_stream);
    }
    if ((vid_stream->mblock.mb_intra) || (vid_stream->mblock.cbp & 0x1)) {
        ParseAwayBlock(5, vid_stream);
    }
  } else {
    if ((ditherType == MBORDERED_DITHER) &&
        (vid_stream->mblock.cbp == 0) &&
        (vid_stream->picture.code_type == 3) &&
        (!vid_stream->mblock.mb_intra) &&
        (!(mb_motion_forw && mb_motion_back))) {
      MBOrderedDitherDisplayCopy(vid_stream, vid_stream->mblock.mb_address, 
                     mb_motion_forw, recon_right_for, recon_down_for, 
                     mb_motion_back, recon_right_back, recon_down_back,
                     vid_stream->past->display, vid_stream->future->display);
      vid_stream->ditherFlags[vid_stream->mblock.mb_address] = 0;
      no_dith_flag = 1;
    }
    else {
#endif
      for (mask = 32, i = 0; i < 6; mask >>= 1, i++) {
        
        /* If block exists... */
        if ((vid_stream->mblock.mb_intra) || (vid_stream->mblock.cbp & mask)) {
          zero_block_flag = 0;
          ParseReconBlock(i, vid_stream);
        } else {
          zero_block_flag = 1;
        }

#ifndef USE_ATI
        /* If macroblock is intra coded... */
        if (vid_stream->mblock.mb_intra) {
          ReconIMBlock(vid_stream, i);
        } else if (mb_motion_forw && mb_motion_back) {
          ReconBiMBlock(vid_stream, i, recon_right_for, recon_down_for,
                        recon_right_back, recon_down_back, zero_block_flag);
        } else if (mb_motion_forw || (vid_stream->picture.code_type == P_TYPE)) {
          ReconPMBlock(vid_stream, i, recon_right_for, recon_down_for,
                       zero_block_flag);
        } else if (mb_motion_back) {
          ReconBMBlock(vid_stream, i, recon_right_back, recon_down_back,
                       zero_block_flag);
        }
#endif
      }

#ifdef USE_ATI
      vhar128_macroblock(vid_stream->ati_handle,
			 (vid_stream->mblock.mb_address % vid_stream->mb_width) << 4,
			 (vid_stream->mblock.mb_address / vid_stream->mb_width) << 4,
			 vid_stream->mblock.mb_intra,
			 mb_motion_back, mb_motion_forw,
			 recon_right_back, recon_down_back,
			 recon_right_for, recon_down_for,
			 vid_stream->block.dct_recon);
#endif

#ifndef DISABLE_DITHER
    }
  }
#endif

#ifndef DISABLE_DITHER
  if ((ditherType == MBORDERED_DITHER) && (!no_dith_flag)) {
    if ((vid_stream->picture.code_type == 2) &&
        (vid_stream->mblock.cbp == 0) &&
        (!vid_stream->mblock.mb_intra)) {
      MBOrderedDitherDisplayCopy(vid_stream, vid_stream->mblock.mb_address,
                                 1, recon_right_for, recon_down_for,
                                 0, 0, 0,
                                 vid_stream->future->display,
                                 (unsigned char *) NULL);
      vid_stream->ditherFlags[vid_stream->mblock.mb_address] = 0;
    }
    else {
      vid_stream->ditherFlags[vid_stream->mblock.mb_address] = 1;
    }
  }
#endif

  /* If D Type picture, flush marker bit. */
  if (vid_stream->picture.code_type == 4)
    flush_bits(1);

  /* If macroblock was intracoded, set macroblock past intra address. */
  if (vid_stream->mblock.mb_intra)
    vid_stream->mblock.past_intra_addr =
      vid_stream->mblock.mb_address;

  /* Store macroblock error for filter info */
  if (vid_stream->mblock.mb_intra)
    vid_stream->current->mb_qscale[vid_stream->mblock.mb_address] = 0;
  else
    vid_stream->current->mb_qscale[vid_stream->mblock.mb_address] = vid_stream->slice.quant_scale;

#ifdef ANALYSIS
  *mbSizePtr += bitCountRead() - mbSizeCount;
#endif
  return PARSE_OK;
}

/* software decoder follows */
#ifndef USE_ATI
/*
 *--------------------------------------------------------------
 *
 * ReconIMBlock --
 *
 *        Reconstructs intra coded macroblock.
 *
 * Results:
 *        None.
 *
 * Side effects:
 *        None.
 *
 *--------------------------------------------------------------
 */
#if defined(USE_CROP_TABLE) && !defined(NDEBUG)
/* If people really want to see such things, check 'em */
#define myassert(x,expression)\
  if (!(expression)) {\
  fprintf (stderr,"Bad crop value (%d) at line %d\n", x, __LINE__);\
  next_start_code(vid_stream); return;}
#define assertCrop(x)        myassert(x,((x) >= -MAX_NEG_CROP) && \
                                 ((x) <= 2048+MAX_NEG_CROP))
#else
#define assertCrop(x)
#endif

static void ReconIMBlock( VidStream* vid_stream, int bnum )
{
  int mb_row, mb_col, row, col, row_size, rr;
  unsigned char *dest;

  /* Calculate macroblock row and column from address. */

  mb_row = vid_stream->mblock.mb_address / vid_stream->mb_width;
  mb_col = vid_stream->mblock.mb_address % vid_stream->mb_width;

  /* If block is luminance block... */

  if (bnum < 4) {

    /* Calculate row and col values for upper left pixel of block. */

    row = mb_row * 16;
    col = mb_col * 16;
    if (bnum > 1)
      row += 8;
    if (bnum % 2)
      col += 8;

    /* Set dest to luminance plane of current pict image. */

    dest = vid_stream->current->luminance;

    /* Establish row size. */

    row_size = vid_stream->mb_width * 16;
  }
  /* Otherwise if block is Cr block... */
  /* Cr first because of the earlier mixup */

  else if (bnum == 5) {

    /* Set dest to Cr plane of current pict image. */

    dest = vid_stream->current->Cr;

    /* Establish row size. */

    row_size = vid_stream->mb_width * 8;

    /* Calculate row,col for upper left pixel of block. */

    row = mb_row * 8;
    col = mb_col * 8;
  }
  /* Otherwise block is Cb block, and ... */

  else {

    /* Set dest to Cb plane of current pict image. */

    dest = vid_stream->current->Cb;

    /* Establish row size. */

    row_size = vid_stream->mb_width * 8;

    /* Calculate row,col for upper left pixel value of block. */

    row = mb_row * 8;
    col = mb_col * 8;
  }

  /*
   * For each pixel in block, set to cropped reconstructed value from inverse
   * dct.
   */
  {
    short *sp = &vid_stream->block.dct_recon[0][0];
#ifdef USE_CROP_TABLE
    unsigned char *cm = cropTbl + MAX_NEG_CROP;
#endif
    dest += row * row_size + col;
    for (rr = 0; rr < 4; rr++, sp += 16, dest += row_size) {
      dest[0] = crop(sp[0]);
          assertCrop(sp[0]);
      dest[1] = crop(sp[1]);
      assertCrop(sp[1]);
      dest[2] = crop(sp[2]);
      assertCrop(sp[2]);
      dest[3] = crop(sp[3]);
      assertCrop(sp[3]);
      dest[4] = crop(sp[4]);
      assertCrop(sp[4]);
      dest[5] = crop(sp[5]);
      assertCrop(sp[5]);
      dest[6] = crop(sp[6]);
      assertCrop(sp[6]);
      dest[7] = crop(sp[7]);
      assertCrop(sp[7]);

      dest += row_size;
      dest[0] = crop(sp[8]);
      assertCrop(sp[8]);
      dest[1] = crop(sp[9]);
      assertCrop(sp[9]);
      dest[2] = crop(sp[10]);
      assertCrop(sp[10]);
      dest[3] = crop(sp[11]);
      assertCrop(sp[11]);
      dest[4] = crop(sp[12]);
      assertCrop(sp[12]);
      dest[5] = crop(sp[13]);
      assertCrop(sp[13]);
      dest[6] = crop(sp[14]);
      assertCrop(sp[14]);
      dest[7] = crop(sp[15]);
      assertCrop(sp[15]);
    }
  }
}


/*
 *--------------------------------------------------------------
 *
 * ReconPMBlock --
 *
 *    Reconstructs forward predicted macroblocks.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    None.
 *
 *--------------------------------------------------------------
 */

static void ReconPMBlock( VidStream* vid_stream, int bnum,
        int recon_right_for, int recon_down_for, int zflag )
{
  int mb_row, mb_col, row, col, row_size, rr;
  unsigned char *dest, *past = 0;
  unsigned char *rindex1, *rindex2, *rindex3, *rindex4;
  unsigned char *index;
  short int *blockvals;

#ifdef LOOSE_MPEG
  int maxx, maxy, cc;
  int illegalBlock = 0;
  int row_start, row_end, rfirst, rlast, col_start, col_end, cfirst, clast;
#endif

  /* Calculate macroblock row and column from address. */

  mb_row = vid_stream->mblock.mb_address / vid_stream->mb_width;
  mb_col = vid_stream->mblock.mb_address % vid_stream->mb_width;

  if (bnum < 4) {

    /* Calculate right_for, down_for motion vectors. */

    vid_stream->right_for = recon_right_for >> 1;
    vid_stream->down_for = recon_down_for >> 1;
    vid_stream->right_half_for = recon_right_for & 0x1;
    vid_stream->down_half_for = recon_down_for & 0x1;

    /* Set dest to luminance plane of current pict image. */

    dest = vid_stream->current->luminance;

    if (vid_stream->picture.code_type == B_TYPE) {
      if (vid_stream->past != NULL)
       past = vid_stream->past->luminance;
    } else {

      /* Set predictive frame to current future frame. */

      if (vid_stream->future != NULL)
        past = vid_stream->future->luminance;
    }

    /* Establish row size. */

    row_size = vid_stream->mb_width << 4;

    /* Calculate row,col of upper left pixel in block. */

    row = mb_row << 4;
    col = mb_col << 4;
    if (bnum > 1)
      row += 8;
    if (bnum % 2)
      col += 8;

#ifdef LOOSE_MPEG
    /* Check for block illegality. */

    maxx = vid_stream->mb_width*16-1;
    maxy = vid_stream->mb_height*16-1;

    if (row + vid_stream->down_for + vid_stream->down_half_for + 7 > maxy) illegalBlock |= 0x4;
    else if (row + vid_stream->down_for < 0)  illegalBlock |= 0x1;
    
    if (col + vid_stream->right_for + vid_stream->right_half_for + 7 > maxx) illegalBlock |= 0x2;
    else if (col + vid_stream->right_for < 0) illegalBlock |= 0x8;

#endif
  }
  /* Otherwise, block is NOT luminance block, ... */

  else {

    /* Construct motion vectors. */

    recon_right_for /= 2;
    recon_down_for /= 2;
    vid_stream->right_for = recon_right_for >> 1;
    vid_stream->down_for = recon_down_for >> 1;
    vid_stream->right_half_for = recon_right_for & 0x1;
    vid_stream->down_half_for = recon_down_for & 0x1;

    /* Establish row size. */

    row_size = vid_stream->mb_width << 3;

    /* Calculate row,col of upper left pixel in block. */

    row = mb_row << 3;
    col = mb_col << 3;

#ifdef LOOSE_MPEG
    /* Check for block illegality. */

    maxx = vid_stream->mb_width*8-1;
    maxy = vid_stream->mb_height*8-1;

    if (row + vid_stream->down_for  + vid_stream->down_half_for  + 7 > maxy) illegalBlock |= 0x4;
    else if (row + vid_stream->down_for < 0) illegalBlock |= 0x1;

    if (col + vid_stream->right_for + vid_stream->right_half_for + 7 > maxx) illegalBlock  |= 0x2;
    else if (col + vid_stream->right_for < 0) illegalBlock |= 0x8;

#endif

    /* If block is Cr block... */
    /* 5 first because order was mixed up in earlier versions */

    if (bnum == 5) {

      /* Set dest to Cr plane of current pict image. */

      dest = vid_stream->current->Cr;

      if (vid_stream->picture.code_type == B_TYPE) {

    if (vid_stream->past != NULL)
      past = vid_stream->past->Cr;
      } else {
    if (vid_stream->future != NULL)
      past = vid_stream->future->Cr;
      }
    }
    /* Otherwise, block is Cb block... */

    else {

      /* Set dest to Cb plane of current pict image. */

      dest = vid_stream->current->Cb;

      if (vid_stream->picture.code_type == B_TYPE) {
        if (vid_stream->past != NULL)
          past = vid_stream->past->Cb;
      } else {
        if (vid_stream->future != NULL)
          past = vid_stream->future->Cb;
      }
    }
  }

  /* For each pixel in block... */

#ifdef LOOSE_MPEG

  if (illegalBlock) {
    if (illegalBlock & 0x1) {
      row_start = 0;
      row_end = row+vid_stream->down_for+8;
      rfirst = rlast = 8 - row_end;
    }
    else if (illegalBlock & 0x4) {
      row_start = row + vid_stream->down_for;
      row_end = maxy+1;
      rlast = row_end - row_start - 1;
      rfirst = 0;
    }
    else {
      row_start = row+vid_stream->down_for;
      row_end = row_start+8;
      rfirst = 0;
    }

    if (illegalBlock & 0x8) {
      col_start = 0;
      col_end = col + vid_stream->right_for + 8;
      cfirst = clast = 8 - col_end;
    }
    else if (illegalBlock & 0x2) {
      col_start = col + vid_stream->right_for;
      col_end = maxx + 1;
      clast = col_end - col_start - 1;
      cfirst = 0;
    }
    else {
      col_start = col + vid_stream->right_for;
      col_end = col_start + 8;
      cfirst = 0;
    }

    for (rr = row_start; rr < row_end; rr++) {
      rindex1 = past + (rr * row_size) + col_start;
      index = dest + ((row + rfirst) * row_size) + col + cfirst;
      for (cc = col_start; cc < col_end; cc++) {
    *index++ = *rindex1++;
      }
    }

    if (illegalBlock & 0x1) {
      for (rr = rlast -1; rr >=0; rr--) {
        index = dest + ((row + rr) * row_size) + col;
        rindex1 = dest + ((row + rlast) * row_size) + col;
        for (cc = 0; cc < 8; cc ++) {
          *index++ = *rindex1++;
        }
      }
    }
    else if (illegalBlock & 0x4) {
      for (rr = rlast+1; rr < 8; rr++) {
        index = dest + ((row + rr) * row_size) + col;
        rindex1 = dest + ((row + rlast) * row_size) + col;
        for (cc = 0; cc < 8; cc ++) {
          *index++ = *rindex1++;
        }
      }
    }

    if (illegalBlock & 0x2) {
      for (cc = clast+1; cc < 8; cc++) {
        index = dest + (row * row_size) + (col + cc);
        rindex1 = dest + (row * row_size) + (col + clast);
        for (rr = 0; rr < 8; rr++) {
          *index = *rindex1;
          index += row_size;
          rindex1 += row_size;
        }
      }
    }
    else if (illegalBlock & 0x8) {
      for (cc = clast-1; cc >= 0; cc--) {
        index = dest + (row * row_size) + (col + cc);
        rindex1 = dest + (row * row_size) + (col + clast);
        for (rr = 0; rr < 8; rr++) {
          *index = *rindex1;
          index += row_size;
          rindex1 += row_size;
        }
      }
    }

    if (!zflag) {
      for (rr = 0; rr < 8; rr++) {
        index = dest + (row*row_size) + col;
        blockvals = &(vid_stream->block.dct_recon[rr][0]);
        index[0] += blockvals[0];
        index[1] += blockvals[1];
        index[2] += blockvals[2];
        index[3] += blockvals[3];
        index[4] += blockvals[4];
        index[5] += blockvals[5];
        index[6] += blockvals[6];
        index[7] += blockvals[7];
      }
    }
  }
  else {

#endif

    index = dest + (row * row_size) + col;
    rindex1 = past + (row + vid_stream->down_for) * row_size 
              + col + vid_stream->right_for;
    
    blockvals = &(vid_stream->block.dct_recon[0][0]);
    
    /*
     * Calculate predictive pixel value based on motion vectors and copy to
     * dest plane.
     */
    
    if ((!vid_stream->down_half_for) && (!vid_stream->right_half_for)) {
#ifdef USE_CROP_TABLE
      unsigned char *cm = cropTbl + MAX_NEG_CROP;
#endif
      if (!zflag)
        for (rr = 0; rr < 4; rr++) {
          index[0] = crop((int) rindex1[0] + (int) blockvals[0]);
          index[1] = crop((int) rindex1[1] + (int) blockvals[1]);
          index[2] = crop((int) rindex1[2] + (int) blockvals[2]);
          index[3] = crop((int) rindex1[3] + (int) blockvals[3]);
          index[4] = crop((int) rindex1[4] + (int) blockvals[4]);
          index[5] = crop((int) rindex1[5] + (int) blockvals[5]);
          index[6] = crop((int) rindex1[6] + (int) blockvals[6]);
          index[7] = crop((int) rindex1[7] + (int) blockvals[7]);
          index += row_size;
          rindex1 += row_size;
      
          index[0] = crop((int) rindex1[0] + (int) blockvals[8]);
          index[1] = crop((int) rindex1[1] + (int) blockvals[9]);
          index[2] = crop((int) rindex1[2] + (int) blockvals[10]);
          index[3] = crop((int) rindex1[3] + (int) blockvals[11]);
          index[4] = crop((int) rindex1[4] + (int) blockvals[12]);
          index[5] = crop((int) rindex1[5] + (int) blockvals[13]);
          index[6] = crop((int) rindex1[6] + (int) blockvals[14]);
          index[7] = crop((int) rindex1[7] + (int) blockvals[15]);
          blockvals += 16;
          index += row_size;
          rindex1 += row_size;
        }
      else {
        if (vid_stream->right_for & 0x1) {
          /* No alignment, use bye copy */
          for (rr = 0; rr < 4; rr++) {
            index[0] = rindex1[0];
            index[1] = rindex1[1];
            index[2] = rindex1[2];
            index[3] = rindex1[3];
            index[4] = rindex1[4];
            index[5] = rindex1[5];
            index[6] = rindex1[6];
            index[7] = rindex1[7];
            index += row_size;
            rindex1 += row_size;
            
            index[0] = rindex1[0];
            index[1] = rindex1[1];
            index[2] = rindex1[2];
            index[3] = rindex1[3];
            index[4] = rindex1[4];
            index[5] = rindex1[5];
            index[6] = rindex1[6];
            index[7] = rindex1[7];
            index += row_size;
            rindex1 += row_size;
          }
        } else if (vid_stream->right_for & 0x2) {
          /* Half-word bit aligned, use 16 bit copy */
          short *src = (short *)rindex1;
          short *dest = (short *)index;
          row_size >>= 1;
          for (rr = 0; rr < 4; rr++) {
            dest[0] = src[0];
            dest[1] = src[1];
            dest[2] = src[2];
            dest[3] = src[3];
            dest += row_size;
            src += row_size;
            
            dest[0] = src[0];
            dest[1] = src[1];
            dest[2] = src[2];
            dest[3] = src[3];
            dest += row_size;
            src += row_size;
          }
        } else {
          /* Word aligned, use 32 bit copy */
          int *src = (int *)rindex1;
          int *dest = (int *)index;
          row_size >>= 2;
          for (rr = 0; rr < 4; rr++) {
            dest[0] = src[0];
            dest[1] = src[1];
            dest += row_size;
            src += row_size;
            
            dest[0] = src[0];
            dest[1] = src[1];
            dest += row_size;
            src += row_size;
          }
        }
      }
    } else {
#ifdef USE_CROP_TABLE
      unsigned char *cm = cropTbl + MAX_NEG_CROP;
#endif
      rindex2 = rindex1 + vid_stream->right_half_for 
                + (vid_stream->down_half_for * row_size);

      /* if one of the two is zero, then quality makes no difference */
      if ((!vid_stream->right_half_for) ||
          (!vid_stream->down_half_for) || (!qualityFlag)) {
        
        if (!zflag) {
          for (rr = 0; rr < 4; rr++) {
            index[0] = crop(((int) (rindex1[0] + rindex2[0] + 1) >> 1) + blockvals[0]);
            index[1] = crop(((int) (rindex1[1] + rindex2[1] + 1) >> 1) + blockvals[1]);
            index[2] = crop(((int) (rindex1[2] + rindex2[2] + 1) >> 1) + blockvals[2]);
            index[3] = crop(((int) (rindex1[3] + rindex2[3] + 1) >> 1) + blockvals[3]);
            index[4] = crop(((int) (rindex1[4] + rindex2[4] + 1) >> 1) + blockvals[4]);
            index[5] = crop(((int) (rindex1[5] + rindex2[5] + 1) >> 1) + blockvals[5]);
            index[6] = crop(((int) (rindex1[6] + rindex2[6] + 1) >> 1) + blockvals[6]);
            index[7] = crop(((int) (rindex1[7] + rindex2[7] + 1) >> 1) + blockvals[7]);
            index += row_size;
            rindex1 += row_size;
            rindex2 += row_size;
        
            index[0] = crop(((int) (rindex1[0] + rindex2[0] + 1) >> 1) + blockvals[8]);
            index[1] = crop(((int) (rindex1[1] + rindex2[1] + 1) >> 1) + blockvals[9]);
            index[2] = crop(((int) (rindex1[2] + rindex2[2] + 1) >> 1) + blockvals[10]);
            index[3] = crop(((int) (rindex1[3] + rindex2[3] + 1) >> 1) + blockvals[11]);
            index[4] = crop(((int) (rindex1[4] + rindex2[4] + 1) >> 1) + blockvals[12]);
            index[5] = crop(((int) (rindex1[5] + rindex2[5] + 1) >> 1) + blockvals[13]);
            index[6] = crop(((int) (rindex1[6] + rindex2[6] + 1) >> 1) + blockvals[14]);
            index[7] = crop(((int) (rindex1[7] + rindex2[7] + 1) >> 1) + blockvals[15]);
            blockvals += 16;
            index += row_size;
            rindex1 += row_size;
            rindex2 += row_size;
          }
        } else { /* zflag */
          for (rr = 0; rr < 8; rr++) {
            index[0] = (int) (rindex1[0] + rindex2[0] + 1) >> 1;
            index[1] = (int) (rindex1[1] + rindex2[1] + 1) >> 1;
            index[2] = (int) (rindex1[2] + rindex2[2] + 1) >> 1;
            index[3] = (int) (rindex1[3] + rindex2[3] + 1) >> 1;
            index[4] = (int) (rindex1[4] + rindex2[4] + 1) >> 1;
            index[5] = (int) (rindex1[5] + rindex2[5] + 1) >> 1;
            index[6] = (int) (rindex1[6] + rindex2[6] + 1) >> 1;
            index[7] = (int) (rindex1[7] + rindex2[7] + 1) >> 1;
            index += row_size;
            rindex1 += row_size;
            rindex2 += row_size;
          }
        }
      } else { /* qualityFlag on and both vectors are non-zero */
        rindex3 = rindex1 + vid_stream->right_half_for;
        rindex4 = rindex1 + (vid_stream->down_half_for * row_size);
        if (!zflag) {
          for (rr = 0; rr < 4; rr++) {
            index[0] = crop(((int) (rindex1[0] + rindex2[0] + rindex3[0] + rindex4[0] + 2) >> 2) + blockvals[0]);
            index[1] = crop(((int) (rindex1[1] + rindex2[1] + rindex3[1] + rindex4[1] + 2) >> 2) + blockvals[1]);
            index[2] = crop(((int) (rindex1[2] + rindex2[2] + rindex3[2] + rindex4[2] + 2) >> 2) + blockvals[2]);
            index[3] = crop(((int) (rindex1[3] + rindex2[3] + rindex3[3] + rindex4[3] + 2) >> 2) + blockvals[3]);
            index[4] = crop(((int) (rindex1[4] + rindex2[4] + rindex3[4] + rindex4[4] + 2) >> 2) + blockvals[4]);
            index[5] = crop(((int) (rindex1[5] + rindex2[5] + rindex3[5] + rindex4[5] + 2) >> 2) + blockvals[5]);
            index[6] = crop(((int) (rindex1[6] + rindex2[6] + rindex3[6] + rindex4[6] + 2) >> 2) + blockvals[6]);
            index[7] = crop(((int) (rindex1[7] + rindex2[7] + rindex3[7] + rindex4[7] + 2) >> 2) + blockvals[7]);
            index += row_size;
            rindex1 += row_size;
            rindex2 += row_size;
            rindex3 += row_size;
            rindex4 += row_size;
        
            index[0] = crop(((int) (rindex1[0] + rindex2[0] + rindex3[0] + rindex4[0] + 2) >> 2) + blockvals[8]);
            index[1] = crop(((int) (rindex1[1] + rindex2[1] + rindex3[1] + rindex4[1] + 2) >> 2) + blockvals[9]);
            index[2] = crop(((int) (rindex1[2] + rindex2[2] + rindex3[2] + rindex4[2] + 2) >> 2) + blockvals[10]);
            index[3] = crop(((int) (rindex1[3] + rindex2[3] + rindex3[3] + rindex4[3] + 2) >> 2) + blockvals[11]);
            index[4] = crop(((int) (rindex1[4] + rindex2[4] + rindex3[4] + rindex4[4] + 2) >> 2) + blockvals[12]);
            index[5] = crop(((int) (rindex1[5] + rindex2[5] + rindex3[5] + rindex4[5] + 2) >> 2) + blockvals[13]);
            index[6] = crop(((int) (rindex1[6] + rindex2[6] + rindex3[6] + rindex4[6] + 2) >> 2) + blockvals[14]);
            index[7] = crop(((int) (rindex1[7] + rindex2[7] + rindex3[7] + rindex4[7] + 2) >> 2) + blockvals[15]);
            blockvals += 16;
            index += row_size;
            rindex1 += row_size;
            rindex2 += row_size;
            rindex3 += row_size;
            rindex4 += row_size;
          }
        } else { /* zflag */
          for (rr = 0; rr < 8; rr++) {
            index[0] = (int) (rindex1[0] + rindex2[0] + rindex3[0] + rindex4[0] + 2) >> 2;
            index[1] = (int) (rindex1[1] + rindex2[1] + rindex3[1] + rindex4[1] + 2) >> 2;
            index[2] = (int) (rindex1[2] + rindex2[2] + rindex3[2] + rindex4[2] + 2) >> 2;
            index[3] = (int) (rindex1[3] + rindex2[3] + rindex3[3] + rindex4[3] + 2) >> 2;
            index[4] = (int) (rindex1[4] + rindex2[4] + rindex3[4] + rindex4[4] + 2) >> 2;
            index[5] = (int) (rindex1[5] + rindex2[5] + rindex3[5] + rindex4[5] + 2) >> 2;
            index[6] = (int) (rindex1[6] + rindex2[6] + rindex3[6] + rindex4[6] + 2) >> 2;
            index[7] = (int) (rindex1[7] + rindex2[7] + rindex3[7] + rindex4[7] + 2) >> 2;
            index += row_size;
            rindex1 += row_size;
            rindex2 += row_size;
            rindex3 += row_size;
            rindex4 += row_size;
          }
        }
      }

    }
#ifdef LOOSE_MPEG
  }
#endif
}


/*
 *--------------------------------------------------------------
 *
 * ReconBMBlock --
 *
 *    Reconstructs back predicted macroblocks.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    None.
 *
 *--------------------------------------------------------------
 */

static void ReconBMBlock( VidStream* vid_stream, int bnum,
        int recon_right_back, int recon_down_back, int zflag )
{
  int mb_row, mb_col, row, col, row_size, rr;
  unsigned char *dest, *future = 0;
  int right_back, down_back, right_half_back, down_half_back;
  unsigned char *rindex1, *rindex2, *rindex3, *rindex4;
  unsigned char *index;
  short int *blockvals;

#ifdef LOOSE_MPEG
  int illegalBlock = 0;
  int maxx, maxy, cc;
  int row_start, row_end, rlast, rfirst, col_start, col_end, clast, cfirst;

  rlast = clast = 0;
#endif

  /* Calculate macroblock row and column from address. */

  mb_row = vid_stream->mblock.mb_address / vid_stream->mb_width;
  mb_col = vid_stream->mblock.mb_address % vid_stream->mb_width;

  /* If block is luminance block... */

  if (bnum < 4) {

    /* Calculate right_back, down_back motion vectors. */

    right_back = recon_right_back >> 1;
    down_back = recon_down_back >> 1;
    right_half_back = recon_right_back & 0x1;
    down_half_back = recon_down_back & 0x1;

    /* Set dest to luminance plane of current pict image. */

    dest = vid_stream->current->luminance;

    /*
     * If future frame exists, set future to luminance plane of future frame.
     */

    if (vid_stream->future != NULL)
      future = vid_stream->future->luminance;

    /* Establish row size. */

    row_size = vid_stream->mb_width << 4;

    /* Calculate row,col of upper left pixel in block. */

    row = mb_row << 4;
    col = mb_col << 4;
    if (bnum > 1)
      row += 8;
    if (bnum % 2)
      col += 8;

#ifdef LOOSE_MPEG

    /* Check for block illegality. */

    maxx = vid_stream->mb_width*16-1;
    maxy = vid_stream->mb_height*16-1;

    if (row + down_back + down_half_back + 7 > maxy) illegalBlock |= 0x4;
    else if (row + down_back < 0)  illegalBlock |= 0x1;
    
    if (col + right_back + right_half_back + 7 > maxx) illegalBlock |= 0x2;
    else if (col + right_back < 0) illegalBlock |= 0x8;

#endif

  }
  /* Otherwise, block is NOT luminance block, ... */

  else {

    /* Construct motion vectors. */

    recon_right_back /= 2;
    recon_down_back /= 2;
    right_back = recon_right_back >> 1;
    down_back = recon_down_back >> 1;
    right_half_back = recon_right_back & 0x1;
    down_half_back = recon_down_back & 0x1;

    /* Establish row size. */

    row_size = vid_stream->mb_width << 3;

    /* Calculate row,col of upper left pixel in block. */

    row = mb_row << 3;
    col = mb_col << 3;

#ifdef LOOSE_MPEG

    /* Check for block illegality. */

    maxx = vid_stream->mb_width*8-1;
    maxy = vid_stream->mb_height*8-1;

    if (row + down_back + down_half_back + 7 > maxy) illegalBlock |= 0x4;
    else if (row + down_back < 0) illegalBlock |= 0x1;

    if (col + right_back + right_half_back + 7 > maxx) illegalBlock  |= 0x2;
    else if (col + right_back < 0) illegalBlock |= 0x8;

#endif

    /* If block is Cr block... */
    /* They were switched earlier, so 5 is first - eyhung */

    if (bnum == 5) {

      /* Set dest to Cr plane of current pict image. */

      dest = vid_stream->current->Cr;

      /*
       * If future frame exists, set future to Cr plane of future image.
       */

      if (vid_stream->future != NULL)
        future = vid_stream->future->Cr;
    }
    /* Otherwise, block is Cb block... */

    else {

      /* Set dest to Cb plane of current pict image. */

      dest = vid_stream->current->Cb;

      /*
       * If future frame exists, set future to Cb plane of future frame.
       */

      if (vid_stream->future != NULL)
        future = vid_stream->future->Cb;
    }
  }

  /* For each pixel in block do... */

#ifdef LOOSE_MPEG

  if (illegalBlock) {
    if (illegalBlock & 0x1) {
      row_start = 0;
      row_end = row+down_back+8;
      rfirst = rlast = 8 - row_end;
    }
    else if (illegalBlock & 0x4) {
      row_start = row + down_back;
      row_end = maxy+1;
      rlast = row_end - row_start - 1;
      rfirst = 0;
    }
    else {
      row_start = row+down_back;
      row_end = row_start+8;
      rfirst = 0;
    }

    if (illegalBlock & 0x8) {
      col_start = 0;
      col_end = col + right_back + 8;
      cfirst = clast = 8 - col_end;
    }
    else if (illegalBlock & 0x2) {
      col_start = col + right_back;
      col_end = maxx + 1;
      clast = col_end - col_start - 1;
      cfirst = 0;
    }
    else {
      col_start = col + right_back;
      col_end = col_start + 8;
      cfirst = 0;
    }

    for (rr = row_start; rr < row_end; rr++) {
      rindex1 = future + (rr * row_size) + col_start;
      index = dest + ((row + rfirst) * row_size) + col + cfirst;
      for (cc = col_start; cc < col_end; cc++) {
        *index++ = *rindex1++;
      }
    }

    if (illegalBlock & 0x1) {
      for (rr = rlast -1; rr >=0; rr--) {
        index = dest + ((row + rr) * row_size) + col;
        rindex1 = dest + ((row + rlast) * row_size) + col;
        for (cc = 0; cc < 8; cc ++) {
          *index++ = *rindex1++;
        }
      }
    }
    else if (illegalBlock & 0x4) {
      for (rr = rlast+1; rr < 8; rr++) {
        index = dest + ((row + rr) * row_size) + col;
        rindex1 = dest + ((row + rlast) * row_size) + col;
        for (cc = 0; cc < 8; cc ++) {
          *index++ = *rindex1++;
        }
      }
    }

    if (illegalBlock & 0x2) {
      for (cc = clast+1; cc < 8; cc++) {
        index = dest + (row * row_size) + (col + cc);
        rindex1 = dest + (row * row_size) + (col + clast);
        for (rr = 0; rr < 8; rr++) {
          *index = *rindex1;
          index += row_size;
          rindex1 += row_size;
        }
      }
    }
    else if (illegalBlock & 0x8) {
      for (cc = clast-1; cc >= 0; cc--) {
        index = dest + (row * row_size) + (col + cc);
        rindex1 = dest + (row * row_size) + (col + clast);
        for (rr = 0; rr < 8; rr++) {
          *index = *rindex1;
          index += row_size;
          rindex1 += row_size;
        }
      }
    }

    if (!zflag) {
      for (rr = 0; rr < 8; rr++) {
        index = dest + (row*row_size) + col;
        blockvals = &(vid_stream->block.dct_recon[rr][0]);
        index[0] += blockvals[0];
        index[1] += blockvals[1];
        index[2] += blockvals[2];
        index[3] += blockvals[3];
        index[4] += blockvals[4];
        index[5] += blockvals[5];
        index[6] += blockvals[6];
        index[7] += blockvals[7];
      }
    }
  }
  else {

#endif
    
    index = dest + (row * row_size) + col;
    rindex1 = future + (row + down_back) * row_size + col + right_back;

    blockvals = &(vid_stream->block.dct_recon[0][0]);

    if ((!right_half_back) && (!down_half_back)) {
#ifdef USE_CROP_TABLE
      unsigned char *cm = cropTbl + MAX_NEG_CROP;
#endif
      if (!zflag)
        for (rr = 0; rr < 4; rr++) {
          index[0] = crop((int) rindex1[0] + (int) blockvals[0]);
          index[1] = crop((int) rindex1[1] + (int) blockvals[1]);
          index[2] = crop((int) rindex1[2] + (int) blockvals[2]);
          index[3] = crop((int) rindex1[3] + (int) blockvals[3]);
          index[4] = crop((int) rindex1[4] + (int) blockvals[4]);
          index[5] = crop((int) rindex1[5] + (int) blockvals[5]);
          index[6] = crop((int) rindex1[6] + (int) blockvals[6]);
          index[7] = crop((int) rindex1[7] + (int) blockvals[7]);
          index += row_size;
          rindex1 += row_size;
          
          index[0] = crop((int) rindex1[0] + (int) blockvals[8]);
          index[1] = crop((int) rindex1[1] + (int) blockvals[9]);
          index[2] = crop((int) rindex1[2] + (int) blockvals[10]);
          index[3] = crop((int) rindex1[3] + (int) blockvals[11]);
          index[4] = crop((int) rindex1[4] + (int) blockvals[12]);
          index[5] = crop((int) rindex1[5] + (int) blockvals[13]);
          index[6] = crop((int) rindex1[6] + (int) blockvals[14]);
          index[7] = crop((int) rindex1[7] + (int) blockvals[15]);
          blockvals += 16;
          index += row_size;
          rindex1 += row_size;
        }
      else {
        if (right_back & 0x1) {
          /* No alignment, use bye copy */
          for (rr = 0; rr < 4; rr++) {
            index[0] = rindex1[0];
            index[1] = rindex1[1];
            index[2] = rindex1[2];
            index[3] = rindex1[3];
            index[4] = rindex1[4];
            index[5] = rindex1[5];
            index[6] = rindex1[6];
            index[7] = rindex1[7];
            index += row_size;
            rindex1 += row_size;
            
            index[0] = rindex1[0];
            index[1] = rindex1[1];
            index[2] = rindex1[2];
            index[3] = rindex1[3];
            index[4] = rindex1[4];
            index[5] = rindex1[5];
            index[6] = rindex1[6];
            index[7] = rindex1[7];
            index += row_size;
            rindex1 += row_size;
          }
        } else if (right_back & 0x2) {
          /* Half-word bit aligned, use 16 bit copy */
          short *src = (short *)rindex1;
          short *dest = (short *)index;
          row_size >>= 1;
          for (rr = 0; rr < 4; rr++) {
            dest[0] = src[0];
            dest[1] = src[1];
            dest[2] = src[2];
            dest[3] = src[3];
            dest += row_size;
            src += row_size;
            
            dest[0] = src[0];
            dest[1] = src[1];
            dest[2] = src[2];
            dest[3] = src[3];
            dest += row_size;
            src += row_size;
          }
        } else {
          /* Word aligned, use 32 bit copy */
          int *src = (int *)rindex1;
          int *dest = (int *)index;
          row_size >>= 2;
          for (rr = 0; rr < 4; rr++) {
            dest[0] = src[0];
            dest[1] = src[1];
            dest += row_size;
            src += row_size;
            
            dest[0] = src[0];
            dest[1] = src[1];
            dest += row_size;
            src += row_size;
          }
        }
      }
    } else {
#ifdef USE_CROP_TABLE
      unsigned char *cm = cropTbl + MAX_NEG_CROP;
#endif
      rindex2 = rindex1 + right_half_back + (down_half_back * row_size);
      if (!qualityFlag) {
        
        if (!zflag) {
          for (rr = 0; rr < 4; rr++) {
            index[0] = crop(((int) (rindex1[0] + rindex2[0] + 1) >> 1) + blockvals[0]);
            index[1] = crop(((int) (rindex1[1] + rindex2[1] + 1) >> 1) + blockvals[1]);
            index[2] = crop(((int) (rindex1[2] + rindex2[2] + 1) >> 1) + blockvals[2]);
            index[3] = crop(((int) (rindex1[3] + rindex2[3] + 1) >> 1) + blockvals[3]);
            index[4] = crop(((int) (rindex1[4] + rindex2[4] + 1) >> 1) + blockvals[4]);
            index[5] = crop(((int) (rindex1[5] + rindex2[5] + 1) >> 1) + blockvals[5]);
            index[6] = crop(((int) (rindex1[6] + rindex2[6] + 1) >> 1) + blockvals[6]);
            index[7] = crop(((int) (rindex1[7] + rindex2[7] + 1) >> 1) + blockvals[7]);
            index += row_size;
            rindex1 += row_size;
            rindex2 += row_size;
        
            index[0] = crop(((int) (rindex1[0] + rindex2[0] + 1) >> 1) + blockvals[8]);
            index[1] = crop(((int) (rindex1[1] + rindex2[1] + 1) >> 1) + blockvals[9]);
            index[2] = crop(((int) (rindex1[2] + rindex2[2] + 1) >> 1) + blockvals[10]);
            index[3] = crop(((int) (rindex1[3] + rindex2[3] + 1) >> 1) + blockvals[11]);
            index[4] = crop(((int) (rindex1[4] + rindex2[4] + 1) >> 1) + blockvals[12]);
            index[5] = crop(((int) (rindex1[5] + rindex2[5] + 1) >> 1) + blockvals[13]);
            index[6] = crop(((int) (rindex1[6] + rindex2[6] + 1) >> 1) + blockvals[14]);
            index[7] = crop(((int) (rindex1[7] + rindex2[7] + 1) >> 1) + blockvals[15]);
            blockvals += 16;
            index += row_size;
            rindex1 += row_size;
            rindex2 += row_size;
          }
        } else { /* zflag */
          for (rr = 0; rr < 8; rr++) {
            index[0] = (int) (rindex1[0] + rindex2[0] + 1) >> 1;
            index[1] = (int) (rindex1[1] + rindex2[1] + 1) >> 1;
            index[2] = (int) (rindex1[2] + rindex2[2] + 1) >> 1;
            index[3] = (int) (rindex1[3] + rindex2[3] + 1) >> 1;
            index[4] = (int) (rindex1[4] + rindex2[4] + 1) >> 1;
            index[5] = (int) (rindex1[5] + rindex2[5] + 1) >> 1;
            index[6] = (int) (rindex1[6] + rindex2[6] + 1) >> 1;
            index[7] = (int) (rindex1[7] + rindex2[7] + 1) >> 1;
            index += row_size;
            rindex1 += row_size;
            rindex2 += row_size;
          }
        }
      } else { /* qualityFlag on */
        rindex3 = rindex1 + right_half_back;
        rindex4 = rindex1 + (down_half_back * row_size);
        if (!zflag) {
          for (rr = 0; rr < 4; rr++) {
            index[0] = crop(((int) (rindex1[0] + rindex2[0] + rindex3[0] + rindex4[0] + 2) >> 2) + blockvals[0]);
            index[1] = crop(((int) (rindex1[1] + rindex2[1] + rindex3[1] + rindex4[1] + 2) >> 2) + blockvals[1]);
            index[2] = crop(((int) (rindex1[2] + rindex2[2] + rindex3[2] + rindex4[2] + 2) >> 2) + blockvals[2]);
            index[3] = crop(((int) (rindex1[3] + rindex2[3] + rindex3[3] + rindex4[3] + 2) >> 2) + blockvals[3]);
            index[4] = crop(((int) (rindex1[4] + rindex2[4] + rindex3[4] + rindex4[4] + 2) >> 2) + blockvals[4]);
            index[5] = crop(((int) (rindex1[5] + rindex2[5] + rindex3[5] + rindex4[5] + 2) >> 2) + blockvals[5]);
            index[6] = crop(((int) (rindex1[6] + rindex2[6] + rindex3[6] + rindex4[6] + 2) >> 2) + blockvals[6]);
            index[7] = crop(((int) (rindex1[7] + rindex2[7] + rindex3[7] + rindex4[7] + 2) >> 2) + blockvals[7]);
            index += row_size;
            rindex1 += row_size;
            rindex2 += row_size;
            rindex3 += row_size;
            rindex4 += row_size;
        
            index[0] = crop(((int) (rindex1[0] + rindex2[0] + rindex3[0] + rindex4[0] + 2) >> 2) + blockvals[8]);
            index[1] = crop(((int) (rindex1[1] + rindex2[1] + rindex3[1] + rindex4[1] + 2) >> 2) + blockvals[9]);
            index[2] = crop(((int) (rindex1[2] + rindex2[2] + rindex3[2] + rindex4[2] + 2) >> 2) + blockvals[10]);
            index[3] = crop(((int) (rindex1[3] + rindex2[3] + rindex3[3] + rindex4[3] + 2) >> 2) + blockvals[11]);
            index[4] = crop(((int) (rindex1[4] + rindex2[4] + rindex3[4] + rindex4[4] + 2) >> 2) + blockvals[12]);
            index[5] = crop(((int) (rindex1[5] + rindex2[5] + rindex3[5] + rindex4[5] + 2) >> 2) + blockvals[13]);
            index[6] = crop(((int) (rindex1[6] + rindex2[6] + rindex3[6] + rindex4[6] + 2) >> 2) + blockvals[14]);
            index[7] = crop(((int) (rindex1[7] + rindex2[7] + rindex3[7] + rindex4[7] + 2) >> 2) + blockvals[15]);
            blockvals += 16;
            index += row_size;
            rindex1 += row_size;
            rindex2 += row_size;
            rindex3 += row_size;
            rindex4 += row_size;
          }
        } else { /* zflag */
          for (rr = 0; rr < 8; rr++) {
            index[0] = (int) (rindex1[0] + rindex2[0] + rindex3[0] + rindex4[0] + 2) >> 2;
            index[1] = (int) (rindex1[1] + rindex2[1] + rindex3[1] + rindex4[1] + 2) >> 2;
            index[2] = (int) (rindex1[2] + rindex2[2] + rindex3[2] + rindex4[2] + 2) >> 2;
            index[3] = (int) (rindex1[3] + rindex2[3] + rindex3[3] + rindex4[3] + 2) >> 2;
            index[4] = (int) (rindex1[4] + rindex2[4] + rindex3[4] + rindex4[4] + 2) >> 2;
            index[5] = (int) (rindex1[5] + rindex2[5] + rindex3[5] + rindex4[5] + 2) >> 2;
            index[6] = (int) (rindex1[6] + rindex2[6] + rindex3[6] + rindex4[6] + 2) >> 2;
            index[7] = (int) (rindex1[7] + rindex2[7] + rindex3[7] + rindex4[7] + 2) >> 2;
            index += row_size;
            rindex1 += row_size;
            rindex2 += row_size;
            rindex3 += row_size;
            rindex4 += row_size;
          }
        }
      }

    }
#ifdef LOOSE_MPEG
  }
#endif
}


/*
 *--------------------------------------------------------------
 *
 * ReconBiMBlock --
 *
 *    Reconstructs bidirectionally predicted macroblocks.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    None.
 *
 *--------------------------------------------------------------
 */

static void ReconBiMBlock( VidStream* vid_stream, int bnum,
        int recon_right_for, int recon_down_for, int recon_right_back,
        int recon_down_back, int zflag )
{
  int mb_row, mb_col, row, col, row_size, rr;
  unsigned char *dest, *past=NULL, *future=NULL;
  int right_for, down_for, right_half_for, down_half_for;
  int right_back, down_back, right_half_back, down_half_back;
  unsigned char *index, *rindex1, *bindex1;
  short int *blockvals;
  int forw_row_start, back_row_start, forw_col_start, back_col_start;

#ifdef LOOSE_MPEG
  int lmaxx = vid_stream->mb_width*16-1;
  int lmaxy = vid_stream->mb_height*16-1;
  int cmaxx = vid_stream->mb_width*8-1;
  int cmaxy = vid_stream->mb_height*8-1;
#endif

#ifdef LOOSE_MPEG
  int illegal_forw = 0;
  int illegal_back = 0;
#endif

  /* Calculate macroblock row and column from address. */

  mb_row = vid_stream->mblock.mb_address / vid_stream->mb_width;
  mb_col = vid_stream->mblock.mb_address % vid_stream->mb_width;

  /* If block is luminance block... */

  if (bnum < 4) {

    /*
     * Calculate right_for, down_for, right_half_for, down_half_for,
     * right_back, down_bakc, right_half_back, and down_half_back, motion
     * vectors.
     */

    right_for = recon_right_for >> 1;
    down_for = recon_down_for >> 1;
    right_half_for = recon_right_for & 0x1;
    down_half_for = recon_down_for & 0x1;

    right_back = recon_right_back >> 1;
    down_back = recon_down_back >> 1;
    right_half_back = recon_right_back & 0x1;
    down_half_back = recon_down_back & 0x1;

    /* Set dest to luminance plane of current pict image. */

    dest = vid_stream->current->luminance;

    /* If past frame exists, set past to luminance plane of past frame. */

    if (vid_stream->past != NULL)
      past = vid_stream->past->luminance;

    /*
     * If future frame exists, set future to luminance plane of future frame.
     */

    if (vid_stream->future != NULL)
      future = vid_stream->future->luminance;

    /* Establish row size. */

    row_size = (vid_stream->mb_width << 4);

    /* Calculate row,col of upper left pixel in block. */

    row = (mb_row << 4);
    col = (mb_col << 4);
    if (bnum > 1)
      row += 8;
    if (bnum & 0x01)
      col += 8;

#ifdef LOOSE_MPEG

    /* Check for illegal pred. blocks. */


    /* Illegal forward Y block, right component */
    if (col + right_for + right_half_for + 7 > lmaxx)
    {
      if(col > lmaxx)                                  // fix mb column 
	col = lmaxx & ~15; 
      if(col + right_for + 7 > lmaxx)                  // fix full component
	right_for = lmaxx - col - 7;
      if(col + right_for + right_half_for + 7 > lmaxx) // fix half component
	right_half_for = 0;
    }
    else if (col + right_for < 0)
    {
      if(col < 0)                                      // fix mb column
	col = 0;
      if(col + right_for < 0)                          // fix full component
	right_for = 0;
    }

    /* Illegal forward Y block, down component */
    if (row + down_for + down_half_for + 7 > lmaxy)
    {
      if(row > lmaxy)                                  // fix mb row
	row = lmaxy & ~15; 
      if(row + down_for + 7 > lmaxy)                   // fix full component
	down_for = lmaxy - row - 7;
      if(row + down_for + down_half_for + 7 > lmaxy)   // fix half component
	down_half_for = 0;
    }
    else if (row + down_for < 0)
    {
      if(row < 0)                                      // fix mb row
	row = 0;
      if(row + down_for < 0)                           // fix full component
	down_for = 0;
    }


    /* Illegal backward Y block, right component */
    if (col + right_back + right_half_back + 7 > lmaxx)
    {
      if(col > lmaxx)                                    // fix mb column 
	col = lmaxx & ~15; 
      if(col + right_back + 7 > lmaxx)                   // fix full component
	right_back = lmaxx - col - 7;
      if(col + right_back + right_half_back + 7 > lmaxx) // fix half component
	right_half_back = 0;
    }
    else if (col + right_back < 0)
    {
      if(col < 0)                                        // fix mb column
	col = 0;
      if(col + right_back < 0)                           // fix full component
	right_back = 0;
    }

    /* Illegal backward Y block, down component */
    if (row + down_back + down_half_back + 7 > lmaxy)
    {
      if(row > lmaxy)                                    // fix mb row
	row = lmaxy & ~15; 
      if(row + down_back + 7 > lmaxy)                    // fix full component
	down_back = lmaxy - row - 7;
      if(row + down_back + down_half_back + 7 > lmaxy)   // fix half component
	down_half_back = 0;
    }
    else if (row + down_back < 0)
    {
      if(row < 0)                                        // fix mb row
	row = 0;
      if(row + down_back < 0)                            // fix full component
	down_back = 0;
    }

#endif

    forw_col_start = col + right_for;
    forw_row_start = row + down_for;

    back_col_start = col + right_back;
    back_row_start = row + down_back;

  }
  /* Otherwise, block is NOT luminance block, ... */

  else {

    /* Construct motion vectors. */

    recon_right_for /= 2;
    recon_down_for /= 2;
    right_for = recon_right_for >> 1;
    down_for = recon_down_for >> 1;
    right_half_for = recon_right_for & 0x1;
    down_half_for = recon_down_for & 0x1;

    recon_right_back /= 2;
    recon_down_back /= 2;
    right_back = recon_right_back >> 1;
    down_back = recon_down_back >> 1;
    right_half_back = recon_right_back & 0x1;
    down_half_back = recon_down_back & 0x1;

    /* Establish row size. */

    row_size = (vid_stream->mb_width << 3);

    /* Calculate row,col of upper left pixel in block. */

    row = (mb_row << 3);
    col = (mb_col << 3);

#ifdef LOOSE_MPEG

    /* Check for illegal pred. blocks. */


    /* Illegal forward C block, right component */
    if (col + right_for + right_half_for + 7 > cmaxx)
    {
      if(col > cmaxx)                                  // fix mb column 
	col = cmaxx & ~15; 
      if(col + right_for + 7 > cmaxx)                  // fix full component
	right_for = cmaxx - col - 7;
      if(col + right_for + right_half_for + 7 > cmaxx) // fix half component
	right_half_for = 0;
    }
    else if (col + right_for < 0)
    {
      if(col < 0)                                      // fix mb column
	col = 0;
      if(col + right_for < 0)                          // fix full component
	right_for = 0;
    }

    /* Illegal forward C block, down component */
    if (row + down_for + down_half_for + 7 > cmaxy)
    {
      if(row > cmaxy)                                  // fix mb row
	row = cmaxy & ~15; 
      if(row + down_for + 7 > cmaxy)                   // fix full component
	down_for = cmaxy - row - 7;
      if(row + down_for + down_half_for + 7 > cmaxy)   // fix half component
	down_half_for = 0;
    }
    else if (row + down_for < 0)
    {
      if(row < 0)                                      // fix mb row
	row = 0;
      if(row + down_for < 0)                           // fix full component
	down_for = 0;
    }


    /* Illegal backward C block, right component */
    if (col + right_back + right_half_back + 7 > cmaxx)
    {
      if(col > cmaxx)                                    // fix mb column 
	col = cmaxx & ~15; 
      if(col + right_back + 7 > cmaxx)                   // fix full component
	right_back = cmaxx - col - 7;
      if(col + right_back + right_half_back + 7 > cmaxx) // fix half component
	right_half_back = 0;
    }
    else if (col + right_back < 0)
    {
      if(col < 0)                                        // fix mb column
	col = 0;
      if(col + right_back < 0)                           // fix full component
	right_back = 0;
    }

    /* Illegal backward C block, down component */
    if (row + down_back + down_half_back + 7 > cmaxy)
    {
      if(row > cmaxy)                                    // fix mb row
	row = cmaxy & ~15; 
      if(row + down_back + 7 > cmaxy)                    // fix full component
	down_back = cmaxy - row - 7;
      if(row + down_back + down_half_back + 7 > cmaxy)   // fix half component
	down_half_back = 0;
    }
    else if (row + down_back < 0)
    {
      if(row < 0)                                        // fix mb row
	row = 0;
      if(row + down_back < 0)                            // fix full component
	down_back = 0;
    }

#endif

    forw_col_start = col + right_for;
    forw_row_start = row + down_for;

    back_col_start = col + right_back;
    back_row_start = row + down_back;
    
    /* If block is Cr block... */
        /* Switched earlier, so we test Cr first - eyhung */

    if (bnum == 5) {

      /* Set dest to Cr plane of current pict image. */

      dest = vid_stream->current->Cr;

      /* If past frame exists, set past to Cr plane of past image. */

      if (vid_stream->past != NULL)
        past = vid_stream->past->Cr;

      /*
       * If future frame exists, set future to Cr plane of future image.
       */

      if (vid_stream->future != NULL)
        future = vid_stream->future->Cr;
    }
    /* Otherwise, block is Cb block... */

    else {

      /* Set dest to Cb plane of current pict image. */

      dest = vid_stream->current->Cb;

      /* If past frame exists, set past to Cb plane of past frame. */

      if (vid_stream->past != NULL)
        past = vid_stream->past->Cb;

      /*
       * If future frame exists, set future to Cb plane of future frame.
       */

      if (vid_stream->future != NULL)
        future = vid_stream->future->Cb;
    }
  }

  /* For each pixel in block... */

  index = dest + (row * row_size) + col;

#ifdef LOOSE_MPEG
  if (illegal_forw) 
    rindex1 = future + back_row_start * row_size + back_col_start;
  else 
#endif
    rindex1 = past + forw_row_start  * row_size + forw_col_start;

#ifdef LOOSE_MPEG
  if (illegal_back) 
    bindex1 = past + forw_row_start * row_size + forw_col_start;
  else 
#endif
    bindex1 = future + back_row_start * row_size + back_col_start;

  blockvals = (short int *) &(vid_stream->block.dct_recon[0][0]);

  {
#ifdef USE_CROP_TABLE
  unsigned char *cm = cropTbl + MAX_NEG_CROP;
#endif
  if (!zflag)
    for (rr = 0; rr < 4; rr++) {
      index[0] = crop(((int) (rindex1[0] + bindex1[0]) >> 1) + blockvals[0]);
      index[1] = crop(((int) (rindex1[1] + bindex1[1]) >> 1) + blockvals[1]);
      index[2] = crop(((int) (rindex1[2] + bindex1[2]) >> 1) + blockvals[2]);
      index[3] = crop(((int) (rindex1[3] + bindex1[3]) >> 1) + blockvals[3]);
      index[4] = crop(((int) (rindex1[4] + bindex1[4]) >> 1) + blockvals[4]);
      index[5] = crop(((int) (rindex1[5] + bindex1[5]) >> 1) + blockvals[5]);
      index[6] = crop(((int) (rindex1[6] + bindex1[6]) >> 1) + blockvals[6]);
      index[7] = crop(((int) (rindex1[7] + bindex1[7]) >> 1) + blockvals[7]);
      index += row_size;
      rindex1 += row_size;
      bindex1 += row_size;

      index[0] = crop(((int) (rindex1[0] + bindex1[0]) >> 1) + blockvals[8]);
      index[1] = crop(((int) (rindex1[1] + bindex1[1]) >> 1) + blockvals[9]);
      index[2] = crop(((int) (rindex1[2] + bindex1[2]) >> 1) + blockvals[10]);
      index[3] = crop(((int) (rindex1[3] + bindex1[3]) >> 1) + blockvals[11]);
      index[4] = crop(((int) (rindex1[4] + bindex1[4]) >> 1) + blockvals[12]);
      index[5] = crop(((int) (rindex1[5] + bindex1[5]) >> 1) + blockvals[13]);
      index[6] = crop(((int) (rindex1[6] + bindex1[6]) >> 1) + blockvals[14]);
      index[7] = crop(((int) (rindex1[7] + bindex1[7]) >> 1) + blockvals[15]);
      blockvals += 16;
      index += row_size;
      rindex1 += row_size;
      bindex1 += row_size;
    }

  else
    for (rr = 0; rr < 4; rr++) {
      index[0] = (int) (rindex1[0] + bindex1[0]) >> 1;
      index[1] = (int) (rindex1[1] + bindex1[1]) >> 1;
      index[2] = (int) (rindex1[2] + bindex1[2]) >> 1;
      index[3] = (int) (rindex1[3] + bindex1[3]) >> 1;
      index[4] = (int) (rindex1[4] + bindex1[4]) >> 1;
      index[5] = (int) (rindex1[5] + bindex1[5]) >> 1;
      index[6] = (int) (rindex1[6] + bindex1[6]) >> 1;
      index[7] = (int) (rindex1[7] + bindex1[7]) >> 1;
      index += row_size;
      rindex1 += row_size;
      bindex1 += row_size;

      index[0] = (int) (rindex1[0] + bindex1[0]) >> 1;
      index[1] = (int) (rindex1[1] + bindex1[1]) >> 1;
      index[2] = (int) (rindex1[2] + bindex1[2]) >> 1;
      index[3] = (int) (rindex1[3] + bindex1[3]) >> 1;
      index[4] = (int) (rindex1[4] + bindex1[4]) >> 1;
      index[5] = (int) (rindex1[5] + bindex1[5]) >> 1;
      index[6] = (int) (rindex1[6] + bindex1[6]) >> 1;
      index[7] = (int) (rindex1[7] + bindex1[7]) >> 1;
      index += row_size;
      rindex1 += row_size;
      bindex1 += row_size;
    }
  }
}

#endif /* USE_ATI */

/*
 *--------------------------------------------------------------
 *
 * ProcessSkippedPFrameMBlocks --
 *
 *    Processes skipped macroblocks in P frames.
 *
 * Results:
 *    Calculates pixel values for luminance, Cr, and Cb planes
 *      in current pict image for skipped macroblocks.
 *
 * Side effects:
 *    Pixel values in pict image changed.
 *
 *--------------------------------------------------------------
 */

static void ProcessSkippedPFrameMBlocks( VidStream* vid_stream )
{
  int row_size, half_row, mb_row, mb_col, row, col, rr;
  int addr, row_incr, half_row_incr, crow, ccol;
  int *dest, *src, *dest1, *src1;
#ifndef DISABLE_DITHER
  int ditherType=vid_stream->ditherType;
#endif

  /* Calculate row sizes for luminance and Cr/Cb macroblock areas. */

  row_size = vid_stream->mb_width << 4;
  half_row = (row_size >> 1);
  row_incr = row_size >> 2;
  half_row_incr = half_row >> 2;

  /* For each skipped macroblock, do... */

  for (addr = vid_stream->mblock.past_mb_addr + 1;
       addr < vid_stream->mblock.mb_address; addr++) {

    /* Calculate macroblock row and col. */

    mb_row = addr / vid_stream->mb_width;
    mb_col = addr % vid_stream->mb_width;

    /* Calculate upper left pixel row,col for luminance plane. */

    row = mb_row << 4;
    col = mb_col << 4;

#ifdef USE_ATI
    vhar128_macroblock(vid_stream->ati_handle,
		       col,
		       row,
		       0,    /* skipped block are empty non-intra blocks */
		       1, 0, /* P frames are based on past picture       */
		       0, 0, /* backward motion is null                  */
		       0, 0, /* forward  motion is null                  */
		       vid_stream->block.dct_recon);
#else

    /* For each row in macroblock luminance plane... */

    dest = (int *)(vid_stream->current->luminance + (row * row_size) + col);
    src = (int *)(vid_stream->future->luminance + (row * row_size) + col);

    for (rr = 0; rr < 8; rr++) {

      /* Copy pixel values from last I or P picture. */

      dest[0] = src[0];
      dest[1] = src[1];
      dest[2] = src[2];
      dest[3] = src[3];
      dest += row_incr;
      src += row_incr;

      dest[0] = src[0];
      dest[1] = src[1];
      dest[2] = src[2];
      dest[3] = src[3];
      dest += row_incr;
      src += row_incr;
    }

    /*
     * Divide row,col to get upper left pixel of macroblock in Cr and Cb
     * planes.
     */

    crow = row >> 1;
    ccol = col >> 1;

    /* For each row in Cr, and Cb planes... */

    dest = (int *)(vid_stream->current->Cr + (crow * half_row) + ccol);
    src = (int *)(vid_stream->future->Cr + (crow * half_row) + ccol);
    dest1 = (int *)(vid_stream->current->Cb + (crow * half_row) + ccol);
    src1 = (int *)(vid_stream->future->Cb + (crow * half_row) + ccol);

    for (rr = 0; rr < 4; rr++) {

      /* Copy pixel values from last I or P picture. */

      dest[0] = src[0];
      dest[1] = src[1];

      dest1[0] = src1[0];
      dest1[1] = src1[1];

      dest += half_row_incr;
      src += half_row_incr;
      dest1 += half_row_incr;
      src1 += half_row_incr;

      dest[0] = src[0];
      dest[1] = src[1];

      dest1[0] = src1[0];
      dest1[1] = src1[1];

      dest += half_row_incr;
      src += half_row_incr;
      dest1 += half_row_incr;
      src1 += half_row_incr;
    }

#ifndef DISABLE_DITHER
    if (ditherType == MBORDERED_DITHER) {
      MBOrderedDitherDisplayCopy(vid_stream, addr,
                                 1, 0, 0, 0, 0, 0,
                                 vid_stream->future->display,
                                 (unsigned char *) NULL);
      vid_stream->ditherFlags[addr] = 0;
    }
#endif
#endif /* USE_ATI */
  }

  vid_stream->mblock.recon_right_for_prev = 0;
  vid_stream->mblock.recon_down_for_prev = 0;
}


/*
 *--------------------------------------------------------------
 *
 * ProcessSkippedBFrameMBlocks --
 *
 *    Processes skipped macroblocks in B frames.
 *
 * Results:
 *    Calculates pixel values for luminance, Cr, and Cb planes
 *      in current pict image for skipped macroblocks.
 *
 * Side effects:
 *    Pixel values in pict image changed.
 *
 *--------------------------------------------------------------
 */

static void ProcessSkippedBFrameMBlocks( VidStream* vid_stream )
{
  int row_size, half_row, mb_row, mb_col, row, col, rr;
  int right_half_for = 0, down_half_for = 0;
  int c_right_half_for = 0, c_down_half_for = 0;
  int right_half_back = 0, down_half_back = 0;
  int c_right_half_back = 0, c_down_half_back = 0;
  int addr, right_for = 0, down_for = 0;
  int recon_right_for, recon_down_for;
  int recon_right_back, recon_down_back;
  int right_back = 0, down_back = 0;
  int c_right_for = 0, c_down_for = 0;
  int c_right_back = 0, c_down_back = 0;
  unsigned char forw_lum[256];
  unsigned char forw_cr[64], forw_cb[64];
  unsigned char back_lum[256], back_cr[64], back_cb[64];
  int row_incr, half_row_incr;
  int ccol, crow;
#ifndef DISABLE_DITHER
  int ditherType=vid_stream->ditherType;
#endif
#ifdef LOOSE_MPEG
  int lmaxx = vid_stream->mb_width*16-1;
  int lmaxy = vid_stream->mb_height*16-1;
  int cmaxx = vid_stream->mb_width*8-1;
  int cmaxy = vid_stream->mb_height*8-1;
#endif

  /* Calculate row sizes for luminance and Cr/Cb macroblock areas. */

  row_size = vid_stream->mb_width << 4;
  half_row = (row_size >> 1);
  row_incr = row_size >> 2;
  half_row_incr =  half_row >> 2;

  /* Establish motion vector codes based on full pixel flag. */

  if (vid_stream->picture.full_pel_forw_vector) {
    recon_right_for = vid_stream->mblock.recon_right_for_prev << 1;
    recon_down_for = vid_stream->mblock.recon_down_for_prev << 1;
  } else {
    recon_right_for = vid_stream->mblock.recon_right_for_prev;
    recon_down_for = vid_stream->mblock.recon_down_for_prev;
  }

  if (vid_stream->picture.full_pel_back_vector) {
    recon_right_back = vid_stream->mblock.recon_right_back_prev << 1;
    recon_down_back = vid_stream->mblock.recon_down_back_prev << 1;
  } else {
    recon_right_back = vid_stream->mblock.recon_right_back_prev;
    recon_down_back = vid_stream->mblock.recon_down_back_prev;
  }

#ifdef USE_ATI

  /* For each skipped macroblock do ... */
  for (addr = vid_stream->mblock.past_mb_addr + 1;
       addr < vid_stream->mblock.mb_address; addr++) {
    
    vhar128_macroblock(vid_stream->ati_handle,
		       (addr % vid_stream->mb_width) << 4,
		       (addr / vid_stream->mb_width) << 4,
		       0,                                  /* skipped blocks are empty non-intra blocks */
		       vid_stream->mblock.bpict_past_back, 
		       vid_stream->mblock.bpict_past_forw,
		       recon_right_back, recon_down_back,  /* backward motion */
		       recon_right_for, recon_down_for,    /* forward  motion */
		       vid_stream->block.dct_recon);
  }

#else

  /* If only one motion vector, do display copy, else do full
     calculation. 
  */

#ifndef DISABLE_DITHER
  if (ditherType == MBORDERED_DITHER) {
    if (vid_stream->mblock.bpict_past_forw &&
        !vid_stream->mblock.bpict_past_back) {
      for (addr = vid_stream->mblock.past_mb_addr+1;
           addr < vid_stream->mblock.mb_address; addr++) {
        
        MBOrderedDitherDisplayCopy(vid_stream, addr,
                                   1, recon_right_for, recon_down_for,
                                 0, 0, 0, vid_stream->past->display,
                                   vid_stream->future->display);
        vid_stream->ditherFlags[addr] = 0;
      }
      return;
    }
    if (vid_stream->mblock.bpict_past_back && 
        !vid_stream->mblock.bpict_past_forw) {
      for (addr = vid_stream->mblock.past_mb_addr+1;
           addr < vid_stream->mblock.mb_address; addr++) {
        
        MBOrderedDitherDisplayCopy(vid_stream, addr,
                                   0, 0, 0,
                                   1, recon_right_back, recon_down_back,
                                   vid_stream->past->display, vid_stream->future->display);
        vid_stream->ditherFlags[addr] = 0;
      }
      return;
    }
  }
#endif

  /* Calculate motion vectors. */
  
  if (vid_stream->mblock.bpict_past_forw) {
    right_for = recon_right_for >> 1;
    down_for = recon_down_for >> 1;
    right_half_for = recon_right_for & 0x1;
    down_half_for = recon_down_for & 0x1;
    
    recon_right_for /= 2;
    recon_down_for /= 2;
    c_right_for = recon_right_for >> 1;
    c_down_for = recon_down_for >> 1;
    c_right_half_for = recon_right_for & 0x1;
    c_down_half_for = recon_down_for & 0x1;
    
  }
  if (vid_stream->mblock.bpict_past_back) {
    right_back = recon_right_back >> 1;
    down_back = recon_down_back >> 1;
    right_half_back = recon_right_back & 0x1;
    down_half_back = recon_down_back & 0x1;
    
    recon_right_back /= 2;
    recon_down_back /= 2;
    c_right_back = recon_right_back >> 1;
    c_down_back = recon_down_back >> 1;
    c_right_half_back = recon_right_back & 0x1;
    c_down_half_back = recon_down_back & 0x1;
    
  }
  /* For each skipped macroblock, do... */
  
  for (addr = vid_stream->mblock.past_mb_addr + 1;
       addr < vid_stream->mblock.mb_address; addr++) {
    
    /* Calculate macroblock row and col. */
    
    mb_row = addr / vid_stream->mb_width;
    mb_col = addr % vid_stream->mb_width;
    
    /* Calculate upper left pixel row,col for luminance plane. */
    
    row = mb_row << 4;
    col = mb_col << 4;
    crow = row / 2;
    ccol = col / 2;

#ifdef LOOSE_MPEG

    /* Check for illegal blocks. */

    /* Illegal forward Y block, right component */
    if (col + right_for + right_half_for + 7 > lmaxx)
    {
      if(col > lmaxx)                                  // fix mb column 
	col = lmaxx & ~15; 
      if(col + right_for + 7 > lmaxx)                  // fix full component
	right_for = lmaxx - col - 7;
      if(col + right_for + right_half_for + 7 > lmaxx) // fix half component
	right_half_for = 0;
    }
    else if (col + right_for < 0)
    {
      if(col < 0)                                      // fix mb column
	col = 0;
      if(col + right_for < 0)                          // fix full component
	right_for = 0;
    }

    /* Illegal forward Y block, down component */
    if (row + down_for + down_half_for + 7 > lmaxy)
    {
      if(row > lmaxy)                                  // fix mb row
	row = lmaxy & ~15; 
      if(row + down_for + 7 > lmaxy)                   // fix full component
	down_for = lmaxy - row - 7;
      if(row + down_for + down_half_for + 7 > lmaxy)   // fix half component
	down_half_for = 0;
    }
    else if (row + down_for < 0)
    {
      if(row < 0)                                      // fix mb row
	row = 0;
      if(row + down_for < 0)                           // fix full component
	down_for = 0;
    }


    /* Illegal backward Y block, right component */
    if (col + right_back + right_half_back + 7 > lmaxx)
    {
      if(col > lmaxx)                                    // fix mb column 
	col = lmaxx & ~15; 
      if(col + right_back + 7 > lmaxx)                   // fix full component
	right_back = lmaxx - col - 7;
      if(col + right_back + right_half_back + 7 > lmaxx) // fix half component
	right_half_back = 0;
    }
    else if (col + right_back < 0)
    {
      if(col < 0)                                        // fix mb column
	col = 0;
      if(col + right_back < 0)                           // fix full component
	right_back = 0;
    }

    /* Illegal backward Y block, down component */
    if (row + down_back + down_half_back + 7 > lmaxy)
    {
      if(row > lmaxy)                                    // fix mb row
	row = lmaxy & ~15; 
      if(row + down_back + 7 > lmaxy)                    // fix full component
	down_back = lmaxy - row - 7;
      if(row + down_back + down_half_back + 7 > lmaxy)   // fix half component
	down_half_back = 0;
    }
    else if (row + down_back < 0)
    {
      if(row < 0)                                        // fix mb row
	row = 0;
      if(row + down_back < 0)                            // fix full component
	down_back = 0;
    }


    /* Illegal forward C block, right component */
    if (ccol + c_right_for + c_right_half_for + 7 > cmaxx)
    {
      if(ccol > cmaxx)                                      // fix mb column 
	ccol = cmaxx & ~15; 
      if(ccol + c_right_for + 7 > cmaxx)                    // fix full component
	c_right_for = cmaxx - ccol - 7;
      if(ccol + c_right_for + c_right_half_for + 7 > cmaxx) // fix half component
	c_right_half_for = 0;
    }
    else if (ccol + c_right_for < 0)
    {
      if(ccol < 0)                                          // fix mb column
	ccol = 0;
      if(ccol + c_right_for < 0)                            // fix full component
	c_right_for = 0;
    }

    /* Illegal forward C block, down component */
    if (crow + c_down_for + c_down_half_for + 7 > cmaxy)
    {
      if(crow > cmaxy)                                      // fix mb row
	crow = cmaxy & ~15; 
      if(crow + c_down_for + 7 > cmaxy)                     // fix full component
	c_down_for = cmaxy - crow - 7;
      if(crow + c_down_for + c_down_half_for + 7 > cmaxy)   // fix half component
	c_down_half_for = 0;
    }
    else if (crow + c_down_for < 0)
    {
      if(crow < 0)                                           // fix mb row
	crow = 0;
      if(crow + c_down_for < 0)                              // fix full component
	c_down_for = 0;
    }

    /* Illegal backward C block, right component */
    if (ccol + c_right_back + c_right_half_back + 7 > cmaxx)
    {
      if(ccol > cmaxx)                                        // fix mb column 
	ccol = cmaxx & ~15; 
      if(ccol + c_right_back + 7 > cmaxx)                     // fix full component
	c_right_back = cmaxx - ccol - 7;
      if(ccol + c_right_back + c_right_half_back + 7 > cmaxx) // fix half component
	c_right_half_back = 0;
    }
    else if (ccol + c_right_back < 0)
    {
      if(ccol < 0)                                            // fix mb column
	ccol = 0;
      if(ccol + c_right_back < 0)                             // fix full component
	c_right_back = 0;
    }

    /* Illegal backward C block, down component */
    if (crow + c_down_back + c_down_half_back + 7 > cmaxy)
    {
      if(crow > cmaxy)                                        // fix mb row
	crow = cmaxy & ~15; 
      if(crow + c_down_back + 7 > cmaxy)                      // fix full component
	c_down_back = cmaxy - crow - 7;
      if(crow + c_down_back + c_down_half_back + 7 > cmaxy)   // fix half component
	c_down_half_back = 0;
    }
    else if (crow + c_down_back < 0)
    {
      if(crow < 0)                                            // fix mb row
	crow = 0;
      if(crow + c_down_back < 0)                              // fix full component
	c_down_back = 0;
    }

#endif

    /* If forward predicted, calculate prediction values. */
    
    if (vid_stream->mblock.bpict_past_forw) {
      
      ReconSkippedBlock(vid_stream->past->luminance, forw_lum,
                        row, col, row_size, right_for, down_for,
                        right_half_for, down_half_for, 16);
      ReconSkippedBlock(vid_stream->past->Cr, forw_cr, crow,
                        ccol, half_row,
                        c_right_for, c_down_for, c_right_half_for, c_down_half_for, 8);
      ReconSkippedBlock(vid_stream->past->Cb, forw_cb, crow,
                        ccol, half_row,
                        c_right_for, c_down_for, c_right_half_for, c_down_half_for, 8);
    }
    /* If back predicted, calculate prediction values. */
    
    if (vid_stream->mblock.bpict_past_back) {
      ReconSkippedBlock(vid_stream->future->luminance, back_lum,
                        row, col, row_size, right_back, down_back,
                        right_half_back, down_half_back, 16);
      ReconSkippedBlock(vid_stream->future->Cr, back_cr, crow,
                        ccol, half_row,
                        c_right_back, c_down_back,
                        c_right_half_back, c_down_half_back, 8);
      ReconSkippedBlock(vid_stream->future->Cb, back_cb, crow,
                        ccol, half_row,
                        c_right_back, c_down_back,
                        c_right_half_back, c_down_half_back, 8);
    }
    if (vid_stream->mblock.bpict_past_forw &&
        !vid_stream->mblock.bpict_past_back) {
      
      int *dest, *dest1;
      int *src, *src1;
      dest = (int *)(vid_stream->current->luminance + (row * row_size) + col);
      src = (int *)forw_lum;
      
      for (rr = 0; rr < 16; rr++) {
        
        /* memcpy(dest, forw_lum+(rr<<4), 16);  */
        dest[0] = src[0];
        dest[1] = src[1];
        dest[2] = src[2];
        dest[3] = src[3];
        dest += row_incr;
        src += 4;
      }
      
      dest = (int *)(vid_stream->current->Cr + (crow * half_row) + ccol);
      dest1 = (int *)(vid_stream->current->Cb + (crow * half_row) + ccol);
      src = (int *)forw_cr;
      src1 = (int *)forw_cb;
      
      for (rr = 0; rr < 8; rr++) {
        /*
         * memcpy(dest, forw_cr+(rr<<3), 8); memcpy(dest1, forw_cb+(rr<<3),
         * 8);
         */
        
        dest[0] = src[0];
        dest[1] = src[1];
        
        dest1[0] = src1[0];
        dest1[1] = src1[1];
        
        dest += half_row_incr;
        dest1 += half_row_incr;
        src += 2;
        src1 += 2;
      }
    } else if (vid_stream->mblock.bpict_past_back &&
               !vid_stream->mblock.bpict_past_forw) {
      
      int *src, *src1;
      int *dest, *dest1;
      dest = (int *)(vid_stream->current->luminance + (row * row_size) + col);
      src = (int *)back_lum;
      
      for (rr = 0; rr < 16; rr++) {
        dest[0] = src[0];
        dest[1] = src[1];
        dest[2] = src[2];
        dest[3] = src[3];
        dest += row_incr;
        src += 4;
      }
      
      
      dest = (int *)(vid_stream->current->Cr + (crow * half_row) + ccol);
      dest1 = (int *)(vid_stream->current->Cb + (crow * half_row) + ccol);
      src = (int *)back_cr;
      src1 = (int *)back_cb;
      
      for (rr = 0; rr < 8; rr++) {
        /*
         * memcpy(dest, back_cr+(rr<<3), 8); memcpy(dest1, back_cb+(rr<<3),
         * 8);
         */
        
        dest[0] = src[0];
        dest[1] = src[1];
        
        dest1[0] = src1[0];
        dest1[1] = src1[1];
        
        dest += half_row_incr;
        dest1 += half_row_incr;
        src += 2;
        src1 += 2;
      }
    } else {
      
      unsigned char *src1, *src2, *src1a, *src2a;
      unsigned char *dest, *dest1;
      dest = vid_stream->current->luminance + (row * row_size) + col;
      src1 = forw_lum;
      src2 = back_lum;
      
      for (rr = 0; rr < 16; rr++) {
        dest[0] = (int) (src1[0] + src2[0]) >> 1;
        dest[1] = (int) (src1[1] + src2[1]) >> 1;
        dest[2] = (int) (src1[2] + src2[2]) >> 1;
        dest[3] = (int) (src1[3] + src2[3]) >> 1;
        dest[4] = (int) (src1[4] + src2[4]) >> 1;
        dest[5] = (int) (src1[5] + src2[5]) >> 1;
        dest[6] = (int) (src1[6] + src2[6]) >> 1;
        dest[7] = (int) (src1[7] + src2[7]) >> 1;
        dest[8] = (int) (src1[8] + src2[8]) >> 1;
        dest[9] = (int) (src1[9] + src2[9]) >> 1;
        dest[10] = (int) (src1[10] + src2[10]) >> 1;
        dest[11] = (int) (src1[11] + src2[11]) >> 1;
        dest[12] = (int) (src1[12] + src2[12]) >> 1;
        dest[13] = (int) (src1[13] + src2[13]) >> 1;
        dest[14] = (int) (src1[14] + src2[14]) >> 1;
        dest[15] = (int) (src1[15] + src2[15]) >> 1;
        dest += row_size;
        src1 += 16;
        src2 += 16;
      }
      
      
      dest = vid_stream->current->Cr + (crow * half_row) + ccol;
      dest1 = vid_stream->current->Cb + (crow * half_row) + ccol;
      src1 = forw_cr;
      src2 = back_cr;
      src1a = forw_cb;
      src2a = back_cb;
      
      for (rr = 0; rr < 8; rr++) {
        dest[0] = (int) (src1[0] + src2[0]) >> 1;
        dest[1] = (int) (src1[1] + src2[1]) >> 1;
        dest[2] = (int) (src1[2] + src2[2]) >> 1;
        dest[3] = (int) (src1[3] + src2[3]) >> 1;
        dest[4] = (int) (src1[4] + src2[4]) >> 1;
        dest[5] = (int) (src1[5] + src2[5]) >> 1;
        dest[6] = (int) (src1[6] + src2[6]) >> 1;
        dest[7] = (int) (src1[7] + src2[7]) >> 1;
        dest += half_row;
        src1 += 8;
        src2 += 8;
        
        dest1[0] = (int) (src1a[0] + src2a[0]) >> 1;
        dest1[1] = (int) (src1a[1] + src2a[1]) >> 1;
        dest1[2] = (int) (src1a[2] + src2a[2]) >> 1;
        dest1[3] = (int) (src1a[3] + src2a[3]) >> 1;
        dest1[4] = (int) (src1a[4] + src2a[4]) >> 1;
        dest1[5] = (int) (src1a[5] + src2a[5]) >> 1;
        dest1[6] = (int) (src1a[6] + src2a[6]) >> 1;
        dest1[7] = (int) (src1a[7] + src2a[7]) >> 1;
        dest1 += half_row;
        src1a += 8;
        src2a += 8;
      }
    }
    
#ifndef DISABLE_DITHER
    if (ditherType == MBORDERED_DITHER) {
      vid_stream->ditherFlags[addr] = 1;
    }
#endif
  }
#endif /* USE_ATI */
}


#ifndef USE_ATI

/*
 *--------------------------------------------------------------
 *
 * ReconSkippedBlock --
 *
 *    Reconstructs predictive block for skipped macroblocks
 *      in B Frames.
 *
 * Results:
 *    No return values.
 *
 * Side effects:
 *    None.
 *
 *--------------------------------------------------------------
 */

static void ReconSkippedBlock( unsigned char *source, unsigned char *dest,
      int row, int col, int row_size, int right, int down,
      int right_half, int down_half, int width )
{
  int rr;
  unsigned char *source2;

  source += ((row + down) * row_size) + col + right;

  if (width == 16) {
    if ((!right_half) && (!down_half)) {
        if (right & 0x1) {
          /* No alignment, use bye copy */
          for (rr = 0; rr < 16; rr++) {
            dest[0] = source[0];
            dest[1] = source[1];
            dest[2] = source[2];
            dest[3] = source[3];
            dest[4] = source[4];
            dest[5] = source[5];
            dest[6] = source[6];
            dest[7] = source[7];
            dest[8] = source[8];
            dest[9] = source[9];
            dest[10] = source[10];
            dest[11] = source[11];
            dest[12] = source[12];
            dest[13] = source[13];
            dest[14] = source[14];
            dest[15] = source[15];
            dest += 16;
            source += row_size;
          }
        } else if (right & 0x2) {
          /* Half-word bit aligned, use 16 bit copy */
          short *src = (short *)source;
          short *d = (short *)dest;
          row_size >>= 1;
          for (rr = 0; rr < 16; rr++) {
            d[0] = src[0];
            d[1] = src[1];
            d[2] = src[2];
            d[3] = src[3];
            d[4] = src[4];
            d[5] = src[5];
            d[6] = src[6];
            d[7] = src[7];
            d += 8;
            src += row_size;
          }
        } else {
          /* Word aligned, use 32 bit copy */
          int *src = (int *)source;
          int *d = (int *)dest;
          row_size >>= 2;
          for (rr = 0; rr < 16; rr++) {
            d[0] = src[0];
            d[1] = src[1];
            d[2] = src[2];
            d[3] = src[3];
            d += 4;
            src += row_size;
          }
        }
    } else {
      source2 = source + right_half + (row_size * down_half);
      for (rr = 0; rr < width; rr++) {
        dest[0] = (int) (source[0] + source2[0]) >> 1;
        dest[1] = (int) (source[1] + source2[1]) >> 1;
        dest[2] = (int) (source[2] + source2[2]) >> 1;
        dest[3] = (int) (source[3] + source2[3]) >> 1;
        dest[4] = (int) (source[4] + source2[4]) >> 1;
        dest[5] = (int) (source[5] + source2[5]) >> 1;
        dest[6] = (int) (source[6] + source2[6]) >> 1;
        dest[7] = (int) (source[7] + source2[7]) >> 1;
        dest[8] = (int) (source[8] + source2[8]) >> 1;
        dest[9] = (int) (source[9] + source2[9]) >> 1;
        dest[10] = (int) (source[10] + source2[10]) >> 1;
        dest[11] = (int) (source[11] + source2[11]) >> 1;
        dest[12] = (int) (source[12] + source2[12]) >> 1;
        dest[13] = (int) (source[13] + source2[13]) >> 1;
        dest[14] = (int) (source[14] + source2[14]) >> 1;
        dest[15] = (int) (source[15] + source2[15]) >> 1;
        dest += width;
        source += row_size;
        source2 += row_size;
      }
    }
  } else {                        /* (width == 8) */
    assert(width == 8);
    if ((!right_half) && (!down_half)) {
      if (right & 0x1) {
        for (rr = 0; rr < width; rr++) {
          dest[0] = source[0];
          dest[1] = source[1];
          dest[2] = source[2];
          dest[3] = source[3];
          dest[4] = source[4];
          dest[5] = source[5];
          dest[6] = source[6];
          dest[7] = source[7];
          dest += 8;
          source += row_size;
        }
      } else if (right & 0x02) {
        short *d = (short *)dest;
        short *src = (short *)source;
        row_size >>= 1;
        for (rr = 0; rr < width; rr++) {
          d[0] = src[0];
          d[1] = src[1];
          d[2] = src[2];
          d[3] = src[3];
          d += 4;
          src += row_size;
        }
      } else {
        int *d = (int *)dest;
        int *src = (int *)source;
        row_size >>= 2;
        for (rr = 0; rr < width; rr++) {
          d[0] = src[0];
          d[1] = src[1];
          d += 2;
          src += row_size;
        }
      }
    } else {
      source2 = source + right_half + (row_size * down_half);
      for (rr = 0; rr < width; rr++) {
        dest[0] = (int) (source[0] + source2[0]) >> 1;
        dest[1] = (int) (source[1] + source2[1]) >> 1;
        dest[2] = (int) (source[2] + source2[2]) >> 1;
        dest[3] = (int) (source[3] + source2[3]) >> 1;
        dest[4] = (int) (source[4] + source2[4]) >> 1;
        dest[5] = (int) (source[5] + source2[5]) >> 1;
        dest[6] = (int) (source[6] + source2[6]) >> 1;
        dest[7] = (int) (source[7] + source2[7]) >> 1;
        dest += width;
        source += row_size;
        source2 += row_size;
      }
    }
  }
}

#endif /* USE_ATI */

/*
 *--------------------------------------------------------------
 *
 * DoPictureDisplay --
 *
 *    Converts image from Lum, Cr, Cb to colormap space. Puts
 *      image in lum plane. Updates past and future frame
 *      pointers. Dithers image. Sends to display mechanism.
 *
 * Results:
 *    Pict image structure locked if displaying or if frame
 *      is needed as past or future reference.
 *
 * Side effects:
 *    Lum plane pummelled.
 *
 *--------------------------------------------------------------
 */

static void DoPictureDisplay( VidStream *vid_stream )
{
#ifdef ANALYSIS
    EndTime();
    stat_a[0].totsize += bitCountRead() - pictureSizeCount;
#ifdef SHOWEACHFLAG
    PrintOneStat();
#endif
    CollectStats();
#endif

#ifdef USE_ATI
    /* Flush the macroblocks to the hardware decoder */
    vhar128_flush( vid_stream->ati_handle );
#endif

    /* Update past and future references if needed. */

    if( (vid_stream->picture.code_type == I_TYPE) ||
        (vid_stream->picture.code_type == P_TYPE) )
    {
        if( vid_stream->future == NULL )
        {
            vid_stream->future = vid_stream->current;
            vid_stream->future->locked |= FUTURE_LOCK;
        }
        else
        {
            if( vid_stream->past != NULL )
            {
                vid_stream->past->locked &= ~PAST_LOCK;
            }
            vid_stream->past = vid_stream->future;
            vid_stream->past->locked &= ~FUTURE_LOCK;
            vid_stream->past->locked |= PAST_LOCK;
            vid_stream->future = vid_stream->current;
            vid_stream->future->locked |= FUTURE_LOCK;
            vid_stream->current = vid_stream->past;

            vid_stream->_smpeg->ExecuteDisplay( vid_stream );
        }
    }
    else
    {
        vid_stream->_smpeg->ExecuteDisplay( vid_stream );
    }
}

/* EOF */
