MegaGlest/source/shared_lib/sources/graphics/BMPReader.cpp

199 lines
6.3 KiB
C++

// ==============================================================
// This file is part of Glest Shared Library (www.glest.org)
//
// Copyright (C) 2001-2010 Martiño Figueroa and others
//
// You can redistribute this code and/or modify it under
// the terms of the GNU General Public License as published
// by the Free Software Foundation; either version 2 of the
// License, or (at your option) any later version
// ==============================================================
#include "BMPReader.h"
#include "data_types.h"
#include "pixmap.h"
#include <stdexcept>
#include "util.h"
#include "leak_dumper.h"
using std::runtime_error;
namespace Shared{ namespace Graphics{
// =====================================================
// Structs used for BMP-reading
// =====================================================
#pragma pack(push, 1)
struct BitmapFileHeader{
uint8 type1;
uint8 type2;
uint32 size;
uint16 reserved1;
uint16 reserved2;
uint32 offsetBits;
};
struct BitmapInfoHeader{
uint32 size;
int32 width;
int32 height;
uint16 planes;
uint16 bitCount;
uint32 compression;
uint32 sizeImage;
int32 xPelsPerMeter;
int32 yPelsPerMeter;
uint32 clrUsed;
uint32 clrImportant;
};
#pragma pack(pop)
/**Returns a string containing the extensions we want, intitialisation is guaranteed*/
//static inline const string* getExtensionsBmp() {
static inline std::vector<string> getExtensionsBmp() {
//static const string extensions[] = {"bmp", ""};
static std::vector<string> extensions;
if(extensions.empty() == true) {
extensions.push_back("bmp");
}
return extensions;
}
// =====================================================
// class BMPReader
// =====================================================
BMPReader::BMPReader(): FileReader<Pixmap2D>(getExtensionsBmp()) {}
/**Reads a Pixmap2D-object
*This function reads a Pixmap2D-object from the given ifstream utilising the already existing Pixmap2D* ret.
*Path is used for printing error messages
*@return <code>NULL</code> if the Pixmap2D could not be read, else the pixmap*/
Pixmap2D* BMPReader::read(ifstream& in, const string& path, Pixmap2D* ret) const {
if(GlobalStaticFlags::getIsNonGraphicalModeEnabled() == true) {
throw megaglest_runtime_error("Loading graphics in headless server mode not allowed!");
}
//read file header
BitmapFileHeader fileHeader;
in.read((char*)&fileHeader, sizeof(BitmapFileHeader));
static bool bigEndianSystem = Shared::PlatformByteOrder::isBigEndian();
if(bigEndianSystem == true) {
fileHeader.offsetBits = Shared::PlatformByteOrder::fromCommonEndian(fileHeader.offsetBits);
fileHeader.reserved1 = Shared::PlatformByteOrder::fromCommonEndian(fileHeader.reserved1);
fileHeader.reserved2 = Shared::PlatformByteOrder::fromCommonEndian(fileHeader.reserved2);
fileHeader.size = Shared::PlatformByteOrder::fromCommonEndian(fileHeader.size);
fileHeader.type1 = Shared::PlatformByteOrder::fromCommonEndian(fileHeader.type1);
fileHeader.type2 = Shared::PlatformByteOrder::fromCommonEndian(fileHeader.type2);
}
if(fileHeader.type1!='B' || fileHeader.type2!='M'){
throw megaglest_runtime_error(path +" is not a bitmap",true);
}
//read info header
BitmapInfoHeader infoHeader;
in.read((char*)&infoHeader, sizeof(BitmapInfoHeader));
if(bigEndianSystem == true) {
infoHeader.bitCount = Shared::PlatformByteOrder::fromCommonEndian(infoHeader.bitCount);
infoHeader.clrImportant = Shared::PlatformByteOrder::fromCommonEndian(infoHeader.clrImportant);
infoHeader.clrUsed = Shared::PlatformByteOrder::fromCommonEndian(infoHeader.clrUsed);
infoHeader.compression = Shared::PlatformByteOrder::fromCommonEndian(infoHeader.compression);
infoHeader.height = Shared::PlatformByteOrder::fromCommonEndian(infoHeader.height);
infoHeader.planes = Shared::PlatformByteOrder::fromCommonEndian(infoHeader.planes);
infoHeader.size = Shared::PlatformByteOrder::fromCommonEndian(infoHeader.size);
infoHeader.sizeImage = Shared::PlatformByteOrder::fromCommonEndian(infoHeader.sizeImage);
infoHeader.width = Shared::PlatformByteOrder::fromCommonEndian(infoHeader.width);
infoHeader.xPelsPerMeter = Shared::PlatformByteOrder::fromCommonEndian(infoHeader.xPelsPerMeter);
infoHeader.yPelsPerMeter = Shared::PlatformByteOrder::fromCommonEndian(infoHeader.yPelsPerMeter);
}
if(infoHeader.bitCount!=24){
throw megaglest_runtime_error(path+" is not a 24 bit bitmap",true);
}
int h= infoHeader.height;
int w= infoHeader.width;
int components= (ret->getComponents() == -1)?3:ret->getComponents();
const int fileComponents = 3;
//std::cout << "BMP-Components: Pic: " << components << " old: " << (ret->getComponents()) << " File: " << 3 << std::endl;
ret->init(w,h,components);
uint8* pixels = ret->getPixels();
char buffer[4];
//BMP is padded to sizes of 4
const int padSize = (4 - (w*fileComponents)%4)%4; //Yeah, could be faster
//std::cout << "Padsize = " << padSize;
for (int y = 0, i = 0; y < h; ++y) {
for (int x = 0; x < w; ++x, i+=components) {
uint8 r, g, b;
// in.read((char*)&b, 1);
// if(bigEndianSystem == true) {
// b = Shared::PlatformByteOrder::fromCommonEndian(b);
// }
//
// in.read((char*)&g, 1);
// if(bigEndianSystem == true) {
// g = Shared::PlatformByteOrder::fromCommonEndian(g);
// }
//
// in.read((char*)&r, 1);
// if(bigEndianSystem == true) {
// r = Shared::PlatformByteOrder::fromCommonEndian(r);
// }
uint8 bgr[3] = {0,0,0};
in.read((char*)&bgr[0], 3);
b = bgr[0];
g = bgr[1];
r = bgr[2];
if(bigEndianSystem == true) {
b = Shared::PlatformByteOrder::fromCommonEndian(b);
g = Shared::PlatformByteOrder::fromCommonEndian(g);
r = Shared::PlatformByteOrder::fromCommonEndian(r);
}
if (!in.good()) {
return NULL;
}
switch(components){
case 1:
pixels[i]= (r+g+b)/3;
break;
case 3:
pixels[i]= r;
pixels[i+1]= g;
pixels[i+2]= b;
break;
case 4:
pixels[i]= r;
pixels[i+1]= g;
pixels[i+2]= b;
pixels[i+3]= 255;
break;
}
}
if (padSize) {
in.read(buffer,padSize);
}
}
/*for(int i = 0; i < w*h*components; ++i) {
if (i%39 == 0) std::cout << std::endl;
int first = pixels[i]/16;
if (first < 10)
std:: cout << first;
else
std::cout << (char)('A'+(first-10));
first = pixels[i]%16;
if (first < 10)
std:: cout << first;
else
std::cout << (char)('A'+(first-10));
std::cout << " ";
}*/
return ret;
}
}} //end namespace