/* -*-C-*-
******************************************************************************
*
* File:         process.c
* RCS:          $Header: /ImageMagick/delegates/fpx/jpeg/process.c,v 1.3 2000/12/23 22:37:48 bfriesen Exp $
* Description:  Default subsampling/YCbCr routines for the HP JPEG Encoder.
* Author:       Kirt Winter
* Created:      Fri Mar  1 09:04:22 1995
* Initial Source Release:     Thursday, March 7 1996
* Language:     C
* Package:      Hewlett-Packard JPEG Encoder/Decoder
*
* Copyright (c) 1999 Digital Imaging Group, Inc.
* For conditions of distribution and use, see copyright notice
* in Flashpix.h
*
******************************************************************************
*/
#include <stdlib.h>
#include "jpegconf.h"
#include "fpxmem.h"
#include "process.h"

#include "ejpeg.h"      /* CHG_JPEG_MEM_FIX - added this to get error code */

/*
** This routine strips color information from an opponent color stream.  Upon completion of
** its assigned duty, the destination buffer will be filled with LLLLabLLLLabLLLLab,
** assuming of course that it was originally LabLabLabLabLab.  Alpha channel is
** supported (as the last channel in the set), but is not subsampled.
**
*/
int
SubSample411(const unsigned char *src, unsigned char *dst, int tileSize, int bpp)
{
  int x, y, blocks, acc, lineOffset;
  unsigned char *dstPtr = dst;
  const unsigned char *p1, *p2, *p3, *p4;

  blocks = tileSize / 2;
  lineOffset = tileSize * bpp;
  for(x = 0; x < blocks; x++)
  {
    for(y = 0; y < blocks; y++)
    {
      p1 = src;
      p2 = p1 + bpp;
      p3 = p1 + lineOffset;
      p4 = p3 + bpp;

      *dstPtr++ = *p1++;
      *dstPtr++ = *p2++;
      *dstPtr++ = *p3++;
      *dstPtr++ = *p4++;
      /* p1 - p4 now point to first chroma sample
          ** We take the average chroma of the 4 points in the quadrant
          ** and place that in the destination buffer. 
      */
      acc = *p1++ + *p2++ + *p3++ + *p4++;
      *dstPtr++ = (unsigned char) ((acc + 2) >> 2);

      /* p1 - p4 now point to second chroma sample
         ** now, we repeat the above process for second chroma
      */
      acc = *p1++ + *p2++ + *p3++ + *p4++;
      *dstPtr++ = (unsigned char) ((acc + 2) >> 2);

      /* Alpha channel, if it exists, is never subsampled */
      if (bpp == 4)
      {
        *dstPtr++ = *p1++;
        *dstPtr++ = *p2++;
        *dstPtr++ = *p3++;
        *dstPtr++ = *p4++;
      }

      src += 2*bpp;   /* skip over one triplet (quadlet?) and point to
                                                 the next quadrant */
    }
    src += lineOffset;
  }
  return(0);
}

int
SubSample422(const unsigned char *src, unsigned char *dst, int tileSize, int bpp)
{
  int x, y, blocks, acc, lineOffset;
  unsigned char *dstPtr = dst;
  const unsigned char *p1, *p2;

  blocks = tileSize / 2;
  lineOffset = tileSize * bpp;
  for(x = 0; x < tileSize; x++)
  {
    for(y = 0; y < blocks; y++)
    {
      p1 = src;
      p2 = p1 + bpp;

      *dstPtr++ = *p1++;
      *dstPtr++ = *p2++;

      /* p1,p2 now point to first chroma sample
          ** here we take the average chroma of the two points
          ** and place that in the destination buffer 
      */
      acc = *p1++ + *p2++;
      *dstPtr++ = (unsigned char) ((acc + 1) >> 1);

      /* p1,p4 now point to second chroma sample
          ** so, we repeat the above process for second chroma
      */
      acc = *p1++ + *p2++;
      *dstPtr++ = (unsigned char) ((acc + 1) >> 1);

      /* If an alpha channel, it is never subsampled.*/
      if(bpp == 4)
      {
        *dstPtr++ = *p1++;
        *dstPtr++ = *p2++;
      }

      src += 2*bpp;   /* skip over one triplet (quadlet?) and point to
                                                 the next quadrant. */
    }
  }
  return(0);
}

