用java2D构造图片遮罩效果

[quote]前些天做一个项目需要实现Applet中图片的遮罩效果,于是在网上搜索了一些资料,终于搞出来了。发出来与大家共享一下[/quote]



import java.awt.AlphaComposite;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GraphicsConfiguration;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.HeadlessException;
import java.awt.Image;
import java.awt.Rectangle;
import java.awt.Shape;
import java.awt.Transparency;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionListener;
import java.awt.image.BufferedImage;
import java.awt.image.ColorModel;
import java.awt.image.DirectColorModel;
import java.awt.image.PixelGrabber;
import java.awt.image.RGBImageFilter;
import java.io.File;
import java.io.FileOutputStream;
import java.net.URL;

import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JFrame;

import com.sun.image.codec.jpeg.JPEGCodec;
import com.sun.image.codec.jpeg.JPEGImageEncoder;

/***
* 功能介绍:
* 两幅图片叠加,一个在上面,一个在下面,上面的图片掏空部分,即可以透明显示下面的图片
*
* 通过鼠标点取实现透过上面的图片控制下面的图片
* 首先确定鼠标点取的地方是透明区域还是非透明区域
*
* 图片的透明区域中的RGB值存在一个有效范围,
* 此范围与图片的格式及其他属性有关,一般范围大小不超过40
* 通过这个有效范围,可以判断鼠标点取处是透明区还是非透明区
* 但这个范围存在精确度的问题,所以在处理图片的时候需要多次试验得到准确数据。

* 当遮罩图片通过getInstanceScale()函数将它的长宽改变时,它的RGB值变成自己定义的值了(0,0,0)
* */
public class ImageCover extends JFrame{

private BufferedImage top_img;
private BufferedImage bot_img;

private int frame_width=600;
private int frame_height=500;

/**指定绘制图片路径
*
* 需要镂空的图片必须是纯正的gif格式图片,最好是表情一类的。
* 不可以从jpg或其他格式转换而来,只有gif格式的图片才可以很好的设置透明度
* */
//图片名 与之对应的rgb值
private String filetop="app7topimg.gif";
private int red=0,green=0,blue=0;

private boolean hascut=false;

//抠出一定范围图片
//private String filetop="21015.gif";
//private int red_min=200,red_max=220,green_min=150,green_max=175,blue_min=135,blue_max=160;
private String filebottom="bottom.jpg";

/**保存图片转换过程中的临时文件*/
private String path="e:/tempImg.gif";


/**临时装载rgb值,alp临时装载像素值*/
int rgb[]=null,alp=255;
/**需要指定的透明度0~255,当=0时,为透明,当大于1,都是不透明的*/
int decAlpha=0;

private int top_x=10,top_y=30;
private int bottom_x=10,bottom_y=20;
private int frame_x=400,frame_y=200;

Shape sh_top,sh_bottom;
Graphics g;
AlphaComposite alphaComposite=null;

/**
* 闪屏问题: 调用repaint()函数,它在调用paint()函数前会自动清除屏幕,
* 所以在一个毫秒内我们会看到一个空白的屏幕,在快速的变换操作中就 出现了闪烁现象。
*
* 利用双缓冲技术处理绘图时的闪屏问题
*
* 1.首先我们在update方法中用createImage方法新建一后台图像类变量
   * 2.然后使用getGraphics()方法得到当前图像的图形关联
   * 3.在后台处理所有相关的处理,如清除屏幕,后台绘画等等
* 其中核心代码见update方法实现。
* 需要注意两点:
* 在paint方法中不可有super.paint()
* 在update方法中已经调用的paint方法,所以其他地方不再需要调用repaint()
* */
BufferedImage offScreenImg=null;

//MyFilter myFilter=new MyFilter();

public static void main(String[] args) throws Exception{
// TODO Auto-generated method stub
new ImageCover("图片遮罩测试");
}

public ImageCover(String title) throws Exception{

MouseControl mc=new MouseControl();
setTitle(title);
setSize(frame_width,frame_height);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setLayout(new BorderLayout());
setBackground(Color.white);
setLocation(frame_x,frame_y);
initImage();

alphaComposite=AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0f);

this.addMouseListener(mc);
//this.addMouseMotionListener(mc);//取消鼠标拖拽图片

setVisible(true);

//getGraphics()方法只有当图像显示以后才可以返回Graphics对象,否则返回null
g=this.getGraphics();
g.setColor(Color.white);
g.fillRect(0, 0, frame_width, frame_height);
}

