2.4 BMP Image

(Image downloaded from International Wolf Center: "grizzer and shadow")

Complete Source Code:

Main.cpp

#include <GL/glew.h>

#include <GLFW/glfw3.h>

#include <glm/glm.hpp>

#include <glm/gtc/type_ptr.hpp>

#include <glm/gtx/transform.hpp>

#include <glm/gtc/matrix_transform.hpp>

#include <glm/gtc/quaternion.hpp>

#include <glm/gtx/rotate_vector.hpp>

#include <iostream>

#include <vector>

#include "Shader.h"

#include "BMP.h"

using namespace std;

using namespace glm;

/* main functions */

void Initialize();

void Render();

void CleanUp();

void CheckForGLError();

/* variables */

GLFWwindow* window = nullptr;

GLuint program = 0;

GLuint vertexshader = 0;

GLuint fragmentshader = 0;

GLuint vertexarray = 0;

GLuint vertexbuffer = 0;

GLuint texture = 0;

/* structures */

struct Vertex {

vec3 Position;

vec2 TexCoord;

};

struct Texel {

unsigned char r, g, b, a;

};

int main(void)

{

/* Initialize the library */

if (!glfwInit())

return 1;

/* Create a windowed mode window and its OpenGL context */

window = glfwCreateWindow(800, 600, "Hello World", NULL, NULL);

if (!window)

{

glfwTerminate();

return 1;

}

/* Make the window's context current */

glfwMakeContextCurrent(window);

/* Initialize GLEW */

if (glewInit() != GLEW_OK)

{

glfwTerminate();

return 1;

}

Initialize();

/* Loop until the user closes the window */

while (!glfwWindowShouldClose(window))

{

/* Render here */

Render();

/* Swap front and back buffers */

glfwSwapBuffers(window);

/* Poll for and process events */

glfwPollEvents();

}

CleanUp();

glfwTerminate();

return 0;

}

void Initialize()

{

// background color = gray

glClearColor(0.5f, 0.5f, 0.5f, 0.0f);

// create all objects

program = glCreateProgram();

vertexshader = glCreateShader(GL_VERTEX_SHADER);

fragmentshader = glCreateShader(GL_FRAGMENT_SHADER);

glGenVertexArrays(1, &vertexarray);

glGenBuffers(1, &vertexbuffer);

glGenTextures(1, &texture);

// shader source code

string vertexshader_source = {

"#version 450 core\n"

"in layout (location = 0) vec3 in_position;"

"in layout (location = 1) vec2 in_texcoord;"

"out vec2 texcoord;"

"void main () {"

"gl_Position = vec4(in_position, 1);"

"texcoord = in_texcoord;"

"}"

};

string fragmentshader_source = {

"#version 450 core\n"

"in vec2 texcoord;"

"uniform layout (binding = 3) sampler2D tex1;"

"out layout (location = 0) vec4 out_color;"

"void main () {"

"out_color = texture(tex1, texcoord);"

"}"

};

// compile shaders and link program

CompileShader(vertexshader, vertexshader_source);

CompileShader(fragmentshader, fragmentshader_source);

LinkProgram(program, { vertexshader, fragmentshader });

// setup vertex array

glBindVertexArray(vertexarray);

glBindBuffer(GL_ARRAY_BUFFER, vertexbuffer);

glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (GLvoid*)(0));

glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), (GLvoid*)(sizeof(vec3)));

glBindBuffer(GL_ARRAY_BUFFER, 0);

glEnableVertexAttribArray(0);

glEnableVertexAttribArray(1);

glBindVertexArray(0);

// setup vertex buffer

float size = 0.9f;

vector<Vertex> vertices = {

{ { -size, -size, 0 },{ 0, 0 } },

{ { +size, -size, 0 },{ 1, 0 } },

{ { +size, +size, 0 },{ 1, 1 } },

{ { -size, +size, 0 },{ 0, 1 } },

};

glBindBuffer(GL_ARRAY_BUFFER, vertexbuffer);

glBufferData(GL_ARRAY_BUFFER, sizeof(Vertex) * vertices.size(), vertices.data(), GL_STATIC_DRAW);

glBindBuffer(GL_ARRAY_BUFFER, 0);

// texture data

unsigned int texturewidth = 2;

unsigned int textureheight = 2;

Texel texturedata_default[] = {

// blue yellow

{ 0x00, 0x00, 0xFF, 0xFF }, { 0xFF, 0xFF, 0x00, 0xFF },

// red green

{ 0xFF, 0x00, 0x00, 0xFF }, { 0x00, 0xFF, 0x00, 0xFF },

};

void* texturedata = texturedata_default;

// load .bmp image

// this bmp file loader was written by Benjamin Kalytta, http://www.kalytta.com/bitmap.h

CBitmap bmp;

if (bmp.Load("image.bmp"))

{

texturewidth = bmp.GetWidth();

textureheight = bmp.GetHeight();

texturedata = bmp.GetBits();

}

// setup texture

glBindTexture(GL_TEXTURE_2D, texture);

// set texture parameters

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);

// allocate texture memory

glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, texturewidth, textureheight);

// set texture data

glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, texturewidth, textureheight, GL_RGBA, GL_UNSIGNED_BYTE, texturedata);

glBindTexture(GL_TEXTURE_2D, 0);

/* connect texture with sampler in fragmentshader */

unsigned int textureunit = 3; // doesnt have to be 3, but thats the value set in the fragmentshader

glBindTextureUnit(textureunit, texture);

}

void Render()

{

/* clear framebuffer */

glClear(GL_COLOR_BUFFER_BIT);

/* draw textured quad */

glUseProgram(program);

glBindVertexArray(vertexarray);

glDrawArrays(GL_QUADS, 0, 4);

glBindVertexArray(0);

glUseProgram(0);

/* check for any error */

CheckForGLError();

}

void CleanUp()

{

// destroy all objects

glDeleteProgram(program);

glDeleteShader(vertexshader);

glDeleteShader(fragmentshader);

glDeleteVertexArrays(1, &vertexarray);

glDeleteBuffers(1, &vertexbuffer);

glDeleteTextures(1, &texture);

}

void CheckForGLError()

{

for (GLenum error; (error = glGetError()) != GL_NO_ERROR;)

{

cout << "OpenGL Error: \t";

if (error == GL_INVALID_ENUM)

cout << "GL_INVALID_ENUM";

if (error == GL_INVALID_VALUE)

cout << "GL_INVALID_VALUE";

if (error == GL_INVALID_OPERATION)

cout << "GL_INVALID_OPERATION";

if (error == GL_STACK_OVERFLOW)

cout << "GL_STACK_OVERFLOW";

if (error == GL_STACK_UNDERFLOW)

cout << "GL_STACK_UNDERFLOW";

if (error == GL_OUT_OF_MEMORY)

cout << "GL_OUT_OF_MEMORY";

if (error == GL_INVALID_FRAMEBUFFER_OPERATION)

cout << "GL_INVALID_FRAMEBUFFER_OPERATION";

if (error == GL_CONTEXT_LOST)

cout << "GL_CONTEXT_LOST";

cout << (char)7 << endl; /*play sound*/

cin.get();

}

}

Shader.h

#pragma once

#include <string>

#include <list>

// shader functions

std::string LoadTextFile(const std::string& filepath);

std::string ShaderTypeName(unsigned int shader);

bool CompileShader(unsigned int shader, const std::string& sourcecode);

std::string ShaderInfoLog(unsigned int shader);

bool LinkProgram(unsigned int program, const std::list<unsigned int>& shaderlist);

std::string ProgramInfoLog(unsigned int program);

Shader.cpp

#include "Shader.h"

#include <iostream>

#include <fstream>

#include <GL/glew.h>

std::string LoadTextFile(const std::string & filepath)

{

std::string result(""), line;

std::fstream f(filepath, std::ios::in);

while (f.good())

{

std::getline(f, line);

result += line + '\n';

}

return result;

}

std::string ShaderTypeName(unsigned int shader)

{

if (glIsShader(shader))

{

int type;

glGetShaderiv(shader, GL_SHADER_TYPE, &type);

if (type == GL_VERTEX_SHADER)

return "Vertex Shader";

if (type == GL_TESS_CONTROL_SHADER)

return "Tessellation Control Shader";

if (type == GL_TESS_EVALUATION_SHADER)

return "Tessellation Evaluation Shader";

if (type == GL_GEOMETRY_SHADER)

return "Geometry Shader";

if (type == GL_FRAGMENT_SHADER)

return "Fragment Shader";

if (type == GL_COMPUTE_SHADER)

return "Compute Shader";

}

return "invalid shader";

}

bool CompileShader(unsigned int shader, const std::string& sourcecode)

{

if (!glIsShader(shader))

{

std::cout << "ERROR: shader compilation failed, no valid shader specified" << std::endl;

return false;

}

if (sourcecode.empty())

{

std::cout << "ERROR: shader compilation failed, no source code specified (" << ShaderTypeName(shader) << ")" << std::endl;

return false;

}

// array of source code components

const char* sourcearray[] = { sourcecode.c_str() };

// set source code

glShaderSource(shader, 1, sourcearray, NULL);

// compile shaders

glCompileShader(shader);

// check compile status

int status;

glGetShaderiv(shader, GL_COMPILE_STATUS, &status);

// successfully compiled shader

if (status == GL_TRUE)

return true;

// show compile errors

std::cout << "ERROR: shader compilation failed (" << ShaderTypeName(shader) << ")" << std::endl << ShaderInfoLog(shader) << std::endl;

return false;

}

std::string ShaderInfoLog(unsigned int shader)

{

if (glIsShader(shader))

{

int logsize;

char infolog[1024] = { 0 };

glGetShaderInfoLog(shader, 1024, &logsize, infolog);

return std::string(infolog);

}

return "invalid shader";

}

bool LinkProgram(unsigned int program, const std::list<unsigned int>& shaderlist)

