280 lines
6.7 KiB
C
280 lines
6.7 KiB
C
|
|
// Author: Christian Vallentin <vallentin.source@gmail.com>
|
|
// Website: http://vallentin.dev
|
|
// Repository: https://github.com/vallentin/LoadBMP
|
|
//
|
|
// Date Created: January 03, 2014
|
|
// Last Modified: August 13, 2016
|
|
//
|
|
// Version: 1.1.0
|
|
|
|
// Copyright (c) 2014-2016 Christian Vallentin <vallentin.source@gmail.com>
|
|
//
|
|
// This software is provided 'as-is', without any express or implied
|
|
// warranty. In no event will the authors be held liable for any damages
|
|
// arising from the use of this software.
|
|
//
|
|
// Permission is granted to anyone to use this software for any purpose,
|
|
// including commercial applications, and to alter it and redistribute it
|
|
// freely, subject to the following restrictions:
|
|
//
|
|
// 1. The origin of this software must not be misrepresented; you must not
|
|
// claim that you wrote the original software. If you use this software
|
|
// in a product, an acknowledgment in the product documentation would be
|
|
// appreciated but is not required.
|
|
//
|
|
// 2. Altered source versions must be plainly marked as such, and must not
|
|
// be misrepresented as being the original software.
|
|
//
|
|
// 3. This notice may not be removed or altered from any source
|
|
// distribution.
|
|
|
|
// Include loadbmp.h as following
|
|
// to create the implementation file.
|
|
//
|
|
// #define LOADBMP_IMPLEMENTATION
|
|
// #include "loadbmp.h"
|
|
#define LOADBMP_IMPLEMENTATION
|
|
|
|
#ifndef LOADBMP_H
|
|
#define LOADBMP_H
|
|
|
|
#ifdef __cplusplus
|
|
extern "C" {
|
|
#endif
|
|
|
|
|
|
// Errors
|
|
#define LOADBMP_NO_ERROR 0
|
|
|
|
#define LOADBMP_OUT_OF_MEMORY 1
|
|
|
|
#define LOADBMP_FILE_NOT_FOUND 2
|
|
#define LOADBMP_FILE_OPERATION 3
|
|
|
|
#define LOADBMP_INVALID_FILE_FORMAT 4
|
|
|
|
#define LOADBMP_INVALID_SIGNATURE 5
|
|
#define LOADBMP_INVALID_BITS_PER_PIXEL 6
|
|
|
|
|
|
// Components
|
|
#define LOADBMP_RGB 3
|
|
#define LOADBMP_RGBA 4
|
|
|
|
|
|
#ifdef LOADBMP_IMPLEMENTATION
|
|
# define LOADBMP_API
|
|
#else
|
|
# define LOADBMP_API extern
|
|
#endif
|
|
|
|
|
|
// LoadBMP uses raw buffers and support RGB and RGBA formats.
|
|
// The order is RGBRGBRGB... or RGBARGBARGBA..., from top left
|
|
// to bottom right, without any padding.
|
|
|
|
|
|
LOADBMP_API unsigned int loadbmp_decode_file(
|
|
const char *filename, unsigned char **imageData, unsigned int *width, unsigned int *height, unsigned int components);
|
|
|
|
LOADBMP_API unsigned int loadbmp_encode_file(
|
|
const char *filename, const unsigned char *imageData, unsigned int width, unsigned int height, unsigned int components);
|
|
|
|
|
|
#ifdef LOADBMP_IMPLEMENTATION
|
|
|
|
// Disable Microsoft Visual C++ compiler security warnings for fopen, strcpy, etc being unsafe
|
|
#if defined(_MSC_VER) && (_MSC_VER >= 1310)
|
|
# pragma warning(disable: 4996)
|
|
#endif
|
|
|
|
#include <stdlib.h> /* malloc(), free() */
|
|
#include <string.h> /* memset(), memcpy() */
|
|
#include <stdio.h> /* fopen(), fwrite(), fread(), fclose() */
|
|
|
|
|
|
LOADBMP_API unsigned int loadbmp_decode_file(
|
|
const char *filename, unsigned char **imageData, unsigned int *width, unsigned int *height, unsigned int components)
|
|
{
|
|
FILE *f = fopen(filename, "rb");
|
|
|
|
if (!f)
|
|
return LOADBMP_FILE_NOT_FOUND;
|
|
|
|
unsigned char bmp_file_header[14];
|
|
unsigned char bmp_info_header[40];
|
|
unsigned char bmp_pad[3];
|
|
|
|
unsigned int w, h;
|
|
unsigned char *data = NULL;
|
|
|
|
unsigned int x, y, i, padding;
|
|
|
|
memset(bmp_file_header, 0, sizeof(bmp_file_header));
|
|
memset(bmp_info_header, 0, sizeof(bmp_info_header));
|
|
|
|
if (fread(bmp_file_header, sizeof(bmp_file_header), 1, f) == 0)
|
|
{
|
|
fclose(f);
|
|
return LOADBMP_INVALID_FILE_FORMAT;
|
|
}
|
|
|
|
if (fread(bmp_info_header, sizeof(bmp_info_header), 1, f) == 0)
|
|
{
|
|
fclose(f);
|
|
return LOADBMP_INVALID_FILE_FORMAT;
|
|
}
|
|
|
|
if ((bmp_file_header[0] != 'B') || (bmp_file_header[1] != 'M'))
|
|
{
|
|
fclose(f);
|
|
return LOADBMP_INVALID_SIGNATURE;
|
|
}
|
|
|
|
if ((bmp_info_header[14] != 24) && (bmp_info_header[14] != 32))
|
|
{
|
|
fclose(f);
|
|
return LOADBMP_INVALID_BITS_PER_PIXEL;
|
|
}
|
|
|
|
w = (bmp_info_header[4] + (bmp_info_header[5] << 8) + (bmp_info_header[6] << 16) + (bmp_info_header[7] << 24));
|
|
h = (bmp_info_header[8] + (bmp_info_header[9] << 8) + (bmp_info_header[10] << 16) + (bmp_info_header[11] << 24));
|
|
|
|
if ((w > 0) && (h > 0))
|
|
{
|
|
data = (unsigned char*)malloc(w * h * components);
|
|
|
|
if (!data)
|
|
{
|
|
fclose(f);
|
|
return LOADBMP_OUT_OF_MEMORY;
|
|
}
|
|
|
|
for (y = (h - 1); y != ((unsigned int) -1); y--)
|
|
{
|
|
for (x = 0; x < w; x++)
|
|
{
|
|
i = (x + y * w) * components;
|
|
|
|
if (fread(data + i, 3, 1, f) == 0)
|
|
{
|
|
free(data);
|
|
|
|
fclose(f);
|
|
return LOADBMP_INVALID_FILE_FORMAT;
|
|
}
|
|
|
|
data[i] ^= data[i + 2] ^= data[i] ^= data[i + 2]; // BGR -> RGB
|
|
|
|
if (components == LOADBMP_RGBA)
|
|
data[i + 3] = 255;
|
|
}
|
|
|
|
padding = ((4 - (w * 3) % 4) % 4);
|
|
|
|
if (fread(bmp_pad, 1, padding, f) != padding)
|
|
{
|
|
free(data);
|
|
|
|
fclose(f);
|
|
return LOADBMP_INVALID_FILE_FORMAT;
|
|
}
|
|
}
|
|
}
|
|
|
|
(*width) = w;
|
|
(*height) = h;
|
|
(*imageData) = data;
|
|
|
|
fclose(f);
|
|
|
|
return LOADBMP_NO_ERROR;
|
|
}
|
|
|
|
|
|
LOADBMP_API unsigned int loadbmp_encode_file(
|
|
const char *filename, const unsigned char *imageData, unsigned int width, unsigned int height, unsigned int components)
|
|
{
|
|
FILE *f = fopen(filename, "wb");
|
|
|
|
if (!f)
|
|
return LOADBMP_FILE_OPERATION;
|
|
|
|
unsigned char bmp_file_header[14] = { 'B', 'M', 0, 0, 0, 0, 0, 0, 0, 0, 54, 0, 0, 0 };
|
|
unsigned char bmp_info_header[40] = { 40, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 24, 0 };
|
|
const unsigned char bmp_pad[3] = { 0, 0, 0 };
|
|
|
|
const unsigned int size = 54 + width * height * 3; // 3 as the BMP format uses 3 channels (red, green, blue and NO alpha)
|
|
|
|
unsigned int x, y, i, padding;
|
|
|
|
unsigned char pixel[3];
|
|
|
|
bmp_file_header[2] = (unsigned char)(size);
|
|
bmp_file_header[3] = (unsigned char)(size >> 8);
|
|
bmp_file_header[4] = (unsigned char)(size >> 16);
|
|
bmp_file_header[5] = (unsigned char)(size >> 24);
|
|
|
|
bmp_info_header[4] = (unsigned char)(width);
|
|
bmp_info_header[5] = (unsigned char)(width >> 8);
|
|
bmp_info_header[6] = (unsigned char)(width >> 16);
|
|
bmp_info_header[7] = (unsigned char)(width >> 24);
|
|
|
|
bmp_info_header[8] = (unsigned char)(height);
|
|
bmp_info_header[9] = (unsigned char)(height >> 8);
|
|
bmp_info_header[10] = (unsigned char)(height >> 16);
|
|
bmp_info_header[11] = (unsigned char)(height >> 24);
|
|
|
|
if (fwrite(bmp_file_header, 14, 1, f) == 0)
|
|
{
|
|
fclose(f);
|
|
return LOADBMP_FILE_OPERATION;
|
|
}
|
|
|
|
if (fwrite(bmp_info_header, 40, 1, f) == 0)
|
|
{
|
|
fclose(f);
|
|
return LOADBMP_FILE_OPERATION;
|
|
}
|
|
|
|
for (y = (height - 1); y != ((unsigned int) -1); y--)
|
|
{
|
|
for (x = 0; x < width; x++)
|
|
{
|
|
i = (x + y * width) * components;
|
|
|
|
memcpy(pixel, imageData + i, sizeof(pixel));
|
|
|
|
pixel[0] ^= pixel[2] ^= pixel[0] ^= pixel[2]; // RGB -> BGR
|
|
|
|
if (fwrite(pixel, sizeof(pixel), 1, f) == 0)
|
|
{
|
|
fclose(f);
|
|
return LOADBMP_FILE_OPERATION;
|
|
}
|
|
}
|
|
|
|
padding = ((4 - (width * 3) % 4) % 4);
|
|
|
|
if (fwrite(bmp_pad, 1, padding, f) != padding)
|
|
{
|
|
fclose(f);
|
|
return LOADBMP_FILE_OPERATION;
|
|
}
|
|
}
|
|
|
|
fclose(f);
|
|
|
|
return LOADBMP_NO_ERROR;
|
|
}
|
|
|
|
|
|
#endif
|
|
|
|
#ifdef __cplusplus
|
|
}
|
|
#endif
|
|
|
|
#endif
|