/* display.c, X11 interface                                                 */



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

 *

 */



#ifdef DISPLAY



 /* the Xlib interface is closely modeled after

  * mpeg_play 2.0 by the Berkeley Plateau Research Group

  */



#include <stdio.h>

#include <stdlib.h>



#include <X11/Xlib.h>

#include <X11/Xutil.h>



#include "config.h"

#include "global.h"



/* private prototypes */

static void Display_Image _ANSI_ARGS_((XImage *Ximage_Ptr, unsigned char *Dithered_Image));

static void Dither_Frame _ANSI_ARGS_((unsigned char *src[]));

static void Dither_Top_Field _ANSI_ARGS_((unsigned char *src[], unsigned char *dst));

static void Dither_Bottom_Field _ANSI_ARGS_((unsigned char *src[], unsigned char *dst));

static void Dither_Top_Field420 _ANSI_ARGS_((unsigned char *src[],

                                      unsigned char *dst));

static void Dither_Bottom_Field420 _ANSI_ARGS_((unsigned char *src[],

                                      unsigned char *dst));



/* local data */

static unsigned char *Dithered_Image, *Dithered_Image2;



static unsigned char Y_Table[256+16];

static unsigned char Cb_Table[128+16];

static unsigned char Cr_Table[128+16];



/* X11 related variables */

static Display *Display_Ptr;

static Window Window_Instance;

static GC GC_Instance;

static XImage *Ximage_Ptr, *Ximage_Ptr2;

static unsigned char Pixel[256];



#ifdef SH_MEM



#include <sys/ipc.h>

#include <sys/shm.h>

#include <X11/extensions/XShm.h>



static int HandleXError _ANSI_ARGS_((Display *dpy, XErrorEvent *event));

static void InstallXErrorHandler _ANSI_ARGS_((void));

static void DeInstallXErrorHandler _ANSI_ARGS_((void));



static int Shmem_Flag;

static XShmSegmentInfo Shminfo1, Shminfo2;

static int gXErrorFlag;

static int CompletionType = -1;



static int HandleXError(Dpy, Event)

Display *Dpy;

XErrorEvent *Event;

{

  gXErrorFlag = 1;



  return 0;

}



static void InstallXErrorHandler()

{

  XSetErrorHandler(HandleXError);

  XFlush(Display_Ptr);

}



static void DeInstallXErrorHandler()

{

  XSetErrorHandler(NULL);

  XFlush(Display_Ptr);

}



#endif



/* connect to server, create and map window,

 * allocate colors and (shared) memory

 */

void Initialize_Display_Process(name)

char *name;

