/*===========================================================================*
 * frame.c								     *
 *									     *
 *	basic frame procedures						     *
 *									     *
 * EXPORTED PROCEDURES:							     *
 *	Frame_Init							     *
 *	Frame_Exit							     *
 *	Frame_New							     *
 *	Frame_Free							     *
 *	Frame_AllocPPM							     *
 *	Frame_AllocBlocks						     *
 *	Frame_AllocYCC							     *
 *	Frame_AllocDecoded						     *
 *	Frame_AllocHalf						             *
 *	Frame_Resize						             * 
 *									     *
 *===========================================================================*/

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


/*==============*
 * HEADER FILES *
 *==============*/

#include <string.h>
#include "all.h"
#include "mtypes.h"
#include "frames.h"
#include "frame.h"
#include "fsize.h"
#include "dct.h"

/*===========*
 * CONSTANTS *
 *===========*/

/* The maximum number of B-Frames allowed between reference frames. */
#define  B_FRAME_RUN  16    

/*==================*
 * GLOBAL VARIABLES *
 *==================*/

MpegFrame      *frameMemory[B_FRAME_RUN+2];
extern boolean stdinUsed;
extern char    *framePattern;


/*===============================*
 * INTERNAL PROCEDURE prototypes *
 *===============================*/

static void FreeFrame _ANSI_ARGS_((MpegFrame * mf));
static MpegFrame *GetUnusedFrame _ANSI_ARGS_((void));
static void GetNumOfFrames _ANSI_ARGS_((int *numOfFrames));
static void ResetFrame _ANSI_ARGS_((int fnumber, int type, MpegFrame *frame));
static void Resize_Width _ANSI_ARGS_((MpegFrame *omfrw,MpegFrame *mfrw, int in_x,
       int in_y, int out_x));
static void Resize_Height _ANSI_ARGS_((MpegFrame *omfrh,MpegFrame *mfrh,
       int in_x,
       int in_y,  int out_y));
static void Resize_Array_Width _ANSI_ARGS_((uint8 **inarray,int in_x,
       int in_y,uint8 **outarray, int out_x));
static void Resize_Array_Height _ANSI_ARGS_((uint8 **inarray,int in_x,
       int in_y,uint8 **outarray, int out_y));


/*=====================*
 * EXPORTED PROCEDURES *
 *=====================*/

/*===============================================================
 *
 * Frame_Resize                  by James Boucher
 *                Boston University Multimedia Communications Lab
 *  
 *     This function takes the mf input frame, read in READFrame(),
 * and resizes all the input component arrays to the output
 * dimensions specified in the parameter file as OUT_SIZE.
 * The new frame is returned with the omf pointer.  As well,
 * the values of Fsize_x and Fsize_y are adjusted.
 ***************************************************************/
