Path: chuka.playstation.co.uk!scea!greg_labrec@interactive.sony.com From: Jack Copper Newsgroups: scea.yaroze.programming.2d_graphics Subject: The Answer to Static Backgrounds Date: Tue, 20 May 1997 23:40:12 -0400 Organization: ArcanaTech Lines: 240 Message-ID: <33826E76.1D5D@earthlink.net> Reply-To: impmeister@earthlink.net NNTP-Posting-Host: 1Cust12.Max33.Cleveland.OH.MS.UU.NET Mime-Version: 1.0 Content-Type: text/plain; charset=us-ascii Content-Transfer-Encoding: 7bit X-Mailer: Mozilla 3.01 (Win95; U) Thanks to some insights from Mario, I was able to get a full screen static background 8-bit CLUT image to display correctly. In the off chance that someone else eventually may want to do something similar, here are the tricks: 1. Save the image as a .BMP file (using, say, paintbrush) at the RENDERED pixel resolution you ultimately want. So, for a 512 x 240 RENDERED pixel image (a full 512 x 240 screen) save a 512 x 240 BMP. 2. Use TIM Utility to load the .BMP file, and save it as a TIM file. Select TIM as the write type, and 8 as the bit depth. Note that the default image org is [0,0] and the default palette org is [0,480] (which is the pixel row immediately below what will become your second display buffer. To store the Pixel data and CLUT in the non-visble portion of the frame buffer, you can either use TIM Utility to change the image origin (say, to [512,0]) and the palette origin (say, to [512,480]) OR you can save the file as is from TIM Utility and then load it into TIM Tool and move it around. Note that TIM Tool shows how many "physical" pixels are used for the Image- in this case, you'll see that the NON-Rendered image Width is 256 16-bit pixels wide (each 16-bit pixel contains TWO rendered pixels). The Colour Depth is 8, and the RENDERED Width is 512. 3. Now that the image and CLUT are correctly positioned in the TIM file, we can deal with the rendering code. The sparse documentation regarding background cells is what caused me so much grief... here's why: Per GsSortFixBG16(...) documentation, the size of a background Cell is fixed at 16 x 16 pixels. What it DOESN'T say, is that these are RENDERED pixels.. so the cell size in terms of a TIM file whose image uses an 8-bit CLUT is really ** 8 ** x 16 16-bit pixels. ARRGH. 4. So, initialization code looks something like this: #define X_RES 512 // Screen RENDERED horizontal resolution #define Y_RES 240 // Screen RENDERED vertical resolution #define CELL_W 8 // Background Cell Width // 8 16-bit pixels that contain two 8-bit CLUT entries // for 2 RENDERED pixels #define CELL_H 16 // Background Cell height #define N_H_CELLS 32 // number of cells (horizontal) // 32 * 8 = 256 16-bit "physical" pixels that become // 512 RENDERED visible pixels #define N_V_CELLS 15 // Number of cells (vertical) #define CELL_COUNT (N_H_CELLS * N_V_CELLS) // total Background cells GsCELL BGCell[ CELL_COUNT ]; // array of background cells (this array // maps over the actual pixel image from the // TIM file) unsigned short index[ CELL_COUNT ]; // cell map index array GsBG Background; // the basic Background structure GsMAP BGMap; // the Background cell map // Background work area (see pg 142 in lib manual) unsigned long BGWorkArea[(((X_RES/CELL_W+1)*(Y_RES/CELL_H+1+1)*6+4)*2+2)]; void main( void ) { // whatever else you need RECT Rectangle; // for TIM image and CLUT position GsIMAGE BGImage; // for TIM file conversion unsigned long* pTimFile = 0xA01E1000; // this is where I loaded TIM file GsCell* pCell = &BGCell[0]; // pointer for initialization unsigned short pIndex = &index[0]; // pointer for initialization int i,j,k; // counters int x; // for texture page x location // do the usual initialization GsInitGraph( X_RES, Y_RES, GsNONINTER | GsOFSGPU, 1, 0); GsDefDispBuff( 0,0,0,Y_RES ); // load raw pixel image GsGetTimInfo( pTimFile+1, &BGImage); // pTimFile+1 effectively skips count // (see TIM file documentation) Rectangle.x = BGImage.px; // pixel image x (should be 512, if you // followed instructions above) Rectangle.y = BGImage.py; // pixel image y (should be 0) Rectangle.w = BGImage.pw; // pixel image width (should be *256* !!) // RENDERED image will be 512 Rectangle.h = BGImage.ph; // pixel image height (should be 240) LoadImage( &Rectangle, BGImage.pixel); // load raw image pixels // now load CLUT (we KNOW it's there !) Rectangle.x = BGImage.cx; // CLUT x (should be 512, if you // followed instructions above) Rectangle.y = BGImage.cy; // CLUT y (should be 480) Rectangle.w = BGImage.cw; // CLUT width (should be 256) Rectangle.h = BGImage.ch; // CLUT height (should be 1) LoadImage( &Rectangle, BGImage.clut); // load CLUT // set up background STRUCTURE Background.attribute = 0x01000000; // we're using an 8-bit CLUT Background.x = 0; // x origin in DISPLAY buffer Background.y = 0; // y origin in DISPLAY buffer Background.w = X_RES; // RENDERED width Background.h = Y_RES; // RENDERED height Background.scrollx = 0; // no scrolling Background.scrolly = 0; Background.r = 128; // no change in intensity Background.g = 128; Background.b = 128; Background.map = &BGMap; // point to GsMAP structure Background.mx = 0; // no change Background.my = 0; Background.scalex = ONE; // no scaling, courtesy Sony Background.scaley = ONE; Background.rotate = 0; // no rotation // set up background MAP // set the cell width BGMap.cellw = CELL_W; // 8 !! NON-RENDERED pixels in "raw" image located // in non-visible portion of frame buffer // set the cell height BGMap.cellh = CELL_H; // 16 NON-RENDERED (== rendered) pixels // set number of cells in horizontal direction BGMap.ncellw = N_H_CELLS; // 32 horizontal cells // set number of cells in vertical direction BGMap.ncellh = N_V_CELLS; // 16 vertical cells BGMap.base = &BGCell[0]; // point to base of GsCELL array BGMap.index = &index[0]; // point to base of index // now the confusing stuff--- // (shifting and pointers should be faster than array indexing) for (i=0; i < CELL_COUNT; i++) { *pIndex = i; // cells are contiguous and displayed sequentially // so counter becomes index // REMEMBER: N_H_CELLS == 32 == 2^5 k = i >> 5; // compute 0-based row number == (i / N_H_CELLS) j = i - (k <<5); // compute 0-based cell in row == (i % N_H_CELLS) // the "texture page" x parameter of GsGetTPage() // MUST be a multiple of 64 // and the y parameter MUST be either 0 or 256 // since our TIM image height is 240 pixels, y // is always 0 ! // THIS IS THE MAGIC !! // the u and v members of the GsCELL structure // are offsets with respect to the "texture page" // a "texture page" is 64 16-bit pixels wide x = 512 + ((j>>3)<<6); // (j>>3) == (cell in row) / 8 THEN // << 6 is: multiply by 64 // what this means is that the TIM image pixels // are on 4 different texture pages, starting at // x = 512, 576, 640, and 704 ! // pCell initialized above ! // compute horizontal offset in page to start // of cell // there are 8 cells per texture page // WITHIN a page, the 8 16-bit pixels in a cell // mean that the offset is 16 RENDERED pixels // (since there are 2 RENDERED pixels in a single // 16-bit pixel) // GsSortFixBG16 expects the offset to be in terms // of RENDERED pixels in this structure !! // j % 8 * 16 offset per cell current cell index pCell->u = ((j - ((j>>3)<<3)) << 4); // 16 * (j%8) // compute vertical offset in page to start of // cell pCell->v = k << 4; // this is (0-based row number) * 16 pCell->cba = 0x7820; // this is CLUT location [512, 480] converted // to hex and put in the right bits per // documentation for GsCELL (pg. 40) pCell->flag = 0; // no flag bits used pCell->tpage = GetTPage(2,0, x, 0); // 2 indicates 8-bit CLUT // x from above // see pg 56 in library doc pCell++; // next cell pIndex++; // next index } // end of initializing for loop // initialize background work area GsInitFixBg16( &Background, &BGWorkArea[0] ); // do other initialization for (;;) // on a clear disk, you can seek forever { // do whatever.... i = GsGetActiveBuff(); // get active buffer GsSetWorkBase( (PACKET *)GpuOutputPacket[i]); // not needed for simple // background display GsClearOt(0, 0, &WorldOrderingTable[i]); // ASSUME the usual ordering // table initialization // was done above !!! // I didn't show it // HERE'S where background gets magically transformed from 256 pixels // to 512 !! GsSortFixBg16( &Background, &BgWorkArea[0], &WorldOrderingTable[i], 100); DrawSync(0); // wait for rendering to complete VSync(0); // wait for Godot GsSwapDispBuff(); // swap buffers // and do the usual drawing stuff.. GsSortClear( 0, 0, 0, &WorldOrderingTable[i]); GsDrawOt( &WorldOrderingTable[i] ); } // end of forever.... // tidy up... } // end main Duck soup... Thanks again, Mario. Jack Copper