/* amidisplay.c, Amiga 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

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

#include "config.h"
#include "global.h"

#define CYBERGFXNAME     "cybergraphics.library"
#define CYBERGFXVERSION  40L

/* Amiga includes */
#include <dos/dos.h>
#include <exec/exec.h>
#include <dos/dos.h>
#include <graphics/gfx.h>
#include <intuition/intuition.h>
#include <cybergraphics/cybergraphics.h>
#include <libraries/picasso96.h>

/* Amiga prototypes */
#include <proto/exec.h>
#include <proto/dos.h>
#include <proto/graphics.h>
#include <proto/intuition.h>
#include <proto/asl.h>
#include <proto/cybergraphics.h>
#include <clib/picasso96_protos.h>
#ifdef __PPC__
#include <clib/powerpc_protos.h>
#endif

struct Library *CyberGfxBase = NULL;
struct Library *P96Base = NULL;
struct Window *displayWindow = NULL;
struct Screen *displayScreen = NULL;
struct RastPort *tempRastPort = NULL;
void *Ximage_Ptr, *Ximage_Ptr2;
struct BitMap displayBitMap;
int crv, cbu, cgu, cgv;


/* private prototypes */
static void Display_Image _ANSI_ARGS_((void *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));
static void  c2p1x1_8_c5_ppc(void *chunky, void *bitplanes,
                             int chunkyx, int chunkyy,
                             int xoffset, int yoffset,
                             int bitplanesize);
static int __amiga_exit(void);
static void __amiga_checkdisplay(void);


/* local data */
static unsigned char *Dithered_Image = NULL, *Dithered_Image2 = NULL, *Doubled_Image = NULL;

static unsigned char Y_Table[256+16];
static unsigned char Cb_Table[128+16];
static unsigned char Cr_Table[128+16];

static unsigned char Pixel[256];

static int displayIsOpen = FALSE;
static int displayIsNative = TRUE;
static int Width,Height,Depth,BytesPerRow,BytesPerPix,PIPwidth,PIPheight;
static void *GfxAddr;



