Page 1 of 1

BS Image Loader Example

Posted: December 11th, 2013, 1:19 pm
by LameGuy64
Here's an example BS image loader I've put together as it could be useful to some including me. Being that I've wrote the entire loader as a single function for convenience, it should be very easy to implement into your own games/apps... Just make sure you have enough memory available when calling the function.

Basically, BS (or Bit Stream) images are like JPEG images but can be decoded quickly by the MDEC chip inside the PlayStation. Being that BS is similar to JPEG, it is lossy and is not a desirable format for texture data but, because BS images are a lot smaller than an uncompressed 640x480 24-bit TIM image (which is very close to a megabyte), it is great for quickly loading and displaying a hi-res 640x480 illustration in 24-bit color during loading sessions.

Here's the code:

Code: Select all

#include <sys/types.h>
#include <libgte.h>
#include <libgpu.h>
#include <libpress.h>

// C file containing our sample BS image
#include "scarlett.c"

void LoadBS (int width, int height, int x, int y, int mode, u_long* bsfile);

int main () {

	RECT	rect={ 0, 0, 1024, 512 };
	DISPENV disp;
	
	// Reset the GPU and clear the entire framebuffer
	ResetGraph(0);
	ClearImage2(&rect, 0, 0, 0);
	
	// Set 640x480 24-bit color video mode (note: only framebuffer operations work in this mode)
	SetDefDispEnv(&disp, 0, 0, 640, 480);
	disp.isrgb24 = 1;
	PutDispEnv(&disp);
	
	// Remove the display mask
	SetDispMask(1);
	
	// Load the BS image
	LoadBS(640, 480, 0, 0, 1, (u_long*)&bs_scarlett[0]);
	
	printf("Decode complete!\n");

}

