LOADING

TGA贴图数据导入导出

加载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;
	}
}