Ich gehe davon aus, dass Sie Code für eine Android-Version unter 3.2 (API-Stufe <12) schreiben, da seitdem das Verhalten der Methoden
BitmapFactory.decodeFile(pathToImage);
BitmapFactory.decodeFile(pathToImage, opt);
bitmapObject.createScaledBitmap(bitmap, desiredWidth, desiredHeight, false );
hat sich verändert.
Auf älteren Plattformen (API-Ebene <12) versuchen die BitmapFactory.decodeFile (..) -Methoden standardmäßig, eine Bitmap mit RGB_565-Konfiguration zurückzugeben, wenn sie kein Alpha finden, was die Qualität eines iamge verringert. Dies ist immer noch in Ordnung, da Sie eine ARGB_8888-Bitmap mit erzwingen können
options.inPrefferedConfig = Bitmap.Config.ARGB_8888
options.inDither = false
Das eigentliche Problem tritt auf, wenn jedes Pixel Ihres Bildes einen Alpha-Wert von 255 hat (dh vollständig undurchsichtig). In diesem Fall wird das Bitmap-Flag 'hasAlpha' auf false gesetzt, obwohl Ihre Bitmap die Konfiguration ARGB_8888 hat. Wenn Ihre * .png-Datei mindestens ein echtes transparentes Pixel hätte, wäre dieses Flag auf true gesetzt worden und Sie müssten sich um nichts kümmern.
Wenn Sie also eine skalierte Bitmap mit erstellen möchten
bitmapObject.createScaledBitmap(bitmap, desiredWidth, desiredHeight, false );
Die Methode prüft, ob das Flag 'hasAlpha' auf true oder false gesetzt ist. In Ihrem Fall wird es auf false gesetzt. Dadurch wird eine skalierte Bitmap erhalten, die automatisch in das Format RGB_565 konvertiert wurde.
Daher gibt es auf API-Ebene> = 12 eine öffentliche Methode namens
public void setHasAlpha (boolean hasAlpha);
das hätte dieses Problem gelöst. Bisher war dies nur eine Erklärung des Problems. Ich habe einige Nachforschungen angestellt und festgestellt, dass die setHasAlpha-Methode schon lange existiert und öffentlich ist, aber ausgeblendet wurde (Anmerkung @hide). So ist es in Android 2.3 definiert:
public void setHasAlpha(boolean hasAlpha) {
nativeSetHasAlpha(mNativeBitmap, hasAlpha);
}
Hier ist mein Lösungsvorschlag. Es werden keine Bitmap-Daten kopiert:
Zur Laufzeit mit java.lang.Reflect überprüft, ob die aktuelle Bitmap-Implementierung eine öffentliche 'setHasAplha'-Methode hat. (Laut meinen Tests funktioniert es perfekt seit API Level 3, und ich habe keine niedrigeren Versionen getestet, weil JNI nicht funktionieren würde). Sie können Probleme haben, wenn ein Hersteller es ausdrücklich privat gemacht, geschützt oder gelöscht hat.
Rufen Sie die 'setHasAlpha'-Methode für ein bestimmtes Bitmap-Objekt mit JNI auf. Dies funktioniert perfekt, auch für private Methoden oder Felder. Es ist offiziell, dass JNI nicht prüft, ob Sie gegen die Zugriffssteuerungsregeln verstoßen oder nicht. Quelle: http://java.sun.com/docs/books/jni/html/pitfalls.html (10.9) Dies gibt uns große Kraft, die mit Bedacht eingesetzt werden sollte. Ich würde nicht versuchen, ein endgültiges Feld zu ändern, selbst wenn es funktionieren würde (nur um ein Beispiel zu geben). Und bitte beachten Sie, dass dies nur eine Problemumgehung ist ...
Hier ist meine Implementierung aller notwendigen Methoden:
JAVA TEIL:
private static final boolean SETHASALPHA_EXISTS = setHasAlphaExists();
private static boolean setHasAlphaExists() {
java.lang.reflect.Method[] methods = Bitmap.class.getMethods();
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) {
return;
}
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.");
return;
}
} else {
bitmap.setHasAlpha(true);
}
}
public Bitmap decodeBitmapFromFile(String pathToImage, int pixels_limit) {
Bitmap bitmap;
Options opt = new Options();
opt.inDither = false;
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);
int numOfPixels = bitmap.getWidth() * bitmap.getHeight();
if(numOfPixels > pixels_limit) {
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);
}
return bitmap;
}
Laden Sie Ihre Bibliothek und deklarieren Sie die native Methode:
static {
System.loadLibrary("bitmaputils");
}
private static native int setHasAlphaNative(Bitmap bitmap, boolean value);
Native Sektion ('jni' Ordner)
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__)
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;
}
if(bitmap_class == NULL) {
bitmap_class = (*env)->GetObjectClass(env, bitmap);
if(bitmap_class == NULL) {
Log_e("bitmap_class == NULL");
return -2;
}
}
if(setHasAlphaMethodID == NULL) {
setHasAlphaMethodID = (*env)->GetMethodID(env, bitmap_class, "setHasAlpha", "(Z)V");
if(setHasAlphaMethodID == NULL) {
Log_e("methodID == NULL");
return -2;
}
}
(*env)->CallVoidMethod(env, bitmap, setHasAlphaMethodID, value);
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;
}
Das ist es. Wir sind fertig. Ich habe den gesamten Code zum Kopieren und Einfügen veröffentlicht. Der eigentliche Code ist nicht so groß, aber all diese paranoiden Fehlerprüfungen machen ihn viel größer. Ich hoffe das könnte für jeden hilfreich sein.
setAntiAlias
auf true setzen (und es macht immer noch keinen Unterschied)?