001package org.vishia.gral.base; 002 003import org.vishia.math.CurveInterpolation; 004import org.vishia.util.Debugutil; 005 006public class GralColorConv 007{ 008 009 010 /**Sensivity of light in respect to the color. It is a table-given function used with interpolation. 011 */ 012 static float[][] clight = 013 { 014 { 0, 1 } //magenta 015 , { 4, 1 } //red 016 , { 8, 1.4f } //yellow 017 , {12, 1.3f } //green 018 , {16, 1.4f } //cyan 019 , {17, 1.1f } //blue 020 , {20, 1 } //blue 021 , {24, 1 } //magenta 022 }; 023 024 025 static float[][] clightSat = 026 { 027 {0, 0 } 028 , { 0.2f, 0.05f } 029 , { 0.3f, 0.1f } 030 , { 0.5f, 0.9f } 031 , { 1, 1 } 032 }; 033 034 /**Light value in depending of hex*/ 035 static float[][] clightVal = 036 { 037 { 0, 0 } 038 , { 0x20, 0.3f } 039 , { 0x40, 0.5f } 040 , { 0x60, 0.65f } 041 , { 0x80, 0.75f } 042 , { 0xa0, 1.0f } 043 , { 0xc0, 1.2f } 044 , { 0xe0, 1.5f } 045 , { 0xff, 2.0f } 046 }; 047 048 049 static float[][] loTable = 050 { { 0, 0, 4, 5, 7, 8, 12, 16, 18, 19, 20, 21, 24 } 051 , {0.0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } 052 , {0.2f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } 053 , {0.5f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } 054 , {0.8f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00 } 055 , {1.0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x60, 0x40, 0x00 } 056 , {1.3f, 0x40, 0x50, 0x20, 0x00, 0x00, 0x00, 0x00, 0x30, 0x40, 0x70, 0x70, 0x50 } 057 , {1.5f, 0x60, 0x80, 0x40, 0x00, 0x00, 0x20, 0x00, 0x60, 0x70, 0x80, 0x70, 0x60 } 058 , {2.0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff } 059 }; 060 061 062 static float[][] grayTable = 063 { {0.0f, 0x00 } 064 , {0.2f, 0x20 } 065 , {0.5f, 0x50 } 066 , {1.0f, 0x8a } 067 , {1.3f, 0xb0 } 068 , {1.5f, 0xc0 } 069 , {2.0f, 0xff } 070 }; 071 072 073 static float[][] hiTable = 074 { { 0, 0, 4, 5, 7, 8, 12, 16, 18, 19, 20, 21, 24 } 075 , {0.0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } 076 , {0.2f, 0x40, 0x50, 0x40, 0x38, 0x38, 0x40, 0x30, 0x50, 0x50, 0x70, 0x50, 0x50 } 077 , {0.5f, 0x98, 0xa6, 0x98, 0x70, 0x68, 0x70, 0x68, 0x80, 0xc0, 0xe0, 0xc0, 0xa6 } 078 , {0.8f, 0xc0, 0xe0, 0xc0, 0xa0, 0x90, 0x9c, 0x90, 0xb0, 0xff, 0xff, 0xc0, 0xa6 } 079 , {1.0f, 0xe0, 0xff, 0xe8, 0xc0, 0xb0, 0xc4, 0xb0, 0xd0, 0xff, 0xff, 0xff, 0xff } 080 , {1.3f, 0xff, 0xff, 0xff, 0xf0, 0xe0, 0xff, 0xd0, 0xf0, 0xff, 0xff, 0xff, 0xff } 081 , {1.5f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff } 082 , {2.0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff } 083 }; 084 085 086 087 public static int HLStoRGB(float hue, float light, float sat){ //// 088 float lo = CurveInterpolation.linearInterpolation(light, hue, loTable, -1); 089 float gr = CurveInterpolation.linearInterpolation(light, grayTable, -1); 090 float hi = CurveInterpolation.linearInterpolation(light, hue, hiTable, -1); 091 //either lo should be 0 or hi should be 0xff, it is full saturation: 092 if(hi < 0xff && lo !=0){ 093 hi += lo/4; lo = 0; 094 } 095 if(lo > 0 && hi !=0xff){ 096 lo -= (0xff - hi); hi = 0xff; 097 } 098 if(lo < 0){ 099 hi -= lo; lo = 0; 100 } 101 if(hi > 0xff){ 102 lo += hi - 0xff; hi = 0xff; 103 } 104 //calculate saturation: 105 float sat1 = sat; 106 if(lo > 0){ //light color 107 lo = gr - sat1 * (gr - lo); //between gr (sat = 0) and lo (sat = 1) 108 hi = gr + sat1 * (hi - gr); 109 } else { //dark color 110 lo = gr - sat1 * (gr - lo); //between gr (sat = 0) and lo (sat = 1) 111 hi = gr + sat1 * (hi - gr); 112 } 113 114 float diff = hi - lo; 115 int rd, gn, bl; 116 float fc; 117 switch((int)(hue / 4)){ 118 case 0: fc = (4 -hue)/4; gn = (int)lo; bl = (int)(lo + diff * fc + 0.5f); rd = (int)(hi); break; //0..4, rt, --bl 119 case 1: fc = (hue- 4)/4; bl = (int)lo; gn = (int)(lo + diff * fc + 0.5f); rd = (int)(hi); break; //4..8 rt, ++gn 120 case 2: fc = (12-hue)/4; bl = (int)lo; rd = (int)(lo + diff * fc + 0.5f); gn = (int)(hi); break; //8..12 gn, --rt 121 case 3: fc = (hue-12)/4; rd = (int)lo; bl = (int)(lo + diff * fc + 0.5f); gn = (int)(hi); break; //12..16 gn, ++bl 122 case 4: fc = (20 -hue)/4; rd = (int)lo; gn = (int)(lo + diff * fc + 0.5f); bl = (int)(hi); break; //16..20 bl, --gn 123 case 5: fc = (hue-20)/4; gn = (int)lo; rd = (int)(lo + diff * fc + 0.5f); bl = (int)(hi); break; //20..24 bl, ++rt 124 default: rd = 0x80; gn = 0; bl = 0x80; 125 } 126 return ((rd << 16) & 0xff0000) + ((gn << 8) & 0x00ff00) + (bl & 0x0000ff); 127 } 128 129 /**Converts a color given by hue, light and sat to rgb. 130 * @param hue The color in range 0..24.0. 0 is magenta, 4: red, 8 yellow, 12 green 16 cyan, 20 blue, 24 magenta. 131 * @param light The light, 0 to 2.0. 1.0 is the saturated red 0xff0000 or a darker yellow. 1.4 is yellow 0xffff00. 132 * till 2.0 it uses the base values for color in white direction. 2.0 it is white. 133 * @param sat Saturation 0..1. part of gray. 1.0 is non-gray. 0 is full gray. 134 * @return rgb 135 */ 136 public static int HLStoRGB3(float hue, float light, float sat){ //// 137 138 if(hue == 9 && sat == 1 && light == 1.95f) 139 Debugutil.stop(); 140 float hue1 = (hue-4) / 24; 141 if(hue1 < 0 ){ hue1 += 1.0f; } 142 float nlight1 = CurveInterpolation.linearInterpolation(hue, clight, -1); 143 float nlightsat = CurveInterpolation.splineInterpolation(sat, clightSat, -1); 144 float nlight; 145 if(light <= 1){ 146 nlight = 1 + (nlight1 - 1) * nlightsat; 147 } else { 148 nlight = nlight1; 149 } 150 float b1 = light / nlight; 151 //float sat1 = sat + (1-sat) * (nlight - 1 )/2; //yellow, saturation is greater 152 float sat1 = sat + (1-sat) * sat * (nlight1 - 1 ); //yellow, saturation is greater 153 float satLight = 1.0f; 154 if(b1 > 1){ 155 //if the brightness of HSB system is >1 yet, decrease the saturation to force that brigthness 156 //float satCorr = sat1 * (light - nlight) / (2 - nlight); 157 //sat1 = sat1 - satCorr; 158 float satCorr = (light - nlight) / (2 - nlight); 159 satLight = 1.0f - satCorr; 160 b1 = 1; 161 } 162 int rgb = java.awt.Color.HSBtoRGB(hue1, satLight, b1); 163 float[] rgb1 = new float[3]; 164 float rd = (rgb>>16) & 0xff; 165 float gn = (rgb>>8) & 0xff; 166 float bl = rgb & 0xff; 167 float hi = rd, lo = rd; 168 if(gn > hi){ hi = gn; } 169 if(bl > hi){ hi = bl; } 170 if(gn < lo){ lo = gn; } 171 if(bl < lo){ lo = bl; } 172 if(lo > 0){ //light color 173 float lightValue = (5 * hi + lo)/6; 174 lo = lightValue - sat1 * (lightValue - lo); //between lightValue (sat = 0) and lo (sat = 1) 175 rd = lightValue + sat1*(rd - lightValue); 176 gn = lightValue + sat1*(gn - lightValue); 177 bl = lightValue + sat1*(bl - lightValue); 178 } else { //dark color 179 //lo = 0, given sat < 1 increased lo 180 rd = hi - sat1 * (hi - rd); 181 gn = hi - sat1 * (hi - gn); 182 bl = hi - sat1 * (hi - bl); 183 } 184 rgb = (((int)(rd + 0.5f)) << 16) + (((int)(gn + 0.5f)) << 8) +((int)(bl + 0.5f)); 185 return rgb; 186 } 187 188 189 public static void HSBtoHLS(float hue, float sat, float brightness, float[] hls) { 190 hls[0] = hue; 191 float nlight1 = CurveInterpolation.linearInterpolation(hue, clight, -1); 192 float nlightsat = CurveInterpolation.splineInterpolation(sat, clightSat, -1); 193 float nlight; 194 if(brightness <= 1/nlight1) { 195 nlight = 1 + (nlight1 - 1) * nlightsat; 196 } else { 197 nlight = nlight1; 198 } 199 float light = brightness * nlight; 200 float s1 = brightness * (1-sat); //part of saturation which does not increase the light. 201 float s2 = (sat + s1); //increast the satuaration by the part which is add to the light. 1.0 if all is add to light. 202 hls[1] = light + (2 - light) * s1; 203 //float s2 = (1 - brightness) * sat; 204 //hls[2] = 1- ((1 - sat) - s1); //same: 205 hls[2] = s2; 206 } 207 208 209 210 public static void RGBtoHLS(int rgb, float[] hls) { 211 float[] hsb = new float[3]; 212 java.awt.Color.RGBtoHSB((rgb>>16)&0xff, (rgb>>8)&0xff, (rgb)&0xff, hsb); 213 float hue = 24*hsb[0] +4; 214 if(hue >=24){ hue -=24; } 215 HSBtoHLS(hue, hsb[1], hsb[2], hls); 216 float light = ligthFromRGB(rgb); 217 hls[1] = light; 218 float sat = satFromRGB(rgb); 219 hls[2] = sat; 220 } 221 222 223 224 static void XXXRGBtoHSL(int rgb, float[] hsl){ 225 if(rgb == 0xF273FF) 226 Debugutil.stop(); 227 float rd = (rgb>>16) & 0xff; 228 float gn = (rgb>>8) & 0xff; 229 float bl = rgb & 0xff; 230 231 232 float lval = Math.min(rd, Math.min(gn, bl)); 233 float hval = Math.max(rd, Math.max(gn, bl)); 234 float mid; 235 hsl[0] = RGBtoHue(rgb); 236 if(hval == 255) { 237 hsl[1] = 1.0f; //white or paster color is not gray. 238 } else if(hval == lval) { 239 hsl[1] = 0; //gray 240 } else { 241 hsl[1] = (float)(hval - lval) / hval; 242 } 243 hsl[2] = RGBtoligth(rd, gn, bl, hsl[1]); 244 } 245 246 247 248 public static float satFromRGB(int rgb){ 249 int rd = (rgb>>16) & 0xff; 250 int gn = (rgb>>8) & 0xff; 251 int bl = rgb & 0xff; 252 float ret; 253 int lighti; 254 int hi, mi, lo; 255 if(rd >= gn && rd >= bl){ 256 hi = rd; 257 if(gn > bl){ mi = gn; lo = bl; } 258 else { mi = bl; lo = gn; } 259 } else if( gn >= rd && gn >= bl){ 260 hi = gn; 261 if(rd > bl){ mi = rd; lo = bl; } 262 else { mi = bl; lo = rd; } 263 } else { 264 hi = bl; 265 if(rd > gn){ mi = rd; lo = gn; } 266 else { mi = gn; lo = rd; } 267 } 268 int add = 255 - hi; 269 if(add > lo){ add = lo; } 270 ret = hi > lo ? ((float)(hi - mi))/(hi - lo) : 0; 271 //// 272 if(lo == 255 || hi == 0) { 273 ret = 1.0f; 274 } 275 else if(255-hi < lo){ 276 ret = (float)(hi - lo) / (255 - lo); //colorvalue 0xff contained: sat = 1.0 277 } else { // if(hi > 0){ 278 ret = (float)(hi-lo) / hi; //same value like HSB-satuartion. 279 } 280 return ret; 281 } 282 283 284 285 public static float ligthFromRGB(int rgb){ 286 int rd = (rgb>>16) & 0xff; 287 int gn = (rgb>>8) & 0xff; 288 int bl = rgb & 0xff; 289 float ret; 290 int lighti; 291 int hi, mi, lo; 292 if(rd >= gn && rd >= bl){ 293 hi = rd; 294 if(gn > bl){ mi = gn; lo = bl; } 295 else { mi = bl; lo = gn; } 296 } else if( gn >= rd && gn >= bl){ 297 hi = gn; 298 if(rd > bl){ mi = rd; lo = bl; } 299 else { mi = bl; lo = rd; } 300 } else { 301 hi = bl; 302 if(rd > gn){ mi = rd; lo = gn; } 303 else { mi = gn; lo = rd; } 304 } 305 float ff = hi == 0 ? 1: ((float)hi) / (hi + 0.4f*mi + 0.2f*lo ); 306 //// 307 float rd1 = CurveInterpolation.linearInterpolation(rd, clightVal, -1); 308 float gn1 = CurveInterpolation.linearInterpolation(gn, clightVal, -1); 309 float bl1 = CurveInterpolation.linearInterpolation(bl, clightVal, -1); 310 //return (0.55f * rd + gn + 0.26f * bl) /255 *ff; 311 return (rd + (1 + 0.4f * (gn-0.6f*rd)/hi) * gn + (1 - 0.4f * (bl-0.5f*gn)/hi) * bl) /255 *ff; 312 } 313 314 315 316 317 static float bfrd = 1.0f; 318 static float bfgn = 1.1f; 319 static float bfbl = 0.9f; 320 321 static float cfrd = 1/0.7f; //1.8f; 322 //static float cfye = 1.0f; 323 static float cfgn = 1/0.8f; //1.5f; 324 //static float cfcy = 1.0f; 325 static float cfbl = 1/0.6f; //2.2f; 326 //static float cfma = 1.3f; 327 328 /**Factors for rd, ye, gn, cy, bl, ma */ 329 static float[] cfrgb = { 1.0f, 1.2f, 0.8f}; //1/0.6f, 1/0.8f, 1/0.5f}; 330 //static float[] cfrgb = { 1/0.6f, 1.0f, 1/0.8f, 1.0f, 1/0.5f, 1/0.8f}; 331 332 333 static float RGBtoHue(int rgb) { 334 float hue; 335 int rd = (rgb>>16) & 0xff; 336 int gn = (rgb>>8) & 0xff; 337 int bl = rgb & 0xff; 338 339 340 int lval = Math.min(rd, Math.min(gn, bl)); 341 int hval = Math.max(rd, Math.max(gn, bl)); 342 int mid; 343 if(hval == lval) { //gray 344 hue = 0; 345 mid = lval; 346 } 347 else if(hval == rd) { 348 if(lval == gn) { //bl is more significant 349 hue = 4 - ((float)(bl-lval) / (rd - lval) * 4 *cfbl / cfrd); 350 mid = bl; 351 } else { 352 hue = 4 + ((float)(gn-lval) / (rd - lval) * 4 *cfgn / cfrd); 353 mid = gn; 354 } 355 } else if(hval == gn) { 356 if(lval == bl) { //rd is more significant 357 hue = 12 - ((float)(rd-lval) / (gn - lval) * 4 * cfrd/ cfgn); 358 mid = rd; 359 } else { //bl is more significant 360 hue = 12 + ((float)(bl-lval) / (gn - lval) * 4 * cfbl / cfgn); 361 mid = bl; 362 } 363 } else { //bl 364 if(lval == rd) { //gn is more significant 365 hue = 20 - ((float)(gn-lval) / (bl - lval) * 4 * cfgn / cfbl); 366 mid = gn; 367 } else { 368 hue = 20 + ((float)(rd-lval) / (bl - lval) * 4 * cfrd / cfbl); 369 mid = rd; 370 } 371 } 372 return hue; 373 } 374 375 376 static float RGBtoligth(float rd, float gn, float bl, float sat){ 377 //++++++++++++++ 378 //++++++++++++++++ //// 379 //+++ 380 float rd1 = bfrd * rd; 381 float gn1 = bfgn * gn; 382 float bl1 = bfbl * bl; 383 //float rd1 = rd; 384 //float gn1 = gn; 385 //float bl1 = bl; 386 387 float lval = Math.min(rd1, Math.min(gn1, bl1)); 388 float hval = Math.max(rd1, Math.max(gn1, bl1)); 389 float mid; 390 if(hval == rd1 && lval == bl1) mid = gn1; 391 else if(hval == rd1 && lval == gn1) mid = bl1; 392 else if(hval == gn1 && lval == bl1) mid = rd1; 393 else if(hval == gn1 && lval == rd1) mid = bl1; 394 else if(hval == bl1 && lval == rd1) mid = gn1; 395 else mid = rd1; 396 397 //float f = hval == 0 ? 1 : (0.2f * lval/hval + 0.2f * mid/hval + 1) / 1.4f; 398 float fmid = 0.3f; //hval == 0 ? 1 : 1.0f - 0.8f * mid/hval; 399 float fl = 0.1f; //hval == 0 ? 1 : 1.0f - 0.8f * lval/hval; 400 //return (0.7f/255 * rd + 0.8f/255 * gn + 0.6f/255 * bl); // * (0.5f + 0.5f * sat); 401 //return (float)Math.sqrt(0.34f/255 * rd + 0.40f/255 * gn + 0.27f/255 * bl); 402 //float light1 = 0.33f/255 * rd + 0.45f/255 * gn + 0.22f/255 * bl; 403 float light1 = (hval + fmid * mid + fl * lval) /255; 404 return light1 / 1.46f; // *f; 405 } 406 407 408 409 410 411 412 413 public static String htmlhls(float h, int l, int s) { 414 int color = HLStoRGB(h, l/100.0f, s/100.0f) & 0xffffff; 415 String ret = String.format("#%06X", color); 416 return ret; 417 } 418 419 public static String htmlhlb(float h, int l, int s) { 420 float hue1 = (h-4) / 24; 421 if(hue1 < 0 ){ hue1 += 1.0f; } 422 int color = java.awt.Color.HSBtoRGB(hue1, s/100.0f, l/100.0f) & 0xffffff; 423 String ret = String.format("#%06X", color); 424 return ret; 425 } 426 427 428}