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

#include "config.h"

#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 "mpeg-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
