艺术效果:波纹
对图像上的像素点进行位置变换,就形成图像的形变效果。因波纹按正弦或余弦方式传播,对图像进行正弦或余弦变换,可形成类似波纹传播的效果。
以下是本文实现的两种波纹的效果。第一张为原始图像,第二张为第一种算法的静态波纹效果,第三张为第一种算法生成的动态GIF图的显示效果,第四张为第二种算法的静态波纹效果,第五张为第二种算法生成的动态GIF图的显示效果。
算法实现
这里直接给出两种算法的代码,感兴趣的朋友可直接通过阅读代码了解实现细节。
import java.awt.image.BufferedImage;
public class RippleEffect {
public static final double PI=3.1415926;
public static final double REFRACT_INDEX = 0.9;
public static final double arc = 3.1415926/6;
// water wave....
public static BufferedImage waveImage(BufferedImage image, int span, int peak, int beta, int offset) {
BufferedImage bimg = new BufferedImage(image.getWidth(),image.getHeight(),BufferedImage.TYPE_INT_RGB);
int x_center = image.getWidth()/2;
int y_center = image.getHeight()/2;
double v = -Math.cos(beta*PI/180.0);
double distance, he;
int y_offset;
for (int y=0; y < image.getHeight(); y++) {
for (int x=0; x < image.getWidth(); x++) {
distance = Math.sqrt((x-x_center)*(x-x_center) + (y-y_center)*(y-y_center)/(v*v));
he = Math.cos((distance+offset)*PI/span)*peak;
y_offset = (int)(he * v);
for (int i=y_offset-2; i <= y_offset+2; i++) {
try {
bimg.setRGB(x, y-i, image.getRGB(x, y));
} catch (Exception e) {
}
}
}
}
return bimg;
}
// ripple ....
public static BufferedImage rippleImage(BufferedImage image, int span, int peak, int off) {
BufferedImage bimg = new BufferedImage(image.getWidth(),image.getHeight(),BufferedImage.TYPE_INT_RGB);
int x_center = image.getWidth()/2;
int y_center = image.getHeight()/2;
boolean isWidth = x_center > y_center;
int r = isWidth ? x_center : y_center;
double distance, d2, he;
double arc_ref;
double offset;
int x_offset;
int y_offset;
int x1, y1;
double scale = x_center/(double)y_center;
double out;
for (int y=0; y < image.getHeight(); y++) {
for (int x=0; x < image.getWidth(); x++) {
x1 = x-x_center;
y1 = y-y_center;
d2 = Math.sqrt(x1*x1+y1*y1*scale*scale) + off;
distance = Math.sqrt(x1*x1+y1*y1);
offset = Math.sin(d2*PI/span)*span*(1-distance/r);
if (x1 == 0 && y1<=0) {
x_offset = -(int)(offset*Math.cos(PI/2.0));
y_offset = -(int)(offset*Math.sin(PI/2.0));
} else if (x1 == 0 && y1>0) {
x_offset = -(int)(offset*Math.cos(PI/2.0));
y_offset = (int)(offset*Math.sin(PI/2.0));
} else if (x > x_center) {
x_offset = (int)(offset*Math.cos(Math.atan(y1/(double)x1)));
y_offset = (int)(offset*Math.sin(Math.atan(y1/(double)x1)));
} else {
x_offset = -(int)(offset*Math.cos(Math.atan(y1/(double)x1)));
y_offset = -(int)(offset*Math.sin(Math.atan(y1/(double)x1)));
}
try {
bimg.setRGB(x, y, image.getRGB(x-x_offset, y-y_offset));
} catch (Exception e) {
}
}
}
return bimg;
}
}
waveImage函数实现第一种算法,rippleImage函数实现第二种算法。
测试代码
下面是测试代码:
public class GifEffectWaveTest {
public static void waveImage() throws IOException {
BufferedImage img = ImageIO.read(new File("cat.jpg"));
BufferedImage resultImage = RippleEffect.waveImage(img, 30, 10, 30, 36);
ImageIO.write(resultImage, "jpeg", new File("cat-wave.jpg"));
}
public static Vector waveEffect(BufferedImage img) {
Vector images = new Vector<>();
PlasmaRoutine pr = new PlasmaRoutine();
pr.create(img.getWidth(), img.getHeight());
pr.setAlpha(30);
for (int i = 15; i >= 0; i--) {
BufferedImage tmpImg = RippleEffect.waveImage(img, 30, 10, 30, i * 12);
images.add(tmpImg);
}
return images;
}
public static void waveImageGif() throws IOException {
BufferedImage img = ImageIO.read(new File("cat.jpg"));
OutputStream out = new FileOutputStream("cat-wave.gif");
GifEncoder gif = new GifEncoder();
if (!gif.isInit()) {
gif.init(img.getWidth(), img.getHeight(), out, true);
gif.setDelay(10);
}
Vector images = waveEffect(img);
for (BufferedImage item : images) {
gif.addImage(item);
}
gif.setCount(0);
gif.encodeMultiple();
out.close();
}
public static void rippleImage() throws IOException {
BufferedImage img = ImageIO.read(new File("cat.jpg"));
BufferedImage resultImage = RippleEffect.rippleImage(img, 50, 10, 30);
ImageIO.write(resultImage, "jpeg", new File("cat-ripple.jpg"));
}
public static Vector rippleEffect(BufferedImage img) {
Vector images = new Vector<>();
PlasmaRoutine pr = new PlasmaRoutine();
pr.create(img.getWidth(), img.getHeight());
pr.setAlpha(30);
for (int i = 8; i >= 0; i--) {
BufferedImage tmpImg = RippleEffect.rippleImage(img, 50, 10, i * 10);
images.add(tmpImg);
}
return images;
}
public static void rippleImageGif() throws IOException {
BufferedImage img = ImageIO.read(new File("cat.jpg"));
OutputStream out = new FileOutputStream("cat-ripple.gif");
GifEncoder gif = new GifEncoder();
if (!gif.isInit()) {
gif.init(img.getWidth(), img.getHeight(), out, true);
gif.setDelay(10);
}
Vector images = rippleEffect(img);
for (BufferedImage item : images) {
gif.addImage(item);
}
gif.setCount(0);
gif.encodeMultiple();
out.close();
}
public static void main(String[] argv) throws IOException {
waveImage();
waveImageGif();
rippleImage();
rippleImageGif();
}
}
GIF图片生成类GifEncoder请参考文章《JAVA实现GIF动画》。