]>
Commit | Line | Data |
---|---|---|
1 | /* converts a QT RAW 8-bit image file into format the kernel panic ui expects. | |
2 | * | |
3 | * to build: cc -o qtif2kraw qtif2kraw.c | |
4 | */ | |
5 | ||
6 | #include <stdio.h> | |
7 | #include <unistd.h> | |
8 | #include <stdlib.h> | |
9 | #include <sys/types.h> | |
10 | #include <fcntl.h> | |
11 | #include <string.h> | |
12 | ||
13 | int EncodeImage( unsigned char * data, int pixels, unsigned char * fileArr ); | |
14 | int findIndexNearMatch( unsigned int color24 ); | |
15 | unsigned int findColor24NearMatch( unsigned int color24 ); | |
16 | unsigned char findIndexMatch( unsigned int color24 ); | |
17 | int convert8toGrey( unsigned char * data, unsigned int size ); | |
18 | int convert8bitIndexto8( unsigned char * data, int height, int width, unsigned char ** dout ); | |
19 | unsigned int * CreateCLUTarry( unsigned char * raw_clut ); | |
20 | unsigned int * ReplaceCLUT( char * iname ); | |
21 | ||
22 | #define offsetof(type, field) ((size_t)(&((type *)0)->field)) | |
23 | ||
24 | struct panicimage { | |
25 | unsigned int pd_sum; | |
26 | unsigned int pd_dataSize; | |
27 | unsigned int pd_tag; | |
28 | unsigned short pd_width; | |
29 | unsigned short pd_height; | |
30 | unsigned char pd_depth; | |
31 | unsigned char pd_info_height; | |
32 | unsigned char pd_info_color[2]; | |
33 | unsigned char data[]; | |
34 | }; | |
35 | ||
36 | ||
37 | void | |
38 | usage( int type ) { | |
39 | printf( | |
40 | "\n" | |
41 | "Usage:\n" | |
42 | "\tqtif2kraw -i <.qtif> -o <.kraw> [operands ...]\n\n" | |
43 | "\tThe following operands are available\n\n" | |
44 | "\t-h\t\tDisplay full help information\n" | |
45 | "\t-i <file>\tUse file containing QuickTime uncompressed raw image as\n" | |
46 | "\t\t\tthe panic dialog (8 bit only)\n" | |
47 | "\t-o <file>\tWrite the output as a compressed kernel RAW image suitable\n" | |
48 | "\t\t\tfor loading into the kernel\n" | |
49 | "\t-c <file>\tUse file containing 256 RGB values for 8-bit indexed \n" | |
50 | "\t\t\tlookups, overrides built-in appleClut8\n" | |
51 | "\t-fg <color>\tForeground color of font used for panic information in\n" | |
52 | "\t\t\t24-bits, default 0xFFFFFF (100%% white)\n" | |
53 | "\t-bg <color>\tBackground color of font used for panic information in\n" | |
54 | "\t\t\t24-bits, default 0x222222 (13%% white, dark gray)\n" | |
55 | "\t-n <lines>\tNumber of lines that have been reserved to display the\n" | |
56 | "\t\t\tpanic information, must be at least 20\n" | |
57 | "\n\n" ); | |
58 | } | |
59 | ||
60 | ||
61 | #include "appleclut8.h" | |
62 | #include "../iso_font.c" | |
63 | ||
64 | struct QTHeader { | |
65 | long idSize; /* total size of ImageDescription including extra data ( CLUTs and other per sequence data ) */ | |
66 | long cType; /* 'raw '; what kind of codec compressed this data */ | |
67 | long resvd1; /* reserved for Apple use */ | |
68 | short resvd2; /* reserved for Apple use */ | |
69 | short dataRefIndex; /* set to zero */ | |
70 | short version; /* which version is this data */ | |
71 | short revisionLevel; /* what version of that codec did this */ | |
72 | long vendor; /* whose codec compressed this data */ | |
73 | long temporalQuality; /* what was the temporal quality factor */ | |
74 | long spatialQuality; /* what was the spatial quality factor */ | |
75 | short width; /* how many pixels wide is this data */ | |
76 | short height; /* how many pixels high is this data */ | |
77 | long hRes; /* horizontal resolution */ | |
78 | long vRes; /* vertical resolution */ | |
79 | long dataSize; /* if known, the size of data for this image descriptor */ | |
80 | short frameCount; /* number of frames this description applies to */ | |
81 | char name[32]; /* name of codec ( in case not installed ) */ | |
82 | short depth; /* what depth is this data (1-32) or ( 33-40 grayscale ) */ | |
83 | short clutID; /* clut id or if 0 clut follows or -1 if no clut */ | |
84 | } image_header; | |
85 | ||
86 | static unsigned int mismatchClut[256]; | |
87 | static int nextmis = -1, neargrey = 0, cvt2grey = 0, exactmatch=0; | |
88 | static int grey = 0, debug = 0; | |
89 | static unsigned char fg, bg; | |
90 | unsigned int * panic_clut = NULL; | |
91 | static char * clutin = NULL; | |
92 | ||
93 | union colors { | |
94 | unsigned int c24; | |
95 | unsigned char rgb[4]; | |
96 | struct { | |
97 | unsigned char dummy; | |
98 | unsigned char red; | |
99 | unsigned char green; | |
100 | unsigned char blue; | |
101 | } clut; | |
102 | }; | |
103 | ||
104 | int | |
105 | main( int argc, char *argv[] ) | |
106 | { | |
107 | char *file = NULL; | |
108 | char *kraw = NULL; | |
109 | FILE * stream; | |
110 | unsigned char * data; | |
111 | unsigned short width = 0, height = 0; | |
112 | unsigned char depth = 0, lines = 20; | |
113 | unsigned int i, pixels, sum, encodedSize, fg24= 0xFFFFFF, bg24=0x222222; | |
114 | unsigned char *fileArr; | |
115 | int next; | |
116 | ||
117 | ||
118 | // pull apart the arguments | |
119 | for( next = 1; next < argc; next++ ) | |
120 | { | |
121 | if (strcmp(argv[next], "-i") == 0) // image file in raw QT uncompressed format (.qtif) | |
122 | file = argv[++next]; | |
123 | ||
124 | else if (strcmp(argv[next], "-o") == 0) // output file for WHD image | |
125 | kraw = argv[++next]; | |
126 | ||
127 | else if (strcmp(argv[next], "-n") == 0) // numbers of reserved lines | |
128 | lines = atoi(argv[++next]); | |
129 | else if (strcmp(argv[next], "-fg") == 0) // foreground color in 24 bits | |
130 | sscanf(argv[++next], "%i", &fg24); | |
131 | else if (strcmp(argv[next], "-bg") == 0) // background color in 24 bits | |
132 | sscanf(argv[++next], "%i", &bg24); | |
133 | else if (strcmp(argv[next], "-c") == 0) // input file for clut | |
134 | clutin = argv[++next]; | |
135 | else if (strcmp(argv[next], "-h") == 0) // display more help | |
136 | { usage(1); exit(1); } | |
137 | ||
138 | else if (strcmp(argv[next], "-debug") == 0) // verbose | |
139 | debug++; | |
140 | } | |
141 | ||
142 | if (!(file || kraw) ) { | |
143 | usage(0); | |
144 | exit(1); | |
145 | } | |
146 | ||
147 | printf("\n"); | |
148 | ||
149 | panic_clut = appleClut8; | |
150 | ||
151 | if ( clutin ) | |
152 | { | |
153 | panic_clut = ReplaceCLUT( clutin ); | |
154 | printf("Built-in CLUT has been replaced with %s...\n", clutin); | |
155 | } | |
156 | ||
157 | fg = findIndexNearMatch(fg24); | |
158 | bg = findIndexNearMatch(bg24); | |
159 | ||
160 | // Begin to process the image | |
161 | ||
162 | if( file == NULL) | |
163 | { | |
164 | printf("No image file was processed...\n\n"); | |
165 | exit(0); | |
166 | } | |
167 | ||
168 | ||
169 | printf("Verifing image file...\n"); | |
170 | if ( file != NULL ) | |
171 | { | |
172 | stream = fopen(file, "r"); | |
173 | if (!stream) { | |
174 | fprintf(stderr, "Err: could not open .qtif image file.\n\n"); | |
175 | exit(1); | |
176 | } | |
177 | ||
178 | { | |
179 | long hdr_off; | |
180 | long hdr_type; | |
181 | int rc; | |
182 | ||
183 | if ( ! fread((void *) &hdr_off, sizeof(long), 1, stream) ) goto errQTimage; | |
184 | if ( ! fread((void *) &hdr_type, sizeof(long), 1, stream) ) goto errQTimage; | |
185 | ||
186 | if ( hdr_type != 'idat' ) goto errQTimage; | |
187 | ||
188 | if ( fseek(stream, hdr_off, SEEK_SET) ) goto errQTimage; | |
189 | if ( ! fread((void *) &hdr_off, sizeof(long), 1, stream) ) goto errQTimage; | |
190 | if ( ! fread((void *) &hdr_type, sizeof(long), 1, stream) ) goto errQTimage; | |
191 | ||
192 | if ( hdr_type != 'idsc' ) goto errQTimage; | |
193 | ||
194 | rc = fread((void *) &image_header, sizeof(image_header), 1, stream); | |
195 | if ( !rc && !feof(stream) ) goto errQTimage; | |
196 | if ( image_header.cType != 'raw ' ) goto errQTimage; | |
197 | if ( image_header.depth != 8 ) goto errQTimage; | |
198 | ||
199 | ||
200 | width = image_header.width; | |
201 | height = image_header.height; | |
202 | depth = image_header.depth; | |
203 | ||
204 | printf("Image info: width: %d height: %d depth: %d...\n", width, height, depth); | |
205 | ||
206 | if (!(width && height && depth)) { | |
207 | fprintf(stderr,"Err: Invalid image file header (width, height, or depth is 0)\n"); | |
208 | exit(1); | |
209 | } | |
210 | } | |
211 | ||
212 | if ( !(data = (char *)malloc(image_header.dataSize))) { | |
213 | fprintf(stderr,"Err: Couldn't malloc file data (%ld bytes)... bailing.\n", image_header.dataSize); | |
214 | exit(1); | |
215 | } | |
216 | ||
217 | // Read the image data | |
218 | if ( fseek(stream, 8, SEEK_SET) ) goto errQTimage; | |
219 | if ( ! fread((void *) data, image_header.dataSize, 1, stream) ) goto errQTimage; | |
220 | fclose( stream ); | |
221 | ||
222 | pixels = image_header.dataSize; | |
223 | ||
224 | if ( grey == 1 ) | |
225 | pixels = convert8toGrey( data, image_header.dataSize ); | |
226 | ||
227 | printf("Converting image file to 8 bit raw...\n"); | |
228 | pixels = convert8bitIndexto8( data, height, width, &data ); | |
229 | image_header.dataSize = pixels; | |
230 | depth = 1; | |
231 | ||
232 | printf("Converted %d pixels%s...\n", pixels/depth, ((grey==1)?" to grayscale":"")); | |
233 | if ( exactmatch > 0 ) | |
234 | printf("Found %d color mathces in CLUT...\n", exactmatch); | |
235 | if ( cvt2grey > 0 ) | |
236 | printf("Converted %d colors to gray...\n", cvt2grey); | |
237 | if ( neargrey > 0 ) | |
238 | printf("Adjusted %d grays to best match...\n", neargrey); | |
239 | if ( nextmis > 0 ) | |
240 | printf("Total of %d seperate color mismatches...\n", nextmis); | |
241 | } | |
242 | ||
243 | printf("Encoding image file...\n"); | |
244 | ||
245 | if (!(fileArr = (unsigned char *) malloc(pixels))) { | |
246 | fprintf(stderr,"Err: Couldn't malloc fileArr (%d pixels)... bailing.\n", pixels); | |
247 | exit(1); | |
248 | } | |
249 | ||
250 | encodedSize = EncodeImage( data, pixels, fileArr ); | |
251 | ||
252 | if ( encodedSize >= pixels ) | |
253 | { | |
254 | printf("Skipping encoding...\n"); | |
255 | } | |
256 | ||
257 | for (sum=0,i=0; i<encodedSize; i++) | |
258 | { | |
259 | sum += fileArr[i]; | |
260 | sum <<= sum&1; | |
261 | } | |
262 | ||
263 | // write raw image suitable for kernel panic dialog | |
264 | if ( kraw ) | |
265 | { | |
266 | FILE * ostream; | |
267 | unsigned int tag; | |
268 | ||
269 | if ( (ostream = fopen(kraw, "wb")) == NULL ) { | |
270 | fprintf(stderr,"Err: Could not open output file %s.\n\n", kraw); | |
271 | exit(1); | |
272 | } | |
273 | ||
274 | printf("Writing to binary panic dialog file %s, which is suitable for loading into kernel...\n", kraw); | |
275 | ||
276 | tag = 'RNMp'; // Raw NMage for Panic dialog | |
277 | depth = 1; // only CLUT is supported | |
278 | ||
279 | fwrite(&sum, sizeof(sum), 1, ostream); | |
280 | sum = encodedSize; | |
281 | encodedSize += (256*3); | |
282 | fwrite(&encodedSize, sizeof(encodedSize), 1, ostream); | |
283 | encodedSize = sum; | |
284 | fwrite(&tag, sizeof(tag), 1, ostream); | |
285 | fwrite(&width, sizeof(width), 1, ostream); | |
286 | fwrite(&height, sizeof(height), 1, ostream); | |
287 | fwrite(&depth, sizeof(depth), 1, ostream); | |
288 | fwrite(&lines, sizeof(lines), 1, ostream); | |
289 | fwrite(&fg, sizeof(fg), 1, ostream); | |
290 | fwrite(&bg, sizeof(bg), 1, ostream); | |
291 | fwrite(fileArr, encodedSize, 1, ostream); | |
292 | ||
293 | for ( i=0; i<256; i++) | |
294 | { | |
295 | union colors c; | |
296 | unsigned char arr[3]; | |
297 | ||
298 | c.c24 = panic_clut[i]; | |
299 | ||
300 | arr[0] = c.clut.red; | |
301 | arr[1] = c.clut.green; | |
302 | arr[2] = c.clut.blue; | |
303 | fwrite(arr, 3, 1, ostream); | |
304 | } | |
305 | fclose(ostream); | |
306 | } | |
307 | ||
308 | return 0; | |
309 | ||
310 | errQTimage: | |
311 | fprintf(stderr,"Err: Input image must be in the QuickTime Raw Uncompressed 256 Colors format\n"); | |
312 | exit(1); | |
313 | } | |
314 | ||
315 | ||
316 | ||
317 | #define RUN_MAX ((1<<20)-1) | |
318 | ||
319 | union RunData { | |
320 | unsigned int i; | |
321 | unsigned char c[4]; | |
322 | }; | |
323 | ||
324 | unsigned int encode_rle( | |
325 | unsigned char * fileArr, | |
326 | unsigned int filePos, | |
327 | unsigned int quantity, | |
328 | union RunData * value, | |
329 | int depth); | |
330 | ||
331 | int | |
332 | compareruns( unsigned char * data, unsigned int * index, unsigned int max, union RunData * currP, int * depth ) | |
333 | { | |
334 | union RunData * nextP; | |
335 | static int retc = 0; | |
336 | ||
337 | if ( currP == NULL || data == NULL ) | |
338 | { | |
339 | retc = 0; | |
340 | goto Leave; | |
341 | } | |
342 | ||
343 | if ( (*index+*depth) > max ) | |
344 | { | |
345 | *depth = 1; | |
346 | retc = 0; | |
347 | goto Leave; | |
348 | } | |
349 | ||
350 | nextP = (union RunData *) &data[*index]; | |
351 | ||
352 | if ( retc == 1 ) | |
353 | { | |
354 | // check current data against current depth | |
355 | switch ( *depth ) | |
356 | { | |
357 | case 1: | |
358 | if ( nextP->c[0] == currP->c[0] ) | |
359 | goto Leave; | |
360 | break; | |
361 | case 2: | |
362 | if ( nextP->c[0] == currP->c[0] && | |
363 | nextP->c[1] == currP->c[1] ) | |
364 | goto Leave; | |
365 | break; | |
366 | case 3: | |
367 | if ( nextP->c[0] == currP->c[0] && | |
368 | nextP->c[1] == currP->c[1] && | |
369 | nextP->c[2] == currP->c[2] ) | |
370 | goto Leave; | |
371 | break; | |
372 | case 4: | |
373 | if ( nextP->c[0] == currP->c[0] && | |
374 | nextP->c[1] == currP->c[1] && | |
375 | nextP->c[2] == currP->c[2] && | |
376 | nextP->c[3] == currP->c[3] ) | |
377 | goto Leave; | |
378 | break; | |
379 | } | |
380 | ||
381 | retc = 0; | |
382 | goto Leave; | |
383 | } | |
384 | ||
385 | // start of a new pattern match begine with depth = 1 | |
386 | ||
387 | if ( (*index+6) <= max ) | |
388 | { | |
389 | // We have at least 8 bytes left in the buffer starting from currP | |
390 | #if 1 | |
391 | nextP = (union RunData *) &data[*index+3]; | |
392 | if ( nextP->c[0] == currP->c[0] && | |
393 | nextP->c[1] == currP->c[1] && | |
394 | nextP->c[2] == currP->c[2] && | |
395 | nextP->c[3] == currP->c[3] ) | |
396 | { | |
397 | // check if they are all the same value | |
398 | if ( currP->c[0] == currP->c[1] && | |
399 | currP->c[1] == currP->c[2] && | |
400 | currP->c[2] == currP->c[3] ) | |
401 | { // if so, leave at depth = 1 | |
402 | retc = 1; | |
403 | *depth = 1; | |
404 | goto Leave; | |
405 | } | |
406 | ||
407 | if (debug>2) printf("Found 4 at %x\n", *index); | |
408 | retc = 1; | |
409 | *depth = 4; | |
410 | *index += 3; | |
411 | goto Leave; | |
412 | } | |
413 | ||
414 | nextP = (union RunData *) &data[*index+2]; | |
415 | if ( nextP->c[0] == currP->c[0] && | |
416 | nextP->c[1] == currP->c[1] && | |
417 | nextP->c[2] == currP->c[2] ) | |
418 | { | |
419 | // check if they are all the same value | |
420 | if ( currP->c[0] == currP->c[1] && | |
421 | currP->c[1] == currP->c[2] ) | |
422 | { // if so, leave at depth = 1 | |
423 | retc = 1; | |
424 | *depth = 1; | |
425 | goto Leave; | |
426 | } | |
427 | ||
428 | if (debug>2) printf("Found 3 at %x\n", *index); | |
429 | retc = 1; | |
430 | *depth = 3; | |
431 | *index += 2; | |
432 | goto Leave; | |
433 | } | |
434 | ||
435 | nextP = (union RunData *) &data[*index+1]; | |
436 | if ( nextP->c[0] == currP->c[0] && | |
437 | nextP->c[1] == currP->c[1] ) | |
438 | { | |
439 | // check if they are all the same value | |
440 | if ( currP->c[0] == currP->c[1] ) | |
441 | { // if so, leave at depth = 1 | |
442 | retc = 1; | |
443 | *depth = 1; | |
444 | goto Leave; | |
445 | } | |
446 | ||
447 | if (debug>2) printf("Found 2 at %x\n", *index); | |
448 | retc = 1; | |
449 | *depth = 2; | |
450 | *index += 1; | |
451 | goto Leave; | |
452 | } | |
453 | ||
454 | #endif | |
455 | nextP = (union RunData *) &data[*index]; | |
456 | ||
457 | } | |
458 | ||
459 | if ( nextP->c[0] == currP->c[0] ) | |
460 | retc = 1; | |
461 | else | |
462 | retc = 0; | |
463 | ||
464 | Leave: | |
465 | ||
466 | if ( retc == 1 ) | |
467 | *index += *depth; | |
468 | ||
469 | return retc; | |
470 | } | |
471 | ||
472 | int | |
473 | EncodeImage( unsigned char * data, int pixels, unsigned char * fileArr ) | |
474 | { | |
475 | union RunData * currP, * norunP ; | |
476 | int i, depth; | |
477 | unsigned int filePos, run, nomatchrun; | |
478 | ||
479 | currP = NULL; | |
480 | norunP = NULL; | |
481 | nomatchrun = 0; | |
482 | filePos = 0; // position in the file we're writing out | |
483 | run = 1; | |
484 | depth = 1; | |
485 | ||
486 | currP = (union RunData *)&data[0]; // start a new run | |
487 | for (i=1; i<pixels;) { | |
488 | if ( compareruns( data, &i, pixels, currP, &depth ) ) | |
489 | run++; | |
490 | else { | |
491 | if ( (run*depth) > 2 ) { | |
492 | unsigned char * p = (unsigned char *)norunP; | |
493 | ||
494 | if( nomatchrun ) { | |
495 | while (nomatchrun) { | |
496 | int cnt; | |
497 | ||
498 | cnt = (nomatchrun > 127) ? 127 : nomatchrun; | |
499 | fileArr[filePos++] = cnt; | |
500 | nomatchrun -= cnt; | |
501 | ||
502 | while ( cnt-- ) | |
503 | fileArr[filePos++] = *p++; | |
504 | } | |
505 | } | |
506 | ||
507 | filePos += encode_rle(fileArr, filePos, run, currP, depth); | |
508 | ||
509 | norunP = NULL; | |
510 | } else { | |
511 | nomatchrun+=run; | |
512 | } | |
513 | ||
514 | currP = (union RunData *)&data[i]; // start a new run | |
515 | ||
516 | if( norunP == NULL ) { | |
517 | nomatchrun = 0; | |
518 | norunP = currP; | |
519 | } | |
520 | ||
521 | depth = 1; // switch back to a single byte depth | |
522 | run = 1; // thee is always at least one entry | |
523 | i++; // point to next byte | |
524 | } | |
525 | } | |
526 | ||
527 | if( nomatchrun ) { | |
528 | unsigned char * p = (unsigned char *)norunP; | |
529 | while (nomatchrun) { | |
530 | int cnt; | |
531 | ||
532 | cnt = (nomatchrun > 127) ? 127 : nomatchrun; | |
533 | fileArr[filePos++] = cnt; | |
534 | nomatchrun -= cnt; | |
535 | ||
536 | while ( cnt-- ) | |
537 | fileArr[filePos++] = *p++; | |
538 | } | |
539 | } | |
540 | ||
541 | // write out any run that was in progress | |
542 | if (run > 0) { | |
543 | filePos += encode_rle(fileArr, filePos, run, currP, depth); | |
544 | } | |
545 | ||
546 | return filePos; | |
547 | } | |
548 | ||
549 | /* encode_rle applies a "modified-RLE encoding to a given image. The encoding works as follows: | |
550 | ||
551 | The quantity is described in the first byte. If the MSB is zero, then the next seven bits | |
552 | are the quantity. If the MSB is set, bits 0-3 of the quantity are in the least significant bits. | |
553 | If bit 5 is set, then the quantity is further described in the next byte, where an additional | |
554 | 7 bits (4-10) worth of quantity will be found. If the MSB of this byte is set, then an additional | |
555 | 7 bits (11-17) worth of quantity will be found in the next byte. This repeats until the MSB of | |
556 | a quantity byte is zero, thus ending the chain. | |
557 | ||
558 | The value is described in the first byte. If the MSB is zero, then the value is in the next byte. | |
559 | If the MSB is set, then bits 5/6 describe the number of value bytes following the quantity bytes. | |
560 | ||
561 | encodings are: (q = quantity, v = value, c = quantity continues) | |
562 | ||
563 | Byte 1 Byte 2 Byte 3 Byte 4 Byte 5 Byte 6 Byte 7 Byte 8 | |
564 | case 1: [ 0 q6-q0 ] [ v7-v0 ] | |
565 | case 2: [ 1 0 0 c q3-q0 ] [ c q10-q4 ] [ c q17-q11 ] [ q24-q18 ] [ v7-v0 ] | |
566 | case 3: [ 1 0 1 c q3-q0 ] [ c q10-q4 ] [ c q17-q11 ] [ q24-q18 ] [ v7-v0 ] [ v7-v0 ] | |
567 | case 4: [ 1 1 0 c q3-q0 ] [ c q10-q4 ] [ c q17-q11 ] [ q24-q18 ] [ v7-v0 ] [ v7-v0 ] [ v7-v0 ] | |
568 | case 5: [ 1 1 1 c q3-q0 ] [ c q10-q4 ] [ c q17-q11 ] [ q24-q18 ] [ v7-v0 ] [ v7-v0 ] [ v7-v0 ] [ v7-v0 ] | |
569 | */ | |
570 | ||
571 | unsigned int | |
572 | encode_length(unsigned char * fileArr, unsigned int filePos, unsigned int quantity, unsigned int mask) | |
573 | { | |
574 | unsigned char single_mask = 0x0F; | |
575 | unsigned char double_mask = 0x7F; | |
576 | unsigned int slots_used = 0; | |
577 | ||
578 | fileArr[filePos] = mask | (quantity & single_mask); // low bits (plus mask) | |
579 | slots_used++; | |
580 | ||
581 | if (quantity >>= 4) { | |
582 | fileArr[filePos++] |= 0x10; // set length continuation bit | |
583 | fileArr[filePos] = quantity & double_mask; | |
584 | slots_used++; | |
585 | ||
586 | while (quantity >>= 7) { | |
587 | fileArr[filePos++] |= 0x80; // set length continuation bit | |
588 | fileArr[filePos] = quantity & double_mask; | |
589 | slots_used++; | |
590 | } | |
591 | } | |
592 | ||
593 | return slots_used; | |
594 | } | |
595 | ||
596 | ||
597 | unsigned int | |
598 | encode_rle(unsigned char * fileArr, unsigned int filePos, unsigned int quantity, union RunData * value, int depth) | |
599 | { | |
600 | unsigned char slots_used = 0; | |
601 | ||
602 | ||
603 | switch ( depth ) { | |
604 | case 1: | |
605 | slots_used += encode_length( fileArr, filePos, quantity, 0x80 ); | |
606 | fileArr[filePos+slots_used++] = value->c[0]; | |
607 | break; | |
608 | ||
609 | case 2: | |
610 | slots_used += encode_length( fileArr, filePos, quantity, 0xA0 ); | |
611 | fileArr[filePos+slots_used++] = value->c[0]; | |
612 | fileArr[filePos+slots_used++] = value->c[1]; | |
613 | break; | |
614 | ||
615 | case 3: | |
616 | slots_used += encode_length( fileArr, filePos, quantity, 0xC0 ); | |
617 | fileArr[filePos+slots_used++] = value->c[0]; | |
618 | fileArr[filePos+slots_used++] = value->c[1]; | |
619 | fileArr[filePos+slots_used++] = value->c[2]; | |
620 | break; | |
621 | ||
622 | case 4: | |
623 | slots_used += encode_length( fileArr, filePos, quantity, 0xE0 ); | |
624 | fileArr[filePos+slots_used++] = value->c[0]; | |
625 | fileArr[filePos+slots_used++] = value->c[1]; | |
626 | fileArr[filePos+slots_used++] = value->c[2]; | |
627 | fileArr[filePos+slots_used++] = value->c[3]; | |
628 | break; | |
629 | } | |
630 | ||
631 | return slots_used; | |
632 | } | |
633 | ||
634 | ||
635 | int | |
636 | findIndexNearMatch( unsigned int color24 ) | |
637 | { | |
638 | union colors color8; | |
639 | union colors clut8; | |
640 | int isGrey = 0; | |
641 | ||
642 | color8.c24 = color24; | |
643 | ||
644 | if ( color8.clut.red == color8.clut.green && color8.clut.green == color8.clut.blue ) | |
645 | isGrey = 1; | |
646 | ||
647 | if ( isGrey ) { | |
648 | int i; | |
649 | unsigned int bestIndex = 0, rel, bestMatch = -1; | |
650 | ||
651 | for (i=0; i<256; i++) { | |
652 | clut8.c24 = panic_clut[i]; | |
653 | ||
654 | if ( clut8.clut.red != clut8.clut.green || clut8.clut.green != clut8.clut.blue ) | |
655 | continue; | |
656 | ||
657 | if ( clut8.clut.red > color8.clut.red) continue; | |
658 | rel = abs(color8.clut.red - clut8.clut.red); | |
659 | if ( rel < bestMatch ) { | |
660 | bestMatch = rel; | |
661 | bestIndex = i; | |
662 | } | |
663 | } | |
664 | ||
665 | return bestIndex; | |
666 | } | |
667 | ||
668 | // we must have a non-grey color | |
669 | return -1; | |
670 | } | |
671 | ||
672 | unsigned int | |
673 | color24toGrey( unsigned int color24 ) | |
674 | { | |
675 | float R, G, B; | |
676 | float Grey; | |
677 | union colors c; | |
678 | unsigned char grey8; | |
679 | unsigned int grey24; | |
680 | ||
681 | c.c24 = color24; | |
682 | ||
683 | R = (c.clut.red & 0xFF) ; | |
684 | G = (c.clut.green & 0xFF) ; | |
685 | B = (c.clut.blue & 0xFF) ; | |
686 | ||
687 | Grey = (R*.30) + (G*.59) + (B*.11); | |
688 | grey8 = (unsigned char) ( Grey + .5); | |
689 | grey24 = (grey8<<16) | (grey8<<8) | grey8; | |
690 | return grey24; | |
691 | } | |
692 | ||
693 | ||
694 | int | |
695 | convert8toGrey( unsigned char * data, unsigned int size ) | |
696 | { | |
697 | int i; | |
698 | unsigned int c24; | |
699 | union colors c; | |
700 | ||
701 | for ( i=0; i<size; i++) { | |
702 | c.c24 = panic_clut[data[i]]; | |
703 | c24 = color24toGrey( c.c24 ); | |
704 | data[i] = findIndexMatch( c24 ); | |
705 | } | |
706 | ||
707 | return size; | |
708 | } | |
709 | ||
710 | unsigned int | |
711 | findColor24NearMatch( unsigned int color24 ) | |
712 | { | |
713 | union colors c; | |
714 | unsigned char prim; | |
715 | static unsigned int last_c = -1, last_co = -1, last_p = -1; | |
716 | ||
717 | if ( last_c == color24 ) | |
718 | return last_co; | |
719 | ||
720 | c.c24 = color24; | |
721 | ||
722 | if ( c.rgb[1] > c.rgb[2] && c.rgb[1] > c.rgb[3] ) | |
723 | prim = 1; | |
724 | else if ( c.rgb[2] > c.rgb[1] && c.rgb[2] > c.rgb[3] ) | |
725 | prim = 2; | |
726 | else if ( c.rgb[3] > c.rgb[1] && c.rgb[3] > c.rgb[2] ) | |
727 | prim = 3; | |
728 | else if ( c.rgb[1] == c.rgb[2] && c.rgb[1] == c.rgb[3] ) | |
729 | prim = 0; // gray | |
730 | else if ( c.rgb[1] == c.rgb[2] ) | |
731 | prim = 0x12; // red green | |
732 | else if ( c.rgb[1] == c.rgb[3] ) | |
733 | prim = 0x13; // red blue | |
734 | else if ( c.rgb[2] == c.rgb[3] ) | |
735 | prim = 0x23; // green blue | |
736 | else | |
737 | printf("cannot tell color %06x\n", color24); | |
738 | ||
739 | last_c = color24; | |
740 | last_p = prim; | |
741 | ||
742 | if ( prim == 0 || prim > 3 ) | |
743 | { | |
744 | last_co = -1; | |
745 | return last_co; | |
746 | } | |
747 | ||
748 | return -1; | |
749 | } | |
750 | ||
751 | ||
752 | unsigned char | |
753 | findIndexMatch( unsigned int color24 ) | |
754 | { | |
755 | int i; | |
756 | unsigned char ri; | |
757 | static unsigned char last = 0; | |
758 | ||
759 | retry: | |
760 | if ( panic_clut[last] == color24 ) | |
761 | { | |
762 | exactmatch++; | |
763 | return last; | |
764 | } | |
765 | ||
766 | for (i=0; i<256; i++) | |
767 | { | |
768 | if ( panic_clut[i] == color24 ) { | |
769 | last = i; | |
770 | exactmatch++; | |
771 | return last; | |
772 | } | |
773 | } | |
774 | ||
775 | if ( nextmis == -1 ) { | |
776 | for (i=0; i<256; i++) mismatchClut[i] = -1; | |
777 | nextmis = 0; | |
778 | } | |
779 | ||
780 | i = findIndexNearMatch(color24); | |
781 | ||
782 | if ( i == -1 ) // found a color that is not grey | |
783 | { | |
784 | unsigned int colormatch = findColor24NearMatch( color24 ); | |
785 | ||
786 | if ( colormatch == -1 ) // cannot convert color | |
787 | { | |
788 | cvt2grey++; | |
789 | if (debug>1) printf("color %06X not matched at all\n", color24); | |
790 | color24 = color24toGrey(color24); | |
791 | if (debug>1) printf("now grey %06X\n", color24); | |
792 | } | |
793 | else | |
794 | color24 = colormatch; | |
795 | ||
796 | goto retry; | |
797 | } | |
798 | ||
799 | if (debug>1) printf("color %06X now matched at %x\n", color24, i); | |
800 | ||
801 | ri = i; | |
802 | ||
803 | neargrey++; | |
804 | ||
805 | // keep track of missed repeats | |
806 | for ( i=0; i<nextmis; i++) | |
807 | if ( mismatchClut[i] == color24 ) | |
808 | return ri; | |
809 | ||
810 | if ( debug) printf("closest match for %06X is at index %d %06X\n", color24, ri, panic_clut[ri]); | |
811 | if ( nextmis < 256 ) | |
812 | mismatchClut[nextmis++] = color24; | |
813 | ||
814 | if ( debug && (nextmis >= 256) ) | |
815 | { | |
816 | fprintf(stderr,"Err: Too many color mismatches detected with this CLUT\n"); | |
817 | exit(1); | |
818 | } | |
819 | ||
820 | return ri; | |
821 | } | |
822 | ||
823 | /* | |
824 | * Convert 8 bit mode to 8 bit, We have to strip off the alignment bytes | |
825 | */ | |
826 | ||
827 | int | |
828 | convert8bitIndexto8( unsigned char * data, int height, int width, unsigned char ** dout ) | |
829 | { | |
830 | unsigned int row, col, i, i8, size, adj; | |
831 | unsigned char index; | |
832 | unsigned char * ddata; | |
833 | union colors color24; | |
834 | ||
835 | adj=(4-(width%4))%4; // adjustment needed to strip off the word alignment padding | |
836 | size = height * width; | |
837 | ddata = (unsigned char *) calloc( size, 1); | |
838 | ||
839 | for (i8=0,row=0; row<height; row++) | |
840 | { | |
841 | for (col=0; col<width; col++) | |
842 | { | |
843 | index = *data++; | |
844 | color24.c24 = panic_clut[index]; | |
845 | index = findIndexMatch( color24.c24 ); | |
846 | ddata[i8++] = index; | |
847 | } | |
848 | ||
849 | for (i=0; i<adj; i++) | |
850 | data++; | |
851 | } | |
852 | ||
853 | * dout = ddata; | |
854 | ||
855 | return (i8); | |
856 | } | |
857 | ||
858 | ||
859 | unsigned int * | |
860 | CreateCLUTarry( unsigned char * raw_clut ) | |
861 | { | |
862 | unsigned int * new_clut, index, i; | |
863 | ||
864 | new_clut = (unsigned int *) calloc(256, sizeof(unsigned int)); | |
865 | for ( index=0,i=0; i<256; index+=3,i++ ) | |
866 | new_clut[i] = (raw_clut[index] << 16) | (raw_clut[index+1] << 8) | raw_clut[index+2]; | |
867 | ||
868 | return new_clut; | |
869 | } | |
870 | ||
871 | ||
872 | unsigned int * | |
873 | ReplaceCLUT( char * iname ) | |
874 | { | |
875 | FILE * stream; | |
876 | unsigned char * raw_clut; | |
877 | unsigned int * new_clut; | |
878 | ||
879 | if ( (stream = fopen(iname, "rb")) == NULL ) { | |
880 | fprintf(stderr,"Err: Could not open input clut file %s.\n\n", iname); | |
881 | exit(1); | |
882 | } | |
883 | ||
884 | raw_clut = (char *) calloc(256, 3); | |
885 | fread(raw_clut, 256, 3, stream); | |
886 | fclose(stream); | |
887 | ||
888 | new_clut = CreateCLUTarry( raw_clut ); | |
889 | ||
890 | free(raw_clut); | |
891 | return new_clut; | |
892 | } |