{

  int crv, cbu, cgu, cgv;

  int Y, Cb, Cr, R, G, B;

  int i;

  char dummy;

  int screen;

  Colormap cmap;

  int private;

  XColor xcolor;

  unsigned int fg, bg;

  char *hello = "MPEG-2 Display";

  XSizeHints hint;

  XVisualInfo vinfo;

  XEvent xev;

  unsigned long tmp_pixel;

  XWindowAttributes xwa;



  Display_Ptr = XOpenDisplay(name);



  if (Display_Ptr == NULL)

    Error("Can not open display\n");



  screen = DefaultScreen(Display_Ptr);



  hint.x = 200;

  hint.y = 200;

  hint.width = horizontal_size;

  hint.height = vertical_size;

  hint.flags = PPosition | PSize;



  /* Get some colors */



  bg = WhitePixel (Display_Ptr, screen);

  fg = BlackPixel (Display_Ptr, screen);



  /* Make the window */



  if (!XMatchVisualInfo(Display_Ptr, screen, 8, PseudoColor, &vinfo))

  {

    if (!XMatchVisualInfo(Display_Ptr, screen, 8, GrayScale, &vinfo))

      Error("requires 8 bit display\n");

  }



  Window_Instance = XCreateSimpleWindow (Display_Ptr, DefaultRootWindow (Display_Ptr),

             hint.x, hint.y, hint.width, hint.height, 4, fg, bg);



  XSelectInput(Display_Ptr, Window_Instance, StructureNotifyMask);



  /* Tell other applications about this window */



  XSetStandardProperties (Display_Ptr, Window_Instance, hello, hello, None, NULL, 0, &hint);



  /* Map window. */



  XMapWindow(Display_Ptr, Window_Instance);



  /* Wait for map. */

  do

  {

    XNextEvent(Display_Ptr, &xev);

  }

  while (xev.type != MapNotify || xev.xmap.event != Window_Instance);



  XSelectInput(Display_Ptr, Window_Instance, NoEventMask);



  /* matrix coefficients */

  crv = Inverse_Table_6_9[matrix_coefficients][0];

  cbu = Inverse_Table_6_9[matrix_coefficients][1];

  cgu = Inverse_Table_6_9[matrix_coefficients][2];

  cgv = Inverse_Table_6_9[matrix_coefficients][3];



  /* allocate colors */



  GC_Instance = DefaultGC(Display_Ptr, screen);

  cmap = DefaultColormap(Display_Ptr, screen);

  private = 0;



  /* color allocation:

   * i is the (internal) 8 bit color number, it consists of separate

   * bit fields for Y, U and V: i = (yyyyuuvv), we don't use yyyy=0000

   * and yyyy=1111, this leaves 32 colors for other applications

   *

   * the allocated colors correspond to the following Y, U and V values:

   * Y:   24, 40, 56, 72, 88, 104, 120, 136, 152, 168, 184, 200, 216, 232

   * U,V: -48, -16, 16, 48

   *

   * U and V values span only about half the color space; this gives

   * usually much better quality, although highly saturated colors can

   * not be displayed properly

   *

   * translation to R,G,B is implicitly done by the color look-up table

   */

  for (i=16; i<240; i++)

  {

    /* color space conversion */

    Y  = 16*((i>>4)&15) + 8;

    Cb = 32*((i>>2)&3)  - 48;

    Cr = 32*(i&3)       - 48;



    Y = 76309 * (Y - 16); /* (255/219)*65536 */



    R = Clip[(Y + crv*Cr + 32768)>>16];

    G = Clip[(Y - cgu*Cb - cgv*Cr + 32768)>>16];

    B = Clip[(Y + cbu*Cb + 32786)>>16];



    /* X11 colors are 16 bit */

    xcolor.red   = R << 8;

    xcolor.green = G << 8;

    xcolor.blue  = B << 8;



    if (XAllocColor(Display_Ptr, cmap, &xcolor) != 0)

      Pixel[i] = xcolor.pixel;

    else

    {

      /* allocation failed, have to use a private colormap */



      if (private)

        Error("Couldn't allocate private colormap");



      private = 1;



      if (!Quiet_Flag)

        fprintf(stderr, "Using private colormap (%d colors were available).\n",

          i-16);



      /* Free colors. */

      while (--i >= 16)

      {

        tmp_pixel = Pixel[i]; /* because XFreeColors expects unsigned long */

        XFreeColors(Display_Ptr, cmap, &tmp_pixel, 1, 0);

      }



      /* i is now 15, this restarts the outer loop */



      /* create private colormap */



      XGetWindowAttributes(Display_Ptr, Window_Instance, &xwa);

      cmap = XCreateColormap(Display_Ptr, Window_Instance, xwa.visual, AllocNone);

      XSetWindowColormap(Display_Ptr, Window_Instance, cmap);

    }

  }



#ifdef SH_MEM

  if (XShmQueryExtension(Display_Ptr))

    Shmem_Flag = 1;

  else

  {

    Shmem_Flag = 0;

    if (!Quiet_Flag)

      fprintf(stderr, "Shared memory not supported\nReverting to normal Xlib\n");

  }



  if (Shmem_Flag)

    CompletionType = XShmGetEventBase(Display_Ptr) + ShmCompletion;



  InstallXErrorHandler();



  if (Shmem_Flag)

  {



    Ximage_Ptr = XShmCreateImage(Display_Ptr, None, 8, ZPixmap, NULL,

                             &Shminfo1,

                             Coded_Picture_Width, Coded_Picture_Height);



    if (!progressive_sequence)

      Ximage_Ptr2 = XShmCreateImage(Display_Ptr, None, 8, ZPixmap, NULL,

                                &Shminfo2,

                                Coded_Picture_Width, Coded_Picture_Height);



    /* If no go, then revert to normal Xlib calls. */



    if (Ximage_Ptr==NULL || (!progressive_sequence && Ximage_Ptr2==NULL))

    {

      if (Ximage_Ptr!=NULL)

        XDestroyImage(Ximage_Ptr);

      if (!progressive_sequence && Ximage_Ptr2!=NULL)

        XDestroyImage(Ximage_Ptr2);

      if (!Quiet_Flag)

        fprintf(stderr, "Shared memory error, disabling (Ximage error)\n");

      goto shmemerror;

    }



    /* Success here, continue. */



    Shminfo1.shmid = shmget(IPC_PRIVATE, 

                            Ximage_Ptr->bytes_per_line * Ximage_Ptr->height,

                            IPC_CREAT | 0777);

    if (!progressive_sequence)

      Shminfo2.shmid = shmget(IPC_PRIVATE, 

                              Ximage_Ptr2->bytes_per_line * Ximage_Ptr2->height,

                              IPC_CREAT | 0777);



    if (Shminfo1.shmid<0 || (!progressive_sequence && Shminfo2.shmid<0))

    {

      XDestroyImage(Ximage_Ptr);

      if (!progressive_sequence)

        XDestroyImage(Ximage_Ptr2);

      if (!Quiet_Flag)

        fprintf(stderr, "Shared memory error, disabling (seg id error)\n");

      goto shmemerror;

    }



    Shminfo1.shmaddr = (char *) shmat(Shminfo1.shmid, 0, 0);

    Shminfo2.shmaddr = (char *) shmat(Shminfo2.shmid, 0, 0);



    if (Shminfo1.shmaddr==((char *) -1) ||

        (!progressive_sequence && Shminfo2.shmaddr==((char *) -1)))

    {

      XDestroyImage(Ximage_Ptr);

      if (Shminfo1.shmaddr!=((char *) -1))

        shmdt(Shminfo1.shmaddr);

      if (!progressive_sequence)

      {

        XDestroyImage(Ximage_Ptr2);

        if (Shminfo2.shmaddr!=((char *) -1))

          shmdt(Shminfo2.shmaddr);

      }

      if (!Quiet_Flag)

      {

        fprintf(stderr, "Shared memory error, disabling (address error)\n");

      }

      goto shmemerror;

    }



    Ximage_Ptr->data = Shminfo1.shmaddr;

    Dithered_Image = (unsigned char *)Ximage_Ptr->data;

    Shminfo1.readOnly = False;

    XShmAttach(Display_Ptr, &Shminfo1);

    if (!progressive_sequence)

    {

      Ximage_Ptr2->data = Shminfo2.shmaddr;

      Dithered_Image2 = (unsigned char *)Ximage_Ptr2->data;

      Shminfo2.readOnly = False;

      XShmAttach(Display_Ptr, &Shminfo2);

    }



    XSync(Display_Ptr, False);



    if (gXErrorFlag)

    {

      /* Ultimate failure here. */

      XDestroyImage(Ximage_Ptr);

      shmdt(Shminfo1.shmaddr);

      if (!progressive_sequence)

      {

        XDestroyImage(Ximage_Ptr2);

        shmdt(Shminfo2.shmaddr);

      }

      if (!Quiet_Flag)

        fprintf(stderr, "Shared memory error, disabling.\n");

      gXErrorFlag = 0;

      goto shmemerror;

    }

    else

    {

      shmctl(Shminfo1.shmid, IPC_RMID, 0);

      if (!progressive_sequence)

        shmctl(Shminfo2.shmid, IPC_RMID, 0);

    }



    if (!Quiet_Flag)

    {

      fprintf(stderr, "Sharing memory.\n");

    }

  }

  else

  {

shmemerror:

    Shmem_Flag = 0;

#endif



    Ximage_Ptr = XCreateImage(Display_Ptr,None,8,ZPixmap,0,&dummy,

                          Coded_Picture_Width,Coded_Picture_Height,8,0);



    if (!(Dithered_Image = (unsigned char *)malloc(Coded_Picture_Width*

                                                   Coded_Picture_Height)))

      Error("malloc failed");



    if (!progressive_sequence)

    {

      Ximage_Ptr2 = XCreateImage(Display_Ptr,None,8,ZPixmap,0,&dummy,

                             Coded_Picture_Width,Coded_Picture_Height,8,0);



      if (!(Dithered_Image2 = (unsigned char *)malloc(Coded_Picture_Width*

                                                      Coded_Picture_Height)))

        Error("malloc failed");

    }



#ifdef SH_MEM

  }



  DeInstallXErrorHandler();

#endif

}