void
Frame_Resize(MpegFrame *omf, MpegFrame *mf,
             int insize_x, int insize_y, int outsize_x, int outsize_y)
{
MpegFrame *frameA;  /* intermediate frame */

frameA = (MpegFrame *)malloc(sizeof(MpegFrame));

if((insize_x != outsize_x)&&(insize_y != outsize_y)){
Resize_Width(frameA,mf,insize_x,insize_y,outsize_x);
Resize_Height(omf,frameA,outsize_x,insize_y,outsize_y);
}else 
if((insize_x ==outsize_x)&&(insize_y != outsize_y)){
Resize_Height(omf,mf,insize_x,insize_y,outsize_y);
} else
if((insize_x !=outsize_x)&&(insize_y == outsize_y)){
Resize_Width(omf,mf,insize_x,insize_y,outsize_x);
}
else{
  throw "Problem in Frame_Resize";
  }
/* Free memory */
free(frameA);
free(mf);
}
/*========================================================
* Resize_Width
*======================================================*/
static void  
Resize_Width(MpegFrame *omfrw, MpegFrame *mfrw,
             int in_x, int in_y, int out_x)
{
register int y;
int i;

omfrw->orig_y = NULL;
Fsize_x = out_x;
/* Allocate new frame memory */
    omfrw->orig_y = (uint8 **) malloc(sizeof(uint8 *) * Fsize_y);
    ERRCHK(omfrw->orig_y, "malloc");
    for (y = 0; y < Fsize_y; y++) {
	omfrw->orig_y[y] = (uint8 *) malloc(sizeof(uint8) * out_x);
	ERRCHK(omfrw->orig_y[y], "malloc");
    }

    omfrw->orig_cr = (uint8 **) malloc(sizeof(int8 *) * Fsize_y / 2);
    ERRCHK(omfrw->orig_cr, "malloc");
    for (y = 0; y < Fsize_y / 2; y++) {
	omfrw->orig_cr[y] = (uint8 *) malloc(sizeof(int8) * out_x / 2);
	ERRCHK(omfrw->orig_cr[y], "malloc");
    }

    omfrw->orig_cb = (uint8 **) malloc(sizeof(int8 *) * Fsize_y / 2);
    ERRCHK(omfrw->orig_cb, "malloc");
    for (y = 0; y < Fsize_y / 2; y++) {
	omfrw->orig_cb[y] = (uint8 *) malloc(sizeof(int8) * out_x / 2);
	ERRCHK(omfrw->orig_cb[y], "malloc");
    }

    if ( referenceFrame == ORIGINAL_FRAME ) {
	omfrw->ref_y = omfrw->orig_y;
	omfrw->ref_cr = omfrw->orig_cr;
	omfrw->ref_cb = omfrw->orig_cb;
    }

/* resize each component array separately */
Resize_Array_Width(mfrw->orig_y,in_x,in_y,omfrw->orig_y,out_x);
Resize_Array_Width(mfrw->orig_cr,(in_x/2),(in_y/2),omfrw->orig_cr,(out_x/2));
Resize_Array_Width(mfrw->orig_cb,(in_x/2),(in_y/2),omfrw->orig_cb,(out_x/2));

/* Free old frame memory */
    if (mfrw->orig_y) {
	for (i = 0; i < in_y; i++) {
	    free(mfrw->orig_y[i]);
	}
	free(mfrw->orig_y);

	for (i = 0; i < in_y / 2; i++) {
	    free(mfrw->orig_cr[i]);
	}
	free(mfrw->orig_cr);

	for (i = 0; i < in_y / 2; i++) {
	    free(mfrw->orig_cb[i]);
	}
	free(mfrw->orig_cb);
    }

}

