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_dcx.c
8 //
9 // Description: Reads from a .dcx file.
10 //
11 //-----------------------------------------------------------------------------
12
13
14 #include "il_internal.h"
15 #ifndef IL_NO_DCX
16 #include "il_dcx.h"
17 #include "il_manip.h"
18
19
20 //! Checks if the file specified in FileName is a valid .dcx file.
ilIsValidDcx(ILconst_string FileName)21 ILboolean ilIsValidDcx(ILconst_string FileName)
22 {
23 ILHANDLE DcxFile;
24 ILboolean bDcx = IL_FALSE;
25
26 if (!iCheckExtension(FileName, IL_TEXT("dcx"))) {
27 ilSetError(IL_INVALID_EXTENSION);
28 return bDcx;
29 }
30
31 DcxFile = iopenr(FileName);
32 if (DcxFile == NULL) {
33 ilSetError(IL_COULD_NOT_OPEN_FILE);
34 return bDcx;
35 }
36
37 bDcx = ilIsValidDcxF(DcxFile);
38 icloser(DcxFile);
39
40 return bDcx;
41 }
42
43
44 //! Checks if the ILHANDLE contains a valid .dcx file at the current position.
ilIsValidDcxF(ILHANDLE File)45 ILboolean ilIsValidDcxF(ILHANDLE File)
46 {
47 ILuint FirstPos;
48 ILboolean bRet;
49
50 iSetInputFile(File);
51 FirstPos = itell();
52 bRet = iIsValidDcx();
53 iseek(FirstPos, IL_SEEK_SET);
54
55 return bRet;
56 }
57
58
59 //! Checks if Lump is a valid .dcx lump.
ilIsValidDcxL(const void * Lump,ILuint Size)60 ILboolean ilIsValidDcxL(const void *Lump, ILuint Size)
61 {
62 iSetInputLump(Lump, Size);
63 return iIsValidDcx();
64 }
65
66
67 // Internal function obtain the .dcx header from the current file.
iGetDcxHead(DCXHEAD * Head)68 ILboolean iGetDcxHead(DCXHEAD *Head)
69 {
70 Head->Xmin = GetLittleUShort();
71 Head->Ymin = GetLittleUShort();
72 Head->Xmax = GetLittleUShort();
73 Head->Ymax = GetLittleUShort();
74 Head->HDpi = GetLittleUShort();
75 Head->VDpi = GetLittleUShort();
76 Head->Bps = GetLittleUShort();
77 Head->PaletteInfo = GetLittleUShort();
78 Head->HScreenSize = GetLittleUShort();
79 Head->VScreenSize = GetLittleUShort();
80
81 return IL_TRUE;
82 }
83
84
85 // Internal function to get the header and check it.
iIsValidDcx()86 ILboolean iIsValidDcx()
87 {
88 ILuint Signature;
89
90 if (iread(&Signature, 1, 4) != 4)
91 return IL_FALSE;
92 iseek(-4, IL_SEEK_CUR);
93
94 return (Signature == 987654321);
95 }
96
97
98 // Internal function used to check if the HEADER is a valid .dcx header.
99 // Should we also do a check on Header->Bpp?
iCheckDcx(DCXHEAD * Header)100 ILboolean iCheckDcx(DCXHEAD *Header)
101 {
102 ILuint Test, i;
103
104 // There are other versions, but I am not supporting them as of yet.
105 // Got rid of the Reserved check, because I've seen some .dcx files with invalid values in it.
106 if (Header->Manufacturer != 10 || Header->Version != 5 || Header->Encoding != 1/* || Header->Reserved != 0*/)
107 return IL_FALSE;
108
109 // See if the padding size is correct
110 Test = Header->Xmax - Header->Xmin + 1;
111 /*if (Header->Bpp >= 8) {
112 if (Test & 1) {
113 if (Header->Bps != Test + 1)
114 return IL_FALSE;
115 }
116 else {
117 if (Header->Bps != Test) // No padding
118 return IL_FALSE;
119 }
120 }*/
121
122 for (i = 0; i < 54; i++) {
123 if (Header->Filler[i] != 0)
124 return IL_FALSE;
125 }
126
127 return IL_TRUE;
128 }
129
130
131 //! Reads a .dcx file
ilLoadDcx(ILconst_string FileName)132 ILboolean ilLoadDcx(ILconst_string FileName)
133 {
134 ILHANDLE DcxFile;
135 ILboolean bDcx = IL_FALSE;
136
137 DcxFile = iopenr(FileName);
138 if (DcxFile == NULL) {
139 ilSetError(IL_COULD_NOT_OPEN_FILE);
140 return bDcx;
141 }
142
143 bDcx = ilLoadDcxF(DcxFile);
144 icloser(DcxFile);
145
146 return bDcx;
147 }
148
149
150 //! Reads an already-opened .dcx file
ilLoadDcxF(ILHANDLE File)151 ILboolean ilLoadDcxF(ILHANDLE File)
152 {
153 ILuint FirstPos;
154 ILboolean bRet;
155
156 iSetInputFile(File);
157 FirstPos = itell();
158 bRet = iLoadDcxInternal();
159 iseek(FirstPos, IL_SEEK_SET);
160
161 return bRet;
162 }
163
164
165 //! Reads from a memory "lump" that contains a .dcx
ilLoadDcxL(const void * Lump,ILuint Size)166 ILboolean ilLoadDcxL(const void *Lump, ILuint Size) {
167 iSetInputLump(Lump, Size);
168 return iLoadDcxInternal();
169 }
170
171
172 // Internal function used to load the .dcx.
iLoadDcxInternal()173 ILboolean iLoadDcxInternal()
174 {
175 DCXHEAD Header;
176 ILuint Signature, i, Entries[1024], Num = 0;
177 ILimage *Image, *Base;
178
179 if (iCurImage == NULL) {
180 ilSetError(IL_ILLEGAL_OPERATION);
181 return IL_FALSE;
182 }
183
184 if (!iIsValidDcx())
185 return IL_FALSE;
186 iread(&Signature, 1, 4);
187
188 do {
189 if (iread(&Entries[Num], 1, 4) != 4)
190 return IL_FALSE;
191 Num++;
192 } while (Entries[Num-1] != 0);
193
194 for (i = 0; i < Num; i++) {
195 iseek(Entries[i], IL_SEEK_SET);
196 iGetDcxHead(&Header);
197 /*if (!iCheckDcx(&Header)) {
198 ilSetError(IL_INVALID_FILE_HEADER);
199 return IL_FALSE;
200 }*/
201
202 Image = iUncompressDcx(&Header);
203 if (Image == NULL)
204 return IL_FALSE;
205
206 if (i == 0) {
207 ilTexImage(Image->Width, Image->Height, 1, Image->Bpp, Image->Format, Image->Type, Image->Data);
208 Base = iCurImage;
209 Base->Origin = IL_ORIGIN_UPPER_LEFT;
210 ilCloseImage(Image);
211 }
212 else {
213 iCurImage->Next = Image;
214 iCurImage = iCurImage->Next;
215 }
216 }
217
218 return ilFixImage();
219 }
220
221
222 // Internal function to uncompress the .dcx (all .dcx files are rle compressed)
iUncompressDcx(DCXHEAD * Header)223 ILimage *iUncompressDcx(DCXHEAD *Header)
224 {
225 ILubyte ByteHead, Colour, *ScanLine = NULL /* Only one plane */;
226 ILuint c, i, x, y;//, Read = 0;
227 ILimage *Image = NULL;
228
229 if (Header->Bpp < 8) {
230 /*ilSetError(IL_FORMAT_NOT_SUPPORTED);
231 return IL_FALSE;*/
232 return iUncompressDcxSmall(Header);
233 }
234
235 Image = ilNewImage(Header->Xmax - Header->Xmin + 1, Header->Ymax - Header->Ymin + 1, 1, Header->NumPlanes, 1);
236 if (Image == NULL)
237 return NULL;
238 /*if (!ilTexImage(Header->Xmax - Header->Xmin + 1, Header->Ymax - Header->Ymin + 1, 1, Header->NumPlanes, 0, IL_UNSIGNED_BYTE, NULL)) {
239 return IL_FALSE;
240 }*/
241 Image->Origin = IL_ORIGIN_UPPER_LEFT;
242
243 ScanLine = (ILubyte*)ialloc(Header->Bps);
244 if (ScanLine == NULL)
245 goto dcx_error;
246
247 switch (Image->Bpp)
248 {
249 case 1:
250 Image->Format = IL_COLOUR_INDEX;
251 Image->Pal.PalType = IL_PAL_RGB24;
252 Image->Pal.PalSize = 256 * 3; // Need to find out for sure...
253 Image->Pal.Palette = (ILubyte*)ialloc(Image->Pal.PalSize);
254 if (Image->Pal.Palette == NULL)
255 goto dcx_error;
256 break;
257 //case 2: // No 16-bit images in the dcx format!
258 case 3:
259 Image->Format = IL_RGB;
260 Image->Pal.Palette = NULL;
261 Image->Pal.PalSize = 0;
262 Image->Pal.PalType = IL_PAL_NONE;
263 break;
264 case 4:
265 Image->Format = IL_RGBA;
266 Image->Pal.Palette = NULL;
267 Image->Pal.PalSize = 0;
268 Image->Pal.PalType = IL_PAL_NONE;
269 break;
270
271 default:
272 ilSetError(IL_ILLEGAL_FILE_VALUE);
273 goto dcx_error;
274 }
275
276
277 /*StartPos = itell();
278 Compressed = (ILubyte*)ialloc(Image->SizeOfData * 4 / 3);
279 iread(Compressed, 1, Image->SizeOfData * 4 / 3);
280
281 for (y = 0; y < Image->Height; y++) {
282 for (c = 0; c < Image->Bpp; c++) {
283 x = 0;
284 while (x < Header->Bps) {
285 ByteHead = Compressed[Read++];
286 if ((ByteHead & 0xC0) == 0xC0) {
287 ByteHead &= 0x3F;
288 Colour = Compressed[Read++];
289 for (i = 0; i < ByteHead; i++) {
290 ScanLine[x++] = Colour;
291 }
292 }
293 else {
294 ScanLine[x++] = ByteHead;
295 }
296 }
297
298 for (x = 0; x < Image->Width; x++) { // 'Cleverly' ignores the pad bytes ;)
299 Image->Data[y * Image->Bps + x * Image->Bpp + c] = ScanLine[x];
300 }
301 }
302 }
303
304 ifree(Compressed);
305 iseek(StartPos + Read, IL_SEEK_SET);*/
306
307 //changed 2003-09-01
308 if (iGetHint(IL_MEM_SPEED_HINT) == IL_FASTEST)
309 iPreCache(iCurImage->SizeOfData);
310
311 //TODO: because the .pcx-code was broken this
312 //code is probably broken, too
313 for (y = 0; y < Image->Height; y++) {
314 for (c = 0; c < Image->Bpp; c++) {
315 x = 0;
316 while (x < Header->Bps) {
317 if (iread(&ByteHead, 1, 1) != 1) {
318 iUnCache();
319 goto dcx_error;
320 }
321 if ((ByteHead & 0xC0) == 0xC0) {
322 ByteHead &= 0x3F;
323 if (iread(&Colour, 1, 1) != 1) {
324 iUnCache();
325 goto dcx_error;
326 }
327 for (i = 0; i < ByteHead; i++) {
328 ScanLine[x++] = Colour;
329 }
330 }
331 else {
332 ScanLine[x++] = ByteHead;
333 }
334 }
335
336 for (x = 0; x < Image->Width; x++) { // 'Cleverly' ignores the pad bytes ;)
337 Image->Data[y * Image->Bps + x * Image->Bpp + c] = ScanLine[x];
338 }
339 }
340 }
341
342 iUnCache();
343
344
345 ifree(ScanLine);
346
347 // Read in the palette
348 if (Image->Bpp == 1) {
349 ByteHead = igetc(); // the value 12, because it signals there's a palette for some reason...
350 // We should do a check to make certain it's 12...
351 if (ByteHead != 12)
352 iseek(-1, IL_SEEK_CUR);
353 if (iread(Image->Pal.Palette, 1, Image->Pal.PalSize) != Image->Pal.PalSize) {
354 ilCloseImage(Image);
355 return NULL;
356 }
357 }
358
359 return Image;
360
361 dcx_error:
362 ifree(ScanLine);
363 ilCloseImage(Image);
364 return NULL;
365 }
366
367
iUncompressDcxSmall(DCXHEAD * Header)368 ILimage *iUncompressDcxSmall(DCXHEAD *Header)
369 {
370 ILuint i = 0, j, k, c, d, x, y, Bps;
371 ILubyte HeadByte, Colour, Data = 0, *ScanLine = NULL;
372 ILimage *Image;
373
374 Image = ilNewImage(Header->Xmax - Header->Xmin + 1, Header->Ymax - Header->Ymin + 1, 1, Header->NumPlanes, 1);
375 if (Image == NULL)
376 return NULL;
377
378 /*if (!ilTexImage(Header->Xmax - Header->Xmin + 1, Header->Ymax - Header->Ymin + 1, 1, 1, 0, IL_UNSIGNED_BYTE, NULL)) {
379 return IL_FALSE;
380 }*/
381 Image->Origin = IL_ORIGIN_UPPER_LEFT;
382
383 switch (Header->NumPlanes)
384 {
385 case 1:
386 Image->Format = IL_LUMINANCE;
387 break;
388 case 4:
389 Image->Format = IL_COLOUR_INDEX;
390 break;
391 default:
392 ilSetError(IL_ILLEGAL_FILE_VALUE);
393 ilCloseImage(Image);
394 return NULL;
395 }
396
397 if (Header->NumPlanes == 1) {
398 for (j = 0; j < Image->Height; j++) {
399 i = 0;
400 while (i < Image->Width) {
401 if (iread(&HeadByte, 1, 1) != 1)
402 goto file_read_error;
403 if (HeadByte >= 192) {
404 HeadByte -= 192;
405 if (iread(&Data, 1, 1) != 1)
406 goto file_read_error;
407
408 for (c = 0; c < HeadByte; c++) {
409 k = 128;
410 for (d = 0; d < 8 && i < Image->Width; d++) {
411 Image->Data[j * Image->Width + i++] = (!!(Data & k) == 1 ? 255 : 0);
412 k >>= 1;
413 }
414 }
415 }
416 else {
417 k = 128;
418 for (c = 0; c < 8 && i < Image->Width; c++) {
419 Image->Data[j * Image->Width + i++] = (!!(HeadByte & k) == 1 ? 255 : 0);
420 k >>= 1;
421 }
422 }
423 }
424 if (Data != 0)
425 igetc(); // Skip pad byte if last byte not a 0
426 }
427 }
428 else { // 4-bit images
429 Bps = Header->Bps * Header->NumPlanes * 2;
430 Image->Pal.Palette = (ILubyte*)ialloc(16 * 3); // Size of palette always (48 bytes).
431 Image->Pal.PalSize = 16 * 3;
432 Image->Pal.PalType = IL_PAL_RGB24;
433 ScanLine = (ILubyte*)ialloc(Bps);
434 if (Image->Pal.Palette == NULL || ScanLine == NULL) {
435 ifree(ScanLine);
436 ilCloseImage(Image);
437 return NULL;
438 }
439
440 memcpy(Image->Pal.Palette, Header->ColMap, 16 * 3);
441 imemclear(Image->Data, Image->SizeOfData); // Since we do a += later.
442
443 for (y = 0; y < Image->Height; y++) {
444 for (c = 0; c < Header->NumPlanes; c++) {
445 x = 0;
446 while (x < Bps) {
447 if (iread(&HeadByte, 1, 1) != 1)
448 goto file_read_error;
449 if ((HeadByte & 0xC0) == 0xC0) {
450 HeadByte &= 0x3F;
451 if (iread(&Colour, 1, 1) != 1)
452 goto file_read_error;
453 for (i = 0; i < HeadByte; i++) {
454 k = 128;
455 for (j = 0; j < 8; j++) {
456 ScanLine[x++] = !!(Colour & k);
457 k >>= 1;
458 }
459 }
460 }
461 else {
462 k = 128;
463 for (j = 0; j < 8; j++) {
464 ScanLine[x++] = !!(HeadByte & k);
465 k >>= 1;
466 }
467 }
468 }
469
470 for (x = 0; x < Image->Width; x++) { // 'Cleverly' ignores the pad bytes. ;)
471 Image->Data[y * Image->Width + x] += ScanLine[x] << c;
472 }
473 }
474 }
475 ifree(ScanLine);
476 }
477
478 return Image;
479
480 file_read_error:
481 ifree(ScanLine);
482 ilCloseImage(Image);
483 return NULL;
484 }
485
486 #endif//IL_NO_DCX
487