import java.awt.datatransfer.*;
import javax.swing.*;
 
 
int [] pixmap;
int numColors = 3;
color [] toneMap = new color[numColors];
int [] populationCount = new int[numColors];
float [] populationPerc = new float[numColors];
int res = 16;
int step = 20;
 
 
 
Slider srOff = new Slider("Red Contrast ",100,400,.6,-50,100);
Slider sgOff = new Slider("Green Contrast ",100,420,.5,-50,100);
Slider sbOff = new Slider("Blue Contrast ",100,440,.5,-50,100);
 
Slider refR = new Slider("Red",100,340,80/255.0,0,255);
Slider refG = new Slider("Green",100,360,60/255.0,0,255);
Slider refB = new Slider("Blue",100,380,40/255.0,0,255);
 
Toggle mirror = new Toggle("Mirror",100,460,1);
Toggle radialSymmetry = new Toggle("Radial",100,480,1);
 
Slider gridSize = new Slider("Resolution",100,320,.2,5,50);
 
 
PImage tile = new PImage(res,res);
PImage mirrorTile = new PImage(2*res,2*res);
color refColor = color(80,60,40);
 
PFont fontA;
boolean outOfGamut = false;
 
Vector controls = new Vector();
 
void setup(){
controls.add(srOff);
controls.add(sgOff);
controls.add(sbOff);
controls.add(refR);
controls.add(refG);
controls.add(refB);
controls.add(gridSize);
controls.add(mirror);
controls.add(radialSymmetry);
 
PFont fontA = loadFont("arial.vlw");
textFont(fontA, 12);
textAlign(RIGHT);
size(550,550);
 
toneMap[0] = refColor;
toneMap[1] = refColor;
setupArrays();
mouseDragged();
 
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
}
catch (Exception e) {
e.printStackTrace();
}
 
 
 
}
 
void setupArrays(){
pixmap = new int[res*res];
for (int i=0; i < res; i++){
for (int j=0; j < res; j++){
pixmap[j*res + i] = 0;
}
}
tile = new PImage(res,res);
mirrorTile = new PImage(2*res,2*res);
}
 
 
void draw(){
stroke(0);
background(refColor);
if (mirror.getValue() == 0){
for (int i=0; i < width/res; i++){
for(int j =0; j < height/res; j++){
image(tile,i*res,j*res);
}
}
}
else{
for (int i=0; i < width/res; i++){
for(int j =0; j < height/res; j++){
image(mirrorTile,2*i*res,2*j*res);
}
}
}
 
step = (width/2)/res;
for (int i=0; i < res; i++){
for (int j=0; j < res; j++){
fill(toneMap[pixmap[j*res + i]]);
rect(i*step,j*step,step,step );
}
}
noStroke();
fill(refColor);
rect(height/2,width/2,height/2,width/2);
for (int i =0; i < controls.size(); i++){
control c = (control)controls.elementAt(i);
c.draw();
}
if (outOfGamut){
fill(0);
noStroke();
rect(120,285,100,20);
fill(255);
text("Out of Gamut",200,300);
}
}
 
 
void mouseDragged(){
int newColor;
int oRes = res;
if (mouseButton == LEFT) newColor = 1;
else newColor = 0;
int ax = mouseX/step;
int ay = mouseY/step;
if (ax < res && ay < res && ax >= 0 && ay >= 0){
pixmap[ay*res + ax] = newColor;
if (radialSymmetry.getValue() == 1) pixmap[ax*res + ay] = newColor;
}
 
 
for (int i =0; i < controls.size(); i++){
control c = (control)controls.elementAt(i);
c.handleInput();
refColor = color(refR.getValue(), refG.getValue(), refB.getValue());
res = gridSize.getValue();
if (oRes != res){
setupArrays();
}
}
updateRatio();
updateTiles();
}
void mousePressed(){
mouseDragged();
}
 
