Calvin Godfrey
Calvin Godfrey

Reputation: 2369

What is wrong with my manually made .tga file?

I'm trying to make a basic ray tracer in C, and decided to use a .tga file as the output render file. But for whatever reason, the file that gets created shows as being solid white in every tga file viewer I can find. The start of the hexdump for the file I write to is here, and it seems pretty clear that large swaths of the image should be black.

The loop that writes to the file is:

void raycast(uint16_t width, uint16_t height, FILE* file, Sphere* sphere) {
    Vec3* camera = makeVec3(0, 0, 0);
    Vec3* light = makeVec3(1, -1, -1);
    int count = 0;
    double zMax = 1 / cos(toRads(vFOV) / 2.0);
    double yMax = 1 / cos(toRads(hFOV) / 2.0);
    for (uint16_t i = 0; i < height; i++) {
        for (uint16_t j = 0; j < width; j++) {
            int16_t zOffset = (height / 2 - i);
            double zprop = zOffset / (height / 2.0);
            double zCoord = zMax * zprop;
            int16_t yOffset = (width / 2 - j);
            double yprop = yOffset / (width / 2.0);
            double yCoord = yMax * yprop;
            Vec3* coord = makeVec3(1, yCoord, zCoord);
            normalize(coord);
            Ray* ray = makeRay(camera, coord);
            Vec3* intersect = sphereIntersect(sphere, ray);
            if (intersect == NULL) {
                fputc(255, file);
                fputc(255, file);
                fputc(255, file);
            } else {
                printf("disp black\n");
                fputc(0x00, file);
                fputc(0x00, file);
                fputc(0x00, file);
                count++;
            }
        }
    }
    printf("%d\n", count);
}

and I'm seeing disp black get printed to the console, and count gets incremented to over 300,000, so there's clearly nothing wrong with writing the zeros to the file. I tried my best to follow the header format that I found here, is there a problem with that?

I also noticed that the image is a solid color of whatever I put in the if block, as though if statement is always taken.

EDIT: Code that writes the header

static void writeHeader(FILE* file) {
    static uint16_t width = 800;
    static uint16_t height = 600;
    // 800x600 image, 24 bits per pixel
    unsigned char header[18] = {
        0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x18, 0x00, 0x00, 0x00, 0x00,
        (unsigned char)(width & 0x00FF), (unsigned char)((width & 0xFF00) / 256),
        (unsigned char)(height & 0x00FF), (unsigned char)((height & 0xFF00) / 256),
        0x08, 0x00
    };
    fwrite(header, sizeof(char), 18, file);
}

Upvotes: 1

Views: 682

Answers (1)

gavinb
gavinb

Reputation: 20018

The header is wrong. The code you posted is writing RGB data, but matching the hex dump with the spec in the page you linked, we see:

Identification Field | 00 = No ID field
Colour Map Type      | 01 = CMap Included
Image Type Code      | 01 = Type 1: Colour Mapped
Colour Map Spec      | 00 00 00 01 18 ...
Image Spec
X Origin             | 00 00 = 0
Y Origin             | 00 00 = 0
Width                | 20 03 = 800px
Height               | 58 02 = 600px
Bits/Pixel           | 08    = 8bpp
Descriptor           | 00...

your header says the file is colour-mapped. Since the header doesn't match the data, it is misinterpreted and not displaying what you expect to see.

You almost certainly want a Type 2 'Unmapped RGB' to match the data you are writing, so the 2nd byte should be 0x00 (you don't supply a colour map) and the 3rd byte should be 0x02 (indicating RGB). The colour map spec should then be ignored, so that can be zero-filled. The size etc looks fine.

I don't recommend simply writing a fixed header as an array of bytes like this. It is error prone and inflexible. It is very easy to define a struct which defines the header, and then you get the advantage of labelled fields, proper types, and symbolic constants if you define an enum for things like the image type code, and so on.

The linked article already provides this, you just need to make sure it is not padded by the compiler by means of a pragma to ensure the layout is exactly as described:

#pragma pack(push, 1)
typedef struct {
   char  idlength;
   char  colourmaptype;
   char  datatypecode;
   short int colourmaporigin;
   short int colourmaplength;
   char  colourmapdepth;
   short int x_origin;
   short int y_origin;
   short width;
   short height;
   char  bitsperpixel;
   char  imagedescriptor;
} HEADER;
#pragma pack(pop)

Upvotes: 3

Related Questions