1 // Copyright (C) 2002-2012 Nikolaus Gebhardt
2 // This file is part of the "Irrlicht Engine".
3 // For conditions of distribution and use, see copyright notice in irrlicht.h
4 
5 #include "CImageLoaderTGA.h"
6 
7 #ifdef _IRR_COMPILE_WITH_TGA_LOADER_
8 
9 #include "IReadFile.h"
10 #include "os.h"
11 #include "CColorConverter.h"
12 #include "CImage.h"
13 #include "irrString.h"
14 
15 
16 namespace irr
17 {
18 namespace video
19 {
20 
21 
22 //! returns true if the file maybe is able to be loaded by this class
23 //! based on the file extension (e.g. ".tga")
isALoadableFileExtension(const io::path & filename) const24 bool CImageLoaderTGA::isALoadableFileExtension(const io::path& filename) const
25 {
26 	return core::hasFileExtension ( filename, "tga" );
27 }
28 
29 
30 //! loads a compressed tga.
loadCompressedImage(io::IReadFile * file,const STGAHeader & header) const31 u8 *CImageLoaderTGA::loadCompressedImage(io::IReadFile *file, const STGAHeader& header) const
32 {
33 	// This was written and sent in by Jon Pry, thank you very much!
34 	// I only changed the formatting a little bit.
35 
36 	s32 bytesPerPixel = header.PixelDepth/8;
37 	s32 imageSize =  header.ImageHeight * header.ImageWidth * bytesPerPixel;
38 	u8* data = new u8[imageSize];
39 	s32 currentByte = 0;
40 
41 	while(currentByte < imageSize)
42 	{
43 		u8 chunkheader = 0;
44 		file->read(&chunkheader, sizeof(u8)); // Read The Chunk's Header
45 
46 		if(chunkheader < 128) // If The Chunk Is A 'RAW' Chunk
47 		{
48 			chunkheader++; // Add 1 To The Value To Get Total Number Of Raw Pixels
49 
50 			file->read(&data[currentByte], bytesPerPixel * chunkheader);
51 			currentByte += bytesPerPixel * chunkheader;
52 		}
53 		else
54 		{
55 			// thnx to neojzs for some fixes with this code
56 
57 			// If It's An RLE Header
58 			chunkheader -= 127; // Subtract 127 To Get Rid Of The ID Bit
59 
60 			s32 dataOffset = currentByte;
61 			file->read(&data[dataOffset], bytesPerPixel);
62 
63 			currentByte += bytesPerPixel;
64 
65 			for(s32 counter = 1; counter < chunkheader; counter++)
66 			{
67 				for(s32 elementCounter=0; elementCounter < bytesPerPixel; elementCounter++)
68 					data[currentByte + elementCounter] = data[dataOffset + elementCounter];
69 
70 				currentByte += bytesPerPixel;
71 			}
72 		}
73 	}
74 
75 	return data;
76 }
77 
78 
79 
80 //! returns true if the file maybe is able to be loaded by this class
isALoadableFileFormat(io::IReadFile * file) const81 bool CImageLoaderTGA::isALoadableFileFormat(io::IReadFile* file) const
82 {
83 	if (!file)
84 		return false;
85 
86 	STGAFooter footer;
87 	memset(&footer, 0, sizeof(STGAFooter));
88 	file->seek(file->getSize()-sizeof(STGAFooter));
89 	file->read(&footer, sizeof(STGAFooter));
90 	return (!strcmp(footer.Signature,"TRUEVISION-XFILE.")); // very old tgas are refused.
91 }
92 
93 
94 
95 //! creates a surface from the file
loadImage(io::IReadFile * file) const96 IImage* CImageLoaderTGA::loadImage(io::IReadFile* file) const
97 {
98 	STGAHeader header;
99 	u32 *palette = 0;
100 
101 	file->read(&header, sizeof(STGAHeader));
102 
103 #ifdef __BIG_ENDIAN__
104 	header.ColorMapLength = os::Byteswap::byteswap(header.ColorMapLength);
105 	header.ImageWidth = os::Byteswap::byteswap(header.ImageWidth);
106 	header.ImageHeight = os::Byteswap::byteswap(header.ImageHeight);
107 #endif
108 
109 	// skip image identification field
110 	if (header.IdLength)
111 		file->seek(header.IdLength, true);
112 
113 	if (header.ColorMapType)
114 	{
115 		// create 32 bit palette
116 		palette = new u32[ header.ColorMapLength];
117 
118 		// read color map
119 		u8 * colorMap = new u8[header.ColorMapEntrySize/8 * header.ColorMapLength];
120 		file->read(colorMap,header.ColorMapEntrySize/8 * header.ColorMapLength);
121 
122 		// convert to 32-bit palette
123 		switch ( header.ColorMapEntrySize )
124 		{
125 			case 16:
126 				CColorConverter::convert_A1R5G5B5toA8R8G8B8(colorMap, header.ColorMapLength, palette);
127 				break;
128 			case 24:
129 				CColorConverter::convert_B8G8R8toA8R8G8B8(colorMap, header.ColorMapLength, palette);
130 				break;
131 			case 32:
132 				CColorConverter::convert_B8G8R8A8toA8R8G8B8(colorMap, header.ColorMapLength, palette);
133 				break;
134 		}
135 		delete [] colorMap;
136 	}
137 
138 	// read image
139 
140 	u8* data = 0;
141 
142 	if (	header.ImageType == 1 || // Uncompressed, color-mapped images.
143 			header.ImageType == 2 || // Uncompressed, RGB images
144 			header.ImageType == 3 // Uncompressed, black and white images
145 		)
146 	{
147 		const s32 imageSize = header.ImageHeight * header.ImageWidth * header.PixelDepth/8;
148 		data = new u8[imageSize];
149 	  	file->read(data, imageSize);
150 	}
151 	else
152 	if(header.ImageType == 10)
153 	{
154 		// Runlength encoded RGB images
155 		data = loadCompressedImage(file, header);
156 	}
157 	else
158 	{
159 		os::Printer::log("Unsupported TGA file type", file->getFileName(), ELL_ERROR);
160 		delete [] palette;
161 		return 0;
162 	}
163 
164 	IImage* image = 0;
165 
166 	switch(header.PixelDepth)
167 	{
168 	case 8:
169 		{
170 			if (header.ImageType==3) // grey image
171 			{
172 				image = new CImage(ECF_R8G8B8,
173 					core::dimension2d<u32>(header.ImageWidth, header.ImageHeight));
174 				if (image)
175 					CColorConverter::convert8BitTo24Bit((u8*)data,
176 						(u8*)image->lock(),
177 						header.ImageWidth,header.ImageHeight,
178 						0, 0, (header.ImageDescriptor&0x20)==0);
179 			}
180 			else
181 			{
182 				image = new CImage(ECF_A1R5G5B5,
183 					core::dimension2d<u32>(header.ImageWidth, header.ImageHeight));
184 				if (image)
185 					CColorConverter::convert8BitTo16Bit((u8*)data,
186 						(s16*)image->lock(),
187 						header.ImageWidth,header.ImageHeight,
188 						(s32*) palette, 0,
189 						(header.ImageDescriptor&0x20)==0);
190 			}
191 		}
192 		break;
193 	case 16:
194 		image = new CImage(ECF_A1R5G5B5,
195 			core::dimension2d<u32>(header.ImageWidth, header.ImageHeight));
196 		if (image)
197 			CColorConverter::convert16BitTo16Bit((s16*)data,
198 				(s16*)image->lock(), header.ImageWidth,	header.ImageHeight, 0, (header.ImageDescriptor&0x20)==0);
199 		break;
200 	case 24:
201 			image = new CImage(ECF_R8G8B8,
202 				core::dimension2d<u32>(header.ImageWidth, header.ImageHeight));
203 			if (image)
204 				CColorConverter::convert24BitTo24Bit(
205 					(u8*)data, (u8*)image->lock(), header.ImageWidth, header.ImageHeight, 0, (header.ImageDescriptor&0x20)==0, true);
206 		break;
207 	case 32:
208 			image = new CImage(ECF_A8R8G8B8,
209 				core::dimension2d<u32>(header.ImageWidth, header.ImageHeight));
210 			if (image)
211 				CColorConverter::convert32BitTo32Bit((s32*)data,
212 					(s32*)image->lock(), header.ImageWidth, header.ImageHeight, 0, (header.ImageDescriptor&0x20)==0);
213 		break;
214 	default:
215 		os::Printer::log("Unsupported TGA format", file->getFileName(), ELL_ERROR);
216 		break;
217 	}
218 	if (image)
219 		image->unlock();
220 
221 	delete [] data;
222 	delete [] palette;
223 
224 	return image;
225 }
226 
227 
228 //! creates a loader which is able to load tgas
createImageLoaderTGA()229 IImageLoader* createImageLoaderTGA()
230 {
231 	return new CImageLoaderTGA();
232 }
233 
234 
235 } // end namespace video
236 } // end namespace irr
237 
238 #endif
239 
240