/*=======================================================
* Resize_Height
*
*   Resize Frame height up or down
*=======================================================*/
static  void
Resize_Height(MpegFrame *omfrh, MpegFrame *mfrh,
              int in_x, int in_y, int out_y)
{
register int y; 
int i;

Fsize_y = out_y;

/* Allocate new frame memory */
    omfrh->orig_y = (uint8 **) malloc(sizeof(uint8 *) * out_y);
    ERRCHK(omfrh->orig_y, "malloc");
    for (y = 0; y < out_y; y++) {
	omfrh->orig_y[y] = (uint8 *) malloc(sizeof(uint8) * Fsize_x);
	ERRCHK(omfrh->orig_y[y], "malloc");
    }

    omfrh->orig_cr = (uint8 **) malloc(sizeof(int8 *) * out_y / 2);
    ERRCHK(omfrh->orig_cr, "malloc");
    for (y = 0; y < out_y / 2; y++) {
	omfrh->orig_cr[y] = (uint8 *) malloc(sizeof(int8) * Fsize_x / 2);
	ERRCHK(omfrh->orig_cr[y], "malloc");
    }

    omfrh->orig_cb = (uint8 **) malloc(sizeof(int8 *) * out_y / 2);
    ERRCHK(omfrh->orig_cb, "malloc");
    for (y = 0; y < out_y / 2; y++) {
	omfrh->orig_cb[y] = (uint8 *) malloc(sizeof(int8) * Fsize_x / 2);
	ERRCHK(omfrh->orig_cb[y], "malloc");
    }

    if ( referenceFrame == ORIGINAL_FRAME ) {
	omfrh->ref_y = omfrh->orig_y;
	omfrh->ref_cr = omfrh->orig_cr;
	omfrh->ref_cb = omfrh->orig_cb;
    }

/* resize component arrays separately */
Resize_Array_Height(mfrh->orig_y,in_x,in_y,omfrh->orig_y,out_y);
Resize_Array_Height(mfrh->orig_cr,(in_x/2),(in_y/2),omfrh->orig_cr,(out_y/2));
Resize_Array_Height(mfrh->orig_cb,(in_x/2),(in_y/2),omfrh->orig_cb,(out_y/2));

/* Free old frame memory */
    if (mfrh->orig_y) {
	for (i = 0; i < in_y; i++) {
	    free(mfrh->orig_y[i]);
	}
	free(mfrh->orig_y);

	for (i = 0; i < in_y / 2; i++) {
	    free(mfrh->orig_cr[i]);
	}
	free(mfrh->orig_cr);

	for (i = 0; i < in_y / 2; i++) {
	    free(mfrh->orig_cb[i]);
	}
	free(mfrh->orig_cb);
    }

}
/*====================================================
* Resize_Array_Width
*    
*   This function will resize any array width up
* or down in size.  The algorithm is based on the
* least common multiple approach more commonly
* used in audio frequency adjustments.
*=====================================================*/
static void 
Resize_Array_Width(uint8 **inarray,
                   int in_x,
                   int in_y,
                   uint8 **outarray,
                   int out_x)
{
int i,j; 
int in_total;
int out_total;
uint8 *inptr;
uint8 *outptr;
uint8 pointA,pointB;
/* double slope,diff; */

 for(i=0;i<in_y;i++){     /* For every row */
  inptr = &inarray[i][0];
  outptr = &outarray[i][0];
  in_total = 0;
  out_total = 0;
  for(j=0;j<out_x;j++){      /* For every output value */
    if(in_total == out_total){  
      *outptr = *inptr;
      outptr++;
      out_total=out_total+in_x;
      while(in_total < out_total){
	in_total = in_total + out_x;
	inptr++;
      }
      if(in_total > out_total){
	in_total = in_total - out_x;
	inptr--;
      }
    } else {  
      pointA = *inptr;
      inptr++;
      pointB = *inptr;
      inptr--;
/*Interpolative solution */
/*      slope = ((double)(pointB -pointA))/((double)(out_x));
      diff = (((double)(out_total - in_total)));
      if(diff < (out_x/2)){
      *outptr = (pointA + (uint8)(slope*diff));
    } else {
      *outptr = (pointB - (uint8)(slope*(((float)(out_x)) - diff)));
    } */
/* Non-Interpolative solution */
    *outptr = *inptr;  

      outptr++;
      out_total=out_total+in_x;
      while(in_total < out_total){
	in_total = in_total + out_x;
	inptr++;
      }
      if(in_total > out_total){
	in_total = in_total - out_x;
	inptr--;
      }
    }  /* end if */
  }  /* end for each output value */

 }  /* end for each row */
}  /* end main */
/*==============================
* Resize_Array_Height
*
*    Resize any array height larger or smaller.
* Same as Resize_array_Width except pointer
* manipulation must change.
*===============================*/
static void 
Resize_Array_Height(uint8 **inarray,
                    int in_x,
                    int in_y,
                    uint8 **outarray,
                    int out_y)
{
int i,j,k; 
int in_total;
int out_total;
uint8 pointA,pointB;
double slope,diff;

 for(i=0;i<in_x;i++){    /* for each column */
  in_total = 0;
  out_total = 0;
  k = 0;
  for(j=0;j<out_y;j++){  /* for each output value */
    if(in_total == out_total){  
      outarray[j][i] = inarray[k][i];
      out_total=out_total+in_y;
      while(in_total < out_total){
	in_total = in_total + out_y;
	k++;
      }
      if(in_total > out_total){
	in_total = in_total - out_y;
	k--;
      }
    } else {  
 
      pointA = inarray[k][i];
      if(k != (in_y -1)){
      pointB = inarray[k+1][i];
      } else {
      pointB = pointA;
      }
/* Interpolative case */
      slope = ((double)(pointB -pointA))/(double)(out_y);
      diff = (double)(out_total - in_total);
/*      outarray[j][i] = (inarray[k][i] + (uint8)(slope*diff));
*/
/* Non-Interpolative case */
    outarray[j][i] = inarray[k][i];
      out_total=out_total+in_y;
      while(in_total < out_total){
	in_total = in_total + out_y;
	k++;
      }
      if(in_total > out_total){
	in_total = in_total - out_y;
	k--;
      }
    } 
  }
 }

}