/**初始化图片*/
private void initImage() throws Exception{


URL bottom=ImageCover.class.getResource(filebottom);
String topfile;
if(hascut){
topfile=filetop;
}else{
topfile=createTopImg();
}
bot_img=ImageIO.read(bottom);

//top_img图像大小缩放到与bot_img大小一样
top_img=toBufferedImage((new ImageIcon(topfile)).getImage().getScaledInstance(bot_img.getWidth(), bot_img.getHeight(), java.awt.Image.SCALE_SMOOTH));
}

/**给定一个Image对象,返回一个BufferedImage对象*/
public BufferedImage toBufferedImage(Image image) {
if (image instanceof BufferedImage) {
return (BufferedImage)image;
}

// 确保Image中的所有像素都被加载进来
image = new ImageIcon(image).getImage();

/**
* 判断该Image是否具有透明度
* */
boolean hasAlpha = hasAlpha(image);

BufferedImage bimage = null;
GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
try {
/**
* OPAQUE 表示保证完全不透明的图像数据,意味着所有像素 alpha 值都为 1.0
* TRANSLUCENT 表示包含或可能包含位于 0.0 和 1.0(含两者)之间的任意 alpha 值的图像数据
* BITMASK 表示保证完全不透明的图像数据(alpha 值为 1.0)或完全透明的图像数据(alpha 值为 0.0)
* */
int transparency = Transparency.OPAQUE;
if (hasAlpha) {
transparency = Transparency.TRANSLUCENT;
}

//创建BufferedImage
GraphicsDevice gs = ge.getDefaultScreenDevice();
GraphicsConfiguration gc = gs.getDefaultConfiguration();
if(image==null){
System.out.println("image is null");
}
bimage = gc.createCompatibleImage(
image.getWidth(null), image.getHeight(null), transparency);
} catch (HeadlessException e) {
e.printStackTrace();
}

if (bimage == null) {
//使用默认ColorModel创建一个BufferedImage
int type = BufferedImage.TYPE_INT_RGB;
if (hasAlpha) {
System.out.println("hasAlpha");
type = BufferedImage.TYPE_INT_ARGB;
}
bimage = new BufferedImage(image.getWidth(null), image.getHeight(null), type);
}

Graphics g = bimage.createGraphics();

g.drawImage(image, 0, 0, null);
g.dispose();

return bimage;
}

/**判断Image是否具有透明度*/
public boolean hasAlpha(Image image) {

if (image instanceof BufferedImage) {
BufferedImage bimage = (BufferedImage)image;
return bimage.getColorModel().hasAlpha();
}

/**构造一个PixelGrabber对象,获取image矩阵上的(0,0)点像素值*/
PixelGrabber pg = new PixelGrabber(image, 0, 0, 1, 1, true);
try {
pg.grabPixels();
} catch (InterruptedException e) {
}

ColorModel cm = pg.getColorModel();
System.out.println("ColorModel : "+cm+" , alpha:"+cm.hasAlpha());

return cm.hasAlpha();
}

