package net.xoetrope.swing.util;
import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.awt.image.ConvolveOp;
import java.awt.image.DataBufferInt;
import java.awt.image.Kernel;
import javax.swing.Timer;
import javax.swing.JComponent;
public class XGraphicsUtils
{
protected int shadowSize = 5;
protected float shadowOpacity = 0.5f;
protected Color shadowColor = new Color(0x000000);
public BufferedImage createShadow(BufferedImage original, int shadowSize)
{
int shadowWidth = original.getWidth() + shadowSize * 2;
int shadowHeight = original.getHeight() + shadowSize * 2;
BufferedImage shadow = new BufferedImage(shadowWidth, shadowHeight, BufferedImage.TYPE_INT_ARGB);
// todo : copy the image as part of the drop shadow computing ... should be a 3x extra boost on my machine !!
Graphics2D g = shadow.createGraphics();
g.drawImage(original, shadowSize, shadowSize, null);
g.dispose();
applyShadow(shadow, shadowSize);
return shadow;
}
public void applyShadow(BufferedImage dst, int shadowSize)
{
int dstWidth = dst.getWidth();
int dstHeight = dst.getHeight();
int left = (shadowSize - 1) >> 1;
int right = shadowSize - left;
int xStart = left;
int xStop = dstWidth - right;
int yStart = left;
int yStop = dstHeight - right;
int shadowRgb = shadowColor.getRGB() & 0x00FFFFFF;
int[] aHistory = new int[shadowSize];
int historyIdx = 0;
int aSum;
int[] dataBuffer = ((DataBufferInt)dst.getRaster().getDataBuffer()).getData();
int lastPixelOffset = right * dstWidth;
float sumDivider = shadowOpacity / shadowSize;
// horizontal pass
for (int y = 0, bufferOffset = 0; y < dstHeight; y++, bufferOffset = y * dstWidth) {
aSum = 0;
historyIdx = 0;
for (int x = 0; x < shadowSize; x++, bufferOffset++) {
int a = dataBuffer[bufferOffset] >>> 24;
aHistory[x] = a;
aSum += a;
}
bufferOffset -= right;
for (int x = xStart; x < xStop; x++, bufferOffset++) {
int a = (int)(aSum * sumDivider);
dataBuffer[bufferOffset] = a << 24 | shadowRgb;
// substract the oldest pixel from the sum
aSum -= aHistory[historyIdx];
// get the lastest pixel
a = dataBuffer[bufferOffset + right] >>> 24;
aHistory[historyIdx] = a;
aSum += a;
if (++historyIdx >= shadowSize)
historyIdx -= shadowSize;
}
}
// vertical pass
for (int x = 0, bufferOffset = 0; x < dstWidth; x++, bufferOffset = x) {
aSum = 0;
historyIdx = 0;
for (int y = 0; y < shadowSize; y++, bufferOffset += dstWidth) {
int a = dataBuffer[bufferOffset] >>> 24;
aHistory[y] = a;
aSum += a;
}
bufferOffset -= lastPixelOffset;
for (int y = yStart; y < yStop; y++, bufferOffset += dstWidth) {
int a = (int)(aSum * sumDivider);
dataBuffer[bufferOffset] = a << 24 | shadowRgb;
// substract the oldest pixel from the sum
aSum -= aHistory[historyIdx];
// get the lastest pixel
a = dataBuffer[bufferOffset + lastPixelOffset] >>> 24;
aHistory[historyIdx] = a;
aSum += a;
if (++historyIdx >= shadowSize)
historyIdx -= shadowSize;
}
}
}
public BufferedImage createDropShadow(BufferedImage image)
{
if ( image != null ) {
BufferedImage shadow = new BufferedImage(image.getWidth(),
image.getHeight(),
BufferedImage.TYPE_INT_ARGB);
BufferedImage shadowMask = createShadowMask( image );
getBlurOp( shadowSize ).filter( shadowMask, shadow );
return shadow;
}
return null;
}
public void applyBlur(BufferedImage src, BufferedImage dst, int shadowSize)
{
if (shadowSize == 1) {
Graphics2D g = dst.createGraphics();
g.drawImage(src, 0, 0, null);
g.dispose();
return;
}
int srcWidth = src.getWidth();
int srcHeight = src.getHeight();
int left = (shadowSize - 1) >> 1;
int right = shadowSize - left;
int xStart = left;
int xStop = srcWidth - right;
int yStart = left;
int yStop = srcHeight - right;
int[] aHistory = new int[shadowSize];
int[] rHistory = new int[shadowSize];
int[] vHistory = new int[shadowSize];
int[] bHistory = new int[shadowSize];
int historyIdx = 0;
int aSum;
int rSum;
int vSum;
int bSum;
// horizontal pass
for (int y = 0; y < srcHeight; y++) {
aSum = 0;
rSum = 0;
vSum = 0;
bSum = 0;
historyIdx = 0;
for (int x = 0; x < shadowSize; x++) {
int argb = src.getRGB(x, y);
int a = (argb & 0xFF000000) >>> 24;
int r = (argb & 0x00FF0000) >>> 16;
int v = (argb & 0x0000FF00) >>> 8;
int b = (argb & 0x000000FF);
aHistory[x] = a;
rHistory[x] = r;
vHistory[x] = v;
bHistory[x] = b;
aSum += a;
rSum += r;
vSum += v;
bSum += b;
}
for (int x = xStart; x < xStop; x++) {
int a = aSum / shadowSize;
int r = rSum / shadowSize;
int v = vSum / shadowSize;
int b = bSum / shadowSize;
dst.setRGB(x, y, a << 24 | r << 16 | v << 8 | b);
// substract the oldest pixel from the sum
aSum -= aHistory[historyIdx];
rSum -= rHistory[historyIdx];
vSum -= vHistory[historyIdx];
bSum -= bHistory[historyIdx];
// get the lastest pixel
int argb = src.getRGB(x + right, y);
a = (argb & 0xFF000000) >>> 24;
r = (argb & 0x00FF0000) >>> 16;
v = (argb & 0x0000FF00) >>> 8;
b = (argb & 0x000000FF);
aHistory[historyIdx] = a;
rHistory[historyIdx] = r;
vHistory[historyIdx] = v;
bHistory[historyIdx] = b;
aSum += a;
rSum += r;
vSum += v;
bSum += b;
if (++historyIdx >= shadowSize)
historyIdx -= shadowSize;
}
}
// vertical pass
for (int x = 0; x < srcWidth; x++) {
aSum = 0;
rSum = 0;
vSum = 0;
bSum = 0;
historyIdx = 0;
for (int y = 0; y < shadowSize; y++) {
int argb = dst.getRGB(x, y);
int a = (argb & 0xFF000000) >>> 24;
int r = (argb & 0x00FF0000) >>> 16;
int v = (argb & 0x0000FF00) >>> 8;
int b = (argb & 0x000000FF);
aHistory[y] = a;
rHistory[y] = r;
vHistory[y] = v;
bHistory[y] = b;
aSum += a;
rSum += r;
vSum += v;
bSum += b;
}
for (int y = yStart; y < yStop; y++) {
int a = aSum / shadowSize;
int r = rSum / shadowSize;
int v = vSum / shadowSize;
int b = bSum / shadowSize;
dst.setRGB(x, y, a << 24 | r << 16 | v << 8 | b);
// substract the oldest pixel from the sum
aSum -= aHistory[historyIdx];
rSum -= rHistory[historyIdx];
vSum -= vHistory[historyIdx];
bSum -= bHistory[historyIdx];
// get the lastest pixel
int argb = dst.getRGB(x, y + right);
a = (argb & 0xFF000000) >>> 24;
r = (argb & 0x00FF0000) >>> 16;
v = (argb & 0x0000FF00) >>> 8;
b = (argb & 0x000000FF);
aHistory[historyIdx] = a;
rHistory[historyIdx] = r;
vHistory[historyIdx] = v;
bHistory[historyIdx] = b;
aSum += a;
rSum += r;
vSum += v;
bSum += b;
if (++historyIdx >= shadowSize)
historyIdx -= shadowSize;
}
}
}
public BufferedImage createShadowMask(BufferedImage image)
{
BufferedImage mask = new BufferedImage(image.getWidth(),
image.getHeight(),
BufferedImage.TYPE_INT_ARGB);
Graphics2D g2d = mask.createGraphics();
g2d.drawImage(image, 0, 0, null);
// Ar = As*Ad - Cr = Cs*Ad --- By using this composite we can extract the alpha layer from the picture ...
g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_IN, shadowOpacity));
g2d.setColor(shadowColor);
g2d.fillRect(0, 0, image.getWidth(), image.getHeight()); // this will fill with the shadowColor and alpha=1.0f
g2d.dispose();
return mask;
}
public ConvolveOp getBlurOp(int size)
{
float[] data = new float[size * size];
float value = 1 / (float)(size * size);
for (int i = 0; i < data.length; i++)
data[i] = value;
return new ConvolveOp(new Kernel(size, size, data));
}
/**
* Briefly vibrate the component by changing it's position
* @param comp the component to move.
*/
public static void vibrate( final JComponent comp )
{
final Point location = comp.getLocation();
final int duration = 500;
final double distance = 5.0;
final double cycles = 50.0;
final long startTime = System.currentTimeMillis();
final Timer shakeTimer = new Timer( 10, new ActionListener() {
public void actionPerformed( ActionEvent e )
{
long elapsedTime = System.currentTimeMillis() - startTime;
double offset = ( elapsedTime % cycles ) / cycles;
double angle = offset * Math.PI * 2.0;
int shakeX = (int)( Math.sin( angle ) * distance + location.x );
comp.setLocation( shakeX, location.y );
comp.repaint();
if ( elapsedTime >= duration )
stopShake( (Timer)e.getSource(), comp );
}
public void stopShake( Timer t, JComponent comp )
{
t.stop();
comp.setLocation( location );
comp.repaint();
}
});
shakeTimer.start();
}
}