void Terminate_Display_Process()

{

#ifdef SH_MEM

  if (Shmem_Flag)

  {

    XShmDetach(Display_Ptr, &Shminfo1);

    XDestroyImage(Ximage_Ptr);

    shmdt(Shminfo1.shmaddr);

    if (!progressive_sequence)

    {

      XShmDetach(Display_Ptr, &Shminfo2);

      XDestroyImage(Ximage_Ptr2);

      shmdt(Shminfo2.shmaddr);

    }

  }

#endif

}



static void Display_Image(Ximage_Ptr,Dithered_Image)

XImage *Ximage_Ptr;

unsigned char *Dithered_Image;

{

  /* display dithered image */

#ifdef SH_MEM

  if (Shmem_Flag)

  {

    XShmPutImage(Display_Ptr, Window_Instance, GC_Instance, Ximage_Ptr, 

       	         0, 0, 0, 0, Ximage_Ptr->width, Ximage_Ptr->height, True);

    XFlush(Display_Ptr);

      

    while (1)

    {

      XEvent xev;

	

      XNextEvent(Display_Ptr, &xev);

      if (xev.type == CompletionType)

        break;

    }

  }

  else 

#endif

  {

    Ximage_Ptr->data = (char *) Dithered_Image; 

    XPutImage(Display_Ptr, Window_Instance, GC_Instance, Ximage_Ptr, 0, 0, 0, 0, Ximage_Ptr->width, Ximage_Ptr->height);

  }

}



void Display_Second_Field()

{

  Display_Image(Ximage_Ptr2,Dithered_Image2);

}



/* 4x4 ordered dither

 *

 * threshold pattern:

 *   0  8  2 10

 *  12  4 14  6

 *   3 11  1  9

 *  15  7 13  5

 */



void Initialize_Dither_Matrix()

{

  int i, v;



  for (i=-8; i<256+8; i++)

  {

    v = i>>4;

    if (v<1)

      v = 1;

    else if (v>14)

      v = 14;

    Y_Table[i+8] = v<<4;

  }



  for (i=0; i<128+16; i++)

  {

    v = (i-40)>>4;

    if (v<0)

      v = 0;

    else if (v>3)

      v = 3;

    Cb_Table[i] = v<<2;

    Cr_Table[i] = v;

  }

}



void dither(src)

unsigned char *src[];

{

  /* should this test only the display flag, not progressive_sequence ? --CF */

  /* CHANGE 95/05/13: progressive_sequence -> progressive_frame */



  if( progressive_frame || Display_Progressive_Flag)

    Dither_Frame(src);

  else

  {

    if ((picture_structure==FRAME_PICTURE && top_field_first) || picture_structure==BOTTOM_FIELD)

    {

      /* top field first */

      if (chroma_format==CHROMA420 && hiQdither)

      {

        Dither_Top_Field420(src,Dithered_Image);

        Dither_Bottom_Field420(src,Dithered_Image2);

      }

      else

      {

        Dither_Top_Field(src,Dithered_Image);

        Dither_Bottom_Field(src,Dithered_Image2);

      }

    }

    else

    {

      /* bottom field first */

      if (chroma_format==CHROMA420 && hiQdither)

      {

        Dither_Bottom_Field420(src,Dithered_Image);

        Dither_Top_Field420(src,Dithered_Image2);

      }

      else

      {

        Dither_Bottom_Field(src,Dithered_Image);

        Dither_Top_Field(src,Dithered_Image2);

      }

    }

  }



  Display_Image(Ximage_Ptr,Dithered_Image);

}



static void Dither_Frame(src)

unsigned char *src[];

{

  int i,j;

  int y,u,v;

  unsigned char *py,*pu,*pv,*dst;



  py = src[0];

  pu = src[1];

  pv = src[2];

  dst = Dithered_Image;



  for (j=0; j<Coded_Picture_Height; j+=4)

  {

    /* line j + 0 */

    for (i=0; i<Coded_Picture_Width; i+=4)

    {

      y = *py++;

      u = *pu++ >> 1;

      v = *pv++ >> 1;

      *dst++ = Pixel[Y_Table[y]|Cb_Table[u]|Cr_Table[v]];

      y = *py++;

      if (chroma_format==CHROMA444)

      {

        u = *pu++ >> 1;

        v = *pv++ >> 1;

      }

      *dst++ = Pixel[Y_Table[y+8]|Cb_Table[u+8]|Cr_Table[v+8]];

      y = *py++;

      u = *pu++ >> 1;

      v = *pv++ >> 1;

      *dst++ = Pixel[Y_Table[y+2]|Cb_Table[u+2]|Cr_Table[v+2]];

      y = *py++;

      if (chroma_format==CHROMA444)

      {

        u = *pu++ >> 1;

        v = *pv++ >> 1;

      }

      *dst++ = Pixel[Y_Table[y+10]|Cb_Table[u+10]|Cr_Table[v+10]];

    }



    if (chroma_format==CHROMA420)

    {

      pu -= Chroma_Width;

      pv -= Chroma_Width;

    }



    /* line j + 1 */

    for (i=0; i<Coded_Picture_Width; i+=4)

    {

      y = *py++;

      u = *pu++ >> 1;

      v = *pv++ >> 1;

      *dst++ = Pixel[Y_Table[y+12]|Cb_Table[u+12]|Cr_Table[v+12]];

      y = *py++;

      if (chroma_format==CHROMA444)

      {

        u = *pu++ >> 1;

        v = *pv++ >> 1;

      }

      *dst++ = Pixel[Y_Table[y+4]|Cb_Table[u+4]|Cr_Table[v+4]];

      y = *py++;

      u = *pu++ >> 1;

      v = *pv++ >> 1;

      *dst++ = Pixel[Y_Table[y+14]|Cb_Table[u+14]|Cr_Table[v+14]];

      y = *py++;

      if (chroma_format==CHROMA444)

      {

        u = *pu++ >> 1;

        v = *pv++ >> 1;

      }

      *dst++ = Pixel[Y_Table[y+6]|Cb_Table[u+6]|Cr_Table[v+6]];

    }



    /* line j + 2 */

    for (i=0; i<Coded_Picture_Width; i+=4)

    {

      y = *py++;

      u = *pu++ >> 1;

      v = *pv++ >> 1;

      *dst++ = Pixel[Y_Table[y+3]|Cb_Table[u+3]|Cr_Table[v+3]];

      y = *py++;

      if (chroma_format==CHROMA444)

      {

        u = *pu++ >> 1;

        v = *pv++ >> 1;

      }

      *dst++ = Pixel[Y_Table[y+11]|Cb_Table[u+11]|Cr_Table[v+11]];

      y = *py++;

      u = *pu++ >> 1;

      v = *pv++ >> 1;

      *dst++ = Pixel[Y_Table[y+1]|Cb_Table[u+1]|Cr_Table[v+1]];

      y = *py++;

      if (chroma_format==CHROMA444)

      {

        u = *pu++ >> 1;

        v = *pv++ >> 1;

      }

      *dst++ = Pixel[Y_Table[y+9]|Cb_Table[u+9]|Cr_Table[v+9]];

    }



    if (chroma_format==CHROMA420)

    {

      pu -= Chroma_Width;

      pv -= Chroma_Width;

    }



    /* line j + 3 */

    for (i=0; i<Coded_Picture_Width; i+=4)

    {

      y = *py++;

      u = *pu++ >> 1;

      v = *pv++ >> 1;

      *dst++ = Pixel[Y_Table[y+15]|Cb_Table[u+15]|Cr_Table[v+15]];

      y = *py++;

      if (chroma_format==CHROMA444)

      {

        u = *pu++ >> 1;

        v = *pv++ >> 1;

      }

      *dst++ = Pixel[Y_Table[y+7]|Cb_Table[u+7]|Cr_Table[v+7]];

      y = *py++;

      u = *pu++ >> 1;

      v = *pv++ >> 1;

      *dst++ = Pixel[Y_Table[y+13]|Cb_Table[u+13]|Cr_Table[v+13]];

      y = *py++;

      if (chroma_format==CHROMA444)

      {

        u = *pu++ >> 1;

        v = *pv++ >> 1;

      }

      *dst++ = Pixel[Y_Table[y+5]|Cb_Table[u+5]|Cr_Table[v+5]];

    }

  }



}



static void Dither_Top_Field(src,dst)

unsigned char *src[];

unsigned char *dst;

{

  int i,j;

  int y,Y2,u,v;

  unsigned char *py,*Y2_ptr,*pu,*pv,*dst2;



  py = src[0];

  Y2_ptr = src[0] + (Coded_Picture_Width<<1);

  pu = src[1];

  pv = src[2];

  dst2 = dst + Coded_Picture_Width;



  for (j=0; j<Coded_Picture_Height; j+=4)

  {

    /* line j + 0, j + 1 */

    for (i=0; i<Coded_Picture_Width; i+=4)

    {

      y = *py++;

      Y2 = *Y2_ptr++;

      u = *pu++ >> 1;

      v = *pv++ >> 1;

      *dst++  = Pixel[Y_Table[y]|Cb_Table[u]|Cr_Table[v]];

      *dst2++ = Pixel[Y_Table[((y+Y2)>>1)+12]|Cb_Table[u+12]|Cr_Table[v+12]];



      y = *py++;

      Y2 = *Y2_ptr++;

      if (chroma_format==CHROMA444)

      {

        u = *pu++ >> 1;

        v = *pv++ >> 1;

      }

      *dst++  = Pixel[Y_Table[y+8]|Cb_Table[u+8]|Cr_Table[v+8]];

      *dst2++ = Pixel[Y_Table[((y+Y2)>>1)+4]|Cb_Table[u+4]|Cr_Table[v+4]];



      y = *py++;

      Y2 = *Y2_ptr++;

      u = *pu++ >> 1;

      v = *pv++ >> 1;

      *dst++  = Pixel[Y_Table[y+2]|Cb_Table[u+2]|Cr_Table[v+2]];

      *dst2++ = Pixel[Y_Table[((y+Y2)>>1)+14]|Cb_Table[u+14]|Cr_Table[v+14]];



      y = *py++;

      Y2 = *Y2_ptr++;

      if (chroma_format==CHROMA444)

      {

        u = *pu++ >> 1;

        v = *pv++ >> 1;

      }

      *dst++  = Pixel[Y_Table[y+10]|Cb_Table[u+10]|Cr_Table[v+10]];

      *dst2++ = Pixel[Y_Table[((y+Y2)>>1)+6]|Cb_Table[u+6]|Cr_Table[v+6]];

    }



    py += Coded_Picture_Width;



    if (j!=(Coded_Picture_Height-4))

      Y2_ptr += Coded_Picture_Width;

    else

      Y2_ptr -= Coded_Picture_Width;



    dst += Coded_Picture_Width;

    dst2 += Coded_Picture_Width;



    if (chroma_format==CHROMA420)

    {

      pu -= Chroma_Width;

      pv -= Chroma_Width;

    }

    else

    {

      pu += Chroma_Width;

      pv += Chroma_Width;

    }



    /* line j + 2, j + 3 */

    for (i=0; i<Coded_Picture_Width; i+=4)

    {

      y = *py++;

      Y2 = *Y2_ptr++;

      u = *pu++ >> 1;

      v = *pv++ >> 1;

      *dst++  = Pixel[Y_Table[y+3]|Cb_Table[u+3]|Cr_Table[v+3]];

      *dst2++ = Pixel[Y_Table[((y+Y2)>>1)+15]|Cb_Table[u+15]|Cr_Table[v+15]];



      y = *py++;

      Y2 = *Y2_ptr++;

      if (chroma_format==CHROMA444)

      {

        u = *pu++ >> 1;

        v = *pv++ >> 1;

      }

      *dst++  = Pixel[Y_Table[y+11]|Cb_Table[u+11]|Cr_Table[v+11]];

      *dst2++ = Pixel[Y_Table[((y+Y2)>>1)+7]|Cb_Table[u+7]|Cr_Table[v+7]];



      y = *py++;

      Y2 = *Y2_ptr++;

      u = *pu++ >> 1;

      v = *pv++ >> 1;

      *dst++  = Pixel[Y_Table[y+1]|Cb_Table[u+1]|Cr_Table[v+1]];

      *dst2++ = Pixel[Y_Table[((y+Y2)>>1)+13]|Cb_Table[u+13]|Cr_Table[v+13]];



      y = *py++;

      Y2 = *Y2_ptr++;

      if (chroma_format==CHROMA444)

      {

        u = *pu++ >> 1;

        v = *pv++ >> 1;

      }

      *dst++  = Pixel[Y_Table[y+9]|Cb_Table[u+9]|Cr_Table[v+9]];

      *dst2++ = Pixel[Y_Table[((y+Y2)>>1)+5]|Cb_Table[u+5]|Cr_Table[v+5]];

    }



    py += Coded_Picture_Width;

    Y2_ptr += Coded_Picture_Width;

    dst += Coded_Picture_Width;

    dst2 += Coded_Picture_Width;

    pu += Chroma_Width;

    pv += Chroma_Width;

  }

}



