/* * @(#)ColorWheelImageProducer.java * * Copyright (c) 2005-2010 Werner Randelshofer, Immensee, 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 ch.randelshofer.quaqua.colorchooser; import java.awt.*; import java.awt.image.*; /** * Produces the image of a ColorWheel. * * @author Werner Randelshofer * @version $Id: ColorWheelImageProducer.java 363 2010-11-21 17:41:04Z wrandelshofer $ */ public class ColorWheelImageProducer extends MemoryImageSource { protected int[] pixels; protected int w, h; protected float brightness = 1f; protected boolean isDirty = true; /** Lookup table for hues. */ protected float[] hues; /** Lookup table for saturations. */ protected float[] saturations; /** Lookup table for alphas. * The alpha value is used for antialiasing the * color wheel. */ protected int[] alphas; /** Creates a new instance. */ public ColorWheelImageProducer(int w, int h) { super(w, h, null, 0, w); pixels = new int[w*h]; this.w = w; this.h = h; generateLookupTables(); newPixels(pixels, ColorModel.getRGBdefault(), 0, w); setAnimated(true); generateColorWheel(); } public int getRadius() { return Math.min(w, h) / 2 - 2; } protected void generateLookupTables() { saturations = new float[w*h]; hues = new float[w*h]; alphas = new int[w*h]; float radius = getRadius(); // blend is used to create a linear alpha gradient of two extra pixels float blend = (radius + 2f) / radius - 1f; // Center of the color wheel circle int cx = w / 2; int cy = h / 2; for (int x=0; x < w; x++) { int kx = x - cx; // Kartesian coordinates of x int squarekx = kx * kx; // Square of kartesian x for (int y=0; y < h; y++) { int ky = cy - y; // Kartesian coordinates of y int index = x + y * w; saturations[index] = (float) Math.sqrt(squarekx + ky*ky) / radius; if (saturations[index] <= 1f) { alphas[index] = 0xff000000; } else { alphas[index] = (int) ((blend - Math.min(blend,saturations[index] - 1f)) * 255 / blend) << 24; saturations[index] = 1f; } if (alphas[index] != 0) { hues[index] = (float) (Math.atan2(ky, kx) / Math.PI / 2d); } } } } public void setBrightness(float newValue) { isDirty = isDirty || brightness != newValue; brightness = newValue; } public boolean needsGeneration() { return isDirty; } public void regenerateColorWheel() { if (isDirty) { generateColorWheel(); } } public void generateColorWheel() { for (int index=0; index < pixels.length; index++) { if (alphas[index] != 0) { pixels[index] = alphas[index] | 0xffffff & Color.HSBtoRGB(hues[index], saturations[index], brightness); } } newPixels(); isDirty = false; } protected Point getColorLocation(Color c, int width, int height) { float[] hsb = new float[3]; Color.RGBtoHSB(c.getRed(), c.getGreen(), c.getBlue(), hsb); return getColorLocation(hsb[0], hsb[1], hsb[2], width, height); } protected Point getColorLocation(float hue, float saturation, float brightness, int width, int height) { float radius = Math.min(width, height) / 2f; return new Point( width / 2 + (int) (radius * saturation * Math.cos(hue * Math.PI * 2d)), height / 2 - (int) (radius * saturation * Math.sin(hue * Math.PI * 2d))); } protected float[] getColorAt(int x, int y, int width, int height) { x -= width / 2; y -= height / 2; float r = (float) Math.sqrt(x * x + y * y); float theta = (float) Math.atan2(y, -x); float[] hsb = { (float) (0.5 + (theta / Math.PI / 2d)), Math.min(1f, (float) r / getRadius()), brightness }; return hsb; } }