void Initialize_Display_Process(char *name)
/* connect to server, create and map window,
 * allocate colors and (shared) memory
 */
{
  int Y, Cb, Cr, R, G, B;
  int i;
  unsigned char *temp;

  displayIsOpen = TRUE;
  displayBitMap.Planes[0]=0;
  CyberGfxBase = OpenLibrary(CYBERGFXNAME,CYBERGFXVERSION);

  /* no mode selected, request one */
  if(Mode_ID == (unsigned long)INVALID_ID && Output_Type < T_AMIGAPIP) {
    if (CyberGfxBase) {
      static short cmodels[] = { PIXFMT_LUT8,PIXFMT_RGB15,
                                 PIXFMT_RGB16,PIXFMT_ARGB32,-1 };

      if ((Mode_ID = CModeRequestTags(NULL,
                        CYBRMREQ_CModelArray,(ULONG)cmodels,
                        CYBRMREQ_MinWidth,Coded_Picture_Width*Double_Pixels,
                        CYBRMREQ_MinHeight,Coded_Picture_Height*Double_Pixels,
                        TAG_DONE)) == 0xffffffff)
        Error("No valid screenmode selected\n");
    }
    else {
      struct ScreenModeRequester *sreq = NULL;

      if (sreq = (struct ScreenModeRequester *)
         AllocAslRequestTags(ASL_ScreenModeRequest,TAG_END)) {

        if (AslRequest(sreq,TAG_END))
          Mode_ID = sreq->sm_DisplayID;
        else
          Error("No valid screenmode selected\n");
        FreeAslRequest(sreq);
      }
    }
    printf("Next time, use option -m0x%lx\n",Mode_ID);
  }

  /* native display mode ? */
  if (CyberGfxBase)
    if (IsCyberModeID(Mode_ID))
      displayIsNative = FALSE;

  if (Output_Type == T_AMIGAPIP) {
    if (P96Base = OpenLibrary("Picasso96API.library",0))
      displayIsNative = FALSE;
    else
      Error("Picasso96 not available!\n");
  }

  /* setup automatically closing of the display */

/*  if(onbreak(&__amiga_exit)) Error("onbreak failed\n");*/
  if(atexit(&__amiga_checkdisplay)) Error("atexit failed\n");

  /* open screen */

  if (displayIsNative) {
    Depth = 8;

    if (!(temp = (unsigned char *)
        AllocRaster(Coded_Picture_Width*Double_Pixels,
                    Coded_Picture_Height*Double_Pixels*8)) )
      Error("AllocRaster failed\n");

    /* initialize bitmap */

    InitBitMap(&displayBitMap, 8, Coded_Picture_Width*Double_Pixels,
               Coded_Picture_Height*Double_Pixels);

    /* attach image data to bitmap */

    for (i=0; i<8; i++)
      displayBitMap.Planes[i] = temp + i * RASSIZE(Coded_Picture_Width*Double_Pixels, Coded_Picture_Height*Double_Pixels);

    if (!(displayScreen = OpenScreenTags(NULL,
              SA_BitMap,&displayBitMap,
              SA_Width,Coded_Picture_Width*Double_Pixels,
              SA_Height,Coded_Picture_Height*Double_Pixels,
              SA_Title,"VMPEG",
              SA_DisplayID,Mode_ID,
              SA_Depth,8,
              SA_ShowTitle,FALSE,
              TAG_END)))
      Error("OpenScreenTags failed\n");
  }

  else if (Output_Type != T_AMIGAPIP) {
    /* CyberGfx display */

    Width = GetCyberIDAttr(CYBRIDATTR_WIDTH,Mode_ID);
    Height = GetCyberIDAttr(CYBRIDATTR_HEIGHT,Mode_ID);
    Depth = GetCyberIDAttr(CYBRIDATTR_DEPTH,Mode_ID);
    if (!(displayScreen = OpenScreenTags(NULL,
                                         SA_Width,Width,
                                         SA_Height,Height,
                                         SA_Title,"VMPEG",
                                         SA_DisplayID,Mode_ID,
                                         SA_Depth,Depth,
                                         SA_ShowTitle,FALSE,
                                         TAG_END)))
      Error("OpenScreenTags failed\n");

    BytesPerRow = GetCyberMapAttr(displayScreen->RastPort.BitMap,
                                  CYBRMATTR_XMOD);
    BytesPerPix = GetCyberMapAttr(displayScreen->RastPort.BitMap,
                                  CYBRMATTR_BPPIX);
    GfxAddr = (void *)GetCyberMapAttr(displayScreen->RastPort.BitMap,
                                      CYBRMATTR_DISPADR);
    /* center display */
    if (Height > Coded_Picture_Height*Double_Pixels)
      GfxAddr = (void *)((char *)GfxAddr + BytesPerRow *
                         ((Height-Coded_Picture_Height*Double_Pixels)>>1));
    if (Width > Coded_Picture_Width*Double_Pixels)
      GfxAddr = (void *)((char *)GfxAddr + (BytesPerPix *
                         ((Width - Coded_Picture_Width*Double_Pixels)
                          >> 1) & ~15));
  }

  if (Output_Type == T_AMIGAPIP) {
    /* Picaoss96 PIP window */

    struct TagItem piptags[] = {
      P96PIP_SourceFormat, RGBFB_Y4U2V2,
      P96PIP_SourceWidth, 0,
      P96PIP_SourceHeight, 0,
      P96PIP_InitialIntScaling,TRUE,
      P96PIP_AllowCropping,TRUE,
      WA_Title, (ULONG)"VMPEG Display",
      WA_Activate, TRUE,
      WA_RMBTrap, TRUE,
      WA_Width, 0,
      WA_Height, 0,
      WA_DragBar,TRUE,
      WA_DepthGadget,TRUE,
      WA_IDCMP,IDCMP_RAWKEY,
      WA_NewLookMenus,TRUE,
      WA_PubScreenName, (ULONG)"Workbench",
      TAG_DONE,0
    };

    piptags[1].ti_Data = Coded_Picture_Width;
    piptags[2].ti_Data = Coded_Picture_Height;
    piptags[8].ti_Data = Coded_Picture_Width*Double_Pixels;
    piptags[9].ti_Data = Coded_Picture_Height*Double_Pixels;
    PIPwidth = ((Coded_Picture_Width+15)&~15)>>1;
    PIPheight = Coded_Picture_Height>>1;
    if (displayWindow = p96PIP_OpenTagList(piptags)) {
      int x,y;
      struct TagItem gtTags[] = {
        {P96PIP_SourceBitMap,0},{TAG_DONE, 0}
      };
      struct RenderInfo renderinfo;
      struct BitMap *bitmap;
      LONG lock;

      gtTags[0].ti_Data = (ULONG)&bitmap;
      p96PIP_GetTagList(displayWindow,gtTags);
      lock = p96LockBitMap(bitmap,(UBYTE *)&renderinfo,
                           sizeof(struct RenderInfo));
      GfxAddr = (void *)renderinfo.Memory;
      p96UnlockBitMap(bitmap, lock);
      Depth = 24;
    }
    else
      Error("PIP window not supported by hardware\n");
  }

  else {
    /* open window on screen */
    
    if (!(displayWindow = OpenWindowTags(NULL,WA_CustomScreen,displayScreen,
            WA_Title,"VMPEG Display",
            WA_Width,Width,
            WA_Height,Height,
            WA_IDCMP,IDCMP_RAWKEY,
            WA_Backdrop,TRUE,
            WA_Borderless,TRUE,
            WA_RMBTrap,TRUE,
            WA_Activate,TRUE,
            WA_NewLookMenus,TRUE,
            WA_ScreenTitle,"VMPEG",
            TAG_END)))
      Error("OpenWindowTags failed\n");
  }

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

  /* 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=0; i<256; i++)
    /* clear to make sure that all colors are correctly freed */
    Pixel[i] = 0xff;

  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];

    if (Depth == 8)
      SetRGB32(&displayScreen->ViewPort,i,R<<24,G<<24,B<<24);
    Pixel[i] = i;
  }

  /* allocate temporary rastport */

  if (!(tempRastPort = malloc(sizeof(struct RastPort))))
    Error("malloc failed\n");

  /* allocate buffers */

  if (!(Dithered_Image = (unsigned char *)
      malloc((Coded_Picture_Width)*(Coded_Picture_Height)*4)))
    Error("malloc failed\n");

  if (!progressive_sequence)
    if (!(Dithered_Image2 = (unsigned char *)
        malloc((Coded_Picture_Width)*(Coded_Picture_Height))))
      Error("malloc failed\n");

  if (!(Doubled_Image = (unsigned char *)
      malloc((Coded_Picture_Width*Double_Pixels)*
             (Coded_Picture_Height*Double_Pixels))))
    Error("malloc failed\n");
}