/*
** This routine does an RGB to YCbCr conversion.  After complettion, the
** destination buffer will be filled with YCbCrYCbCrYCbCrYCbCrYCbCr,
** assuming of course that it was originally RGBRGBRGBRGBRGB.
** The routine also handles RGBa data, and in that situation, you'll
** get YCbCraYCbCraYCbCra...
**   
*/
int
RGBtoYCrCb(const unsigned char *src, unsigned char *dst, int tilerow, int bpp)
{
  int x;
  int tilesize = tilerow * tilerow * bpp;
  unsigned char inpR,inpG,inpB;
  long yo, cr, cb;
  long A1, A2, A3, A4;
  float F1, F2;

  /*
  ** the rotation routine is out of section 2.4.4.2
  ** of Pennebaker/Mitchell book.
  ** r,g,b values of 0-255 are mapped to 0-1 range, and then 
  ** all values are left shifted by 26 bits.
  */
  #if 0
  /* Pennebaker book's numbers: */
  A1 = 78952;
  A2 = 157903;
  A3 = 26317;
  A4 = 33554432;
  F1 = (float)1.6;
  #endif

  #if 1
  /* IJG's numbers: */
  A1 = 78381;
  A2 = 153879;
  A3 = 29884;
  A4 = 33554432;
  F1 = (float)1.402;
  F2 = (float)1.772;
  #endif

  if (bpp==3) {
    for(x = 0; x < tilesize; x+=bpp)
    {
      yo = A1*(long)(src[x]) + A2*(long)(src[x+1]) + A3*(long)(src[x+2]);

      /* old (Pennebaker book's) way:
      cb = (long)((((src[x+2])<<18) - yo) >> 1) + A4;
      cr = (long)((((src[x])<<18) - yo) / F1) + A4;
      */
      cb = (long)((((src[x+2])<<18) - yo) / F2) + A4;
      cr = (long)((((src[x])<<18) - yo) / F1) + A4;

      dst[x] = (unsigned char)(yo >> 18);
      dst[x+1] = (unsigned char)(cb >> 18);
      dst[x+2] = (unsigned char)(cr >> 18);
    }
  } else { /* bpp = 4 */
    for(x = 0; x < tilesize; x+=bpp)
    {
      inpR= (unsigned char) (255-src[x]);
      inpG= (unsigned char) (255-src[x+1]);
      inpB= (unsigned char) (255-src[x+2]);
      yo = A1*(long)(inpR) + A2*(long)(inpG) + A3*(long)(inpB);

      /* old (Pennebaker book's) way:
      cb = (long)((((inpB)<<18) - yo) >> 1) + A4;
      cr = (long)((((inpR)<<18) - yo) / F1) + A4;
      */
      cb = (long)((((inpB)<<18) - yo) / F2) + A4;
      cr = (long)((((inpR)<<18) - yo) / F1) + A4;
      dst[x] = (unsigned char)(yo >> 18);
      dst[x+1] = (unsigned char)(cb >> 18);
      dst[x+2] = (unsigned char)(cr >> 18);
      dst[x+3] = src[x+3];
    }
  }
  return(0);

} /* RGBtoYCrCb() */


