1 //-----------------------------------------------------------------------------
2 //
3 // ImageLib Sources
4 // Copyright (C) 2000-2009 by Denton Woods
5 // Last modified: 03/07/2009
6 //
7 // Filename: src-IL/src/il_tpl.c
8 //
9 // Description: Reads from a Gamecube Texture Palette (.tpl).
10 // Specifications were found at
11 // http://pabut.homeip.net:8000/yagcd/chap14.html.
12 //
13 //-----------------------------------------------------------------------------
14
15
16 #include "il_internal.h"
17 #ifndef IL_NO_TPL
18 #include "il_dds.h"
19
20
21 typedef struct TPLHEAD
22 {
23 ILuint Magic;
24 ILuint nTextures;
25 ILuint HeaderSize;
26 } TPLHEAD;
27
28 // Data formats
29 #define TPL_I4 0
30 #define TPL_I8 1
31 #define TPL_IA4 2
32 #define TPL_IA8 3
33 #define TPL_RGB565 4
34 #define TPL_RGB5A3 5
35 #define TPL_RGBA8 6
36 #define TPL_CI4 8
37 #define TPL_CI8 9
38 #define TPL_CI14X2 10
39 #define TPL_CMP 14
40
41 // Wrapping
42 #define TPL_CLAMP 0
43 #define TPL_REPEAT 1
44 #define TPL_MIRROR 2
45
46 // Palette entries
47 #define TPL_PAL_IA8 0
48 #define TPL_PAL_RGB565 1
49 #define TPL_PAL_RGB5A3 2
50
51
52 ILboolean iIsValidTpl(void);
53 ILboolean iCheckTpl(TPLHEAD *Header);
54 ILboolean iLoadTplInternal(void);
55 ILboolean TplGetIndexImage(ILimage *Image, ILuint TexOff, ILuint DataFormat);
56
57
58 //! Checks if the file specified in FileName is a valid TPL file.
ilIsValidTpl(ILconst_string FileName)59 ILboolean ilIsValidTpl(ILconst_string FileName)
60 {
61 ILHANDLE TplFile;
62 ILboolean bTpl = IL_FALSE;
63
64 if (!iCheckExtension(FileName, IL_TEXT("tpl"))) {
65 ilSetError(IL_INVALID_EXTENSION);
66 return bTpl;
67 }
68
69 TplFile = iopenr(FileName);
70 if (TplFile == NULL) {
71 ilSetError(IL_COULD_NOT_OPEN_FILE);
72 return bTpl;
73 }
74
75 bTpl = ilIsValidTplF(TplFile);
76 icloser(TplFile);
77
78 return bTpl;
79 }
80
81
82 //! Checks if the ILHANDLE contains a valid TPL file at the current position.
ilIsValidTplF(ILHANDLE File)83 ILboolean ilIsValidTplF(ILHANDLE File)
84 {
85 ILuint FirstPos;
86 ILboolean bRet;
87
88 iSetInputFile(File);
89 FirstPos = itell();
90 bRet = iIsValidTpl();
91 iseek(FirstPos, IL_SEEK_SET);
92
93 return bRet;
94 }
95
96
97 //! Checks if Lump is a valid TPL lump.
ilIsValidTplL(const void * Lump,ILuint Size)98 ILboolean ilIsValidTplL(const void *Lump, ILuint Size)
99 {
100 iSetInputLump(Lump, Size);
101 return iIsValidTpl();
102 }
103
104
105 // Internal function used to get the TPL header from the current file.
iGetTplHead(TPLHEAD * Header)106 ILboolean iGetTplHead(TPLHEAD *Header)
107 {
108 Header->Magic = GetBigUInt();
109 Header->nTextures = GetBigUInt();
110 Header->HeaderSize = GetBigUInt();
111 return IL_TRUE;
112 }
113
114
115 // Internal function to get the header and check it.
iIsValidTpl(void)116 ILboolean iIsValidTpl(void)
117 {
118 TPLHEAD Header;
119
120 if (!iGetTplHead(&Header))
121 return IL_FALSE;
122 iseek(-12, IL_SEEK_CUR);
123
124 return iCheckTpl(&Header);
125 }
126
127
128 // Internal function used to check if the HEADER is a valid TPL header.
iCheckTpl(TPLHEAD * Header)129 ILboolean iCheckTpl(TPLHEAD *Header)
130 {
131 // The file signature is 0x0020AF30.
132 if (Header->Magic != 0x0020AF30)
133 return IL_FALSE;
134 // Only valid header size is 0x0C.
135 if (Header->HeaderSize != 0x0C)
136 return IL_FALSE;
137 // We have to have at least 1 texture.
138 if (Header->nTextures == 0)
139 return IL_FALSE;
140
141 return IL_TRUE;
142 }
143
144
145 //! Reads a TPL file
ilLoadTpl(ILconst_string FileName)146 ILboolean ilLoadTpl(ILconst_string FileName)
147 {
148 ILHANDLE TplFile;
149 ILboolean bTpl = IL_FALSE;
150
151 TplFile = iopenr(FileName);
152 if (TplFile == NULL) {
153 ilSetError(IL_COULD_NOT_OPEN_FILE);
154 return bTpl;
155 }
156
157 bTpl = ilLoadTplF(TplFile);
158 icloser(TplFile);
159
160 return bTpl;
161 }
162
163
164 //! Reads an already-opened TPL file
ilLoadTplF(ILHANDLE File)165 ILboolean ilLoadTplF(ILHANDLE File)
166 {
167 ILuint FirstPos;
168 ILboolean bRet;
169
170 iSetInputFile(File);
171 FirstPos = itell();
172 bRet = iLoadTplInternal();
173 iseek(FirstPos, IL_SEEK_SET);
174
175 return bRet;
176 }
177
178
179 //! Reads from a memory "lump" that contains a TPL
ilLoadTplL(const void * Lump,ILuint Size)180 ILboolean ilLoadTplL(const void *Lump, ILuint Size)
181 {
182 iSetInputLump(Lump, Size);
183 return iLoadTplInternal();
184 }
185
186
187 // Internal function used to load the TPL.
iLoadTplInternal(void)188 ILboolean iLoadTplInternal(void)
189 {
190 TPLHEAD Header;
191 ILimage *Image/*, *BaseImage*/;
192 ILuint Pos, TexOff, PalOff, DataFormat, Bpp, DataOff, WrapS, WrapT;
193 ILuint x, y, xBlock, yBlock, i, j, k, n;
194 ILenum Format;
195 ILushort Width, Height, ShortPixel;
196 ILubyte BytePixel, CompData[8];
197 Color8888 colours[4], *col;
198 ILushort color_0, color_1;
199 ILuint bitmask, Select;
200
201 if (iCurImage == NULL) {
202 ilSetError(IL_ILLEGAL_OPERATION);
203 return IL_FALSE;
204 }
205 Image = iCurImage; // Top-level image
206
207 if (!iGetTplHead(&Header))
208 return IL_FALSE;
209 if (!iCheckTpl(&Header)) {
210 ilSetError(IL_INVALID_FILE_HEADER);
211 return IL_FALSE;
212 }
213
214 // Points to the beginning of the texture header directory.
215 Pos = itell();
216
217 for (n = 0; n < Header.nTextures; n++) {
218 // Go back to the texture header directory for texture number n+1.
219 iseek(Pos + n * 8, IL_SEEK_SET);
220 TexOff = GetBigUInt();
221 PalOff = GetBigUInt();
222 // Go to the texture header.
223 if (iseek(TexOff, IL_SEEK_SET))
224 return IL_FALSE;
225
226 Height = GetBigUShort();
227 Width = GetBigUShort();
228 // It looks like files actually have n-1 images, with the nth one having 0 height and width.
229 if (Width == 0 || Height == 0) {
230 // If this is our first image, however, we error out.
231 if (Image == iCurImage) {
232 ilSetError(IL_ILLEGAL_FILE_VALUE);
233 return IL_FALSE;
234 }
235 // Break out of our for loop and run ilFixImage on the images.
236 break;
237 }
238
239 DataFormat = GetBigUInt();
240 TexOff = GetBigUInt();
241 WrapS = GetBigUInt();
242 WrapT = GetBigUInt();
243 if (WrapS == TPL_REPEAT || WrapS == TPL_MIRROR) {
244 // By the specs, repeated and mirrored textures must have dimensions of power of 2.
245 if ((Width != ilNextPower2(Width)) || (Height != ilNextPower2(Height))) {
246 ilSetError(IL_ILLEGAL_FILE_VALUE);
247 return IL_FALSE;
248 }
249 }
250
251 // Go to the actual texture data.
252 if (iseek(TexOff, IL_SEEK_SET))
253 return IL_FALSE;
254
255 switch (DataFormat)
256 {
257 case TPL_I4:
258 case TPL_I8:
259 Format = IL_LUMINANCE;
260 Bpp = 1;
261 break;
262 case TPL_IA4:
263 case TPL_IA8:
264 Format = IL_LUMINANCE_ALPHA;
265 Bpp = 1;
266 break;
267 case TPL_RGB565:
268 Format = IL_RGB;
269 Bpp = 3;
270 break;
271 case TPL_RGB5A3:
272 Format = IL_RGBA;
273 Bpp = 4;
274 break;
275 case TPL_RGBA8:
276 Format = IL_RGBA;
277 Bpp = 4;
278 break;
279 case TPL_CI4:
280 case TPL_CI8:
281 Format = IL_COLOR_INDEX;
282 Bpp = 1;
283 break;
284 case TPL_CI14X2:
285 Format = IL_RGBA;
286 Bpp = 3;
287 break;
288 case TPL_CMP:
289 Format = IL_RGBA;
290 Bpp = 4;
291 break;
292
293 default:
294 ilSetError(IL_FORMAT_NOT_SUPPORTED);
295 return IL_FALSE;
296 }
297
298 if (Image == iCurImage) { // This is our first image.
299 if (!ilTexImage(Width, Height, 1, Bpp, Format, IL_UNSIGNED_BYTE, NULL))
300 return IL_FALSE;
301 }
302 else {
303 Image->Next = ilNewImageFull(Width, Height, 1, Bpp, Format, IL_UNSIGNED_BYTE, NULL);
304 if (Image->Next == NULL)
305 return IL_FALSE;
306 Image = Image->Next;
307 }
308 Image->Origin = IL_ORIGIN_UPPER_LEFT; // Origins are always fixed here.
309
310 switch (DataFormat)
311 {
312 case TPL_I4:
313 // 8x8 tiles of 4-bit intensity values
314 for (y = 0; y < Image->Height; y += 8) {
315 for (x = 0; x < Image->Width; x += 8) {
316 for (yBlock = 0; yBlock < 8; yBlock++) {
317 if ((y + yBlock) >= Image->Height) {
318 iseek(8, IL_SEEK_CUR); // Entire row of pad bytes skipped.
319 continue;
320 }
321 DataOff = Image->Bps * (y + yBlock) + Image->Bpp * x;
322 for (xBlock = 0; xBlock < 8; xBlock += 2) {
323 BytePixel = igetc();
324 if ((x + xBlock) >= Image->Width)
325 continue; // Already read the pad byte.
326 Image->Data[DataOff] = (BytePixel & 0xF0) | (BytePixel & 0xF0) >> 4;
327 DataOff++;
328 // We have to do this check again, so we do not go past the last pixel in the image (ex. 1 pixel wide image).
329 if ((x + xBlock) >= Image->Width)
330 continue; // Already read the pad byte.
331 Image->Data[DataOff+1] = (BytePixel & 0x0F) << 4 | (BytePixel & 0x0F);
332 DataOff++;
333 }
334 }
335 }
336 }
337 break;
338
339 case TPL_I8:
340 // 8x4 tiles of 8-bit intensity values
341 for (y = 0; y < Image->Height; y += 4) {
342 for (x = 0; x < Image->Width; x += 8) {
343 for (yBlock = 0; yBlock < 4; yBlock++) {
344 if ((y + yBlock) >= Image->Height) {
345 iseek(8, IL_SEEK_CUR); // Entire row of pad bytes skipped.
346 continue;
347 }
348 DataOff = Image->Bps * (y + yBlock) + Image->Bpp * x;
349 for (xBlock = 0; xBlock < 8; xBlock++) {
350 if ((x + xBlock) >= Image->Width) {
351 igetc(); // Skip the pad byte.
352 continue;
353 }
354 Image->Data[DataOff] = igetc(); // Luminance value
355 DataOff++;
356 }
357 }
358 }
359 }
360 break;
361
362 case TPL_IA4:
363 // 8x4 tiles of 4-bit intensity and 4-bit alpha values
364 for (y = 0; y < Image->Height; y += 4) {
365 for (x = 0; x < Image->Width; x += 8) {
366 for (yBlock = 0; yBlock < 4; yBlock++) {
367 if ((y + yBlock) >= Image->Height) {
368 iseek(8, IL_SEEK_CUR); // Entire row of pad bytes skipped.
369 continue;
370 }
371 DataOff = Image->Bps * (y + yBlock) + Image->Bpp * x;
372 for (xBlock = 0; xBlock < 8; xBlock += 2) {
373 BytePixel = igetc();
374 if ((x + xBlock) >= Image->Width)
375 continue; // Already read the pad byte.
376 Image->Data[DataOff] = (BytePixel & 0xF0) | (BytePixel & 0xF0) >> 4;
377 Image->Data[DataOff+1] = (BytePixel & 0x0F) << 4 | (BytePixel & 0x0F);
378 DataOff += 2;
379 }
380 }
381 }
382 }
383 break;
384
385 case TPL_IA8:
386 // 4x4 tiles of 8-bit intensity and 8-bit alpha values
387 for (y = 0; y < Image->Height; y += 4) {
388 for (x = 0; x < Image->Width; x += 4) {
389 for (yBlock = 0; yBlock < 4; yBlock++) {
390 if ((y + yBlock) >= Image->Height) {
391 iseek(8, IL_SEEK_CUR); // Entire row of pad bytes skipped.
392 continue;
393 }
394 DataOff = Image->Bps * (y + yBlock) + Image->Bpp * x;
395 for (xBlock = 0; xBlock < 4; xBlock += 2) {
396 if ((x + xBlock) >= Image->Width) {
397 iseek(2, IL_SEEK_CUR); // Skip the pad bytes.
398 continue;
399 }
400 Image->Data[DataOff] = igetc();
401 Image->Data[DataOff+1] = igetc();
402 DataOff += 2;
403 }
404 }
405 }
406 }
407 break;
408
409 case TPL_RGB565:
410 // 4x4 tiles of RGB565 data
411 for (y = 0; y < Image->Height; y += 4) {
412 for (x = 0; x < Image->Width; x += 4) {
413 for (yBlock = 0; yBlock < 4; yBlock++) {
414 if ((y + yBlock) >= Image->Height) {
415 iseek(8, IL_SEEK_CUR); // Entire row of pad bytes skipped.
416 continue;
417 }
418 DataOff = Image->Bps * (y + yBlock) + Image->Bpp * x;
419 for (xBlock = 0; xBlock < 4; xBlock++) {
420 ShortPixel = GetBigUShort();
421 if ((x + xBlock) >= Image->Width)
422 continue; // Already read the pad byte.
423 Image->Data[DataOff] = ((ShortPixel & 0xF800) >> 8) | ((ShortPixel & 0xE000) >> 13); // Red
424 Image->Data[DataOff+1] = ((ShortPixel & 0x7E0) >> 3) | ((ShortPixel & 0x600) >> 9); // Green
425 Image->Data[DataOff+2] = ((ShortPixel & 0x1f) << 3) | ((ShortPixel & 0x1C) >> 2); // Blue
426 DataOff += 3;
427 }
428 }
429 }
430 }
431 break;
432
433 case TPL_RGB5A3:
434 // 4x4 tiles of either RGB5 or RGB4A3 depending on the MSB. 0x80
435 for (y = 0; y < Image->Height; y += 4) {
436 for (x = 0; x < Image->Width; x += 4) {
437 for (yBlock = 0; yBlock < 4; yBlock++) {
438 if ((y + yBlock) >= Image->Height) {
439 iseek(8, IL_SEEK_CUR); // Entire row of pad bytes skipped.
440 continue;
441 }
442 DataOff = Image->Bps * (y + yBlock) + Image->Bpp * x;
443 for (xBlock = 0; xBlock < 4; xBlock++) {
444 ShortPixel = GetBigUShort();
445 if ((x + xBlock) >= Image->Width)
446 continue; // Already read the pad byte.
447
448 if (ShortPixel & 0x8000) { // Check MSB.
449 // We have RGB5.
450 Image->Data[DataOff] = ((ShortPixel & 0x7C00) >> 7) | ((ShortPixel & 0x7000) >> 12); // Red
451 Image->Data[DataOff+1] = ((ShortPixel & 0x3E0) >> 2) | ((ShortPixel & 0x380) >> 7); // Green
452 Image->Data[DataOff+2] = ((ShortPixel & 0x1F) << 3) | ((ShortPixel & 0x1C) >> 2); // Blue
453 Image->Data[DataOff+3] = 0xFF; // I am just assuming that it is opaque.
454 }
455 else {
456 // We have RGB4A3.
457 Image->Data[DataOff] = ((ShortPixel & 0x7800) >> 7) | ((ShortPixel & 0x7800) >> 11); // Red
458 Image->Data[DataOff+1] = ((ShortPixel & 0x0780) >> 3) | ((ShortPixel & 0x0780) >> 7); // Green
459 Image->Data[DataOff+2] = ((ShortPixel & 0x0078) << 1) | ((ShortPixel & 0x0078) >> 3); // Blue
460 Image->Data[DataOff+3] = ((ShortPixel & 0x07) << 5) | ((ShortPixel & 0x07) << 2) | (ShortPixel >> 1); // Alpha
461 }
462 DataOff += 3;
463 }
464 }
465 }
466 }
467 break;
468
469 case TPL_RGBA8:
470 // 4x4 tiles of RGBA data
471 for (y = 0; y < Image->Height; y += 4) {
472 for (x = 0; x < Image->Width; x += 4) {
473 for (yBlock = 0; yBlock < 4; yBlock++) {
474 // Skip pad bytes at the bottom of the tile if any.
475 if ((y + yBlock) >= Image->Height) {
476 iseek(16, IL_SEEK_CUR); // Entire row of pad bytes skipped
477 continue;
478 }
479
480 // First it has the AR data.
481 DataOff = Image->Bps * (y + yBlock) + Image->Bpp * x;
482 for (xBlock = 0; xBlock < 4; xBlock++) {
483 if ((x + xBlock) >= Image->Width) {
484 iseek(2, IL_SEEK_CUR); // Skip pad bytes.
485 continue;
486 }
487 Image->Data[DataOff+3] = igetc(); // Alpha
488 Image->Data[DataOff] = igetc(); // Red
489 DataOff += 3;
490 }
491
492 // Then it has the GB data.
493 DataOff = Image->Bps * (y + yBlock) + Image->Bpp * x;
494 for (xBlock = 0; xBlock < 4 && x + xBlock < Image->Width; xBlock++) {
495 if ((x + xBlock) >= Image->Width) {
496 iseek(2, IL_SEEK_CUR); // Skip pad bytes.
497 continue;
498 }
499 Image->Data[DataOff+1] = igetc(); // Green
500 Image->Data[DataOff+2] = igetc(); // Blue
501 DataOff += 3;
502 }
503 }
504 }
505 }
506 break;
507
508 case TPL_CI4:
509 case TPL_CI8:
510 case TPL_CI14X2:
511 // Seek to the palette header.
512 if (iseek(PalOff, IL_SEEK_SET))
513 return IL_FALSE;
514 if (!TplGetIndexImage(Image, TexOff, DataFormat))
515 return IL_FALSE;
516 break;
517
518 case TPL_CMP:
519 // S3TC 2x2 blocks of 4x4 tiles. I am assuming that this is DXT1, since it is not specified in the specs.
520 // Most of this ended up being copied from il_dds.c, from the DecompressDXT1 function.
521 //@TODO: Make this/that code a bit more modular.
522 for (y = 0; y < Image->Height; y += 8) {
523 for (x = 0; x < Image->Width; x += 8) {
524 for (yBlock = 0; yBlock < 8 && (y + yBlock) < Image->Height; yBlock += 4) {
525 for (xBlock = 0; xBlock < 8 && (x + xBlock) < Image->Width; xBlock += 4) {
526 if (iread(CompData, 1, 8) != 8)
527 return IL_FALSE; //@TODO: Need to do any cleanup here?
528 color_0 = *((ILushort*)CompData);
529 UShort(&color_0);
530 color_1 = *((ILushort*)(CompData + 2));
531 UShort(&color_1);
532 DxtcReadColor(color_0, colours);
533 DxtcReadColor(color_1, colours + 1);
534 bitmask = ((ILuint*)CompData)[1];
535 UInt(&bitmask);
536
537 if (color_0 > color_1) {
538 // Four-color block: derive the other two colors.
539 // 00 = color_0, 01 = color_1, 10 = color_2, 11 = color_3
540 // These 2-bit codes correspond to the 2-bit fields
541 // stored in the 64-bit block.
542 colours[2].b = (2 * colours[0].b + colours[1].b + 1) / 3;
543 colours[2].g = (2 * colours[0].g + colours[1].g + 1) / 3;
544 colours[2].r = (2 * colours[0].r + colours[1].r + 1) / 3;
545 //colours[2].a = 0xFF;
546
547 colours[3].b = (colours[0].b + 2 * colours[1].b + 1) / 3;
548 colours[3].g = (colours[0].g + 2 * colours[1].g + 1) / 3;
549 colours[3].r = (colours[0].r + 2 * colours[1].r + 1) / 3;
550 colours[3].a = 0xFF;
551 }
552 else {
553 // Three-color block: derive the other color.
554 // 00 = color_0, 01 = color_1, 10 = color_2,
555 // 11 = transparent.
556 // These 2-bit codes correspond to the 2-bit fields
557 // stored in the 64-bit block.
558 colours[2].b = (colours[0].b + colours[1].b) / 2;
559 colours[2].g = (colours[0].g + colours[1].g) / 2;
560 colours[2].r = (colours[0].r + colours[1].r) / 2;
561 //colours[2].a = 0xFF;
562
563 colours[3].b = (colours[0].b + 2 * colours[1].b + 1) / 3;
564 colours[3].g = (colours[0].g + 2 * colours[1].g + 1) / 3;
565 colours[3].r = (colours[0].r + 2 * colours[1].r + 1) / 3;
566 colours[3].a = 0x00;
567 }
568
569 for (j = 0, k = 0; j < 4; j++) {
570 for (i = 0; i < 4; i++, k++) {
571 Select = (bitmask & (0x03 << k*2)) >> k*2;
572 col = &colours[Select];
573
574 if (((x + xBlock + i) < Image->Width) && ((y + yBlock + j) < Image->Height)) {
575 DataOff = (y + yBlock + j) * Image->Bps + (x + xBlock + i) * Image->Bpp;
576 Image->Data[DataOff + 0] = col->r;
577 Image->Data[DataOff + 1] = col->g;
578 Image->Data[DataOff + 2] = col->b;
579 Image->Data[DataOff + 3] = col->a;
580 }
581 }
582 }
583 }
584 }
585 }
586 }
587 break;
588 }
589 }
590
591 return ilFixImage();
592 }
593
594
TplGetIndexImage(ILimage * Image,ILuint TexOff,ILuint DataFormat)595 ILboolean TplGetIndexImage(ILimage *Image, ILuint TexOff, ILuint DataFormat)
596 {
597 ILushort NumPal, ShortPixel;
598 ILubyte LumVal, BytePixel;
599 ILuint PalFormat, PalOff, PalBpp, DataOff;
600 ILuint x, y, xBlock, yBlock, i;
601
602 NumPal = GetBigUShort();
603 iseek(2, IL_SEEK_CUR); // Do we need to do anything with the 'unpacked' entry? I see nothing in the specs about it.
604 PalFormat = GetBigUInt();
605
606 // Now we have to find out where the actual palette data is stored.
607 //@TODO: Do we need to set any errors here?
608 PalOff = GetBigUInt();
609 if (iseek(PalOff, IL_SEEK_SET))
610 return IL_FALSE;
611
612 switch (PalFormat)
613 {
614 case TPL_PAL_IA8:
615 Image->Pal.Palette = (ILubyte*)ialloc(NumPal * 4);
616 if (Image->Pal.Palette == NULL)
617 return IL_FALSE;
618 Image->Pal.PalType = IL_PAL_RGBA32; //@TODO: Use this format natively.
619 Image->Pal.PalSize = NumPal * 4;
620 PalBpp = 4;
621
622 for (i = 0; i < NumPal; i++) {
623 LumVal = igetc();
624 //@TODO: Do proper conversion of luminance, or support this format natively.
625 Image->Pal.Palette[i * 4] = LumVal; // Assign the luminance value.
626 Image->Pal.Palette[i * 4 + 1] = LumVal;
627 Image->Pal.Palette[i * 4 + 2] = LumVal;
628 Image->Pal.Palette[i * 4 + 3] = igetc(); // Get alpha value.
629 }
630 break;
631
632 case TPL_PAL_RGB565:
633 Image->Pal.Palette = (ILubyte*)ialloc(NumPal * 3);
634 if (Image->Pal.Palette == NULL)
635 return IL_FALSE;
636 Image->Pal.PalType = IL_PAL_RGB24;
637 Image->Pal.PalSize = NumPal * 3;
638 PalBpp = 3;
639
640 for (i = 0; i < NumPal; i++) {
641 ShortPixel = GetBigUShort();
642 // This is mostly the same code as in the TPL_RGB565 case.
643 Image->Pal.Palette[i*3] = ((ShortPixel & 0xF800) >> 8) | ((ShortPixel & 0xE000) >> 13); // Red
644 Image->Pal.Palette[i*3+1] = ((ShortPixel & 0x7E0) >> 3) | ((ShortPixel & 0x600) >> 9); // Green
645 Image->Pal.Palette[i*3+2] = ((ShortPixel & 0x1f) << 3) | ((ShortPixel & 0x1C) >> 2); // Blue
646 }
647 break;
648
649 case TPL_PAL_RGB5A3:
650 Image->Pal.Palette = (ILubyte*)ialloc(NumPal * 4);
651 if (Image->Pal.Palette == NULL)
652 return IL_FALSE;
653 Image->Pal.PalType = IL_PAL_RGBA32;
654 Image->Pal.PalSize = NumPal * 4;
655 PalBpp = 4;
656
657 for (i = 0; i < NumPal; i++) {
658 ShortPixel = GetBigUShort();
659 // This is mostly the same code as in the TPL_RGB565 case.
660 if (ShortPixel & 0x8000) { // Check MSB.
661 // We have RGB5.
662 Image->Pal.Palette[i*4] = ((ShortPixel & 0x7C00) >> 7) | ((ShortPixel & 0x7000) >> 12); // Red
663 Image->Pal.Palette[i*4+1] = ((ShortPixel & 0x3E0) >> 2) | ((ShortPixel & 0x380) >> 7); // Green
664 Image->Pal.Palette[i*4+2] = ((ShortPixel & 0x1F) << 3) | ((ShortPixel & 0x1C) >> 2); // Blue
665 Image->Pal.Palette[i*4+3] = 0xFF; // I am just assuming that it is opaque.
666 }
667 else {
668 // We have RGB4A3.
669 Image->Pal.Palette[i*4] = ((ShortPixel & 0x7800) >> 7) | ((ShortPixel & 0x7800) >> 11); // Red
670 Image->Pal.Palette[i*4+1] = ((ShortPixel & 0x0780) >> 3) | ((ShortPixel & 0x0780) >> 7); // Green
671 Image->Pal.Palette[i*4+2] = ((ShortPixel & 0x0078) << 1) | ((ShortPixel & 0x0078) >> 3); // Blue
672 Image->Pal.Palette[i*4+3] = ((ShortPixel & 0x07) << 5) | ((ShortPixel & 0x07) << 2) | (ShortPixel >> 1); // Alpha
673 }
674 }
675 break;
676
677 default:
678 ilSetError(IL_ILLEGAL_FILE_VALUE);
679 return IL_FALSE;
680 }
681
682 // Go back to the texture data.
683 if (iseek(TexOff, IL_SEEK_SET))
684 return IL_FALSE;
685
686 switch (DataFormat)
687 {
688 case TPL_CI4:
689 // 8x8 tiles of 4-bit color indices
690 // This is the exact same code as the TPL_I4 case.
691 for (y = 0; y < Image->Height; y += 8) {
692 for (x = 0; x < Image->Width; x += 8) {
693 for (yBlock = 0; yBlock < 8; yBlock++) {
694 if ((y + yBlock) >= Image->Height) {
695 iseek(8, IL_SEEK_CUR); // Entire row of pad bytes skipped.
696 continue;
697 }
698 DataOff = Image->Bps * (y + yBlock) + Image->Bpp * x;
699 for (xBlock = 0; xBlock < 8; xBlock += 2) {
700 BytePixel = igetc();
701 if ((x + xBlock) >= Image->Width)
702 continue; // Already read the pad byte.
703 Image->Data[DataOff] = (BytePixel & 0xF0) | (BytePixel & 0xF0) >> 4;
704 DataOff++;
705 // We have to do this check again, so we do not go past the last pixel in the image (ex. 1 pixel wide image).
706 if ((x + xBlock) >= Image->Width)
707 continue; // Already read the pad byte.
708 Image->Data[DataOff+1] = (BytePixel & 0x0F) << 4 | (BytePixel & 0x0F);
709 DataOff++;
710 }
711 }
712 }
713 }
714 break;
715
716 case TPL_CI8:
717 // 8x4 tiles of 8-bit intensity values
718 // This is the exact same code as the TPL_I8 case.
719 for (y = 0; y < Image->Height; y += 4) {
720 for (x = 0; x < Image->Width; x += 8) {
721 for (yBlock = 0; yBlock < 4; yBlock++) {
722 if ((y + yBlock) >= Image->Height) {
723 iseek(8, IL_SEEK_CUR); // Entire row of pad bytes skipped.
724 continue;
725 }
726 DataOff = Image->Bps * (y + yBlock) + Image->Bpp * x;
727 for (xBlock = 0; xBlock < 8; xBlock++) {
728 if ((x + xBlock) >= Image->Width) {
729 igetc(); // Skip the pad byte.
730 continue;
731 }
732 Image->Data[DataOff] = igetc(); // Color index
733 DataOff++;
734 }
735 }
736 }
737 }
738 break;
739
740
741 //@TODO: Convert to more formats than just RGBA.
742 case TPL_CI14X2:
743 // 4x4 tiles of 14-bit indices into a palette. Not supported at all in DevIL, since
744 // it has a huge palette (> 256 colors). Convert to RGBA.
745 for (y = 0; y < Image->Height; y += 4) {
746 for (x = 0; x < Image->Width; x += 4) {
747 for (yBlock = 0; yBlock < 4; yBlock++) {
748 if ((y + yBlock) >= Image->Height) {
749 iseek(8, IL_SEEK_CUR); // Entire row of pad bytes skipped.
750 continue;
751 }
752 DataOff = Image->Bps * (y + yBlock) + Image->Bpp * x;
753 for (xBlock = 0; xBlock < 4; xBlock++) {
754 if ((x + xBlock) >= Image->Width) {
755 GetBigUShort(); // Skip the pad short.
756 continue;
757 }
758 ShortPixel = GetBigUShort();
759 ShortPixel >>= 2; // Lower 2 bits are padding bits.
760 Image->Data[DataOff] = Image->Pal.Palette[ShortPixel * PalBpp];
761 Image->Data[DataOff+1] = Image->Pal.Palette[ShortPixel * PalBpp + 1];
762 Image->Data[DataOff+2] = Image->Pal.Palette[ShortPixel * PalBpp + 2];
763 if (PalFormat == TPL_PAL_RGB565)
764 Image->Data[DataOff+3] = 0xFF;
765 else
766 Image->Data[DataOff+3] = Image->Pal.Palette[ShortPixel * PalBpp + 3];
767 DataOff++;
768 }
769 }
770 }
771 }
772 // Get rid of the palette, since we no longer need it.
773 ifree(Image->Pal.Palette);
774 Image->Pal.PalType = IL_PAL_NONE;
775 Image->Pal.PalSize = 0;
776 break;
777
778 }
779
780 return IL_TRUE;
781 }
782 #endif//IL_NO_TPL
783