/*===========================================================================*
 *
 * Frame_Init
 *
 *	initializes the memory associated with all frames ever
 *	If the input is not coming in from stdin, only 3 frames are needed ;
 *      else, the program must create frames equal to the greatest distance
 *      between two reference frames to hold the B frames while it is parsing
 *      the input from stdin.
 *
 * RETURNS:	nothing
 *
 * SIDE EFFECTS:    frameMemory
 *
 *===========================================================================*/
void
Frame_Init()
{
    register int idx;
    int numOfFrames = 0;

    GetNumOfFrames(&numOfFrames);

    for ( idx = 0; idx < numOfFrames; idx++ ) {
	frameMemory[idx] = (MpegFrame *) malloc(sizeof(MpegFrame));
	frameMemory[idx]->inUse = FALSE;
	frameMemory[idx]->ppm_data = NULL;
	frameMemory[idx]->rgb_data = NULL;
	frameMemory[idx]->orig_y = NULL;	/* if NULL, then orig_cr, orig_cb invalid */
	frameMemory[idx]->y_blocks = NULL; /* if NULL, then cr_blocks, cb_blocks invalid */
	frameMemory[idx]->decoded_y = NULL;	/* if NULL, then blah blah */
	frameMemory[idx]->halfX = NULL;
	frameMemory[idx]->next = NULL;
    }

#ifdef BLEAH
fprintf (stderr, "%d frames allocated.\n", numOfFrames);
#endif
}


/*===========================================================================*
 *
 * Frame_Exit
 *
 *	frees the memory associated with frames
 *
 * RETURNS:	nothing
 *
 * SIDE EFFECTS:    frameMemory
 *
 *===========================================================================*/
void
Frame_Exit()
{
    register int idx;
    int numOfFrames = 0;

    GetNumOfFrames(&numOfFrames);

    for ( idx = 0; idx < numOfFrames; idx++ ) {
	FreeFrame(frameMemory[idx]);
    }
}


/*===========================================================================*
 *
 * Frame_Free
 *
 *	frees the given frame -- allows it to be re-used
 *
 * RETURNS:	nothing
 *
 * SIDE EFFECTS:    none
 *
 *===========================================================================*/
void
Frame_Free(MpegFrame *frame)
{
    frame->inUse = FALSE;
}


/*===========================================================================*
 *
 * Frame_New
 *
 *	finds a frame that isn't currently being used and resets it
 *
 * RETURNS:	the frame
 *
 * SIDE EFFECTS:    none
 *
 *===========================================================================*/
MpegFrame *
Frame_New(int id, int type)
{
    MpegFrame *frame;

    frame = GetUnusedFrame();
    ResetFrame(id, type, frame);

    return frame;
}


/*===========================================================================*
 *
 * Frame_AllocPPM
 *
 *	allocate memory for ppm data for the given frame, if required
 *
 * RETURNS:	nothing
 *
 * SIDE EFFECTS:    none
 *
 *===========================================================================*/
void
Frame_AllocPPM(MpegFrame *frame)
{
    register int y;

    if ( frame->ppm_data != NULL ) {	/* already allocated */
	return;
    }

    frame->ppm_data = (uint8 **) malloc(sizeof(uint8 *) * Fsize_y);
    ERRCHK(frame->ppm_data, "malloc");

    for ( y = 0; y < Fsize_y; y++ ) {
	frame->ppm_data[y] = (uint8 *) malloc(3*sizeof(uint8) * Fsize_x);
	ERRCHK(frame->ppm_data[y], "malloc");
    }
}


/*===========================================================================*
 *
 * Frame_AllocBlocks
 *
 *	allocate memory for blocks for the given frame, if required
 *
 * RETURNS:	nothing
 *
 * SIDE EFFECTS:    none
 *
 *===========================================================================*/
