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