/*
** This routine does an RGB to YCbCr conversion, then strips color information.  After
** completion, the destination buffer will be filled with YYYYCbCrYYYYCbCrYYYYCbCr,
** assuming of course that it was originally RGBRGBRGBRGBRGB.  Note that the routine does
** support conversion in place (i.e. src and dst can be the same buffer).
*/
int
RGBtoYCrCb_SubSample411(const unsigned char *src, unsigned char *dst, int tilerow, int bpp)
{
  int x, y, z, blocks, lineOffset;
  unsigned char *dstPtr = dst;
  const unsigned char *p[4], *q[4];
  unsigned char inpR,inpG,inpB;
  long yo[4], cr, cb;
  long A1, A2, A3, A4;
  float F1, F2;

  blocks = tilerow / 2;
  lineOffset = tilerow * bpp;

  #if 0
  /* Pennebaker book's numbers */
  A1 = 78952;
  A2 = 157903;
  A3 = 26317;
  A4 = 33554432;
  F1 = 1.6;
  F2 = 2.0; /* this can be a right shift */
  #endif

  #if 1
  /* IJG's numbers: */
  A1 = 78381;
  A2 = 153879;
  A3 = 29884;
  A4 = 33554432;
  F1 = (float)1.402;
  F2 = (float)1.772;
  #endif

  if (bpp == 3)
  { /* 3 channel */
    for(x = 0; x < blocks; x++)
    {
      for(y = 0; y < blocks; y++)
      {
        p[0] = src;
        p[1] = p[0] + bpp;
        p[2] = p[0] + lineOffset;
        p[3] = p[2] + bpp;
        cb = 0;
        cr = 0;
        for(z = 0; z < 4; z++)
        {
          yo[z] = A1*(long)*(p[z]) + A2*(long)*(p[z]+1) + A3*(long)*(p[z]+2);

          /* old (pink's book) way:
          cb += ((((*(p[z]+2)<<18) - yo[z]) >> 1) + A4) >> 2;
          cr += ((long)(((*(p[z])<<18) - yo[z]) / F1) + A4) >> 2;
          */
          cb += ((long)((((*(p[z]+2)<<18) - yo[z]) / F2) + A4) >> 2);
          cr += ((long)((((*(p[z])<<18) - yo[z]) / F1) + A4) >> 2);
          
          *dstPtr++ = (unsigned char)(yo[z] >> 18);
        }

        /* now it's time to write out a "u" and a "v" */
        *dstPtr++ = (unsigned char)(cb >> 18);
        *dstPtr++ = (unsigned char)(cr >> 18);

        src += bpp*2; /* skip over one triplet and point to the next quadrant */
      } /* for y */

      src += lineOffset;
    } /* for x */

  }
  else if (bpp == 4)
  { /* 4 channel */
    for(x = 0; x < blocks; x++)
    {
      for(y = 0; y < blocks; y++)
      {
        p[0] = src;
        p[1] = p[0] + bpp;
        p[2] = p[0] + lineOffset;
        p[3] = p[2] + bpp;

        q[0]=p[0]+3; /* used for alpha channel */
        q[1]=p[1]+3;
        q[2]=p[2]+3;
        q[3]=p[3]+3;
        cb = 0;
        cr = 0;
        for(z = 0; z < 4; z++)
        {
          inpR = (unsigned char) (255-*p[z]);
          inpG = (unsigned char) (255-*(p[z]+1));
          inpB = (unsigned char) (255-*(p[z]+2));
          yo[z] = A1*(long)inpR + A2*(long)inpG + A3*(long)inpB;

          /* old (Pennebaker book's) way:
          cb += ((((inpB<<18) - yo[z]) >> 1) + A4) >> 2;
          cr += ((long)(((inpR<<18) - yo[z]) / F1) + A4) >> 2;
          */
          cb += ((long)((((inpB<<18) - yo[z]) / F2) + A4) >> 2);
          cr += ((long)((((inpR<<18) - yo[z]) / F1) + A4) >> 2);
          
          *dstPtr++ = (unsigned char)(yo[z] >> 18);
        }

        /* now it's time to write out a "u" and a "v" */
        *dstPtr++ = (unsigned char)(cb >> 18);
        *dstPtr++ = (unsigned char)(cr >> 18);

        /* write out the Alpha channel */
        *dstPtr++ = *q[0];
        *dstPtr++ = *q[1];
        *dstPtr++ = *q[2];
        *dstPtr++ = *q[3];

        src += bpp*2; /* skip over one triplet and point to the next quadrant */
      }
      src += lineOffset;
    }
  }
  return(0);

} /* RGBtoYCrCb_SubSample411() */


/*
** This routine does an RGB to YCbCr conversion, then strips color information.
** This is not done particularly efficiently, but it's important that it get done
** correctly and on time.  So, rather than write an integrated routine like the 411
** subsampling, the individual pieces are called, simplifying coding and testing.
*/
int
RGBtoYCrCb_SubSample422(const unsigned char *src, unsigned char *dst, int tileSize, int bpp)
{
  unsigned char *dst0;
  int error;

  dst0 = (unsigned char *)FPX_malloc((size_t)(tileSize * tileSize * bpp));
  if(dst0 == NULL)            /* CHG_JPEG_MEM_FIX - added check */
    return EJPEG_ERROR_MEM;

  RGBtoYCrCb(src, dst0, tileSize, bpp);
  error = SubSample422(dst0, dst, tileSize, bpp);

  FPX_free(dst0);

  return error;

} /* RGBtoYCrCb_SubSample422() */
