[펌] 명도 또는 채도 조절하는 칼라코드들
외국에는 색체학 관련 소스코드들이 많이 제공되고 있는데 국내에서는 거의 연구를 하지 않더군요
제가 프로그램 만들때 마다 RGB 함수를 이용하여 일일이 명도를 찾아내는 것이 귀잖아서
명도와 채도를 구하는 클래서를 한번 만들어 보았습니다
그런데 실제로 색체학을 한달가량 연구하니 이 동네 괘나 복잡한 표준을 가지고 있더군요
미국,한국,일본,유럽이 다르고, 모니터와 TV, 옷감에서 사용하는 표준도 다르고
아이고 복잡해라...
여하튼 윈도우의 RGB 를 기준으로 명도와 채도를 구하는 클래스를 만들었습니다
약간 허접할지는 몰라도 아주 유용하게 사용할 수 있습니다
특히 버튼이나 그리드 색깔정할 때 아주 유용하게 이용할 수 있습니다
첨부파일에 소스와 실행파일이 있습니다
사실 조금 손을보고 소스를 올려라고하니 다시 색체학을 검토하니 머리가 너무아파서...
그냥 올립니다... 이해 하시길....
주의
색체학 에서 HSV 나 HSI 함수를 구하면 소수점이 나옵니다
그래서 소수점은 아무래도 프로그램 속도를 저하시키기 대문에
정수로 계산하였습니다
포투샵에서도 아마도 소수점을 없애고 명도와 채도를 조절하는 듯 합니다
아래는 칼라코드 소스입니다..각주 적은것 양해 하시길... 혹 틀릴수도..
unit kmcolor_ut;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls,math ;
const
inc_5 : byte = 5 ;
type
// 색상, 채도, 명도
// 원통형 좌표계를 사용하는 경우
THSVColor = record
H : word; // 색상 0 도부터 360 도 까지
S : byte; // 채도 0 부터 255 까지
V : byte; // 명도 0 부터 255 까지
end;
// 색상, 채도, 명도
// 원뿔형 좌표계를 사용하는 경우
THSIColor = record
H : word; // 색상 0 도부터 360 도 까지
S : byte; // 채도 0 부터 255 까지
I : byte; // 명도 0 부터 255 까지
end;
//
Tkmcolor = class
gray_0 : TColor ;
red_0 : TColor ;
green_0 : TColor ;
blue_0 : TColor ;
magenta_0 : TColor ;
cyan_0 : TColor ;
yellow_0 : TColor ;
orange_0 : TColor ;
olive_0 : TColor ;
YellowGreen_0 : TColor ;
CyanGreen_0 : TColor ;
BlueCyan_0 : TColor ;
violet_0 : TColor ;
pink_0 : TColor ;
constructor create;
private
public
// rgb 에서 가장 적은 수
function min_rgb(Const r,g,b:byte): byte;
// rgb 에서 가장 큰 수
function max_rgb(Const r,g,b:byte): byte;
// 색상 , 채도 , 명도 함수
function HSV(Const H,S,V:Integer):THSVColor;
// RGB 에서 색상 구하기 - 원뿔 좌표계에서 -- 라디안으로 구해짐 ;;;
function get_H_inRGB(Const value:TColor):double ;
// RGB 에서 채도 구하기 - 원뿔 좌표계에서 -- >> 0 부터 1 까지;;;
function get_S_inRGB(Const value:TColor):double ;
// RGB 에서 명도 구하기 - 원뿔 좌표계에서 -- >> 0 부터 1 까지;;; ;;;
function get_I_inRGB(Const value:TColor):double ;
// HSI -- > 문자로 변환 0 부터 1 까지 변환
function HSI_toString(Const value:TColor): string ;
// HSI -- > 문자로 변환 0 부터 240 까지 변환
function HSI_toString_240(Const value:TColor): string ;
// 색상 0..360 , 채도 0..255 , 명도 0..255 가지 값을 가짐
// 실수 계산을 줄이기 위하여 사용함
function get_HSIinRGB_255(Const value:TColor) : THSIColor ;
//
function HSI_toString_255(Const value:TColor): string ;
// RGB 에서 명도 구하기 - 원통 좌표계에서
function get_II_inRGB(Const value:TColor): word ;
// RGB 에서 채도 구하기 - 원통 좌표계에서
function get_SS_inRGB(Const value:TColor): byte ;
// RGB 에서 색상 구하기 - 원통 좌표계에서
function get_HH_inRGB(Const value:TColor): word ;
// 명도 구하는 공식 ( 명도 + 10 ) - 밝아짐
// cnum -> 색깔 , inum -> 명도
function plus_light(const cnum , inc_num : byte ) : integer ;
// 명도 구하는 공식 ( 명도 - 10 ) - 어두어짐
// cnum -> 색깔 , inum -> 명도
function minus_light(const cnum , inc_num: byte ) : integer ;
// 명도를 높여주는 함수 - 120 을 기준으로
function plus_lightness(Value: Tcolor; inc_num : byte): Tcolor;
// 명도를 낮여주는 함수
function minus_lightness(Value: Tcolor; inc_num : byte): Tcolor;
// 보색을 구하는 함수
function get_complement_color(Value: Tcolor): Tcolor;
// RGB 칼라를 CMY 로 변환 --->> RGB 와 CMY 서로 보색관계에 있음
function RGB_to_CMY(Value: Tcolor): Tcolor;
// CMY 칼라를 RGB 로 변환 --->> RGB 와 CMY 서로 보색관계에 있음
//function CMY_toRGB(Value: Tcolor): Tcolor;
// RGB 를 colorref 로 변환
function RGB_to_BGR(Value: Tcolor): COLORREF ;
// RGB 를 webcolor 로 변환
function RGB_to_webcolor(Value: Tcolor): String ;
//
function RGB_to_YCbCr(Value: Tcolor): COLORREF;
// rgb 를 스트링으로 변환
function Rgb_toString(Value: Tcolor): string;
// 적색 증가하기
function plus_red(Value: Tcolor; inc_num : byte): Tcolor;
// 녹색 증가하기
function plus_green(Value: Tcolor; inc_num : byte): Tcolor;
// 남색 증가하기
function plus_blue(Value: Tcolor; inc_num : byte): Tcolor;
// 적색 inc_num 만큼 빼기
function minus_red(Value: Tcolor; inc_num : byte): Tcolor;
// 녹색 inc_num 만큼 빼기
function minus_green(Value: Tcolor; inc_num : byte): Tcolor;
// 남색 inc_num 만큼 빼기
function minus_blue(Value: Tcolor; inc_num : byte): Tcolor;
end;
var
kmcolor : Tkmcolor ;
implementation
constructor Tkmcolor.create;
begin
inherited create ;
gray_0 := rgb(128,128,128) ;
red_0 := rgb(255,0,0) ;
green_0 := rgb(0,255,0) ;
blue_0 := rgb(0,0,255) ;
magenta_0 := rgb(255,0,255) ;
cyan_0 := rgb(0,255,255) ;
yellow_0 := rgb(255,255,0) ;
orange_0 := rgb(255,128,0) ;
olive_0 := rgb(255,190,0) ;
YellowGreen_0 := rgb(128,255,0) ;
CyanGreen_0 := rgb(0,255,190) ;
BlueCyan_0 := rgb(0,128,255) ;
violet_0 := rgb(128,0,255) ;
pink_0 := rgb(255,0,190) ;
end;
function Tkmcolor.minus_light(const cnum, inc_num : byte): integer;
var
num_buf : real ;
begin
num_buf := abs(0 - cnum) ;
num_buf := ((num_buf / 120 ) * inc_num) ;
result := cnum - (round(num_buf)) ;
end;
function Tkmcolor.plus_light(const cnum, inc_num: byte): integer;
var
num_buf : real ;
begin
num_buf := 255 - cnum ;
num_buf := (num_buf / 120 ) * inc_num ;
result := round(num_buf) + cnum ;
end;
function Tkmcolor.Rgb_toString(Value: Tcolor): string;
var
r,g,b : byte ;
begin
R := Byte(Value);
G := Byte(Value shr 8);
B := Byte(Value shr 16);
result := 'RGB(' + inttostr(r) + ',' +inttostr(g) + ',' + inttostr(b) + ')' ;
end;
function Tkmcolor.plus_lightness(Value: Tcolor; inc_num: byte): Tcolor;
var
r, g, b ,ir,ig,ib : byte ;
begin
R := Byte(Value);
G := Byte(Value shr 8);
B := Byte(Value shr 16);
// 붉은색의 명도의 증가
if (r < 0) or (r > 255) then
begin
ir := r ;
end
else
begin
ir := kmcolor.plus_light(r, inc_num);
end ;
// 초록색의 명도의 증가
if ( g < 0) or (g > 255) then
begin
ig := g ;
end
else
begin
ig := kmcolor.plus_light(g, inc_num);
end ;
// 파랑색의 명도의 증가
if ( b < 0) or (b > 255) then
begin
ib := b ;
end
else
begin
ib := kmcolor.plus_light(b, inc_num);
end ;
result := rgb(ir,ig,ib) ;
end;
function Tkmcolor.minus_lightness(Value: Tcolor; inc_num: byte): Tcolor;
var
r, g, b ,ir,ig,ib : byte ;
begin
R := Byte(Value);
G := Byte(Value shr 8);
B := Byte(Value shr 16);
// 붉은색의 명도의 감소
if (r < 0) or (r > 255) then
begin
ir := r ;
end
else
begin
ir := kmcolor.minus_light(r, inc_num);
end ;
// 초록색의 명도의 감소
if ( g < 0) or (g > 255) then
begin
ig := g ;
end
else
begin
ig := kmcolor.minus_light(g, inc_num);
end ;
// 파랑색의 명도의 감소
if ( b < 0) or (b > 255) then
begin
ib := b ;
end
else
begin
ib := kmcolor.minus_light(b, inc_num);
end ;
result := rgb(ir,ig,ib) ;
end;
function Tkmcolor.plus_red(Value: Tcolor; inc_num: byte): Tcolor;
var
r, g, b ,ir : byte ;
begin
R := Byte(Value);
G := Byte(Value shr 8);
B := Byte(Value shr 16);
ir := R + inc_num ;
if ir > 255 then
begin
result := (255 or G or B);
end
else
begin
result := (ir or G or B) ;
end;
end;
function Tkmcolor.plus_green(Value: Tcolor; inc_num: byte): Tcolor;
var
r, g, b ,ig : byte ;
begin
R := Byte(Value);
G := Byte(Value shr 8);
B := Byte(Value shr 16);
ig := G + inc_num ;
if ig > 255 then
begin
result := (R or 255 or B);
end
else
begin
result := (R or ig or B) ;
end;
end;
function Tkmcolor.plus_blue(Value: Tcolor; inc_num: byte): Tcolor;
var
r, g, b ,ib : byte ;
begin
R := Byte(Value);
G := Byte(Value shr 8);
B := Byte(Value shr 16);
ib := B + inc_num ;
if ib > 255 then
begin
result := (R or G or 255);
end
else
begin
result := (R or G or ib) ;
end;
end;
function Tkmcolor.minus_blue(Value: Tcolor; inc_num: byte): Tcolor;
var
r, g, b ,db : byte ;
begin
R := Byte(Value);
G := Byte(Value shr 8);
B := Byte(Value shr 16);
db := B - inc_num ;
if db < 0 then
begin
result := (R or G or 0);
end
else
begin
result := (R or G or db) ;
end;
end;
function Tkmcolor.minus_green(Value: Tcolor; inc_num: byte): Tcolor;
var
r, g, b ,dg : byte ;
begin
R := Byte(Value);
G := Byte(Value shr 8);
B := Byte(Value shr 16);
dg := G - inc_num ;
if dg < 0 then
begin
result := (R or 0 or B);
end
else
begin
result := (R or dg or B) ;
end;
end;
function Tkmcolor.minus_red(Value: Tcolor; inc_num: byte): Tcolor;
var
r, g, b ,dr : byte ;
begin
R := Byte(Value);
G := Byte(Value shr 8);
B := Byte(Value shr 16);
dr := R - inc_num ;
if dr < 0 then
begin
result := (0 or G or B);
end
else
begin
result := (dr or G or B) ;
end;
end;
function Tkmcolor.get_complement_color(Value: Tcolor): Tcolor;
var
r,g,b,cr,cg,cb : byte ;
begin
R := Byte(Value);
G := Byte(Value shr 8);
B := Byte(Value shr 16);
// 색과 보색을 합하면 255 가 된다
// cr + r = 255
cr := 255 - r ;
cg := 255 - g ;
cb := 255 - b ;
result := rgb(cr,cg,cb) ;
end;
function Tkmcolor.RGB_to_CMY(Value: Tcolor): Tcolor;
var
r,g,b,c,m,y : byte ;
begin
// RGB 와 CMY 서로 보색관계에 있음
R := Byte(Value);
G := Byte(Value shr 8);
B := Byte(Value shr 16);
// 색과 보색을 합하면 255 가 된다
// cr + r = 255
c := 255 - r ;
m := 255 - g ;
y := 255 - b ;
result := rgb(c,m,y) ;
end;
function Tkmcolor.RGB_to_YCbCr(Value: Tcolor): COLORREF;
var
r,g,b : byte ;
//vv : Tbitmap ;
y,Cb,Cr : double ;
begin
// RGB 와 CMY 서로 보색관계에 있음
R := Byte(Value);
G := Byte(Value shr 8);
B := Byte(Value shr 16);
//vv.PixelFormat
{ Y := 0.299*R + 0.589*G + 0.114*B ;
Cb := 0.564*( B - Y );
Cr := 0.713*( R - Y );
result := rgb(y,cb,cr) ; }
end;
function Tkmcolor.HSV(const H, S, V: Integer): THSVColor;
begin
if H > 239 then
begin
Result.H:= 239
end else begin
Result.H:= H ;
end;
if S > 240 then
begin
Result.S:= 240
end else begin
Result.S:= S;
end;
if H > 240 then
begin
Result.V:= 240
end else begin
Result.V:= V;
end;
end;
function Tkmcolor.get_H_inRGB(const value: TColor): double;
var
child_buf, parent_buf: double;
r,g,b: byte;
dr,dg,db, hue , re_buf : double ;
begin
R := Byte(Value);
G := Byte(Value shr 8);
B := Byte(Value shr 16);
dr := r/255 ;
dg := g/255 ;
db := b/255 ;
{ 색상각도공식 = 아크코사인(0.5*((r-g) + (r-b) )) 또는 -->> rgb 값이 0부터 1인경우
색상각도공식 = 255 - (3*255)/(R + G + B) * min(r,g,b) -->> rgb 값이 0부터 255인경우
색상각도 국제표준은 0 부터 360 사이의 숫자이나
윈도 팔레트에서는 명도를 0 부터 239 가지만 존재
0.666 = 240/360 }
child_buf := 0.5 * ((dR - dG) + (dR - dB) ) ;
parent_buf := sqrt(sqr(dR - dG) + ((dR - dB) * (dG - dB))) ;
// 흑뱃색은 색상이 없는 것임
if (r=g) and (r=b) then
begin
re_buf := 7 ;
end
else
begin
hue := arccos(child_buf/parent_buf) ;
end;
//
if (b <= g) then
begin
re_buf := hue;
end
else
begin
re_buf := 2 * PI - hue ;
end;
result := roundto(re_buf,-3) ;
end;
function Tkmcolor.get_I_inRGB(const value: TColor): double ;
var
r,g,b : byte;
dr,dg,db, re_buf : double ;
begin
R := Byte(Value);
G := Byte(Value shr 8);
B := Byte(Value shr 16);
// 소수점 3 자리 이하 제거
dr := r/255 ;
dg := g/255 ;
db := b/255 ;
//명도 공식 = (R + G + B) / 3 또는 -->> rgb 값이 0부터 1인경우
if (dr + dg + db) = 0 then
begin
re_buf := 0 ;
end
else
begin
re_buf := (dr + dg + db)/3 ;
end;
result := roundto(re_buf,-3) ;
end;
function Tkmcolor.get_S_inRGB(const value: TColor): double;
var
r,g,b : byte;
dr,dg,db, re_buf, sum_buf, min_buf, max_buf, delta : double ;
begin
R := Byte(Value);
G := Byte(Value shr 8);
B := Byte(Value shr 16);
dr := r/255 ;
dg := g/255 ;
db := b/255 ;
sum_buf := dR + dG + dB ;
Min_buf := minvalue([dr,dg,db]) ;
//Max_buf := maxvalue([dr,dg,db]) ;
//delta := Max_buf - Min_buf ;
//채도 공식 = 1 - 3/(R + G + B) * min(r,g,b) 또는 -->> rgb 값이 0부터 1인경우
// 채도는 흑백인 경우 는 항상 0 이다
if delta = 0 then
begin
result := 0 ;
exit ;
end ;
if sum_buf = 0 then
begin
re_buf := 1 ;
end
else
begin
re_buf := 1- (3/sum_buf) * Min_buf ;
end;
result := roundto(re_buf,-3) ;
end;
function Tkmcolor.HSI_toString(const value: TColor): string;
var
H,S,V : double ;
begin
h := kmcolor.get_H_inRGB(value) ;
v := kmcolor.get_I_inRGB(value) ;
s := kmcolor.get_S_inRGB(value) ;
result := floattostr(h) + ',' + floattostr(s) + ',' + floattostr(v) ;
end;
function Tkmcolor.HSI_toString_240(const value: TColor): string;
var
H,S,I : double ;
bh,bs,bi : word ;
begin
h := kmcolor.get_H_inRGB(value) ;
s := kmcolor.get_S_inRGB(value) ;
I := kmcolor.get_I_inRGB(value) ;
// 라디안의 값을 도의 값으로 변환
bh := round((h * 180.0) / PI) ;
// 0..1 사이 값을 0..240 으로 변환
bs := round(240 * s) ;
bi := round(240 * i) ;
//result := ',' + inttostr(bs) + ',' + inttostr(bi) ;
result := inttostr(bh) + ',' + inttostr(bs) + ',' + inttostr(bi) ;
end;
function Tkmcolor.min_rgb(const r, g, b: byte): byte;
begin
if (r <= g) and (r <= b) then
begin
result := r ;
end
else
begin
if (g <= b) then
begin
result := g ;
end
else
begin
result := b ;
end;
end;
end;
function Tkmcolor.max_rgb(const r, g, b: byte): byte;
begin
if (r >= g) and (r >= b) then
begin
result := r ;
end
else
begin
if (g >= b) then
begin
result := g ;
end
else
begin
result := b ;
end;
end;
end;
function Tkmcolor.get_HSIinRGB_255(const value: TColor): THSIColor;
var
r,g,b : byte;
sum_buf, Min_buf, re_buf : word ;
child_buf, parent_buf,hue : double;
begin
R := Byte(Value);
G := Byte(Value shr 8);
B := Byte(Value shr 16);
sum_buf := r + g + b ;
Min_buf := kmcolor.min_rgb(r,g,b) ;
// 명도
result.I := round(sum_buf/3) ;
// 채도
if sum_buf = 0 then
begin
result.S := 0 ;
end
else
begin
result.S := 255 - round((765/sum_buf) * Min_buf) ;
end;
// 색상 --> 흑백색
if (r=g) and (r=b) then
begin
re_buf := 444 ;
end
else
begin
hue := kmcolor.get_H_inRGB(value) ;
// 라디안을 도로 변환
re_buf := round((hue*180)/pi) ;
end;
// 라디안의 값을 도의 값으로 변환
result.H := re_buf ;
end;
function Tkmcolor.HSI_toString_255(const value: TColor): string;
var
aHSI : THSIColor ;
begin
aHSI := kmcolor.get_HSIinRGB_255(value) ;
result := inttostr(aHSI.H) + ',' + inttostr(aHSI.S) + ',' + inttostr(aHSI.I) ;
end;
function Tkmcolor.get_II_inRGB(const value: TColor): word ;
var
r,g,b : byte;
begin
R := Byte(Value);
G := Byte(Value shr 8);
B := Byte(Value shr 16);
result := kmcolor.max_rgb(r,g,b) ;
end;
function Tkmcolor.get_SS_inRGB(const value: TColor): byte;
var
r,g,b , Delta , max_buf , min_buf : byte;
begin
R := Byte(Value);
G := Byte(Value shr 8);
B := Byte(Value shr 16);
max_buf := kmcolor.max_rgb(r,g,b) ;
min_buf := kmcolor.min_rgb(r,g,b) ;
Delta := max_buf - min_buf ;
if max_buf = 0 then
begin
result := 0 ;
end
else
begin
result := round(Delta / max_buf) ;
end;
end;
function Tkmcolor.get_HH_inRGB(const value: TColor): word;
var
r,g,b , Delta , max_buf , min_buf : byte;
begin
R := Byte(Value);
G := Byte(Value shr 8);
B := Byte(Value shr 16);
max_buf := kmcolor.max_rgb(r,g,b) ;
min_buf := kmcolor.min_rgb(r,g,b) ;
Delta := max_buf - min_buf ;
// 채도가 0 이면 -->> 흑백색이면
if (r = g) and (r = b) then
begin
result := 444 ; // 정의 되지 않는 값
end
else
begin
if R = max_buf then // between yellow and magenta [degrees]
begin
result := round(60.0 * (G - B) / Delta);
end
else if G = max_buf THEN // between cyan and yellow
begin
result := round(120.0 + 60.0 * (B - R) / Delta);
end
else if B = max_buf THEN // between magenta and cyan
begin
result := round(240.0 + 60.0 * (R - G) / Delta) ;
end;
end;
end;
function Tkmcolor.RGB_to_BGR(Value: Tcolor): COLORREF;
var
r,g,b : byte;
begin
R := Byte(Value);
G := Byte(Value shr 8);
B := Byte(Value shr 16);
Result := $00 + (Integer(B) shl 16) + (Integer(G) shl 8) + R;
end;
function Tkmcolor.RGB_to_webcolor(Value: Tcolor): String;
var
r,g,b : byte;
begin
R := Byte(Value);
G := Byte(Value shr 8);
B := Byte(Value shr 16);
Result := '#' + inttohex(r,2) + inttohex(g,2) + inttohex(b,2);
end;
end.
///////////// 다른쪽 소스/////////////
PROCEDURE RGBToHSV (CONST R,G,B: TReal; VAR H,S,V: TReal);
VAR
Delta: TReal;
Min : TReal;
BEGIN
Min := MinValue( [R, G, B] );
V := MaxValue( [R, G, B] );
Delta := V - Min;
// Calculate saturation: saturation is 0 if r, g and b are all 0
IF V = 0.0
THEN S := 0
ELSE S := Delta / V;
IF S = 0.0
THEN H := NaN // Achromatic: When s = 0, h is undefined
ELSE BEGIN // Chromatic
IF R = V
THEN // between yellow and magenta [degrees]
H := 60.0 * (G - B) / Delta
ELSE
IF G = V
THEN // between cyan and yellow
H := 120.0 + 60.0 * (B - R) / Delta
ELSE
IF B = V
THEN // between magenta and cyan
H := 240.0 + 60.0 * (R - G) / Delta;
IF H < 0.0
THEN H := H + 360.0
END
END {RGBtoHSV};
출처 : 델마당 수련중님 글