void keyPressed(){
try{
if (key=='m'){
mirror.toggle();
}
else if (key == 'c'){
BufferedImage img=new BufferedImage(res, res, BufferedImage.TYPE_INT_RGB);
img.setRGB(0, 0, res, res, tile.pixels, 0, res);
ImageSelection imgSel = new ImageSelection(img);
Toolkit.getDefaultToolkit().getSystemClipboard().setContents(imgSel, null);
 
}
else if(key == 's'){
SwingUtilities.invokeLater(new Runnable() {
public void run() {
try {
JFileChooser fc = new JFileChooser();
int returnVal = fc.showSaveDialog(null);
if (returnVal == JFileChooser.APPROVE_OPTION) {
File selFile = fc.getSelectedFile();
if (selFile != null){
String outName = selFile.getAbsolutePath();
if (!outName.endsWith(".png") && !outName.endsWith(".PNG")) outName = outName + ".png";
if (mirror.getValue() == 1){
mirrorTile.save(outName);
}
else{
tile.save(outName);
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
 
}
 
});
 
updateTiles();
}
}catch(Exception e){
print("These functions do not work in applets");
}
 
}
void updateTiles(){
for (int i=0; i < res; i++){
for (int j=0; j < res; j++){
tile.set(i,j,toneMap[pixmap[j*res + i]]);
mirrorTile.set(i,j,toneMap[pixmap[j*res + i]]);
mirrorTile.set(2*res-i-1,j,toneMap[pixmap[j*res + i]]);
mirrorTile.set(i,2*res-j-1,toneMap[pixmap[j*res + i]]);
mirrorTile.set(2*res-i-1,2*res-j-1,toneMap[pixmap[j*res + i]]);
}
}
tile.updatePixels();
mirrorTile.updatePixels();
}
 
void updateRatio(){
for (int i=0; i < numColors; i++){
populationCount[i] = 0;
}
for (int i=0; i < res; i++){
for (int j=0; j < res; j++){
populationCount[pixmap[j*res + i]]++;
}
}
for (int i=0; i < numColors; i++){
populationPerc[i] = (float)populationCount[i]/(res*res);
}
 
calcTone();
 
 
}
 
 
void calcTone(){
toneMap[0] = color(red(refColor) + srOff.getValue(),green(refColor) + sgOff.getValue(),blue(refColor) + sbOff.getValue() );
float tr = (red(refColor)-(populationPerc[0]*red(toneMap[0])))/populationPerc[1];
float tg = (green(refColor)-(populationPerc[0]*green(toneMap[0])))/populationPerc[1];
float tb = (blue(refColor)-(populationPerc[0]*blue(toneMap[0])))/populationPerc[1];
if (tr < 0 || tg < 0 || tb < 0 || tr > 255 || tg > 255 || tb > 255 ||
red(toneMap[0]) < 0 || red(toneMap[0]) > 255 || green(toneMap[0]) < 0 || green(toneMap[0]) > 255 || blue(toneMap[0]) < 0 || blue(toneMap[0]) > 255 ){
outOfGamut = true;
}
else{
outOfGamut = false;
}
toneMap[1] = color(tr,tg,tb);
}
 
 
class Slider extends control{
int min = 0;
int span = 255;
String label = "label";
float value = .5;
int locx, locy;
Slider(String label, int locx, int locy, float value, int min, int span){
this.min = min;
this.span = span;
this.label = label;
this.locx = locx;
this.locy = locy;
this.value = value;
}
void draw(){
stroke(255);
fill(0,0,0,0);
rect(locx ,locy,100,10);
fill(255);
rect(locx + value*90,locy,10,10);
text(label + ":" + getValue(),locx,locy + 10);
}
void handleInput(){
int dx = (int)(mouseX - (locx + value*100));
int dy = mouseY - locy;
if (dy > 0 && dy < 10){
if (dx > -50 && dx < 50){
value += dx/100.f;
if (value > 1.f) value = 1.f;
if (value < 0.f) value = 0.f;
}
}
 
}
int getValue(){
return (int)(min + value*span);
}
 
}
 
class Toggle extends control{
 
String label = "label";
int value = 0;
int locx, locy;
Toggle(String label, int locx, int locy, int value){
 
this.label = label;
this.locx = locx;
this.locy = locy;
this.value = value;
}
void draw(){
stroke(255);
if (value == 1){
fill(128);
}
else{
fill(0,0,0,0);
}
rect(locx ,locy,100,10);
fill(255);
text(label + ":" + getValue(),locx,locy + 10);
}
void handleInput(){
if (mouseX > locx && mouseX < locx+ 100 && mouseY > locy && mouseY < locy + 10){
toggle();
}
}
int getValue(){
return value;
}
void toggle(){
value = 1-value;
}
 
}
 
 
 
abstract class control{
abstract void draw();
abstract void handleInput();
}
 
public static class ImageSelection implements Transferable {
private Image image;
public ImageSelection(Image image) {
this.image = image;
}
public DataFlavor[] getTransferDataFlavors() {
return new DataFlavor[]{DataFlavor.imageFlavor};
}
public boolean isDataFlavorSupported(DataFlavor flavor) {
return DataFlavor.imageFlavor.equals(flavor);
}
public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException, IOException {
if (!DataFlavor.imageFlavor.equals(flavor)) {
throw new UnsupportedFlavorException(flavor);
}
return image;
}
}