static void Dither_Bottom_Field(src,dst)

unsigned char *src[];

unsigned char *dst;

{

  int i,j;

  int y,Y2,u,v;

  unsigned char *py,*Y2_ptr,*pu,*pv,*dst2;



  py = src[0] + Coded_Picture_Width;

  Y2_ptr = py;

  pu = src[1] + Chroma_Width;

  pv = src[2] + Chroma_Width;

  dst2 = dst + Coded_Picture_Width;



  for (j=0; j<Coded_Picture_Height; j+=4)

  {

    /* line j + 0, j + 1 */

    for (i=0; i<Coded_Picture_Width; i+=4)

    {

      y = *py++;

      Y2 = *Y2_ptr++;

      u = *pu++ >> 1;

      v = *pv++ >> 1;

      *dst++  = Pixel[Y_Table[((y+Y2)>>1)]|Cb_Table[u]|Cr_Table[v]];

      *dst2++ = Pixel[Y_Table[Y2+12]|Cb_Table[u+12]|Cr_Table[v+12]];



      y = *py++;

      Y2 = *Y2_ptr++;

      if (chroma_format==CHROMA444)

      {

        u = *pu++ >> 1;

        v = *pv++ >> 1;

      }

      *dst++  = Pixel[Y_Table[((y+Y2)>>1)+8]|Cb_Table[u+8]|Cr_Table[v+8]];

      *dst2++ = Pixel[Y_Table[Y2+4]|Cb_Table[u+4]|Cr_Table[v+4]];



      y = *py++;

      Y2 = *Y2_ptr++;

      u = *pu++ >> 1;

      v = *pv++ >> 1;

      *dst++  = Pixel[Y_Table[((y+Y2)>>1)+2]|Cb_Table[u+2]|Cr_Table[v+2]];

      *dst2++ = Pixel[Y_Table[Y2+14]|Cb_Table[u+14]|Cr_Table[v+14]];



      y = *py++;

      Y2 = *Y2_ptr++;

      if (chroma_format==CHROMA444)

      {

        u = *pu++ >> 1;

        v = *pv++ >> 1;

      }

      *dst++  = Pixel[Y_Table[((y+Y2)>>1)+10]|Cb_Table[u+10]|Cr_Table[v+10]];

      *dst2++ = Pixel[Y_Table[Y2+6]|Cb_Table[u+6]|Cr_Table[v+6]];

    }



    if (j==0)

      py -= Coded_Picture_Width;

    else

      py += Coded_Picture_Width;



    Y2_ptr += Coded_Picture_Width;

    dst += Coded_Picture_Width;

    dst2 += Coded_Picture_Width;



    if (chroma_format==CHROMA420)

    {

      pu -= Chroma_Width;

      pv -= Chroma_Width;

    }

    else

    {

      pu += Chroma_Width;

      pv += Chroma_Width;

    }



    /* line j + 2. j + 3 */

    for (i=0; i<Coded_Picture_Width; i+=4)

    {

      y = *py++;

      Y2 = *Y2_ptr++;

      u = *pu++ >> 1;

      v = *pv++ >> 1;

      *dst++  = Pixel[Y_Table[((y+Y2)>>1)+3]|Cb_Table[u+3]|Cr_Table[v+3]];

      *dst2++ = Pixel[Y_Table[Y2+15]|Cb_Table[u+15]|Cr_Table[v+15]];



      y = *py++;

      Y2 = *Y2_ptr++;

      if (chroma_format==CHROMA444)

      {

        u = *pu++ >> 1;

        v = *pv++ >> 1;

      }

      *dst++  = Pixel[Y_Table[((y+Y2)>>1)+11]|Cb_Table[u+11]|Cr_Table[v+11]];

      *dst2++ = Pixel[Y_Table[Y2+7]|Cb_Table[u+7]|Cr_Table[v+7]];



      y = *py++;

      Y2 = *Y2_ptr++;

      u = *pu++ >> 1;

      v = *pv++ >> 1;

      *dst++  = Pixel[Y_Table[((y+Y2)>>1)+1]|Cb_Table[u+1]|Cr_Table[v+1]];

      *dst2++ = Pixel[Y_Table[Y2+13]|Cb_Table[u+13]|Cr_Table[v+13]];



      y = *py++;

      Y2 = *Y2_ptr++;

      if (chroma_format==CHROMA444)

      {

        u = *pu++ >> 1;

        v = *pv++ >> 1;

      }

      *dst++  = Pixel[Y_Table[((y+Y2)>>1)+9]|Cb_Table[u+9]|Cr_Table[v+9]];

      *dst2++ = Pixel[Y_Table[Y2+5]|Cb_Table[u+5]|Cr_Table[v+5]];

    }



    py += Coded_Picture_Width;

    Y2_ptr += Coded_Picture_Width;

    dst += Coded_Picture_Width;

    dst2 += Coded_Picture_Width;

    pu += Chroma_Width;

    pv += Chroma_Width;

  }

}



