Android - calculateInSampleSize,为什么在宽度大于高度时,使用Math.round处理高度(height / reqHeight)?

10

我正在浏览 'developer.android.com' 网站上有关缩小位图文件的信息,但发现有一件事情我不明白,请您帮忙解答。

以下是来自 developer.android.com 的片段

public static int calculateInSampleSize(
        BitmapFactory.Options options, int reqWidth, int reqHeight) {
  // Raw height and width of image
  final int height = options.outHeight;
  final int width = options.outWidth;
  int inSampleSize = 1;

  if (height > reqHeight || width > reqWidth) {
    if (width > height) {
      inSampleSize = Math.round((float)height / (float)reqHeight);
    } else {
      inSampleSize = Math.round((float)width / (float)reqWidth);
    }
  }
  return inSampleSize;
}
在if语句中,为什么要计算"(float)height / (float)reqHeight",例如,当width=600,height=800,reqWidth=100,reqHeight=100时,inSampleSize将为6,并且计算出的尺寸为width=100,height=133。 height仍然高于reqHeight...所以有人能给我解释一下吗?抱歉解释有些复杂,但我希望有人能给我一些想法。 :)
5个回答

4
上面问题中的代码已经过时。根据自2013年3月8日以来记录在 BitmapFactory.Options参考文档中的内容,inSampleSize将会被舍入到最接近2的幂次方。

如果设置一个大于1的值,则请求解码器对原始图像进行子采样,返回一个较小的图像以节省内存。样本大小是与解码位图中的单个像素相对应的每个维度中的像素数量。例如,inSampleSize == 4返回一个宽度/高度为原始图像的1/4,像素数为1/16的图像。任何<=1的值都被视为1。注意:解码器使用基于2的幂的最终值,任何其他值都将被舍入到最接近2的幂。

2013年3月8日的BitmapFactory.Options参考文档

因此,正确计算inSampleSize的代码可以参考加载大型位图

public static int calculateInSampleSize(BitmapFactory.Options options, 
    int reqWidth, int reqHeight) {

    // Raw height and width of image
    final int height = options.outHeight;
    final int width = options.outWidth;
    int inSampleSize = 1;

    if (height > reqHeight || width > reqWidth) {

        final int halfHeight = height / 2;
        final int halfWidth = width / 2;

        // Calculate the largest inSampleSize value that is a power of 2 and keeps both
        // height and width larger than the requested height and width.
        while ((halfHeight / inSampleSize) > reqHeight
                && (halfWidth / inSampleSize) > reqWidth) {
            inSampleSize *= 2;
        }
    }    
    return inSampleSize;
}

然而,以上的代码还可以进一步优化。由于 halfWidthhalfHeight 已经被除以了 2,因此循环可以重写为返回图像大于或等于请求的大小边界。

        while ((halfHeight / inSampleSize) >= reqHeight
                && (halfWidth / inSampleSize) >= reqWidth) {
            inSampleSize *= 2;
        }

将大小为800x800像素的图像适应于100x100像素的框中,原始代码会返回inSampleSize值为4 -> 返回的图像将为200 x 200像素,并且修改后的代码将返回inSampleSize值为8 -> 返回的图像将为100 x 100像素。

注意: 使用inSampleSizeBitmapFactory.decodeXXX方法对图像进行缩小的主要目的是在加载明显大于显示需求的图像时保留内存。与上面的代码结合使用的这些方法将始终为您提供最小的图像(按2的幂缩放),该图像大于或等于所请求的边界,而不是适应于该边界的图像。