static int __amiga_exit(void)
{
  fprintf(stderr,"CTRL-C received\n");
  return 1;
}


static void __amiga_checkdisplay(void)
{
  if(displayIsOpen) Terminate_Display_Process();
}


void Terminate_Display_Process()
{
  int i;

  if(Doubled_Image) {
    free(Doubled_Image);
    Doubled_Image = NULL;
  }
  if(Dithered_Image2) {
    free(Dithered_Image2);
    Dithered_Image2 = NULL;
  }
  if(Dithered_Image) {
    free(Dithered_Image);
    Dithered_Image = NULL;
  }

  if (displayWindow) {
    if (Output_Type == T_AMIGAPIP)
      p96PIP_Close(displayWindow);
    else
      CloseWindow(displayWindow);
  }
  if (displayScreen)
    CloseScreen(displayScreen);

  if(displayBitMap.Planes[0]) {
    FreeRaster(displayBitMap.Planes[0],Coded_Picture_Width*Double_Pixels,Coded_Picture_Height*Double_Pixels*8);
    displayBitMap.Planes[0]=NULL;
  }
  displayIsOpen = FALSE;

  /* close libraries */

  if (P96Base) {
    CloseLibrary(P96Base);
    P96Base = NULL;
  }
  if (CyberGfxBase) {
    CloseLibrary(CyberGfxBase);
    CyberGfxBase = NULL;
  }
}