void LoadBS (int width, int height, int x, int y, int mode, u_long* bsfile_ptr) {

	/*
	
	*TINY* BS image loader coded by Lameguy64 of Meido-Tek Productions (2013)
	
	SYNTAX:
	width, height 	- Size of the BS image (image must be a multiple of 16 for both dimensions)
	x, y			- Framebuffer location on where to draw the decoded image
	mode			- Decode color depth mode (0 - 16-bit, 1 - 24-bit)
	bsfile_ptr		- Pointer to the BS image data to decode
	
	NOTE:
	The BS image must be at least 24 sectors (49152 bytes) or less in size otherwise, the MDEC
	will freeze at a certain point of decoding. This can be set in MC32 by clicking the Attributes
	button, click the Custom check box, enter 24 sectors for maximum frame size, and then click
	the Variable Frame Size radio button before converting.
	
	*/

	int		mdec_col;
	int		mdec_cellsize	= (16 + (8 * mode));		// Calculate the size of a cell (16 pixels in 16-bit mode, 24 'odd' pixels in 24-bit mode)
	int		mdec_stripsize	= (mdec_cellsize * height); // Calculate the size for storing several cells in a single strip
	RECT	mdec_rect;
	
	u_long 	mdec_strip[mdec_stripsize];					// Buffer for one vertical strip of decoded image data
	u_long	mdec_buff[DecDCTBufSize(bsfile_ptr) + 1]; 	// I want to avoid using mallocs (for convenience :>)
	
	DecDCTReset(0);							// Reset the MDEC chip
	
	DecDCTvlc(bsfile_ptr, &mdec_buff[0]);	// Decompress DCT data
	DecDCTin(&mdec_buff[0], mode);			// Set MDEC input to point to decompressed DCT data
	
	// Parameters for uploading the strip of decoded MDEC cells into the framebuffer
	mdec_rect.y = y;
	mdec_rect.w = mdec_cellsize;
	mdec_rect.h = height;
	
	// This is where the actual decode process takes place
	for (mdec_col = 0; mdec_col < (width / 16); mdec_col += 1) {
	
		DecDCTout(&mdec_strip[0], mdec_stripsize / 2);	// Grab a strip of decoded image data
		DecDCToutSync(0);								// Wait for the decode to finish
		
		// Upload the strip into the framebuffer
		mdec_rect.x = x + (mdec_cellsize * mdec_col);
		LoadImage(&mdec_rect, mdec_strip);
	
		// Wait until the upload has finished before loading another strip
		DrawSync(0);
	
	}

}
Compile it by entering the following into the command prompt (assuming you've already run PSPATHS.BAT):

Code: Select all

ccpsx -O -Xo$80010000 bs_test.c -obs_test.cpe,bs_test.sym
cpe2x bs_test.cpe
Here's a little tutorial on how to encode BS images:

1. Convert the image you're going to use into a TIM image using TIMTOOL (but make sure you import in 24-bit color depth).
2. Open up MC32 and select 'bs' on the right drop-down list on the small window.
3. Click the Attributes button and a window should pop-up.
4. Check the Custom box and enter 24 sectors as the maximum frame size (you can use a lower value for a smaller file size but lower quality)
5. Click the 'Variable Frame Size' option and then close the window.
6. Select the TIM file you've created with TIMTOOL on the left text box on the small window.
7. Once selected, a preview of the image should show up. Click the Encode button to see what it'll look like when encoded to BS.
8. Click the Go button to begin conversion.

Re: BS Image Loader Example

Posted: March 19th, 2019, 11:31 pm
by NITROYUASH
This code is one of the first things that i tested. Thanks!
But i have a maybe dumb question. Can we use LoadBS() and sort TIM's and/or work with frame buffer at the same time?

Here is the example from "Duke Nukem: Time to Kill":
► Show Spoiler
It's a .BS image, but it's using some TIM's (font and button icon) at the same time.

Re: BS Image Loader Example

Posted: March 20th, 2019, 4:13 am
by TriMesh
NITROYUASH wrote: March 19th, 2019, 11:31 pm This code is one of the first things that i tested. Thanks!
But i have a maybe dumb question. Can we use LoadBS() and sort TIM's and/or work with frame buffer at the same time?

Here is the example from "Duke Nukem: Time to Kill":
► Show Spoiler
It's a .BS image, but it's using some TIM's (font and button icon) at the same time.
You can certainly combine them on the same screen - just wait until the background image is loaded and then copy the other stuff to the framebuffer using LoadImage() - note that since the output of the MDEC is 24 bit, anything you write on top of it has to be 24 bit too. One other thing to watch out for is that LoadImage() writes a rectangular area and there is no alpha channel - one possible solution to this is to grab the background using StoreImage(), then update it with the non-transparent pixels and then write it out again.

Re: BS Image Loader Example

Posted: March 20th, 2019, 11:23 am
by LameGuy64
You can make the MDEC output 16-bit pixels instead of 24-bit so you can use the output as a texture since the GPU only supports drawing pixels in 16-bit color and reading textures in 16-bit color depth or less. This is how that video screen in one of the Tony Hawk games was achieved.

Re: BS Image Loader Example

Posted: March 22nd, 2019, 1:33 am
by Shadow
LameGuy64 wrote: March 20th, 2019, 11:23 am ...this is how that video screen in one of the Tony Hawk games was achieved.
The way Neversoft managed to pull that off was quite amazing and quite mind blowing. They managed to render the framebuffer inside of itself (almost like a camera-in-camera perspective). They also played music videos on it too. It can be seen in the level "Chicago' in Tony Hawks Pro Skateboarding.

Re: BS Image Loader Example

Posted: April 9th, 2019, 1:28 am
by NITROYUASH
LoadBS isn't working with Display() for some reason. It starts blinking like a crazy, so i can't sort BS images correctly in loop.

Code: Select all

void Display() {
	FntFlush(-1);
	VSync(0);

	GsSwapDispBuff(); // <- It starts blinking here.   (╯°□°)╯︵ ┻━┻
	GsSortClear(g_iGS_R, g_iGS_G, g_iGS_B, &myOT[ActiveBuffer]);
	GsDrawOt(&myOT[ActiveBuffer]);
}

Re: BS Image Loader Example

Posted: April 10th, 2019, 5:49 am
by Orion_
you should understand what you are doing, this will avoid you headache.

on the PS1, you have 2 screen buffers, one that you drawing in, and one that is currently displayed on screen (while you draw on the other), this avoid flickering.
The function GsSwapDispBuff swap between these 2 buffers.
Now, a BS image is a still image, it's not a sprite that you are rendering in the OT each frame.
The BS image is an image that you are copying once in the VRAM.
If you copy the image ONCE, in only one buffer, each time you will swap to the other buffer, it will display nothing, and the other time it will display the image again, that's why you get "blinking".
now, you must copy the BS image in the 2 buffers, and never clear the screen with GsSortClear, or your image will be erased

Re: BS Image Loader Example

Posted: April 10th, 2019, 7:52 am
by NITROYUASH
True. That's the problem with the 2'nd screen buffer. I know about double buffer but can't understand how to set the BS image into the 2 screens.

Oh, and i really can't clear the image with BS stuff?

Re: BS Image Loader Example

Posted: April 10th, 2019, 12:39 pm
by LameGuy64
Are you trying to show a sequence of BS'es? The recommended method would be to LoadBS to one part of the screen and use SetDispEnv to display the area the image was loaded to then you load the next BS on another part of the framebuffer which you then display with SetDispEnv. This closely resembles how FMVs are done on the PS1 except FMVs load frames in strips as data is being streamed in from the CD.

If you were to display it like a fullscreen background image load it to some part of the framebuffer and draw it as a texture with sprite primitives.

Re: BS Image Loader Example

Posted: April 28th, 2019, 6:20 am
by Shadow
Love it :lol:

Code: Select all

(╯°□°)╯︵ ┻━┻