Java根据EXIF方向旋转BufferedImage后图片变黑问题
BufferedImage旋转后输出全黑问题修复
核心问题原因
你的代码存在4个直接导致输出全黑/异常的错误:
Graphics2D.rotate()方法接收的参数是弧度值而非角度值,直接传入90/180/270会导致旋转角度完全错误,图片被绘制到画布可视区域外- 旋转时未做坐标平移:默认旋转是绕画布左上角(0,0)点执行,90°、270°旋转后如果不追加对应平移,图片会整体偏移出画布范围
- 图形上下文操作顺序错误:你在调用
g.drawImage()之后才执行g.dispose(),后续设置的渲染参数、合成规则完全不会生效,且dispose()调用后图形上下文已经释放,后续操作属于无效调用 - 额外隐患:JPG格式不支持透明通道,使用
TYPE_INT_ARGB类型写出JPG时容易出现色彩异常,且你代码注释标注缩放50%,实际宽高除以4是缩放到原尺寸的1/4,逻辑不一致。
修复后可直接运行的代码
import com.drew.imaging.ImageProcessingException; import com.drew.metadata.Metadata; import com.drew.metadata.exif.ExifIFD0Directory; import com.drew.imaging.ImageMetadataReader; import java.awt.*; import java.awt.geom.AffineTransform; import java.awt.image.BufferedImage; import java.io.*; import javax.imageio.ImageIO; public class ImageCompressUtil { public static byte[] compress(byte[] img) throws IOException { ByteArrayOutputStream rescale = rescale(img); return rescale.toByteArray(); } private static Integer getOrientation(InputStream inputstream) throws Exception { Metadata metadata = ImageMetadataReader.readMetadata(inputstream); ExifIFD0Directory exifIFD0 = metadata.getFirstDirectoryOfType(ExifIFD0Directory.class); // 无EXIF方向信息直接返回正常方向 if (exifIFD0 == null || !exifIFD0.containsTag(ExifIFD0Directory.TAG_ORIENTATION)) { return 1; } return exifIFD0.getInt(ExifIFD0Directory.TAG_ORIENTATION); } private static ByteArrayOutputStream rescale(byte[] img) throws IOException { InputStream inputstreamMeta = new ByteArrayInputStream(img); InputStream inputstream = new ByteArrayInputStream(img); int orientation = 1; try { orientation = getOrientation(inputstreamMeta); } catch (Exception e) { // 读取EXIF失败默认按正常方向处理 } BufferedImage bi = ImageIO.read(inputstream); int srcWidth = bi.getWidth(); int srcHeight = bi.getHeight(); // 计算旋转后的目标宽高,这里按缩放到原尺寸1/4设置,需要50%就把/4改成/2 int targetWidth, targetHeight; AffineTransform transform = new AffineTransform(); switch (orientation) { case 6: // 顺时针转90度 targetWidth = srcHeight /4; targetHeight = srcWidth /4; // 角度转弧度,绕画布中心旋转后平移到可视区域 transform.rotate(Math.toRadians(90), targetWidth/2d, targetHeight/2d); transform.translate((targetWidth - targetHeight)/2d, (targetHeight - targetWidth)/2d); break; case 3: // 转180度 targetWidth = srcWidth /4; targetHeight = srcHeight /4; transform.rotate(Math.toRadians(180), targetWidth/2d, targetHeight/2d); break; case 8: // 顺时针转270度 targetWidth = srcHeight /4; targetHeight = srcWidth /4; transform.rotate(Math.toRadians(270), targetWidth/2d, targetHeight/2d); transform.translate((targetWidth - targetHeight)/2d, (targetHeight - targetWidth)/2d); break; default: // 正常方向 targetWidth = srcWidth /4; targetHeight = srcHeight /4; break; } // JPG不支持透明,统一用TYPE_INT_RGB避免黑块 BufferedImage resizedImage = new BufferedImage(targetWidth, targetHeight, BufferedImage.TYPE_INT_RGB); Graphics2D g = resizedImage.createGraphics(); // 所有渲染参数在绘图前设置 g.setComposite(AlphaComposite.Src); g.setRenderingHint(RenderingHints.KEY_INTERPOLATION,RenderingHints.VALUE_INTERPOLATION_BILINEAR); g.setRenderingHint(RenderingHints.KEY_RENDERING,RenderingHints.VALUE_RENDER_QUALITY); g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON); g.setTransform(transform); // 绘图 g.drawImage(bi, 0, 0, targetWidth, targetHeight, null); // 所有操作完成后再释放上下文 g.dispose(); ByteArrayOutputStream baos = new ByteArrayOutputStream(); ImageIO.write(resizedImage, "jpg",baos); return baos; } }
关键修改点说明
- 用
Math.toRadians()把角度转为弧度值传入旋转方法,保证旋转角度正确 - 旋转时指定绕画布中心旋转,追加对应平移量,保证旋转后的图片完整落在画布可视区域内
- 调整Graphics2D操作顺序:先设置渲染参数、应用旋转变换,再绘制图片,最后调用dispose释放资源
- 针对JPG格式统一使用
TYPE_INT_RGB类型,避免透明通道导致的黑块问题 - 补全EXIF目录非空判断,避免无EXIF信息的图片触发空指针异常
内容的提问来源于stack exchange,提问作者mobiw
相关产品推荐
相关产品推荐

