转一篇当图片源大小大于ImageView大小定死时的处理方法,但不适用于图片大小小于ImageView时的情况,因为inSampleSize不能<1, 谁有特别好的放大的解决方案,除了设置ImageView固定大小让其自动放大。如果用createScaledBitmap至少要生成两次bitmap。

原文地址:http://developer.sonymobile.com/2011/06/27/how-to-scale-images-for-your-android-application/

How to scale images for your Android™ application

Hard to get images scaled correctly for your application? Are your images too large and causing memory problems? Or are they scaled incorrectly with a poor user experience as a result? To find a good solution for this, we asked Andreas Agvard from the Sony Ericsson software department to help shed some light on this topic.

Note: we are aware about the code examples not being displayed properly. We are currently working on a fix for this. In the meantime, you can download this article as a PDF, where the code examples are correctly displayed.

Andreas Agvard, Sony Ericsson.

Working in the Sony Ericsson software department, I often come across applications where image scaling is needed, for example when handling images from external sources such as content providers or the web. Scaling is needed since the image you wish to present usually doesn’t fit the way you wish to present the image.

This is typical if you are developing a LiveView™ extension for your application. Most the people developing applications utilising LiveView™ and other second screen devices, probably need to rescale images, where it will be important to maintain a proper ratio and image quality. This is of course applicable in a lot other cases as well. Rescaling images can be a bit difficult to do in an effective way.

ImageView solves many scaling problems, at least as long as you can set an image source directly without decoding or scaling the image yourself first. But sometimes you need to take control of the decoding yourself, and that is where this tutorial comes in. Along with this tutorial, I’ve written a code sample project. Download the image scaling code example project from Developer World  to learn more. The results presented in this text can be achieved by compiling and running that project.

Isolating the problem
I’ve made this tutorial because I’ve implemented a number of useful utility methods for doing scaling in a way that avoids the most common image scaling pitfalls. Pitfalls such as the naive example below:

Bitmap unscaledBitmap = BitmapFactory.decodeResource(getResources(), mSourceId);
Bitmap scaledBitmap = Bitmap.createScaledBitmap(unscaledBitmap, wantedWidth, wantedHeight, true);

So what is correct and what is wrong in the code above then? Let’s look at the different lines of code.

Line 1: The entire source image is decoded to a bitmap.

  • This might cause an out of memory error if the image is too large.
  • This might result in a decoded image with a higher resolution than required. It might also be unnecessarily slow as smart decoders can scale when decoding at improved performance.
  • Scaling an image a lot, as when scaling a high resolution bitmap to a low resolution, causes aliasingproblems. Using bitmap filtering (for example, passing true as the latter parameter to Bitmap.createScaledBitmap(…)) reduces the aliasing but is not enough when a lot of scaling is applied.

Line 2: The decoded bitmap is scaled to the wanted size.

  • The aspect ratio of the source image dimensions and the wanted image dimensions may not be the same. This will result in a stretched image.

Left image: Original image. Right image: Image scaled down with a naive method. Aliasing problems can be seen such as one eye having a sharp highlight and the other having none. Stretching occurs on the height.

Creating a solution
Our solution will have a structure similar to the code above with where one part will replace line 1, where we decode an image in preparation for scaling. Another part will be to replace line 2, and do the final scaling. We’ll start with the part replacing of line 2 as it will introduce two new concepts, crop and fit, which will impact the solution for replacing line 1 as well.

Replacing line 2
In this part we are scaling the bitmap according to our needs. This step is necessary since the decoding line that precedes this will have limited capabilities to scale. Also in this step, we might have to adjust the wanted size of our image if we wish to avoid stretching.

To avoid stretching, there are two possibilities. Either we adjust the wanted dimensions by making sure they have the same aspect ratio as the source image, i.e. scaling the source image until it fits within the wanted dimensions, or we crop the source image with an area that has the same aspect ratio as the wanted dimensions.

Left image: Image scaled by the fit method. Image has been scaled to fit within the wanted dimensions and as a result the height of the image is smaller than the wanted height. Right image: Image scaled by the crop method. Image has been scaled to fit at least one of the wanted dimensions and as a result the source has been cropped, cutting away the left and right parts of the source image.

