加载TGA格式的贴图
分享下tga图像如何导入导出,为了兼容不同API,稍微做了一下自定义枚举,
DString是我自定义的字符串类,自行改为标准库的std::string也行。
H头文件
#pragma once
#include "Common.h"
#include "TypeConversion.h"
enum TextureFormat
{
R32G32B32A32_FLOAT ,
R16G16B16A16_FLOAT ,
R16G16B16A16_UNORM ,
R8G8B8_UNORM,
R8G8B8A8_UNORM ,
B8G8R8_UNORM,
B8G8R8A8_UNORM ,
B8G8R8X8_UNORM ,
R10G10B10_XR_BIAS_A2_UNORM ,
R10G10B10A2_UNORM ,
B5G5R5A1_UNORM ,
A1R5G5B5_UNORM,
B5G6R5_UNORM ,
R32_FLOAT ,
R16_FLOAT ,
R16_UNORM ,
R8_UNORM ,
A8_UNORM ,
R32G32B32_FLOAT
};
inline const char* GetTGAImageTypeString(uint8_t imageType)
{
if (imageType == 0) return "no image data is present";
else if (imageType == 1) return "uncompressed color-mapped image";
else if (imageType == 2) return "uncompressed true-color image";
else if (imageType == 3) return "uncompressed black-and-white(grayscale) image";
else if (imageType == 9) return "run-length encoded color-mapped image";
else if (imageType == 10) return "run-length encoded true-color image";
else if (imageType == 11) return "run-length encoded black-and-white(grayscale) image";
return "UnKnow";
}
//TGA文件头
struct TGAHeader
{
uint8_t idLength;
uint8_t colormapType;
uint8_t imageType; //图像类型
uint16_t colormapOrigin;
uint16_t colormapLength;
uint8_t colormapDepth;
uint16_t xOrigin;
uint16_t yOrigin;
uint16_t width; //宽度
uint16_t height; //高度
uint8_t bitsPerPixel; //每像素多少位
uint8_t imageDescriptor;
};
//图像数据结构
typedef struct tagImageData
{
TGAHeader data_header;
DString fileName;
DString filePath;
std::vector<unsigned char> imageData;
TextureFormat texFormat;
unsigned int imageSize = 0;
GUID guid;//暂时没用,以后研究出一下TGA的最后一个自定义分区怎么使用,多余的东西可以存里面
}ImageData, * LPImage;
class ImageTool
{
public:
static bool SaveTgaImage(const char* filename, ImageData* tgaData);
static ImageData* ReadTgaImage(const char* filename);
};
CPP源文件#include "ImageTool.h"
ImageData* ImageTool::ReadTgaImage(const char* filename)
{
ImageData* out = NULL;
//open TGA file
errno_t err_src_name;
FILE* file = NULL;
err_src_name = fopen_s(&file, filename, "rb");//读取,二进制模式
if (file == NULL || err_src_name != 0)
{
ConsoleDebug::print_endl("open TGA image file failed.", "255,255,0");
return NULL;
}
//读取文件头
TGAHeader data_header;
{
//fread(&data_header, sizeof(TGAHeader), 1, file_tga);//直接读取会有结构体的对齐问题
fread(&data_header.idLength, sizeof(uint8_t), 1, file);
fread(&data_header.colormapType, sizeof(uint8_t), 1, file);
fread(&data_header.imageType, sizeof(uint8_t), 1, file);
fread(&data_header.colormapOrigin, sizeof(uint16_t), 1, file);
fread(&data_header.colormapLength, sizeof(uint16_t), 1, file);
fread(&data_header.colormapDepth, sizeof(uint8_t), 1, file);
fread(&data_header.xOrigin, sizeof(uint16_t), 1, file);
fread(&data_header.yOrigin, sizeof(uint16_t), 1, file);
fread(&data_header.width, sizeof(uint16_t), 1, file);
fread(&data_header.height, sizeof(uint16_t), 1, file);
fread(&data_header.bitsPerPixel, sizeof(uint8_t), 1, file);
fread(&data_header.imageDescriptor, sizeof(uint8_t), 1, file);
}
out = new ImageData;
out->data_header = data_header;
unsigned int pixelSize = data_header.height * data_header.width;
unsigned int imageSize = pixelSize * (data_header.bitsPerPixel / 8);//像素 × 每个像素占的字节数 = 总长度
ConsoleDebug::print_endl("Get targa image:" + DString(filename)
+ "\n\twidth:" + DString::FromInt(data_header.width)
+ "\n\theight:" + DString::FromInt(data_header.height)
+ "\n\tdepth:" + DString::FromInt(data_header.bitsPerPixel)
+ "\n\timageSize:" + DString::FromInt(imageSize)
, "205,225,255");
//读取像素
byte* tgaData = new byte[imageSize];
if (fread(tgaData, 1, imageSize, file) != imageSize)
{
ConsoleDebug::print_endl("Read TGA image color error.all pixel size is not equal the image size.", "255,255,0");
if (out)
delete out;
if (tgaData)
delete[] out;
return NULL;
}
try {
switch (data_header.bitsPerPixel)
{
case 8:
if (data_header.imageType == 3)//8位单通道灰度图
{
out->texFormat = R8_UNORM;
out->imageData.resize(imageSize);
out->imageSize = imageSize;
if (memcpy_s(out->imageData.data(), imageSize, tgaData, imageSize) != 0)
{
if (out)
delete out;
if (tgaData)
delete[] out;
return NULL;
}
}
break;
case 16:
{
out->imageData.resize(imageSize);
out->texFormat = A1R5G5B5_UNORM;
out->imageSize = imageSize;
if (memcpy_s(out->imageData.data(), imageSize, tgaData, imageSize) != 0)
{
if (out)
delete out;
if (tgaData)
delete[] out;
return NULL;
}
}
break;
case 24:
//rgb888 to rgb 555
//DX 不支持单纯的R8 G8 B8 :24bit,所以要转一下16bit
out->imageData.resize(pixelSize * 2);
out->texFormat = A1R5G5B5_UNORM;
data_header.bitsPerPixel = 16;
out->data_header.bitsPerPixel = data_header.bitsPerPixel;
for (unsigned int i = 0; i < pixelSize; i++)
{
byte R5, G5, B5;
float rgb555i = (float)(255.0f / 31.0f);
B5 = (byte)(tgaData[i * 3] / rgb555i) & 0xff;
G5 = (byte)(tgaData[i * 3 + 1] / rgb555i) & 0xff;
R5 = (byte)(tgaData[i * 3 + 2] / rgb555i) & 0xff;
byte B1, B2;
B1 = B5 | (G5 << 5);
B2 = (R5 << 2) | (G5 >> 3);
out->imageData[i * 2] = B1;
out->imageData[i * 2 + 1] = B2;
}
out->imageSize = pixelSize * 2;
break;
case 32://32位BGRA
out->texFormat = B8G8R8A8_UNORM;
out->imageData.resize(imageSize);
out->imageSize = imageSize;
if (memcpy_s(out->imageData.data(), imageSize, tgaData, imageSize) != 0)
{
if (out)
delete out;
if (tgaData)
delete[] out;
return NULL;
}
break;
default:
ConsoleDebug::print_endl("Read TGA image file header failed.Image depth is " + DString::FromInt(data_header.bitsPerPixel) + ".that is unuseful depth.", "255,255,0");
if (out)
delete out;
if (tgaData)
delete[] out;
return NULL;
}
}
catch (std::exception ex)
{
ConsoleDebug::print_endl(DString("Read Tga Image Pixel Failed: ") + ex.what(), "255,255,0");
}
ConsoleDebug::print_endl("Get image texture:" + DString(filename)
+ "\n\twidth:" + DString::FromInt(data_header.width)
+ "\n\theight:" + DString::FromInt(data_header.height)
+ "\n\tdepth:" + DString::FromInt(data_header.bitsPerPixel)
+ "\n\timageSize:" + DString::FromInt(out->imageSize)
, "205,225,255");
if (file != NULL)
fclose(file);
if (tgaData != NULL)
{
delete[] tgaData;
tgaData = NULL;
}
DString cbPath = filename;
out->filePath = cbPath;
out->fileName = cbPath.GetBaseName();
//SaveTgaImage("D:\\123.tga", out);
return out;
}
bool ImageTool::SaveTgaImage(const char* filename, ImageData* tgaData)
{
try {
if (tgaData == NULL)
{
ConsoleDebug::print_endl("TGA data was null", "255,255,0");
return false;
}
if (tgaData->imageData.size() <= 0)
{
ConsoleDebug::print_endl(DString("Save TGA image failed.The TGA data is empty!!") + filename, "255,255,0");
return false;
}
FILE* file = NULL;
if (fopen_s(&file, filename, "wb") != 0)//二进制+写
{
ConsoleDebug::print_endl(DString("open tga file failed: ") + filename, "255,255,0");
return false;
}
//12 byte Header
byte TGA_UnCompressed_HeaderRef[12] = { 0,0,2,0,0,0,0,0,0,0,0,0 };
//6 byte image data
byte* TGAwidth_data = TypeConversion::UIntToBytes(tgaData->data_header.width);
byte* TGAheight_data = TypeConversion::UIntToBytes(tgaData->data_header.height);
byte* TGAdepth_data = TypeConversion::UIntToBytes(tgaData->data_header.bitsPerPixel);
byte TGA_Image_Data_Ref[6] = { TGAwidth_data[0],TGAwidth_data[1],TGAheight_data[0],TGAheight_data[1],TGAdepth_data[0],TGAdepth_data[1] };
//color message
unsigned int pixelSize = tgaData->data_header.height * tgaData->data_header.width;
const unsigned int imageSize = pixelSize * (tgaData->data_header.bitsPerPixel / 8);
byte* TGA_Image_Message = new byte[imageSize];
memcpy_s(TGA_Image_Message, imageSize, tgaData->imageData.data(), imageSize);
//
fwrite(TGA_UnCompressed_HeaderRef, 1, 12, file);
fwrite(TGA_Image_Data_Ref, 1, 6, file);
fwrite(TGA_Image_Message, 1, imageSize, file);
fclose(file);
delete[] TGAwidth_data;
delete[] TGAheight_data;
delete[] TGAdepth_data;
delete[] TGA_Image_Message;
ConsoleDebug::print_endl(DString("Save Tga Image Successful: ") + filename, "0,255,0");
return true;
}
catch (std::exception ex)
{
ConsoleDebug::print_endl(DString("Save Tga Image Failed: ") + ex.what(), "255,255,0");
return false;
}
}