/* putmpg.c, block and motion vector encoding routines                      */

/* Copyright (C) 1996, MPEG Software Simulation Group. All Rights Reserved. */

/*
 * Disclaimer of Warranty
 *
 * These software programs are available to the user without any license fee or
 * royalty on an "as is" basis.  The MPEG Software Simulation Group disclaims
 * any and all warranties, whether express, implied, or statuary, including any
 * implied warranties or merchantability or of fitness for a particular
 * purpose.  In no event shall the copyright-holder be liable for any
 * incidental, punitive, or consequential damages of any kind whatsoever
 * arising from the use of these programs.
 *
 * This disclaimer of warranty extends to the user of these programs and user's
 * customers, employees, agents, transferees, successors, and assigns.
 *
 * The MPEG Software Simulation Group does not represent or warrant that the
 * programs furnished hereunder are free of infringement of any third-party
 * patents.
 *
 * Commercial implementations of MPEG-1 and MPEG-2 video, including shareware,
 * are subject to royalty fees to patent holders.  Many of these patents are
 * general enough such that they are unavoidable regardless of implementation
 * design.
 *
 */

#include <stdio.h>
#include "config.h"
#include "global.h"

/* generate variable length codes for an intra-coded block (6.2.6, 6.3.17) */
void putintrablk(slice_engine_t *engine, 
	pict_data_s *picture, 
	short *blk, 
	int cc)
{
	int n, dct_diff, run, signed_level;

/* DC coefficient (7.2.1) */
	dct_diff = blk[0] - engine->dc_dct_pred[cc]; /* difference to previous block */
	engine->dc_dct_pred[cc] = blk[0];

	if(cc == 0)
    	putDClum(engine, dct_diff);
	else
    	putDCchrom(engine, dct_diff);

/* AC coefficients (7.2.2) */
	run = 0;
	for(n = 1; n < 64; n++)
	{
/* use appropriate entropy scanning pattern */
    	signed_level = blk[(picture->altscan ? alternate_scan_hv : mpeg2_zig_zag_scan)[n]];
    	if (signed_level!=0)
    	{
//printf("putintrablk 1 %d\n", picture->altscan);slice_testbits(engine);
    		putAC(engine, run, signed_level, picture->intravlc);
//printf("putintrablk 2\n");slice_testbits(engine);
    		run = 0;
    	}
    	else
    		run++; /* count zero coefficients */
	}

/* End of Block -- normative block punctuation */
	if (picture->intravlc)
    	slice_putbits(engine, 6, 4); /* 0110 (Table B-15) */
	else
    	slice_putbits(engine, 2, 2); /* 10 (Table B-14) */
}

/* generate variable length codes for a non-intra-coded block (6.2.6, 6.3.17) */
void putnonintrablk(slice_engine_t *engine, 
	pict_data_s *picture, 
	short *blk)
{
	int n, run, signed_level, first;

	run = 0;
	first = 1;

	for(n = 0; n < 64; n++)
	{
/* use appropriate entropy scanning pattern */
      signed_level = blk[(picture->altscan ? alternate_scan_hv : mpeg2_zig_zag_scan)[n]];

      if (signed_level!=0)
      {
    	  if (first)
    	  {
/* first coefficient in non-intra block */
        	  putACfirst(engine, run, signed_level);
        	  first = 0;
    	  }
    	  else
        	putAC(engine, run, signed_level, 0);

    	  run = 0;
      }
      else
          run++; /* count zero coefficients */
	}

/* End of Block -- normative block punctuation  */
	slice_putbits(engine, 2, 2);
}

/* generate variable length code for a motion vector component (7.6.3.1) */
void putmv(slice_engine_t *engine, int dmv, int f_code)
{
	int r_size, f, vmin, vmax, dv, temp, motion_code, motion_residual;

	r_size = f_code - 1; /* number of fixed length code ('residual') bits */
	f = 1 << r_size;
	vmin = -16 * f; /* lower range limit */
	vmax = 16 * f - 1; /* upper range limit */
	dv = 32 * f;

/* fold vector difference into [vmin...vmax] */
	if(dmv > vmax)
    	dmv -= dv;
	else 
	if(dmv < vmin)
    	dmv += dv;

/* check value */
	if(dmv < vmin || dmv > vmax)
    	if(!quiet)
    		fprintf(stderr,"invalid motion vector\n");

/* split dmv into motion_code and motion_residual */
	temp = ((dmv < 0) ? -dmv : dmv) + f - 1;
	motion_code = temp >> r_size;
	if(dmv < 0)
        motion_code = -motion_code;
	motion_residual = temp & (f - 1);

	putmotioncode(engine, motion_code); /* variable length code */

	if (r_size != 0 && motion_code != 0)
    	slice_putbits(engine, motion_residual, r_size); /* fixed length code */
}