In order to scale like this we implement the following method:

public static Bitmap createScaledBitmap(Bitmap unscaledBitmap, int dstWidth, int dstHeight, ScalingLogic scalingLogic) {
Rect srcRect = calculateSrcRect(unscaledBitmap.getWidth(), unscaledBitmap.getHeight(), dstWidth, dstHeight, scalingLogic);
Rect dstRect = calculateDstRect(unscaledBitmap.getWidth(), unscaledBitmap.getHeight(), dstWidth, dstHeight, scalingLogic);
Bitmap scaledBitmap = Bitmap.createBitmap(dstRect.width(), dstRect.height(), Config.ARGB_8888);
Canvas canvas = new Canvas(scaledBitmap);
canvas.drawBitmap(unscaledBitmap, srcRect, dstRect, new Paint(Paint.FILTER_BITMAP_FLAG));
return scaledBitmap;
}

In the code above, we use canvas.drawBitmap(…) to do the scaling. This method crops the area specified by the source rectangle from the source image and scales it to an area in the canvas defined by the destination rectangle. In order to avoid stretching, these two rectangles need to have the same aspect ratio. We also call two utility methods, one for creating the source rectangle and another for creating the destination rectangle. These are implemented like this:

public static Rect calculateSrcRect(int srcWidth, int srcHeight, int dstWidth, int dstHeight, ScalingLogic scalingLogic) {
if (scalingLogic == ScalingLogic.CROP) {
final float srcAspect = (float)srcWidth / (float)srcHeight;
final float dstAspect = (float)dstWidth / (float)dstHeight;
if (srcAspect > dstAspect) {
final int srcRectWidth = (int)(srcHeight * dstAspect);
final int srcRectLeft = (srcWidth - srcRectWidth) / 2;
return new Rect(srcRectLeft, 0, srcRectLeft + srcRectWidth, srcHeight);
} else {
final int srcRectHeight = (int)(srcWidth / dstAspect);
final int scrRectTop = (int)(srcHeight - srcRectHeight) / 2;
return new Rect(0, scrRectTop, srcWidth, scrRectTop + srcRectHeight);
}
} else {
return new Rect(0, 0, srcWidth, srcHeight);
}
}
public static Rect calculateDstRect(int srcWidth, int srcHeight, int dstWidth, int dstHeight, ScalingLogic scalingLogic) {
if (scalingLogic == ScalingLogic.FIT) {
final float srcAspect = (float)srcWidth / (float)srcHeight;
final float dstAspect = (float)dstWidth / (float)dstHeight;
if (srcAspect > dstAspect) {
return new Rect(0, 0, dstWidth, (int)(dstWidth / srcAspect));
} else {
return new Rect(0, 0, (int)(dstHeight * srcAspect), dstHeight);
}
} else {
return new Rect(0, 0, dstWidth, dstHeight);
}
}

The source rectangle will be the entire source dimension in the fit case. In the crop case, it is calculated to have the same aspect ratio as the destination image, resulting either in the width or the height of the source image being cropped. The destination rectangle will be the entire wanted dimension in the crop case. In the fit case, it will have the same aspect ratio as the source image, resulting in either the width or the height of the wanted dimensions being adjusted.

Replacing line 1
Decoders are smart, especially the ones used for the JPEG and PNG formats. These decoders can scale the image when decoding, with improved performance.  When doing so, aliasing problems are also avoided. Also, since the image is smaller after decoding, less memory will be needed.

Scaling when decoding is as simple as setting the inSampleSize parameter on a BitmapFactory.Options object and passing it to the BitmapFactory when decoding. The sample size specifies a factor of which each side of the image is scaled, for example a factor of 2 on a 640×480 image will result in a 320×240 image being decoded. When setting a sample size, you are not guaranteed the image will be scaled down exactly according to that number, but at least it will never be smaller. For example, a factor of 3 on a 640×480 image could result in a 320×240 image since the value 3 might not be supported. Commonly, at least the first powers of 2 are supported [1, 2, 4, 8, …].

The next step is to specify a proper sample size. The proper sample size would be the one resulting in the largest amount of scaling, but still being equal to or larger than the wanted image dimensions. This is implemented like this:

