Java调用createGraphics向BufferedImage追加绘制内容失效排查
Java跑马灯图片滚动绘制覆盖问题解决方案
问题背景
实现跑马灯风格横向图片滚动的预期逻辑:
- 从原始图像左侧切取固定宽度的切片
- 将剩余右侧图像区域整体左移到画布最左侧
- 把切下的左侧切片拼接至画布最右侧,完成单帧滚动
直接在切取的切片对象上调用createGraphics追加绘制时,会出现原图像被覆盖的异常。
正常实现效果参考:
异常表现为原图像像素被错误覆盖,无法得到预期拼接结果。
问题根因
问题确实由getSubimage方法的实现特性导致:
BufferedImage.getSubimage()返回的子图像不是独立的新图像对象,只是原图像对应坐标区域的共享数据视图,所有对子图像的读写操作都会直接作用在原始图像的像素数组上,且子图像自身宽高与切取区域完全一致,并非原图尺寸。
异常代码的两个核心错误:
- 代码中先为
leftSlice、rightSlice新建全尺寸BufferedImage对象,下一行立刻用getSubimage()的返回值覆盖了对象引用,之前创建的新对象直接被丢弃,完全没有被使用 - 切取得到的
leftSlice实际宽度只有设定的sliceSize(示例中为23像素),在这个窄图上从0坐标开始绘制宽度为「原图宽度-sliceSize」的右侧切片,绘制范围远超子图边界,再加上子图和原图共享像素数据,直接写坏了原图的像素内容,最终出现覆盖错乱。
注:第一段可运行代码中也存在冗余逻辑:给
leftSlice、rightSlice新建全尺寸BufferedImage的步骤完全无效,属于可以删除的冗余代码。
精简可复用实现
不需要创建多余的中间对象,仅需创建一个输出用画布,利用drawImage的源区域参数直接从原图切取对应区域绘制即可,性能更高、无冗余对象,适合复用于多组小型动画元素场景:
try { originalImage = ImageIO.read(new File("c:\\temp\\scanner.png")); } catch (IOException ex) { ex.printStackTrace(); } int sliceSize = 23; int imgWidth = originalImage.getWidth(); int imgHeight = originalImage.getHeight(); // 仅创建一个输出画布,无多余中间对象 BufferedImage scrolledImage = new BufferedImage(imgWidth, imgHeight, BufferedImage.TYPE_INT_ARGB); Graphics2D g2 = scrolledImage.createGraphics(); // 直接从原图指定区域绘制到目标位置,不需要额外生成切片对象 // 1. 右侧区域左移到画布起点 g2.drawImage(originalImage, 0, 0, imgWidth - sliceSize, imgHeight, sliceSize, 0, imgWidth, imgHeight, null); // 2. 左侧切片拼接至画布最右侧 g2.drawImage(originalImage, imgWidth - sliceSize, 0, imgWidth, imgHeight, 0, 0, sliceSize, imgHeight, null); g2.dispose();
如果后续场景确实需要操作独立子图,不要直接使用getSubimage()返回的共享视图,可以新建一个和切取区域同尺寸的BufferedImage,将子图内容绘制到新对象后再做自定义修改,就不会影响原始图像数据。
内容的提问来源于stack exchange,提问作者Zizzyzizzy
相关产品推荐
相关产品推荐

