From cBitmap to resource
Earlier, we decided that all the graphics are going to be stored as resources in a shared object. For that reason, we need to store all the required information in a C++ file. The following type will provide everything we need to instantiate a cBitmap object:
typedef struct tBitmapResource {
tUint32 iWidth; // width of the bitmap
tUint32 iHeight; // height of the bitmap
tUint32 iIndex; // index of the transparent color in the palette
tUint16 iColors; // number of color in the palette
tUint32 iLength; // length in bytes of the bitmap's data
tUint8* iBits; // bitmap's data
tUint32* iPalette; // palette of colors
} tBitmapResource;
|
Here's the function DumpBitmapToFile() that will output a bitmap into a C++ compilable form:
void DumpBitmapToFile(cFile &aHeader,cFile &aCode,cBitmap *aBitmap,tColor *aPalette,
tUint16 aColors,const char *aName)
{
tUint16 lWidth,lHeight;
tUint8 lIndex;
tUint32 lLength;
tUint8* lBits;
PgColor_t lColor;
aBitmap->GetTransparent(lIndex);
aBitmap->GetSize(lWidth,lHeight);
lLength = aBitmap->BitsLength();
lBits = aBitmap->GetBits();
|
First, we retrieve the size, transparency and pixel data from aBitmap. The local variable lLength will contain the length of the data in bytes.
aHeader.Write("extern const tBitmapResource kRscBitmap%s;\n\n",aName);
|
aHeader is a reference to the cFile object pointing to the C++ header file. Here, we define a constant of type tBitmapResource for the bitmap we are currently outputting.
aCode.Write("tUint8 kBitmap%sBits [] = {\n\n\t",aName);
for(tUint16 k=0;k<lLength;k++)
{
lIndex = lBits[k];
if(k + 1 == lLength)
aCode.Write("0x%02x",lIndex);
else
aCode.Write("0x%02x,",lIndex);
if(!((1+k) % 16))
aCode.Write("\n\t");
}
aCode.Write("};\n\n");
|
Writing each pixel index color is fairly easy. Just as a reminder, we are using palette based graphics. We simply loop over all the pixels and write their hexadecimal value. For better legibility, we output these values into lines 16 pixels wide.
aCode.Write("tUint32 kBitmap%sPalette[%d] = {\n\n\t",aName,aColors);
for(tUint16 i=0;i<aColors;i++)
{
lColor = PgARGB(aPalette[i].iAlpha,aPalette[i].iRed,aPalette[i].iGreen,
aPalette[i].iBlue);
aCode.Write("0x%06x,",(tUint32)lColor);
if(!((1+i) % 8))
aCode.Write("\n\t");
}
|
Outputting the palette of color used by the bitmap is not very complicated. However, there is a little trick to know. If we write the RGB color in the form of an unsigned 32 bit integer, we can use them directly when instantiating the bitmap. By using the Photon macro PgARGB(), we insure that the color will be coded in a format that can be directly understood by the underlying Photon. To improve the legibility of the C++ file, we will also output only 8 colors per lines.
aCode.Write("\n};\n\n");
aCode.Write("const tBitmapResource kRscBitmap%s\t= {\n",aName);
aCode.Write("\t%d,%d,0x%02x,%d,%d,kBitmap%sBits,kBitmap%sPalette\n};\n",lWidth,
lHeight,lIndex,aColors,lLength,aName,aName);
}
|
Last but not least, in the C++ file we declare the same constant that we have previously defined in the header file. This time, we fill in all the data specifics to the bitmap.
Now, how will it look like in the C++ file ?
tUint8 kBitmapStopDBits [] = {
0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
...
...
...
0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,
0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00
};
tUint32 kBitmapStopDPalette[4] = {
0x88c188,0x82bd82,0x77b577,0x6bad6b,
};
const tBitmapResource kRscBitmapStopD = {
25,25,0x02,4,800,kBitmapStopDBits,kBitmapStopDPalette
};
|
The final touch to img2res
What's left to do in img2res is to use the 2 functions we implemented above in the main:
int main(int argc,char **argv)
{
tColor lPalette[256];
tChar lFilename[256];
cBitmap* lBitmap;
cFile lHeader;
cFile lCode;
tChar* lName;
tErr lErr;
if(argc<3)
{
printf("Usage : <filename> (<image> <label>)+\n");
return 1;
}
lName = argv[1];
// create header file
sprintf(lFilename,"%s.h",lName);
lErr = lHeader.Open(lFilename,kFileWrite | kFileTrunc);
if(lErr)
return 1;
// create code file
sprintf(lFilename,"%s.cpp",lName);
lErr = lCode.Open(lFilename,kFileWrite | kFileTrunc);
if(lErr)
{
lHeader.Close();
return 1;
}
|
To create and fill both C++ and header files, we use the class cFile. This class provides writing and/or reading in a file.
lHeader.Write("#ifndef _RES_%s\n",lName);
lHeader.Write("#define _RES_%s\n\n",lName);
lHeader.Write("#include <Cincinella/Types.h>\n\n");
lHeader.Write("using namespace zSDK::Cincinella;\n\n");
lHeader.Write("typedef struct tBitmapResource {\n");
lHeader.Write("\ttUint32 iWidth;\n");
lHeader.Write("\ttUint32 iHeight;\n");
lHeader.Write("\ttUint32 iIndex;\n");
lHeader.Write("\ttUint16 iColors;\n");
lHeader.Write("\ttUint32 iLength;\n");
lHeader.Write("\ttUint8* iBits;\n");
lHeader.Write("\ttUint32* iPalette;\n");
lHeader.Write("} tBitmapResource;\n\n");
|
In the header file we output the definition of the type tBitmapResource.
lCode.Write("#include |
For each graphic file given in the command line, we will try to load it. If this is successful and the bitmap is palette based, then we will process it further:
if(lBitmap)
{
if(lBitmap->GetColorSpace() == eSpaceColor8Bit)
{
tUint16 lWidth,lHeight;
tUint16 lColors = lBitmap->CountColors();
lBitmap->GetSize(lWidth,lHeight);
printf("bitmap is %dx%d pixels and have %d colors\n",lWidth,lHeight,lColors);
if(!lBitmap->GetPalette(lPalette))
{
DumpBitmapToFile(lHeader,lCode,lBitmap,lPalette,lColors,argv[i+1]);
printf("dumped\n");
}
else
printf("failed to get the colors palette\n");
}
else
printf("sorry this isn't a palette based image!\n");
}
else
printf("failed\n");
}
// fill files with closing stuff
lHeader.Write("\n");
lHeader.Write("#endif\n");
// and close them
lHeader.Close();
lCode.Close();
return 0;
}
|
To simplify things, I created a simple shell script to rebuild a C++ file from the graphics whenever we needed to. We would need to do this when the graphic team modifies some of them, or when we create a new skin:
#/bin/sh img2res Bitmaps \ Images/background.gif Background \ Images/dimmed.gif Dimmed \ Images/normal.gif Normal \ Images/pressed.gif Pressed \ Images/animate2.gif Animate1 \ Images/animate3.gif Animate2 \ Images/animate4.gif Animate3 \ Images/forward.gif ForwardN \ Images/forward_dim.gif ForwardD \ Images/pause.gif PauseN \ Images/pause_dim.gif PauseD \ Images/play.gif PlayN \ Images/play_dim.gif PlayD \ Images/rewind.gif BackwardN \ Images/rewind_dim.gif BackwardD \ Images/stop.gif StopN \ Images/stop_dim.gif StopD |
When creating a new skin, even if the name of each graphic file needs to be changed, the resource name should stay the same in order to keep the necessary adaptations from a skin to another as small as possible.
You can have a look at the complete source code of img2res, as well as an example of the header and source file generated by this tool.



0