1 /* $RCSfile$ 2 * $Author: hansonr $ 3 * $Date: 2015-01-22 11:34:27 -0600 (Thu, 22 Jan 2015) $ 4 * $Revision: 20231 $ 5 * 6 * Copyright (C) 2003-2005 Miguel, Jmol Development, www.jmol.org 7 * 8 * Contact: jmol-developers@lists.sf.net 9 * 10 * This library is free software; you can redistribute it and/or 11 * modify it under the terms of the GNU Lesser General Public 12 * License as published by the Free Software Foundation; either 13 * version 2.1 of the License, or (at your option) any later version. 14 * 15 * This library is distributed in the hope that it will be useful, 16 * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 18 * Lesser General Public License for more details. 19 * 20 * You should have received a copy of the GNU Lesser General Public 21 * License along with this library; if not, write to the Free Software 22 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 23 */ 24 25 package org.jmol.util; 26 27 28 import javajs.util.AU; 29 import javajs.util.CU; 30 import javajs.util.PT; 31 import javajs.util.SB; 32 33 import org.jmol.c.PAL; 34 35 /** 36 * 37 * Note: Color table is now in javajs/util/CU.java 38 * 39 *<p> 40 * Implements a color index model using a colix as a 41 * <strong>COLor IndeX</strong>. 42 *</p> 43 *<p> 44 * A colix is a color index represented as a short int. 45 *</p> 46 *<p> 47 * The value 0 is considered a null value ... for no color. In Jmol this 48 * generally means that the value is inherited from some other object. 49 *</p> 50 *<p> 51 * The value 1 is used to indicate that color only is to be inherited. 52 * 53 * 0x0001 INHERIT_OPAQUE -- opaque, but with the color coming from the parent. 54 * 0x4001 INHERIT_TRANSLUCENT -- translucent but with the color coming from the parent. 55 * 56 * The value 2 is used to indicate that one of the palettes is to be used. 57 * 58 * 0x0002 PALETTE, opaque 59 * 0x4002 PALETTE, translucent 60 * 61 * Palettes themselves are coded separately in a Palette ID that is tracked with 62 *</p> 63 * 64 * @author Miguel, miguel@jmol.org 65 */ 66 67 public final class C { 68 69 // final here because we are initializing public static fields using static{} 70 71 /* *************************************************************** 72 * color indexes -- colix 73 * ***************************************************************/ 74 75 /* entries 0 and 1 are reserved and are special inheritance 76 0 INHERIT_ALL inherits both color and translucency 77 1 INHERIT_COLOR is used to inherit just the color 78 79 80 0x8000 changeable flag (elements and isotopes, about 200; negative) 81 0x7800 translucent flag set 82 83 NEW: 84 0x0000 translucent level 0 (opaque) 85 0x0800 translucent level 1 86 0x1000 translucent level 2 87 0x1800 translucent level 3 88 0x2000 translucent level 4 89 0x2800 translucent level 5 90 0x3000 translucent level 6 91 0x3800 translucent level 7 92 0x4000 translucent level 8 (invisible) 93 94 0x0000 inherit color and translucency 95 0x0001 inherit color; translucency determined by mask 96 0x0002 special palette ("group", "structure", etc.); translucency by mask 97 98 Note that inherited colors and special palettes are not handled here. 99 They could be anything, including totally variable quantities such as 100 distance to an object. So there are two stages of argb color determination 101 from a colix. The special palette flag is only used transiently - just to 102 indicate that the color selected isn't a known color. The actual palette-based 103 colix is saved here, and the atom or shape's byte paletteID is set as well. 104 105 Shapes/ColorManager: responsible for assigning argb colors based on 106 color palettes. These argb colors are then used directly. 107 108 Graphics3D: responsible for "system" colors and caching of user-defined rgbs. 109 110 111 112 0x0004 black... 113 .... 114 0x0017 ...gold 115 0x00?? additional colors used from JavaScript list or specified by user 116 117 0x0177 last available colix 118 119 Bob Hanson 3/2007 120 121 */ 122 123 public final static short INHERIT_ALL = 0; // do not change this from 0; new colix[n] must be this 124 public final static short INHERIT_COLOR = 1; 125 public final static short USE_PALETTE = 2; 126 public final static short RAW_RGB = 3; 127 public final static short SPECIAL_COLIX_MAX = 4; 128 129 static int colixMax = SPECIAL_COLIX_MAX; 130 131 static int[] argbs = new int[128]; 132 133 private static int[] argbsGreyscale; 134 135 private static final Int2IntHash colixHash = new Int2IntHash(256); 136 private static final int RAW_RGB_INT = RAW_RGB; 137 public final static short UNMASK_CHANGEABLE_TRANSLUCENT = 0x07FF; 138 public final static short CHANGEABLE_MASK = (short) 0x8000; // negative 139 public final static int LAST_AVAILABLE_COLIX = UNMASK_CHANGEABLE_TRANSLUCENT; 140 public final static int TRANSLUCENT_SHIFT = 11; 141 public final static int ALPHA_SHIFT = 24 - TRANSLUCENT_SHIFT; 142 public final static int TRANSLUCENT_MASK = 0xF << TRANSLUCENT_SHIFT; //0x7800 143 public final static int TRANSLUCENT_SCREENED = TRANSLUCENT_MASK; 144 public final static int TRANSPARENT = 8 << TRANSLUCENT_SHIFT; //0x4000 145 public final static short OPAQUE_MASK = ~TRANSLUCENT_MASK; 146 147 public final static short BLACK = 4; 148 public final static short ORANGE = 5; 149 public final static short PINK = 6; 150 public final static short BLUE = 7; 151 public final static short WHITE = 8; 152 public final static short CYAN = 9; 153 public final static short RED = 10; 154 public final static short GREEN = 11; 155 public final static short GRAY = 12; 156 public final static short SILVER = 13; 157 public final static short LIME = 14; 158 public final static short MAROON = 15; 159 public final static short NAVY = 16; 160 public final static short OLIVE = 17; 161 public final static short PURPLE = 18; 162 public final static short TEAL = 19; 163 public final static short MAGENTA = 20; 164 public final static short YELLOW = 21; 165 public final static short HOTPINK = 22; 166 public final static short GOLD = 23; 167 C()168 public C() { 169 } 170 getColix(int argb)171 public static short getColix(int argb) { 172 if (argb == 0) 173 return 0; 174 int translucentFlag = 0; 175 // in JavaScript argb & 0xFF000000 will be a negative long value 176 if ((argb & 0xFF000000) != (0xFF000000 & 0xFF000000)) { 177 translucentFlag = getTranslucentFlag((argb >> 24) & 0xFF); 178 argb |= 0xFF000000; 179 } 180 int c = colixHash.get(argb); 181 if ((c & RAW_RGB_INT) == RAW_RGB_INT) 182 translucentFlag = 0; 183 return (short) ((c > 0 ? c : allocateColix(argb, false)) | translucentFlag); 184 } 185 allocateColix(int argb, boolean forceLast)186 public synchronized static int allocateColix(int argb, boolean forceLast) { 187 // in JavaScript argb & 0xFF000000 will be a long 188 //if ((argb & 0xFF000000) != (0xFF000000 & 0xFF000000)) 189 // throw new IndexOutOfBoundsException(); 190 // double-check to make sure that someone else did not allocate 191 // something of the same color while we were waiting for the lock 192 int n; 193 if (forceLast) { 194 n = LAST_AVAILABLE_COLIX; 195 } else { 196 for (int i = colixMax; --i >= SPECIAL_COLIX_MAX;) 197 if ((argb & 0xFFFFFF) == (argbs[i] & 0xFFFFFF)) 198 return i; 199 n = colixMax; 200 } 201 if (n >= argbs.length) { 202 int newSize = (forceLast ? n + 1 : colixMax * 2); 203 if (newSize > LAST_AVAILABLE_COLIX + 1) 204 newSize = LAST_AVAILABLE_COLIX + 1; 205 argbs = AU.arrayCopyI(argbs, newSize); 206 if (argbsGreyscale != null) 207 argbsGreyscale = AU.arrayCopyI(argbsGreyscale, newSize); 208 } 209 argbs[n] = argb; 210 if (argbsGreyscale != null) 211 argbsGreyscale[n] = CU.toFFGGGfromRGB(argb); 212 colixHash.put(argb, n); 213 return (n < LAST_AVAILABLE_COLIX ? colixMax++ : colixMax); 214 } 215 setLastGrey(int argb)216 static void setLastGrey(int argb) { 217 calcArgbsGreyscale(); 218 argbsGreyscale[LAST_AVAILABLE_COLIX] = CU.toFFGGGfromRGB(argb); 219 } 220 calcArgbsGreyscale()221 synchronized static void calcArgbsGreyscale() { 222 if (argbsGreyscale != null) 223 return; 224 int[] a = new int[argbs.length]; 225 for (int i = argbs.length; --i >= SPECIAL_COLIX_MAX;) 226 a[i] = CU.toFFGGGfromRGB(argbs[i]); 227 argbsGreyscale = a; 228 } 229 getArgbGreyscale(short colix)230 public final static int getArgbGreyscale(short colix) { 231 if (argbsGreyscale == null) 232 calcArgbsGreyscale(); 233 return argbsGreyscale[colix & OPAQUE_MASK]; 234 } 235 236 /* 237 Int2IntHash hashMix2 = new Int2IntHash(32); 238 239 short getColixMix(short colixA, short colixB) { 240 if (colixA == colixB) 241 return colixA; 242 if (colixA <= 0) 243 return colixB; 244 if (colixB <= 0) 245 return colixA; 246 int translucentMask = colixA & colixB & TRANSLUCENT_MASK; 247 colixA &= ~TRANSLUCENT_MASK; 248 colixB &= ~TRANSLUCENT_MASK; 249 int mixId = ((colixA < colixB) 250 ? ((colixA << 16) | colixB) 251 : ((colixB << 16) | colixA)); 252 int mixed = hashMix2.get(mixId); 253 if (mixed == Integer.MIN_VALUE) { 254 int argbA = argbs[colixA]; 255 int argbB = argbs[colixB]; 256 int r = (((argbA & 0x00FF0000)+(argbB & 0x00FF0000)) >> 1) & 0x00FF0000; 257 int g = (((argbA & 0x0000FF00)+(argbB & 0x0000FF00)) >> 1) & 0x0000FF00; 258 int b = (((argbA & 0x000000FF)+(argbB & 0x000000FF)) >> 1); 259 int argbMixed = 0xFF000000 | r | g | b; 260 mixed = getColix(argbMixed); 261 hashMix2.put(mixId, mixed); 262 } 263 return (short)(mixed | translucentMask); 264 } 265 */ 266 267 static { 268 int[] predefinedArgbs = { // For Google Closure Compiler 269 0xFF000000, // black 270 0xFFFFA500, // orange 271 0xFFFFC0CB, // pink 272 0xFF0000FF, // blue 273 0xFFFFFFFF, // white 274 0xFF00FFFF, // cyan 275 0xFFFF0000, // red 276 0xFF008000, // green -- really! 277 0xFF808080, // gray 278 0xFFC0C0C0, // silver 279 0xFF00FF00, // lime -- no kidding! 280 0xFF800000, // maroon 281 0xFF000080, // navy 282 0xFF808000, // olive 283 0xFF800080, // purple 284 0xFF008080, // teal 285 0xFFFF00FF, // magenta 286 0xFFFFFF00, // yellow 287 0xFFFF69B4, // hotpink 288 0xFFFFD700, // gold 289 }; 290 // OK for J2S compiler because this is a final class 291 for (int i = 0; i < predefinedArgbs.length; ++i) 292 getColix(predefinedArgbs[i]); 293 } 294 getColixO(Object obj)295 public static short getColixO(Object obj) { 296 if (obj == null) 297 return INHERIT_ALL; 298 if (obj instanceof PAL) 299 return (((PAL) obj) == PAL.NONE ? INHERIT_ALL 300 : USE_PALETTE); 301 if (obj instanceof Integer) 302 return getColix(((Integer) obj).intValue()); 303 if (obj instanceof String) 304 return getColixS((String) obj); 305 if (obj instanceof Byte) 306 return (((Byte) obj).byteValue() == 0 ? INHERIT_ALL : USE_PALETTE); 307 if (Logger.debugging) { 308 Logger.debug("?? getColix(" + obj + ")"); 309 } 310 return HOTPINK; 311 } 312 getTranslucentFlag(float translucentLevel)313 private static int getTranslucentFlag(float translucentLevel) { 314 // 0.0 to 1.0 ==> MORE translucent 315 // 1/8 1/4 3/8 1/2 5/8 3/4 7/8 8/8 316 // t 32 64 96 128 160 192 224 255 or 256 317 // t >> 5 1 2 3 4 5 6 7 8 318 // (t >> 5) + 1 2 3 4 5 6 7 8 9 319 // 15 is reserved for screened, so 9-14 just map to 9, "invisible" 320 321 if (translucentLevel == 0) //opaque 322 return 0; 323 if (translucentLevel < 0) //screened 324 return TRANSLUCENT_SCREENED; 325 // if (translucentLevel < 0) //screened 326 // translucentLevel = 128;//return TRANSLUCENT_SCREENED; 327 if (Float.isNaN(translucentLevel) || translucentLevel >= 255 328 || translucentLevel == 1.0) 329 return TRANSPARENT; 330 int iLevel = (int) Math.floor(translucentLevel < 1 ? translucentLevel * 256 331 : translucentLevel >= 15 ? translucentLevel 332 : translucentLevel <= 9 ? ((int) Math.floor(translucentLevel - 1)) << 5 333 : 8 << 5); 334 return (((iLevel >> 5) & 0xF) << TRANSLUCENT_SHIFT); 335 } 336 isColixLastAvailable(short colix)337 public static boolean isColixLastAvailable(short colix) { 338 return (colix > 0 && (colix & LAST_AVAILABLE_COLIX) == LAST_AVAILABLE_COLIX); 339 } 340 getArgb(short colix)341 public static int getArgb(short colix) { 342 return argbs[colix & OPAQUE_MASK]; 343 } 344 isColixColorInherited(short colix)345 public final static boolean isColixColorInherited(short colix) { 346 switch (colix) { 347 case INHERIT_ALL: 348 case INHERIT_COLOR: 349 return true; 350 default: //could be translucent of some sort 351 return (colix & OPAQUE_MASK) == INHERIT_COLOR; 352 } 353 } 354 getColixInherited(short myColix, short parentColix)355 public final static short getColixInherited(short myColix, short parentColix) { 356 switch (myColix) { 357 case INHERIT_ALL: 358 return parentColix; 359 case INHERIT_COLOR: 360 return (short) (parentColix & OPAQUE_MASK); 361 default: 362 //check this colix irrespective of translucency, and if inherit, then 363 //it must be inherit color but not translucent level; 364 return ((myColix & OPAQUE_MASK) == INHERIT_COLOR ? (short) (parentColix 365 & OPAQUE_MASK | myColix & TRANSLUCENT_MASK) : myColix); 366 } 367 } 368 renderPass2(short colix)369 public final static boolean renderPass2(short colix) { 370 int c = colix & TRANSLUCENT_MASK; 371 return (c != 0 && c != TRANSLUCENT_SCREENED); 372 } 373 isColixTranslucent(short colix)374 public final static boolean isColixTranslucent(short colix) { 375 return ((colix & TRANSLUCENT_MASK) != 0); 376 } 377 getChangeableColixIndex(short colix)378 public final static short getChangeableColixIndex(short colix) { 379 return (colix >= 0 ? -1 : (short) (colix & UNMASK_CHANGEABLE_TRANSLUCENT)); 380 } 381 getColixTranslucent3(short colix, boolean isTranslucent, float translucentLevel)382 public final static short getColixTranslucent3(short colix, 383 boolean isTranslucent, 384 float translucentLevel) { 385 colix &= ~TRANSLUCENT_MASK; 386 if (colix == INHERIT_ALL) 387 colix = INHERIT_COLOR; 388 return (isTranslucent ? (short) (colix | getTranslucentFlag(translucentLevel)) 389 : colix); 390 } 391 copyColixTranslucency(short colixFrom, short colixTo)392 public final static short copyColixTranslucency(short colixFrom, short colixTo) { 393 return getColixTranslucent3(colixTo, isColixTranslucent(colixFrom), 394 getColixTranslucencyLevel(colixFrom)); 395 } 396 getColixTranslucencyFractional(short colix)397 public static float getColixTranslucencyFractional(short colix) { 398 int translevel = getColixTranslucencyLevel(colix); 399 return (translevel == -1 ? 0.5f : translevel == 0 ? 0 400 : translevel == 255 ? 1 : translevel / 256f); 401 } 402 getColixTranslucencyLabel(short colix)403 public static String getColixTranslucencyLabel(short colix) { 404 return "translucent " + ((colix & TRANSLUCENT_SCREENED) == TRANSLUCENT_SCREENED ? -1 : getColixTranslucencyFractional(colix)); 405 } 406 getColixTranslucencyLevel(short colix)407 public final static int getColixTranslucencyLevel(short colix) { 408 int logAlpha = (colix >> TRANSLUCENT_SHIFT) & 0xF; 409 switch (logAlpha) { 410 case 0: 411 return 0; 412 case 1: // 32 413 case 2: // 64 414 case 3: // 96 415 case 4: // 128 416 case 5: // 160 417 case 6: // 192 418 case 7: // 224 419 return logAlpha << 5; 420 case 15: 421 return -1; 422 default: 423 return 255; 424 } 425 } 426 getColixS(String colorName)427 public static short getColixS(String colorName) { 428 int argb = CU.getArgbFromString(colorName); 429 if (argb != 0) 430 return getColix(argb); 431 if ("none".equalsIgnoreCase(colorName)) 432 return INHERIT_ALL; 433 if ("opaque".equalsIgnoreCase(colorName)) 434 return INHERIT_COLOR; 435 return USE_PALETTE; 436 } 437 getColixArray(String colorNames)438 public static short[] getColixArray(String colorNames) { 439 if (colorNames == null || colorNames.length() == 0) 440 return null; 441 String[] colors = PT.getTokens(colorNames); 442 short[] colixes = new short[colors.length]; 443 for (int j = 0; j < colors.length; j++) { 444 colixes[j] = getColix(CU.getArgbFromString(colors[j])); 445 if (colixes[j] == 0) 446 return null; 447 } 448 return colixes; 449 } 450 getHexCode(short colix)451 public static String getHexCode(short colix) { 452 return Escape.escapeColor(getArgb(colix)); 453 } 454 getHexCodes(short[] colixes)455 public static String getHexCodes(short[] colixes) { 456 if (colixes == null) 457 return null; 458 SB s = new SB(); 459 for (int i = 0; i < colixes.length; i++) 460 s.append(i == 0 ? "" : " ").append(getHexCode(colixes[i])); 461 return s.toString(); 462 } 463 getColixTranslucent(int argb)464 public static short getColixTranslucent(int argb) { 465 int a = (argb >> 24) & 0xFF; 466 return (a == 0xFF ? getColix(argb) : getColixTranslucent3(getColix(argb), true, a / 255f)); 467 } 468 getBgContrast(int argb)469 public static short getBgContrast(int argb) { 470 return ((CU.toFFGGGfromRGB(argb) & 0xFF) < 128 ? WHITE 471 : BLACK); 472 } 473 474 } 475