Les bitmaps sous Android
Charger un flux bitmap
Il est possible de charger des bitmaps sous Android. Les formats connus sont le PNG (format préféré - 32 bits: 16 millions de couleurs + transparence - 32bits), JPEG (format acceptable - 16 millions de couleurs) et le GIF (format déconseillé - En fait, depuis quelques années, il y a une tentative pour utiliser le PNG à la place du GIF qui lui est soumis à license Unisys. Cependant, le PNG ne permet pas l'animation mais supporte les images en 24 bits soit 16 millions couleurs contrairement au gif).
Et pour faire compliquer, pour travailler avec des bitmaps, vous utiliserez la classe Bitmap !
Les images peuvent être stockées dans votre projet dans le répertoire assets. Vous ferez donc:
Appel à assetManager pour récupérer votre bitmap directement sous assets
AssetManager assetManager = context.getAssets();
InputStream inputStream = assetManager.open("nomFichier"); Avec nomFichier
le nom du bitmap PNG ou JPEG à charger (cf. pour InputStream : les
flux de fichiers en JAVA)
Fabrique de bitmap
Enfin, pour décoder l'image, vous utiliserez le filtre decodeStream sur votre flux de données.
Il s'agit d'une factory pour Bitmap (Cf. design pattern factory sur wikipedia).
Bitmap bitmap = BitmapFactory.decodeStream(inputStream);
Ce qui génrèrera votre instance bitmap avec l'image décodée.
Vous ouvrez un
flux, vous aurez donc tout à gérer les exceptions (try, catch,
finally ...). Et surtout, ne pas oublier de fermer votre fichier dans finally
par inputStream.close();
Options de lecture du flux
Des options peuvent être positionnées :
Vous pourrez préciser le codage ARVB que vous voulez obtenir. Pour cela, il faut instancier BitmapFactory.Options:
BitmapFactory.Options options = new BitmapFactory.Options();
Puis préciser le format de sortie. Pour cela vous utiliserez les constantes de Bitmap.Config qui sont:
Config.ALPHA_8
Config.ARGB_4444 (Attention, cet encodage est déprécié,
car trop pauvre en information !)
Config.ARGB_8888
Config.RGB_565
Par exemple ARGB_8888:
options.inPreferredConfig = Bitmap.Config.ARGB_8888;
Puis décoder l'image en précisant vos options:
Bitmap bitmap = BitmapFactory.decodeStream(inputStream, null, options);
- inPurgeable à true ou false, permet au système de libérer la mémoire si nécessaire. Dans cette option, l'image sera automatiquemnt re-décodée si elle a été prugée et que votre programme en a de nouveau besoin.
- inSampleSize permet de retailler l'image par rapport à l'originale, permettant d'occuper moins de place en mémoire. La valeur doit être une puissance de 2. En passant par exemple la valeur 4, vous obtenez une image dont la largeur et la hauteur sera égale à 1/4 de la largeur et la hauteur de l'originale ou 1/16 ième de pixel (4x4). La valeur allant de 1 à n. si n n'est pas une puissance de 2, alors la valeur prise en compte sera celle en puissance de 2 immédiatement inférieure. Tous nombres inférieures à 1 étant arrondis à 1.
- inJustDecodeBounds si true, permet de demander à la factory de produire les informations sur le bitmap sans pour autant produire le bitmap (donc sans occupation mémoire).
Options en sortie de décodage
decodeStream attend une instance de BitmapFactory.Options en entrée pour fabriquer le bitmap, et en profite pour positionner des valeurs en sortie correspondant aux attributs de l'image:
public int outWidth: largeur de l'image décodée (-1 si pb.)
public int outHeight: hauteur (-1 si pb.)
public String outMimeType: Le MIME du fichier (si connu)
Si vous avez besoin
des informations mais pas de l'image, n'oubliez pas de positionner inJustDecodeBounds
à true !
Lecture d'un fichier bitmap
Evidemment, il sera possible d'ouvrir un bitmap dont le nom du fichier sera fourni:
Bitmap myBitmap = BitmapFactory.decodeFile(myFile);
Où myFile est de type String.
Des informations sur le bitmap
Tout d'abord les dimensions de l'image:
int width = bitmap.getWidth();
int height = bitmap.getHeight();
Ensuite, vous pouvez connaître le codage des couleurs utilisées
Bitmap.Config config = bitmap.getConfig();
En retour, vous pourrez récupérer l'une des constantes de BitmapConfig que vous avez vu précédemment.
Création d'une instance de bitmap
Vous avez vu qu'il était possible de créer un bitmap à partir d'une image. Mais vous pouvez aussi instancier un bitmap sans fichier. Vous pourrez ainsi préciser sa taille, son format d'encodage des couleurs.
Bitmap bitmap = Bitmap.createBitmap(int width, int height, Bitmap.Config config);
Avec width la largeur de votre image en pixel
height la hauteur en pixel
Bitmap.Config pour préciser l'encodage ARVB
Evidemment, cela n'a pas d'interêt si vous ne pouvez pas dessiner dedans ! Donc il y a une solution: à l'aide d'un Canvas, vous pourrez dessiner sur votre bitmap (exemple avec les opérations graphiques de base) en transférant cet image dans ce Canvas.
Pour cela, vous utiliserez:
Canvas canvas = new Canvas(bitmap);
Création d'une instance de bitmap à partir d'une ressource
Une autre façon de récupérer une image est de la "fabriquer" à partir d'un identifiant de ressource. En somme l'image existe déjà quelque part, mais pour la charger, il faudra passer par un identifiant.
Il faudra là encore utiliser une factory pour Bitmap:
Bitmap bitmap = BitmapFactory.decodeResources(Resources res, int id, BitmapFactory.Options opts);
Exemple
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), ressourceId);
avec resourceId par exemple à R.drawable.MonImage
ce qui signifie que vous devez avoir une image dans l'un des répertoires res\drawable de votre projet.
Libérer de l'espace mémoire
Les bitpmaps occupent une grosse quantité de mémoires (Taille de l'encodage d'une couleur * largeur * hauteur). Pour notre "prauvre petit" androphone, il peut être nécessaire de faire du nettoyage !
Si vous savez durant l'exécution de votre code qu'un bitmap ne vous sera plus utile, vous pouvez marquer celui-ci comme étant à recycler afin que le garbage collertor libère la mémoire:
Il s'agit de la méthode Bitmap.recycle();
Par exemple:
if (myBitmap != null) {
myBitmap.recycle();
myBitmap = null; // Une vieille habitude du C/C++ pour indiquer que myBitmap est mainenant inutilisable...
}
Agrandir/réduire un bitmap
Bitmap original = BitmapFactory.decodeFile(myFile); // Taille originale de
l'image
myBitmap = Bitmap.createScaledBitmap(original, displayWidth, displayHeight,
true); // Nouveau Bitmap de taille différente
Si l'image d'origine
ne sert plus, n'oubliez pas de la recycler pour que le Garbage collector libère
la mémoire !
"Afficher"/"dessiner sur" le bitmap
Pour afficher un bitmap dans une surface, vous devez utiliser un Canvas, Un Canevas étant une interface entre votre image et la surface. Ce Canvas permettra aussi d'effectuer d'autres opérations graphiques.
Il faut donc transférer ce bitmap vers un canevas:
-
"Afficher"/"dessiner sur" depuis une instance existante de canevas
Vous pourrez transférer la totalité d'un bitmap dans un canevas:
Canvas.drawBitmap(Bitmap bitmap, float topLeftX, float topLeftY, Paint paint);
Avec
bitmap, l'instance de votre bitmap à transférerer.
topLeftX et topLeftY qui sont les coordonnées sur l'écran à partir desquelles votre image sera affichée.
Le dernier argument sera à priori souvent positionné à null, il s'agit en fait d'une instance de paint permettant de positionner des paramètres évolués pour l'affichage du bitmap.
-
Ou "afficher"/"dessiner sur" une portion du bitmap:
Canvas.drawBitmap(Bitmap bitmap, Rect src, Rect dst, Paint paint);
Avec
Toujours bitmap qui est l'instance de bitmap à afficher
Rect src: Portion de l'image bitmap que l'on veut afficher. Cette valeur pouvant être null. Dans ce cas, c'est l'ensemble du bitmap qui sera traité. Sinon, il faudra spécifier les coordonnées du coin supérieur gauche et inférieur droit de la portion du bitmap à afficher
Rect dst: Même chose qui ci-dessus, mais pour la destination.
La surface des rectangles source et destination pourront être différentes. Dans ce cas, l'image sera automatiquement modifiée(donc agrandie ou réduite - scale) (et non retaillée) pour occuper la totalité de la zone destination.
Si le bitmap et la surface n'ont pas le même encodage des couleurs, le canevas prendra en charge la conversion. Cependant, le traitement sera forcément un peu plus lent que s'ils avaient été dans le même format.
-
Depuis une nouvelle instance de Canvas
Pour rappel, il est possible d'instancier un Canvas avec un bitmap passé en paramètre dans le constructeur:
Canvas canvas = new Canvas(bitmap);
Une autre solution est d'utiliser un widget
-
Depuis le widget ImageView
Pour rappel, la méthode setImageBitmap(bitmap) permet d'afficher bitmap dans une instande de widget ImageView.
Blending et bitmap
Le blending (mélange de couleurs du bitmap avec celle du fond de la surface) est activé par défaut. Par conséquent, il y aura bien interprétation de la valeur alpha positionnée sur les pixels du bitmap.
Exemple
Voici un exemple tout bête qui va afficher vingt robots android à l'écran, de tailles différentes:
Et l'image du robot à sauver dans le répertoire
assets de votre projet android avec le nom android_robot.png: