All Downloads are FREE. Search and download functionalities are using the official Maven repository.

eu.hansolo.steelseries.tools.Shadow Maven / Gradle / Ivy

package eu.hansolo.steelseries.tools;

import java.awt.AlphaComposite;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Paint;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.Stroke;
import java.awt.image.BufferedImage;
import java.awt.Transparency;
import java.awt.geom.AffineTransform;
import java.awt.image.Raster;
import java.awt.image.WritableRaster;


/**
 * Definition of methods to create inner shadows and
 * drop shadows for shapes. The syntax of the methods
 * uses the same parameters as Adobe Fireworks.
 * @author hansolo
 */
public enum Shadow {

    INSTANCE;
    private final Util UTIL = Util.INSTANCE;

    /**
     * 

Return a new compatible image that contains the given shape * with the paint, color, stroke etc. that is specified.

* @param SHAPE the shape to create the image fromf * @param PAINT the paint of the shape * @param COLOR the color of the shape * @param FILLED indicator if the shape is filled or not * @param STROKE the stroke of the shape * @param STROKE_COLOR color of the stroke * @return a new compatible image that contains the given shape */ public BufferedImage createImageFromShape(final Shape SHAPE, final Paint PAINT, final Color COLOR, final boolean FILLED, final Stroke STROKE, final Color STROKE_COLOR) { final BufferedImage IMAGE = UTIL.createImage(SHAPE.getBounds().width, SHAPE.getBounds().height, Transparency.TRANSLUCENT); final Graphics2D G2 = IMAGE.createGraphics(); G2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); G2.translate(-SHAPE.getBounds2D().getX(), -SHAPE.getBounds2D().getY()); if (PAINT != null) { G2.setPaint(PAINT); if (FILLED) { G2.fill(SHAPE); } else { G2.draw(SHAPE); } } if (COLOR != null) { G2.setColor(COLOR); if (FILLED) { G2.fill(SHAPE); } else { G2.draw(SHAPE); } } if (STROKE != null) { if (STROKE_COLOR != null) { G2.setColor(STROKE_COLOR); } G2.setStroke(STROKE); if (!FILLED) { G2.draw(SHAPE); } } G2.dispose(); return IMAGE; } public BufferedImage createDropShadow(final BufferedImage SRC_IMAGE, final int DISTANCE, final float ALPHA, final int SOFTNESS, final int ANGLE, final Color SHADOW_COLOR) { final float TRANSLATE_X = (float) (DISTANCE * Math.cos(Math.toRadians(360 - ANGLE))); final float TRANSLATE_Y = (float) (DISTANCE * Math.sin(Math.toRadians(360 - ANGLE))); final BufferedImage SHADOW_IMAGE = renderDropShadow(SRC_IMAGE, SOFTNESS, ALPHA, SHADOW_COLOR); final BufferedImage RESULT = new BufferedImage(SHADOW_IMAGE.getWidth(), SHADOW_IMAGE.getHeight(), BufferedImage.TYPE_INT_ARGB); final Graphics2D G2 = RESULT.createGraphics(); G2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); G2.translate(TRANSLATE_X, TRANSLATE_Y); G2.drawImage(SHADOW_IMAGE, 0, 0, null); G2.translate(-TRANSLATE_X, -TRANSLATE_Y); G2.translate(SOFTNESS, SOFTNESS); G2.drawImage(SRC_IMAGE, 0, 0, null); G2.dispose(); return RESULT; } public BufferedImage createDropShadow(final Shape SHAPE, final Paint PAINT, final Color COLOR, final boolean FILLED, final Stroke STROKE, final Color STROKE_COLOR, final int DISTANCE, final float ALPHA, final int SOFTNESS, final int ANGLE, final Color SHADOW_COLOR) { final float TRANSLATE_X = (float) (DISTANCE * Math.cos(Math.toRadians(360 - ANGLE))); final float TRANSLATE_Y = (float) (DISTANCE * Math.sin(Math.toRadians(360 - ANGLE))); final BufferedImage SHAPE_IMAGE = createImageFromShape(SHAPE, PAINT, COLOR, FILLED, STROKE, STROKE_COLOR); final BufferedImage SHADOW_IMAGE = renderDropShadow(SHAPE_IMAGE, SOFTNESS, ALPHA, SHADOW_COLOR); final BufferedImage RESULT = new BufferedImage(SHADOW_IMAGE.getWidth(), SHADOW_IMAGE.getHeight(), BufferedImage.TYPE_INT_ARGB); final Graphics2D G2 = RESULT.createGraphics(); G2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); G2.translate(TRANSLATE_X, TRANSLATE_Y); G2.drawImage(SHADOW_IMAGE, 0, 0, null); G2.translate(-TRANSLATE_X, -TRANSLATE_Y); G2.translate(SOFTNESS, SOFTNESS); G2.drawImage(SHAPE_IMAGE, 0, 0, null); G2.dispose(); return RESULT; } /** *

Method to create a inner shadow on a given shape

* @param SOFT_CLIP_IMAGE softclipimage create by method createSoftClipImage() * @param SHAPE shape that should get the inner shadow * @param DISTANCE distance of the shadow * @param ALPHA alpha value of the shadow * @param SHADOW_COLOR color of the shadow * @param SOFTNESS softness/fuzzyness of the shadow * @param ANGLE angle under which the shadow should appear * @return IMAGE buffered image that contains the shape including the inner shadow */ public BufferedImage createInnerShadow(final BufferedImage SOFT_CLIP_IMAGE, final Shape SHAPE, final int DISTANCE, final float ALPHA, final Color SHADOW_COLOR, final int SOFTNESS, final int ANGLE) { final float COLOR_CONSTANT = 1f / 255f; final float RED = COLOR_CONSTANT * SHADOW_COLOR.getRed(); final float GREEN = COLOR_CONSTANT * SHADOW_COLOR.getGreen(); final float BLUE = COLOR_CONSTANT * SHADOW_COLOR.getBlue(); final float MAX_STROKE_WIDTH = SOFTNESS * 2; final float ALPHA_STEP = 1f / (2 * SOFTNESS + 2) * ALPHA; final float TRANSLATE_X = (float) (DISTANCE * Math.cos(Math.toRadians(ANGLE))); final float TRANSLATE_Y = (float) (DISTANCE * Math.sin(Math.toRadians(ANGLE))); final Graphics2D G2 = SOFT_CLIP_IMAGE.createGraphics(); // Enable Antialiasing G2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); // Translate the coordinate system to 0,0 G2.translate(-SHAPE.getBounds2D().getX(), -SHAPE.getBounds2D().getY()); // Set the color G2.setColor(new Color(RED, GREEN, BLUE, ALPHA_STEP)); // Set the alpha transparency of the whole image G2.setComposite(AlphaComposite.SrcAtop); // Translate the coordinate system related to the given distance and angle G2.translate(TRANSLATE_X, -TRANSLATE_Y); // Draw the inner shadow for (float strokeWidth = SOFTNESS; strokeWidth >= 1; strokeWidth -= 1) { G2.setStroke(new BasicStroke((float) (MAX_STROKE_WIDTH * Math.pow(0.85, strokeWidth)))); G2.draw(SHAPE); } G2.dispose(); return SOFT_CLIP_IMAGE; } /** *

Method to create a inner shadow on a given shape

* @param SHAPE shape that should get the inner shadow * @param SHAPE_PAINT paint of the given shape * @param DISTANCE distance of the shadow * @param ALPHA alpha value of the shadow * @param SHADOW_COLOR color of the shadow * @param SOFTNESS softness/fuzzyness of the shadow * @param ANGLE angle under which the shadow should appear * @return IMAGE buffered image that contains the shape including the inner shadow */ public BufferedImage createInnerShadow(final Shape SHAPE, final Paint SHAPE_PAINT, final int DISTANCE, final float ALPHA, final Color SHADOW_COLOR, final int SOFTNESS, final int ANGLE) { final float COLOR_CONSTANT = 1f / 255f; final float RED = COLOR_CONSTANT * SHADOW_COLOR.getRed(); final float GREEN = COLOR_CONSTANT * SHADOW_COLOR.getGreen(); final float BLUE = COLOR_CONSTANT * SHADOW_COLOR.getBlue(); final float MAX_STROKE_WIDTH = SOFTNESS * 2; final float ALPHA_STEP = 1f / (2 * SOFTNESS + 2) * ALPHA; final float TRANSLATE_X = (float) (DISTANCE * Math.cos(Math.toRadians(ANGLE))); final float TRANSLATE_Y = (float) (DISTANCE * Math.sin(Math.toRadians(ANGLE))); final BufferedImage IMAGE = createSoftClipImage(SHAPE, SHAPE_PAINT, 0, 0, 0); final Graphics2D G2 = IMAGE.createGraphics(); // Enable Antialiasing G2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); // Translate the coordinate system to 0,0 G2.translate(-SHAPE.getBounds2D().getX(), -SHAPE.getBounds2D().getY()); // Set the color G2.setColor(new Color(RED, GREEN, BLUE, ALPHA_STEP)); // Set the alpha transparency of the whole image G2.setComposite(AlphaComposite.SrcAtop); // Translate the coordinate system related to the given distance and angle G2.translate(TRANSLATE_X, -TRANSLATE_Y); // Draw the inner shadow for (float strokeWidth = SOFTNESS; strokeWidth >= 1; strokeWidth -= 1) { G2.setStroke(new BasicStroke((float) (MAX_STROKE_WIDTH * Math.pow(0.85, strokeWidth)))); G2.draw(SHAPE); } G2.dispose(); return IMAGE; } /** * Method that creates a intermediate image to enable soft clipping functionality * This code was taken from Chris Campbells blog http://weblogs.java.net/blog/campbell/archive/2006/07/java_2d_tricker.html * @param SHAPE * @param SHAPE_PAINT * @return IMAGE buffered image that will be used for soft clipping */ public BufferedImage createSoftClipImage(final Shape SHAPE, final Paint SHAPE_PAINT) { final BufferedImage IMAGE = UTIL.createImage(SHAPE.getBounds().width, SHAPE.getBounds().height, Transparency.TRANSLUCENT); final Graphics2D G2 = IMAGE.createGraphics(); // Clear the image so all pixels have zero alpha G2.setComposite(AlphaComposite.Clear); G2.fillRect(0, 0, IMAGE.getWidth(), IMAGE.getHeight()); // Render our clip shape into the image. Note that we enable // antialiasing to achieve the soft clipping effect. Try // commenting out the line that enables antialiasing, and // you will see that you end up with the usual hard clipping. G2.setComposite(AlphaComposite.Src); G2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); // Set Color or Gradient here if (SHAPE_PAINT != null) { G2.setPaint(SHAPE_PAINT); } // Translate the coordinate system to 0,0 G2.translate(-SHAPE.getBounds2D().getX(), -SHAPE.getBounds2D().getY()); G2.fill(SHAPE); return IMAGE; } /** *

Method that creates a intermediate image to enable soft clipping functionality * This code was taken from Chris Campbells blog http://weblogs.java.net/blog/campbell/archive/2006/07/java_2d_tricker.html

* @param SHAPE * @param SHAPE_PAINT * @param SOFTNESS * @param TRANSLATE_X * @param TRANSLATE_Y * @return IMAGE buffered image that will be used for soft clipping */ public BufferedImage createSoftClipImage(final Shape SHAPE, final Paint SHAPE_PAINT, final int SOFTNESS, final int TRANSLATE_X, final int TRANSLATE_Y) { final BufferedImage IMAGE = UTIL.createImage(SHAPE.getBounds().width + 2 * SOFTNESS + TRANSLATE_X, SHAPE.getBounds().height + 2 * SOFTNESS + TRANSLATE_Y, Transparency.TRANSLUCENT); final Graphics2D G2 = IMAGE.createGraphics(); G2.translate(SOFTNESS / 2.0, SOFTNESS / 2.0); // Clear the image so all pixels have zero alpha G2.setComposite(AlphaComposite.Clear); G2.fillRect(0, 0, IMAGE.getWidth(), IMAGE.getHeight()); // Render our clip shape into the image. Note that we enable // antialiasing to achieve the soft clipping effect. Try // commenting out the line that enables antialiasing, and // you will see that you end up with the usual hard clipping. G2.setComposite(AlphaComposite.Src); G2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); // Set Color or Gradient here if (SHAPE_PAINT != null) { G2.setPaint(SHAPE_PAINT); } // Translate the coordinate system to 0,0 G2.translate(-SHAPE.getBounds2D().getX(), -SHAPE.getBounds2D().getY()); G2.fill(SHAPE); return IMAGE; } /** *

Generates the shadow for a given picture and the current properties * of the renderer.

*

The generated image dimensions are computed as following

*
     * width  = imageWidth  + 2 * shadowSize
     * height = imageHeight + 2 * shadowSize
     * 
* @param IMAGE image the picture from which the shadow must be cast * @param SOFTNESS * @param ALPHA * @param SHADOW_COLOR * @return the picture containing the shadow of image */ public BufferedImage renderDropShadow(final BufferedImage IMAGE, final int SOFTNESS, final float ALPHA, final Color SHADOW_COLOR) { // Written by Sesbastien Petrucci final int SHADOW_SIZE = SOFTNESS * 2; final int SRC_WIDTH = IMAGE.getWidth(); final int SRC_HEIGHT = IMAGE.getHeight(); final int DST_WIDTH = SRC_WIDTH + SHADOW_SIZE; final int DST_HEIGHT = SRC_HEIGHT + SHADOW_SIZE; final int LEFT = SOFTNESS; final int RIGHT = SHADOW_SIZE - LEFT; final int Y_STOP = DST_HEIGHT - RIGHT; final int SHADOW_RGB = SHADOW_COLOR.getRGB() & 0x00FFFFFF; int[] aHistory = new int[SHADOW_SIZE]; int historyIdx; int aSum; final BufferedImage DST = new BufferedImage(DST_WIDTH, DST_HEIGHT, BufferedImage.TYPE_INT_ARGB); int[] dstBuffer = new int[DST_WIDTH * DST_HEIGHT]; int[] srcBuffer = new int[SRC_WIDTH * SRC_HEIGHT]; getPixels(IMAGE, 0, 0, SRC_WIDTH, SRC_HEIGHT, srcBuffer); final int LAST_PIXEL_OFFSET = RIGHT * DST_WIDTH; final float H_SUM_DIVIDER = 1.0f / SHADOW_SIZE; final float V_SUM_DIVIDER = ALPHA / SHADOW_SIZE; int max; int[] hSumLookup = new int[256 * SHADOW_SIZE]; max = hSumLookup.length; for (int i = 0; i < max; i++) { hSumLookup[i] = (int) (i * H_SUM_DIVIDER); } int[] vSumLookup = new int[256 * SHADOW_SIZE]; max = vSumLookup.length; for (int i = 0; i < max; i++) { vSumLookup[i] = (int) (i * V_SUM_DIVIDER); } int srcOffset; // horizontal pass extract the alpha mask from the source picture and // blur it into the destination picture for (int srcY = 0, dstOffset = LEFT * DST_WIDTH; srcY < SRC_HEIGHT; srcY++) { // first pixels are empty for (historyIdx = 0; historyIdx < SHADOW_SIZE;) { aHistory[historyIdx++] = 0; } aSum = 0; historyIdx = 0; srcOffset = srcY * SRC_WIDTH; // compute the blur average with pixels from the source image for (int srcX = 0; srcX < SRC_WIDTH; srcX++) { int a = hSumLookup[aSum]; dstBuffer[dstOffset++] = a << 24; // store the alpha value only // the shadow color will be added in the next pass aSum -= aHistory[historyIdx]; // substract the oldest pixel from the sum // extract the new pixel ... a = srcBuffer[srcOffset + srcX] >>> 24; aHistory[historyIdx] = a; // ... and store its value into history aSum += a; // ... and add its value to the sum if (++historyIdx >= SHADOW_SIZE) { historyIdx -= SHADOW_SIZE; } } // blur the end of the row - no new pixels to grab for (int i = 0; i < SHADOW_SIZE; i++) { final int A = hSumLookup[aSum]; dstBuffer[dstOffset++] = A << 24; // substract the oldest pixel from the sum ... and nothing new to add ! aSum -= aHistory[historyIdx]; if (++historyIdx >= SHADOW_SIZE) { historyIdx -= SHADOW_SIZE; } } } // vertical pass for (int x = 0, bufferOffset = 0; x < DST_WIDTH; x++, bufferOffset = x) { aSum = 0; // first pixels are empty for (historyIdx = 0; historyIdx < LEFT;) { aHistory[historyIdx++] = 0; } // and then they come from the dstBuffer for (int y = 0; y < RIGHT; y++, bufferOffset += DST_WIDTH) { final int A = dstBuffer[bufferOffset] >>> 24; // extract alpha aHistory[historyIdx++] = A; // store into history aSum += A; // and add to sum } bufferOffset = x; historyIdx = 0; // compute the blur avera`ge with pixels from the previous pass for (int y = 0; y < Y_STOP; y++, bufferOffset += DST_WIDTH) { int a = vSumLookup[aSum]; dstBuffer[bufferOffset] = a << 24 | SHADOW_RGB; // store alpha value + shadow color aSum -= aHistory[historyIdx]; // substract the oldest pixel from the sum a = dstBuffer[bufferOffset + LAST_PIXEL_OFFSET] >>> 24; // extract the new pixel ... aHistory[historyIdx] = a; // ... and store its value into history aSum += a; // ... and add its value to the sum if (++historyIdx >= SHADOW_SIZE) { historyIdx -= SHADOW_SIZE; } } // blur the end of the column - no pixels to grab anymore for (int y = Y_STOP; y < DST_HEIGHT; y++, bufferOffset += DST_WIDTH) { final int A = vSumLookup[aSum]; dstBuffer[bufferOffset] = A << 24 | SHADOW_RGB; aSum -= aHistory[historyIdx]; // substract the oldest pixel from the sum if (++historyIdx >= SHADOW_SIZE) { historyIdx -= SHADOW_SIZE; } } } setPixels(DST, 0, 0, DST_WIDTH, DST_HEIGHT, dstBuffer); return DST; } /** *

Returns an array of pixels, stored as integers, from a * BufferedImage. The pixels are grabbed from a rectangular * area defined by a location and two dimensions. Calling this method on * an image of type different from BufferedImage.TYPE_INT_ARGB * and BufferedImage.TYPE_INT_RGB will unmanage the image.

* * @param IMAGE the source image * @param X the x location at which to start grabbing pixels * @param Y the y location at which to start grabbing pixels * @param W the width of the rectangle of pixels to grab * @param H the height of the rectangle of pixels to grab * @param pixels a pre-allocated array of pixels of size w*h; can be null * @return pixels if non-null, a new array of integers * otherwise * @throws IllegalArgumentException is pixels is non-null and * of length < w*h */ public int[] getPixels(final BufferedImage IMAGE, final int X, final int Y, final int W, final int H, int[] pixels) { if (W == 0 || H == 0) { return new int[0]; } if (pixels == null) { pixels = new int[W * H]; } else if (pixels.length < W * H) { throw new IllegalArgumentException("pixels array must have a length " + " >= w*h"); } int imageType = IMAGE.getType(); if (imageType == BufferedImage.TYPE_INT_ARGB || imageType == BufferedImage.TYPE_INT_RGB) { Raster raster = IMAGE.getRaster(); return (int[]) raster.getDataElements(X, Y, W, H, pixels); } // Unmanages the image return IMAGE.getRGB(X, Y, W, H, pixels, 0, W); } /** *

Writes a rectangular area of pixels in the destination * BufferedImage. Calling this method on * an image of type different from BufferedImage.TYPE_INT_ARGB * and BufferedImage.TYPE_INT_RGB will unmanage the image.

* * @param IMAGE the destination image * @param X the x location at which to start storing pixels * @param Y the y location at which to start storing pixels * @param W the width of the rectangle of pixels to store * @param H the height of the rectangle of pixels to store * @param pixels an array of pixels, stored as integers * @throws IllegalArgumentException is pixels is non-null and * of length < w*h */ public void setPixels(final BufferedImage IMAGE, final int X, final int Y, final int W, final int H, int[] pixels) { if (pixels == null || W == 0 || H == 0) { return; } else if (pixels.length < W * H) { throw new IllegalArgumentException("pixels array must have a length" + " >= w*h"); } int imageType = IMAGE.getType(); if (imageType == BufferedImage.TYPE_INT_ARGB || imageType == BufferedImage.TYPE_INT_RGB) { WritableRaster raster = IMAGE.getRaster(); raster.setDataElements(X, Y, W, H, pixels); } else { // Unmanages the image IMAGE.setRGB(X, Y, W, H, pixels, 0, W); } } /** *

Method to create a inner shadow on a given shape

* @param G2 graphics2d object that contains the shape which will get the inner shadow * @param SHAPE shape that should get the inner shadow * @param DISTANCE distance of the shadow * @param ALPHA alpha value of the shadow * @param SHADOW_COLOR color of the shadow * @param SOFTNESS softness/fuzzyness of the shadow * @param ANGLE angle under which the shadow should appear */ public void addInnerShadow(final Graphics2D G2, final Shape SHAPE, final Color SHADOW_COLOR, final int DISTANCE, final float ALPHA, final int SOFTNESS, final int ANGLE) { final float COLOR_CONSTANT = 1f / 255f; final float RED = COLOR_CONSTANT * SHADOW_COLOR.getRed(); final float GREEN = COLOR_CONSTANT * SHADOW_COLOR.getGreen(); final float BLUE = COLOR_CONSTANT * SHADOW_COLOR.getBlue(); final float MAX_STROKE_WIDTH = SOFTNESS * 2; final float ALPHA_STEP = 1f / (2 * SOFTNESS + 2) * ALPHA; final float TRANSLATE_X = (float) (DISTANCE * Math.cos(Math.toRadians(ANGLE))); final float TRANSLATE_Y = (float) (DISTANCE * Math.sin(Math.toRadians(ANGLE))); // Enable Antialiasing G2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); // Store existing parameters final Shape OLD_CLIP = G2.getClip(); final AffineTransform OLD_TRANSFORM = G2.getTransform(); final Stroke OLD_STROKE = G2.getStroke(); final Paint OLD_PAINT = G2.getPaint(); // Set the color G2.setColor(new Color(RED, GREEN, BLUE, ALPHA_STEP)); // Set the alpha transparency of the whole image G2.setComposite(AlphaComposite.SrcAtop); // Translate the coordinate system related to the given distance and angle G2.translate(TRANSLATE_X, -TRANSLATE_Y); G2.setClip(SHAPE); // Draw the inner shadow for (float strokeWidth = SOFTNESS; strokeWidth >= 1; strokeWidth -= 1) { G2.setStroke(new BasicStroke((float) (MAX_STROKE_WIDTH * Math.pow(0.85, strokeWidth)))); G2.draw(SHAPE); } // Restore old parameters G2.setTransform(OLD_TRANSFORM); G2.setClip(OLD_CLIP); G2.setStroke(OLD_STROKE); G2.setPaint(OLD_PAINT); } /** * Adds a simple glow around the given shape * @param G2 * @param CANVAS * @param SHAPE * @param SHAPE_PAINT * @param GLOW_COLOR * @param ALPHA * @param SOFTNESS */ public void addGlow(final Graphics2D G2, final Rectangle CANVAS, final Shape SHAPE, final Paint SHAPE_PAINT, final Color GLOW_COLOR, final float ALPHA, final int SOFTNESS) { final float COLOR_CONSTANT = 1f / 255f; final float RED = COLOR_CONSTANT * GLOW_COLOR.getRed(); final float GREEN = COLOR_CONSTANT * GLOW_COLOR.getGreen(); final float BLUE = COLOR_CONSTANT * GLOW_COLOR.getBlue(); final float MAX_STROKE_WIDTH = SOFTNESS * 2; final float ALPHA_STEP = 1f / (2 * SOFTNESS + 2) * ALPHA; // Enable Antialiasing G2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); // Store existing parameters final Shape OLD_CLIP = G2.getClip(); final AffineTransform OLD_TRANSFORM = G2.getTransform(); final Stroke OLD_STROKE = G2.getStroke(); final Paint OLD_PAINT = G2.getPaint(); // Set the color G2.setColor(new Color(RED, GREEN, BLUE, ALPHA_STEP)); // Draw the inner shadow for (float strokeWidth = SOFTNESS; strokeWidth >= 1; strokeWidth -= 1) { G2.setStroke(new BasicStroke((float) (MAX_STROKE_WIDTH * Math.pow(0.85, strokeWidth)))); G2.draw(SHAPE); } // Restore old parameters G2.setTransform(OLD_TRANSFORM); G2.setClip(OLD_CLIP); G2.setStroke(OLD_STROKE); // Draw the original shape on top G2.setPaint(SHAPE_PAINT); G2.fill(SHAPE); G2.setPaint(OLD_PAINT); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy