Android 是不是;位图.createScaledBitmap";将32位图像转换为24位?
在我的应用程序中,我以32位(ARGB_8888)的方式加载图像:Android 是不是;位图.createScaledBitmap";将32位图像转换为24位?,android,bitmap,Android,Bitmap,在我的应用程序中,我以32位(ARGB_8888)的方式加载图像: Bitmap.Config mBitmapConfig; mBitmapConfig = Bitmap.Config.ARGB_8888; BitmapFactory.Options options = new BitmapFactory.Options(); options.inPreferredConfig = mBitmapConfig; mBitmap = BitmapFactory.decodeFile(S
Bitmap.Config mBitmapConfig;
mBitmapConfig = Bitmap.Config.ARGB_8888;
BitmapFactory.Options options = new BitmapFactory.Options();
options.inPreferredConfig = mBitmapConfig;
mBitmap = BitmapFactory.decodeFile(SourceFileName, options);
然后缩放:
mBitmap = Bitmap.createScaledBitmap(mBitmap, iW, iH, true);
如果我使用缩放原始位图的相同宽度和高度,它是大小的1/2(兆字节)(我在观察堆大小)。
将值“ARGB_8888”更改为“RGB_565”(24位),则缩放后的大小以兆字节为单位
有人能解释一下这种现象吗?也许能给我一个建议,如何在32位颜色空间中缩放位图?
谢谢 我在源代码中查找了Bitmap类()的createScaledBitmap方法: 调用createBitmap()应返回未更改的源位图,因为方法体中有此检查:
if (!source.isMutable() && x == 0 && y == 0 && width == source.getWidth() &&
height == source.getHeight() && (m == null || m.isIdentity())) {
return source;
}
仅看这一点,您的原始位图似乎已返回,但是,如果您的位图碰巧是可变的,您实际上会跳过此检查,并在此处结束:
if (m == null || m.isIdentity()) {
bitmap = createBitmap(neww, newh,
source.hasAlpha() ? Config.ARGB_8888 : Config.RGB_565);
paint = null; // not needed
}
由于不执行任何缩放,因此矩阵将是单位矩阵,并且满足条件。如您所见,创建的位图取决于源位图中的alpha。如果不存在alpha,则最终得到的结果位图为RGB_565格式,而不是ARGB_8888格式
因此,要缩放并保留32位格式,位图应该是不可变的,或者使用Alpha通道。可以轻松创建自己的版本,保持源的像素格式:
public static Bitmap CreateScaledBitmap(Bitmap src, int dstWidth, int dstHeight, bool filter)
{
var m = new Matrix();
m.SetScale(dstWidth / (float)src.Width, dstHeight / (float)src.Height);
var result = Bitmap.CreateBitmap(dstWidth, dstHeight, src.GetConfig());
using (var canvas = new Canvas(result))
{
var paint = new Paint();
paint.FilterBitmap = filter;
canvas.DrawBitmap(src, m, paint);
}
return result;
}
(代码是针对Monodroid的,但应该很容易翻译成Java)色带已解决ooooooooyyyyyyyaaaaaaaaaaaa 我分两个阶段解决了色带问题 1) *当我们使用BitmapFactory解码资源时,它会解码RGB565中显示色带的资源,而不是使用ARGB_8888,因此我使用BitmapFactory.Options将解码选项设置为ARGB_8888 第二个问题是,每当我缩放位图时,它就会再次被绑定 2) 这是一个艰难的部分,经过了大量的搜索,终于成功了 *用于缩放位图的方法Bitmap.createScaledBitmap在缩放我得到的带状图像后也将图像缩减为RGB565格式(解决此问题的旧方法是在png中使用至少一个透明像素,但没有其他格式(如jpg或bmp)工作)因此,我在这里创建了一个方法CreateScaledBitmap,以使用生成的缩放位图中的原始位图配置来缩放位图(实际上,我从logicnet.dk的文章中复制了该方法,并用java翻译) //功能
public static Bitmap CreateScaledBitmap(Bitmap src, int dstWidth, int dstHeight, boolean filter)
{
Matrix m = new Matrix();
m.setScale(dstWidth / (float)src.getWidth(), dstHeight / (float)src.getHeight());
Bitmap result = Bitmap.createBitmap(dstWidth, dstHeight, src.getConfig());
Canvas canvas = new Canvas(result);
Paint paint = new Paint();
paint.setFilterBitmap(filter);
canvas.drawBitmap(src, m, paint);
return result;
}
如果我错了,请纠正我。
如果它对你有用,也要发表评论
我很高兴我解决了它,希望它能为您工作。我假设您正在为低于3.2(API级别<12)的Android版本编写代码,因为从那时起,这些方法的行为
BitmapFactory.decodeFile(pathToImage);
BitmapFactory.decodeFile(pathToImage, opt);
bitmapObject.createScaledBitmap(bitmap, desiredWidth, desiredHeight, false /*filter?*/);
已经改变了
在较旧的平台(API级别<12)上,BitmapFactory.decodeFile(..)方法如果找不到任何alpha,则会尝试返回默认为RGB_565配置的位图,这会降低图像质量。这仍然可以,因为您可以使用
options.inPrefferedConfig = Bitmap.Config.ARGB_8888
options.inDither = false
bitmapObject.createScaledBitmap(bitmap, desiredWidth, desiredHeight, false /*filter?*/);
当图像的每个像素的alpha值为255(即完全不透明)时,真正的问题就会出现。在这种情况下,位图的标志“hasAlpha”设置为false,即使位图具有ARGB_8888配置。如果您的*.png文件至少有一个真正的透明像素,那么该标志将被设置为true,您就不必担心任何事情
因此,当您要使用创建缩放位图时
options.inPrefferedConfig = Bitmap.Config.ARGB_8888
options.inDither = false
bitmapObject.createScaledBitmap(bitmap, desiredWidth, desiredHeight, false /*filter?*/);
该方法检查“hasAlpha”标志是否设置为true或false,在您的情况下,该标志设置为false,从而获得缩放位图,该位图已自动转换为RGB_565格式
因此,在API级别>=12时,有一个名为
public void setHasAlpha (boolean hasAlpha);
这本可以解决这个问题。到目前为止,这只是对问题的一种解释。
我做了一些研究,发现setHasAlpha方法已经存在很长时间了,它是公开的,但是已经被隐藏了(@hide annotation)。以下是它在Android 2.3上的定义:
/**
* Tell the bitmap if all of the pixels are known to be opaque (false)
* or if some of the pixels may contain non-opaque alpha values (true).
* Note, for some configs (e.g. RGB_565) this call is ignore, since it does
* not support per-pixel alpha values.
*
* This is meant as a drawing hint, as in some cases a bitmap that is known
* to be opaque can take a faster drawing case than one that may have
* non-opaque per-pixel alpha values.
*
* @hide
*/
public void setHasAlpha(boolean hasAlpha) {
nativeSetHasAlpha(mNativeBitmap, hasAlpha);
}
这是我的解决方案。它不涉及位图数据的任何复制:
// NOTE: this cannot be used in switch statements
private static final boolean SETHASALPHA_EXISTS = setHasAlphaExists();
private static boolean setHasAlphaExists() {
// get all puplic Methods of the class Bitmap
java.lang.reflect.Method[] methods = Bitmap.class.getMethods();
// search for a method called 'setHasAlpha'
for(int i=0; i<methods.length; i++) {
if(methods[i].getName().contains("setHasAlpha")) {
Log.i(TAG, "method setHasAlpha was found");
return true;
}
}
Log.i(TAG, "couldn't find method setHasAlpha");
return false;
}
private static void setHasAlpha(Bitmap bitmap, boolean value) {
if(bitmap.hasAlpha() == value) {
Log.i(TAG, "bitmap.hasAlpha() == value -> do nothing");
return;
}
if(!SETHASALPHA_EXISTS) { // if we can't find it then API level MUST be lower than 12
// couldn't find the setHasAlpha-method
// <-- provide alternative here...
return;
}
// using android.os.Build.VERSION.SDK to support API level 3 and above
// use android.os.Build.VERSION.SDK_INT to support API level 4 and above
if(Integer.valueOf(android.os.Build.VERSION.SDK) <= 11) {
Log.i(TAG, "BEFORE: bitmap.hasAlpha() == " + bitmap.hasAlpha());
Log.i(TAG, "trying to set hasAplha to true");
int result = setHasAlphaNative(bitmap, value);
Log.i(TAG, "AFTER: bitmap.hasAlpha() == " + bitmap.hasAlpha());
if(result == -1) {
Log.e(TAG, "Unable to access bitmap."); // usually due to a bug in the own code
return;
}
} else { //API level >= 12
bitmap.setHasAlpha(true);
}
}
/**
* Decodes a Bitmap from the SD card
* and scales it if necessary
*/
public Bitmap decodeBitmapFromFile(String pathToImage, int pixels_limit) {
Bitmap bitmap;
Options opt = new Options();
opt.inDither = false; //important
opt.inPreferredConfig = Bitmap.Config.ARGB_8888;
bitmap = BitmapFactory.decodeFile(pathToImage, opt);
if(bitmap == null) {
Log.e(TAG, "unable to decode bitmap");
return null;
}
setHasAlpha(bitmap, true); // if necessary
int numOfPixels = bitmap.getWidth() * bitmap.getHeight();
if(numOfPixels > pixels_limit) { //image needs to be scaled down
// ensures that the scaled image uses the maximum of the pixel_limit while keeping the original aspect ratio
// i use: private static final int pixels_limit = 1280*960; //1,3 Megapixel
imageScaleFactor = Math.sqrt((double) pixels_limit / (double) numOfPixels);
Bitmap scaledBitmap = Bitmap.createScaledBitmap(bitmap,
(int) (imageScaleFactor * bitmap.getWidth()), (int) (imageScaleFactor * bitmap.getHeight()), false);
bitmap.recycle();
bitmap = scaledBitmap;
Log.i(TAG, "scaled bitmap config: " + bitmap.getConfig().toString());
Log.i(TAG, "pixels_limit = " + pixels_limit);
Log.i(TAG, "scaled_numOfpixels = " + scaledBitmap.getWidth()*scaledBitmap.getHeight());
setHasAlpha(bitmap, true); // if necessary
}
return bitmap;
}
本机节('jni'文件夹)
Android.mk:
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := bitmaputils
LOCAL_SRC_FILES := bitmap_utils.c
LOCAL_LDLIBS := -llog -ljnigraphics -lz -ldl -lgcc
include $(BUILD_SHARED_LIBRARY)
bitmapUtils.c:
#include <jni.h>
#include <android/bitmap.h>
#include <android/log.h>
#define LOG_TAG "BitmapTest"
#define Log_i(...) __android_log_print(ANDROID_LOG_INFO,LOG_TAG,__VA_ARGS__)
#define Log_e(...) __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__)
// caching class and method IDs for a faster subsequent access
static jclass bitmap_class = 0;
static jmethodID setHasAlphaMethodID = 0;
jint Java_com_example_bitmaptest_MainActivity_setHasAlphaNative(JNIEnv * env, jclass clazz, jobject bitmap, jboolean value) {
AndroidBitmapInfo info;
void* pixels;
if (AndroidBitmap_getInfo(env, bitmap, &info) < 0) {
Log_e("Failed to get Bitmap info");
return -1;
}
if (info.format != ANDROID_BITMAP_FORMAT_RGBA_8888) {
Log_e("Incompatible Bitmap format");
return -1;
}
if (AndroidBitmap_lockPixels(env, bitmap, &pixels) < 0) {
Log_e("Failed to lock the pixels of the Bitmap");
return -1;
}
// get class
if(bitmap_class == NULL) { //initializing jclass
// NOTE: The class Bitmap exists since API level 1, so it just must be found.
bitmap_class = (*env)->GetObjectClass(env, bitmap);
if(bitmap_class == NULL) {
Log_e("bitmap_class == NULL");
return -2;
}
}
// get methodID
if(setHasAlphaMethodID == NULL) { //initializing jmethodID
// NOTE: If this fails, because the method could not be found the App will crash.
// But we only call this part of the code if the method was found using java.lang.Reflect
setHasAlphaMethodID = (*env)->GetMethodID(env, bitmap_class, "setHasAlpha", "(Z)V");
if(setHasAlphaMethodID == NULL) {
Log_e("methodID == NULL");
return -2;
}
}
// call java instance method
(*env)->CallVoidMethod(env, bitmap, setHasAlphaMethodID, value);
// if an exception was thrown we could handle it here
if ((*env)->ExceptionOccurred(env)) {
(*env)->ExceptionDescribe(env);
(*env)->ExceptionClear(env);
Log_e("calling setHasAlpha threw an exception");
return -2;
}
if(AndroidBitmap_unlockPixels(env, bitmap) < 0) {
Log_e("Failed to unlock the pixels of the Bitmap");
return -1;
}
return 0; // success
}
#包括
#包括
#包括
#定义日志标签“BitmapTest”
#定义日志i(…)\uuuuuAndroid\uLog\uPrint(android\uLog\u信息、日志标签、参数)
#定义日志(…)\uuuuuAndroid\uLog\uPrint(android\uLog\uError,Log\uTag,\uuu VA\uArgs\uuuuuu)
//缓存类和方法ID以加快后续访问
静态jclass位图_class=0;
静态jmethodID setHasAlphaMethodID=0;
jint Java_com_示例_bitmaptest_MainActivity_setHasAlphaNative(JNIEnv*env、jclass clazz、jobject位图、jboolean值){
AndroidBitmapInfo信息;
空*像素;
if(AndroidBitmap_getInfo(环境、位图和信息)<0){
日志(无法获取位图信息);
返回-1;
}
如果(info.format!=ANDROID\u位图\u格式\u RGBA\u 8888){
日志(“不兼容的位图格式”);
返回-1;
}
if(AndroidBitmap_lockPixels(环境、位图和像素)<0){
Log_e(“未能锁定位图的像素”);
重新
#include <jni.h>
#include <android/bitmap.h>
#include <android/log.h>
#define LOG_TAG "BitmapTest"
#define Log_i(...) __android_log_print(ANDROID_LOG_INFO,LOG_TAG,__VA_ARGS__)
#define Log_e(...) __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__)
// caching class and method IDs for a faster subsequent access
static jclass bitmap_class = 0;
static jmethodID setHasAlphaMethodID = 0;
jint Java_com_example_bitmaptest_MainActivity_setHasAlphaNative(JNIEnv * env, jclass clazz, jobject bitmap, jboolean value) {
AndroidBitmapInfo info;
void* pixels;
if (AndroidBitmap_getInfo(env, bitmap, &info) < 0) {
Log_e("Failed to get Bitmap info");
return -1;
}
if (info.format != ANDROID_BITMAP_FORMAT_RGBA_8888) {
Log_e("Incompatible Bitmap format");
return -1;
}
if (AndroidBitmap_lockPixels(env, bitmap, &pixels) < 0) {
Log_e("Failed to lock the pixels of the Bitmap");
return -1;
}
// get class
if(bitmap_class == NULL) { //initializing jclass
// NOTE: The class Bitmap exists since API level 1, so it just must be found.
bitmap_class = (*env)->GetObjectClass(env, bitmap);
if(bitmap_class == NULL) {
Log_e("bitmap_class == NULL");
return -2;
}
}
// get methodID
if(setHasAlphaMethodID == NULL) { //initializing jmethodID
// NOTE: If this fails, because the method could not be found the App will crash.
// But we only call this part of the code if the method was found using java.lang.Reflect
setHasAlphaMethodID = (*env)->GetMethodID(env, bitmap_class, "setHasAlpha", "(Z)V");
if(setHasAlphaMethodID == NULL) {
Log_e("methodID == NULL");
return -2;
}
}
// call java instance method
(*env)->CallVoidMethod(env, bitmap, setHasAlphaMethodID, value);
// if an exception was thrown we could handle it here
if ((*env)->ExceptionOccurred(env)) {
(*env)->ExceptionDescribe(env);
(*env)->ExceptionClear(env);
Log_e("calling setHasAlpha threw an exception");
return -2;
}
if(AndroidBitmap_unlockPixels(env, bitmap) < 0) {
Log_e("Failed to unlock the pixels of the Bitmap");
return -1;
}
return 0; // success
}