posted by Jean-Louis Villecroze on Wed 8th Dec 2004 21:40 UTC

"Zinzala, Page 4/11"
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 \n\n");
   lCode.Write("#include <%s.h>\n\n",lName);
   lCode.Write("using namespace zSDK::Cincinella;\n\n");

   for(int i=2;i<argc;i+=2)
   {
      printf("loading %s ...\n",argv[i]);
      lBitmap = LoadBitmap(argv[i]);

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.

Table of contents
  1. "Zinzala, Page 1/11"
  2. "Zinzala, Page 2/11"
  3. "Zinzala, Page 3/11"
  4. "Zinzala, Page 4/11"
  5. "Zinzala, Page 5/11"
  6. "Zinzala, Page 6/11"
  7. "Zinzala, Page 7/11"
  8. "Zinzala, Page 8/11"
  9. "Zinzala, Page 9/11"
  10. "Zinzala, Page 10/11"
  11. "Zinzala, Page 11/11"
e p (0)    12 Comment(s)

Technology White Papers

See More