一个小细节引起的bug

近日想找一找java裁剪图片的方法,结果各种方法层出不穷,其中这几种方法占大多数:

用图片流来处理,还用到了Iterator迭代器?? 而且使用结构还不规范,正确:Iterator<ImageReader>

一个小细节引起的bug

 一个小细节引起的bug

 还有一种,用复杂的坐标来计算

一个小细节引起的bug

然而在BufferedImage 里有个getSubimage方法可以直接用来裁剪图片,难到这群人从来都不看源码的么

   /**
     * Returns a subimage defined by a specified rectangular region.
     * The returned <code>BufferedImage</code> shares the same
     * data array as the original image.
     * @param x the X coordinate of the upper-left corner of the
     *          specified rectangular region
     * @param y the Y coordinate of the upper-left corner of the
     *          specified rectangular region
     * @param w the width of the specified rectangular region
     * @param h the height of the specified rectangular region
     * @return a <code>BufferedImage</code> that is the subimage of this
     *          <code>BufferedImage</code>.
     * @exception RasterFormatException if the specified
     * area is not contained within this <code>BufferedImage</code>.
     */
    public BufferedImage getSubimage (int x, int y, int w, int h) {
        return new BufferedImage (colorModel,
                                  raster.createWritableChild(x, y, w, h,
                                                             0, 0, null),
                                  colorModel.isAlphaPremultiplied(),
                                  properties);
    }

所以裁剪图片的代码就精简成了

public static BufferedImage getCuttedImage(BufferedImage bufferedImage, int x, int y, int length, int width) {
		return bufferedImage.getSubimage(x, y, length, width);
	}

******************************************************************************************************************************************

下面进入正题,先说需求:给定一张图片,从图片中裁剪下来一个随机位置指定大小的小图,并把在原图中把裁下来的区域做变色处理,最后返回这两张图片的base64和被裁区域左上角顶点的xy坐标,先贴代码

/**
 * 图片处理工具类
 * 
 * @author xuLiang
 * @since 1.0.0
 */

@SuppressWarnings("restriction")
@Component
public class VerifyImageUtil {

	/**
	 * 用来裁剪到滑动的方块
	 */
	public static BufferedImage getCuttedImage(BufferedImage bufferedImage, int x, int y, int length, int width) {
		return bufferedImage.getSubimage(x, y, length, width);
	}

	/**
	 * 被抠滑块的坐标集合
	 * 
	 * @param targetLength
	 *            原图的长度
	 * @param targetWidth
	 *            原图的宽度
	 * @param x
	 *            裁剪区域的x坐标
	 * @param y
	 *            裁剪区域的y坐标
	 * @param length
	 *            抠图的长度
	 * @param width
	 *            抠图的宽度
	 * @return 被抠滑块的坐标
	 */
	public static int[][] getCutAreaData(int targetLength, int targetWidth, int x, int y, int length, int width) {
		int[][] data = new int[targetLength][targetWidth];
		for (int i = 0; i < targetLength; i++) {
			for (int j = 0; j < targetWidth; j++) {
				if (i < x + length && i >= x && j < y + width && j > y) {
					data[i][j] = 1;
				} else {
					data[i][j] = 0;
				}
			}
		}
		return data;
	}

	/**
	 * 得到抠掉滑块后的图并加阴影
	 * 
	 * @param oriImage
	 * @param templateImage
	 * 
	 */
	public static void cutByTemplate(BufferedImage oriImage, int[][] templateImage) {
		for (int i = 0; i < oriImage.getWidth(); i++) {
			for (int j = 0; j < oriImage.getHeight(); j++) {
				int rgb = templateImage[i][j];
				if (rgb == 1) {
					oriImage.setRGB(i, j, -1); // 原图对应位置颜色变化
				}
			}
		}
	}

	/**
	 * 图片转Base64
	 * 
	 * @param image
	 * @return Base64
	 * @throws Exception
	 */
	public static String imageToBase64(BufferedImage image) throws Exception {
		byte[] imagedata = null;
		ByteArrayOutputStream bao = new ByteArrayOutputStream();
		imagedata = bao.toByteArray();
		BASE64Encoder encoder = new BASE64Encoder();
		String BASE64IMAGE = encoder.encodeBuffer(imagedata).trim();
		BASE64IMAGE = BASE64IMAGE.replaceAll("\n", "").replaceAll("\r", "");// 删除 \r\n
		return BASE64IMAGE;
	}

	/**
	 * Base64转图片
	 * 
	 * @param base64String
	 * @return image
	 * @throws Exception
	 */
	public static BufferedImage base64StringToImage(String base64String) {
		try {
			BASE64Decoder decoder = new BASE64Decoder();
			byte[] bytes1 = decoder.decodeBuffer(base64String);
			ByteArrayInputStream bais = new ByteArrayInputStream(bytes1);
			return ImageIO.read(bais);
		} catch (IOException e) {
			e.printStackTrace();
		}
		return null;
	}

}

service中:

