/**
* Palette.java
*
* Copyright (c) 2013-2016, F(X)yz
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of F(X)yz, any associated website, nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL F(X)yz BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.fxyz3d.scene.paint;
import java.io.File;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.DoubleStream;
import java.util.stream.IntStream;
import javafx.embed.swing.SwingFXUtils;
import javafx.scene.image.Image;
import javafx.scene.image.PixelWriter;
import javafx.scene.image.WritableImage;
import javafx.scene.paint.Color;
import javax.imageio.ImageIO;
/**
*
* @author jpereda
*/
public class Palette {
public enum ColorPalette {
HSB,GREEN
}
private final List<Color> GREEN_COLORS = Arrays.asList(Color.rgb(0,0,0,1),Color.rgb(30,49,29,1),
Color.rgb(35,80,33,1),Color.rgb(56,122,54,1),Color.rgb(45,187,40,1),Color.rgb(8,231,0,1));
private final int numColors;
private final ColorPalette colorPalette;
private int width;
private int height;
private Image imgPalette;
private final static ColorPalette DEFAULT_COLOR_PALETTE = ColorPalette.HSB;
private final static int DEFAULT_NUMCOLORS = 10000; // 100x100 palette image
public Palette(){
this(DEFAULT_NUMCOLORS, DEFAULT_COLOR_PALETTE);
}
public Palette(int numColors){
this(numColors, DEFAULT_COLOR_PALETTE);
}
public Palette(int numColors, ColorPalette colorPalette){
this.numColors=numColors;
this.colorPalette = colorPalette;
}
public Image createPalette(boolean save){
if(numColors<1){
return null;
}
// try to create a square image
width=(int)Math.sqrt(numColors);
height=numColors/width;
imgPalette = new WritableImage(width, height);
PixelWriter pw = ((WritableImage)imgPalette).getPixelWriter();
AtomicInteger count = new AtomicInteger();
IntStream.range(0, height).boxed()
.forEach(y->IntStream.range(0, width).boxed()
.forEach(x->pw.setColor(x, y, getColor(count.getAndIncrement()))));
if(save){
saveImage();
}
return imgPalette;
}
public DoubleStream getTextureLocation(int iPoint){
if(width==0 || height==0){
return DoubleStream.of(0f,0f);
}
int y = iPoint/width;
int x = iPoint-width*y;
// add 0.5 to interpolate colors from the middle of the pixel
return DoubleStream.of((((float)x+0.5f)/((float)width)),(((float)y+0.5f)/((float)height)));
}
private void saveImage(){
try {
// save
ImageIO.write(SwingFXUtils.fromFXImage(imgPalette, null), "png", new File("palette_"+numColors+".png"));
} catch (IOException ex) {
System.out.println("Error saving image");
}
}
/*
int iColor [0-numColors]
*/
private Color getColor(int iColor){
double fact=(double) (iColor) / (double) numColors;
switch(colorPalette){
case HSB:
// if (iColor == 0) {
// return Color.TRANSPARENT;
// }
// There are 6*255=1530 distinct pure colors, 255 colors every 60ยบ, with 100% saturation and value
return Color.hsb(360d*fact, 1d, 1d);
case GREEN:
return GREEN_COLORS.get((int)(fact*GREEN_COLORS.size()));
}
return Color.rgb((iColor >> 16) & 0xFF, (iColor >> 8) & 0xFF, iColor & 0xFF);
}
public int getNumColors() {
return numColors;
}
public int getWidth() {
return width;
}
public int getHeight() {
return height;
}
public Image getPaletteImage() {
return imgPalette;
}
public ColorPalette getColorPalette(){
return colorPalette;
}
}