/*
 * file:     illegal-colors.c
 * author:   Wes Barris
 * date:     11/05/93
 * purpose:  determines which colors are legal in NTSC (PAL) color space.
 *
 * copyright info:
 *
 *    @Copyright 1993
 *    Research Equipment Inc. dba Minnesota Supercomputer Center
 *
 * RESTRICTED RIGHTS LEGEND
 *
 * Use, duplication, or disclosure of this software and its documentation
 * by the Government is subject to restrictions as set forth in subdivision
 * { (b) (3) (ii) } of the Rights in Technical Data and Computer Software
 * clause at 52.227-7013.
 */
#include <stdio.h>
#include <math.h>

void
rgbtoyiq(int r, int g, int b, double *y, double *i, double *q)
{
/*
 * By inspection, the range for I and Q are determined to be:
 *
 * -.596 <= I <= .596
 * -.523 <= Q <= .523
 *
 */
   *y = .299*(r/255.0) + .587*(g/255.0) + .114*(b/255.0);
   *i = .596*(r/255.0) - .274*(g/255.0) - .322*(b/255.0);
   *q = .211*(r/255.0) - .523*(g/255.0) + .312*(b/255.0);
}

void
yiqtorgb(double y, double i, double q, int *r, int *g, int *b)
{
   *r = 255.0*(1.00*y + .9562*i + .6214*q);
   *g = 255.0*(1.00*y - .2727*i - .6468*q);
   *b = 255.0*(1.00*y -1.1037*i +1.7006*q);
}

void
rgbtoyuv(int r, int g, int b, double *y, double *u, double *v)
{
/*
 * By inspection, the range for U and V are determined to be:
 *
 * -.436 <= U <= .437
 * -.615 <= Q <= .615
 *
 */
   *y =  .299*(r/255.0) + .587*(g/255.0) + .114*(b/255.0);
   *u = -.147*(r/255.0) - .289*(g/255.0) + .437*(b/255.0);
   *v =  .615*(r/255.0) - .515*(g/255.0) - .100*(b/255.0);
}

void
yuvtorgb(double y, double u, double v, int *r, int *g, int *b)
{
   *r = 255.0*(1.00*y + .0000*u +1.1398*v);
   *g = 255.0*(1.00*y - .3938*u - .5805*v);
   *b = 255.0*(1.00*y +2.0279*u + .0000*v);
}

int
is_illegal_yiq(double y, double i, double q)
{
   double saturation;
/*
 * By inspection, the range for saturation can be determined to be:
 *
 * Given: -.596 <= I <= .596
 * Given: -.523 <= Q <= .523
 * 0 <= saturation <= .792
 */
   saturation = sqrt(i*i + q*q);
   if (y+saturation > 1.0)
      return 1;
   else if (y-saturation <= -0.251)
      return -1;
   else
      return 0;
}

int
is_illegal_yuv(double y, double u, double v)
{
   double saturation;

   saturation = sqrt(u*u + v*v);
   if (y+saturation >= 1.334)
      return 1;
   else if (y-saturation <= -0.339)
      return -1;
   else
      return 0;
}

int
make_legal_yiq(double y, double i, double q, double *y_new, double *i_new, double *q_new)
{
   double sat_old, sat_new;
   double diff;
/*
 * I and Q are legs of a right triangle.  Saturation is the hypotenuse.
 */
   sat_old = sqrt(i*i + q*q);
   if (y+sat_old > 1.0) {
      diff = 0.5*((y+sat_old) - 1.0);
      *y_new = y - diff;
      sat_new = 1.0 - *y_new;

      /*sat_new = 1.0 - y;*/
      *i_new = i*(sat_new/sat_old);
      *q_new = q*(sat_new/sat_old);
      return 1;
      }
   else if (y-sat_old <=-0.251) {
      diff = 0.5*((sat_old-y) - 0.251);
      *y_new = y + diff;
      sat_new = 0.250 + *y_new;

      /*sat_new = 0.250 + y;*/
      *i_new = i*(sat_new/sat_old);
      *q_new = q*(sat_new/sat_old);
      return -1;
      }
   else {
      *y_new = y;
      *i_new = i;
      *q_new = q;
      return 0;
      }
}