4
我只能说他们的逻辑看起来是错误的 :( 无论如何,这种方法相当简单,所以重新实现它并添加正确的条件应该不会有太大问题!我的意思是,当你查看decodeSampledBitmapFromResource时,它只想要缩小位图以使其适合所需的边界,因此这必须是一个错误。
编辑:: 对我来说,这看起来甚至更糟糕,因为它在某些情况下不起作用。假设您的宽度= 200,高度= 600。您将最大边界设置为宽度= 100和高度= 500。您的高度>宽度,但是如果您希望它们都适合,则返回结果中的inSampleSize必须为200/100而不是600/500。 因此,基本上,如果您要重新实现该方法,我会这样做:
public static int calculateInSampleSize(
            BitmapFactory.Options options, int reqWidth, int reqHeight) {
    // Raw height and width of image
    final int height = options.outHeight;
    final int width = options.outWidth;

    int stretch_width = Math.round((float)width / (float)reqWidth);
    int stretch_height = Math.round((float)height / (float)reqHeight);

    if (stretch_width <= stretch_height) 
        return stretch_height;
    else 
        return stretch_width;
}

但是他们的代码存在太多问题了,让我难以相信我正确理解了它的观点!

3

在遇到许多缩放问题后,我认为我找到了自己的答案:

使用来自http://developer.android.com/training/displaying-bitmaps/load-bitmap.html的“高效加载大型位图”文章,我想发布一些我的结果(下面是原始代码):

// Calculate ratios of height and width to requested height and width
final int heightRatio = Math.round((float) height / (float) reqHeight);
final int widthRatio = Math.round((float) width / (float) reqWidth);

我目前正在使用分辨率为1280 x 752的Galaxy-Tab 7.7横屏模式进行工作。想象一下以下规格的图像:

1920 x 1200 ..会发生什么?

高度比例= 1920/1280 = 1.5,宽度比例= 1200/752 = 1.59

两个数字都舍入为2,因此如果我理解正确,图像将缩小2倍。这将导致图像变为1920/2 = 960 * 600,小于我所需的分辨率1280 * 752

我通过用floor替换round来解决这个问题:

// Calculate ratios of height and width to requested height and width
final int heightRatio = (int)Math.floor((float) height / (float) reqHeight);
final int widthRatio = (int)Math.floor((float) width / (float) reqWidth);

这实际上防止了我的一些图片被缩放得太小。我目前仍在研究参数inSampleSize,以查看是否可以使用“分数”选项。当前所有尺寸不超过1280x752(*2)= 2560 * 1504的图像将不会被缩放。我正在编写的imageView是图像的详细视图,因此现在应该不会有太大问题。
我与修改后的代码一起使用:
returnview.setAdjustViewBounds(true);

这将防止比我的屏幕大的图像出现混乱的边界框。如果将实际图像设置为颜色背景,您可以看到它。此外,通过现在修复的代码,我可以实现一些onClick处理程序来检测用户是否单击图像之外的区域以关闭图像。


1
他们的逻辑不仅拙劣,而且实际上是错误的。在图像中需要做的不是将其缩小,直到一个参数小于所需值,如果将其减半,而是确保没有任何尺寸大于2048。在此之上,很多时候无法渲染纹理或显示图像。如果您有一张4000x1300大小的图片,并且想要至少768像素大小。它实际上无法将1300减半而不会低于该值。因此程序什么也不做就退出了。会尝试加载一个4000像素宽度的图像,但是会失败。
public static int calculateInSampleSize(BitmapFactory.Options options, int maxWidth, int maxHeight) {
    int height = options.outHeight;
    int width = options.outWidth;
    int inSampleSize = 1;
    while (height > maxHeight || width > maxWidth) {
        height /= 2;
        width /= 2;
        inSampleSize *= 2;
    }
    return inSampleSize;
}

0

我觉得他们的逻辑很有道理

if (height > reqHeight || width > reqWidth) {
    if (width > height) {
        inSampleSize = Math.round((float)height / (float)reqHeight);
    } else {
        inSampleSize = Math.round((float)width / (float)reqWidth);
    }
}

这也太复杂了。我会采用更简单的方法。我的逻辑是,如果图像是横向的,则按宽度缩放图像并忽略所需的高度;如果图像是纵向的,则按高度缩放图像并忽略所需的宽度。

if (height > reqHeight || width > reqWidth) {
        if (width > height) {
            inSampleSize = Math.round((float) width / (float) reqWidth);
        } else {
            inSampleSize = Math.round((float) height / (float) reqHeight);
        }
    }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接