转一篇当图片源大小大于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. (大数据工程师学习路径)第二步 Vim编辑器----Vim快速入门

    vim模式介绍 以下介绍内容来自维基百科Vim 从vi演生出来的Vim具有多种模式,这种独特的设计容易使初学者产生混淆.几乎所有的编辑器都会有插入和执行命令两种模式,并且大多数的编辑器使用了与Vim截 ...

  2. 二叉搜索树(Binary Search Tree)--C语言描述(转)

    图解二叉搜索树概念 二叉树呢,其实就是链表的一个二维形式,而二叉搜索树,就是一种特殊的二叉树,这种二叉树有个特点:对任意节点而言,左孩子(当然了,存在的话)的值总是小于本身,而右孩子(存在的话)的值总 ...

  3. javaEE异常摘要——更换工作区相同tomcat当部署在同一个项目疑难解答

    我有一个项目,我的工作区公告,没问题,它可以运行正常,但我把项目copy还有一个工作空间,然后发布到tomcat(随着tomcat,先前的工作空间remove deployment,公布信息)上去,想 ...

  4. open-flash-chart2各种效果

    <pre class="html" name="code"><pre class="html" name="co ...

  5. java线 生产者和消费者

    watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvbGlhbmdydWkxOTg4/font/5a6L5L2T/fontsize/400/fill/I0JBQk ...

  6. strip 使用命令

    使用 通过消除使用调试器的粘合剂和符号信息,减少扩展公共对象文件格式(XCOFF)对象文件大小. 语法 strip [ -V ] [ -r [ -l ] | -x [ -l ] | -t | -H | ...

  7. AspNet.WebAPI.OData.ODataPQ实现WebAPI的分页查询服务-(个人拙笔)

    AspNet.WebAPI.OData.ODataPQ 这是针对 Asp.net WebAPI OData 协议下,查询分页.或者是说 本人在使用Asp.Net webAPI 做服务接口时写的一个分页 ...

  8. Web字体@font-face对于中文字体的使用

    今天算是刚开始玩博客园..感觉很新鲜在首页 上看到了一个博客http://www.cnblogs.com/liuminghai/p/4238256.html是关于web文字的,挺不错 但是B/S的前端 ...

  9. Android Application.java以及它的作用

    What is Application Application和Activity,Service一样是android框架的一个系统组件,当android程序启动时系统会创建一个 application ...

  10. Android锁定EditText内容和随机生成验证码

    昨天写了个小Demo,实现了随机生成验证码,和锁定EditText两个小功能,先看一下效果图: 锁定EditText在我们不须要用户编辑EditText内容的时候能够用到,实现还是非常easy的,一行 ...