Decodificación de mapas de bits en Android con el tamaño correcto
Decodificar mapas de bits de la tarjeta SD utilizando BitmapFactory.decodeFile
. A veces, los mapas de bits son más grandes de lo que la aplicación necesita o que permite el montón, por lo que utilizar BitmapFactory.Options.inSampleSize
para solicitar un mapa de bits subampled (menor).
El problema es que la plataforma no aplica el valor exacto de inSampleSize, ya veces termino con un mapa de bits demasiado pequeño o demasiado grande para la memoria disponible.
- Uso de memoria entre Android 2.2 y 2.3
- El objeto de conexión de SQLite se filtró - Android
- Prueba Android "destruir actividad para ahorrar espacio"
- ¿Los oyentes crean fugas de memoria si no se eliminan de una actividad destruida?
- ¿Cuándo es exactamente seguro usar clases anónimas internas?
Desde http://developer.android.com/reference/android/graphics/BitmapFactory.Options.html#inSampleSize :
Nota: el decodificador intentará satisfacer esta petición, pero el mapa de bits resultante puede tener dimensiones diferentes que precisamente lo que se ha solicitado. Además, las potencias de 2 son a menudo más rápidas / más fáciles para el decodificador de honrar.
¿Cómo debo descodificar los mapas de bits de la tarjeta del SD para conseguir un mapa de bits del tamaño exacto que necesito mientras que consume tan poca memoria como posible descodificarla?
Editar:
Código fuente actual:
BitmapFactory.Options bounds = new BitmapFactory.Options(); this.bounds.inJustDecodeBounds = true; BitmapFactory.decodeFile(filePath, bounds); if (bounds.outWidth == -1) { // TODO: Error } int width = bounds.outWidth; int height = bounds.outHeight; boolean withinBounds = width <= maxWidth && height <= maxHeight; if (!withinBounds) { int newWidth = calculateNewWidth(int width, int height); float sampleSizeF = (float) width / (float) newWidth; int sampleSize = Math.round(sampleSizeF); BitmapFactory.Options resample = new BitmapFactory.Options(); resample.inSampleSize = sampleSize; bitmap = BitmapFactory.decodeFile(filePath, resample); }
- Error Android Out of Memory con imágenes de Lazy Load
- Gestión de la memoria android en el ciclo de vida de la actividad
- Android java.lang.OutOfMemoryError?
- Android "cerrar la fuerza" lo que exactamente sucede con la memoria
- Callback de Android: ¿es una posible pérdida de memoria?
- Error de memoria insuficiente al reiniciar la aplicación (Android)
- Android Studio Libgdx Problemas de memoria
- Obtenga el tamaño de RAM total que utiliza Aplicación en Android
Usted está en el camino correcto, sin embargo, está tratando de hacer dos cosas a la vez: lea el archivo y escala el tamaño adecuado.
El primer paso es leer el archivo a un mapa de bits un poco más grande de lo que necesita, utilizando BitmapFactory.Options.inSampleSize para asegurarse de que no consumir memoria excesiva de lectura de un mapa de bits grande cuando todo lo que desea es una imagen en miniatura o resolución de pantalla más pequeña.
El segundo paso es llamar a Bitmap.createScaledBitmap () para crear un nuevo mapa de bits con la resolución exacta que necesita.
Asegúrese de limpiar después del mapa de bits temporal para recuperar su memoria. (Deje que la variable salga del alcance y deje que el GC se ocupe de ella, o llame a .recycle () si está cargando muchas imágenes y se está agotando la memoria).
Es posible que desee utilizar inJustDecodeBounds
. Establecer a TRUE
y cargar el archivo como es.
La imagen no se cargará en la memoria. Pero las propiedades outheight y outwidth de BitmapFactory.Options contendrán los parámetros de tamaño real de la imagen especificada. Calcular cuánto quieres subamplear. Es decir 1/2 o 1/4 o 1/8 etc. y asignar 2/4/8 etc. de acuerdo con el inSampleSize .
Ahora establece inJustDecodeBounds
a FALSE
y llama a BitmapFactory.decodeFile()
para cargar la imagen del tamaño exacto como se calculó anteriormente.
Primero necesitas probar la imagen al valor de muestreo más cercano para que el mapa de bits se convierta en eficiente de memoria, lo que has descrito perfectamente. Una vez hecho esto, puede escalarlo para ajustarlo a su pantalla.
// This function taking care of sampling backImage =decodeSampledBitmapFromResource(getResources(),R.drawable.back, width, height); // This will scale it to your screen width and height. you need to pass screen width and height. backImage = Bitmap.createScaledBitmap(backImage, width, height, false);
Dado que la API ya indica que el tamaño de la muestra puede o no ser respetado. Así que supongo ur fuera de suerte durante el uso de BitmapFactory
.
Pero la salida sería el uso de su propio lector de mapas de bits. Pero yo sugeriría que te quedas con BitmapFactory como su bastante bien probado y estandarizado.
Intentaría no preocuparme demasiado sobre poco consumo adicional de la memoria, pero intento y descubre porqué su no honrar los valores. Lamentablemente no tengo ni idea de eso 🙁
ThumbnailUtils.extractThumbnail(BitmapFactory.decodeFile(filesPath), THUMBSIZE, THUMBSIZE))
Usted puede fijar directamente el THUMBSIZE dependiendo de su requisito, sin embargo no estoy seguro sobre los detalles inferiores de la puesta en práctica del nivel, la calidad de la imagen de la salida y su funcionamiento sino que lo prefiero generalmente cuando necesito mostrar la miniatura.
Para los que están buscando la imagen exacta del tamaño de recursos.
Para la anchura exacta pase reqHight = 0 y para la altura exacta reqWidth = 0
public static Bitmap decodeBitmapResource(Context context, int resId, int reqWidth, int reqHeight) { final BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; BitmapFactory.decodeResource(context.getResources(), resId, options); float scale = 1; if (reqWidth != 0 && reqHeight == 0) { options.inSampleSize = (int) Math.ceil(options.outWidth / (float) reqWidth); scale = (float) options.outWidth / (float) reqWidth; } else if (reqHeight != 0 && reqWidth == 0) { options.inSampleSize = (int) Math.ceil(options.outHeight / (float) reqHeight); scale = (float) options.outHeight / (float) reqHeight; } else if (reqHeight != 0 && reqWidth != 0) { float x = (float) options.outWidth / (float) reqWidth; float y = (float) options.outHeight / (float) reqHeight; scale = x > y ? x : y; } reqWidth = (int) ((float) options.outWidth / scale); reqHeight = (int) ((float) options.outHeight / scale); options.inJustDecodeBounds = false; Bitmap tmp = BitmapFactory.decodeResource(context.getResources(), resId, options); Bitmap out = Bitmap.createScaledBitmap(tmp, reqWidth, reqHeight, false); tmp.recycle(); tmp = null; return out; }
Ya que uso inSampleSize nunca me enfrento a problemas de memoria más. Así que inSampleSize funciona bastante estable para mí. Yo recomendaría que revise el problema. El tamaño puede no ser exacly el mismo que usted pidió. Pero debe ser reducido de todos modos y no debería haber más "Fuera de memoria". También recomendaría publicar su fragmento de código aquí, tal vez hay algo malo con él.
Como dijo el 100rabh, si usas BitmapFactory realmente no tienes mucha opción. Sin embargo, la decodificación de mapa de bits es muy simple y, dependiendo del algoritmo de escalado que desee utilizar, suele ser bastante sencillo escalarla al vuelo sin tener que leer grandes partes del mapa de bits original en la memoria (en general, Sólo necesita duplicar la memoria necesaria para 1-2 líneas del mapa de bits original).
Cuando se establece el indicador inScaled (por defecto es TRUE), si inDensity y inTargetDensity no son 0, el mapa de bits se escalará para que coincida con InTargetDensity cuando se cargue, en lugar de confiar en el sistema gráfico escalándolo cada vez que se dibuja a un Canvas. Así que simplemente establecer el inScaled a false hará.
BitmapFactory.Options options = new BitmapFactory.Options(); options.inScaled = false;
Fui capaz de obtener aproximadamente el "tamaño correcto" por reescalar el archivo de imagen decodificada a 70% de su tamaño de mapa de bits.
Bitmap myBitmap = BitmapFactory.decodeFile(path); if(myBitmap != null) { //reduce to 70% size; bitmaps produce larger than actual image size Bitmap rescaledMyBitmap = Bitmap.createScaledBitmap( myBitmap, myBitmap.getWidth()/10*7, myBitmap.getHeight()/10*7, false); image.setImageBitmap(rescaledMyBitmap); }
Espero que esto te ayude de alguna manera! ¡Paz!
- Eclipse: no me deja usar Android SDK, afirma erróneamente que mi ADT está desactualizada
- Iniciar la aplicación sabiendo el nombre del paquete