>binaervarianz >projects >CocoaRay
Chapter 3: Anatomy of a Bitmap

After representing our picture as a NSBitmapImageRep, the question arises how to fill this image with content. Besides its comlicated name, this Cocoa class is nothing more than a clasiscal bitmap, known since the dawn of Windows as .bmp. The class just takes responibilty for managing the header data. The image data can be accessed with a pointer to it.

But how is this content represented in code? What our class is returning as image data is essentially a pointer to a field of unsigned char. Siplier put, these are just raw bytes. A byte, meaning 8 bit, is enough to save the information of one base color. Three bytes define the color of a pixel as red, green and blue. our color resolution therefor is 3*8 = 24 bit, thats what Mac OS X is calling 'millions of colors'
If the three bytes of one pixel are aligned directly after one another, the configuration is called 'merged planes'. If all red values of all pixels come first, then all blue values and so on, it's a 'planar' configuration. As another sample, or another plane, an alpha value can be stored to represent the transparency if that pixel. We will only use three samples and order them in a merged configuration for a 360*240px image.

This configuration is done in the -(id)initWithRect:(NSRect)rect method of the BIRenderer after the initialisation of the NSBitmapImageRep:


[image initWithBitmapDataPlanes:planes
pixelsWide:360 pixelsHigh:240
bitsPerSample:8 samplesPerPixel:3
hasAlpha:NO isPlanar:NO
colorSpaceName:NSCalibratedRGBColorSpace
bytesPerRow:280*3*8/8 bitsPerPixel: 3*8 ];

All values should be pretty self-explaining, but what is it about that planes variable? This is an array for the mentioned planes of the image. Because we use a merged configuration we only use the first plane. This first plane is initialised to the contents of a NSMutableData object. It is the Cocoa version of a simple byte buffer. We have to keep the pointer to this, cause with this we will be able to change the bytes in oure image. Now we pushed ourselfs backwards through the following definitions, which of course must appear befor the initialisation:

Header:


NSMutableData* pixels;
unsigned char* baseAddr;
-(id)initWithRect:(NSRect)rect:

pixels = [[NSMutableData alloc] initWithLength:360*240*3];
baseAddr = [pixels mutableBytes];
unsigned char* planes[4]={baseAddr, NULL, NULL, NULL};

Of course instead of a NSMutableData we could have used an unsigned char[360*240*3]. But this would need a fixed size at time of definition, while the Cocoa class can be resized.

So lets see if we can display something. We noe implementa our -(void)drawImage method and put some code in. We need two nested loops to go through all pixels row- and columnwise. With the row- and column index we can then calculate the offset for the pointer to access the pixel. The offset can be seen as the address of the three color bytes in the buffer. We achieve this with:

row index * bytes per row + column index * bytes per pixel

With this we get the address of oure first color byte (red) of the indexed pixel. Lets try to draw a red rectangle:


- (void)drawImage{
int columnIndex=0, rowIndex=0;
for(rowIndex=0;rowIndex<360;rowIndex++){
for(columnIndex=0;columnIndex<240;columnIndex++){
if(columnIndex>100 && columnIndex<280 && rowIndex>100 && rowIndex<140){
baseAddr[rowIndex*3*360 + columnIndex*3+0]='\xFF';
baseAddr[rowIndex*3*360 + columnIndex*3+1]='\x00';
baseAddr[rowIndex*3*360 + columnIndex*3+2]='\x00';
}
else{
baseAddr[rowIndex*3*360 + columnIndex*3+0]='\x00';
baseAddr[rowIndex*3*360 + columnIndex*3+1]='\x00';
baseAddr[rowIndex*3*360 + columnIndex*3+2]='\x00';
}
}
}
}

InterfaceBuilder


TADAAAA!!! Our first, self created output. You now can draw some pictures if you like to train your 2D imagination capabilitys. The first optimization homework you can do, is to put all those absolut numbers we used in variables and see if you can calculate them out of each other. For example the pixel sizes can be retrieved from the NSRect given for initialisation. The codng style would be improved, if you can manage to define all independent parameters in a general -(id)init method while setting all somewhat dependent variables inside the -(id)initWithRect:(NSRect)rect. The last mentioned can call the first one to initialise.

>binaervarianz >projects >CocoaRay