#ifdef __PPC__
static void Display_Fps()
{
  static char buf[8];

  sprintf(buf,"%3.1f",real_frame_rate);
  Move(&displayScreen->RastPort,0,10);
  Text(&displayScreen->RastPort,buf,strlen(buf));
}
#endif


static void show_timecode()
{
  static char buf[12];

  sprintf(buf,"%02d:%02d:%02d",hour,minute,sec);
  Move(&displayScreen->RastPort,
       Coded_Picture_Width>>1,Coded_Picture_Height>>1);
  Text(&displayScreen->RastPort,buf,8);
}


static void Display_Image(void *Ximage_Ptr,unsigned char *Dithered_Image)
{
  /* scale picture to the double if -h is present */
  if(Double_Pixels == 2)
  {
    int i,j;
    unsigned char *dest_pointer = Doubled_Image;
    unsigned char *source_pointer = Dithered_Image;
    for(i=0;i<Coded_Picture_Height;i++)
    {
      for(j=0;j<Coded_Picture_Width;j++)
      {
        *(dest_pointer++)=*source_pointer;
        *(dest_pointer++)=*(source_pointer++);
      }
      source_pointer-=Coded_Picture_Width;
      for(j=0;j<Coded_Picture_Width;j++)
      {
        *(dest_pointer)++=*source_pointer;
        *(dest_pointer)++=*(source_pointer++);
      }
    }

    Dithered_Image = Doubled_Image;
  }

  if (displayIsNative) {
    c2p1x1_8_c5_ppc(Dithered_Image,displayBitMap.Planes[0],
                    Coded_Picture_Width*Double_Pixels,
                    Coded_Picture_Height*Double_Pixels, 0, 0,
                    ((Coded_Picture_Width*Double_Pixels) *
                    (Coded_Picture_Height*Double_Pixels)) >> 3);
  }
  else {
#ifdef __PPC__
    if (Output_Type == T_AMIGADIRECT)
      WriteFrame8(Dithered_Image,GfxAddr,BytesPerRow,
                  Coded_Picture_Width*Double_Pixels,
                  Coded_Picture_Height*Double_Pixels);
    else
#endif
      WritePixelArray8(&displayScreen->RastPort,0,0,
                       (Coded_Picture_Width*Double_Pixels)-1,
                       (Coded_Picture_Height*Double_Pixels)-1,
                       Dithered_Image,&displayScreen->RastPort);
  }

#ifdef __PPC__
  if (Fps_Flag)
    Display_Fps();
#endif
}


static void WriteARGBPixelArray(unsigned char *src[],unsigned char *dst)
{
  unsigned char *py1,*py2,*pu,*pv;
  unsigned char *buf = Dithered_Image;
  int y,u,v,i,j;
  int bpr = Coded_Picture_Width*4;
  ULONG d;

  py1 = src[0];
  py2 = src[0]+Coded_Picture_Width;
  pu = src[1];
  pv = src[2];
  for (j=0; j<Coded_Picture_Height; j+=2, buf+=bpr,
       py1+=Coded_Picture_Width,py2+=Coded_Picture_Width) {

    for (i=0; i<Coded_Picture_Width; i+=2, buf+=8) {
      u = *pu++ - 128;
      v = *pv++ - 128;

      y = 76309 * ((int)*py1++ - 16); /* (255/219)*65536 */
      d = ((Clip[(y + crv*v + 32768)>>16] & 0xff) << 16) |
          ((Clip[(y - cgu*u -cgv*v + 32768)>>16] & 0xff) << 8) |
          (Clip[(y + cbu*u + 32786)>>16] & 0xff);
      *(ULONG *)(buf+0) = d;

      y = 76309 * ((int)*py1++ - 16); /* (255/219)*65536 */
      d = ((Clip[(y + crv*v + 32768)>>16] & 0xff) << 16) |
          ((Clip[(y - cgu*u -cgv*v + 32768)>>16] & 0xff) << 8) |
          (Clip[(y + cbu*u + 32786)>>16] & 0xff);
      *(ULONG *)(buf+4) = d;

      y = 76309 * ((int)*py2++ - 16); /* (255/219)*65536 */
      d = ((Clip[(y + crv*v + 32768)>>16] & 0xff) << 16) |
          ((Clip[(y - cgu*u -cgv*v + 32768)>>16] & 0xff) << 8) |
          (Clip[(y + cbu*u + 32786)>>16] & 0xff);
      *(ULONG *)(buf+bpr) = d;

      y = 76309 * ((int)*py2++ - 16); /* (255/219)*65536 */
      d = ((Clip[(y + crv*v + 32768)>>16] & 0xff) << 16) |
          ((Clip[(y - cgu*u -cgv*v + 32768)>>16] & 0xff) << 8) |
          (Clip[(y + cbu*u + 32786)>>16] & 0xff);
      *(ULONG *)(buf+bpr+4) = d;
    }
  }
  WritePixelArray(Dithered_Image,0,0,bpr,&displayScreen->RastPort,0,0,
                  Coded_Picture_Width,Coded_Picture_Height,RECTFMT_ARGB);
}


