Intereting Posts
Создавая зачеркнутый текст в Android? Как играть в локальные swf-файлы в веб-просмотре Как получить Uri изображения, хранящегося на SDCARD? Воспроизведение аудио по активному телефонному звонку, так что на другом конце его можно услышать LocationServices.FusedLocationApi.removeLocationUpdates (mGoogleApiClient, this); Вызывается как com.google.android.gms.location.LocationListener) Странное поведение в инструкции do / while на Galaxy S5 и Android 5.1.1 Приостановка и возобновление дочерних действий в ActivityGroup Требуется Gradle версия 2.10. Текущая версия 2.8 Ошибка Как зашифровать файл с SD-карты с помощью AES на Android? (Java LibGDX) Как изменить размеры текстур в LibGDX? Как отладить собственный код в проекте библиотеки Android? Что именно вызывает ошибку «отключение при приостановке» в Android? Libgdx не корректно отображает шрифты после возобновления Установка Android-приложения на устройство из Android Studio Не удается загрузить веб-страницу с помощью WebView на Android

«Bitmap.createScaledBitmap» преобразует 32-битное изображение в 24 бит?

В моем приложении я загружаю изображение как 32 бит (ARGB_8888) следующим образом:

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-битном цветовом пространстве? Благодаря!

Solutions Collecting From Web of "«Bitmap.createScaledBitmap» преобразует 32-битное изображение в 24 бит?"

Я искал метод createScaledBitmap в источнике для класса Bitmap ( ссылка ):

 public static Bitmap createScaledBitmap(Bitmap src, int dstWidth, int dstHeight, boolean filter) { Matrix m; synchronized (Bitmap.class) { // small pool of just 1 matrix m = sScaleMatrix; sScaleMatrix = null; } if (m == null) { m = new Matrix(); } final int width = src.getWidth(); final int height = src.getHeight(); final float sx = dstWidth / (float)width; final float sy = dstHeight / (float)height; m.setScale(sx, sy); Bitmap b = Bitmap.createBitmap(src, 0, 0, width, height, m, filter); synchronized (Bitmap.class) { // do we need to check for null? why not just assign everytime? if (sScaleMatrix == null) { sScaleMatrix = m; } } return b; } 

И вызов 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 } 

Поскольку вы не выполняете масштабирование, ваша матрица будет идентификационной матрицей, и условие выполняется. Созданное растровое изображение, как вы можете видеть, зависит от альфы в исходном растровом изображении. Если альфа отсутствует, вы получаете растровое изображение результата с форматом RGB_565, а не ARGB_8888.

Таким образом, чтобы масштабировать и сохранять 32-битный формат, ваше растровое изображение должно быть неизменным или использовать альфа-канал.

Цветовая гамма Резолюция ooooooooooyyyyyyaaaaaaaaaa

Я решил цветовое полотно в две фазы

1) *, когда мы используем BitmapFactory для декодирования ресурсов, он декодирует ресурс в RGB565, который показывает цветовой диапазон, вместо использования ARGB_8888, поэтому я использовал BitmapFactory.Options для установки параметров декодирования ARGB_8888

Вторая проблема заключалась в том, что когда я масштабировал растровое изображение, он снова получил полосу

2) Это была сложная часть, и она много искала и, наконец, работала * метод Bitmap.createScaledBitmap для масштабирования растровых изображений также уменьшил изображения до формата RGB565 после масштабирования, я получил полосатые изображения (старый метод для решения этого вопроса использовал, по крайней мере, один Прозрачный пиксель в png, но никакой другой формат, как jpg или bmp, не работал), поэтому здесь я создал метод CreateScaledBitmap, чтобы масштабировать растровое изображение с исходными конфигурациями растровых изображений в результирующем растровом масштабе (фактически, я скопировал метод из сообщения через logicnet.dk и Переведена в java)

  BitmapFactory.Options myOptions = new BitmapFactory.Options(); myOptions.inDither = true; myOptions.inScaled = false; myOptions.inPreferredConfig = Bitmap.Config.ARGB_8888;//important //myOptions.inDither = false; myOptions.inPurgeable = true; Bitmap tempImage = BitmapFactory.decodeResource(getResources(),R.drawable.defaultart, myOptions);//important //this is important part new scale method created by someone else tempImage = CreateScaledBitmap(tempImage,300,300,false); ImageView v = (ImageView)findViewById(R.id.imageView1); v.setImageBitmap(tempImage); 

