Прежде всего позвольте мне заметить, что вы делаете вещи лучше, чем худший метод (чтение в упакованную структуру, которая есть). Для надежного анализатора файлов всегда читайте файл по частям. Это единственный способ избежать ошибок и эксплойтов.
К сожалению, есть еще несколько проблем, но это легко исправить:
struct TGA
{
GLuint Load(const char* filename);
unsigned char header_[12];
unsigned char bpp_;
unsigned char id_;
unsigned short width_;
unsigned short height_;
ширина и высота быть коротким, может стать проблемой. Дьявол в деталях, но я объясню вовремя. На данный момент мы должны уважать то, что на некоторых архитектурах short
может быть на самом деле слишком коротким для нас. Чтобы быть в безопасности, используйте uint16_t
от stdint.h
(часть стандарта C99) или еще лучше uint32_t
. Посмотрим, почему.
unsigned char* data_;
};
GLuint TGA::Load(const char* filename)
{
width_ = 0; height_ = 0;
bpp_ = 32; id_ = 8;
data_ = 0;
Мы все равно перепишем их, поэтому не нужно их очищать, но и не повредить.
std::ifstream file;
file.open(filename, std::ios::binary | std::ios::ate);
if(file.fail())
{
std::cout<<filename<<" could not be opened."<<std::endl;
return 0;
}
file.seekg(0, std::ios::beg);
file.read((char*)header_, sizeof(unsigned char)*12);
file.read((char*)&width_, sizeof(unsigned short));
file.read((char*)&height_, sizeof(unsigned short));
Хорошо, это немного проблематично, потому что они предполагают, что программа будет работать на малоэтажной машине. Скажите, что вы должны были разрабатывать PowerPC (подумайте, что PlayStation 3, XBox или ARM работают в режиме большого эндиана) этот код сломается.Решение:
char buf[2]; file.read(buf, 2); width = buf[0] | buf[1]<<8;
и то же для высоты. Этот код по-прежнему возникает при запуске на машине с размером символа, отличным от 8, но в последнее время они находятся между далекими и немногими. Если вы хотите быть в безопасности, добавьте #if CHAR_BITS!=8 #error "char bits not 8" #endif
где-нибудь.
file.read((char*)&bpp_, sizeof(unsigned char));
file.read((char*)&id_, sizeof(unsigned char));
Теперь следующая строка является неправильным:
int imageSize = width_ * height_; // <<<<<<<<<
С width_
и height_
оба типа short
умножение будет coerece к ширине коротка. Это особое поведение C и C++ является одним из самых больших источников неприятных, уязвимых ошибок. Скажем, я дам вам картинку, в которой заголовок был объявлен width = height 2^15-1
, номер, подходящий для ожидаемых 16 бит. Если вы умножаете эти два и принуждаете их к короткому, вы получаете ... 1. И тогда есть еще одна проблема.
std::cout<<imageSize<<std::endl;
data_ = new unsigned char[imageSize * 3]; //3 means RGB
file.read((char*)data_, imageSize * 3);
Хорошая вещь, что вы читаете только количество байтов, выделенных с помощью переменной прокси, так что я не могу вводить код в вашу программу. Однако я могу свернуть его, потому что позже ваш вызов glTexImage2D попытается прочитать (2^15 -1)^2 байта из нераспределенного хранилища и, следовательно, вызвать segfault. Хитрость, чтобы избежать этого, - это превратить отдельные переменные в тип, достаточно большой в вычислении. Или вы делаете так, как я предложил, и используйте для этого значение также значение uint32_t
. Я кодирую по правилу, что каждая переменная должна содержать как минимум два бита, как требуется для наибольшего разрешенного значения в нем. К сожалению, стандарт C не может быть «исправлен» для принуждения к размеру L-типа инструкции, поскольку это нарушит много существующего кода.
Другая проблема, с которой вы сталкиваетесь, заключается в том, что вы запрограммировали 3 канала. Зачем? У вас есть bpp-заголовок, который сообщает вам формат. Итак, давайте изменим это: uint32_t imageSize = (uint_32)width_ * height_ * bpp_/8;
. Обратите внимание, что необходимо приложить только одну переменную выражения к большему типу. Однако не делайте эту ошибку: (uint32_t)(width_ * height_ * bpp_/8)
, которая выдаст короткий типизированный результат uint32_t - умьте круглые скобки.
Последнее, но не менее важное, мы можем передать это glTexImage2D или gluBuildMipMap2D. Я настоятельно рекомендую не использовать gluBuildMipmap, потому что он преобразует вашу текстуру в параметры мощности-2, которые больше не нужны с OpenGL-2. Вместо этого вызывается glGenerateMipmap
Параметром формата должно быть не количество каналов, а маркер GL_RED, GL_RG, GL_RGB, GL_RGBA. Поэтому используйте инструкцию для малого коммутатора для этого:
GLenum internal_format;
GLenum format;
GLenum buffer_format;
switch(bpp_) {
case 8:
internal_format = GL_R8;
format = GL_RED;
buffer_format = GL_UNSIGNED_BYTE;
break;
case 16:
internal_format = GL_RGB5;
format = GL_RGB;
buffer_format = GL_UNSIGNED_SHORT_5_6_5;
break;
case 24:
internal_format = GL_RGB8;
format = RGB;
buffer_format = GL_UNSIGNED_BYTE;
break;
case 32:
internal_format = GL_RGBA8;
format = RGB;
buffer_format = GL_UNSIGNED_BYTE;
break;
default:
format_unsupported_error(); return;
}
glTexImage2D(..., internal_format, ..., internal_format, format, ...);
Не ответ .. но .. убедитесь, что простыни и прочитайте все сразу! И не забудьте endianess, если вы когда-нибудь захотите, чтобы она была переносимой. –
Что такое 'bpp_' после прочтения заголовка файла? (Влад: Я считаю, что TGA не накладывается, как BMP.) – user7116
bpp должен быть бит на пиксель или глубину пикселя. Это 24 изображения RGB. Я попытался проверить добавление/выравнивание различных битов, но, похоже, все в порядке. Я не уверен, как анализировать данные после прочтения его как одного большого фрагмента. – Sullivan