{

if (!glIsProgram(program))

{

std::cout << "ERROR: shader linking failed, no valid program specified" << std::endl;

return false;

}

// attach all shaders to the program

for (auto& shader : shaderlist)

{

if (glIsShader(shader))

glAttachShader(program, shader);

}

// link program

glLinkProgram(program);

// detach all shaders again

for (auto& shader : shaderlist)

{

if (glIsShader(shader))

glDetachShader(program, shader);

}

int status;

glGetProgramiv(program, GL_LINK_STATUS, &status);

// successfully linked program

if (status == GL_TRUE)

return true;

// show link errors

std::cout << "ERROR: shader linking failed" << std::endl << ProgramInfoLog(program) << std::endl;

return false;

}

std::string ProgramInfoLog(unsigned int program)

{

if (glIsProgram(program))

{

int logsize;

char infolog[1024] = { 0 };

glGetProgramInfoLog(program, 1024, &logsize, infolog);

return std::string(infolog);

}

return "invalid program";

}

BMP.h

This bmp file loader was written by Benjamin Kalytta, http://www.kalytta.com/bitmap.h

/*

* Windows Bitmap File Loader

* Version 1.2.5 (20120929)

*

* Supported Formats: 1, 4, 8, 16, 24, 32 Bit Images

* Alpha Bitmaps are also supported.

* Supported compression types: RLE 8, BITFIELDS

*

* Created by: Benjamin Kalytta, 2006 - 2012

* Thanks for bug fixes goes to: Chris Campbell

*

* Licence: Free to use, URL to my source and my name is required in your source code.

*

* Source can be found at http://www.kalytta.com/bitmap.h

*

* Warning: This code should not be used in unmodified form in a production environment.

* It should only serve as a basis for your own development.

* There is only a minimal error handling in this code. (Notice added 20111211)

*/

#ifndef BITMAP_H

#define BITMAP_H

#include <iostream>

#include <fstream>

#include <string>

#ifndef __LITTLE_ENDIAN__

#ifndef __BIG_ENDIAN__

#define __LITTLE_ENDIAN__

#endif

#endif

#ifdef __LITTLE_ENDIAN__

#define BITMAP_SIGNATURE 0x4d42

#else

#define BITMAP_SIGNATURE 0x424d

#endif

#if defined(_MSC_VER) || defined(__INTEL_COMPILER)

typedef unsigned __int32 uint32_t;

typedef unsigned __int16 uint16_t;

typedef unsigned __int8 uint8_t;

typedef __int32 int32_t;

#elif defined(__GNUC__) || defined(__CYGWIN__) || defined(__MWERKS__) || defined(__WATCOMC__) || defined(__PGI) || defined(__LCC__)

#include <stdint.h>

#else

typedef unsigned int uint32_t;

typedef unsigned short int uint16_t;

typedef unsigned char uint8_t;

typedef int int32_t;

#endif

#pragma pack(push, 1)

typedef struct _BITMAP_FILEHEADER {

uint16_t Signature;

uint32_t Size;

uint32_t Reserved;

uint32_t BitsOffset;

} BITMAP_FILEHEADER;

#define BITMAP_FILEHEADER_SIZE 14

typedef struct _BITMAP_HEADER {

uint32_t HeaderSize;

int32_t Width;

int32_t Height;

uint16_t Planes;

uint16_t BitCount;

uint32_t Compression;

uint32_t SizeImage;

int32_t PelsPerMeterX;

int32_t PelsPerMeterY;

uint32_t ClrUsed;

uint32_t ClrImportant;

uint32_t RedMask;

uint32_t GreenMask;

uint32_t BlueMask;

uint32_t AlphaMask;

uint32_t CsType;

uint32_t Endpoints[9]; // see http://msdn2.microsoft.com/en-us/library/ms536569.aspx

uint32_t GammaRed;

uint32_t GammaGreen;

uint32_t GammaBlue;

} BITMAP_HEADER;

typedef struct _RGBA {

uint8_t Red;

uint8_t Green;

uint8_t Blue;

uint8_t Alpha;

} RGBA;

typedef struct _BGRA {

uint8_t Blue;

uint8_t Green;

uint8_t Red;

uint8_t Alpha;

} BGRA;

#pragma pack(pop)