// функция

 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; } 

Пожалуйста, поправьте меня, если я ошибаюсь. Также прокомментируйте, если он сработает для вас.

Я так счастлив, что решил, надеюсь, это сработает для вас.

Легко создать свою собственную версию, которая поддерживает формат пикселя источника:

 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)

Я предполагаю, что вы пишете код для версии Android ниже 3.2 (уровень API <12), потому что с тех пор поведение методов

 BitmapFactory.decodeFile(pathToImage); BitmapFactory.decodeFile(pathToImage, opt); bitmapObject.createScaledBitmap(bitmap, desiredWidth, desiredHeight, false /*filter?*/); 

Изменилось.

На более старых платформах (уровень API <12) методы BitmapFactory.decodeFile (..) пытаются вернуть Bitmap с конфигурацией RGB_565 по умолчанию, если они не могут найти альфа, что снижает качество iamge. Это все еще нормально, потому что вы можете использовать растровое изображение ARGB_8888, используя

 options.inPrefferedConfig = Bitmap.Config.ARGB_8888 options.inDither = false 

Реальная проблема возникает, когда каждый пиксель вашего изображения имеет альфа-значение 255 (то есть полностью непрозрачный). В этом случае флаг Bitmap 'hasAlpha' имеет значение false, даже если ваш битмап имеет конфигурацию ARGB_8888. Если ваш * .png-файл имел по крайней мере один реальный прозрачный пиксель, этот флаг был бы установлен как true, и вам ни о чем не беспокоиться.

Поэтому, когда вы хотите создать масштабированный битмап, используя

 bitmapObject.createScaledBitmap(bitmap, desiredWidth, desiredHeight, false /*filter?*/); 

Метод проверяет, установлен ли флаг hasAlpha равным true или false, а в вашем случае он установлен в false, что приводит к получению масштабированного растрового изображения, которое автоматически преобразуется в формат RGB_565.

Поэтому на уровне API> = 12 существует общедоступный метод, называемый

 public void setHasAlpha (boolean hasAlpha); 

Которая решила бы эту проблему. До сих пор это было просто объяснением проблемы. Я сделал некоторые исследования и заметил, что метод setHasAlpha существует уже давно, и он является общедоступным, но был скрыт (аннотация @hide). Вот как это определено на 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 (eg 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); } 

Теперь вот мое решение. Это не связано с копированием растровых данных:

  1. Проверено во время выполнения с помощью java.lang.Reflect, если текущая реализация Bitmap имеет общедоступный метод setHasAplha. (По моим тестам он отлично работает с уровня API 3, и я не тестировал более низкие версии, потому что JNI не работал). У вас могут возникнуть проблемы, если производитель явно сделал это частным, защищенным или удалил его.

  2. Вызовите метод setHasAlpha для данного объекта Bitmap с использованием JNI. Это работает отлично, даже для частных методов или полей. Официально, что JNI не проверяет, нарушаете ли вы правила контроля доступа или нет. Источник: http://java.sun.com/docs/books/jni/html/pitfalls.html (10.9) Это дает нам большую силу, которую следует использовать мудро. Я бы не стал модифицировать последнее поле, даже если бы он работал (просто чтобы привести пример). И обратите внимание, что это всего лишь обходной путь …

Вот моя реализация всех необходимых методов:

ЧАСТЬ JAVA:

 // 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; } 

Загрузите свой lib и объявите собственный метод:

 static { System.loadLibrary("bitmaputils"); } private static native int setHasAlphaNative(Bitmap bitmap, boolean value); 

Родной раздел (папка «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 } 

Вот и все. Мы сделали. Я опубликовал весь код для целей копирования и вставки. Фактический код не такой уж большой, но все эти проверки параноидальных ошибок делают его намного больше. Надеюсь, это может быть полезно для всех.