static void Display_Truecolor(unsigned char *src[],unsigned char *dst)
{
#ifdef __PPC__
  if (Output_Type == T_AMIGADIRECT) {
    int modulo = BytesPerRow - Coded_Picture_Width * BytesPerPix;

    switch (Depth) {

      case 15:  /* RGB15 */
        WriteFrame15(src,dst,Coded_Picture_Width,Coded_Picture_Height,
                     BytesPerRow,modulo);
        break;

      case 16:  /* RGB16 */
        WriteFrame16(src,dst,Coded_Picture_Width,Coded_Picture_Height,
                     BytesPerRow,modulo);
        break;

      case 32:  /* RGB32 */
      case 24:  /* ... is reporting depth 24? Hmm... */
        WriteFrame32(src,dst,Coded_Picture_Width,Coded_Picture_Height,
                     BytesPerRow,modulo);
        break;
    }
  }
  else
#endif
    WriteARGBPixelArray(src,dst);

#ifdef __PPC__
  if (Fps_Flag)
    Display_Fps();
#endif
}


void Display_Second_Field()
{
  Display_Image(Ximage_Ptr2,Dithered_Image2);

  /* @@@ what happens for true color ? */
}


void Initialize_Dither_Matrix()
/* 4x4 ordered dither
 *
 * threshold pattern:
 *   0  8  2 10
 *  12  4 14  6
 *   3 11  1  9
 *  15  7 13  5
 */
{
  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(unsigned char *src[])
{
  /* should this test only the display flag, not progressive_sequence ? --CF */
  /* CHANGE 95/05/13: progressive_sequence->progressive_frame */
  static unsigned char *lmb = (unsigned char *)0xbfe001;
  static unsigned short *rmb = (unsigned short *)0xdff016;
  static unsigned char *keyc = (unsigned char *)0xbfec01;

  if ((*lmb & 0x40)==0 || *keyc==0x75)
    /* left mouse button or ESC-key quits */
    exit(0);

  if (Depth <= 8) {

    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);
  }

  else if (Output_Type == T_AMIGAPIP)
    /* Picasso96 PIP window */
    yuv2pip(GfxAddr,src,PIPwidth,PIPheight,0);

  else
    /* 15/16/24/32 bit direct yuv to rgb conversion */
    Display_Truecolor(src,(unsigned char *)GfxAddr);

  if (TimeCode_Flag)
    show_timecode();
}