class CBitmap {

private:

BITMAP_FILEHEADER m_BitmapFileHeader;

BITMAP_HEADER m_BitmapHeader;

RGBA *m_BitmapData;

unsigned int m_BitmapSize;

// Masks and bit counts shouldn't exceed 32 Bits

public:

class CColor {

public:

static inline unsigned int BitCountByMask(unsigned int Mask) {

unsigned int BitCount = 0;

while (Mask) {

Mask &= Mask - 1;

BitCount++;

}

return BitCount;

}

static inline unsigned int BitPositionByMask(unsigned int Mask) {

return BitCountByMask((Mask & (~Mask + 1)) - 1);

}

static inline unsigned int ComponentByMask(unsigned int Color, unsigned int Mask) {

unsigned int Component = Color & Mask;

return Component >> BitPositionByMask(Mask);

}

static inline unsigned int BitCountToMask(unsigned int BitCount) {

return (BitCount == 32) ? 0xFFFFFFFF : (1 << BitCount) - 1;

}

static unsigned int Convert(unsigned int Color, unsigned int FromBitCount, unsigned int ToBitCount) {

if (ToBitCount < FromBitCount) {

Color >>= (FromBitCount - ToBitCount);

}

else {

Color <<= (ToBitCount - FromBitCount);

if (Color > 0) {

Color |= BitCountToMask(ToBitCount - FromBitCount);

}

}

return Color;

}

};

public:

CBitmap() : m_BitmapData(0), m_BitmapSize(0) {

Dispose();

}

CBitmap(const char* Filename) : m_BitmapData(0), m_BitmapSize(0) {

Load(Filename);

}

~CBitmap() {

Dispose();

}

void Dispose() {

if (m_BitmapData) {

delete[] m_BitmapData;

m_BitmapData = 0;

}

memset(&m_BitmapFileHeader, 0, sizeof(m_BitmapFileHeader));

memset(&m_BitmapHeader, 0, sizeof(m_BitmapHeader));

}

/* Load specified Bitmap and stores it as RGBA in an internal buffer */

bool Load(const char *Filename) {

std::ifstream file(Filename, std::ios::binary | std::ios::in);

if (file.bad()) {

return false;

}

if (file.is_open() == false) {

return false;

}

Dispose();

file.read((char*)&m_BitmapFileHeader, BITMAP_FILEHEADER_SIZE);

if (m_BitmapFileHeader.Signature != BITMAP_SIGNATURE) {

return false;

}

file.read((char*)&m_BitmapHeader, sizeof(BITMAP_HEADER));

/* Load Color Table */

file.seekg(BITMAP_FILEHEADER_SIZE + m_BitmapHeader.HeaderSize, std::ios::beg);

unsigned int ColorTableSize = 0;

if (m_BitmapHeader.BitCount == 1) {

ColorTableSize = 2;

}

else if (m_BitmapHeader.BitCount == 4) {

ColorTableSize = 16;

}

else if (m_BitmapHeader.BitCount == 8) {

ColorTableSize = 256;

}

// Always allocate full sized color table

BGRA* ColorTable = new BGRA[ColorTableSize]; // std::bad_alloc exception should be thrown if memory is not available

file.read((char*)ColorTable, sizeof(BGRA) * m_BitmapHeader.ClrUsed);

/* ... Color Table for 16 bits images are not supported yet */

m_BitmapSize = GetWidth() * GetHeight();

m_BitmapData = new RGBA[m_BitmapSize];

unsigned int LineWidth = ((GetWidth() * GetBitCount() / 8) + 3) & ~3;

uint8_t *Line = new uint8_t[LineWidth];

file.seekg(m_BitmapFileHeader.BitsOffset, std::ios::beg);

int Index = 0;

bool Result = true;

if (m_BitmapHeader.Compression == 0) {

for (unsigned int i = 0; i < GetHeight(); i++) {

file.read((char*)Line, LineWidth);

uint8_t *LinePtr = Line;

for (unsigned int j = 0; j < GetWidth(); j++) {

if (m_BitmapHeader.BitCount == 1) {

uint32_t Color = *((uint8_t*)LinePtr);

for (int k = 0; k < 8; k++) {

m_BitmapData[Index].Red = ColorTable[Color & 0x80 ? 1 : 0].Red;

m_BitmapData[Index].Green = ColorTable[Color & 0x80 ? 1 : 0].Green;

m_BitmapData[Index].Blue = ColorTable[Color & 0x80 ? 1 : 0].Blue;

m_BitmapData[Index].Alpha = ColorTable[Color & 0x80 ? 1 : 0].Alpha;

Index++;

Color <<= 1;

}

LinePtr++;

j += 7;

}

else if (m_BitmapHeader.BitCount == 4) {

uint32_t Color = *((uint8_t*)LinePtr);

m_BitmapData[Index].Red = ColorTable[(Color >> 4) & 0x0f].Red;

m_BitmapData[Index].Green = ColorTable[(Color >> 4) & 0x0f].Green;

m_BitmapData[Index].Blue = ColorTable[(Color >> 4) & 0x0f].Blue;

m_BitmapData[Index].Alpha = ColorTable[(Color >> 4) & 0x0f].Alpha;

Index++;

m_BitmapData[Index].Red = ColorTable[Color & 0x0f].Red;

m_BitmapData[Index].Green = ColorTable[Color & 0x0f].Green;

m_BitmapData[Index].Blue = ColorTable[Color & 0x0f].Blue;

m_BitmapData[Index].Alpha = ColorTable[Color & 0x0f].Alpha;

Index++;

LinePtr++;

j++;

}

else if (m_BitmapHeader.BitCount == 8) {

uint32_t Color = *((uint8_t*)LinePtr);

m_BitmapData[Index].Red = ColorTable[Color].Red;

m_BitmapData[Index].Green = ColorTable[Color].Green;

m_BitmapData[Index].Blue = ColorTable[Color].Blue;

m_BitmapData[Index].Alpha = ColorTable[Color].Alpha;

Index++;

LinePtr++;

}

else if (m_BitmapHeader.BitCount == 16) {

uint32_t Color = *((uint16_t*)LinePtr);

m_BitmapData[Index].Red = ((Color >> 10) & 0x1f) << 3;

m_BitmapData[Index].Green = ((Color >> 5) & 0x1f) << 3;

m_BitmapData[Index].Blue = (Color & 0x1f) << 3;

m_BitmapData[Index].Alpha = 255;

Index++;

LinePtr += 2;

}

else if (m_BitmapHeader.BitCount == 24) {

uint32_t Color = *((uint32_t*)LinePtr);

m_BitmapData[Index].Blue = Color & 0xff;

m_BitmapData[Index].Green = (Color >> 8) & 0xff;

m_BitmapData[Index].Red = (Color >> 16) & 0xff;

m_BitmapData[Index].Alpha = 255;

Index++;

LinePtr += 3;

}

else if (m_BitmapHeader.BitCount == 32) {

uint32_t Color = *((uint32_t*)LinePtr);

m_BitmapData[Index].Blue = Color & 0xff;

m_BitmapData[Index].Green = (Color >> 8) & 0xff;

m_BitmapData[Index].Red = (Color >> 16) & 0xff;

m_BitmapData[Index].Alpha = Color >> 24;

Index++;

LinePtr += 4;

}

}

}

}

else if (m_BitmapHeader.Compression == 1) { // RLE 8

uint8_t Count = 0;

uint8_t ColorIndex = 0;

int x = 0, y = 0;

while (file.eof() == false) {

file.read((char*)&Count, sizeof(uint8_t));

file.read((char*)&ColorIndex, sizeof(uint8_t));

if (Count > 0) {

Index = x + y * GetWidth();

for (int k = 0; k < Count; k++) {

m_BitmapData[Index + k].Red = ColorTable[ColorIndex].Red;

m_BitmapData[Index + k].Green = ColorTable[ColorIndex].Green;

m_BitmapData[Index + k].Blue = ColorTable[ColorIndex].Blue;

m_BitmapData[Index + k].Alpha = ColorTable[ColorIndex].Alpha;

}

x += Count;

}

else if (Count == 0) {

int Flag = ColorIndex;

if (Flag == 0) {

x = 0;

y++;

}

else if (Flag == 1) {

break;

}

else if (Flag == 2) {

char rx = 0;

char ry = 0;

file.read((char*)&rx, sizeof(char));

file.read((char*)&ry, sizeof(char));

x += rx;

y += ry;

}

else {

Count = Flag;

Index = x + y * GetWidth();

for (int k = 0; k < Count; k++) {

file.read((char*)&ColorIndex, sizeof(uint8_t));

m_BitmapData[Index + k].Red = ColorTable[ColorIndex].Red;

m_BitmapData[Index + k].Green = ColorTable[ColorIndex].Green;

m_BitmapData[Index + k].Blue = ColorTable[ColorIndex].Blue;

m_BitmapData[Index + k].Alpha = ColorTable[ColorIndex].Alpha;

}

x += Count;

// Attention: Current Microsoft STL implementation seems to be buggy, tellg() always returns 0.

if (file.tellg() & 1) {

file.seekg(1, std::ios::cur);

}

}

}

}

}

else if (m_BitmapHeader.Compression == 2) { // RLE 4

/* RLE 4 is not supported */

Result = false;

}

else if (m_BitmapHeader.Compression == 3) { // BITFIELDS

/* We assumes that mask of each color component can be in any order */

uint32_t BitCountRed = CColor::BitCountByMask(m_BitmapHeader.RedMask);

uint32_t BitCountGreen = CColor::BitCountByMask(m_BitmapHeader.GreenMask);

uint32_t BitCountBlue = CColor::BitCountByMask(m_BitmapHeader.BlueMask);

uint32_t BitCountAlpha = CColor::BitCountByMask(m_BitmapHeader.AlphaMask);

for (unsigned int i = 0; i < GetHeight(); i++) {

file.read((char*)Line, LineWidth);

uint8_t *LinePtr = Line;

for (unsigned int j = 0; j < GetWidth(); j++) {

uint32_t Color = 0;

if (m_BitmapHeader.BitCount == 16) {

Color = *((uint16_t*)LinePtr);

LinePtr += 2;

}

else if (m_BitmapHeader.BitCount == 32) {

Color = *((uint32_t*)LinePtr);

LinePtr += 4;

}

else {

// Other formats are not valid

}

m_BitmapData[Index].Red = CColor::Convert(CColor::ComponentByMask(Color, m_BitmapHeader.RedMask), BitCountRed, 8);

m_BitmapData[Index].Green = CColor::Convert(CColor::ComponentByMask(Color, m_BitmapHeader.GreenMask), BitCountGreen, 8);

m_BitmapData[Index].Blue = CColor::Convert(CColor::ComponentByMask(Color, m_BitmapHeader.BlueMask), BitCountBlue, 8);

m_BitmapData[Index].Alpha = CColor::Convert(CColor::ComponentByMask(Color, m_BitmapHeader.AlphaMask), BitCountAlpha, 8);

Index++;

}

}

}

delete[] ColorTable;

delete[] Line;

file.close();

return Result;

}

bool Save(const char* Filename, unsigned int BitCount = 32) {

bool Result = true;

std::ofstream file(Filename, std::ios::out | std::ios::binary);

if (file.is_open() == false) {

return false;

}

BITMAP_FILEHEADER bfh;

BITMAP_HEADER bh;

memset(&bfh, 0, sizeof(bfh));

memset(&bh, 0, sizeof(bh));

bfh.Signature = BITMAP_SIGNATURE;

bfh.BitsOffset = BITMAP_FILEHEADER_SIZE + sizeof(BITMAP_HEADER);

bfh.Size = (GetWidth() * GetHeight() * BitCount) / 8 + bfh.BitsOffset;

bh.HeaderSize = sizeof(BITMAP_HEADER);

bh.BitCount = BitCount;

if (BitCount == 32) {

bh.Compression = 3; // BITFIELD

bh.AlphaMask = 0xff000000;

bh.BlueMask = 0x00ff0000;

bh.GreenMask = 0x0000ff00;

bh.RedMask = 0x000000ff;

}

else if (BitCount == 16) {

bh.Compression = 3; // BITFIELD

bh.AlphaMask = 0x00000000;

bh.BlueMask = 0x0000001f;

bh.GreenMask = 0x000007E0;

bh.RedMask = 0x0000F800;

}

else {

bh.Compression = 0; // RGB

}

unsigned int LineWidth = (GetWidth() + 3) & ~3;

bh.Planes = 1;

bh.Height = GetHeight();

bh.Width = GetWidth();

bh.SizeImage = (LineWidth * BitCount * GetHeight()) / 8;

bh.PelsPerMeterX = 3780;

bh.PelsPerMeterY = 3780;

if (BitCount == 32) {

file.write((char*)&bfh, sizeof(BITMAP_FILEHEADER));

file.write((char*)&bh, sizeof(BITMAP_HEADER));

file.write((char*)m_BitmapData, bh.SizeImage);

}

else if (BitCount < 16) {

uint8_t* Bitmap = new uint8_t[bh.SizeImage];

BGRA *Palette = 0;

unsigned int PaletteSize = 0;

if (GetBitsWithPalette(Bitmap, bh.SizeImage, BitCount, Palette, PaletteSize)) {

bfh.BitsOffset += PaletteSize * sizeof(BGRA);

file.write((char*)&bfh, BITMAP_FILEHEADER_SIZE);

file.write((char*)&bh, sizeof(BITMAP_HEADER));

file.write((char*)Palette, PaletteSize * sizeof(BGRA));

file.write((char*)Bitmap, bh.SizeImage);

}

delete[] Bitmap;

delete[] Palette;

}

else {

uint32_t RedMask = 0;

uint32_t GreenMask = 0;

uint32_t BlueMask = 0;

uint32_t AlphaMask = 0;

if (BitCount == 16) {

RedMask = 0x0000F800;

GreenMask = 0x000007E0;

BlueMask = 0x0000001F;

AlphaMask = 0x00000000;

}

else if (BitCount == 24) {

RedMask = 0x00FF0000;

GreenMask = 0x0000FF00;

BlueMask = 0x000000FF;

}

else {

// Other color formats are not valid

Result = false;

}

if (Result) {

if (GetBits(NULL, bh.SizeImage, RedMask, GreenMask, BlueMask, AlphaMask)) {

uint8_t* Bitmap = new uint8_t[bh.SizeImage];

if (GetBits(Bitmap, bh.SizeImage, RedMask, GreenMask, BlueMask, AlphaMask)) {

file.write((char*)&bfh, sizeof(BITMAP_FILEHEADER));

file.write((char*)&bh, sizeof(BITMAP_HEADER));

file.write((char*)Bitmap, bh.SizeImage);

}

delete[] Bitmap;

}

}

}

file.close();

return Result;

}

unsigned int GetWidth() {

/* Add plausibility test */

// if (abs(m_BitmapHeader.Width) > 8192) {

// m_BitmapHeader.Width = 8192;

// }

return m_BitmapHeader.Width < 0 ? -m_BitmapHeader.Width : m_BitmapHeader.Width;

}

unsigned int GetHeight() {

/* Add plausibility test */

// if (abs(m_BitmapHeader.Height) > 8192) {

// m_BitmapHeader.Height = 8192;

// }

return m_BitmapHeader.Height < 0 ? -m_BitmapHeader.Height : m_BitmapHeader.Height;

}

unsigned int GetBitCount() {

/* Add plausibility test */

// if (m_BitmapHeader.BitCount > 32) {

// m_BitmapHeader.BitCount = 32;

// }

return m_BitmapHeader.BitCount;

}

/* Copies internal RGBA buffer to user specified buffer */

bool GetBits(void* Buffer, unsigned int &Size) {

bool Result = false;

if (Size == 0 || Buffer == 0) {

Size = m_BitmapSize * sizeof(RGBA);

Result = m_BitmapSize != 0;

}

else {

memcpy(Buffer, m_BitmapData, Size);

Result = true;

}

return Result;

}

/* Returns internal RGBA buffer */

void* GetBits() {

return m_BitmapData;

}

/* Copies internal RGBA buffer to user specified buffer and converts it into destination

* bit format specified by component masks.

*

* Typical Bitmap color formats (BGR/BGRA):

*

* Masks for 16 bit (5-5-5): ALPHA = 0x00000000, RED = 0x00007C00, GREEN = 0x000003E0, BLUE = 0x0000001F

* Masks for 16 bit (5-6-5): ALPHA = 0x00000000, RED = 0x0000F800, GREEN = 0x000007E0, BLUE = 0x0000001F

* Masks for 24 bit: ALPHA = 0x00000000, RED = 0x00FF0000, GREEN = 0x0000FF00, BLUE = 0x000000FF

* Masks for 32 bit: ALPHA = 0xFF000000, RED = 0x00FF0000, GREEN = 0x0000FF00, BLUE = 0x000000FF

*

* Other color formats (RGB/RGBA):

*

* Masks for 32 bit (RGBA): ALPHA = 0xFF000000, RED = 0x000000FF, GREEN = 0x0000FF00, BLUE = 0x00FF0000

*

* Bit count will be rounded to next 8 bit boundary. If IncludePadding is true, it will be ensured

* that line width is a multiple of 4. padding bytes are included if necessary.

*

* NOTE: systems with big endian byte order may require masks in inversion order.

*/

bool GetBits(void* Buffer, unsigned int &Size, unsigned int RedMask, unsigned int GreenMask, unsigned int BlueMask, unsigned int AlphaMask, bool IncludePadding = true) {

bool Result = false;

uint32_t BitCountRed = CColor::BitCountByMask(RedMask);

uint32_t BitCountGreen = CColor::BitCountByMask(GreenMask);

uint32_t BitCountBlue = CColor::BitCountByMask(BlueMask);

uint32_t BitCountAlpha = CColor::BitCountByMask(AlphaMask);

unsigned int BitCount = (BitCountRed + BitCountGreen + BitCountBlue + BitCountAlpha + 7) & ~7;

if (BitCount > 32) {

return false;

}

unsigned int w = GetWidth();

//unsigned int LineWidth = (w + 3) & ~3;

unsigned int dataBytesPerLine = (w * BitCount + 7) / 8;

unsigned int LineWidth = (dataBytesPerLine + 3) & ~3;

if (Size == 0 || Buffer == 0) {

//Size = (LineWidth * GetHeight() * BitCount) / 8 + sizeof(unsigned int);

Size = (GetWidth() * GetHeight() * BitCount) / 8 + sizeof(unsigned int);

return true;

}

uint8_t* BufferPtr = (uint8_t*)Buffer;

Result = true;

uint32_t BitPosRed = CColor::BitPositionByMask(RedMask);

uint32_t BitPosGreen = CColor::BitPositionByMask(GreenMask);

uint32_t BitPosBlue = CColor::BitPositionByMask(BlueMask);

uint32_t BitPosAlpha = CColor::BitPositionByMask(AlphaMask);

unsigned int j = 0;

for (unsigned int i = 0; i < m_BitmapSize; i++) {

*(uint32_t*)BufferPtr =

(CColor::Convert(m_BitmapData[i].Blue, 8, BitCountBlue) << BitPosBlue) |

(CColor::Convert(m_BitmapData[i].Green, 8, BitCountGreen) << BitPosGreen) |

(CColor::Convert(m_BitmapData[i].Red, 8, BitCountRed) << BitPosRed) |

(CColor::Convert(m_BitmapData[i].Alpha, 8, BitCountAlpha) << BitPosAlpha);

if (IncludePadding) {

j++;

if (j >= w) {

for (unsigned int k = 0; k < LineWidth - dataBytesPerLine; k++) {

BufferPtr += (BitCount >> 3);

}

j = 0;

}

}

BufferPtr += (BitCount >> 3);

}

Size -= sizeof(unsigned int);

return Result;

}

/* See GetBits().

* It creates a corresponding color table (palette) which have to be destroyed by the user after usage.

*

* Supported Bit depths are: 4, 8

*

* Todo: Optimize, use optimized palette, do ditehring (see my dithering class), support padding for 4 bit bitmaps

*/

bool GetBitsWithPalette(void* Buffer, unsigned int &Size, unsigned int BitCount, BGRA* &Palette, unsigned int &PaletteSize, bool OptimalPalette = false, bool IncludePadding = true) {

bool Result = false;

if (BitCount > 16) {

return false;

}

unsigned int w = GetWidth();

unsigned int dataBytesPerLine = (w * BitCount + 7) / 8;

unsigned int LineWidth = (dataBytesPerLine + 3) & ~3;

if (Size == 0 || Buffer == 0) {

Size = (LineWidth * GetHeight() * BitCount) / 8;

return true;

}

if (OptimalPalette) {

PaletteSize = 0;

// Not implemented

}

else {

if (BitCount == 1) {

PaletteSize = 2;

// Not implemented: Who need that?

}

else if (BitCount == 4) { // 2:2:1

PaletteSize = 16;

Palette = new BGRA[PaletteSize];

for (int r = 0; r < 4; r++) {

for (int g = 0; g < 2; g++) {

for (int b = 0; b < 2; b++) {

Palette[r | g << 2 | b << 3].Red = r ? (r << 6) | 0x3f : 0;

Palette[r | g << 2 | b << 3].Green = g ? (g << 7) | 0x7f : 0;

Palette[r | g << 2 | b << 3].Blue = b ? (b << 7) | 0x7f : 0;

Palette[r | g << 2 | b << 3].Alpha = 0xff;

}

}

}

}

else if (BitCount == 8) { // 3:3:2

PaletteSize = 256;

Palette = new BGRA[PaletteSize];

for (int r = 0; r < 8; r++) {

for (int g = 0; g < 8; g++) {

for (int b = 0; b < 4; b++) {

Palette[r | g << 3 | b << 6].Red = r ? (r << 5) | 0x1f : 0;

Palette[r | g << 3 | b << 6].Green = g ? (g << 5) | 0x1f : 0;

Palette[r | g << 3 | b << 6].Blue = b ? (b << 6) | 0x3f : 0;

Palette[r | g << 3 | b << 6].Alpha = 0xff;

}

}

}

}

else if (BitCount == 16) { // 5:5:5

// Not implemented

}

}

unsigned int j = 0;

uint8_t* BufferPtr = (uint8_t*)Buffer;

for (unsigned int i = 0; i < m_BitmapSize; i++) {

if (BitCount == 1) {

// Not implemented: Who needs that?

}

else if (BitCount == 4) {

*BufferPtr = ((m_BitmapData[i].Red >> 6) | (m_BitmapData[i].Green >> 7) << 2 | (m_BitmapData[i].Blue >> 7) << 3) << 4;

i++;

*BufferPtr |= (m_BitmapData[i].Red >> 6) | (m_BitmapData[i].Green >> 7) << 2 | (m_BitmapData[i].Blue >> 7) << 3;

}

else if (BitCount == 8) {

*BufferPtr = (m_BitmapData[i].Red >> 5) | (m_BitmapData[i].Green >> 5) << 3 | (m_BitmapData[i].Blue >> 5) << 6;

}

else if (BitCount == 16) {

// Not implemented

}

if (IncludePadding) {

j++;

if (j >= w) {

for (unsigned int k = 0; k < (LineWidth - dataBytesPerLine); k++) {

BufferPtr += BitCount / 8;

}

j = 0;

}

}

BufferPtr++;

}

Result = true;

return Result;

}

/* Set Bitmap Bits. Will be converted to RGBA internally */

bool SetBits(void* Buffer, unsigned int Width, unsigned int Height, unsigned int RedMask, unsigned int GreenMask, unsigned int BlueMask, unsigned int AlphaMask = 0) {

if (Buffer == 0) {

return false;

}

uint8_t *BufferPtr = (uint8_t*)Buffer;

Dispose();

m_BitmapHeader.Width = Width;

m_BitmapHeader.Height = Height;

m_BitmapHeader.BitCount = 32;

m_BitmapHeader.Compression = 3;

m_BitmapSize = GetWidth() * GetHeight();

m_BitmapData = new RGBA[m_BitmapSize];

/* Find bit count by masks (rounded to next 8 bit boundary) */

unsigned int BitCount = (CColor::BitCountByMask(RedMask | GreenMask | BlueMask | AlphaMask) + 7) & ~7;

uint32_t BitCountRed = CColor::BitCountByMask(RedMask);

uint32_t BitCountGreen = CColor::BitCountByMask(GreenMask);

uint32_t BitCountBlue = CColor::BitCountByMask(BlueMask);

uint32_t BitCountAlpha = CColor::BitCountByMask(AlphaMask);

for (unsigned int i = 0; i < m_BitmapSize; i++) {

unsigned int Color = 0;

if (BitCount <= 8) {

Color = *((uint8_t*)BufferPtr);

BufferPtr += 1;

}

else if (BitCount <= 16) {

Color = *((uint16_t*)BufferPtr);

BufferPtr += 2;

}

else if (BitCount <= 24) {

Color = *((uint32_t*)BufferPtr);

BufferPtr += 3;

}

else if (BitCount <= 32) {

Color = *((uint32_t*)BufferPtr);

BufferPtr += 4;

}

else {

/* unsupported */

BufferPtr += 1;

}

m_BitmapData[i].Alpha = CColor::Convert(CColor::ComponentByMask(Color, AlphaMask), BitCountAlpha, 8);

m_BitmapData[i].Red = CColor::Convert(CColor::ComponentByMask(Color, RedMask), BitCountRed, 8);

m_BitmapData[i].Green = CColor::Convert(CColor::ComponentByMask(Color, GreenMask), BitCountGreen, 8);

m_BitmapData[i].Blue = CColor::Convert(CColor::ComponentByMask(Color, BlueMask), BitCountBlue, 8);

}

return true;

}

};

#endif