static void Dither_Top_Field420(src,dst)

unsigned char *src[];

unsigned char *dst;

{

  int i,j;

  int Y1,Cb1,Cr1,Y2,Cb2,Cr2;

  unsigned char *Y1_ptr,*Cb1_ptr,*Cr1_ptr,*Y2_ptr,*Cb2_ptr,*Cr2_ptr,*dst2;



  Y1_ptr = src[0];

  Cb1_ptr = src[1];

  Cr1_ptr = src[2];



  Y2_ptr = Y1_ptr + (Coded_Picture_Width<<1);

  Cb2_ptr = Cb1_ptr + (Chroma_Width<<1);

  Cr2_ptr = Cr1_ptr + (Chroma_Width<<1);



  dst2 = dst + Coded_Picture_Width;



  for (j=0; j<Coded_Picture_Height; j+=4)

  {

    /* line j + 0, j + 1 */

    for (i=0; i<Coded_Picture_Width; i+=4)

    {

      Y1 = *Y1_ptr++;

      Y2 = *Y2_ptr++;

      Cb1 = *Cb1_ptr++ >> 1;

      Cr1 = *Cr1_ptr++ >> 1;

      Cb2 = *Cb2_ptr++ >> 1;

      Cr2 = *Cr2_ptr++ >> 1;

      *dst++  = Pixel[Y_Table[((3*Y1+Y2)>>2)]|Cb_Table[Cb1]|Cr_Table[Cr1]];

      *dst2++ = Pixel[Y_Table[((Y1+3*Y2)>>2)+12]|Cb_Table[((3*Cb1+Cb2)>>2)+12]

                                             |Cr_Table[((3*Cr1+Cr2)>>2)+12]];



      Y1 = *Y1_ptr++;

      Y2 = *Y2_ptr++;

      *dst++  = Pixel[Y_Table[((3*Y1+Y2)>>2)+8]|Cb_Table[Cb1+8]|Cr_Table[Cr1+8]];

      *dst2++ = Pixel[Y_Table[((Y1+3*Y2)>>2)+4]|Cb_Table[((3*Cb1+Cb2)>>2)+4]

                                            |Cr_Table[((3*Cr1+Cr2)>>2)+4]];



      Y1 = *Y1_ptr++;

      Y2 = *Y2_ptr++;

      Cb1 = *Cb1_ptr++ >> 1;

      Cr1 = *Cr1_ptr++ >> 1;

      Cb2 = *Cb2_ptr++ >> 1;

      Cr2 = *Cr2_ptr++ >> 1;

      *dst++  = Pixel[Y_Table[((3*Y1+Y2)>>2)+2]|Cb_Table[Cb1+2]|Cr_Table[Cr1+2]];

      *dst2++ = Pixel[Y_Table[((Y1+3*Y2)>>2)+14]|Cb_Table[((3*Cb1+Cb2)>>2)+14]

                                             |Cr_Table[((3*Cr1+Cr2)>>2)+14]];



      Y1 = *Y1_ptr++;

      Y2 = *Y2_ptr++;

      *dst++  = Pixel[Y_Table[((3*Y1+Y2)>>2)+10]|Cb_Table[Cb1+10]|Cr_Table[Cr1+10]];

      *dst2++ = Pixel[Y_Table[((Y1+3*Y2)>>2)+6]|Cb_Table[((3*Cb1+Cb2)>>2)+6]

                                            |Cr_Table[((3*Cr1+Cr2)>>2)+6]];

    }



    Y1_ptr += Coded_Picture_Width;



    if (j!=(Coded_Picture_Height-4))

      Y2_ptr += Coded_Picture_Width;

    else

      Y2_ptr -= Coded_Picture_Width;



    Cb1_ptr -= Chroma_Width;

    Cr1_ptr -= Chroma_Width;

    Cb2_ptr -= Chroma_Width;

    Cr2_ptr -= Chroma_Width;



    dst  += Coded_Picture_Width;

    dst2 += Coded_Picture_Width;



    /* line j + 2, j + 3 */

    for (i=0; i<Coded_Picture_Width; i+=4)

    {

      Y1 = *Y1_ptr++;

      Y2 = *Y2_ptr++;

      Cb1 = *Cb1_ptr++ >> 1;

      Cr1 = *Cr1_ptr++ >> 1;

      Cb2 = *Cb2_ptr++ >> 1;

      Cr2 = *Cr2_ptr++ >> 1;

      *dst++  = Pixel[Y_Table[((3*Y1+Y2)>>2)+3]|Cb_Table[((Cb1+Cb2)>>1)+3]

                                            |Cr_Table[((Cr1+Cr2)>>1)+3]];

      *dst2++ = Pixel[Y_Table[((Y1+3*Y2)>>2)+15]|Cb_Table[((Cb1+3*Cb2)>>2)+15]

                                             |Cr_Table[((Cr1+3*Cr2)>>2)+15]];



      Y1 = *Y1_ptr++;

      Y2 = *Y2_ptr++;

      *dst++  = Pixel[Y_Table[((3*Y1+Y2)>>2)+11]|Cb_Table[((Cb1+Cb2)>>1)+11]

                                             |Cr_Table[((Cr1+Cr2)>>1)+11]];

      *dst2++ = Pixel[Y_Table[((Y1+3*Y2)>>2)+7]|Cb_Table[((Cb1+3*Cb2)>>2)+7]

                                            |Cr_Table[((Cr1+3*Cr2)>>2)+7]];



      Y1 = *Y1_ptr++;

      Y2 = *Y2_ptr++;

      Cb1 = *Cb1_ptr++ >> 1;

      Cr1 = *Cr1_ptr++ >> 1;

      Cb2 = *Cb2_ptr++ >> 1;

      Cr2 = *Cr2_ptr++ >> 1;

      *dst++  = Pixel[Y_Table[((3*Y1+Y2)>>2)+1]|Cb_Table[((Cb1+Cb2)>>1)+1]

                                            |Cr_Table[((Cr1+Cr2)>>1)+1]];

      *dst2++ = Pixel[Y_Table[((Y1+3*Y2)>>2)+13]|Cb_Table[((Cb1+3*Cb2)>>2)+13]

                                             |Cr_Table[((Cr1+3*Cr2)>>2)+13]];



      Y1 = *Y1_ptr++;

      Y2 = *Y2_ptr++;

      *dst++  = Pixel[Y_Table[((3*Y1+Y2)>>2)+9]|Cb_Table[((Cb1+Cb2)>>1)+9]

                                            |Cr_Table[((Cr1+Cr2)>>1)+9]];

      *dst2++ = Pixel[Y_Table[((Y1+3*Y2)>>2)+5]|Cb_Table[((Cb1+3*Cb2)>>2)+5]

                                            |Cr_Table[((Cr1+3*Cr2)>>2)+5]];

    }



    Y1_ptr += Coded_Picture_Width;

    Y2_ptr += Coded_Picture_Width;

    Cb1_ptr += Chroma_Width;

    Cr1_ptr += Chroma_Width;

    if (j!=(Coded_Picture_Height-8))

    {

      Cb2_ptr += Chroma_Width;

      Cr2_ptr += Chroma_Width;

    }

    else

    {

      Cb2_ptr -= Chroma_Width;

      Cr2_ptr -= Chroma_Width;

    }

    dst += Coded_Picture_Width;

    dst2+= Coded_Picture_Width;

  }

}



