在保持比率的同时调整大小到最大可能的图像

时间:2016-06-08 16:17:54

标签: java bufferedimage

有时我的图片太大而且我收到了这个错误:

  

线程中的异常" main" java.lang.NegativeArraySizeException at       java.awt.image.DataBufferByte。(未知来源)at       java.awt.image.Raster.createInterleavedRaster(未知来源)at       java.awt.image.BufferedImage。(未知来源)

每当我得到这个,我想在保持比率的同时将我的图像调整到尽可能大的尺寸。

我最终得到了以下公式:

    if ( targetWidth * targetHeight >= Integer.MAX_VALUE || targetWidth * targetHeight < 0 ) {
        System.out.println( "Target image too big... Size will be adjusted!" );
        if ( targetWidth > targetHeight ) {
            targetWidth = (int)Math.sqrt( ( Integer.MAX_VALUE ) * (float)( targetWidth / targetHeight ) );
            targetHeight = ( Integer.MAX_VALUE ) / targetWidth;
        } else {
            targetHeight = (int)Math.sqrt( ( Integer.MAX_VALUE ) * (float)( targetHeight / targetWidth ) );
            targetWidth = ( Integer.MAX_VALUE ) / targetHeight;
        }
    }

我仍然遇到同样的问题,我的条件也很满意。 我想那个

  

宽度*高度&lt; Integer.MAX_VALUE的

显然不是我正在寻找的条件 有什么帮助吗?

编辑:经过一番讨论后,我认为这个问题的真正问题是: 为了不在以下位置获取NegativeArraySizeException,我可以传递给BufferedImage构造函数的最大可能大小是多少:

  

at java.awt.image.DataBufferByte。(Unknown Source)

1 个答案:

答案 0 :(得分:1)

由于Raster类(ref)的限制,

BufferedImage限制与byte[Integer.MAX_VALUE]的限制相同。您还有标头的开销,这是平台和实现相关的。这就是为什么我推荐一个长度为long的安全缓冲区。

(Integer.MAX_VALUE - 8) / 4应该是一个很好的安全限制。

注意:您必须考虑每个像素的大小(ref)。例如,BufferedImage.TYPE_4BYTE_ABGR是每像素4个字节。这意味着在这种情况下您的区域限制为Integer.MAX_VALUE / 4。当然,每个像素消耗的字节数因您使用的类型而异。通过表示每个像素的字节数来调整测试最大区域。您必须查看API才能弄明白:https://docs.oracle.com/javase/7/docs/api/java/awt/image/BufferedImage.html

为了在保持宽高比的同时调整图像大小,数学运算非常简单:

double aspectRatio = width / height;

if (aspectRatio < 1) {
    // taller than wide
    targetHeight = maxDimension;
    targetWidth = (int)(targetHeight * aspectRatio);
} else {
    // wider than tall
    targetWidth = maxDimension;
    targetHeight = (int)(targetWidth / aspectRatio);
}

离开是基于图像的总面积计算maxDimension的问题。为了论证,我们说最大面积是Integer.MAX_VALUE - 8relatively safe)。我们可以使用代数推断它。我们知道width * height = area,使用上面的公式,我们有两种方法需要为最大区域求解。

对于aspectRatio < 1,我们用公式替换宽度来获取它:

  

身高*身高* aspectRatio =面积

解决高度:

  • height^2 * aspectRatio = area
  • height^2 = area / aspectRatio
  • height = Math.sqrt(area / aspectRatio

对于asptectRatio >= 1,我们用公式代替高度来获取它:

  

width * width / aspectRatio = area

求解宽度:

  • width ^ 2 / aspectRatio = area
  • width ^ 2 = area * aspectRatio
  • width = Math.sqrt(area * aspectRatio)

现在我们可以更新基本公式以考虑最大区域中的形状:

public static final long MAX_AREA = (Integer.MAX_VALUE - 8) / 4;

if (aspectRatio < 1) {
    targetHeight = Math.sqrt(MAX_AREA / aspectRatio);
    targetWidth = (int)(targetHeight * aspectRatio);
} else {
    // wider than tall
    targetWidth = Math.sqrt(MAX_AREA * aspectRatio);
    targetHeight = (int)(targetWidth / aspectRatio);
}

当然,如果超过最大面积阈值,这就留下了测试的基本问题。这需要使用int之外的其他内容来完成。

public static final long MAX_AREA = (Integer.MAX_VALUE - 8) / 4;
long area = (long)width * (long)height;

if(area < MAX_AREA) {
    // recalculate size
}

否则你会遇到溢出问题。