void
Frame_AllocBlocks(MpegFrame *frame)
{
    int dctx, dcty;
    int i;

    if ( frame->y_blocks != NULL ) {	    /* already allocated */
	return;
    }

    dctx = Fsize_x / DCTSIZE;
    dcty = Fsize_y / DCTSIZE;

    frame->y_blocks = (Block **) malloc(sizeof(Block *) * dcty);
    ERRCHK(frame->y_blocks, "malloc");
    for (i = 0; i < dcty; i++) {
	frame->y_blocks[i] = (Block *) malloc(sizeof(Block) * dctx);
	ERRCHK(frame->y_blocks[i], "malloc");
    }

    frame->cr_blocks = (Block **) malloc(sizeof(Block *) * (dcty >> 1));
    frame->cb_blocks = (Block **) malloc(sizeof(Block *) * (dcty >> 1));
    ERRCHK(frame->cr_blocks, "malloc");
    ERRCHK(frame->cb_blocks, "malloc");
    for (i = 0; i < (dcty >> 1); i++) {
	frame->cr_blocks[i] = (Block *) malloc(sizeof(Block) * (dctx >> 1));
	frame->cb_blocks[i] = (Block *) malloc(sizeof(Block) * (dctx >> 1));
	ERRCHK(frame->cr_blocks[i], "malloc");
	ERRCHK(frame->cb_blocks[i], "malloc");
    }
}


/*===========================================================================*
 *
 * Frame_AllocYCC
 *
 *	allocate memory for YCC info for the given frame, if required
 *
 * RETURNS:	nothing
 *
 * SIDE EFFECTS:    none
 *
 *===========================================================================*/
void
Frame_AllocYCC(MpegFrame *frame)
{
    register int y;

    if ( frame->orig_y != NULL ) {	/* already allocated */
	return /* nothing */ ;
    }

    DBG_PRINT(("ycc_calc:\n"));
    /*
     * first, allocate tons of memory
     */
    frame->orig_y = (uint8 **) malloc(sizeof(uint8 *) * Fsize_y);
    ERRCHK(frame->orig_y, "malloc");
    for (y = 0; y < Fsize_y; y++) {
	frame->orig_y[y] = (uint8 *) malloc(sizeof(uint8) * Fsize_x);
	ERRCHK(frame->orig_y[y], "malloc");
    }

    frame->orig_cr = (uint8 **) malloc(sizeof(int8 *) * (Fsize_y >> 1));
    ERRCHK(frame->orig_cr, "malloc");
    for (y = 0; y < (Fsize_y >> 1); y++) {
	frame->orig_cr[y] = (uint8 *) malloc(sizeof(int8) * (Fsize_x >> 1));
	ERRCHK(frame->orig_cr[y], "malloc");
    }

    frame->orig_cb = (uint8 **) malloc(sizeof(int8 *) * (Fsize_y >> 1));
    ERRCHK(frame->orig_cb, "malloc");
    for (y = 0; y < (Fsize_y >> 1); y++) {
	frame->orig_cb[y] = (uint8 *) malloc(sizeof(int8) * (Fsize_x >> 1));
	ERRCHK(frame->orig_cb[y], "malloc");
    }

    if ( referenceFrame == ORIGINAL_FRAME ) {
	frame->ref_y = frame->orig_y;
	frame->ref_cr = frame->orig_cr;
	frame->ref_cb = frame->orig_cb;
    }
}



/*===========================================================================*
 *
 * Frame_AllocHalf
 *
 *	allocate memory for half-pixel values for the given frame, if required
 *
 * RETURNS:	nothing
 *
 * SIDE EFFECTS:    none
 *
 *===========================================================================*/
