/*
* @(#)Colors.java 1.0 2011-03-13
*
* Copyright (c) 2011 Werner Randelshofer, Goldau, Switzerland.
* All rights reserved.
*
* You may not use, copy or modify this file, except in compliance with the
* license agreement you entered into with Werner Randelshofer.
* For details see accompanying license terms.
*/
package org.monte.media.color;
import java.awt.image.IndexColorModel;
import static java.lang.Math.*;
/**
* {@code Colors}.
*
* @author Werner Randelshofer
* @version 1.0 2011-03-13 Created.
*/
public class Colors {
/** Prevent instance creation. */
private Colors() {
}
/**
* The macintosh palette is arranged as follows: there are 256 colours to
* allocate, an even distribution of colors through the color cube might be
* desirable but 256 is not the cube of an integer. 6x6x6 is 216 and so the
* first 216 colors are an equal 6x6x6 sampling of the color cube.
* This leaves 40 colours to allocate, this has been done by choosing a ramp of
* 10 shades each for red, green, blue and grey.
*
* <p>
* References:<br>
* <a href="http://paulbourke.net/texture_colour/colourramp/">http://paulbourke.net/texture_colour/colourramp/</a>
*
* @return The Macintosh color palette.
*/
public static IndexColorModel createMacColors() {
byte[] r = new byte[256];
byte[] g = new byte[256];
byte[] b = new byte[256];
// Generate color cube with 216 colors
int index = 0;
for (int i = 0; i < 6; i++) {
for (int j = 0; j < 6; j++) {
for (int k = 0; k < 6; k++) {
r[index] = (byte) (255 - 51 * i);
g[index] = (byte) (255 - 51 * j);
b[index] = (byte) (255 - 51 * k);
index++;
}
}
}
index--; // overwrite last color (black) with color ramp
// Generate red ramp
byte[] ramp = {(byte) 238, (byte) 221, (byte) 187, (byte) 170, (byte) 136, (byte) 119, 85, 68, 34, 17};
for (int i = 0; i < 10; i++) {
r[index] = ramp[i];
g[index] = (byte) (0);
b[index] = (byte) (0);
index++;
}
// Generate green ramp
for (int j = 0; j < 10; j++) {
r[index] = (byte) (0);
g[index] = ramp[j];
b[index] = (byte) (0);
index++;
}
// Generate blue ramp
for (int k = 0; k < 10; k++) {
r[index] = (byte) (0);
g[index] = (byte) (0);
b[index] = ramp[k];
index++;
}
// Generate gray ramp
for (int ijk = 0; ijk < 10; ijk++) {
r[index] = ramp[ijk];
g[index] = ramp[ijk];
b[index] = ramp[ijk];
index++;
}
// last color is black (nothing to do)
/*
for (int i=0;i<256;i++) {
if (i%6==0) System.out.println(); else System.out.print(" ");
System.out.print(Integer.toHexString(r[i]&0xff)+","+Integer.toHexString(g[i]&0xff)+","+Integer.toHexString(b[i]&0xff));
}*/
IndexColorModel icm = new IndexColorModel(8, 256, r, g, b);
return icm;
}
private static void RGBtoYCC(float[] rgb, float[] ycc) {
float R = rgb[0];
float G = rgb[1];
float B = rgb[2];
float Y = 0.3f * R + 0.6f * G + 0.1f * B;
float V = R - Y;
float U = B - Y;
float Cb = (U / 2f) + 0.5f;
float Cr = (V / 1.6f) + 0.5f;
ycc[0] = Y;
ycc[1] = Cb;
ycc[2] = Cr;
}
private static void YCCtoRGB(float[] ycc, float[] rgb) {
float Y = ycc[0];
float Cb = ycc[1];
float Cr = ycc[2];
float U = (Cb - 0.5f) * 2f;
float V = (Cr - 0.5f) * 1.6f;
float R = V + Y;
float B = U + Y;
float G = (Y - 0.3f * R - 0.1f * B) / 0.6f;
rgb[0] = R;
rgb[1] = G;
rgb[2] = B;
}
/** RGB 8-bit per channel to YCC 16-bit per channel. */
private static void RGB8toYCC16(int[] rgb, int[] ycc) {
int R = rgb[0];
int G = rgb[1];
int B = rgb[2];
int Y = 77 * R + 153 * G + 26 * B;
int V = R * 256 - Y;
int U = B * 256 - Y;
int Cb = (U / 2) + 128 * 256;
int Cr = (V * 5 / 8) + 128 * 256;
ycc[0] = Y;
ycc[1] = Cb;
ycc[2] = Cr;
}
/** RGB 8-bit per channel to YCC 16-bit per channel. */
private static void RGB8toYCC16(int rgb, int[] ycc) {
int R = (rgb & 0xff0000) >>> 16;
int G = (rgb & 0xff00) >>> 8;
int B = rgb & 0xff;
int Y = 77 * R + 153 * G + 26 * B;
int V = R * 256 - Y;
int U = B * 256 - Y;
int Cb = (U / 2) + 128 * 256;
int Cr = (V * 5 / 8) + 128 * 256;
ycc[0] = Y;
ycc[1] = Cb;
ycc[2] = Cr;
}
/** YCC 16-bit per channel to RGB 8-bit per channel. */
private static void YCC16toRGB8(int[] ycc, int[] rgb) {
int Y = ycc[0];
int Cb = ycc[1];
int Cr = ycc[2];
int U = (Cb - 128 * 256) * 2;
int V = (Cr - 128 * 256) * 8 / 5;
int R = min(255, max(0, (V + Y) / 256));
int B = min(255, max(0, (U + Y) / 256));
int G = min(255, max(0, (Y - 77 * R - 26 * B) / 153));
rgb[0] = R;
rgb[1] = G;
rgb[2] = B;
}
/** YCC 8-bit per channel to RGB 8-bit per channel.
*/
private static void YCC8toRGB8(int[] ycc, int[] rgb) {
int Y = ycc[0];
int Cb = ycc[1];
int Cr = ycc[2];
// Source: JPEG File Interchange Format Version 1.02, September 1, 1992
//RGB can be computed directly from YCbCr (256 levels) as follows:
//R = Y + 1.402 (Cr-128)
//G = Y - 0.34414 (Cb-128) - 0.71414 (Cr-128)
//B = Y + 1.772 (Cb-128)
int R = (1000 * Y + 1402 * (Cr - 128)) / 1000;
int G = (100000 * Y - 34414 * (Cb - 128) - 71414 * (Cr - 128)) / 100000;
int B = (1000 * Y + 1772 * (Cb - 128)) / 1000;
rgb[0] = min(255, max(0, R));
rgb[1] = min(255, max(0, G));
rgb[2] = min(255, max(0, B));
}
/** YCC 8-bit per channel to RGB 8-bit per channel.
*/
private static void RGB8toYCC8(int[] rgb, int[] ycc) {
int R = rgb[0];
int G = rgb[1];
int B = rgb[2];
// Source: JPEG File Interchange Format Version 1.02, September 1, 1992
//YCbCr (256 levels) can be computed directly from 8-bit RGB as follows:
//Y = 0.299R +0.587G +0.114B
//Cb = - 0.1687 R - 0.3313 G + 0.5 B + 128
//Cr = 0.5 R - 0.4187 G - 0.0813 B + 128
int Y = (299 * R + 587 * G + 114 * B) / 1000;
int Cb = (-1687 * R - 3313 * G + 5000 * B) / 10000 + 128;
int Cr = (5000 * R - 4187 * G - 813 * B) / 10000 + 128;
ycc[0] = min(255, max(0, Y));
ycc[1] = min(255, max(0, Cb));
ycc[2] = min(255, max(0, Cr));
}
}