转一篇当图片源大小大于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. Flux demo

    Flux demo Introduction flux应用架构如下图所示,本文并不是讲述怎么立即做一个酷炫的应用,而是讲述如何依照这种框架,来进行代码的组织.我们先把这个流程转述为文字:抛开与webA ...

  2. ListView单选的实现总结(转)

    今天在智能停车场项目中需要实现PullToRefreshListView的单选功能,考虑到分页,刷新等,以前的实现方式是采用自己维护一个集合保存选中位置的选中状态,但这个方式比较繁琐,今天采用了lis ...

  3. UVALive 5099 Nubulsa Expo 全球最小割 非网络流量 n^3

    主题链接:点击打开链接 意甲冠军: 给定n个点m条无向边 源点S 以下m行给出无向边以及边的容量. 问: 找一个汇点,使得图的最大流最小. 输出最小的流量. 思路: 最大流=最小割. 所以题意就是找全 ...

  4. Unix / 类 Unix shell 中有哪些很酷很冷门很少用很有用的命令?(转)

    著作权归作者所有. 商业转载请联系作者获得授权,非商业转载请注明出处. 作者:孙立伟 链接:http://www.zhihu.com/question/20140085/answer/14107336 ...

  5. malloc,free简单的实现

    有关标准库首先简要malloc其原理:     标准库内部通过一个双向链表.管理在堆中动态分配的内存.     malloc函数分配内存时会附加若干(一般是12个)字节,存放控制信息.     该信息 ...

  6. Map <STL>

    map的使用方法: #include <cstdio> #include <map> #include <string> using namespace std; ...

  7. 第2章 简单工厂模式(Sample Factory)

    原文 第2章 简单工厂模式(Sample Factory) 一般用到的场景:对象多次被实例引用,切有可能会发生变化 拿我们的简单三层举例子 先定义dal层 1 2 3 4 5 6 7 8     cl ...

  8. ASP.NET——两个下拉框来实现动态联动

    介绍: 在网页中.我们常常会遇到下图中的情况.首先在下拉框中选择所在的省.选择之后,第二个下拉框会自己主动载入出该省中的市.这样设计极大的方便了用户的查找.那这是怎样实现的呢? 1.建立数据库 &qu ...

  9. 删除句子UITableView额外的底线和切割线

    于viewDidLoad添加代码功能句子: self.tableView.tableFooterView = [[UIView alloc] initWithFrame:CGRectZero]; 它可 ...

  10. IOS的UITextField,UIButton,UIWebView它描述的一些属性和IOS提示图像资源

    有时UI要开发的资源与实际frame不符.这一次,我们要绘制图片 UIImage* image = [[UIImage imageNamed:@"text_field_bg.png" ...