/* * CMWrgbhsv.c -- * * Procedures to translate between RGB color space and HSV color space. * Courtesy of Ken Fishkin. * * ********************************************************************* * * Copyright (C) 1985, 1990 Regents of the University of California. * * * Permission to use, copy, modify, and distribute this * * * software and its documentation for any purpose and without * * * fee is hereby granted, provided that the above copyright * * * notice appear in all copies. The University of California * * * makes no representations about the suitability of this * * * software for any purpose. It is provided "as is" without * * * express or implied warranty. Export of this software outside * * * of the United States of America may require an export license. * * ********************************************************************* */ #ifndef lint static const char rcsid[] __attribute__ ((unused)) = "$Header: /usr/cvsroot/magic-8.0/cmwind/CMWrgbhsv.c,v 1.1.1.1 2008/02/03 20:43:50 tim Exp $"; #endif /* not lint */ #include #include #include "utils/magic.h" /* max of three numbers */ #define MAX3(a,b,c) (a > b ? (a > c ? a : c) : ( b > c ?b : c)) /* and min */ #define MIN3(a,b,c) (a < b ? (a < c ? a : c) : ( b < c ?b : c)) /* |-----------------------------------------------------------------------| | procedure : RGBxHSV | | description : given r,g,b, each on [0,1], return hue, sat- | | : uration, and value, each on [0,1]. | | : returns true <==> hue is defined. | | source : Smith, Alvy Ray. "Color Tutorial Notes". | | : Technical Memo 37, LucasFilm. | | : hue calc changed a tad, according to Foley | | : and Van Dam. | |-----------------------------------------------------------------------| */ bool RGBxHSV( double r, double g, double b, double *h, double *s, double *v) { double max, delta; double mr,mg,mb; *v = max = MAX3(r,g,b); if ( (max == 0) || ( (delta = max - MIN3(r,g,b) ) == 0) ) { /* achromatic */ *s = 0; *h = 0; return(FALSE); }; *s = delta/ max; mr = (max - r)/delta; mg = (max - g)/delta; mb = (max - b)/delta; if (r == max) *h = mb - mg; else if ( g == max) *h = 2.0 + mr - mb; else if ( b == max) *h = 4.0 + mg - mr; *h /= 6; if (*h < 0.0) *h += 1.0; return(TRUE); } /* |-----------------------------------------------------------------------| | procedure : HSVxRGB | | description : given HSV on [0,1], returns equivalent | | : RGB on [0,1]. | | source : Smith, Alvy Ray. "Color Tutorial Notes". | | : Technical Memo 37, LucasFilm. | |-----------------------------------------------------------------------| */ #define SETRGB(rr,gg,bb) *r=rr;*g=gg;*b=bb void HSVxRGB( double h, double s, double v, double *r, double *g, double *b) { double f,m,n,k; int i; double vs,vsf; h *= 6; i = h; /* integer part of hue */ f = h - i; /* fractional part */ vs = v*s; vsf = vs*f; m = v - vs; n = v - vsf; k = v - vs + vsf; switch(i % 6) { case 0 : SETRGB( v,k,m); break; case 1 : SETRGB( n,v,m); break; case 2 : SETRGB( m,v,k); break; case 3 : SETRGB( m,n,v); break; case 4 : SETRGB( k,m,v); break; case 5 : SETRGB( v,m,n); break; } } /* |-----------------------------------------------------------------------| | procedure : RGBxHSL | | description : converts from RGB to HSL coordinates. | | source : '79 graphics core. | |-----------------------------------------------------------------------| */ bool RGBxHSL( double r, double g, double b, double *h, double *s, double *l) { double min, max; double delta, mr, mg, mb; min = MIN3( r,g,b); max = MAX3( r,g,b); /* Lightness calculation */ /* source - 79 graphics core */ *l = 0.5 * (max + min); if ( (delta = max - min) == 0) { /* achromatic */ *s = 0.0; *h = 0.0; return FALSE; } /* Saturation calculation */ /* HSL is a double cone */ if (*l < 0.5) *s = (delta)/(max+min); else *s = (delta)/(2.0-(max+min)); /* hue calculation */ mr = (max - r)/delta; mg = (max - g)/delta; mb = (max - b)/delta; if (r == max) *h = mb - mg; else if ( g == max) *h = 2.0 + mr - mb; else if ( b == max) *h = 4.0 + mg - mr; *h /= 6; if (*h < 0.0) *h += 1.0; return(TRUE); } /* |-----------------------------------------------------------------------| | procedure : HSLxRGB | | description : converts from HSL, each on [0,1], to RGB, | | : each on [0,1]. | | note : the idea is to use s,l to get HSV s,v, and | | : then use HSVxRGB - this is faster than the | | : '79 core algorithm. | |-----------------------------------------------------------------------| */ void HSLxRGB( double h, double s, double l, double *r, double *g, double *b) { double min; double v; double f,n,k; int i; double vsf; if ( l <= 0.5) v = l * (1.0 + s); else v = l + s - l*s; min = 2*l - v; if ( (s == 0.0) || ( l == 0.0) || ( l == 1.0 ) ) { SETRGB(l,l,l); return; } /* now just use hsvxrgb */ s = ( v - min) / v; h *= 6; i = h; /* integer part of hue */ f = h - i; /* fractional part */ vsf = v*s*f; n = v - vsf; k = min + vsf; switch(i % 6) { case 0 : SETRGB( v,k,min); break; case 1 : SETRGB( n,v,min); break; case 2 : SETRGB( min,v,k); break; case 3 : SETRGB( min,n,v); break; case 4 : SETRGB( k,min,v); break; case 5 : SETRGB( v,min,n); break; } } /* |-----------------------------------------------------------------------| | procedure : Correct_chromaticity | | description : given the white point at location (wx,wy), | | : and an existing point (x,y), such that | | : x + y > 1, intersect a line through the | | : white point and the exisiting point with the | | : line x+y = 1. side-affects new point. | |-----------------------------------------------------------------------| */ void Correct_chromaticity( double *x, double *y, double wx, double wy) { double oldx,oldy; double slope; double b; oldx = *x; oldy = *y; slope = ( oldy - wy ) / ( oldx - wx); /* find b by eval'ing at white point */ b = wy - slope*wx; *x = ( 1.0 - b ) / ( 1.0 + slope); *y = 1.0 - *x; } /* |-----------------------------------------------------------------------| | procedure : xyz_to_mrgb | | description : converts xyz to monitor rgb. | | : only really correct for mitsubishi mon. | |-----------------------------------------------------------------------| */ void xyz_to_mrgb( double x, double y, double z, double *mr, double *mg, double *mb) { *mr = 2.4513*x - 1.2249*y - 0.3237*z; *mg = -1.4746*x + 2.5052*y + 0.0596*z; *mb = 0.0212*x - 0.2550*y + 1.1487*z; } /* |-----------------------------------------------------------------------| | procedure : Make_mRGB_Nice | | description : make mRGB values lie in 0..1, correcting for | | : luminance overflow, chrominance irreproduc- | | : ibility. | | assumptions : at most one of mR,mG,mB < 0 | |-----------------------------------------------------------------------| */ void Make_mRGB_Nice( double *mR, double *mG, double *mB) { double min,max; double mr, mg, mb; double mY; double white_mr,white_mg,white_mb; max = *mR; if ( max < *mG) max = *mG; if ( max < *mB) max = *mB; if ( max > 1.0) { /* luminance overflow, maintain chromaticity */ *mR /= max; *mG /= max; *mB /= max; } min = *mR; if ( min > *mG) min = *mG; if ( min > *mB) min = *mB; if ( min >= 0.0 ) return; /* correct in mrgb space */ mY = *mR + *mG + *mB; mr = *mR/mY; mg = *mG / mY; mb = *mB / mY; /* correct based on which component is < 0 */ /* find monitor white point */ xyz_to_mrgb(1.0/3.0,1.0/3.0,1.0/3.0,&white_mr,&white_mg,&white_mb); if ( mr < 0 ) { mr = 0.0; Correct_chromaticity( &mg, &mb, white_mg,white_mb); } else if ( mg < 0 ) { mg = 0.0; Correct_chromaticity( &mr, &mb, white_mr,white_mb); } else if ( mb < 0 ) { mb = 0.0; Correct_chromaticity( &mr, &mg, white_mr,white_mg); } *mR = mr*mY; *mG = mg*mY; *mB = mb*mY; }