package org.sunflow.image; public final class ColorFactory { /** * Return the name of the internal color space. This string can be used * interchangeably with * null in the following methods. * * @return internal colorspace name */ public static String getInternalColorspace() { return "sRGB linear"; } /** * Checks to see how many values are required to specify a color using the * given colorspace. This number can be variable for spectrum colors, in * which case the returned value is -1. If the colorspace name is invalid, * this method returns -2. No exception is thrown. This method is intended * for parsers that want to know how many floating values to retrieve from a * file. * * @param colorspace * @return number of floating point numbers expected, -1 for any, -2 on * error */ public static int getRequiredDataValues(String colorspace) { if (colorspace == null) { return 3; } if (colorspace.equals("sRGB nonlinear")) { return 3; } else if (colorspace.equals("sRGB linear")) { return 3; } else if (colorspace.equals("XYZ")) { return 3; } else if (colorspace.equals("blackbody")) { return 1; } else if (colorspace.startsWith("spectrum")) { return -1; } else { return -2; } } /** * Creates a color value in the renderer's internal color space from a * string (representing the color space name) and an array of floating point * values. If the colorspace string is null, we assume the data was supplied * in internal space. This method does much error checking and may throw a * {@link RuntimeException} if its parameters are not consistent. Here are * the currently supported color spaces: * * * @param colorspace color space name * @param data data describing this color * @return a valid color in the renderer's color space * @throws ColorSpecificationException */ public static Color createColor(String colorspace, float... data) throws ColorSpecificationException { int required = getRequiredDataValues(colorspace); if (required == -2) { throw new ColorSpecificationException("unknown colorspace %s"); } if (required != -1 && required != data.length) { throw new ColorSpecificationException(required, data.length); } if (colorspace == null) { return new Color(data[0], data[1], data[2]); } else if (colorspace.equals("sRGB nonlinear")) { return new Color(data[0], data[1], data[2]).toLinear(); } else if (colorspace.equals("sRGB linear")) { return new Color(data[0], data[1], data[2]); } else if (colorspace.equals("XYZ")) { return RGBSpace.SRGB.convertXYZtoRGB(new XYZColor(data[0], data[1], data[2])); } else if (colorspace.equals("blackbody")) { return RGBSpace.SRGB.convertXYZtoRGB(new BlackbodySpectrum(data[0]).toXYZ()); } else if (colorspace.startsWith("spectrum")) { String[] tokens = colorspace.split("\\s+"); if (tokens.length != 3) { throw new ColorSpecificationException("invalid spectrum specification"); } if (data.length == 0) { throw new ColorSpecificationException("missing spectrum data"); } try { float lambdaMin = Float.parseFloat(tokens[1]); float lambdaMax = Float.parseFloat(tokens[2]); return RGBSpace.SRGB.convertXYZtoRGB(new RegularSpectralCurve(data, lambdaMin, lambdaMax).toXYZ()); } catch (NumberFormatException e) { throw new ColorSpecificationException("unable to parse spectrum wavelength range"); } } throw new ColorSpecificationException(String.format("Inconsistent code! Please report this error. (Input %s - %d)", colorspace, data.length)); } @SuppressWarnings("serial") public static final class ColorSpecificationException extends Exception { private ColorSpecificationException() { super("Invalid color specification"); } private ColorSpecificationException(String message) { super(String.format("Invalid color specification: %s", message)); } private ColorSpecificationException(int expected, int found) { this(String.format("invalid data length, expecting %d values, found %d", expected, found)); } } }