00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019 #include "Core/precomp.h"
00020 #include <iostream>
00021 #include <stdio.h>
00022 #include "API/Display/SurfaceProviders/provider_targa.h"
00023 #include "API/Core/IOData/cl_endian.h"
00024 #include "API/Core/System/error.h"
00025 #include "API/Core/System/cl_assert.h"
00026 #include "API/Display/Display/res_surface.h"
00027 #include "API/Core/Resources/resourceoptions.h"
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038
00039
00040
00041
00042
00043
00044
00045
00046
00047
00048
00049
00050
00051
00052
00053
00054
00055
00056
00057
00058
00059
00060
00061
00062
00063
00064
00065
00066
00067 CL_Surface *CL_TargaProvider::create(
00068 std::string file,
00069 CL_InputSourceProvider *provider,
00070 bool transparent,
00071 bool ignore_alphachannel,
00072 unsigned char trans_red,
00073 unsigned char trans_green,
00074 unsigned char trans_blue)
00075 {
00076 return CL_Surface::create(
00077 new CL_TargaProvider(
00078 file,
00079 provider,
00080 transparent,
00081 ignore_alphachannel,
00082 trans_red,
00083 trans_green,
00084 trans_blue),
00085 true);
00086 }
00087
00088 CL_TargaProvider::CL_TargaProvider(
00089 std::string name,
00090 CL_InputSourceProvider *_provider,
00091 bool _transparent,
00092 bool _ignore_alphachannel,
00093 unsigned char trans_red,
00094 unsigned char trans_green,
00095 unsigned char trans_blue)
00096 {
00097 if (_provider == NULL)
00098 {
00099 provider = CL_InputSourceProvider::create_file_provider(".");
00100 }
00101 else
00102 {
00103 provider = _provider->clone();
00104 }
00105
00106 ignore_alphachannel = _ignore_alphachannel;
00107 transparent = _transparent;
00108 use_alphapixels = transparent && !ignore_alphachannel;
00109 trans_redcol = trans_red;
00110 trans_greencol = trans_green;
00111 trans_bluecol = trans_blue;
00112
00113 if (transparent)
00114 {
00115 trans_col = (((unsigned int) trans_redcol) << 24) +
00116 (((unsigned int) trans_greencol)<<16) +
00117 (((unsigned int) trans_bluecol) << 8);
00118 }
00119 else
00120 {
00121 trans_col = -1;
00122 }
00123
00124 locked = false;
00125 filename = name;
00126 file = image = color_map = NULL;
00127 }
00128
00129 CL_TargaProvider::~CL_TargaProvider()
00130 {
00131 perform_unlock();
00132 delete provider;
00133 }
00134
00135
00136
00137
00138
00139
00140 void CL_TargaProvider::read_data()
00141 {
00142 datatype = file[2];
00143
00144 switch (datatype)
00145 {
00146 case 1:
00147 read_colormapped();
00148 return;
00149 break;
00150 case 2:
00151 read_uncompressed_rgb();
00152 return;
00153 break;
00154 case 9:
00155
00156 read_runlength_encoded_colormapped_rgb();
00157 return;
00158 break;
00159 case 10:
00160 read_runlength_encoded_rgb();
00161 return;
00162 break;
00163 }
00164
00165 std::string err("CL_TargaProvider Fatal Error: Unsupported TGA filetype encountered");
00166 throw CL_Error(err);
00167
00168 }
00169
00170
00171
00172
00173
00174
00175 void CL_TargaProvider::read_header(bool read_colormap)
00176 {
00177 unsigned char idlength = file[0];
00178 unsigned char colormaptype = file[1];
00179
00180 pos = 18;
00181
00182
00183 if (idlength != 0)
00184 {
00185 pos += idlength;
00186 }
00187
00188 bpp = file[16];
00189
00190
00191 if (colormaptype == 1)
00192 {
00193 map_length = *((unsigned short *) &file[5]);
00194 SWAP_IF_BIG(map_length);
00195 unsigned char map_size = file[7]>>3;
00196
00197 if (!read_colormap)
00198 {
00199
00200 pos += map_length*map_size;
00201 }
00202 else
00203 {
00204
00205 color_map = new unsigned char[map_length * 4];
00206
00207
00208 for (unsigned int i=0;i<map_length;i++)
00209 {
00210 switch (bpp)
00211 {
00212 case 16:
00213 read_rgb_16(&color_map[i*4+0],
00214 &color_map[i*4+1],
00215 &color_map[i*4+2],
00216 &color_map[i*4+3]);
00217 break;
00218 case 24:
00219 read_rgb_24(&color_map[i*4+0],
00220 &color_map[i*4+1],
00221 &color_map[i*4+2],
00222 &color_map[i*4+3]);
00223 break;
00224 case 32:
00225 read_rgb_32(&color_map[i*4+0],
00226 &color_map[i*4+1],
00227 &color_map[i*4+2],
00228 &color_map[i*4+3]);
00229 break;
00230 default:
00231 {
00232 char aBuf[500];
00233 sprintf(aBuf, "Unsupported bit-depth in targafile '%s'", filename.c_str());
00234 cl_info(1, aBuf);
00235 }
00236 }
00237 }
00238 }
00239 }
00240
00241
00242 pitch = *((unsigned short *) &file[12]);
00243 SWAP_IF_BIG(pitch);
00244 bounding_left = pitch;
00245 bounding_right = 0;
00246
00247 height = *((unsigned short *) &file[14]);
00248 SWAP_IF_BIG(height);
00249 bounding_top = height;
00250 bounding_bottom = 0;
00251
00252 map_direction_x = (file[17] & 0x10) == 0 ? 1 : -1;
00253 map_direction_y = (file[17] & 0x20) == 0 ? -1 : 1;
00254 }
00255
00256
00257
00258
00259
00260 inline bool CL_TargaProvider::read_rgb_16(
00261 unsigned char *a,
00262 unsigned char *b,
00263 unsigned char *g,
00264 unsigned char *r)
00265 {
00266 bool ret = true;
00267
00268 if (pos >= filesize)
00269 throw CL_Error("Invalid targa file!?");
00270
00271
00272
00273
00274
00275
00276
00277
00278
00279
00280 *a = (int(file[pos+1]&128)>>7)*255;
00281
00282 if (use_alphapixels && !(*a))
00283 {
00284 *r = trans_redcol;
00285 *g = trans_greencol;
00286 *b = trans_bluecol;
00287 ret = false;
00288 }
00289 else
00290 {
00291 *r = ((file[pos+1]>>2) & 0x1f)<<3;
00292 *g = (((file[pos+1] & 0x03)<<3) +
00293 ((file[pos]>>5) & 0x7))<<3;
00294 *b = (file[pos] & 0x1f)<<3;
00295
00296 if (transparent &&
00297 *r == trans_redcol &&
00298 *g == trans_greencol &&
00299 *b == trans_bluecol)
00300 {
00301 *a = 0;
00302 ret = false;
00303 }
00304
00305 else
00306 if (ignore_alphachannel)
00307 {
00308
00309 *a = 255;
00310 }
00311
00312 }
00313
00314 pos += 2;
00315
00316 return ret;
00317 }
00318
00319 inline bool CL_TargaProvider::read_rgb_24(
00320 unsigned char *a,
00321 unsigned char *b,
00322 unsigned char *g,
00323 unsigned char *r)
00324 {
00325 bool ret = true;
00326
00327 if (pos >= filesize)
00328 throw CL_Error("Invalid targa file!?");
00329
00330 *b = file[pos];
00331 *g = file[pos+1];
00332 *r = file[pos+2];
00333 *a = 255;
00334
00335 if (transparent &&
00336 *r == trans_redcol &&
00337 *g == trans_greencol &&
00338 *b == trans_bluecol)
00339 {
00340 *a = 0;
00341 ret = false;
00342 }
00343
00344
00345 pos += 3;
00346
00347 return ret;
00348 }
00349
00350 inline bool CL_TargaProvider::read_rgb_32(
00351 unsigned char *a,
00352 unsigned char *b,
00353 unsigned char *g,
00354 unsigned char *r)
00355 {
00356 bool ret = true;
00357
00358 if (pos >= filesize)
00359 throw CL_Error("Invalid targa file!?");
00360
00361 *a = file[pos+3];
00362 if (use_alphapixels && !(*a))
00363 {
00364 *r = trans_redcol;
00365 *g = trans_greencol;
00366 *b = trans_bluecol;
00367
00368 ret = false;
00369 }
00370 else
00371 {
00372 *b = file[pos];
00373 *g = file[pos+1];
00374 *r = file[pos+2];
00375
00376 if (transparent &&
00377 *r == trans_redcol &&
00378 *g == trans_greencol &&
00379 *b == trans_bluecol)
00380 {
00381
00382 *a = 0;
00383 ret = false;
00384 }
00385
00386 else
00387 if (ignore_alphachannel)
00388 {
00389
00390 *a = 255;
00391 }
00392
00393 }
00394
00395 pos += 4;
00396
00397 return ret;
00398 }
00399
00400 inline bool CL_TargaProvider::read_rgb(
00401 unsigned char *a,
00402 unsigned char *b,
00403 unsigned char *g,
00404 unsigned char *r)
00405 {
00406 bool ret = true;
00407
00408 if (pos >= filesize)
00409 throw CL_Error("Invalid targa file!?");
00410
00411 switch (bpp)
00412 {
00413 case 16:
00414
00415
00416
00417
00418
00419
00420
00421
00422
00423 *a = (int(file[pos+1]&128)>>7)*255;
00424
00425 if (use_alphapixels && !(*a))
00426 {
00427 *r = trans_redcol;
00428 *g = trans_greencol;
00429 *b = trans_bluecol;
00430 ret = false;
00431 }
00432 else
00433 {
00434 *r = ((file[pos+1]>>2) & 0x1f)<<3;
00435 *g = (((file[pos+1] & 0x03)<<3) +
00436 ((file[pos]>>5) & 0x7))<<3;
00437 *b = (file[pos] & 0x1f)<<3;
00438
00439 if (transparent &&
00440 *r == trans_redcol &&
00441 *g == trans_greencol &&
00442 *b == trans_bluecol)
00443 {
00444 *a = 0;
00445 ret = false;
00446 }
00447
00448 else
00449 if (ignore_alphachannel)
00450 {
00451
00452 *a = 255;
00453 }
00454
00455 }
00456
00457 pos += 2;
00458 break;
00459
00460 case 24:
00461 *b = file[pos];
00462 *g = file[pos+1];
00463 *r = file[pos+2];
00464 *a = 255;
00465
00466 if (transparent &&
00467 *r == trans_redcol &&
00468 *g == trans_greencol &&
00469 *b == trans_bluecol)
00470 {
00471 *a = 0;
00472 ret = false;
00473 }
00474
00475 pos += 3;
00476 break;
00477
00478 case 32:
00479 *a = file[pos+3];
00480 if (use_alphapixels && !(*a))
00481 {
00482 *r = trans_redcol;
00483 *g = trans_greencol;
00484 *b = trans_bluecol;
00485
00486 ret = false;
00487 }
00488 else
00489 {
00490 *b = file[pos];
00491 *g = file[pos+1];
00492 *r = file[pos+2];
00493
00494 if (transparent &&
00495 *r == trans_redcol &&
00496 *g == trans_greencol &&
00497 *b == trans_bluecol)
00498 {
00499 *a = 0;
00500 ret = false;
00501 }
00502
00503 else
00504 if (ignore_alphachannel)
00505 {
00506
00507 *a = 255;
00508 }
00509
00510 }
00511
00512 pos += 4;
00513 break;
00514 }
00515
00516 return ret;
00517 }
00518
00519
00520
00521
00522
00523
00524 void CL_TargaProvider::read_from_colormap(
00525 unsigned char *a,
00526 unsigned char *b,
00527 unsigned char *g,
00528 unsigned char *r)
00529 {
00530
00531 if (pos >= filesize)
00532 throw CL_Error("Invalid targa file!?");
00533
00534
00535
00536
00537 int entry;
00538 if (map_length <= 256)
00539 {
00540 entry = file[pos++];
00541 }
00542 else
00543 {
00544 entry = *((unsigned short *) &file[pos]);
00545 SWAP_IF_BIG(entry);
00546 pos += 2;
00547 }
00548
00549
00550 if (entry < 0 || entry >= map_length)
00551 throw CL_Error("Invalid targa file!?");
00552
00553
00554 *r = color_map[entry*4+0];
00555 *g = color_map[entry*4+1];
00556 *b = color_map[entry*4+2];
00557 *a = color_map[entry*4+3];
00558 }
00559
00560
00561
00562
00563 void CL_TargaProvider::read_colormapped()
00564 {
00565 read_header(true);
00566
00567 image = new unsigned char[pitch * height * 4];
00568
00569 int ystart = map_direction_y == 1 ? 0 : height-1;
00570 int xstart = map_direction_x == 1 ? 0 : pitch-1;
00571
00572 for (int y = ystart;y>=0&&y<height;y+=map_direction_y)
00573 {
00574 for (int x = xstart; x>=0 && x<pitch;x+=map_direction_x)
00575 {
00576 read_from_colormap(&image[(x+y*pitch)*4],
00577 &image[(x+y*pitch)*4+1],
00578 &image[(x+y*pitch)*4+2],
00579 &image[(x+y*pitch)*4+3]);
00580 }
00581 }
00582 }
00583
00584
00585
00586
00587 void CL_TargaProvider::read_uncompressed_rgb()
00588 {
00589 read_header(false);
00590
00591 int ystart = map_direction_y == 1 ? 0 : height-1;
00592 int xstart = map_direction_x == 1 ? 0 : pitch-1;
00593
00594 image = new unsigned char[pitch * height * 4];
00595 switch (bpp)
00596 {
00597 case 16:
00598 {
00599 for (int y = ystart;y>=0&&y<height;y+=map_direction_y)
00600 {
00601 for (int x = xstart; x>=0 && x<pitch;x+=map_direction_x)
00602 {
00603 bool v = read_rgb_16(&image[(x+y*pitch)*4+0],
00604 &image[(x+y*pitch)*4+1],
00605 &image[(x+y*pitch)*4+2],
00606 &image[(x+y*pitch)*4+3]);
00607 if (v)
00608 {
00609 if (x < bounding_left) bounding_left = x;
00610 if (y < bounding_top) bounding_top = y;
00611 if (x > bounding_right) bounding_right = x;
00612 if (y > bounding_bottom) bounding_bottom = y;
00613 }
00614 }
00615 }
00616 }
00617 break;
00618
00619 case 24:
00620 {
00621 for (int y = ystart;y>=0&&y<height;y+=map_direction_y)
00622 {
00623 for (int x = xstart; x>=0 && x<pitch;x+=map_direction_x)
00624 {
00625 bool v = read_rgb_24(&image[(x+y*pitch)*4+0],
00626 &image[(x+y*pitch)*4+1],
00627 &image[(x+y*pitch)*4+2],
00628 &image[(x+y*pitch)*4+3]);
00629 if (v)
00630 {
00631 if (x < bounding_left) bounding_left = x;
00632 if (y < bounding_top) bounding_top = y;
00633 if (x > bounding_right) bounding_right = x;
00634 if (y > bounding_bottom) bounding_bottom = y;
00635 }
00636
00637 }
00638 }
00639 }
00640 break;
00641
00642 case 32:
00643 {
00644 for (int y = ystart;y>=0&&y<height;y+=map_direction_y)
00645 {
00646 for (int x = xstart; x>=0 && x<pitch;x+=map_direction_x)
00647 {
00648 bool v = read_rgb_32(&image[(x+y*pitch)*4+0],
00649 &image[(x+y*pitch)*4+1],
00650 &image[(x+y*pitch)*4+2],
00651 &image[(x+y*pitch)*4+3]);
00652 if (v)
00653 {
00654 if (x < bounding_left) bounding_left = x;
00655 if (y < bounding_top) bounding_top = y;
00656 if (x > bounding_right) bounding_right = x;
00657 if (y > bounding_bottom) bounding_bottom = y;
00658 }
00659 }
00660 }
00661 }
00662 break;
00663
00664 default:
00665 std::cout << "Unknown bpp!" << std::endl;
00666 cl_assert(false);
00667 }
00668 }
00669
00670
00671
00672
00673
00674 void CL_TargaProvider::read_runlength_encoded_colormapped_rgb()
00675 {
00676 read_header(true);
00677
00678 image = new unsigned char[pitch * height * 4];
00679
00680 int ystart = map_direction_y == 1 ? 0 : height-1;
00681 int xstart = map_direction_x == 1 ? 0 : pitch-1;
00682
00683 int line = ystart;
00684 int xpos = xstart;
00685 while (line >= 0 && line < height)
00686 {
00687 unsigned char head = file[pos++];
00688 unsigned char type = (head & 128)>>7;
00689 unsigned char repcount = (head & 127)+1;
00690
00691 switch (type)
00692 {
00693
00694 case 0:
00695 for (;repcount>0;repcount--)
00696 {
00697 read_from_colormap(&image[(xpos+line*pitch)*4],
00698 &image[(xpos+line*pitch)*4+1],
00699 &image[(xpos+line*pitch)*4+2],
00700 &image[(xpos+line*pitch)*4+3]);
00701
00702 xpos+=map_direction_x;
00703 if (xpos < 0 || xpos >= pitch)
00704 {
00705 xpos = xstart;
00706 line += map_direction_y;
00707 }
00708 }
00709 break;
00710
00711
00712 case 1:
00713 {
00714 unsigned char r, g, b, a;
00715 read_from_colormap(&r, &g, &b, &a);
00716
00717 for (;repcount>0;repcount--)
00718 {
00719 image[((line*pitch+xpos)*4)+0] = r;
00720 image[((line*pitch+xpos)*4)+1] = g;
00721 image[((line*pitch+xpos)*4)+2] = b;
00722 image[((line*pitch+xpos)*4)+3] = a;
00723
00724 xpos+=map_direction_x;
00725 if (xpos < 0 || xpos >= pitch)
00726 {
00727 xpos = xstart;
00728 line += map_direction_y;
00729 }
00730 }
00731 }
00732 break;
00733 }
00734 }
00735 }
00736
00737
00738
00739
00740 void CL_TargaProvider::read_runlength_encoded_rgb()
00741 {
00742 read_header(false);
00743
00744 image = new unsigned char[pitch * height * 4];
00745
00746 int ystart = map_direction_y == 1 ? 0 : height-1;
00747 int xstart = map_direction_x == 1 ? 0 : pitch-1;
00748
00749 int line = ystart;
00750 int xpos = xstart;
00751 while (line >= 0 && line < height)
00752 {
00753 unsigned char head = file[pos++];
00754 unsigned char type = (head & 128)>>7;
00755 unsigned char repcount = (head & 127)+1;
00756
00757 switch (type)
00758 {
00759
00760 case 0:
00761 for (;repcount>0;repcount--)
00762 {
00763 bool v = read_rgb(&image[((line*pitch+xpos)*4)+0],
00764 &image[((line*pitch+xpos)*4)+1],
00765 &image[((line*pitch+xpos)*4)+2],
00766 &image[((line*pitch+xpos)*4)+3]);
00767 if (v)
00768 {
00769 if (xpos < bounding_left) bounding_left = xpos;
00770 if (line < bounding_top) bounding_top = line;
00771 if (xpos > bounding_right) bounding_right = xpos;
00772 if (line > bounding_bottom) bounding_bottom = line;
00773 }
00774
00775 xpos+=map_direction_x;
00776 if (xpos < 0 || xpos >= pitch)
00777 {
00778 xpos = xstart;
00779 line += map_direction_y;
00780 }
00781 }
00782 break;
00783
00784
00785 case 1:
00786 {
00787 unsigned char r, g, b, a;
00788 bool v = read_rgb(&r, &g, &b, &a);
00789
00790 for (;repcount>0;repcount--)
00791 {
00792 image[((line*pitch+xpos)*4)+0] = r;
00793 image[((line*pitch+xpos)*4)+1] = g;
00794 image[((line*pitch+xpos)*4)+2] = b;
00795 image[((line*pitch+xpos)*4)+3] = a;
00796
00797 if (v)
00798 {
00799 if (xpos < bounding_left) bounding_left = xpos;
00800 if (line < bounding_top) bounding_top = line;
00801 if (xpos > bounding_right) bounding_right = xpos;
00802 if (line > bounding_bottom) bounding_bottom = line;
00803 }
00804
00805 xpos+=map_direction_x;
00806 if (xpos < 0 || xpos >= pitch)
00807 {
00808 xpos = xstart;
00809 line += map_direction_y;
00810 }
00811 }
00812 }
00813 break;
00814 }
00815 }
00816 }
00817
00818 void *CL_TargaProvider::get_data() const
00819 {
00820 return image+bounding_left*4+bounding_top*pitch*4;
00821 }
00822
00823
00824
00825
00826
00827
00828 void CL_TargaProvider::perform_lock()
00829 {
00830 if (locked) return;
00831
00832 cl_assert(provider != NULL);
00833 input_source = provider->open_source(filename.c_str());
00834 cl_assert(input_source!=NULL);
00835
00836 no_sprs = 1;
00837
00838 filesize = input_source->size();
00839 file = new unsigned char[filesize];
00840 cl_assert(file != NULL);
00841
00842 int num_bytes_read = input_source->read(file, filesize);
00843 cl_assert(num_bytes_read == ((int) filesize));
00844
00845
00846 read_data();
00847
00848 if (bounding_left > bounding_right) bounding_left = bounding_right;
00849 if (bounding_top > bounding_bottom) bounding_top = bounding_bottom;
00850
00851 delete[] file;
00852 delete input_source;
00853 file = NULL;
00854
00855 locked = true;
00856 }
00857
00858 void CL_TargaProvider::perform_unlock()
00859 {
00860 locked = false;
00861 delete[] color_map;
00862 delete[] image;
00863
00864 image = color_map = NULL;
00865 }