转一篇当图片源大小大于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. SVN和Git的一些用法总结(转)

    转载请注明出处:http://www.codelast.com/ 以下都是比较基础的操作,高手们请绕道,不必浪费时间来看了. (A)SVN (1)查看日志提交的时候一般会写上注释,如果要查看提交日志, ...

  2. MvcMailer通过ASP.NET MVC Razor视图和基架发送邮件

    MvcMailer是一个有趣的组件,您可以使用ASP.NET MVC框架在发送邮件.很重要的是,它使用Razor视图引擎的观点作为电子邮件模板和很容易安装和使用.在本文中你将看到如何安装,设置邮件模板 ...

  3. GitHub版本控制

    版本控制-GitHub 前面几篇文章,我们介绍了Git的基本用法及Git服务器的搭建,本篇文章来学习一下如何使用GitHub.GitHub是开源的代码库以及版本控制库,是目前使用网络上使用最为广泛的服 ...

  4. 2机器学习实践笔记(k-最近邻)

    1:算法是简单的叙述说明 由于训练数据样本和标签,为测试数据的示例,从最近的距离k训练样本,此k练样本中所属类别最多的类即为该測试样本的预測标签. 简称kNN.通常k是不大于20的整数,这里的距离通常 ...

  5. Claris and XOR

    Problem Description Claris loves bitwise operations very much, especially XOR, because it has many b ...

  6. ClassLoader—流程观察程序执行类加载-verbose:class

    当调试器,有时你需要看到程序加载的类.记忆的恢复情况.本地接口调用,等等..这时候就需要-verbose命令. 在myeclipse能够通过右键设置(例如以下).也能够在命令行输入java -verb ...

  7. bigdata_一篇文看懂Hadoop

    本文转载:暂未找到原出处,如需署名 请联系 我们很荣幸能够见证Hadoop十年从无到有,再到称王.感动于技术的日新月异时,希望通过这篇内容深入解读Hadoop的昨天.今天和明天,憧憬下一个十年. 本文 ...

  8. Android开发之控制Toast的开启与关闭

    开发这个程序之前先解释一下,为什么Toast信息提示框在显示一定时间后会自己主动消失?由于在Android系统中有一个Toast队列,系统会依次从这个队列中取出一个Toast,并显示它.在显示了指定时 ...

  9. 通过SMTP协议来发送邮件

    简单邮件传输协议 (Simple Mail Transfer Protocol, SMTP) 是事实上的在Internet传输email的标准. SMTP是一个相对简单的基于文本的协议.在其之上指定了 ...

  10. 访问Ice-Pick Lodge:假设公众筹款网站Kickstarter在成功

    Xsolla非常高兴採訪了来自莫斯科的工作室 Ice-Pick Lodge的Golubeva.数天前,该公司已成功在Kickstarter上募集资金,创造出最知名的游戏"Pathologic ...