
org.jmol.util.C Maven / Gradle / Ivy
/* $RCSfile$
* $Author: hansonr $
* $Date: 2015-01-22 18:34:27 +0100 (Thu, 22 Jan 2015) $
* $Revision: 20231 $
*
* Copyright (C) 2003-2005 Miguel, Jmol Development, www.jmol.org
*
* Contact: [email protected]
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*/
package org.jmol.util;
import javajs.util.AU;
import javajs.util.CU;
import javajs.util.PT;
import javajs.util.SB;
import org.jmol.c.PAL;
/**
*
* Note: Color table is now in javajs/util/CU.java
*
*
* Implements a color index model using a colix as a
* COLor IndeX.
*
*
* A colix is a color index represented as a short int.
*
*
* The value 0 is considered a null value ... for no color. In Jmol this
* generally means that the value is inherited from some other object.
*
*
* The value 1 is used to indicate that color only is to be inherited.
*
* 0x0001 INHERIT_OPAQUE -- opaque, but with the color coming from the parent.
* 0x4001 INHERIT_TRANSLUCENT -- translucent but with the color coming from the parent.
*
* The value 2 is used to indicate that one of the palettes is to be used.
*
* 0x0002 PALETTE, opaque
* 0x4002 PALETTE, translucent
*
* Palettes themselves are coded separately in a Palette ID that is tracked with
*
*
* @author Miguel, [email protected]
*/
public final class C {
// final here because we are initializing public static fields using static{}
/* ***************************************************************
* color indexes -- colix
* ***************************************************************/
/* entries 0 and 1 are reserved and are special inheritance
0 INHERIT_ALL inherits both color and translucency
1 INHERIT_COLOR is used to inherit just the color
0x8000 changeable flag (elements and isotopes, about 200; negative)
0x7800 translucent flag set
NEW:
0x0000 translucent level 0 (opaque)
0x0800 translucent level 1
0x1000 translucent level 2
0x1800 translucent level 3
0x2000 translucent level 4
0x2800 translucent level 5
0x3000 translucent level 6
0x3800 translucent level 7
0x4000 translucent level 8 (invisible)
0x0000 inherit color and translucency
0x0001 inherit color; translucency determined by mask
0x0002 special palette ("group", "structure", etc.); translucency by mask
Note that inherited colors and special palettes are not handled here.
They could be anything, including totally variable quantities such as
distance to an object. So there are two stages of argb color determination
from a colix. The special palette flag is only used transiently - just to
indicate that the color selected isn't a known color. The actual palette-based
colix is saved here, and the atom or shape's byte paletteID is set as well.
Shapes/ColorManager: responsible for assigning argb colors based on
color palettes. These argb colors are then used directly.
Graphics3D: responsible for "system" colors and caching of user-defined rgbs.
0x0004 black...
....
0x0017 ...gold
0x00?? additional colors used from JavaScript list or specified by user
0x0177 last available colix
Bob Hanson 3/2007
*/
public final static short INHERIT_ALL = 0; // do not change this from 0; new colix[n] must be this
public final static short INHERIT_COLOR = 1;
public final static short USE_PALETTE = 2;
public final static short RAW_RGB = 3;
public final static short SPECIAL_COLIX_MAX = 4;
static int colixMax = SPECIAL_COLIX_MAX;
static int[] argbs = new int[128];
private static int[] argbsGreyscale;
private static final Int2IntHash colixHash = new Int2IntHash(256);
private static final int RAW_RGB_INT = RAW_RGB;
public final static short UNMASK_CHANGEABLE_TRANSLUCENT = 0x07FF;
public final static short CHANGEABLE_MASK = (short) 0x8000; // negative
public final static int LAST_AVAILABLE_COLIX = UNMASK_CHANGEABLE_TRANSLUCENT;
public final static int TRANSLUCENT_SHIFT = 11;
public final static int ALPHA_SHIFT = 24 - TRANSLUCENT_SHIFT;
public final static int TRANSLUCENT_MASK = 0xF << TRANSLUCENT_SHIFT; //0x7800
public final static int TRANSLUCENT_SCREENED = TRANSLUCENT_MASK;
public final static int TRANSPARENT = 8 << TRANSLUCENT_SHIFT; //0x4000
public final static short OPAQUE_MASK = ~TRANSLUCENT_MASK;
public final static short BLACK = 4;
public final static short ORANGE = 5;
public final static short PINK = 6;
public final static short BLUE = 7;
public final static short WHITE = 8;
public final static short CYAN = 9;
public final static short RED = 10;
public final static short GREEN = 11;
public final static short GRAY = 12;
public final static short SILVER = 13;
public final static short LIME = 14;
public final static short MAROON = 15;
public final static short NAVY = 16;
public final static short OLIVE = 17;
public final static short PURPLE = 18;
public final static short TEAL = 19;
public final static short MAGENTA = 20;
public final static short YELLOW = 21;
public final static short HOTPINK = 22;
public final static short GOLD = 23;
public C() {
}
public static short getColix(int argb) {
if (argb == 0)
return 0;
int translucentFlag = 0;
// in JavaScript argb & 0xFF000000 will be a negative long value
if ((argb & 0xFF000000) != (0xFF000000 & 0xFF000000)) {
translucentFlag = getTranslucentFlag((argb >> 24) & 0xFF);
argb |= 0xFF000000;
}
int c = colixHash.get(argb);
if ((c & RAW_RGB_INT) == RAW_RGB_INT)
translucentFlag = 0;
return (short) ((c > 0 ? c : allocateColix(argb, false)) | translucentFlag);
}
public synchronized static int allocateColix(int argb, boolean forceLast) {
// in JavaScript argb & 0xFF000000 will be a long
//if ((argb & 0xFF000000) != (0xFF000000 & 0xFF000000))
// throw new IndexOutOfBoundsException();
// double-check to make sure that someone else did not allocate
// something of the same color while we were waiting for the lock
int n;
if (forceLast) {
n = LAST_AVAILABLE_COLIX;
} else {
for (int i = colixMax; --i >= SPECIAL_COLIX_MAX;)
if ((argb & 0xFFFFFF) == (argbs[i] & 0xFFFFFF))
return i;
n = colixMax;
}
if (n >= argbs.length) {
int newSize = (forceLast ? n + 1 : colixMax * 2);
if (newSize > LAST_AVAILABLE_COLIX + 1)
newSize = LAST_AVAILABLE_COLIX + 1;
argbs = AU.arrayCopyI(argbs, newSize);
if (argbsGreyscale != null)
argbsGreyscale = AU.arrayCopyI(argbsGreyscale, newSize);
}
argbs[n] = argb;
if (argbsGreyscale != null)
argbsGreyscale[n] = CU.toFFGGGfromRGB(argb);
colixHash.put(argb, n);
return (n < LAST_AVAILABLE_COLIX ? colixMax++ : colixMax);
}
static void setLastGrey(int argb) {
calcArgbsGreyscale();
argbsGreyscale[LAST_AVAILABLE_COLIX] = CU.toFFGGGfromRGB(argb);
}
synchronized static void calcArgbsGreyscale() {
if (argbsGreyscale != null)
return;
int[] a = new int[argbs.length];
for (int i = argbs.length; --i >= SPECIAL_COLIX_MAX;)
a[i] = CU.toFFGGGfromRGB(argbs[i]);
argbsGreyscale = a;
}
public final static int getArgbGreyscale(short colix) {
if (argbsGreyscale == null)
calcArgbsGreyscale();
return argbsGreyscale[colix & OPAQUE_MASK];
}
/*
Int2IntHash hashMix2 = new Int2IntHash(32);
short getColixMix(short colixA, short colixB) {
if (colixA == colixB)
return colixA;
if (colixA <= 0)
return colixB;
if (colixB <= 0)
return colixA;
int translucentMask = colixA & colixB & TRANSLUCENT_MASK;
colixA &= ~TRANSLUCENT_MASK;
colixB &= ~TRANSLUCENT_MASK;
int mixId = ((colixA < colixB)
? ((colixA << 16) | colixB)
: ((colixB << 16) | colixA));
int mixed = hashMix2.get(mixId);
if (mixed == Integer.MIN_VALUE) {
int argbA = argbs[colixA];
int argbB = argbs[colixB];
int r = (((argbA & 0x00FF0000)+(argbB & 0x00FF0000)) >> 1) & 0x00FF0000;
int g = (((argbA & 0x0000FF00)+(argbB & 0x0000FF00)) >> 1) & 0x0000FF00;
int b = (((argbA & 0x000000FF)+(argbB & 0x000000FF)) >> 1);
int argbMixed = 0xFF000000 | r | g | b;
mixed = getColix(argbMixed);
hashMix2.put(mixId, mixed);
}
return (short)(mixed | translucentMask);
}
*/
static {
int[] predefinedArgbs = { // For Google Closure Compiler
0xFF000000, // black
0xFFFFA500, // orange
0xFFFFC0CB, // pink
0xFF0000FF, // blue
0xFFFFFFFF, // white
0xFF00FFFF, // cyan
0xFFFF0000, // red
0xFF008000, // green -- really!
0xFF808080, // gray
0xFFC0C0C0, // silver
0xFF00FF00, // lime -- no kidding!
0xFF800000, // maroon
0xFF000080, // navy
0xFF808000, // olive
0xFF800080, // purple
0xFF008080, // teal
0xFFFF00FF, // magenta
0xFFFFFF00, // yellow
0xFFFF69B4, // hotpink
0xFFFFD700, // gold
};
// OK for J2S compiler because this is a final class
for (int i = 0; i < predefinedArgbs.length; ++i)
getColix(predefinedArgbs[i]);
}
public static short getColixO(Object obj) {
if (obj == null)
return INHERIT_ALL;
if (obj instanceof PAL)
return (((PAL) obj) == PAL.NONE ? INHERIT_ALL
: USE_PALETTE);
if (obj instanceof Integer)
return getColix(((Integer) obj).intValue());
if (obj instanceof String)
return getColixS((String) obj);
if (obj instanceof Byte)
return (((Byte) obj).byteValue() == 0 ? INHERIT_ALL : USE_PALETTE);
if (Logger.debugging) {
Logger.debug("?? getColix(" + obj + ")");
}
return HOTPINK;
}
private static int getTranslucentFlag(float translucentLevel) {
// 0.0 to 1.0 ==> MORE translucent
// 1/8 1/4 3/8 1/2 5/8 3/4 7/8 8/8
// t 32 64 96 128 160 192 224 255 or 256
// t >> 5 1 2 3 4 5 6 7 8
// (t >> 5) + 1 2 3 4 5 6 7 8 9
// 15 is reserved for screened, so 9-14 just map to 9, "invisible"
if (translucentLevel == 0) //opaque
return 0;
if (translucentLevel < 0) //screened
return TRANSLUCENT_SCREENED;
// if (translucentLevel < 0) //screened
// translucentLevel = 128;//return TRANSLUCENT_SCREENED;
if (Float.isNaN(translucentLevel) || translucentLevel >= 255
|| translucentLevel == 1.0)
return TRANSPARENT;
int iLevel = (int) Math.floor(translucentLevel < 1 ? translucentLevel * 256
: translucentLevel >= 15 ? translucentLevel
: translucentLevel <= 9 ? ((int) Math.floor(translucentLevel - 1)) << 5
: 8 << 5);
return (((iLevel >> 5) & 0xF) << TRANSLUCENT_SHIFT);
}
public static boolean isColixLastAvailable(short colix) {
return (colix > 0 && (colix & LAST_AVAILABLE_COLIX) == LAST_AVAILABLE_COLIX);
}
public static int getArgb(short colix) {
return argbs[colix & OPAQUE_MASK];
}
public final static boolean isColixColorInherited(short colix) {
switch (colix) {
case INHERIT_ALL:
case INHERIT_COLOR:
return true;
default: //could be translucent of some sort
return (colix & OPAQUE_MASK) == INHERIT_COLOR;
}
}
public final static short getColixInherited(short myColix, short parentColix) {
switch (myColix) {
case INHERIT_ALL:
return parentColix;
case INHERIT_COLOR:
return (short) (parentColix & OPAQUE_MASK);
default:
//check this colix irrespective of translucency, and if inherit, then
//it must be inherit color but not translucent level;
return ((myColix & OPAQUE_MASK) == INHERIT_COLOR ? (short) (parentColix
& OPAQUE_MASK | myColix & TRANSLUCENT_MASK) : myColix);
}
}
public final static boolean renderPass2(short colix) {
int c = colix & TRANSLUCENT_MASK;
return (c != 0 && c != TRANSLUCENT_SCREENED);
}
public final static boolean isColixTranslucent(short colix) {
return ((colix & TRANSLUCENT_MASK) != 0);
}
public final static short getChangeableColixIndex(short colix) {
return (colix >= 0 ? -1 : (short) (colix & UNMASK_CHANGEABLE_TRANSLUCENT));
}
public final static short getColixTranslucent3(short colix,
boolean isTranslucent,
float translucentLevel) {
colix &= ~TRANSLUCENT_MASK;
if (colix == INHERIT_ALL)
colix = INHERIT_COLOR;
return (isTranslucent ? (short) (colix | getTranslucentFlag(translucentLevel))
: colix);
}
public final static short copyColixTranslucency(short colixFrom, short colixTo) {
return getColixTranslucent3(colixTo, isColixTranslucent(colixFrom),
getColixTranslucencyLevel(colixFrom));
}
public static float getColixTranslucencyFractional(short colix) {
int translevel = getColixTranslucencyLevel(colix);
return (translevel == -1 ? 0.5f : translevel == 0 ? 0
: translevel == 255 ? 1 : translevel / 256f);
}
public static String getColixTranslucencyLabel(short colix) {
return "translucent " + ((colix & TRANSLUCENT_SCREENED) == TRANSLUCENT_SCREENED ? -1 : getColixTranslucencyFractional(colix));
}
public final static int getColixTranslucencyLevel(short colix) {
int logAlpha = (colix >> TRANSLUCENT_SHIFT) & 0xF;
switch (logAlpha) {
case 0:
return 0;
case 1: // 32
case 2: // 64
case 3: // 96
case 4: // 128
case 5: // 160
case 6: // 192
case 7: // 224
return logAlpha << 5;
case 15:
return -1;
default:
return 255;
}
}
public static short getColixS(String colorName) {
int argb = CU.getArgbFromString(colorName);
if (argb != 0)
return getColix(argb);
if ("none".equalsIgnoreCase(colorName))
return INHERIT_ALL;
if ("opaque".equalsIgnoreCase(colorName))
return INHERIT_COLOR;
return USE_PALETTE;
}
public static short[] getColixArray(String colorNames) {
if (colorNames == null || colorNames.length() == 0)
return null;
String[] colors = PT.getTokens(colorNames);
short[] colixes = new short[colors.length];
for (int j = 0; j < colors.length; j++) {
colixes[j] = getColix(CU.getArgbFromString(colors[j]));
if (colixes[j] == 0)
return null;
}
return colixes;
}
public static String getHexCode(short colix) {
return Escape.escapeColor(getArgb(colix));
}
public static String getHexCodes(short[] colixes) {
if (colixes == null)
return null;
SB s = new SB();
for (int i = 0; i < colixes.length; i++)
s.append(i == 0 ? "" : " ").append(getHexCode(colixes[i]));
return s.toString();
}
public static short getColixTranslucent(int argb) {
int a = (argb >> 24) & 0xFF;
return (a == 0xFF ? getColix(argb) : getColixTranslucent3(getColix(argb), true, a / 255f));
}
public static short getBgContrast(int argb) {
return ((CU.toFFGGGfromRGB(argb) & 0xFF) < 128 ? WHITE
: BLACK);
}
}