bmap_savetga

Posted By: HeelX

bmap_savetga - 04/14/13 07:36

Some users reported that they can't properly save a bitmap as TGA with bmap_save, because the bitmap gets mirrored. This is due the fact that TGA stores a handedness flag that defines if the image origin is in the upper- or lower left corner. --- I found out, that if you set the flag in the TGA, that indicates the origin in the lower left corner, and you write the image upside down into the file, the bitmap is saved correctly and is loaded correctly back into the engine.

So I have written the following function "bmap_savetga" a while ago (inspired by ventilator's C-Script in the wiki) in Lite-C for another project to circumvent the upside-down problem; it also saves the alpha channel in the TGA, if and only if the passed BMAP* has an alpha channel, too; otherwise the TGA just saves RGB data.

Here is the function:

Code:
// Saves a bitmap as tga file
BOOL bmap_savetga (BMAP* b, char* filename)
{
    BOOL bSuccess = false;

    if (b && filename && strlen(filename) > 0)
    {
        var fh = file_open_write(filename);
        if (fh)
        {
            int height = b->height;
            int width = b->width;

            var format = bmap_lock(b, 0);

            // if the bitmap has an alpha channel
            BOOL bAlpha = (((format == 8888) || (format == 1555) || (format == 4444)) && (b->bytespp == 4));

            // header

            int i;
            for (i = 0; i < 2; i++)
                file_asc_write(fh, 0);

            file_asc_write(fh, 2); // uncompressed

            for (i = 0; i < 7; i++)
                file_asc_write(fh, 0);

            // y origin (lower left corner)
            file_asc_write(fh, height & 255);
            file_asc_write(fh, (height >> 8) & 255);

            file_asc_write(fh, width & 255);
            file_asc_write(fh, (width >> 8) & 255);

            file_asc_write(fh, height & 255);
            file_asc_write(fh, (height >> 8) & 255);
            
            int bypp = 24;
            if (bAlpha)
                bypp = 32;

            file_asc_write(fh, bypp); // bypp
            file_asc_write(fh, 0);

            // pixels

            COLOR bgr;
            var alpha;

            int ix, iy;
            for (iy = 0; iy < height; iy++)
            {
                for (ix = 0; ix < width; ix++)
                {
                    // retrieve BGR color and alpha
                    pixel_to_vec(&bgr, &alpha, format, pixel_for_bmap(b, ix, height - 1 - iy));

                    file_asc_write(fh, bgr.blue); // b
                    file_asc_write(fh, bgr.green); // g
                    file_asc_write(fh, bgr.red); // r

                    if (bAlpha)
                        file_asc_write(fh, alpha * 2.55); // a
                }
            }

            // done!
            bmap_unlock(b);
            file_close(fh);

            bSuccess = true;
        }
    }

    return(bSuccess);
}



Here is an example, that creates an RGBA image with a horizontal color gradient and a vertical alpha gradient, saves it and loads it again:

Code:
#include <acknex.h>
#include <default.c>

int main ()
{
    wait(3);
    
    int format = 8888;
    BMAP* b = bmap_createblack(100, 200, format);
    
    bmap_lock(b, 0);
    
    // create red to green gradient from left to right
    // and alpha gradient from translucent to solid from up to bottom
    
    int ix, iy;
    for (ix = 0; ix < b->width; ix++)
    {
        for (iy = 0; iy < b->height; iy++)
        {
            double fx = (double)ix / (double)(b->width-1);
            double fy = (double)iy / (double)(b->height-1);
            
            VECTOR color;
            vec_lerp(&color, COLOR_RED, COLOR_GREEN, fx);
            
            var pixel = pixel_for_vec(&color, 100 * fy, format);
            
            pixel_to_bmap(b, ix, iy, pixel);
        }
    }
    
    bmap_unlock(b);
    
    // save to file
    bmap_savetga(b, "b.tga");
    
    // load from file to compare
    BMAP* bf = bmap_create("b.tga");
    
    video_set(32+b->width+16+bf->width+32, 32+maxv(b->height, bf->height)+32, 0, 2);
    
    while (1)
    {
        draw_text("engine", 32, 32, COLOR_WHITE);
        draw_quad(b, vector(32, 48, 0), NULL, NULL, NULL, NULL, 100, 0);
        
        draw_text("file", 32 + b->width + 16, 32, COLOR_WHITE);
        draw_quad(bf, vector(32 + b->width + 16, 48, 0), NULL, NULL, NULL, NULL, 100, 0);
        
        wait(1);
    }
}



Screenshot:


I hope you find this useful.

Regards,
-Christian
Posted By: sivan

Re: bmap_savetga - 04/14/13 08:31

useful, I did also something similar based on the wiki script.
just some further info:
- bmap_save works fine with bmap_load (as I remember they are not engine functions, probably directx calls),
- bmap_create results in a flipped image if it was saved by bmap_save (and as I read earlier not planned to be fixed),
- image editors handles fine tga images created by bmap_save
Posted By: rojart

Re: bmap_savetga - 04/14/13 08:45

Yes, I remember some time ago for my Vader's entry contest I used flipped data to get this working, thanks for share it.
Posted By: NeoNeper

Re: bmap_savetga - 04/14/13 17:35

Thank you for sharing with us! (^.^)
Very helpful for my project and also learning
Posted By: PadMalcom

Re: bmap_savetga - 04/15/13 06:25

This should go to the TUST image lib! laugh
Posted By: sivan

Re: bmap_savetga - 04/15/13 06:57

it could be extended with a bmap_to_format conversion because currently it is not okay for dds images, but not essetnial, as it should be done before pixel manipulations...
Posted By: djfeeler

Re: bmap_savetga - 05/31/13 04:51

Thanks for this contibution ^^
© 2024 lite-C Forums