int
make_legal_yuv(double y, double u, double v, double *y_new, double *u_new, double *v_new)
{
   double sat_old, sat_new;
   double diff;
/*
 * U and V are legs of a right triangle.  Saturation is the hypotenuse.
 */
   sat_old = sqrt(u*u + v*v);
   if (y+sat_old >= 1.334) {
      diff = 0.5*((y+sat_old) - 1.334);
      *y_new = y - diff;
      sat_new = 1.333 - *y_new;

      /*sat_new = 1.333 - y;*/
      *u_new = u*(sat_new/sat_old);
      *v_new = v*(sat_new/sat_old);
      return 1;
      }
   else if (y-sat_old <= -0.339) {
      diff = 0.5*((sat_old-y) - 0.339);
      *y_new = y + diff;
      sat_new = 0.338 + *y_new;

      /*sat_new = 0.338 + y;*/
      *u_new = u*(sat_new/sat_old);
      *v_new = v*(sat_new/sat_old);
      return -1;
      }
   else {
      *y_new = y;
      *u_new = u;
      *v_new = v;
      return 0;
      }
}

int
make_legal_yiq_i(int r_in, int g_in, int b_in, int* r_out, int* g_out, int* b_out)
{
   int iret;
   double y, i, q;
   double y_new, i_new, q_new;
/*
 * Convert to YIQ and compute the new saturation.
 */
   rgbtoyiq(r_in, g_in, b_in, &y, &i, &q);
   if ((iret = make_legal_yiq(y, i, q, &y_new, &i_new, &q_new)))
/*
 * Given the new I and Q, compute new RGB values.
 */
      yiqtorgb(y_new, i_new, q_new, r_out, g_out, b_out);
   else {
      *r_out = r_in;
      *g_out = g_in;
      *b_out = b_in;
      }
   return iret;
}

int
make_legal_yuv_i(int r_in, int g_in, int b_in, int* r_out, int* g_out, int* b_out)
{
   int iret;
   double y, u, v;
   double y_new, u_new, v_new;
/*
 * Convert to YUV and compute the new saturation.
 */
   rgbtoyuv(r_in, g_in, b_in, &y, &u, &v);
   if ((iret = make_legal_yuv(y, u, v, &y_new, &u_new, &v_new)))
/*
 * Given the new U and V, compute new RGB values.
 */
      yuvtorgb(y_new, u_new, v_new, r_out, g_out, b_out);
   else {
      *r_out = r_in;
      *g_out = g_in;
      *b_out = b_in;
      }
   return iret;
}

int
make_legal_yiq_b(unsigned char r_in, unsigned char g_in, unsigned char b_in, unsigned char* r_out, unsigned char* g_out, unsigned char* b_out)
{
   int i;
   int ir_in, ig_in, ib_in;
   int ir_out, ig_out, ib_out;

   ir_in = (int)r_in;
   ig_in = (int)g_in;
   ib_in = (int)b_in;
   i = make_legal_yiq_i(ir_in, ig_in, ib_in, &ir_out, &ig_out, &ib_out);
   *r_out = (unsigned char)ir_out;
   *g_out = (unsigned char)ig_out;
   *b_out = (unsigned char)ib_out;
   return i;
}

int
make_legal_yuv_b(unsigned char r_in, unsigned char g_in, unsigned char b_in, unsigned char* r_out, unsigned char* g_out, unsigned char* b_out)
{
   int i;
   int ir_in, ig_in, ib_in;
   int ir_out, ig_out, ib_out;

   ir_in = (int)r_in;
   ig_in = (int)g_in;
   ib_in = (int)b_in;
   i = make_legal_yuv_i(ir_in, ig_in, ib_in, &ir_out, &ig_out, &ib_out);
   *r_out = (unsigned char)ir_out;
   *g_out = (unsigned char)ig_out;
   *b_out = (unsigned char)ib_out;
   return i;
}
