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.

Image non trouvé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);

 

D'autres 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)

Image non trouvée !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

Image non trouvée !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.

Image non trouvée !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.

 

Image non trouvée !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:Image non trouvée !