Очень небольшая проблема с моим Targa Reader. Один пиксель отключен, а 2 дополнительных пикселя черные: S
24-битный файл targa, который я загружаю, выглядит следующим образом:
Обратите внимание на белый пиксель в самом верхнем левом углу. Самый первый пиксель вверху слева — белый.
Но когда я сохраняю пиксели файла обратно как 24-битное растровое изображение, это выглядит так:
Обратите внимание, что белый пиксель находится сверху справа, а также 2 черных пикселя над ним.
Я не могу понять, почему все остальное правильно, за исключением этого … Это беспокоит меня, потому что я так близок к возможности загружать TGA.
Любая идея, что не так с моим кодом ниже? Я пишу TGA Reader для 16, 24 и 32-битных файлов TGA, созданных с помощью Photoshop.
typedef union RGB //Struct which will hold all pixels.
{
uint32_t Color;
struct
{
unsigned char B, G, R, A;
} RGBA;
} *PRGB;class Tga //My Targa class that will load the TGA file.
{
private:
std::vector<RGB> Pixels;
uint32_t width, height, size, BitsPerPixel;
public:
Tga(const char* FilePath);
};Tga::Tga(const char* FilePath)
{
//Open the file for reading..
std::fstream hFile(FilePath, std::ios::in | std::ios::binary);
if (!hFile.is_open()){throw std::invalid_argument("File Not Found.");}
//The header is 12 bytes. By reading it, I can tell if the TGA is compressed or not.
byte Header[12] = {0};
byte DeCompressed[12] = {0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0};
byte IsCompressed[12] = {0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0};hFile.read(reinterpret_cast<char*>(&Header), sizeof(Header)); //Read the header.
if (memcmp(DeCompressed, &Header, sizeof(Header)) == 0) //If it is not compressed.
{
hFile.read(reinterpret_cast<char*>(&Header), sizeof(Header));
BitsPerPixel = Header[4]; //Displays correctly.
width = Header[1] * 256 + Header[0]; //Displays correctly.
height = Header[3] * 256 + Header[2]; //Displays correctly.
size = ((width * BitsPerPixel + 31) / 32) * 4 * height; //Same algorithm for bitmaps. width * height * 3 OR width * height * 4. Taken from MSDN.
if ((BitsPerPixel != 24) && (BitsPerPixel != 32) && ((width < 1) || (height < 1)))
{
hFile.close();
throw std::logic_error("Error: Invalid TGA File. Width And Height cannot be less than 0. BitsPerPixel must be 24 or 32 bits.");
}
std::vector<unsigned char> ImageData(size); //An array for holding the image data.
hFile.read(reinterpret_cast<char*>(ImageData.data()), size);
unsigned char* BuffPos = ImageData.data();
Pixels.resize(width * height); //A vector holding my structs that will hold the pxiels.//The following is supposed to flip the Image and copy it into my struct vector.
for (int I = 0; I < height; I++)
{
for (int J = 0; J < width; J++)
{
Pixels[(height - 1 - I) * width + J].RGBA.B = *(BuffPos++);
Pixels[(height - 1 - I) * width + J].RGBA.G = *(BuffPos++);
Pixels[(height - 1 - I) * width + J].RGBA.R = *(BuffPos++);
Pixels[(height - 1 - I) * width + J].RGBA.A = (BitsPerPixel > 24 ? *(BuffPos++) : 0xFF);
}
if(BitsPerPixel == 24) //Has padding?
BuffPos += (4 - ((width * 3) % 4)) % 4;
}
}
else if (memcmp(IsCompressed, &Header, sizeof(Header)) == 0) //The TGA is compressed..
{
hFile.read(reinterpret_cast<char*>(&Header), sizeof(Header));
BitsPerPixel = Header[4];
height = Header[1] * 256 + Header[0];
width = Header[3] * 256 + Header[2];
size = ((width * BitsPerPixel + 31) / 32) * 4 * height;
if ((BitsPerPixel != 24) && (BitsPerPixel != 32) && ((width < 1) || (height < 1)))
{
hFile.close();
throw std::logic_error("Error: Invalid TGA File. Width And Height cannot be less than 0. BitsPerPixel must be 24 or 32 bits.");
}
RGB Pixel = {0};
char* BuffPos = reinterpret_cast<char*>(&Pixel);
int CurrentByte = 0, CurrentPixel = 0;
int BytesPerPixel = (BitsPerPixel / 8);
Pixels.resize(width * height * sizeof(RGB));
do
{
byte ChunkHeader = 0;
hFile.read(reinterpret_cast<char*>(&ChunkHeader), sizeof(byte));
if (ChunkHeader < 128)
{
++ChunkHeader;
for (int I = 0; I < ChunkHeader; ++I)
{
hFile.read(BuffPos, BytesPerPixel);
Pixels[CurrentByte].RGBA.B = Pixel.RGBA.B;
Pixels[CurrentByte].RGBA.G = Pixel.RGBA.G;
Pixels[CurrentByte].RGBA.R = Pixel.RGBA.R;
Pixels[CurrentByte].RGBA.A = (BitsPerPixel > 24) ? Pixel.RGBA.A : 0xFF;
CurrentByte += BytesPerPixel;
++CurrentPixel;
}
}
else
{
ChunkHeader -= 127;
hFile.read(BuffPos, BytesPerPixel);
for (int I = 0; I < ChunkHeader; ++I)
{
Pixels[CurrentByte].RGBA.B = Pixel.RGBA.B;
Pixels[CurrentByte].RGBA.G = Pixel.RGBA.G;
Pixels[CurrentByte].RGBA.R = Pixel.RGBA.R;
Pixels[CurrentByte].RGBA.A = (BitsPerPixel > 24) ? Pixel.RGBA.A : 0xFF;
CurrentByte += BytesPerPixel;
++CurrentPixel;
}
}
} while(CurrentPixel < (width * height));
}
hFile.close();//I can guarantee nothing is wrong with the bitmap creator.
Bitmap BMP(width, height, 24); //Take the pixels structure and create a 24 bit bitmap. This works 100% of the time for PNG and Bitmap using the same structure.
BMP.Set(Pixels);
BMP.Save("C:/Foo.bmp");
}
int main()
{
Tga F("C:/Foo.tga");
}
Я думаю, что у вас неправильный размер заголовка. Различные биты определены как ниже; утверждения существуют только для того, чтобы убедиться, что компилятор правильно упаковывает структуры.
typedef uint8_t byte;
enum class color_map_type : byte {
absent = 0,
present = 1,
};
enum class image_type : byte {
none = 0,
color_mapped = 1,
true_color = 2,
black_white = 3,
rle_color_mapped = 9,
rle_true_color = 10,
rle_black_white = 11,
};
struct color_map_spec {
uint16_t first_entry_index;
uint16_t length;
byte entry_size;
};
static_assert(sizeof(color_map_spec) == 5, "bad size");
struct image_descriptor {
byte alpha : 4;
byte right : 1;
byte top : 1;
byte reserved : 2;
};
static_assert(sizeof(image_descriptor) == 1, "bad size");
struct image_spec {
uint16_t x_origin;
uint16_t y_origin;
uint16_t width;
uint16_t height;
byte depth;
image_descriptor descriptor;
};
static_assert(sizeof(image_spec) == 10, "bad size");
struct header {
byte id_length;
color_map_type color_map_type;
image_type image_type;
color_map_spec color_map_spec;
image_spec image_spec;
};
static_assert(sizeof(header) == 18, "bad size");
struct footer {
uint32_t ext_area_offset;
uint32_t dev_dir_offset;
char signature[16];
char dot_terminator;
char null_terminator;
}
Других решений пока нет …