/**抠出topimg图片,同时返回图片路径*/
private String createTopImg() throws Exception{

BufferedImage temImg=null;
URL top=ImageCover.class.getResource(filetop);

temImg=ImageIO.read(top);

ColorModel cm=temImg.getColorModel();
System.out.println("cm : "+cm);

if(!cm.hasAlpha()){
setImageAlpha(temImg);
File newTopFile=new File(path);
temImg=ImageIO.read(newTopFile);
}
ColorModel cm2=temImg.getColorModel();
System.out.println("cm2 : "+cm2);
/**
* 将top图片抠图操作与重绘操作分离开,降低了资源损耗
* 将top图片镂空以后,保存在指定路径下,然后再读取该路径下的图片
*
* 镂空图片方法,将图片中不需要的部分设置成同一颜色,然后将其抠出
* */
for (int x = temImg.getMinX(); x < temImg.getWidth(); x++) {
for (int y = temImg.getMinY(); y < temImg.getHeight(); y++){
int pixel = temImg.getRGB(x, y);
rgb = new int [ 3 ];
rgb[ 0 ] = (pixel & 0xff0000 ) >> 16 ;
rgb[ 1 ] = (pixel & 0xff00 ) >> 8 ;
rgb[ 2 ] = (pixel & 0xff );

//alphaComposite=AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.0f);

if(rgb[0]==red && rgb[1]==green && rgb[2]==blue){
alp= decAlpha<< 24 | rgb[ 0 ] << 16 | rgb[ 1 ] << 8 | rgb[ 2 ];
temImg.setRGB(x,y,alp);
}else{


}
/*设置抠出图片中一定范围的图像*/
/*if (rgb[0] > red_min && rgb[0] < red_max && rgb[1] > green_min
&& rgb[1] < green_max && rgb[2] > blue_min
&& rgb[2] < blue_max) {
rgb[0]=1;
rgb[1]=1;
rgb[2]=1;
alp=0;
alp= decAlpha<< 24 | rgb[ 0 ] << 16 | rgb[ 1 ] << 8 | rgb[ 2 ];
temImg.setRGB(x,y,alp);
}*/

}
}
try{
FileOutputStream out = new FileOutputStream(path);
JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(out);
encoder.encode(temImg);
out.close();
}catch(Exception e){

}

return path;
}


/**有些图片没有透明度,所以必须添加透明度
* */
private void setImageAlpha(BufferedImage temImg){


int setAlpha=0;

for (int x = temImg.getMinX(); x < temImg.getWidth(); x++) {
for (int y = temImg.getMinY(); y < temImg.getHeight(); y++){

int pixel = temImg.getRGB(x, y);
rgb = new int [ 3 ];
rgb[ 0 ] = (pixel & 0xff0000 ) >> 16 ;
rgb[ 1 ] = (pixel & 0xff00 ) >> 8 ;
rgb[ 2 ] = (pixel & 0xff );

alp= setAlpha<< 24 | rgb[ 0 ] << 16 | rgb[ 1 ] << 8 | rgb[ 2 ];
temImg.setRGB(x,y,alp);

}
}

try{
FileOutputStream out = new FileOutputStream(path);
JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(out);
encoder.encode(temImg);
out.close();
}catch(Exception e){
e.printStackTrace();
}
}

public void paint(Graphics g){
//super.paint(g);

Graphics2D g2d=(Graphics2D)g;
g2d.drawImage(bot_img,bottom_x, bottom_y, null);

g2d.setComposite(alphaComposite);
sh_bottom=new Rectangle.Double(bottom_x,bottom_y,bot_img.getWidth(),bot_img.getHeight());
g2d.draw(sh_bottom);

alphaComposite=AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 1.0f);
g2d.setComposite(alphaComposite);
g2d.drawImage(top_img, top_x, top_y, null);
alphaComposite=AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0f);
g2d.setComposite(alphaComposite);
if(top_img==null){
System.out.println("null");
}
sh_top=new Rectangle.Double(top_x,top_y,top_img.getWidth(),top_img.getHeight());
g2d.draw(sh_top);
g2d.dispose();
}

public void update(Graphics g) {
if (offScreenImg == null) {
offScreenImg = (BufferedImage) this.createImage(frame_width,
frame_height);
}
Graphics goffScreen = offScreenImg.getGraphics();
Color c = goffScreen.getColor();
goffScreen.setColor(Color.white);
goffScreen.fillRect(0, 0, frame_width, frame_height);
goffScreen.setColor(c);
paint(goffScreen);
g.drawImage(offScreenImg, 0, 0, this);

}