public static Bitmap decodeFile(String pathName, int dstWidth, int dstHeight, ScalingLogic scalingLogic) {
Options options = new Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(pathName, options);
options.inJustDecodeBounds = false;
options.inSampleSize = calculateSampleSize(options.outWidth, options.outHeight, dstWidth, dstHeight, scalingLogic);
Bitmap unscaledBitmap = BitmapFactory.decodeFile(pathName, options);
return unscaledBitmap;
}
public static int calculateSampleSize(int srcWidth, int srcHeight, int dstWidth, int dstHeight, ScalingLogic scalingLogic) {
if (scalingLogic == ScalingLogic.FIT) {
final float srcAspect = (float)srcWidth / (float)srcHeight;
final float dstAspect = (float)dstWidth / (float)dstHeight;
if (srcAspect > dstAspect) {
return srcWidth / dstWidth;
} else {
return srcHeight / dstHeight;
}
} else {
final float srcAspect = (float)srcWidth / (float)srcHeight;
final float dstAspect = (float)dstWidth / (float)dstHeight;
if (srcAspect > dstAspect) {
return srcHeight / dstHeight;
} else {
return srcWidth / dstWidth;
}
}
}

In the decodeFile(…) method, we decode a file optimized for the final downscaling. This is done by first decoding only the dimensions of the source image, then calculating the optimal sample size using calculateSampleSize(…), and finally decoding the image using this sample size. I’ll leave it up to you if you’d like to dig deeper into understanding the calculateSampleSize(…) method. But basically it makes sure the image is scaled as much as possible while still being equal to, or larger, than the source rectangle that was applied before.

Putting it all together
With the help from the utility methods specified above we can now implement the following replacement lines for the initial code presented:

Bitmap unscaledBitmap = decodeFile(pathname, dstWidth, dstHeight, scalingLogic);
Bitmap scaledBitmap = createScaledBitmap(unscaledBitmap, dstWidth, dstHeight, scalingLogic);

Left image: Done by a naive solution on mdpi device, decoding consumed 6693 kb of memory and took about 1/4 second. The result is stretched and suffers from aliasing artifacts. Middle image: Achived by the fit solution on mdpi device, decoding consumed 418 kb of memory and took about 1/10 second . Right image: Achived by the crop solution on mdpi device, decoding consumed 418 kb of memory and took about 1/10 second.

To learn more, download our code sample project. With this project, you can see the results on your Android phone and follow the flow in the source code.

Feel free to comment and ask questions about this topic in the forum thread regarding image scaling for Androidon Google groups.

Andreas Agvard
Sony Ericsson Software department

More information:

[转]当图片源大小大于ImageView大小时的处理方式(缩放)的更多相关文章

  1. chart.js插件生成折线图时数据普遍较大时Y轴数据不从0开始的解决办法[bubuko.com]

    chart.js插件生成折线图时数据普遍较大时Y轴数据不从0开始的解决办法,原文:http://bubuko.com/infodetail-328671.html 默认情况下如下图 Y轴并不是从0开始 ...

  2. 【转帖】自助式BI的崛起:三张图看清商业智能和大数据分析市场趋势

    自助式BI的崛起:三张图看清商业智能和大数据分析市场趋势 大数据时代,商业智能和数据分析软件市场正在经历一场巨变,那些强调易用性的,人人都能使用的分析软件正在取代传统复杂的商业智能和分析软件成为市场的 ...

  3. Unity3D研究院之动态修改烘培贴图的大小&脚本烘培场景

    Unity默认烘培场景以后每张烘培贴图的大小是1024.但是有可能你的场景比较简单,用1024会比较浪费.如下图所示,这是我的一个场景的烘培贴图,右上角一大部分完全是没有用到,但是它却占着空间.  有 ...

  4. java GUI 返回图片源码

    返回图片源码,重开一个类粘贴即可 package cn.littlepage.game; import java.awt.Image; import java.awt.image.BufferedIm ...

  5. Winform中使用FastReport的PictureObject时通过代码设置图片源并使Image图片旋转90度

    场景 FastReport安装包下载.安装.去除使用限制以及工具箱中添加控件: https://blog.csdn.net/BADAO_LIUMANG_QIZHI/article/details/10 ...

  6. 排查在 Azure 中创建、重启 Windows VM 或调整其大小时发生的分配失败

    创建 VM.重新启动已停止(解除分配)的 VM 和重设 VM 大小时,Azure 会为订阅分配计算资源. 执行这些操作时,即使尚未达到 Azure 订阅限制,也可能偶尔收到错误. 本文说明一些常见分配 ...

  7. 排查在 Azure 中创建、重启 Linux VM 或调整其大小时发生的分配故障

    创建 VM.重启已停止(解除分配)的 VM 和重设 VM 大小时,Azure 会为订阅分配计算资源. 执行这些操作时,即使尚未达到 Azure 订阅限制,也可能偶尔收到错误. 本文说明一些常见分配故障 ...

  8. null值与非null只比较大小时,只会返回false

    DateTime? time=null; DateTime now=DateTime.Now; null值与非null只比较大小时,只会返回false 无论是大于比较还是小于比较还是等于,都会返回fa ...

  9. PyQt通过resize改变窗体大小时ListWidget显示异常

    前几天开始的pygame音乐播放器Doco,做的差不多了,上午做到了歌词显示和搜索页面.遇到bug,即通过resize改变ui大小时ListWidget显示异常 #目的: 增加一部分窗口用来显示歌词和 ...