void
Frame_AllocHalf(MpegFrame *frame)
{
    register int y;

    if ( frame->halfX != NULL ) {
        return;
    }

	frame->halfX = (uint8 **) malloc(Fsize_y*sizeof(uint8 *));
	ERRCHK(frame->halfX, "malloc");
	frame->halfY = (uint8 **) malloc((Fsize_y-1)*sizeof(uint8 *));
	ERRCHK(frame->halfY, "malloc");
	frame->halfBoth = (uint8 **) malloc((Fsize_y-1)*sizeof(uint8 *));
	ERRCHK(frame->halfBoth, "malloc");
	for ( y = 0; y < Fsize_y; y++ ) {
	    frame->halfX[y] = (uint8 *) malloc((Fsize_x-1)*sizeof(uint8));
	    ERRCHK(frame->halfX[y], "malloc");
	}
	for ( y = 0; y < Fsize_y-1; y++ ) {
	    frame->halfY[y] = (uint8 *) malloc(Fsize_x*sizeof(uint8));
	    ERRCHK(frame->halfY[y], "malloc");
	}
	for ( y = 0; y < Fsize_y-1; y++ ) {
	    frame->halfBoth[y] = (uint8 *) malloc((Fsize_x-1)*sizeof(uint8));
	    ERRCHK(frame->halfBoth[y], "malloc");
	}
}


/*===========================================================================*
 *
 * Frame_AllocDecoded
 *
 *	allocate memory for decoded frame for the given frame, if required
 *	if makeReference == TRUE, then makes it reference frame
 * 
 * RETURNS:	nothing
 *
 * SIDE EFFECTS:    none
 *
 *===========================================================================*/
void
Frame_AllocDecoded(MpegFrame *frame, boolean makeReference)
{
    register int y;

    if ( frame->decoded_y != NULL) {	/* already allocated */
	return;
    }

    /* allocate memory for decoded image */
    /* can probably reuse original image memory, but may decide to use
       it for some reason, so do it this way at least for now -- more
       flexible
     */
    frame->decoded_y = (uint8 **) malloc(sizeof(uint8 *) * Fsize_y);
    ERRCHK(frame->decoded_y, "malloc");
    for (y = 0; y < Fsize_y; y++) {
	frame->decoded_y[y] = (uint8 *) malloc(sizeof(uint8) * Fsize_x);
	ERRCHK(frame->decoded_y[y], "malloc");
    }

    frame->decoded_cr = (uint8 **) malloc(sizeof(int8 *) * (Fsize_y >> 1));
    ERRCHK(frame->decoded_cr, "malloc");
    for (y = 0; y < (Fsize_y >> 1); y++) {
	frame->decoded_cr[y] = (uint8 *) malloc(sizeof(uint8) * (Fsize_x >> 1));
	ERRCHK(frame->decoded_cr[y], "malloc");
    }

    frame->decoded_cb = (uint8 **) malloc(sizeof(int8 *) * (Fsize_y >> 1));
    ERRCHK(frame->decoded_cb, "malloc");
    for (y = 0; y < (Fsize_y >> 1); y++) {
	frame->decoded_cb[y] = (uint8 *) malloc(sizeof(uint8) * (Fsize_x >> 1));
	ERRCHK(frame->decoded_cb[y], "malloc");
    }

    if ( makeReference ) {
	frame->ref_y = frame->decoded_y;
	frame->ref_cr = frame->decoded_cr;
	frame->ref_cb = frame->decoded_cb;
    }
}


/*=====================*
 * INTERNAL PROCEDURES *
 *=====================*/


/*===========================================================================*
 *
 * GetUnusedFrame
 *
 *	return an unused frame
 *
 * RETURNS:	the frame
 *
 * SIDE EFFECTS:    none
 *
 *===========================================================================*/
static MpegFrame *
GetUnusedFrame()
{
    register int idx;
    int numOfFrames;

    GetNumOfFrames(&numOfFrames);

    for ( idx = 0; idx < numOfFrames; idx++ ) {
	if ( ! frameMemory[idx]->inUse ) {
	    frameMemory[idx]->inUse = TRUE;
	    return frameMemory[idx];
	}
    }

    throw "No unused frames: "
      "If you are using stdin for input, it is likely that you have too many "
      "B-frames between two reference frames.  See the man page for help.";
}


/*===========================================================================*
 *
 * GetNumOfFrames
 *
 *	return the number of frames to allocate
 *
 * RETURNS:	nothing
 *
 * SIDE EFFECTS:    numOfFrames contains the number to allocate
 *
 *===========================================================================*/