static void Dither_Bottom_Field420(src,dst)

unsigned char *src[];

unsigned char *dst;

{

  int i,j;

  int Y1,Cb1,Cr1,Y2,Cb2,Cr2;

  unsigned char *Y1_ptr,*Cb1_ptr,*Cr1_ptr,*Y2_ptr,*Cb2_ptr,*Cr2_ptr,*dst2;



  Y2_ptr = Y1_ptr = src[0] + Coded_Picture_Width;

  Cb2_ptr = Cb1_ptr = src[1] + Chroma_Width;

  Cr2_ptr = Cr1_ptr = src[2] + Chroma_Width;



  dst2 = dst;



  for (j=0; j<Coded_Picture_Height; j+=4)

  {

    /* line j + 0, j + 1 */

    for (i=0; i<Coded_Picture_Width; i+=4)

    {

      Y1 = *Y1_ptr++;

      Y2 = *Y2_ptr++;

      Cb1 = *Cb1_ptr++ >> 1;

      Cr1 = *Cr1_ptr++ >> 1;

      Cb2 = *Cb2_ptr++ >> 1;

      Cr2 = *Cr2_ptr++ >> 1;

      *dst++  = Pixel[Y_Table[((3*Y1+Y2)>>2)+15]|Cb_Table[((3*Cb1+Cb2)>>2)+15]

                                             |Cr_Table[((3*Cr1+Cr2)>>2)+15]];

      *dst2++ = Pixel[Y_Table[((Y1+3*Y2)>>2)]|Cb_Table[((Cb1+Cb2)>>1)]

                                          |Cr_Table[((Cr1+Cr2)>>1)]];



      Y1 = *Y1_ptr++;

      Y2 = *Y2_ptr++;

      *dst++  = Pixel[Y_Table[((3*Y1+Y2)>>2)+7]|Cb_Table[((3*Cb1+Cb2)>>2)+7]

                                            |Cr_Table[((3*Cr1+Cr2)>>2)+7]];

      *dst2++ = Pixel[Y_Table[((Y1+3*Y2)>>2)+8]|Cb_Table[((Cb1+Cb2)>>1)+8]

                                            |Cr_Table[((Cr1+Cr2)>>1)+8]];



      Y1 = *Y1_ptr++;

      Y2 = *Y2_ptr++;

      Cb1 = *Cb1_ptr++ >> 1;

      Cr1 = *Cr1_ptr++ >> 1;

      Cb2 = *Cb2_ptr++ >> 1;

      Cr2 = *Cr2_ptr++ >> 1;

      *dst++  = Pixel[Y_Table[((3*Y1+Y2)>>2)+13]|Cb_Table[((3*Cb1+Cb2)>>2)+13]

                                             |Cr_Table[((3*Cr1+Cr2)>>2)+13]];

      *dst2++ = Pixel[Y_Table[((Y1+3*Y2)>>2)+2]|Cb_Table[((Cb1+Cb2)>>1)+2]

                                            |Cr_Table[((Cr1+Cr2)>>1)+2]];



      Y1 = *Y1_ptr++;

      Y2 = *Y2_ptr++;

      *dst++  = Pixel[Y_Table[((3*Y1+Y2)>>2)+5]|Cb_Table[((3*Cb1+Cb2)>>2)+5]

                                            |Cr_Table[((3*Cr1+Cr2)>>2)+5]];

      *dst2++ = Pixel[Y_Table[((Y1+3*Y2)>>2)+10]|Cb_Table[((Cb1+Cb2)>>1)+10]

                                             |Cr_Table[((Cr1+Cr2)>>1)+10]];

    }



    if (j!=0)

      Y1_ptr += Coded_Picture_Width;

    else

      Y1_ptr -= Coded_Picture_Width;



    Y2_ptr += Coded_Picture_Width;



    Cb1_ptr -= Chroma_Width;

    Cr1_ptr -= Chroma_Width;

    Cb2_ptr -= Chroma_Width;

    Cr2_ptr -= Chroma_Width;



    if (j!=0)

      dst  += Coded_Picture_Width;



    dst2 += Coded_Picture_Width;



    /* line j + 2, j + 3 */

    for (i=0; i<Coded_Picture_Width; i+=4)

    {

      Y1 = *Y1_ptr++;

      Y2 = *Y2_ptr++;

      Cb1 = *Cb1_ptr++ >> 1;

      Cr1 = *Cr1_ptr++ >> 1;

      Cb2 = *Cb2_ptr++ >> 1;

      Cr2 = *Cr2_ptr++ >> 1;

      *dst++  = Pixel[Y_Table[((3*Y1+Y2)>>2)+12]|Cb_Table[((Cb1+3*Cb2)>>2)+12]

                                             |Cr_Table[((Cr1+3*Cr2)>>2)+12]];

      *dst2++ = Pixel[Y_Table[((Y1+3*Y2)>>2)+3]|Cb_Table[Cb2+3]

                                            |Cr_Table[Cr2+3]];



      Y1 = *Y1_ptr++;

      Y2 = *Y2_ptr++;

      *dst++  = Pixel[Y_Table[((3*Y1+Y2)>>2)+4]|Cb_Table[((Cb1+3*Cb2)>>2)+4]

                                            |Cr_Table[((Cr1+3*Cr2)>>2)+4]];

      *dst2++ = Pixel[Y_Table[((Y1+3*Y2)>>2)+11]|Cb_Table[Cb2+11]

                                             |Cr_Table[Cr2+11]];



      Y1 = *Y1_ptr++;

      Y2 = *Y2_ptr++;

      Cb1 = *Cb1_ptr++ >> 1;

      Cr1 = *Cr1_ptr++ >> 1;

      Cb2 = *Cb2_ptr++ >> 1;

      Cr2 = *Cr2_ptr++ >> 1;

      *dst++  = Pixel[Y_Table[((3*Y1+Y2)>>2)+14]|Cb_Table[((Cb1+3*Cb2)>>2)+14]

                                             |Cr_Table[((Cr1+3*Cr2)>>2)+14]];

      *dst2++ = Pixel[Y_Table[((Y1+3*Y2)>>2)+1]|Cb_Table[Cb2+1]

                                            |Cr_Table[Cr2+1]];



      Y1 = *Y1_ptr++;

      Y2 = *Y2_ptr++;

      *dst++  = Pixel[Y_Table[((3*Y1+Y2)>>2)+6]|Cb_Table[((Cb1+3*Cb2)>>2)+6]

                                            |Cr_Table[((Cr1+3*Cr2)>>2)+6]];

      *dst2++ = Pixel[Y_Table[((Y1+3*Y2)>>2)+9]|Cb_Table[Cb2+9]

                                            |Cr_Table[Cr2+9]];

    }



    Y1_ptr += Coded_Picture_Width;

    Y2_ptr += Coded_Picture_Width;



    if (j!=0)

    {

      Cb1_ptr += Chroma_Width;

      Cr1_ptr += Chroma_Width;

    }

    else

    {

      Cb1_ptr -= Chroma_Width;

      Cr1_ptr -= Chroma_Width;

    }



    Cb2_ptr += Chroma_Width;

    Cr2_ptr += Chroma_Width;



    dst += Coded_Picture_Width;

    dst2+= Coded_Picture_Width;

  }



  Y2_ptr -= (Coded_Picture_Width<<1);

  Cb2_ptr -= (Chroma_Width<<1);

  Cr2_ptr -= (Chroma_Width<<1);



  /* dither last line */

  for (i=0; i<Coded_Picture_Width; i+=4)

  {

    Y1 = *Y1_ptr++;

    Y2 = *Y2_ptr++;

    Cb1 = *Cb1_ptr++ >> 1;

    Cr1 = *Cr1_ptr++ >> 1;

    Cb2 = *Cb2_ptr++ >> 1;

    Cr2 = *Cr2_ptr++ >> 1;

    *dst++  = Pixel[Y_Table[((3*Y1+Y2)>>2)+15]|Cb_Table[((3*Cb1+Cb2)>>2)+15]

                                           |Cr_Table[((3*Cr1+Cr2)>>2)+15]];



    Y1 = *Y1_ptr++;

    Y2 = *Y2_ptr++;

    *dst++  = Pixel[Y_Table[((3*Y1+Y2)>>2)+7]|Cb_Table[((3*Cb1+Cb2)>>2)+7]

                                          |Cr_Table[((3*Cr1+Cr2)>>2)+7]];



    Y1 = *Y1_ptr++;

    Y2 = *Y2_ptr++;

    Cb1 = *Cb1_ptr++ >> 1;

    Cr1 = *Cr1_ptr++ >> 1;

    Cb2 = *Cb2_ptr++ >> 1;

    Cr2 = *Cr2_ptr++ >> 1;

    *dst++  = Pixel[Y_Table[((3*Y1+Y2)>>2)+13]|Cb_Table[((3*Cb1+Cb2)>>2)+13]

                                           |Cr_Table[((3*Cr1+Cr2)>>2)+13]];



    Y1 = *Y1_ptr++;

    Y2 = *Y2_ptr++;

    *dst++  = Pixel[Y_Table[((3*Y1+Y2)>>2)+5]|Cb_Table[((3*Cb1+Cb2)>>2)+5]

                                          |Cr_Table[((3*Cr1+Cr2)>>2)+5]];

    }



}

#endif