随机推荐

  1. android 有效载荷大图,避OOM

    我们的项目往往会载入图片.有时,承担太多,再装图片,它导致了非常小的程序卡,而在铅oom从而导致异常app再见,今天翻译google官方网站,它已经做了很好的图像处理汇总,由于Google我们已经给解 ...

  2. 第四章:Django 的模板系统(转)

    在之前的章节中,你可能觉得例子中视图返回文本有点不妥.即是, HTML 是直接写在 Python 代码中的.     这种做法会导致这些问题:     要做任何设计上的更改就必须改写 Python 代 ...

  3. C 这些东西的内存管理

    一.内存介绍 本文主要介绍C内存管理基本概念,以及C语言编译后的可执行程序的存储结构和执行结构. 在用户存储空间,一个C程序的在内存中的分配分类5大部分:代码段.全局已初始化数据段.bss段.堆和栈. ...

  4. python fabric远程操作和部署

    博客迁往:新地址(点击直达) 新博客使用markdown维护,线下有版本号库,自己写的所以会定时更新同步.同一时候提供更好的导航和阅读体验 csdn对markdown支持不好.所以旧版不会花时间进行同 ...

  5. Swiftly语言学习1

    单纯值: 1.let常量声明,var声明变量(同时宣布福值,编译器会自己主动判断出类型) var myVariable = 42 myVariable 50 let myConstant = 42 l ...

  6. 基于 Android 的 3D 视频样本代码

    作者:Mark Liu 下载样本代码 简单介绍 在Android 中,创建一个可以播放视频剪辑的应用很easy:创建一个採用 3D 图形平面的游戏应用也很easy.可是,创建一个可以在 3D 图形对象 ...

  7. INF 右安装驱动程序和卸载

    INF 右键安装驱动以及卸载 之前写过一篇文章是关于INF文件具体解释的.大家能够參看INF文件具体解释,这次写的是关于INF右键安装,这样比較方便. 卸载的话也是一句话.能够大大降低安装时间: 先将 ...

  8. iOS10收集IDFA,植入第三方广告[终结]--ADMob

    [PS: 前段时间,公司做ASO推广,需要在应用中收集IDFA值,跟广告平台做交互!于是有了这个需求--] 1.首先,考虑了一下情况(自己懒 -_-#),就直接在首页上写了一个Banner,循环加载广 ...

  9. 【转】Appium基于安卓的各种FindElement的控件定位方法实践

    原文地址:http://blog.csdn.net/zhubaitian/article/details/39754041#t11 AppiumDriver的各种findElement方法的尝试,尝试 ...

  10. POJ 3299 Humidex(简单的问题)

    [简要题意]:什么是温度,湿度--,之间的转换.. [分析]:式已被赋予. // 252k 0Ms /* 当中exp表示的是求e的x次幂 解法就直接依据题目中的公式解决就好!! */ #include ...