With OSLib, you can easily load images on the Memory Stick, for the moment in the PNG format only, manipulate them,
draw and deform them. The alpha channel is also supported.
Let's take a look to the necessary functions for this.
First of all, it is possible to create a new image with the following function:
OSL_IMAGE *oslCreateImage(int larg, int haut, short location, short pixelFormat);
This function creates a new empty image of the specified size. Empty, it is not very useful, but you will able to modify it later as we will see.
In every case, when OSLib cannot create an object, the NULL value returned at the object place. So, you have to verify that the image was able to be created before using it, otherwise you will be gratified by a beautiful crash. In the following example, we try first to create the image in VRAM, and if that fails, we try in RAM, and if none of them works, we throw a fatal error and then quit the game:
//Tries to create a new image image = oslCreateImage(32, 32, OSL_IN_VRAM, OSL_PF_4444); //Image is null? if (!image) { //Unable to create in VRAM... we'll try in RAM. image = oslCreateImage(32, 32, OSL_IN_RAM, OSL_PF_4444); //Is it okay now? if (!image) { oslFatalError("Impossible de créer l'image"); return; } } //Starting from here, we can use it oslDrawImage(image);
Memory locations for images
Most of the image creation and manipulation functions help you to define where you can put the image. On PSP, there are two possible places: the RAM (main memory, 20 MB, slow) and the VRAM (video memory, 2 MB, fast). For normal images, I recommend you to put them in RAM (because there is more available space). And for the images for which the speed is important, put them in VRAM. To put an image in RAM, put OSL_IN_RAM as argument in location and OSL_IN_VRAM for the VRAM.
The PixelFormats
PixelFormats are used to describe the format of colors for every pixel. On PSP there are 6 main pixelformats, some of them are only used in certain circumstances:
- OSL_PF_8888: 32 bits per pixel, 8 bits for red, green, blue and alpha. The alpha component
is used to define the opacity of a pixel (255=opaque, 0=transparent).
- OSL_PF_5650: 16 bits per pixel, 5 bits for red, 6 bits for green, 5 bits for blue and 0 for the alpha value.
These images should not be used for sprites or maps, because they can't have any transparent part because there is not a
alpha channel.
- OSL_PF_5551: 16 bits per pixel, 5 bits for red, 5 bits for green, 5 bits for blue, and 1 bit for the alpha component,
it just show you if the pixel is transparent or displayed. Useful for simple sprites.
- OSL_PF_4444: 16 bits per pixel, 4 bits for red, green, blue and alpha components.
These images have a limited amount of colors (4096), but they can define 16 levels of transparency for each pixel.
- OSL_PF_8BIT: 8 bits per pixel, indexed on a palette.
- OSL_PF_4BIT: 4 bits per pixel, indexed on a palette.
OSL_IMAGE *oslLoadImageFile(char *filename, int location, int pixelFormat);
This function can load an image from a file. You can go back to the root of the Memory Stick by typing a path like this one: ms0:/path/file.png. Be careful: respect the case upper and the case lower, for example if a file called "image.png" is in the MS, and you put "Image.png" in your code, the load will fail!
void oslCopyImage(OSL_IMAGE *imgDst, OSL_IMAGE *imgSrc);
This function can copy an image to another one, but both have to have identical formats. Example:
myImage1 = oslCreateImage(32, 32, OSL_IN_RAM, OSL_PF_8888); myImage2 = oslCreateImage(32, 32, OSL_IN_RAM, OSL_PF_8888); //myImage2 <- myImage1 oslCopyImage(myImage2, myImage1);
OSL_IMAGE *oslCreateImageCopy(OSL_IMAGE *src, int newLocation);
Create a copy of an image, with the possibility to choose where the copy will take place. Exept that, the returned image will have exactly the same format as the source image passed as a parameter. The example of the oslCopyImage function can also be done like this:
monImage1 = oslCreateImage(32, 32, OSL_IN_RAM, OSL_PF_8888); monImage2 = oslCreateImageCopy(monImage1, OSL_IN_RAM);
void oslUncacheImage(OSL_IMAGE *img);
Put out an image from the CPU cache. If you access to the image data (from its data member), you must call this function passing the image in question before you can draw it. For more informations, go to the chapiter about the CPU cache. Example:
img = oslCreateImage(32, 32, OSL_IN_RAM, OSL_PF_8888); unsigned long *ptr = oslGetImagePixelAdr(img, 15, 10); //Colorizes 5 red pixels starting from (15,10). for (i=0;i<5;i++) *ptr++ = RGB(255,0,0); oslUncacheImage(img); //Without the oslUncacheImage call, the GPU will draw the image incorrectly (Because it is still in the CPU cache and the GPU has no access to it). oslDrawImage(img);
Once you finished with an image, you can erase it by simply calling the following function:
void oslDeleteImage(OSL_IMAGE *img);
Unfortunately, as there is no VRAM heap manager yet, images in VRAM can be erased only if it was the last created! Example:
//Creation of images myImage1 = oslCreateImage(16, 16, OSL_IN_VRAM, OSL_PF_8888); myImage2 = oslCreateImage(32, 32, OSL_IN_VRAM, OSL_PF_8888); [...] //They have to be deleted in the inverse order they have been created (like a plate stack). oslDeleteImage(myImage2); oslDeleteImage(myImage1);
If another image is created in between two images, you cannot erase myImage1 before myImage2. If you do not make it, the program will not crash, but your images can become corrupted. Here is an example that will generate image corruption:
image1 = oslLoadImageFile("image1.png", OSL_IN_VRAM, OSL_PF_5551); image2 = oslLoadImageFile("image2.png", OSL_IN_VRAM, OSL_PF_5551); //Big mistake! You must delete image2 before image1! oslDeleteImage(image1); //Image2 will be corrupted by image3. image3 = oslLoadImageFile("image3.png", OSL_IN_VRAM, OSL_PF_5551);
However, there is no problem if you put your images in RAM, that's why you should avoid using VRAM unless you know exactly what you are doing.
Image manipulation
An image is a structure wich contain all the informations about itself. The first elements (public) are those who can be modified, the others (private) are internal it is recommended to not change them. Example:
myImage = oslCreateImage(16, 16, OSL_IN_VRAM, OSL_PF_8888); myImage->angle = 20; //The image will be drawn with a rotation of about 20° oslDrawImage(myImage);
Here are the members we were talking about:
typedef struct { //public int x, y; //Positions int stretchX, stretchY; //Final size (stretched) //Piece float offsetX0, offsetY0, offsetX1, offsetY1; //Rotation int centerX, centerY; //Rotation center float angle; //Angle (rotation) //Parameters bool autoStrip; //Auto stripping (incompatible with rotation) bool bilinear; OSL_PALETTE *palette; //Palette for 4 and 8 bits modes //private int sizeX, sizeY; //Real size int sysSizeX, sysSizeY; //Aligned size int realSizeX, realSizeY; //Size if aligned void *data; //Image data bool isCopy; //Is a copy of another image? bool isSwizzled; //Is swizzled (optimized)? int totalSize; //Size in bytes short location, pixelFormat; } OSL_IMAGE;
The x and y positions are used to control where the image will be drawn on the screen. The upper-left corner is 0,0 and the bottom-right is
479,271. The first coordinate (x) is the horizontal component et the other (y) the vertical one.
The stretchX and stretchY properties can zoom an image. Put them at the size you want. For example
a 16x16 image you want to zoom 2x will have put 32 to stretchX and stretchY. To calculate this, you can use the sizeX and sizeY
properties, or offsetX1-offsetX0 and offsetY1-offsetY0 if it's an image tile:
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);
The properties offset** have to be used to adjust the image frame of the image which will be copied on the screen. For example, if your image is 16x16 and you want only the upper-right 8x8 part, you can put 8 to offsetX0, 0 to offsetY0, 16 to offsetX1 and 8 to offsetY1. To define these properties more simply, use this:
oslSetImageTile(image,x0,y0,x1,y1); oslSetImageTileSize(image,x,y,width,height);
The centerX and centerY properties define the center of the rotation of the image, and angle defines the angle of rotation of the image. Using rotation reduces performances, especially for big images, so be sure to make tests before using it.
autostrip optimizes display speed of big images (enabled by default).
bilinear defines a bilinear filter to the image.
palette defines the palette used to draw an indexed image. You can create one and associate it to the image like this:
const unsigned long myPalette[2] = {RGB(0,0,0), RGB(255,0,0)}; //Red and white myImage->palette = oslLoadPalette(myPalette, 2, OSL_PF_8888);
With OSLib, you can redirect all drawing operations to an image. It is useful, because you can draw this image on the screen by applying it all the effects allowed to a standard image. For example you can draw the contents of a window then make it transparent or stretch it.
For that, we use the following function:
void oslSetDrawBuffer(OSL_IMAGE *img);
This function defines the drawbuffer, that means where you draw.
You just have to pass the following parameters:
- OSL_DEFAULT_BUFFER: Default screen (resets to normal operation).
- OSL_SECONDARY_BUFFER: In double buffering mode, the displayed screen (unlike the drawing one).
- an image: Will draw on this image.
OSLib includes a global variable named osl_curBuf, which is a pointer to the current drawing image. So you can obtain the properties of the screen on which you are drawing, and adapt yourself to the resolution or the pixelformat. Here is an example of use of this function:
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);
Warning: The image on which you want to draw HAS TO be in VRAM, otherwise it won't work! Moreover it is why I erase it just after, because you should not stack too many images in VRAM for the enumerated reasons higher.