/**
	 * 获取图片和坐标值
	 * 
	 * @param originImgX
	 *            原图的长
	 * @param originImgY
	 *            原图的宽
	 * @param cuttedImgX
	 *            裁出来的方块的长
	 * @param cuttedImgY
	 *            裁出来的方块的宽
	 * @param path
	 *            原图文件路径
	 * @return VerificationCodeResp
	 * @author xuLiang
	 * @since 1.0.0
	 * @throws Exception
	 */
	@Override
	public VerificationCodeResp getImage(int originImgX, int originImgY, int cuttedImgX, int cuttedImgY, String path)
			throws Exception {
		int x = new Random().nextInt(originImgX - cuttedImgX);
		int y = new Random().nextInt(originImgY - cuttedImgY);
		BufferedImage bufferedImage = ImageIO.read(new FileInputStream(path));
		BufferedImage cuttedImg = VerifyImageUtil.getCuttedImage(bufferedImage, x, y, cuttedImgX, cuttedImgY);// 用来裁剪到滑动的方块
		int[][] cuttedOriginImgCoordinate = VerifyImageUtil.getCutAreaData(originImgX, originImgY, x, y, cuttedImgX,
				cuttedImgY);// 被抠滑块的坐标集合
		VerifyImageUtil.cutByTemplate(bufferedImage, cuttedOriginImgCoordinate);// 得到抠掉滑块后的图并加阴影
                VerificationCodeResp response = new VerificationCodeResp();
                response.setCuttedOriginImgBase64(VerifyImageUtil.imageToBase64(bufferedImage)); 
                response.setCuttedImgBase64(VerifyImageUtil.imageToBase64(cuttedImg));
                response.setXCoordinate(x + "");
		response.setYCoordinate(y + "");
		return response;
	}
/**
 * 获取验证图片响应体
 * 
 * @author xuLiang
 * @since 1.0.0
 */
public class VerificationCodeResp {

	private String cuttedImgBase64;
	private String cuttedOriginImgBase64;
	private String XCoordinate;
	private String YCoordinate;

	@Override
	public String toString() {
		return "VerificationCodeResp [cuttedImgBase64=" + cuttedImgBase64 + ", cuttedOriginImgBase64="
				+ cuttedOriginImgBase64 + ", XCoordinate=" + XCoordinate + ", YCoordinate=" + YCoordinate + "]";
	}

	public String getCuttedImgBase64() {
		return cuttedImgBase64;
	}

	public void setCuttedImgBase64(String cuttedImgBase64) {
		this.cuttedImgBase64 = cuttedImgBase64;
	}

	public String getCuttedOriginImgBase64() {
		return cuttedOriginImgBase64;
	}

	public void setCuttedOriginImgBase64(String cuttedOriginImgBase64) {
		this.cuttedOriginImgBase64 = cuttedOriginImgBase64;
	}

	public String getXCoordinate() {
		return XCoordinate;
	}

	public void setXCoordinate(String xCoordinate) {
		XCoordinate = xCoordinate;
	}

	public String getYCoordinate() {
		return YCoordinate;
	}

	public void setYCoordinate(String yCoordinate) {
		YCoordinate = yCoordinate;
	}

}

代码看似一切正常,但是在测试的时候发现一个很奇怪的bug,进行裁剪的图片变成了已经经过变色处理后的图片,而不是源图,在经过漫长的debug后发现了问题所在,

一个小细节引起的bug

简单来说,就是图片处理方法中的参数bufferedImage指向了和处理过后的图片相同的地址值 ,返回的图片也没有开辟新的内存来存放,做如下改动

	public static BufferedImage getCuttedImage(BufferedImage bufferedImage, int x, int y, int length, int width) {
		BufferedImage bi = bufferedImage.getSubimage(x, y, length, width);
		return bi;
	}
	@Override
	public VerificationCodeResp getImage(int originImgX, int originImgY, int cuttedImgX, int cuttedImgY, String path)
			throws Exception {
		VerificationCodeResp response = new VerificationCodeResp();
		int x = new Random().nextInt(originImgX - cuttedImgX);
		int y = new Random().nextInt(originImgY - cuttedImgY);
		response.setXCoordinate(x + "");
		response.setYCoordinate(y + "");
		BufferedImage bufferedImage = ImageIO.read(new FileInputStream(path));
		BufferedImage cuttedImg = VerifyImageUtil.getCuttedImage(bufferedImage, x, y, cuttedImgX, cuttedImgY);// 用来裁剪到滑动的方块
		response.setCuttedImgBase64(VerifyImageUtil.imageToBase64(cuttedImg));
		int[][] cuttedOriginImgCoordinate = VerifyImageUtil.getCutAreaData(originImgX, originImgY, x, y, cuttedImgX,
				cuttedImgY);// 被抠滑块的坐标集合
		VerifyImageUtil.cutByTemplate(bufferedImage, cuttedOriginImgCoordinate);// 得到抠掉滑块后的图并加阴影
		response.setCuttedOriginImgBase64(VerifyImageUtil.imageToBase64(bufferedImage));
		return response;
	}

 

一个小细节引起的bug

 经测试,改动后的代码就能得到预期结果了。