Bitmap Converter for Arduino LCD

By | November 3, 2013

I wrote this small utility last night to convert 24-bit bitmaps, which somewhat inconveniently load from the bottom up, into a 16-bit, top-down format that can be directly loaded into the graphic memory of the small LCD I’m working with (see more here).  The result is a pretty drastic increase in load speed, not to mention the smaller file size.

There is some general error checking, but I didn’t go overboard.  Source code below.

Download bmp2lcd.c


/*

    bmp2lcd.c
    
    This program creates a graphic file that may be better suited for faster 
    loading on certain low-end LCD screens by simplifying the header and
    converting from 24 bits to 16 (5 bits red, 6 bits green, 5 bits blue).
    
    Output File:
    
    4-byte width
    4-byte height
    width * height * 2 bytes of pixel data stored top-down, left-to-right
    
    Compile:    gcc -Wall -o bmp2lcd bmp2lcd.c
    Usage:         bmp2lcd [input file name] [output file name]
    
    // License //
    
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
    
    
*/    
#include <stdio.h>
#include <stdlib.h>

#define BYTES_PER_PIXEL 3

typedef struct  __attribute__((__packed__)) {
    unsigned short  bmpID;
    unsigned int    fileSize;
    unsigned short  reserved1;
    unsigned short  reserved2;
    unsigned int    offset;
    unsigned int    headerSize;
    unsigned int    width;
    unsigned int    height;
    unsigned short  compression;
    unsigned short  bitDepth;
} bitmapHeader;

FILE *inFile = NULL;
FILE *outFile = NULL;

bitmapHeader header;
unsigned char *picBuffer = NULL;


int readBitmapHeader()
{
    int retVal = 0;

    if( fread( &header, sizeof(bitmapHeader), 1, inFile) )
    {
        if( header.bmpID == 0x4d42 )
        {        
            printf("offset: %u\n", header.offset);
            printf("width: %u\n", header.width );
            printf("height: %u\n", header.height ); 
            printf("depth: %u\n", header.bitDepth );
            
            if( header.bitDepth != 24 )
            {
                fprintf(stderr, "Must be a 24-bit graphic.\n");
            }
            else
                retVal = 1;
        }
    }
    return retVal;
}

int getMemory()
{
    picBuffer = (unsigned char *) malloc( header.width * header.height * BYTES_PER_PIXEL );
    if( ! picBuffer )
    {
        fprintf(stderr, "Unable to allocate memory.\n");
        return 0;
    }
    return 1;
}

int doBitmapConversion()
{
    int x, y;
    int rowIndex;
    
    unsigned short r, g, b, outColor;
    
    // Write dimensions to output file
    if( ! fwrite(&header.width, sizeof(unsigned int), 2, outFile) )
    {
        fprintf(stderr, "Error writing dimensions to output file.\n");
        return 0;
    }   
    
    // Go to start of pixel data
    fseek(inFile, header.offset, SEEK_SET);
    
    if( fread( picBuffer, header.width * header.height * BYTES_PER_PIXEL, 1, inFile) )
    {
        for(y = 0; y < header.height; y++ )
        {
            // Bitmaps are stored bottom-up, but I want to reverse that order
            rowIndex = (header.height - y - 1) * header.width * BYTES_PER_PIXEL;
            
            for(x = 0; x < header.width; x++ )
            {
                b = picBuffer[rowIndex++];
                g = picBuffer[rowIndex++];
                r = picBuffer[rowIndex++];
                outColor = ((r>>3) << 11) | ((g>>2) << 5) | (b >> 3);
                if( ! fwrite(&outColor, sizeof(unsigned short), 1, outFile) )
                {
                    fprintf(stderr, "Error writing output file.\n");
                    return 0;
                }
            }
        }
    }
    else
    {
        fprintf(stderr, "Error reading input file.\n");
        return 0;
    }
    
    return 1;
}

void cleanUp()
{
    if( inFile )
        fclose(inFile);
    
    if( outFile )
        fclose(outFile);
        
    if( picBuffer )
        free( picBuffer );
}

int openInputFile(char *fileName)
{
    inFile = fopen( fileName, "r" );
    if( ! inFile )
    {
        fprintf(stderr, "Error opening input file.\n");
        return 0;
    }
    return 1;
}

int openOutputFile(char *fileName)
{
    outFile = fopen( fileName, "w" );
    if( ! outFile )
    {
        fprintf(stderr, "Error opening output file.\n");
        return 0;
    }
    return 1;
}

int main( int argc, char **argv )
{
    if
    ( 
        argc == 3 && openInputFile(argv[1]) && openOutputFile(argv[2]) && 
        readBitmapHeader() && getMemory() && doBitmapConversion()
    )
    {
        printf("Bitmap successfully converted.\n");
    }
    else 
    {
        if( argc == 3 ) {
            printf("Failed to convert file.");
        }
        else {
            printf("\nUsage:\n");
            printf("------\n");
            printf("bmp2lcd [input file name] [output file name]\n\n");
        }
    }
    
    // Close files and release memory
    cleanUp();
    
    return 0;
}

8 thoughts on “Bitmap Converter for Arduino LCD

  1. Pingback: Touch Screen Shield for Arduino UNO | misc.ws

  2. Gunter Otté

    Hi,

    Thanks, great tool and articles about the touch screen.
    Concerning the code above. I just want to mention (for other people) that I ran into problems with GCC 4.8.1, it apears like it ignores the (__packed__) attribute. With GCC 4.8.3, no problems.
    This could be a problem for people who are using the MiniGW installer, because the latest still ships with GCC 4.8.1…

    Reply
    1. Justin Post author

      Thank you, Gunter. I appreciate the feedback and assistance.

      Reply
  3. EOLS

    hello, I need to display a image but its displaying it in bad quality, how could I add this code to my code?

    thanks

    Reply
  4. Pooya Eghbali

    a note to people who need this:
    If you’re compiling with mingw you should pass -mno-ms-bitfields to gcc or the utility will not work

    Reply
  5. Thiago

    The program is not working for me. It says that my image is not 24-bit but it is. Someone knows what’s going on? I am using Windows to run the converter.

    Reply

Leave a Reply

Your email address will not be published. Required fields are marked *