/* * @(#)ColorWheelImageProducer.java * * Copyright (c) 2008 The authors and contributors of JHotDraw. * You may not use, copy or modify this file, except in compliance with the * accompanying license terms. */ package org.jhotdraw.color; import java.awt.*; import java.awt.color.ColorSpace; import java.awt.geom.Point2D; import static java.lang.Math.*; /** * Produces the image of a {@link JColorWheel} by interpreting two components of * a {@code ColorSpace} as complex numbers (real and imaginary). * * * @see JColorWheel * * @author Werner Randelshofer * @version $Id: ColorWheelImageProducer.java 527 2009-06-07 14:28:19Z rawcoder * $ */ public class ComplexColorWheelImageProducer extends AbstractColorWheelImageProducer { /** * Lookup table for angular component values. */ protected float[] angulars; /** * Lookup table for radial component values. */ protected float[] radials; /** * Lookup table for alphas. The alpha value is used for antialiasing the * color wheel. */ protected int[] alphas; private boolean flipX, flipY; /** * Creates a new instance. */ public ComplexColorWheelImageProducer(ColorSpace sys, int w, int h) { this(sys, w, h, false, false); } /** * Creates a new instance. */ public ComplexColorWheelImageProducer(ColorSpace sys, int w, int h, boolean flipX, boolean flipY) { super(sys, w, h); this.flipX = flipX; this.flipY = flipY; } protected void generateLookupTables() { radials = new float[w * h]; angulars = new float[w * h]; alphas = new int[w * h]; float radius = getRadius(); Point2D.Float center = getCenter(); // 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 float maxR = colorSpace.getMaxValue(radialIndex); float minR = colorSpace.getMinValue(radialIndex); float extentR = maxR - minR; float maxA = colorSpace.getMaxValue(angularIndex); float minA = colorSpace.getMinValue(angularIndex); float extentA = maxA - minA; int side = Math.min(w, h); // side length float cx = center.x; float cy = center.y; float extentX = side - 1; float extentY = extentX; for (int x = 0; x < w; x++) { float kx = (x - cx) / radius; if (flipX) { kx = -kx; } float squarekx = kx * kx; for (int y = 0; y < h; y++) { float ky = (y - cy) / radius; if (flipY) { ky = -ky; } int index = x + y * w; float r = (float) Math.sqrt(squarekx + ky * ky); if (r <= 1f) { alphas[index] = 0xff000000; //radials[index] = radiusRatio; } else { alphas[index] = (int) ((blend - Math.min(blend, r - 1f)) * 255 / blend) << 24; //radials[index] = maxR; } if (alphas[index] != 0) { //angulars[index] = (float) (Math.atan2(ky, kx)); } double angle = atan2(ky, kx); // distort from disk to box float scale = (float) max(abs(sin(angle)), abs(cos(angle))); // we don't want too much distortion at the center of the disk scale = (1 - r) + scale * r; // perform distortion radials[index] = max(minR,min((kx / scale + 1) / 2 * extentR + minR,maxR)); angulars[index] = max(minA,min((ky / scale + 1) / 2 * extentA + minA,maxA)); } } isLookupValid = true; } @Override public boolean needsGeneration() { return !isPixelsValid; } @Override public void regenerateColorWheel() { if (!isPixelsValid) { generateColorWheel(); } } @Override public void generateColorWheel() { if (!isLookupValid) { generateLookupTables(); } float[] components = new float[colorSpace.getNumComponents()]; float[] rgb=new float[3]; for (int index = 0; index < pixels.length; index++) { if (alphas[index] != 0) { components[angularIndex] = angulars[index]; components[radialIndex] = radials[index]; components[verticalIndex] = verticalValue; pixels[index] = alphas[index] | 0xffffff & ColorUtil.CStoRGB24(colorSpace, components,rgb); } } newPixels(); isPixelsValid = true; } @Override public Point getColorLocation(float[] components) { float radius = getRadius(); Point2D.Float center = getCenter(); float radial = (components[radialIndex] - colorSpace.getMinValue(radialIndex))// / (colorSpace.getMaxValue(radialIndex) - colorSpace.getMinValue(radialIndex)) * 2 - 1; float angular = (components[angularIndex] - colorSpace.getMinValue(angularIndex))// / (colorSpace.getMaxValue(angularIndex) - colorSpace.getMinValue(angularIndex)) * 2 - 1; if (flipX) { radial = -radial; } if (flipY) { angular = -angular; } radial = max(-1, min(radial, 1)); angular = max(-1, min(angular, 1)); double a = atan2(radial, angular); double sina = sin(a); double cosa = cos(a); double d = max(abs(sina), abs(cosa)); double dx = (abs(sina) > abs(cosa)) ? sina : cosa; double bx = (abs(sina) > abs(cosa)) ? radial : angular; double r; if (d == 1 && dx != 0) { r = bx / dx; } else { r = (d * dx - sqrt(d * dx * (-4 * bx * d + 4 * bx + d * dx))) / (2 * (d - 1) * dx); if (r < 0) { r = (d * dx + sqrt(d * dx * (-4 * bx * d + 4 * bx + d * dx))) / (2 * (d - 1) * dx); } } Point p = new Point( (int) (r * sina * radius + center.x), (int) (r * cosa * radius + center.y)// ); return p; } @Override public float[] getColorAt(int x, int y) { float radius = getRadius(); Point2D.Float center = getCenter(); float maxR = colorSpace.getMaxValue(radialIndex); float minR = colorSpace.getMinValue(radialIndex); float extentR = maxR - minR; float maxA = colorSpace.getMaxValue(angularIndex); float minA = colorSpace.getMinValue(angularIndex); float extentA = maxA - minA; int side = Math.min(w, h); // side length float cx = center.x; float cy = center.y; float extentX = side - 1; float extentY = extentX; float radial, angular; float kx = (x - cx) / radius; if (flipX) { kx = -kx; } float squarekx = kx * kx; float ky = (y - cy) / radius; if (flipY) { ky = -ky; } int index = x + y * w; float r = (float) Math.sqrt(squarekx + ky * ky); double angle = atan2(ky, kx); // distort from disk to box float scale = (float) max(abs(sin(angle)), abs(cos(angle))); // we don't want too much distortion at the center of the disk scale = (1 - r) + scale * r; // perform distortion radial = (kx / scale + 1) / 2 * extentR + minR; angular = (ky / scale + 1) / 2 * extentA + minA; float[] rav = new float[3]; rav[angularIndex] = angular; rav[radialIndex] = radial; rav[verticalIndex] = verticalValue; return rav; } public float[] getColorAtOld(int x, int y) { int side = Math.min(w - 1, h - 1); // side length int xOffset = (w - side) / 2; int yOffset = (h - side) / 2; float radial = (x - xOffset) / (float) side; float angular = (y - yOffset) / (float) side; if (flipX) { radial = 1f - radial; } if (!flipY) { angular = 1f - angular; } float[] rav = new float[3]; rav[angularIndex] = angular// * (colorSpace.getMaxValue(angularIndex) - colorSpace.getMinValue(angularIndex))// + colorSpace.getMinValue(angularIndex); rav[radialIndex] = radial// * (colorSpace.getMaxValue(radialIndex) - colorSpace.getMinValue(radialIndex))// + colorSpace.getMinValue(radialIndex); rav[verticalIndex] = verticalValue; int xy = x + y * w; System.out.println("ComplexColorWheelImageProducer.getColorAt " + rav[angularIndex] + "," + rav[radialIndex] + " ~ " + angulars[xy] + "," + radials[xy]); rav[angularIndex] = angulars[xy]; rav[radialIndex] = radials[xy]; Point p = getColorLocation(rav); System.out.println("ComplexColorWheelImageProducer.getColorAt( " + x + "," + y + " => " + p.x + "," + p.y); return rav; } }