Avec OSLib, vous pouvez facilement charger des images sur la Memory Stick, pour le moment au
format PNG uniquement, les manipuler, les dessiner et les déformer. Le canal alpha est
également supporté.
Passons en revue les fonctions nécessaires pour cela.
Tout d'abord, il est possible de créer une nouvelle image avec la fonction suivante:
OSL_IMAGE *oslCreateImage(int larg, int haut, short location, short pixelFormat);
Cette fonction crée une nouvelle image vide de la taille spécifiée. Vide, elle ne sert pas à grand chose, mais vous pourrez la modifier par après comme nous allons le voir.
Note: Dans tous les cas, lorsque OSLib ne peut pas créer un objet, la valeur NULL est retournée en place de l'objet. C'est donc à vous de vérifier que l'image a bien pu être créée avant de l'utiliser sinon vous aurez droit à un plantage. Dans l'exemple suivant, on essaie de créer d'abord l'image en VRAM, et si ça échoue, on tente en RAM, et si aucun des deux ne fonctionne, on lance une erreur fatale et on quitte:
//Tente de créer une nouvelle image image = oslCreateImage(32, 32, OSL_IN_VRAM, OSL_PF_4444); //L'image est-elle nulle? if (!image) { //Impossible de créer en VRAM... on essaie en RAM. image = oslCreateImage(32, 32, OSL_IN_RAM, OSL_PF_4444); //Est-ce bon maintenant? if (!image) { oslFatalError("Impossible de créer l'image"); return; } } //A partir d'ici, on peut l'utiliser oslDrawImage(image);
Emplacement mémoire de l'image
La plupart des fonctions de création et de manipulation d'images vous permettent de définir à quel endroit placer l'image. Sur PSP, il y a deux emplacements possibles: la RAM (mémoire principale, 20 Mo, lente) et la VRAM (mémoire vidéo, 2 Mo, rapide). Pour les images normales, je vous recommande de les mettre en RAM (car il y a plus de place) et pour les images pour lesquelles la vitesse est critique, placez-les en VRAM. Pour placer l'image en RAM, mettez OSL_IN_RAM comme argument à location et OSL_IN_VRAM pour la VRAM.
Les PixelFormats
Les PixelFormats servent à décrire le format des couleurs pour chaque pixel. Sur PSP il y a 6 principaux pixelformats, dont certains qui ne sont utilisés que dans certaines circonstances:
- OSL_PF_8888: 32 bits par pixel, 8 composantes de rouge, vert, bleu et alpha. La composante
alpha sert à définir l'opacité d'un pixel (255=opaque, 0=transparent).
- OSL_PF_5650: 16 bits par pixel, 5 composantes de rouge, 6 composantes de vert, 5 composantes de bleu
et 0 d'alpha. Ces images ne devraient pas être utilisées pour les sprites ou les maps notamment, car
elles peuvent pas avoir de partie transparente puisqu'elle n'ont pas de composante alpha.
- OSL_PF_5551: 16 bits par pixel, 5 composantes de rouge, vert et bleu, et 1 composante d'alpha, qui
permet juste de dire si le pixel est transparent ou affiché. Utile pour les sprites simples.
- OSL_PF_4444: 16 bits par pixel, 4 composantes de rouge, vert, bleu et alpha. Ces images ont une
étendue de couleurs assez limitée (4096 couleurs), mais elles permettent de définir 16 niveaux de
transparence par pixel.
- OSL_PF_8BIT: 8 bits par pixel, indexé sur une palette.
- OSL_PF_4BIT: 4 bits par pixel, indexé sur une palette.
OSL_IMAGE *oslLoadImageFile(char *filename, int location, int pixelFormat);
Cette fonction permet de charger une image depuis un fichier. Vous pouvez remonter à la racine de la Memory Stick en tapant un chemin du style: ms0:/chemin/fichier.png. Faites attention: les majuscules et les minuscules comptent, et si un fichier "image.png" se trouve dans la MS, et que vous avez mis "Image.png" dans votre code, le chargement échouera!
void oslCopyImage(OSL_IMAGE *imgDst, OSL_IMAGE *imgSrc);
Cette fonction permet de copier une image vers une autre, mais les deux doivent être de formats identiques. Exemple:
monImage1 = oslCreateImage(32, 32, OSL_IN_RAM, OSL_PF_8888); monImage2 = oslCreateImage(32, 32, OSL_IN_RAM, OSL_PF_8888); //monImage2 <- monImage1 oslCopyImage(monImage2, monImage1);
OSL_IMAGE *oslCreateImageCopy(OSL_IMAGE *src, int newLocation);
Crée une copie d'une image, avec en plus la possibilité de choisir où se placera la copie. A part cela, l'image retournée aura exactement le même format que l'image passée en paramètre. L'exemple de la fonction oslCopyImage peut être réalisé ainsi:
monImage1 = oslCreateImage(32, 32, OSL_IN_RAM, OSL_PF_8888); monImage2 = oslCreateImageCopy(monImage1, OSL_IN_RAM);
void oslUncacheImage(OSL_IMAGE *img);
Sort une image du cache CPU. Si vous accédez aux données de l'image (à partir du membre data), vous devrez appeler cette fonction en passant l'image en question avant de pouvoir la dessiner. Pour plus d'informations, voir le chapitre sur le cache CPU. Exemple:
img = oslCreateImage(32, 32, OSL_IN_RAM, OSL_PF_8888); unsigned long *ptr = oslGetImagePixelAdr(img, 15, 10); //Colorie 5 pixels en rouge à partir de (15,10). for (i=0;i<5;i++) *ptr++ = RGB(255,0,0); oslUncacheImage(img); //Sans le oslUncacheImage, le GPU dessinera l'image incorrectement (puisqu'elle est encore dans le cache CPU et le GPU n'y a pas accès). oslDrawImage(img);
Une fois que vous avez terminé avec une image, vous pouvez la supprimer en appelant simplement la fonction suivante:
void oslDeleteImage(OSL_IMAGE *img);
Malheureusement, comme il n'y a pas encore de gestionnaire de VRAM, les images en VRAM ne peuvent être supprimées qu'à une seule condition: ce doit être la dernière qui a été créée! Exemple:
//Création des images monImage1 = oslCreateImage(16, 16, OSL_IN_VRAM, OSL_PF_8888); monImage2 = oslCreateImage(32, 32, OSL_IN_VRAM, OSL_PF_8888); [...] //Elles doivent être supprimées dans l'ordre inverse où elles ont été créées! oslDeleteImage(monImage2); oslDeleteImage(monImage1);
En imaginant qu'une autre image est créée entre deux, vous ne pourrez pas supprimer monImage1 ou monImage2 avant d'avoir supprimé celle-ci. Si vous ne le faites pas, le programme ne plantera pas, mais vos images risquent d'être corrompues. Voici un exemple à ne surtout pas faire:
image1 = oslLoadImageFile("image1.png", OSL_IN_VRAM, OSL_PF_5551); image2 = oslLoadImageFile("image2.png", OSL_IN_VRAM, OSL_PF_5551); //Grave erreur! Il faut effacer image2 avant image1! oslDeleteImage(image1); //Image2 va être corrompu par image3. image3 = oslLoadImageFile("image3.png", OSL_IN_VRAM, OSL_PF_5551);
Toutefois, il n'y a pas de problème si vous placez les images en RAM, c'est pourquoi évitez d'utiliser la VRAM à moins de savoir exactement ce que vous faites.
Manipulation des images
Une image est une structure contenant toutes les informations à propos de l'image elle-même. Les premiers éléments (publics) sont ceux qui peuvent être modifiés, les autres (privés) étant internes il est recommandé de ne pas y toucher. Exemple:
monImage = oslCreateImage(16, 16, OSL_IN_VRAM, OSL_PF_8888); monImage->angle = 20; //L'image sera dessinée avec une rotation de 20° oslDrawImage(monImage);
Voici donc les membres en question:
typedef struct { //publiques int x, y; //Positions int stretchX, stretchY; //Taille finale (étirée) //Morceau float offsetX0, offsetY0, offsetX1, offsetY1; //Rotation int centerX, centerY; //Centre de rotation float angle; //Angle (rotation) //Paramètres bool autoStrip; //Stripping auto (incompatible avec rotation) bool bilinear; OSL_PALETTE *palette; //Palette pour les modes 4 et 8 bits //privées int sizeX, sizeY; //Taille réelle int sysSizeX, sysSizeY; //Taille alignée int realSizeX, realSizeY; //Taille si aligné void *data; //Image en mémoire bool isCopy; //Est une copie d'une autre image (=ne pas la libérer) bool isSwizzled; //Est swizzlée (optimisée) int totalSize; //Taille en octets short location, pixelFormat; } OSL_IMAGE;
Les positions x et y sont utilisées pour placer l'image sur l'écran. Le bord haut-gauche est 0,0 et le bas-droit est
479,271. La première coordonnée (x) est horizontale et l'autre (y) verticale.
Les propriétés stretchX et stretchY permettent de zoomer une image. Mettez-y la taille désirée de l'image. Par exemple
une image de 16x16 que vous voulez zoomer 2x aura stretchX et stretchY à 32. Vous pouvez pour cela utiliser les propriétés
sizeX et sizeY, ou offsetX1-offsetX0 et offsetY1-offsetY0 si l'image est découpée (oslSetImageTile et compagnie):
img->stretchX = 2 * img->sizeX; img->stretchY = 2 * img->sizeY;
img->stretchX = 2 * abs(img->offsetX1-img->offsetX0); img->stretchY = 2 * abs(img->offsetY1-img->offsetY0);
Les propriétés offset** sont à utiliser pour régler le cadre de l'image qui sera copié sur l'écran. Par exemple, si votre image fait 16x16 et que vous ne voulez que la partie 8x8 supérieure droit, vous pouvez mettre offsetX0 à 8, offsetY0 à 0, offsetX1 à 16 et offsetY1 à 8. Pour définir ces propriétés plus simplement, utilisez ceci:
oslSetImageTile(image,x0,y0,x1,y1); oslSetImageTileSize(image,x,y,largeur,hauteur);
Les propriétés centerX et centerY définissent le centre de rotation de l'image, et angle définit l'angle de rotation de l'image. La rotation réduit les performances, surtout pour les grandes images, faites donc d'abord des tests.
autostrip optimise la vitesse d'affichage des grandes images (activé par défaut).
bilinear permet de définir si on souhaite appliquer un filtre bilinéaire ('flou') à l'image.
palette définit la palette utilisée pour l'image. Vous pouvez en créer une et l'associer à l'image comme suit:
const unsigned long maPalette[2] = {RGB(0,0,0), RGB(255,0,0)}; //Rouge et blanc monImage->palette = oslLoadPalette(maPalette, 2, OSL_PF_8888);
Avec OSLib, vous pouvez rediriger toutes les opérations de dessin vers une image. C'est pratique, car vous pouvez ensuite dessiner cette image sur l'écran en y appliquant tous les effets permis à une image standard. Vous pouvez par exemple dessiner le contenu d'une fenêtre et ensuite le rendre en transparence, l'agrandir, etc.
Pour cela, on utilise la fonction suivante:
void oslSetDrawBuffer(OSL_IMAGE *img);
Cette fonction définit le drawbuffer, c'est-à-dire l'endroit sur lequel on dessine. Il suffit de passer les paramètres
suivants:
- OSL_DEFAULT_BUFFER: Ecran par défaut (pour revenir à l'état normal).
- OSL_SECONDARY_BUFFER: En mode double buffer, l'écran affiché (et non celui de dessin).
- une image: Dessinera sur cette image.
OSLib inclut une variable globale osl_curBuf, qui est un pointeur sur l'image de dessin courante. Vous pouvez ainsi obtenir les propriétés de l'écran sur lequel vous dessinez, et vous adapter à la résolution, au pixelformat, etc. Voici un exemple d'utilisation de cette fonction:
image = oslCreateImage(160, 100, OSL_IN_VRAM, OSL_PF_5650); oslSetDrawBuffer(image); oslDrawGradientRect(0,0,osl_curBuf->sizeX,osl_curBuf->sizeY, RGB(0,0,0), RGB(255,0,0), RGB(0,255,0), RGB(0,0,255)); oslSetDrawBuffer(OSL_DEFAULT_BUFFER); //Dessine l'image centrée image->angle = 35; image->centerX = image->sizeX/2; image->centerY = image->sizeY/2; oslDrawImageXY(osl_curBuf->sizeX/2, osl_curBuf->sizeY/2, image); oslDeleteImage(image);
Note importante: l'image sur laquelle vous dessinez DOIT se trouver en VRAM, sinon cela ne fonctionnera pas! C'est d'ailleurs pour cela que je la supprime juste après car il ne faut pas empiler trop d'images en VRAM pour les raisons énumérées plus haut.