从内存中写入时有时会损坏 TGA-file
Sometimes broken TGA-file when written from memory
我观察到有时当从内存中写入一个image-buffer(包含从中加载的RGBA-pixel-data时的奇怪行为a tga-file) 作为 tga.
返回文件
image-buffer 是从 TGA-file 加载的,其算法是从该线程中窃取和改编的:
Loading a tga/bmp file in C++/OpenGL
writing-method我盗用并改编自这个地址:
http://www.paulbourke.net/dataformats/tga/
这是一个最小的可编译示例,其中 tileA.tga 被正确保存回光盘为 tileA_new.tga,但 TileB_new.tga 已损坏(图形看起来很奇怪,像素错误)!为什么 TileB_new.tga 坏了?
这两个源tga-file是不同的,但是在gimp和irfanview和我double-checked load-algorithm中都可以正确查看。它有效,因为当我将两个已加载图块(使用 OpenGL)的 image-buffer 渲染到屏幕时,它们看起来是正确的!但是将 raw-buffers 写入光盘的行为不同,为什么?我比较了 hex-editor 中源 tga-file 的 header,但它们是相等的。书面的 tga-files 也等于 headers。
我可以看到,tileB.tga 的大小是 tileA.tga 的 5 倍,但这似乎是正确的,因为 gimp/irfanview 显示它们是正确的。也许你可以看到我在这里犯的错误?
//一个小 visual-studio-project 包括两个 tga-file 可以在这里下载
https://www.file-upload.net/download-13208817/WhosebugTGA.zip.html
最小示例:
#include <vector>
#include <fstream>
//special-sausage for microsoft
#ifdef _MSC_VER
#pragma warning(disable:4996)
#endif
//=========================================
// Code for loading a TGA-file
//=========================================
typedef union PixelInfo
{
std::uint32_t Colour;
struct
{
std::uint8_t R, G, B, A;
};
} *PPixelInfo;
class Tga
{
private:
std::vector<std::uint8_t> Pixels;
bool ImageCompressed;
std::uint32_t width, height, size, BitsPerPixel;
public:
Tga(const char* FilePath);
std::vector<std::uint8_t> GetPixels() { return this->Pixels; }
std::uint32_t GetWidth() const { return this->width; }
std::uint32_t GetHeight() const { return this->height; }
std::uint32_t GetBitsPerPixel() const { return this->BitsPerPixel; }
bool HasAlphaChannel() { return BitsPerPixel == 32; }
};
Tga::Tga(const char* FilePath)
{
std::fstream hFile(FilePath, std::ios::in | std::ios::binary);
if (!hFile.is_open()) { throw std::invalid_argument("File Not Found."); }
std::uint8_t Header[18] = { 0 };
std::vector<std::uint8_t> ImageData;
static std::uint8_t DeCompressed[12] = { 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 };
static std::uint8_t IsCompressed[12] = { 0x0, 0x0, 0xA, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 };
hFile.read(reinterpret_cast<char*>(&Header), sizeof(Header));
if (!std::memcmp(DeCompressed, &Header, sizeof(DeCompressed)))
{
BitsPerPixel = Header[16];
width = Header[13] * 256 + Header[12];
height = Header[15] * 256 + Header[14];
size = ((width * BitsPerPixel + 31) / 32) * 4 * height;
if ((BitsPerPixel != 24) && (BitsPerPixel != 32))
{
hFile.close();
throw std::invalid_argument("Invalid File Format. Required: 24 or 32 Bit Image.");
}
ImageData.resize(size);
ImageCompressed = false;
hFile.read(reinterpret_cast<char*>(ImageData.data()), size);
}
else if (!std::memcmp(IsCompressed, &Header, sizeof(IsCompressed)))
{
BitsPerPixel = Header[16];
width = Header[13] * 256 + Header[12];
height = Header[15] * 256 + Header[14];
size = ((width * BitsPerPixel + 31) / 32) * 4 * height;
if ((BitsPerPixel != 24) && (BitsPerPixel != 32))
{
hFile.close();
throw std::invalid_argument("Invalid File Format. Required: 24 or 32 Bit Image.");
}
PixelInfo Pixel = { 0 };
int CurrentByte = 0;
std::size_t CurrentPixel = 0;
ImageCompressed = true;
std::uint8_t ChunkHeader = { 0 };
int BytesPerPixel = (BitsPerPixel / 8);
ImageData.resize(static_cast<size_t>(width) * static_cast<size_t>(height) * sizeof(PixelInfo));
do
{
hFile.read(reinterpret_cast<char*>(&ChunkHeader), sizeof(ChunkHeader));
if (ChunkHeader < 128)
{
++ChunkHeader;
for (int I = 0; I < ChunkHeader; ++I, ++CurrentPixel)
{
hFile.read(reinterpret_cast<char*>(&Pixel), BytesPerPixel);
ImageData[CurrentByte++] = Pixel.B;
ImageData[CurrentByte++] = Pixel.G;
ImageData[CurrentByte++] = Pixel.R;
if (BitsPerPixel > 24) ImageData[CurrentByte++] = Pixel.A;
}
}
else
{
ChunkHeader -= 127;
hFile.read(reinterpret_cast<char*>(&Pixel), BytesPerPixel);
for (int I = 0; I < ChunkHeader; ++I, ++CurrentPixel)
{
ImageData[CurrentByte++] = Pixel.B;
ImageData[CurrentByte++] = Pixel.G;
ImageData[CurrentByte++] = Pixel.R;
if (BitsPerPixel > 24) ImageData[CurrentByte++] = Pixel.A;
}
}
} while (CurrentPixel < (static_cast<size_t>(width) * static_cast<size_t>(height)));
}
else
{
hFile.close();
throw std::invalid_argument("Invalid File Format. Required: 24 or 32 Bit TGA File.");
}
hFile.close();
this->Pixels = ImageData;
}
//=========================================
// code for writing a TGA-file
//=========================================
void writeTGA(const std::string &refFile, Tga &refTGA)
{
unsigned short width = static_cast<unsigned short>(refTGA.GetWidth());
unsigned short height = static_cast<unsigned short>(refTGA.GetWidth());
unsigned char bitsPerPixel = static_cast<unsigned char>(refTGA.GetBitsPerPixel());
unsigned char bitsAlphaChannel = (bitsPerPixel == 32 ? 8 : 0);
FILE * fptr = fopen(refFile.c_str(), "w");
putc(0, fptr);
putc(0, fptr);
putc(2, fptr); /* uncompressed RGB */
putc(0, fptr); putc(0, fptr);
putc(0, fptr); putc(0, fptr);
putc(0, fptr);
putc(0, fptr); putc(0, fptr); /* X origin */
putc(0, fptr); putc(0, fptr); /* y origin */
putc((width & 0x00FF), fptr);
putc((width & 0xFF00) / 256, fptr);
putc((height & 0x00FF), fptr);
putc((height & 0xFF00) / 256, fptr);
putc(bitsPerPixel, fptr); /* 24/32 bit bitmap */
putc(bitsAlphaChannel, fptr); /* When 32 bit, write 8, else 0 */
auto pixelData = refTGA.GetPixels();
for (size_t i = 0; i < static_cast<size_t>(width) * static_cast<size_t>(height) * (bitsPerPixel/8); i += (bitsPerPixel/8))
{
unsigned char r = pixelData[i];
unsigned char g = pixelData[i + 1];
unsigned char b = pixelData[i + 2];
unsigned char a = (bitsAlphaChannel == 8 ? pixelData[i + 3] : 0);
putc(b, fptr);
putc(g, fptr);
putc(r, fptr);
if (bitsAlphaChannel == 8)
putc(a, fptr);
}
fclose(fptr);
}
//=========================================
// main
//=========================================
int main()
{
Tga oTgaA("tileA.tga");
writeTGA("tileA_new.tga", oTgaA); // works correct as aspected
Tga oTgaB("tileB.tga");
writeTGA("tileB_new.tga", oTgaB); // graphic-file has artefacts, why?
}
由于我的评论似乎已经解决了问题(见上^^^^)。我会在这里展开。
在 Windows(至少是 Microsoft 的 CRT)上,以文本模式写入文件和以二进制模式写入文件之间存在显着差异。
具体来说,任何匹配 '\n' 的字符都将扩展为两个字符序列“\r\n”。此外,某些函数应用 MB 和 unicode 字符之间的转换。更多信息可以在 fopen
上的 MSDN 文档中找到,网址为:https://msdn.microsoft.com/en-us/library/yeby3zcb.aspx
因此,当 reading/writing 非文本数据时,请务必将 "rb"
或 "wb"
标志适当地传递给 fopen
。
在 Posix 系统上,此注意事项不适用,但明确您的意图仍然是一种很好的做法。
我观察到有时当从内存中写入一个image-buffer(包含从中加载的RGBA-pixel-data时的奇怪行为a tga-file) 作为 tga.
返回文件image-buffer 是从 TGA-file 加载的,其算法是从该线程中窃取和改编的:
Loading a tga/bmp file in C++/OpenGL
writing-method我盗用并改编自这个地址:
http://www.paulbourke.net/dataformats/tga/
这是一个最小的可编译示例,其中 tileA.tga 被正确保存回光盘为 tileA_new.tga,但 TileB_new.tga 已损坏(图形看起来很奇怪,像素错误)!为什么 TileB_new.tga 坏了?
这两个源tga-file是不同的,但是在gimp和irfanview和我double-checked load-algorithm中都可以正确查看。它有效,因为当我将两个已加载图块(使用 OpenGL)的 image-buffer 渲染到屏幕时,它们看起来是正确的!但是将 raw-buffers 写入光盘的行为不同,为什么?我比较了 hex-editor 中源 tga-file 的 header,但它们是相等的。书面的 tga-files 也等于 headers。 我可以看到,tileB.tga 的大小是 tileA.tga 的 5 倍,但这似乎是正确的,因为 gimp/irfanview 显示它们是正确的。也许你可以看到我在这里犯的错误?
//一个小 visual-studio-project 包括两个 tga-file 可以在这里下载 https://www.file-upload.net/download-13208817/WhosebugTGA.zip.html
最小示例:
#include <vector>
#include <fstream>
//special-sausage for microsoft
#ifdef _MSC_VER
#pragma warning(disable:4996)
#endif
//=========================================
// Code for loading a TGA-file
//=========================================
typedef union PixelInfo
{
std::uint32_t Colour;
struct
{
std::uint8_t R, G, B, A;
};
} *PPixelInfo;
class Tga
{
private:
std::vector<std::uint8_t> Pixels;
bool ImageCompressed;
std::uint32_t width, height, size, BitsPerPixel;
public:
Tga(const char* FilePath);
std::vector<std::uint8_t> GetPixels() { return this->Pixels; }
std::uint32_t GetWidth() const { return this->width; }
std::uint32_t GetHeight() const { return this->height; }
std::uint32_t GetBitsPerPixel() const { return this->BitsPerPixel; }
bool HasAlphaChannel() { return BitsPerPixel == 32; }
};
Tga::Tga(const char* FilePath)
{
std::fstream hFile(FilePath, std::ios::in | std::ios::binary);
if (!hFile.is_open()) { throw std::invalid_argument("File Not Found."); }
std::uint8_t Header[18] = { 0 };
std::vector<std::uint8_t> ImageData;
static std::uint8_t DeCompressed[12] = { 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 };
static std::uint8_t IsCompressed[12] = { 0x0, 0x0, 0xA, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 };
hFile.read(reinterpret_cast<char*>(&Header), sizeof(Header));
if (!std::memcmp(DeCompressed, &Header, sizeof(DeCompressed)))
{
BitsPerPixel = Header[16];
width = Header[13] * 256 + Header[12];
height = Header[15] * 256 + Header[14];
size = ((width * BitsPerPixel + 31) / 32) * 4 * height;
if ((BitsPerPixel != 24) && (BitsPerPixel != 32))
{
hFile.close();
throw std::invalid_argument("Invalid File Format. Required: 24 or 32 Bit Image.");
}
ImageData.resize(size);
ImageCompressed = false;
hFile.read(reinterpret_cast<char*>(ImageData.data()), size);
}
else if (!std::memcmp(IsCompressed, &Header, sizeof(IsCompressed)))
{
BitsPerPixel = Header[16];
width = Header[13] * 256 + Header[12];
height = Header[15] * 256 + Header[14];
size = ((width * BitsPerPixel + 31) / 32) * 4 * height;
if ((BitsPerPixel != 24) && (BitsPerPixel != 32))
{
hFile.close();
throw std::invalid_argument("Invalid File Format. Required: 24 or 32 Bit Image.");
}
PixelInfo Pixel = { 0 };
int CurrentByte = 0;
std::size_t CurrentPixel = 0;
ImageCompressed = true;
std::uint8_t ChunkHeader = { 0 };
int BytesPerPixel = (BitsPerPixel / 8);
ImageData.resize(static_cast<size_t>(width) * static_cast<size_t>(height) * sizeof(PixelInfo));
do
{
hFile.read(reinterpret_cast<char*>(&ChunkHeader), sizeof(ChunkHeader));
if (ChunkHeader < 128)
{
++ChunkHeader;
for (int I = 0; I < ChunkHeader; ++I, ++CurrentPixel)
{
hFile.read(reinterpret_cast<char*>(&Pixel), BytesPerPixel);
ImageData[CurrentByte++] = Pixel.B;
ImageData[CurrentByte++] = Pixel.G;
ImageData[CurrentByte++] = Pixel.R;
if (BitsPerPixel > 24) ImageData[CurrentByte++] = Pixel.A;
}
}
else
{
ChunkHeader -= 127;
hFile.read(reinterpret_cast<char*>(&Pixel), BytesPerPixel);
for (int I = 0; I < ChunkHeader; ++I, ++CurrentPixel)
{
ImageData[CurrentByte++] = Pixel.B;
ImageData[CurrentByte++] = Pixel.G;
ImageData[CurrentByte++] = Pixel.R;
if (BitsPerPixel > 24) ImageData[CurrentByte++] = Pixel.A;
}
}
} while (CurrentPixel < (static_cast<size_t>(width) * static_cast<size_t>(height)));
}
else
{
hFile.close();
throw std::invalid_argument("Invalid File Format. Required: 24 or 32 Bit TGA File.");
}
hFile.close();
this->Pixels = ImageData;
}
//=========================================
// code for writing a TGA-file
//=========================================
void writeTGA(const std::string &refFile, Tga &refTGA)
{
unsigned short width = static_cast<unsigned short>(refTGA.GetWidth());
unsigned short height = static_cast<unsigned short>(refTGA.GetWidth());
unsigned char bitsPerPixel = static_cast<unsigned char>(refTGA.GetBitsPerPixel());
unsigned char bitsAlphaChannel = (bitsPerPixel == 32 ? 8 : 0);
FILE * fptr = fopen(refFile.c_str(), "w");
putc(0, fptr);
putc(0, fptr);
putc(2, fptr); /* uncompressed RGB */
putc(0, fptr); putc(0, fptr);
putc(0, fptr); putc(0, fptr);
putc(0, fptr);
putc(0, fptr); putc(0, fptr); /* X origin */
putc(0, fptr); putc(0, fptr); /* y origin */
putc((width & 0x00FF), fptr);
putc((width & 0xFF00) / 256, fptr);
putc((height & 0x00FF), fptr);
putc((height & 0xFF00) / 256, fptr);
putc(bitsPerPixel, fptr); /* 24/32 bit bitmap */
putc(bitsAlphaChannel, fptr); /* When 32 bit, write 8, else 0 */
auto pixelData = refTGA.GetPixels();
for (size_t i = 0; i < static_cast<size_t>(width) * static_cast<size_t>(height) * (bitsPerPixel/8); i += (bitsPerPixel/8))
{
unsigned char r = pixelData[i];
unsigned char g = pixelData[i + 1];
unsigned char b = pixelData[i + 2];
unsigned char a = (bitsAlphaChannel == 8 ? pixelData[i + 3] : 0);
putc(b, fptr);
putc(g, fptr);
putc(r, fptr);
if (bitsAlphaChannel == 8)
putc(a, fptr);
}
fclose(fptr);
}
//=========================================
// main
//=========================================
int main()
{
Tga oTgaA("tileA.tga");
writeTGA("tileA_new.tga", oTgaA); // works correct as aspected
Tga oTgaB("tileB.tga");
writeTGA("tileB_new.tga", oTgaB); // graphic-file has artefacts, why?
}
由于我的评论似乎已经解决了问题(见上^^^^)。我会在这里展开。
在 Windows(至少是 Microsoft 的 CRT)上,以文本模式写入文件和以二进制模式写入文件之间存在显着差异。
具体来说,任何匹配 '\n' 的字符都将扩展为两个字符序列“\r\n”。此外,某些函数应用 MB 和 unicode 字符之间的转换。更多信息可以在 fopen
上的 MSDN 文档中找到,网址为:https://msdn.microsoft.com/en-us/library/yeby3zcb.aspx
因此,当 reading/writing 非文本数据时,请务必将 "rb"
或 "wb"
标志适当地传递给 fopen
。
在 Posix 系统上,此注意事项不适用,但明确您的意图仍然是一种很好的做法。