Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
package eu.hansolo.steelseries.tools;
import java.awt.Color;
import java.awt.Paint;
import java.awt.PaintContext;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.Transparency;
import java.awt.geom.AffineTransform;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.awt.image.ColorModel;
import java.awt.image.Raster;
import java.awt.image.WritableRaster;
import java.util.HashMap;
import java.util.List;
/**
* A paint class that creates conical gradients around a given center point
* It could be used in the same way as LinearGradientPaint and RadialGradientPaint
* and follows the same syntax.
* You could use floats from 0.0 to 1.0 for the fractions which is standard but it's
* also possible to use angles from 0.0 to 360 degrees which is most of the times
* much easier to handle.
* Gradients always start at the top with a clockwise direction and you could
* rotate the gradient around the center by given offset.
* The offset could also be defined from -0.5 to +0.5 or -180 to +180 degrees.
* If you would like to use degrees instead of values from 0 to 1 you have to use
* the full constructor and set the USE_DEGREES variable to true
* @version 1.0
* @author hansolo
*/
public final class ConicalGradientPaint implements Paint {
private final Point2D CENTER;
private final float[] FRACTION_ANGLES;
private final float[] RED_STEP_LOOKUP;
private final float[] GREEN_STEP_LOOKUP;
private final float[] BLUE_STEP_LOOKUP;
private final float[] ALPHA_STEP_LOOKUP;
private final Color[] COLORS;
private static final float INT_TO_FLOAT_CONST = 1f / 255f;
/**
* Standard constructor which takes the FRACTIONS in values from 0.0f to 1.0f
* @param CENTER
* @param GIVEN_FRACTIONS
* @param GIVEN_COLORS
* @throws IllegalArgumentException
*/
public ConicalGradientPaint(final Point2D CENTER, final float[] GIVEN_FRACTIONS, final Color[] GIVEN_COLORS) throws IllegalArgumentException {
this(false, CENTER, 0.0f, GIVEN_FRACTIONS, GIVEN_COLORS);
}
/**
* Enhanced constructor which takes the FRACTIONS in degress from 0.0f to 360.0f and
* also an GIVEN_OFFSET in degrees around the rotation CENTER
* @param USE_DEGREES
* @param CENTER
* @param GIVEN_OFFSET
* @param GIVEN_FRACTIONS
* @param GIVEN_COLORS
* @throws IllegalArgumentException
*/
public ConicalGradientPaint(final boolean USE_DEGREES, final Point2D CENTER, final float GIVEN_OFFSET, final float[] GIVEN_FRACTIONS, final Color[] GIVEN_COLORS) throws IllegalArgumentException {
// Check that fractions and colors are of the same size
if (GIVEN_FRACTIONS.length != GIVEN_COLORS.length) {
throw new IllegalArgumentException("Fractions and colors must be equal in size");
}
final java.util.List fractionList = new java.util.ArrayList(GIVEN_FRACTIONS.length);
final float OFFSET;
if (USE_DEGREES) {
final float DEG_FRACTION = 1f / 360f;
if (Float.compare((GIVEN_OFFSET * DEG_FRACTION), -0.5f) == 0) {
OFFSET = -0.5f;
} else if (Float.compare((GIVEN_OFFSET * DEG_FRACTION), 0.5f) == 0) {
OFFSET = 0.5f;
} else {
OFFSET = (GIVEN_OFFSET * DEG_FRACTION);
}
for (final float fraction : GIVEN_FRACTIONS) {
fractionList.add((fraction * DEG_FRACTION));
}
} else {
// Now it seems to work with rotation of 0.5f, below is the old code to correct the problem
// if (GIVEN_OFFSET == -0.5)
// {
// // This is needed because of problems in the creation of the Raster
// // with a angle offset of exactly -0.5
// OFFSET = -0.49999f;
// }
// else if (GIVEN_OFFSET == 0.5)
// {
// // This is needed because of problems in the creation of the Raster
// // with a angle offset of exactly +0.5
// OFFSET = 0.499999f;
// }
// else
{
OFFSET = GIVEN_OFFSET;
}
for (final float fraction : GIVEN_FRACTIONS) {
fractionList.add(fraction);
}
}
// Check for valid offset
if (OFFSET > 0.5f || OFFSET < -0.5f) {
throw new IllegalArgumentException("Offset has to be in the range of -0.5 to 0.5");
}
// Adjust fractions and colors array in the case where startvalue != 0.0f and/or endvalue != 1.0f
final java.util.List colorList = new java.util.ArrayList(GIVEN_COLORS.length);
colorList.addAll(java.util.Arrays.asList(GIVEN_COLORS));
// Assure that fractions start with 0.0f
if (fractionList.get(0) != 0.0f) {
fractionList.add(0, 0.0f);
final Color TMP_COLOR = colorList.get(0);
colorList.add(0, TMP_COLOR);
}
// Assure that fractions end with 1.0f
if (fractionList.get(fractionList.size() - 1) != 1.0f) {
fractionList.add(1.0f);
colorList.add(GIVEN_COLORS[0]);
}
// Recalculate the fractions and colors with the given offset
final java.util.Map fractionColors = recalculate(fractionList, colorList, OFFSET);
// Clear the original FRACTION_LIST and COLOR_LIST
fractionList.clear();
colorList.clear();
// Sort the hashmap by fraction and add the values to the FRACION_LIST and COLOR_LIST
final java.util.SortedSet sortedFractions = new java.util.TreeSet(fractionColors.keySet());
for (final Float CURRENT_FRACTION : sortedFractions) {
fractionList.add(CURRENT_FRACTION);
colorList.add(fractionColors.get(CURRENT_FRACTION));
}
// Set the values
this.CENTER = CENTER;
COLORS = colorList.toArray(new Color[colorList.size()]);
// Prepare lookup table for the angles of each fraction
final int MAX_FRACTIONS = fractionList.size();
this.FRACTION_ANGLES = new float[MAX_FRACTIONS];
for (int i = 0; i < MAX_FRACTIONS; i++) {
FRACTION_ANGLES[i] = fractionList.get(i) * 360f;
}
// Prepare lookup tables for the color stepsize of each color
RED_STEP_LOOKUP = new float[COLORS.length];
GREEN_STEP_LOOKUP = new float[COLORS.length];
BLUE_STEP_LOOKUP = new float[COLORS.length];
ALPHA_STEP_LOOKUP = new float[COLORS.length];
for (int i = 0; i < (COLORS.length - 1); i++) {
RED_STEP_LOOKUP[i] = ((COLORS[i + 1].getRed() - COLORS[i].getRed()) * INT_TO_FLOAT_CONST) / (FRACTION_ANGLES[i + 1] - FRACTION_ANGLES[i]);
GREEN_STEP_LOOKUP[i] = ((COLORS[i + 1].getGreen() - COLORS[i].getGreen()) * INT_TO_FLOAT_CONST) / (FRACTION_ANGLES[i + 1] - FRACTION_ANGLES[i]);
BLUE_STEP_LOOKUP[i] = ((COLORS[i + 1].getBlue() - COLORS[i].getBlue()) * INT_TO_FLOAT_CONST) / (FRACTION_ANGLES[i + 1] - FRACTION_ANGLES[i]);
ALPHA_STEP_LOOKUP[i] = ((COLORS[i + 1].getAlpha() - COLORS[i].getAlpha()) * INT_TO_FLOAT_CONST) / (FRACTION_ANGLES[i + 1] - FRACTION_ANGLES[i]);
}
}
/**
* Recalculates the fractions in the FRACTION_LIST and their associated colors in the COLOR_LIST with a given OFFSET.
* Because the conical gradients always starts with 0 at the top and clockwise direction
* you could rotate the defined conical gradient from -180 to 180 degrees which equals values from -0.5 to +0.5
* @param fractionList
* @param colorList
* @param OFFSET
* @return Hashmap that contains the recalculated fractions and colors after a given rotation
*/
private java.util.HashMap recalculate(final List fractionList, final List colorList, final float OFFSET) {
// Recalculate the fractions and colors with the given offset
final int MAX_FRACTIONS = fractionList.size();
final HashMap fractionColors = new HashMap(MAX_FRACTIONS);
for (int i = 0; i < MAX_FRACTIONS; i++) {
// Add offset to fraction
final float TMP_FRACTION = fractionList.get(i) + OFFSET;
// Color related to current fraction
final Color TMP_COLOR = colorList.get(i);
// Check each fraction for limits (0...1)
if (TMP_FRACTION <= 0) {
fractionColors.put(1.0f + TMP_FRACTION + 0.0001f, TMP_COLOR);
final float NEXT_FRACTION;
final Color NEXT_COLOR;
if (i < MAX_FRACTIONS - 1) {
NEXT_FRACTION = fractionList.get(i + 1) + OFFSET;
NEXT_COLOR = colorList.get(i + 1);
} else {
NEXT_FRACTION = 1 - fractionList.get(0) + OFFSET;
NEXT_COLOR = colorList.get(0);
}
if (NEXT_FRACTION > 0) {
final Color NEW_FRACTION_COLOR = getColorFromFraction(TMP_COLOR, NEXT_COLOR, (int) ((NEXT_FRACTION - TMP_FRACTION) * 10000), (int) ((-TMP_FRACTION) * 10000));
fractionColors.put(0.0f, NEW_FRACTION_COLOR);
fractionColors.put(1.0f, NEW_FRACTION_COLOR);
}
} else if (TMP_FRACTION >= 1) {
fractionColors.put(TMP_FRACTION - 1.0f - 0.0001f, TMP_COLOR);
final float PREVIOUS_FRACTION;
final Color PREVIOUS_COLOR;
if (i > 0) {
PREVIOUS_FRACTION = fractionList.get(i - 1) + OFFSET;
PREVIOUS_COLOR = colorList.get(i - 1);
} else {
PREVIOUS_FRACTION = fractionList.get(MAX_FRACTIONS - 1) + OFFSET;
PREVIOUS_COLOR = colorList.get(MAX_FRACTIONS - 1);
}
if (PREVIOUS_FRACTION < 1) {
final Color NEW_FRACTION_COLOR = getColorFromFraction(TMP_COLOR, PREVIOUS_COLOR, (int) ((TMP_FRACTION - PREVIOUS_FRACTION) * 10000), (int) (TMP_FRACTION - 1.0f) * 10000);
fractionColors.put(1.0f, NEW_FRACTION_COLOR);
fractionColors.put(0.0f, NEW_FRACTION_COLOR);
}
} else {
fractionColors.put(TMP_FRACTION, TMP_COLOR);
}
}
// Clear the original FRACTION_LIST and COLOR_LIST
fractionList.clear();
colorList.clear();
return fractionColors;
}
/**
* With the START_COLOR at the beginning and the DESTINATION_COLOR at the end of the given RANGE the method will calculate
* and return the color that equals the given VALUE.
* e.g. a START_COLOR of BLACK (R:0, G:0, B:0, A:255) and a DESTINATION_COLOR of WHITE(R:255, G:255, B:255, A:255)
* with a given RANGE of 100 and a given VALUE of 50 will return the color that is exactly in the middle of the
* gradient between black and white which is gray(R:128, G:128, B:128, A:255)
* So this method is really useful to calculate colors in gradients between two given colors.
* @param START_COLOR
* @param DESTINATION_COLOR
* @param RANGE
* @param VALUE
* @return Color calculated from a range of values by given value
*/
public static Color getColorFromFraction(final Color START_COLOR, final Color DESTINATION_COLOR, final int RANGE, final int VALUE) {
final float SOURCE_RED = START_COLOR.getRed() * INT_TO_FLOAT_CONST;
final float SOURCE_GREEN = START_COLOR.getGreen() * INT_TO_FLOAT_CONST;
final float SOURCE_BLUE = START_COLOR.getBlue() * INT_TO_FLOAT_CONST;
final float SOURCE_ALPHA = START_COLOR.getAlpha() * INT_TO_FLOAT_CONST;
final float DESTINATION_RED = DESTINATION_COLOR.getRed() * INT_TO_FLOAT_CONST;
final float DESTINATION_GREEN = DESTINATION_COLOR.getGreen() * INT_TO_FLOAT_CONST;
final float DESTINATION_BLUE = DESTINATION_COLOR.getBlue() * INT_TO_FLOAT_CONST;
final float DESTINATION_ALPHA = DESTINATION_COLOR.getAlpha() * INT_TO_FLOAT_CONST;
final float RED_DELTA = DESTINATION_RED - SOURCE_RED;
final float GREEN_DELTA = DESTINATION_GREEN - SOURCE_GREEN;
final float BLUE_DELTA = DESTINATION_BLUE - SOURCE_BLUE;
final float ALPHA_DELTA = DESTINATION_ALPHA - SOURCE_ALPHA;
final float RED_FRACTION = RED_DELTA / RANGE;
final float GREEN_FRACTION = GREEN_DELTA / RANGE;
final float BLUE_FRACTION = BLUE_DELTA / RANGE;
final float ALPHA_FRACTION = ALPHA_DELTA / RANGE;
float red = SOURCE_RED + RED_FRACTION * VALUE;
float green = SOURCE_GREEN + GREEN_FRACTION * VALUE;
float blue = SOURCE_BLUE + BLUE_FRACTION * VALUE;
float alpha = SOURCE_ALPHA + ALPHA_FRACTION * VALUE;
red = red < 0f ? 0f : (red > 1f ? 1f : red);
green = green < 0f ? 0f : (green > 1f ? 1f : green);
blue = blue < 0f ? 0f : (blue > 1f ? 1f : blue);
alpha = alpha < 0f ? 0f : (alpha > 1f ? 1f : alpha);
return new Color(red, green, blue, alpha);
}
@Override
public java.awt.PaintContext createContext(final ColorModel COLOR_MODEL,
final Rectangle DEVICE_BOUNDS,
final Rectangle2D USER_BOUNDS,
final AffineTransform TRANSFORM,
final RenderingHints HINTS) {
final Point2D TRANSFORMED_CENTER = TRANSFORM.transform(CENTER, null);
return new ConicalGradientPaintContext(TRANSFORMED_CENTER);
}
@Override
public int getTransparency() {
return Transparency.TRANSLUCENT;
}
private final class ConicalGradientPaintContext implements PaintContext {
final private Point2D CENTER;
public ConicalGradientPaintContext(final Point2D CENTER) {
this.CENTER = new Point2D.Double(CENTER.getX(), CENTER.getY());
}
@Override
public void dispose() {
}
@Override
public java.awt.image.ColorModel getColorModel() {
return ColorModel.getRGBdefault();
}
@Override
public Raster getRaster(final int X, final int Y, final int TILE_WIDTH, final int TILE_HEIGHT) {
final double ROTATION_CENTER_X = -X + CENTER.getX();
final double ROTATION_CENTER_Y = -Y + CENTER.getY();
final int MAX = FRACTION_ANGLES.length - 1;
// Create raster for given colormodel
final WritableRaster RASTER = getColorModel().createCompatibleWritableRaster(TILE_WIDTH, TILE_HEIGHT);
// Create data array with place for red, green, blue and alpha values
final int[] data = new int[(TILE_WIDTH * TILE_HEIGHT * 4)];
double dx;
double dy;
double distance;
double angle;
double currentRed = 0;
double currentGreen = 0;
double currentBlue = 0;
double currentAlpha = 0;
for (int tileY = 0; tileY < TILE_HEIGHT; tileY++) {
for (int tileX = 0; tileX < TILE_WIDTH; tileX++) {
// Calculate the distance between the current position and the rotation angle
dx = tileX - ROTATION_CENTER_X;
dy = tileY - ROTATION_CENTER_Y;
distance = Math.sqrt(dx * dx + dy * dy);
// Avoid division by zero
if (distance == 0) {
distance = 1;
}
// 0 degree on top
angle = Math.abs(Math.toDegrees(Math.acos(dx / distance)));
if (dx >= 0 && dy <= 0) {
angle = 90.0 - angle;
} else if (dx >= 0 && dy >= 0) {
angle += 90.0;
} else if (dx <= 0 && dy >= 0) {
angle += 90.0;
} else if (dx <= 0 && dy <= 0) {
angle = 450.0 - angle;
}
// Check for each angle in fractionAngles array
for (int i = 0; i < MAX; i++) {
if ((angle >= FRACTION_ANGLES[i])) {
currentRed = COLORS[i].getRed() * INT_TO_FLOAT_CONST + (angle - FRACTION_ANGLES[i]) * RED_STEP_LOOKUP[i];
currentGreen = COLORS[i].getGreen() * INT_TO_FLOAT_CONST + (angle - FRACTION_ANGLES[i]) * GREEN_STEP_LOOKUP[i];
currentBlue = COLORS[i].getBlue() * INT_TO_FLOAT_CONST + (angle - FRACTION_ANGLES[i]) * BLUE_STEP_LOOKUP[i];
currentAlpha = COLORS[i].getAlpha() * INT_TO_FLOAT_CONST + (angle - FRACTION_ANGLES[i]) * ALPHA_STEP_LOOKUP[i];
}
}
// Fill data array with calculated color values
final int BASE = (tileY * TILE_WIDTH + tileX) * 4;
data[BASE] = (int) (currentRed * 255);
data[BASE + 1] = (int) (currentGreen * 255);
data[BASE + 2] = (int) (currentBlue * 255);
data[BASE + 3] = (int) (currentAlpha * 255);
}
}
// Fill the raster with the data
RASTER.setPixels(0, 0, TILE_WIDTH, TILE_HEIGHT, data);
return RASTER;
}
}
}