static void Dither_Frame(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(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(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(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(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]];
    }

}


#define twomerges1(a, b, c, d, shift, mask) \
  { \
  temp1 = b; \
  temp2 = d; \
  temp1 >>= shift; \
  temp2 >>= shift; \
  temp1 ^= a; \
  temp2 ^= c; \
  temp1 &= mask; \
  temp2 &= mask; \
  a ^= temp1; \
  c ^= temp2; \
  temp1 <<= shift; \
  temp2 <<= shift; \
  b ^= temp1; \
  d ^= temp2; \
  }

#define twomerges2(a, b, c, d, shift, mask) \
  { \
  temp1 = (a ^ (b >> shift)) & mask; \
  temp2 = (c ^ (d >> shift)) & mask; \
  a ^= temp1; \
  c ^= temp2; \
  b ^= temp1 << shift; \
  d ^= temp2 << shift; \
  }


static void c2p1x1_8_c5_ppc(void *chunky, void *bitplanes,
                            int chunkyx, int chunkyy,
                            int xoffset, int yoffset,
                            int bitplanesize)
{
  ULONG *c;
  ULONG temp1, temp2;
  ULONG *p0, *p1, *p2, *p3, *p4, *p5, *p6, *p7;
  ULONG d0, d1, d2, d3, d4, d5, d6, d7;
  ULONG t0, t1, t2, t3, t4, t5, t6, t7;
  int i;

  c = (ULONG *) chunky;
  p0 = (ULONG *) (&(((char *) bitplanes)[yoffset * chunkyx >> 3]));
  p1 = (ULONG *) (&(((char *) p0)[bitplanesize]));
  p2 = (ULONG *) (&(((char *) p1)[bitplanesize]));
  p3 = (ULONG *) (&(((char *) p2)[bitplanesize]));
  p4 = (ULONG *) (&(((char *) p3)[bitplanesize]));
  p5 = (ULONG *) (&(((char *) p4)[bitplanesize]));
  p6 = (ULONG *) (&(((char *) p5)[bitplanesize]));
  p7 = (ULONG *) (&(((char *) p6)[bitplanesize]));

  i = chunkyx * chunkyy / (sizeof (ULONG) * 8);

  d0 = *c++;
  d1 = *c++;
  d2 = *c++;
  d3 = *c++;
  d4 = *c++;
  d5 = *c++;
  d6 = *c++;
  d7 = *c++;

  twomerges1(d0, d4, d1, d5, 16, 0x0000ffff);
  twomerges1(d2, d6, d3, d7, 16, 0x0000ffff);

  twomerges1(d0, d2, d1, d3, 8, 0x00ff00ff);
  twomerges1(d4, d6, d5, d7, 8, 0x00ff00ff);

  twomerges1(d0, d1, d2, d3, 4, 0x0f0f0f0f);
  twomerges1(d4, d5, d6, d7, 4, 0x0f0f0f0f);

  twomerges1(d0, d4, d1, d5, 2, 0x33333333);
  twomerges1(d2, d6, d3, d7, 2, 0x33333333);

  twomerges1(d0, d2, d1, d3, 1, 0x55555555);
  twomerges1(d4, d6, d5, d7, 1, 0x55555555);

  t0 = d7;
  t1 = d5;
  t2 = d3;
  t3 = d1;
  t4 = d6;
  t5 = d4;
  t6 = d2;
  t7 = d0;

  while (--i)
  {
    d0 = *c++;
    d1 = *c++;
    d2 = *c++;
    d3 = *c++;
    d4 = *c++;
    d5 = *c++;
    d6 = *c++;
    d7 = *c++;

    *p0++ = t0;

    twomerges1(d0, d4, d1, d5, 16, 0x0000ffff);
    twomerges1(d2, d6, d3, d7, 16, 0x0000ffff);

    *p1++ = t1;

    twomerges1(d0, d2, d1, d3, 8, 0x00ff00ff);
    twomerges1(d4, d6, d5, d7, 8, 0x00ff00ff);

    *p2++ = t2;

    twomerges1(d0, d1, d2, d3, 4, 0x0f0f0f0f);
    twomerges1(d4, d5, d6, d7, 4, 0x0f0f0f0f);

    *p3++ = t3;

    twomerges1(d0, d4, d1, d5, 2, 0x33333333);
    twomerges1(d2, d6, d3, d7, 2, 0x33333333);

    *p4++ = t4;

    twomerges1(d0, d2, d1, d3, 1, 0x55555555);
    twomerges1(d4, d6, d5, d7, 1, 0x55555555);

    *p5++ = t5;
    *p6++ = t6;
    *p7++ = t7;

    t0 = d7;
    t1 = d5;
    t2 = d3;
    t3 = d1;
    t4 = d6;
    t5 = d4;
    t6 = d2;
    t7 = d0;

  }

  *p0++ = t0;
  *p1++ = t1;
  *p2++ = t2;
  *p3++ = t3;
  *p4++ = t4;
  *p5++ = t5;
  *p6++ = t6;
  *p7++ = t7;
}

#endif /* DISPLAY */