static void
GetNumOfFrames(int *numOfFrames)
{
    int idx, bcount;

    if (stdinUsed) {
      for ( idx = 0, bcount = 0; idx < (int)strlen(framePattern); idx++) {

	/* counts the maximum number of B frames between two reference
	 * frames. 
	 */

	switch( framePattern[idx] ) {
	  case 'b': 
	    bcount++;
	    break;
	  case 'i':
	  case 'p':
            if (bcount > *numOfFrames) {
              *numOfFrames = bcount;
            }
	    bcount = 0;
	    break;
        }

	/* add 2 to hold the forward and past reference frames in addition
	 * to the maximum number of B's 
	 */
      }

      *numOfFrames += 2;

    } else {
      /* non-interactive, only 3 frames needed */
      *numOfFrames = 3;
    }
}

/*===========================================================================*
 *
 * ResetFrame
 *
 *	reset a frame to the given id and type
 *
 * RETURNS:	nothing
 *
 * SIDE EFFECTS:    none
 *
 *===========================================================================*/
static void
ResetFrame(int id, int type, MpegFrame *frame)
{
    switch (type) {
    case 'i':
	frame->type = TYPE_IFRAME;
	break;
    case 'p':
	frame->type = TYPE_PFRAME;
	break;
    case 'b':
	frame->type = TYPE_BFRAME;
	break;
    default:
        throw "frame type: not supported";
    }

    frame->id = id;
    frame->halfComputed = FALSE;
    frame->next = NULL;
}


/*===========================================================================*
 *
 * FreeFrame
 *
 *	frees the memory associated with the given frame
 *
 * RETURNS:	nothing
 *
 * SIDE EFFECTS:    none
 *
 *===========================================================================*/
static void
FreeFrame(MpegFrame *frame)
{
    int i;

    if (!frame) {
	return;
    }

    if ( frame->ppm_data ) {
	/* it may be a little bigger than Fsize_y, but that's fine for
	   our purposes, since we aren't going to free until we exit anyway,
	   so by the time we call this we won't care
	 */
	pnm_freearray(frame->ppm_data, Fsize_y);
	frame->ppm_data = NULL;
    }

    if (frame->rgb_data) {
	pnm_freearray(frame->rgb_data, Fsize_y);
    }
    if (frame->orig_y) {
	for (i = 0; i < Fsize_y; i++) {
	    free(frame->orig_y[i]);
	}
	free(frame->orig_y);

	for (i = 0; i < (Fsize_y >> 1); i++) {
	    free(frame->orig_cr[i]);
	}
	free(frame->orig_cr);

	for (i = 0; i < (Fsize_y >> 1); i++) {
	    free(frame->orig_cb[i]);
	}
	free(frame->orig_cb);
    }
    if ( frame->decoded_y ) {
	for (i = 0; i < Fsize_y; i++) {
	    free(frame->decoded_y[i]);
	}
	free(frame->decoded_y);

	for (i = 0; i < (Fsize_y >> 1); i++) {
	    free(frame->decoded_cr[i]);
	}
	free(frame->decoded_cr);

	for (i = 0; i < (Fsize_y >> 1); i++) {
	    free(frame->decoded_cb[i]);
	}
	free(frame->decoded_cb);
    }

    if (frame->y_blocks) {
	for (i = 0; i < Fsize_y / DCTSIZE; i++) {
	    free(frame->y_blocks[i]);
	}
	free(frame->y_blocks);

	for (i = 0; i < Fsize_y / (2 * DCTSIZE); i++) {
	    free(frame->cr_blocks[i]);
	}
	free(frame->cr_blocks);

	for (i = 0; i < Fsize_y / (2 * DCTSIZE); i++) {
	    free(frame->cb_blocks[i]);
	}
	free(frame->cb_blocks);
    }
    if ( frame->halfX ) {
	for ( i = 0; i < Fsize_y; i++ ) {
	    free(frame->halfX[i]);
	}
	free(frame->halfX);

	for ( i = 0; i < Fsize_y-1; i++ ) {
	    free(frame->halfY[i]);
	}
	free(frame->halfY);

	for ( i = 0; i < Fsize_y-1; i++ ) {
	    free(frame->halfBoth[i]);
	}
	free(frame->halfBoth);
    }

        
    free(frame);
}