/**鼠标控制两张图片移动*/
class MouseControl extends MouseAdapter implements MouseMotionListener{

private int x;
private int y;

private int maxr=0,maxg=0,maxb=0;
private int minr=255,ming=255,minb=255;

private int mrgb[]=null;

private boolean onTop=false,onBottom=false;

public void mousePressed(MouseEvent e) {
x = e.getX();
y = e.getY();

/**通过两个shape对象来获取鼠标点取的图形*/
if(sh_top.contains(x, y)){
//点取top图
onTop=true;
onBottom=false;

getPixelAlpha(top_img,x-top_x,y-top_y);

}else if(sh_bottom.contains(x, y)){
//点取bottom
onBottom=true;
onTop=false;
//getPixelAlpha(bot_img,x-bottom_x,y-bottom_y);
}

}

public void mouseReleased(MouseEvent e){
onTop=false;
onBottom=false;
}

public void mouseDragged(MouseEvent e) {

int dx = e.getX()-x;
int dy = e.getY()-y;

if(onTop){

top_x=top_x+dx;
top_y=top_y+dy;

}else if(onBottom){

bottom_x=bottom_x+dx;
bottom_y=bottom_y+dy;

}else{
System.out.println("doNothing");
}
update(g);
//repaint();

x=x+dx;
y=y+dy;
}

/**给定一个BufferedImage,判断该图像上点Pt的透明度值
*
* ptx:bfImg图像上点x坐标
* pty:bfImg图像上点y坐标
*
* 当ptx或pty值超过了图像大小则抛出ArrayOutOfBoundsException异常
* */
private void getPixelAlpha(BufferedImage bfImg,int ptx, int pty){

int pixel = bfImg.getRGB(ptx, pty);
mrgb = new int [ 3 ];
mrgb[ 0 ] = (pixel & 0xff0000 ) >> 16 ;
mrgb[ 1 ] = (pixel & 0xff00 ) >> 8 ;
mrgb[ 2 ] = (pixel & 0xff );


if(mrgb[0]==0 && mrgb[1]==0 && mrgb[2]==0){
System.out.println("透明区域");
}else{
System.out.println("不透明区域");
}

// printRGB(mrgb[0],mrgb[1],mrgb[2]);
}

/**
* 通过鼠标点取找到透明区域中RGB值的最大值和最小值从而确定一个有效范围
* 输出最大值和最小值
* */
private void printRGB(int r,int g,int b){

if(r>maxr){
maxr=r;
}else if(r<minr){
minr=r;
}
if(r>maxg){
maxg=g;
}else if(g<ming){
ming=g;
}
if(r>maxb){
maxb=b;
}else if(b<minb){
minb=b;
}
System.out.println(minr+"<R<"+maxr+" , "+ming+"<G<"+maxg+" , "+minb+"<B<"+maxb);
}


}

/**
* MyFilter类,用于过滤指定像素值,
* 但频繁的函数调用会使运行效率降低,所以不采用
* */
class MyFilter extends RGBImageFilter {

int alpha=0;

/**构造器*/
public MyFilter() {
this.canFilterIndexColorModel = true;
}
/***********************************************************************
* i: 图像的x坐标值 j: 图像的y坐标值 k: 图像(i,j)坐标点上的RGB值
**********************************************************************/
public int filterRGB(int i, int j, int k) {

DirectColorModel dcm = (DirectColorModel) ColorModel
.getRGBdefault();
// DirectColorModel类用来将ARGB值独立分解出来
int red = dcm.getRed(k);
int green =0;
int blue = 0;
//int alp = dcm.getAlpha(k);

// 如果像素为指定RGB值,则让它透明
if (red==0 ) {
green = dcm.getGreen(k);
if(green==74){
blue = dcm.getBlue(k);
if(blue==74){
alpha = decAlpha;
return alpha << 24 | red << 16 | green << 8 | blue;
}
}
}
return alpha << 24 | red << 16 | green << 8 | blue;
}

}

}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值