SWF_Image.cpp (19213B)
1 /* 2 =========================================================================== 3 4 Doom 3 BFG Edition GPL Source Code 5 Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company. 6 7 This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code"). 8 9 Doom 3 BFG Edition Source Code is free software: you can redistribute it and/or modify 10 it under the terms of the GNU General Public License as published by 11 the Free Software Foundation, either version 3 of the License, or 12 (at your option) any later version. 13 14 Doom 3 BFG Edition Source Code is distributed in the hope that it will be useful, 15 but WITHOUT ANY WARRANTY; without even the implied warranty of 16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 GNU General Public License for more details. 18 19 You should have received a copy of the GNU General Public License 20 along with Doom 3 BFG Edition Source Code. If not, see <http://www.gnu.org/licenses/>. 21 22 In addition, the Doom 3 BFG Edition Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 BFG Edition Source Code. If not, please request a copy in writing from id Software at the address below. 23 24 If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. 25 26 =========================================================================== 27 */ 28 #pragma hdrstop 29 #include "../idlib/precompiled.h" 30 #include "../renderer/Image.h" 31 //#include "../../renderer/ImageTools/ImageProcess.h" 32 #include "../renderer/jpeg-6/jpeglib.h" 33 34 /* 35 ======================== 36 idSWF::idDecompressJPEG 37 These are the static callback functions the jpeg library calls 38 ======================== 39 */ 40 void swf_jpeg_error_exit( jpeg_common_struct * cinfo ) { 41 char buffer[JMSG_LENGTH_MAX] = {0}; 42 (*cinfo->err->format_message)( cinfo, buffer ); 43 throw idException( buffer ); 44 } 45 void swf_jpeg_output_message( jpeg_common_struct * cinfo ) { 46 char buffer[JMSG_LENGTH_MAX] = {0}; 47 (*cinfo->err->format_message)( cinfo, buffer ); 48 idLib::Printf( "%s\n", buffer ); 49 } 50 void swf_jpeg_init_source( jpeg_decompress_struct * cinfo ) { 51 } 52 boolean swf_jpeg_fill_input_buffer( jpeg_decompress_struct * cinfo ) { 53 return TRUE; 54 } 55 void swf_jpeg_skip_input_data( jpeg_decompress_struct * cinfo, long num_bytes ) { 56 cinfo->src->next_input_byte += num_bytes; 57 cinfo->src->bytes_in_buffer -= num_bytes; 58 } 59 void swf_jpeg_term_source( jpeg_decompress_struct * cinfo ) { 60 } 61 62 /* 63 ======================== 64 idSWF::idDecompressJPEG::idDecompressJPEG 65 ======================== 66 */ 67 idSWF::idDecompressJPEG::idDecompressJPEG() { 68 jpeg_decompress_struct * cinfo = new (TAG_SWF) jpeg_decompress_struct; 69 memset( cinfo, 0, sizeof( *cinfo ) ); 70 71 cinfo->err = new (TAG_SWF) jpeg_error_mgr; 72 memset( cinfo->err, 0, sizeof( jpeg_error_mgr ) ); 73 jpeg_std_error( cinfo->err ); 74 cinfo->err->error_exit = swf_jpeg_error_exit; 75 cinfo->err->output_message = swf_jpeg_output_message; 76 77 jpeg_create_decompress( cinfo ); 78 79 vinfo = cinfo; 80 } 81 82 /* 83 ======================== 84 idSWF::idDecompressJPEG::~idDecompressJPEG 85 ======================== 86 */ 87 idSWF::idDecompressJPEG::~idDecompressJPEG() { 88 jpeg_decompress_struct * cinfo = (jpeg_decompress_struct *)vinfo; 89 90 jpeg_destroy_decompress( cinfo ); 91 delete cinfo->err; 92 delete cinfo; 93 } 94 95 /* 96 ======================== 97 idSWF::idDecompressJPEG::Load 98 ======================== 99 */ 100 byte * idSWF::idDecompressJPEG::Load( const byte * input, int inputSize, int & width, int & height ) { 101 jpeg_decompress_struct * cinfo = (jpeg_decompress_struct *)vinfo; 102 103 try { 104 105 width = 0; 106 height = 0; 107 108 jpeg_source_mgr src; 109 memset( &src, 0, sizeof( src ) ); 110 src.next_input_byte = (JOCTET *)input; 111 src.bytes_in_buffer = inputSize; 112 src.init_source = swf_jpeg_init_source; 113 src.fill_input_buffer = swf_jpeg_fill_input_buffer; 114 src.skip_input_data = swf_jpeg_skip_input_data; 115 src.resync_to_restart = jpeg_resync_to_restart; 116 src.term_source = swf_jpeg_term_source; 117 cinfo->src = &src; 118 119 int result = 0; 120 do { 121 result = jpeg_read_header( cinfo, FALSE ); 122 } while ( result == JPEG_HEADER_TABLES_ONLY ); 123 124 if ( result == JPEG_SUSPENDED ) { 125 return NULL; 126 } 127 128 jpeg_start_decompress( cinfo ); 129 if ( cinfo->output_components != 4 ) { 130 // This shouldn't really be possible, unless the source image is some kind of strange grayscale format or something 131 idLib::Warning( "JPEG output is not 4 components" ); 132 jpeg_abort_decompress( cinfo ); 133 cinfo->src = NULL; // value goes out of scope 134 return NULL; 135 } 136 int outputSize = cinfo->output_width * cinfo->output_height * cinfo->output_components; 137 byte * output = (byte *)Mem_Alloc( outputSize, TAG_SWF ); 138 memset( output, 255, outputSize ); 139 while ( cinfo->output_scanline < cinfo->output_height ) { 140 JSAMPROW scanlines = output + cinfo->output_scanline * cinfo->output_width * cinfo->output_components; 141 jpeg_read_scanlines( cinfo, &scanlines, 1 ); 142 } 143 jpeg_finish_decompress( cinfo ); 144 145 width = cinfo->output_width; 146 height = cinfo->output_height; 147 148 cinfo->src = NULL; // value goes out of scope 149 return output; 150 151 } catch ( idException & ) { 152 swf_jpeg_output_message( (jpeg_common_struct *)cinfo ); 153 return NULL; 154 } 155 } 156 157 158 /* 159 ======================== 160 idSWF::WriteSwfImageAtlas 161 162 Now that all images have been found, allocate them in an atlas 163 and write it out. 164 ======================== 165 */ 166 void RectAllocator( const idList<idVec2i> &inputSizes, idList<idVec2i> &outputPositions, idVec2i &totalSize ); 167 float RectPackingFraction( const idList<idVec2i> &inputSizes, const idVec2i totalSize ); 168 169 void idSWF::WriteSwfImageAtlas( const char *filename ) { 170 idList<idVec2i> inputSizes; 171 inputSizes.SetNum( packImages.Num() ); 172 for ( int i = 0 ; i < packImages.Num() ; i++ ) { 173 // these are in DXT blocks, not pixels 174 inputSizes[i] = packImages[i].allocSize; 175 } 176 177 idList<idVec2i> outputPositions; 178 idVec2i totalSize; 179 // smart allocator 180 RectAllocator( inputSizes, outputPositions, totalSize ); 181 182 float frac = RectPackingFraction( inputSizes, totalSize ); 183 idLib::Printf( "%5.2f packing fraction in %ix%i image\n", frac, totalSize.x*4, totalSize.y*4 ); 184 185 int atlasWidth = Max( 4, totalSize.x * 4 ) ; 186 int atlasHeight = Max( 4, totalSize.y * 4 ) ; 187 188 // we require multiple-of-128 widths to use the image data directly 189 // without re-packing on the 360 and PS3. The growth checks in RectAllocator() 190 // will always align, but a single image won't necessarily be. 191 atlasWidth = ( atlasWidth + 127 ) & ~127; 192 193 idTempArray<byte> swfAtlas( atlasWidth * atlasHeight * 4 ); 194 195 // fill everything with solid red 196 for ( int i = 0; i < atlasWidth * atlasHeight; i++ ) { 197 swfAtlas[i*4+0] = 255; 198 swfAtlas[i*4+1] = 0; 199 swfAtlas[i*4+2] = 0; 200 swfAtlas[i*4+3] = 255; 201 } 202 203 // allocate the blocks and copy the texels 204 for ( int i = 0 ; i < packImages.Num() ; i++ ) { 205 imageToPack_t & pack = packImages[i]; 206 assert( pack.imageData != NULL ); 207 208 int blockWidth = pack.allocSize.x; 209 int blockHeight = pack.allocSize.y; 210 211 int x = outputPositions[i].x; 212 int y = outputPositions[i].y; 213 214 // get the range for each channel so we can maximize it 215 // for better compression 216 int minV[4] = { 255, 255, 255, 255 }; 217 int maxV[4] = { 0, 0, 0, 0 }; 218 for ( int j = 0 ; j < pack.trueSize.x * pack.trueSize.y * 4 ; j++ ) { 219 int v = pack.imageData[ j ]; 220 int x = j & 3; 221 if ( v < minV[x] ) { 222 minV[x] = v; 223 } 224 if ( v > maxV[x] ) { 225 maxV[x] = v; 226 } 227 } 228 // idLib::Printf( "Color normalize: %3i:%3i %3i:%3i %3i:%3i %3i:%3i\n", 229 // minV[0], maxV[0], minV[1], maxV[1], minV[2], maxV[2], minV[3], maxV[3] ); 230 231 // don't divide by zero 232 for ( int x = 0 ; x < 4 ; x++ ) { 233 if ( maxV[x] == 0 ) { 234 maxV[x] = 1; 235 } 236 } 237 // rescale the image 238 // 239 // Note that this must be done in RGBA space, before YCoCg conversion, 240 // or the scale factors couldn't be combined with the normal swf coloring. 241 // 242 // If we create an idMaterial for each atlas element, we could add 243 // a bias as well as a scale to enable us to take advantage of the 244 // min values as well as the max, but very few gui images don't go to black, 245 // and just doing a scale avoids changing more code. 246 for ( int j = 0; j < pack.trueSize.x * pack.trueSize.y * 4; j++ ) { 247 int v = pack.imageData[ j ]; 248 int x = j & 3; 249 v = v * 255 / maxV[x]; 250 pack.imageData[ j ] = v; 251 } 252 253 assert( ( x + blockWidth )* 4 <= atlasWidth ); 254 assert( ( y + blockHeight )* 4 <= atlasHeight ); 255 // Extend the pixels with clamp-to-edge to the edge of the allocation block. 256 // The GPU hardware addressing should completely ignore texels outside the true block 257 // size, but the compressor works on complete blocks, regardless of the true rect size. 258 x <<= 2; 259 y <<= 2; 260 for ( int dstY = 0; dstY < blockHeight<<2; dstY++ ) { 261 int srcY = dstY-1; 262 if ( srcY < 0 ) { 263 srcY = 0; 264 } 265 if ( srcY >= pack.trueSize.y ) { 266 srcY = pack.trueSize.y - 1; 267 } 268 for ( int dstX = 0 ; dstX < blockWidth<<2 ; dstX++ ) { 269 int srcX = dstX-1; 270 if ( srcX < 0 ) { 271 srcX = 0; 272 } 273 if ( srcX >= pack.trueSize.x ) { 274 srcX = pack.trueSize.x - 1; 275 } 276 ((int *)swfAtlas.Ptr())[ (y+dstY) * atlasWidth + (x+dstX) ] = 277 ((int *)pack.imageData)[ srcY * pack.trueSize.x + srcX ]; 278 } 279 } 280 281 // save the information in the SWF dictionary 282 idSWFDictionaryEntry * entry = FindDictionaryEntry( pack.characterID ); 283 assert( entry->material == NULL ); 284 entry->imageSize.x = pack.trueSize.x; 285 entry->imageSize.y = pack.trueSize.y; 286 entry->imageAtlasOffset.x = x + 1; 287 entry->imageAtlasOffset.y = y + 1; 288 for ( int i = 0; i < 4; i++ ) { 289 entry->channelScale[i] = maxV[i] / 255.0f; 290 } 291 292 Mem_Free( pack.imageData ); 293 pack.imageData = NULL; 294 } 295 296 // the TGA is only for examination during development 297 R_WriteTGA( filename, swfAtlas.Ptr(), atlasWidth, atlasHeight, false, "fs_basepath" ); 298 } 299 300 /* 301 ======================== 302 idSWF::LoadImage 303 Loads RGBA data into an image at the specificied character id in the dictionary 304 ======================== 305 */ 306 void idSWF::LoadImage( int characterID, const byte * imageData, int width, int height ) { 307 idSWFDictionaryEntry * entry = AddDictionaryEntry( characterID, SWF_DICT_IMAGE ); 308 if ( entry == NULL ) { 309 return; 310 } 311 312 // save the data off so we can do the image atlas allocation after we have collected 313 // all the images that are used by the entire swf 314 imageToPack_t pack; 315 pack.characterID = characterID; 316 pack.imageData = (byte *)Mem_Alloc( width*height*4, TAG_SWF ); 317 memcpy( pack.imageData, imageData, width*height*4 ); 318 pack.trueSize.x = width; 319 pack.trueSize.y = height; 320 for ( int i = 0 ; i < 2 ; i++ ) { 321 int v = pack.trueSize[i]; 322 // Swf images are usually completely random in size, but perform all allocations in 323 // DXT blocks of 4. If we choose to DCT / HDP encode the image block, we should probably 324 // increae the block size to 8 or 16 to prevent neighbor effects. 325 v = ( v + 3 ) >> 2; 326 327 // Allways allocate a single pixel border around the images so there won't be any edge 328 // bleeds. This can often be hidden in in the round-up to DXT size. 329 if ( ( v << 2 ) - pack.trueSize[i] < 2 ) { 330 v++; 331 } 332 pack.allocSize[i] = v; 333 } 334 packImages.Append( pack ); 335 336 entry->material = NULL; 337 } 338 339 /* 340 ======================== 341 idSWF::JPEGTables 342 Reads jpeg table data, there can only be one of these in the file, and it has to come before any DefineBits tags 343 We don't have to worry about clearing the jpeg object because jpeglib will automagically overwrite any tables that are already set (I think?) 344 ======================== 345 */ 346 void idSWF::JPEGTables( idSWFBitStream & bitstream ) { 347 if ( bitstream.Length() == 0 ) { 348 // no clue why this happens 349 return; 350 } 351 int width, height; 352 jpeg.Load( bitstream.ReadData( bitstream.Length() ), bitstream.Length(), width, height ); 353 } 354 355 /* 356 ======================== 357 idSWF::DefineBits 358 Reads a partial jpeg image, using the tables set by the JPEGTables tag 359 ======================== 360 */ 361 void idSWF::DefineBits( idSWFBitStream & bitstream ) { 362 uint16 characterID = bitstream.ReadU16(); 363 364 int jpegSize = bitstream.Length() - sizeof( uint16 ); 365 366 int width, height; 367 byte * imageData = jpeg.Load( bitstream.ReadData( jpegSize ), jpegSize, width, height ); 368 if ( imageData == NULL ) { 369 return; 370 } 371 372 LoadImage( characterID, imageData, width, height ); 373 374 Mem_Free( imageData ); 375 } 376 377 /* 378 ======================== 379 idSWF::DefineBitsJPEG2 380 Identical to DefineBits, except it uses a local JPEG table (not the one defined by JPEGTables) 381 ======================== 382 */ 383 void idSWF::DefineBitsJPEG2( idSWFBitStream & bitstream ) { 384 uint16 characterID = bitstream.ReadU16(); 385 386 idDecompressJPEG jpeg; 387 388 int jpegSize = bitstream.Length() - sizeof( uint16 ); 389 390 int width, height; 391 byte * imageData = jpeg.Load( bitstream.ReadData( jpegSize ), jpegSize, width, height ); 392 if ( imageData == NULL ) { 393 return; 394 } 395 396 LoadImage( characterID, imageData, width, height ); 397 398 Mem_Free( imageData ); 399 } 400 401 /* 402 ======================== 403 idSWF::DefineBitsJPEG3 404 Mostly identical to DefineBitsJPEG2, except it has an additional zlib compressed alpha map 405 ======================== 406 */ 407 void idSWF::DefineBitsJPEG3( idSWFBitStream & bitstream ) { 408 uint16 characterID = bitstream.ReadU16(); 409 uint32 jpegSize = bitstream.ReadU32(); 410 411 idDecompressJPEG jpeg; 412 413 int width, height; 414 byte * imageData = jpeg.Load( bitstream.ReadData( jpegSize ), jpegSize, width, height ); 415 if ( imageData == NULL ) { 416 return; 417 } 418 419 { 420 idTempArray<byte> alphaMap( width * height ); 421 422 int alphaSize = bitstream.Length() - jpegSize - sizeof( characterID ) - sizeof( jpegSize ); 423 if ( !Inflate( bitstream.ReadData( alphaSize ), alphaSize, alphaMap.Ptr(), (int)alphaMap.Size() ) ) { 424 idLib::Warning( "DefineBitsJPEG3: Failed to inflate alpha data" ); 425 Mem_Free( imageData ); 426 return; 427 } 428 for ( int i = 0; i < width * height; i++ ) { 429 imageData[i*4+3] = alphaMap[i]; 430 } 431 } 432 433 LoadImage( characterID, imageData, width, height ); 434 435 Mem_Free( imageData ); 436 } 437 438 /* 439 ======================== 440 idSWF::DefineBitsLossless 441 ======================== 442 */ 443 void idSWF::DefineBitsLossless( idSWFBitStream & bitstream ) { 444 uint16 characterID = bitstream.ReadU16(); 445 uint8 format = bitstream.ReadU8(); 446 uint16 width = bitstream.ReadU16(); 447 uint16 height = bitstream.ReadU16(); 448 449 idTempArray< byte > buf( width * height * 4 ); 450 byte * imageData = buf.Ptr(); 451 452 if ( format == 3 ) { 453 uint32 paddedWidth = ( width + 3 ) & ~3; 454 uint32 colorTableSize = ( bitstream.ReadU8() + 1 ) * 3; 455 idTempArray<byte> colorMapData( colorTableSize + ( paddedWidth * height ) ); 456 uint32 colorDataSize = bitstream.Length() - bitstream.Tell(); 457 if ( !Inflate( bitstream.ReadData( colorDataSize ), colorDataSize, colorMapData.Ptr(), (int)colorMapData.Size() ) ) { 458 idLib::Warning( "DefineBitsLossless: Failed to inflate color map data" ); 459 return; 460 } 461 byte * indices = colorMapData.Ptr() + colorTableSize; 462 for ( int h = 0; h < height; h++ ) { 463 for ( int w = 0; w < width; w++ ) { 464 byte index = indices[w + (h*paddedWidth)]; 465 byte * pixel = &imageData[(w + (h*width)) * 4]; 466 pixel[0] = colorMapData[index * 3 + 0]; 467 pixel[1] = colorMapData[index * 3 + 1]; 468 pixel[2] = colorMapData[index * 3 + 2]; 469 pixel[3] = 0xFF; 470 } 471 } 472 } else if ( format == 4 ) { 473 uint32 paddedWidth = ( width + 1 ) & 1; 474 idTempArray<uint16> bitmapData( paddedWidth * height * 2 ); 475 uint32 colorDataSize = bitstream.Length() - bitstream.Tell(); 476 if ( !Inflate( bitstream.ReadData( colorDataSize ), colorDataSize, (byte *)bitmapData.Ptr(), (int)bitmapData.Size() ) ) { 477 idLib::Warning( "DefineBitsLossless: Failed to inflate bitmap data" ); 478 return; 479 } 480 for ( int h = 0; h < height; h++ ) { 481 for ( int w = 0; w < width; w++ ) { 482 uint16 pix15 = bitmapData[w + (h*paddedWidth)]; 483 idSwap::Big( pix15 ); 484 byte * pixel = &imageData[(w + (h*width)) * 4]; 485 pixel[0] = ( pix15 >> 10 ) & 0x1F; 486 pixel[1] = ( pix15 >> 5 ) & 0x1F; 487 pixel[2] = ( pix15 >> 0 ) & 0x1F; 488 pixel[3] = 0xFF; 489 } 490 } 491 } else if ( format == 5 ) { 492 idTempArray<uint32> bitmapData( width * height ); 493 uint32 colorDataSize = bitstream.Length() - bitstream.Tell(); 494 if ( !Inflate( bitstream.ReadData( colorDataSize ), colorDataSize, (byte *)bitmapData.Ptr(), (int)bitmapData.Size() ) ) { 495 idLib::Warning( "DefineBitsLossless: Failed to inflate bitmap data" ); 496 return; 497 } 498 for ( int h = 0; h < height; h++ ) { 499 for ( int w = 0; w < width; w++ ) { 500 uint32 pix24 = bitmapData[w + (h*width)]; 501 idSwap::Big( pix24 ); 502 byte * pixel = &imageData[(w + (h*width)) * 4]; 503 pixel[0] = ( pix24 >> 16 ) & 0xFF; 504 pixel[1] = ( pix24 >> 8 ) & 0xFF; 505 pixel[2] = ( pix24 >> 0 ) & 0xFF; 506 pixel[3] = 0xFF; 507 } 508 } 509 } else { 510 idLib::Warning( "DefineBitsLossless: Unknown image format %d", format ); 511 memset( imageData, 0xFF, width * height * 4 ); 512 } 513 514 LoadImage( characterID, imageData, width, height ); 515 } 516 517 /* 518 ======================== 519 idSWF::DefineBitsLossless2 520 ======================== 521 */ 522 void idSWF::DefineBitsLossless2( idSWFBitStream & bitstream ) { 523 uint16 characterID = bitstream.ReadU16(); 524 uint8 format = bitstream.ReadU8(); 525 uint16 width = bitstream.ReadU16(); 526 uint16 height = bitstream.ReadU16(); 527 528 idTempArray< byte > buf( width * height * 4 ); 529 byte * imageData = buf.Ptr(); 530 531 if ( format == 3 ) { 532 uint32 paddedWidth = ( width + 3 ) & ~3; 533 uint32 colorTableSize = ( bitstream.ReadU8() + 1 ) * 4; 534 idTempArray<byte> colorMapData( colorTableSize + ( paddedWidth * height ) ); 535 uint32 colorDataSize = bitstream.Length() - bitstream.Tell(); 536 if ( !Inflate( bitstream.ReadData( colorDataSize ), colorDataSize, colorMapData.Ptr(), (int)colorMapData.Size() ) ) { 537 idLib::Warning( "DefineBitsLossless2: Failed to inflate color map data" ); 538 return; 539 } 540 byte * indices = colorMapData.Ptr() + colorTableSize; 541 for ( int h = 0; h < height; h++ ) { 542 for ( int w = 0; w < width; w++ ) { 543 byte index = indices[w + (h*paddedWidth)]; 544 byte * pixel = &imageData[(w + (h*width)) * 4]; 545 pixel[0] = colorMapData[index * 4 + 0]; 546 pixel[1] = colorMapData[index * 4 + 1]; 547 pixel[2] = colorMapData[index * 4 + 2]; 548 pixel[3] = colorMapData[index * 4 + 3]; 549 } 550 } 551 } else if ( format == 5 ) { 552 idTempArray<uint32> bitmapData( width * height ); 553 uint32 colorDataSize = bitstream.Length() - bitstream.Tell(); 554 if ( !Inflate( bitstream.ReadData( colorDataSize ), colorDataSize, (byte *)bitmapData.Ptr(), (int)bitmapData.Size() ) ) { 555 idLib::Warning( "DefineBitsLossless2: Failed to inflate bitmap data" ); 556 return; 557 } 558 for ( int h = 0; h < height; h++ ) { 559 for ( int w = 0; w < width; w++ ) { 560 uint32 pix32 = bitmapData[w + (h*width)]; 561 idSwap::Big( pix32 ); 562 byte * pixel = &imageData[(w + (h*width)) * 4]; 563 pixel[0] = ( pix32 >> 16 ) & 0xFF; 564 pixel[1] = ( pix32 >> 8 ) & 0xFF; 565 pixel[2] = ( pix32 >> 0 ) & 0xFF; 566 pixel[3] = ( pix32 >> 24 ) & 0xFF; 567 } 568 } 569 } else { 570 idLib::Warning( "DefineBitsLossless2: Unknown image format %d", format ); 571 memset( imageData, 0xFF, width * height * 4 ); 572 } 573 574 LoadImage( characterID, imageData, width, height ); 575 }