调整位图大小/缩放后图像质量较差
我正在编写一个纸牌游戏,需要我的纸牌在不同情况下具有不同的尺寸。我将图像存储为位图,以便可以快速绘制和重绘它们(用于动画)。
我的问题是,无论我如何尝试缩放图像(无论是通过matrix.postScale、matrix.preScale还是createScaledBitmap函数),它们总是会出现像素化和模糊的情况。我知道是缩放导致了问题,因为在不调整大小的情况下绘制图像时看起来很完美。
我已经完成了这两个线程中描述的每个解决方案:
在运行时调整大小的图像的 Android 质量
在运行时调整图像大小时出现质量问题
但仍然没有'没有得到任何地方。
我使用以下代码存储我的位图(到哈希图中):
cardImages = new HashMap<Byte, Bitmap>();
cardImages.put(GameUtil.hearts_ace, BitmapFactory.decodeResource(r, R.drawable.hearts_ace));
并使用此方法绘制它们(在 Card 类中):
public void drawCard(Canvas c)
{
//retrieve the cards image (if it doesn't already have one)
if (image == null)
image = Bitmap.createScaledBitmap(GameUtil.cardImages.get(ID),
(int)(GameUtil.standardCardSize.X*scale), (int)(GameUtil.standardCardSize.Y*scale), false);
//this code (non-scaled) looks perfect
//image = GameUtil.cardImages.get(ID);
matrix.reset();
matrix.setTranslate(position.X, position.Y);
//These methods make it look worse
//matrix.preScale(1.3f, 1.3f);
//matrix.postScale(1.3f, 1.3f);
//This code makes absolutely no difference
Paint drawPaint = new Paint();
drawPaint.setAntiAlias(false);
drawPaint.setFilterBitmap(false);
drawPaint.setDither(true);
c.drawBitmap(image, matrix, drawPaint);
}
任何见解将不胜感激。谢谢
I'm writing a card game and need my cards to be different sizes in different circumstances. I am storing my images as bitmaps so that they can be quickly drawn and redrawn (for animation).
My problem is that no matter how I try and scale my images (whether through a matrix.postScale, a matrix.preScale, or a createScaledBitmap function) they always come out pixelated and blurry. I know that its the scaling thats causing the problem because the images look perfect when drawn without resizing.
I have worked through every solution described in these two threads:
android quality of the images resized in runtime
quality problems when resizing an image at runtime
but still haven't gotten anywhere.
I store my bitmaps (into a hashmap) with this code:
cardImages = new HashMap<Byte, Bitmap>();
cardImages.put(GameUtil.hearts_ace, BitmapFactory.decodeResource(r, R.drawable.hearts_ace));
and draw them with this method (in a Card class):
public void drawCard(Canvas c)
{
//retrieve the cards image (if it doesn't already have one)
if (image == null)
image = Bitmap.createScaledBitmap(GameUtil.cardImages.get(ID),
(int)(GameUtil.standardCardSize.X*scale), (int)(GameUtil.standardCardSize.Y*scale), false);
//this code (non-scaled) looks perfect
//image = GameUtil.cardImages.get(ID);
matrix.reset();
matrix.setTranslate(position.X, position.Y);
//These methods make it look worse
//matrix.preScale(1.3f, 1.3f);
//matrix.postScale(1.3f, 1.3f);
//This code makes absolutely no difference
Paint drawPaint = new Paint();
drawPaint.setAntiAlias(false);
drawPaint.setFilterBitmap(false);
drawPaint.setDither(true);
c.drawBitmap(image, matrix, drawPaint);
}
Any insight would be greatly appreciated. Thanks
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(11)
使用createScaledBitmap 会让你的图像看起来很糟糕。
我遇到了这个问题并且已经解决了。
下面的代码将解决该问题:
Use createScaledBitmap will make your image looks very bad.
I've met this problem and I've resolved it.
Below code will fix the problem:
在我禁用从资源加载位图时的缩放之前,我在低屏幕分辨率下的图像很模糊:
I had blury images on low screen resolutions until I disabled scaling on bitmap load from resources:
createScaledBitmap
有一个标志,您可以在其中设置是否应过滤缩放后的图像。该标志提高了位图的质量......createScaledBitmap
has a flag where you can set if the scaled image should be filtered or not. That flag improves the quality of the bitmap...用作
Paint.FILTER_BITMAP_FLAG
对我来说很有效use as
Paint.FILTER_BITMAP_FLAG
is work for me我假设您正在为低于 3.2 的 Android 版本(API 级别 < 12)编写代码,因为从那时起,方法的行为
已经发生了变化。
在较旧的平台(API 级别 < 12)上,如果找不到任何 alpha,BitmapFactory.decodeFile(..) 方法默认会尝试返回具有 RGB_565 配置的位图,这会降低图像的质量。这仍然没问题,因为您可以使用强制执行 ARGB_8888 位图。
真正的问题是当图像的每个像素的 alpha 值为 255(即完全不透明)时出现。在这种情况下,即使您的位图具有 ARGB_8888 配置,位图的标志“hasAlpha”也会设置为 false。如果您的 *.png 文件至少有一个真正的透明像素,则该标志将被设置为 true,您无需担心任何事情。
因此,当您想要使用
该方法创建缩放位图时,请检查“hasAlpha”标志是否设置为 true 或 false,在您的情况下,将其设置为 false,这会导致获得缩放位图,该位图会自动转换为RGB_565 格式。
因此,在 API 级别 >= 12 上,有一个名为的公共方法
可以解决此问题。到目前为止,这只是对问题的解释。
我做了一些研究,发现 setHasAlpha 方法已经存在很长时间了,并且是公开的,但已被隐藏(@hide 注释)。以下是它在 Android 2.3 上的定义:
现在这是我的解决方案建议。它不涉及任何位图数据的复制:
在运行时使用 java.lang.Reflect 检查当前是否
位图实现有一个公共的“setHasAplha”方法。
(根据我的测试,它从 API 级别 3 开始就完美工作,并且我没有测试较低版本,因为 JNI 不起作用)。如果制造商明确将其设为私有、保护或删除,您可能会遇到问题。
使用 JNI 调用给定 Bitmap 对象的“setHasAlpha”方法。
即使对于私有方法或字段,这也非常有效。据官方说法,JNI 不会检查您是否违反了访问控制规则。
来源: http://java.sun.com/docs/books/jni /html/pitfalls.html (10.9)
这给了我们巨大的力量,我们应该明智地使用它。我不会尝试修改最终字段,即使它可以工作(仅举一个例子)。请注意,这只是一种解决方法...
这是我对所有必要方法的实现:
JAVA 部分:
加载您的库并声明本机方法:
本机部分('jni'文件夹)
Android.mk:bitmapUtils.c
:
就是这样。我们完成了。我已经发布了整个代码以供复制和粘贴之用。
实际的代码并没有那么大,但是进行所有这些偏执的错误检查使它变得更大。我希望这对任何人都有帮助。
I assume you are writing code for a version of Android lower than 3.2 (API level < 12), because since then the behavior of the methods
has changed.
On older platforms (API level < 12) the BitmapFactory.decodeFile(..) methods try to return a Bitmap with RGB_565 config by default, if they can't find any alpha, which lowers the quality of an iamge. This is still ok, because you can enforce an ARGB_8888 bitmap using
The real problem comes when each pixel of your image has an alpha value of 255 (i.e. completely opaque). In that case the Bitmap's flag 'hasAlpha' is set to false, even though your Bitmap has ARGB_8888 config. If your *.png-file had at least one real transparent pixel, this flag would have been set to true and you wouldn't have to worry about anything.
So when you want to create a scaled Bitmap using
the method checks whether the 'hasAlpha' flag is set to true or false, and in your case it is set to false, which results in obtaining a scaled Bitmap, which was automatically converted to the RGB_565 format.
Therefore on API level >= 12 there is a public method called
which would have solved this issue. So far this was just an explanation of the problem.
I did some research and noticed that the setHasAlpha method has existed for a long time and it's public, but has been hidden (@hide annotation). Here is how it is defined on Android 2.3:
Now here is my solution proposal. It does not involve any copying of bitmap data:
Checked at runtime using java.lang.Reflect if the current
Bitmap implementation has a public 'setHasAplha' method.
(According to my tests it works perfectly since API level 3, and i haven't tested lower versions, because JNI wouldn't work). You may have problems if a manufacturer has explicitly made it private, protected or deleted it.
Call the 'setHasAlpha' method for a given Bitmap object using JNI.
This works perfectly, even for private methods or fields. It is official that JNI does not check whether you are violating the access control rules or not.
Source: http://java.sun.com/docs/books/jni/html/pitfalls.html (10.9)
This gives us great power, which should be used wisely. I wouldn't try modifying a final field, even if it would work (just to give an example). And please note this is just a workaround...
Here is my implementation of all necessary methods:
JAVA PART:
Load your lib and declare the native method:
Native section ('jni' folder)
Android.mk:
bitmapUtils.c:
That's it. We are done. I've posted the whole code for copy-and-paste purposes.
The actual code isn't that big, but making all these paranoid error checks makes it a lot bigger. I hope this could be helpful to anyone.
良好的缩小算法(不像最近邻,因此不添加像素化)仅包含 2 个步骤(加上输入/输出图像裁剪的精确矩形的计算):
以下是 SonyMobile 如何解决此任务的详细说明:https://web.archive.org/web/20171011183652/http://developer.sonymobile。 com/2011/06/27/how-to-scale-images-for-your-android-application/
这是 SonyMobile 缩放工具的源代码:
https://web.archive.org/web/20170105181810/http://developer.sonymobile.com:80/downloads/code-example-module/image-scaling-code-example -for-android/
Good downscaling algorithm (not nearest neighbor like, so no pixelation is added) consists of just 2 steps (plus calculation of the exact Rect for input/output images crop):
Here is detailed explanation how SonyMobile resolved this task: https://web.archive.org/web/20171011183652/http://developer.sonymobile.com/2011/06/27/how-to-scale-images-for-your-android-application/
Here is the source code of SonyMobile scale utils:
https://web.archive.org/web/20170105181810/http://developer.sonymobile.com:80/downloads/code-example-module/image-scaling-code-example-for-android/
如果放大位图,您永远不会得到完美的结果。
您应该从所需的最高分辨率开始,然后按比例缩小。
放大位图时,缩放无法猜测每个现有点之间缺少的点,因此它要么复制邻居(=> 尖锐),要么计算邻居之间的平均值(=> 模糊)。
You will never have a perfect result if you scale your bitmaps up.
You should start with the highest resolution you need and scale down.
When scaling a bitmap up, the scaling can't guess what are the missing points between each existing point, so it either duplicates a neighbour (=> edgy) or calculates a mean value between neighbours (=> blurry).
我刚刚使用了标志
filter=true
bitmap = Bitmap.createScaledBitmap(bitmap, width, height, true);
为了模糊。
I just used flag
filter=true
inbitmap = Bitmap.createScaledBitmap(bitmap, width, height, true);
for blur.
如果您想要高质量的结果,请使用 [RapidDecoder][1] 库。很简单:
如果您想缩小小于 50% 并获得 HQ 结果,请不要忘记使用内置解码器。我在 API 8 上测试过。
If you want high quality result, so use [RapidDecoder][1] library. It is simple as follow:
Don't forget to use builtin decoder if you want to scale down less than 50% and a HQ result. I tested it on API 8.
将 Android 目标框架从 Android 8.1 更新到 Android 9 时出现此问题,并在我的 ImageEntryRenderer 上体现出来。希望这有帮助
注意:我正在 Xamarin 3.4.0.10 框架下开发
Had this issue upon updating Android Target Framework from Android 8.1 to Android 9 and manifested on my ImageEntryRenderer. Hope this helps
Note: I am developing under Xamarin 3.4.0.10 framework