bitmapPng.cpp
Engine/source/gfx/bitmap/loaders/bitmapPng.cpp
Classes:
class
Public Defines
define
PNG_INTERNAL() 1
Public Variables
struct _privateRegisterPNG
Public Functions
pngFatalErrorFn(png_structp , png_const_charp pMessage)
pngFlushDataFn(png_structp )
png_voidp
pngMallocFn(png_structp , png_size_t size)
pngReadDataFn(png_structp png_ptr, png_bytep data, png_size_t length)
pngRealFreeFn(png_structp , png_voidp mem)
png_voidp
pngRealMallocFn(png_structp , png_size_t size)
pngWarningFn(png_structp , png_const_charp )
pngWriteDataFn(png_structp png_ptr, png_bytep data, png_size_t length)
Detailed Description
Public Defines
PNG_INTERNAL() 1
Public Variables
struct _privateRegisterPNG sStaticRegisterPNG
Public Functions
_writePNG(GBitmap * bitmap, Stream & stream, U32 compressionLevel, U32 strategy, U32 filter)
pngFatalErrorFn(png_structp , png_const_charp pMessage)
pngFlushDataFn(png_structp )
pngFreeFn(png_structp , png_voidp )
pngMallocFn(png_structp , png_size_t size)
pngReadDataFn(png_structp png_ptr, png_bytep data, png_size_t length)
pngRealFreeFn(png_structp , png_voidp mem)
pngRealMallocFn(png_structp , png_size_t size)
pngWarningFn(png_structp , png_const_charp )
pngWriteDataFn(png_structp png_ptr, png_bytep data, png_size_t length)
sReadPNG(Stream & stream, GBitmap * bitmap)
sWritePNG(GBitmap * bitmap, Stream & stream, U32 compressionLevel)
Compression levels for PNGs range from 0-9.
A value outside that range will cause the write routine to look for the best compression for a given PNG. This can be slow.
1 2//----------------------------------------------------------------------------- 3// Copyright (c) 2012 GarageGames, LLC 4// 5// Permission is hereby granted, free of charge, to any person obtaining a copy 6// of this software and associated documentation files (the "Software"), to 7// deal in the Software without restriction, including without limitation the 8// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 9// sell copies of the Software, and to permit persons to whom the Software is 10// furnished to do so, subject to the following conditions: 11// 12// The above copyright notice and this permission notice shall be included in 13// all copies or substantial portions of the Software. 14// 15// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 20// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 21// IN THE SOFTWARE. 22//----------------------------------------------------------------------------- 23 24#include "platform/platform.h" 25 26#include "core/stream/fileStream.h" 27#include "core/stream/memStream.h" 28#include "core/strings/stringFunctions.h" 29 30#include "gfx/bitmap/gBitmap.h" 31#include "gfx/bitmap/pngUtils.h" 32 33#define PNG_INTERNAL 1 34#include <time.h> 35#include "lpng/png.h" 36#include "zlib/zlib.h" 37 38#ifdef NULL 39#undef NULL 40#define NULL 0 41#endif 42 43 44static bool sReadPNG(Stream &stream, GBitmap *bitmap); 45 46/// Compression levels for PNGs range from 0-9. 47/// A value outside that range will cause the write routine to look for the best compression for a given PNG. This can be slow. 48static bool sWritePNG(GBitmap *bitmap, Stream &stream, U32 compressionLevel); 49static bool _writePNG(GBitmap *bitmap, Stream &stream, U32 compressionLevel, U32 strategy, U32 filter); 50 51static struct _privateRegisterPNG 52{ 53 _privateRegisterPNG() 54 { 55 GBitmap::Registration reg; 56 57 reg.priority = 100; 58 reg.extensions.push_back( "png" ); 59 60 reg.readFunc = sReadPNG; 61 reg.writeFunc = sWritePNG; 62 reg.defaultCompression = 6; 63 64 GBitmap::sRegisterFormat( reg ); 65 } 66} sStaticRegisterPNG; 67 68 69//-------------------------------------- Replacement I/O for standard LIBPng 70// functions. we don't wanna use 71// FILE*'s... 72static void pngReadDataFn(png_structp png_ptr, 73 png_bytep data, 74 png_size_t length) 75{ 76 AssertFatal(png_get_io_ptr(png_ptr) != NULL, "No stream?"); 77 78 Stream *strm = (Stream*)png_get_io_ptr(png_ptr); 79 bool success = strm->read(length, data); 80 AssertFatal(success, "pngReadDataFn - failed to read from stream!"); 81} 82 83 84//-------------------------------------- 85static void pngWriteDataFn(png_structp png_ptr, 86 png_bytep data, 87 png_size_t length) 88{ 89 AssertFatal(png_get_io_ptr(png_ptr) != NULL, "No stream?"); 90 91 Stream *strm = (Stream*)png_get_io_ptr(png_ptr); 92 bool success = strm->write(length, data); 93 AssertFatal(success, "pngWriteDataFn - failed to write to stream!"); 94} 95 96 97//-------------------------------------- 98static void pngFlushDataFn(png_structp /*png_ptr*/) 99{ 100 // 101} 102 103static png_voidp pngMallocFn(png_structp /*png_ptr*/, png_size_t size) 104{ 105 return FrameAllocator::alloc(size); 106} 107 108static void pngFreeFn(png_structp /*png_ptr*/, png_voidp /*mem*/) 109{ 110} 111 112static png_voidp pngRealMallocFn(png_structp /*png_ptr*/, png_size_t size) 113{ 114 return (png_voidp)dMalloc(size); 115} 116 117static void pngRealFreeFn(png_structp /*png_ptr*/, png_voidp mem) 118{ 119 dFree(mem); 120} 121 122//-------------------------------------- 123static void pngFatalErrorFn(png_structp /*png_ptr*/, 124 png_const_charp pMessage) 125{ 126 AssertISV(false, avar("Error reading PNG file:\n %s", pMessage)); 127} 128 129 130//-------------------------------------- 131static void pngWarningFn(png_structp, png_const_charp /*pMessage*/) 132{ 133 // AssertWarn(false, avar("Warning reading PNG file:\n %s", pMessage)); 134} 135 136 137//-------------------------------------- 138static bool sReadPNG(Stream &stream, GBitmap *bitmap) 139{ 140 static const U32 cs_headerBytesChecked = 8; 141 142 U8 header[cs_headerBytesChecked]; 143 stream.read(cs_headerBytesChecked, header); 144 145 bool isPng = png_check_sig(header, cs_headerBytesChecked) != 0; 146 if (isPng == false) 147 { 148 AssertWarn(false, "GBitmap::readPNG: stream doesn't contain a PNG"); 149 return false; 150 } 151 152 U32 prevWaterMark = FrameAllocator::getWaterMark(); 153 png_structp png_ptr = png_create_read_struct_2(PNG_LIBPNG_VER_STRING, 154 NULL, 155 pngFatalErrorFn, 156 pngWarningFn, 157 NULL, 158 pngRealMallocFn, 159 pngRealFreeFn); 160 161 if (png_ptr == NULL) 162 { 163 FrameAllocator::setWaterMark(prevWaterMark); 164 return false; 165 } 166 167 png_infop info_ptr = png_create_info_struct(png_ptr); 168 if (info_ptr == NULL) 169 { 170 png_destroy_read_struct(&png_ptr, 171 (png_infopp)NULL, 172 (png_infopp)NULL); 173 174 FrameAllocator::setWaterMark(prevWaterMark); 175 return false; 176 } 177 178 png_infop end_info = png_create_info_struct(png_ptr); 179 if (end_info == NULL) 180 { 181 png_destroy_read_struct(&png_ptr, 182 &info_ptr, 183 (png_infopp)NULL); 184 185 FrameAllocator::setWaterMark(prevWaterMark); 186 return false; 187 } 188 189 png_set_read_fn(png_ptr, &stream, pngReadDataFn); 190 191 // Read off the info on the image. 192 png_set_sig_bytes(png_ptr, cs_headerBytesChecked); 193 png_read_info(png_ptr, info_ptr); 194 195 // OK, at this point, if we have reached it ok, then we can reset the 196 // image to accept the new data... 197 // 198 bitmap->deleteImage(); 199 200 png_uint_32 width; 201 png_uint_32 height; 202 S32 bit_depth; 203 S32 color_type; 204 205 png_get_IHDR(png_ptr, info_ptr, 206 &width, &height, // obv. 207 &bit_depth, &color_type, // obv. 208 NULL, // interlace 209 NULL, // compression_type 210 NULL); // filter_type 211 212 // First, handle the color transformations. We need this to read in the 213 // data as RGB or RGBA, _always_, with a maximal channel width of 8 bits. 214 // 215 bool transAlpha = false; 216 GFXFormat format = GFXFormatR8G8B8; 217 218 // Strip off any 16 bit info 219 // 220 if (bit_depth == 16 && color_type != PNG_COLOR_TYPE_GRAY) 221 { 222 png_set_strip_16(png_ptr); 223 } 224 225 // Expand a transparency channel into a full alpha channel... 226 // 227 if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) 228 { 229 png_set_expand(png_ptr); 230 transAlpha = true; 231 } 232 233 if (color_type == PNG_COLOR_TYPE_PALETTE) 234 { 235 png_set_expand(png_ptr); 236 format = transAlpha ? GFXFormatR8G8B8A8 : GFXFormatR8G8B8; 237 } 238 else if (color_type == PNG_COLOR_TYPE_GRAY) 239 { 240 png_set_expand(png_ptr); 241 242 if (bit_depth == 16) 243 format = GFXFormatR5G6B5; 244 else 245 format = GFXFormatA8; 246 } 247 else if (color_type == PNG_COLOR_TYPE_GRAY_ALPHA) 248 { 249 png_set_expand(png_ptr); 250 png_set_gray_to_rgb(png_ptr); 251 format = GFXFormatR8G8B8A8; 252 } 253 else if (color_type == PNG_COLOR_TYPE_RGB) 254 { 255 format = transAlpha ? GFXFormatR8G8B8A8 : GFXFormatR8G8B8; 256 png_set_expand(png_ptr); 257 } 258 else if (color_type == PNG_COLOR_TYPE_RGB_ALPHA) 259 { 260 png_set_expand(png_ptr); 261 format = GFXFormatR8G8B8A8; 262 } 263 264 // Update the info pointer with the result of the transformations 265 // above... 266 png_read_update_info(png_ptr, info_ptr); 267 268 png_uint_32 rowBytes = png_get_rowbytes(png_ptr, info_ptr); 269 if (format == GFXFormatR8G8B8) 270 { 271 AssertFatal(rowBytes == width * 3, 272 "Error, our rowbytes are incorrect for this transform... (3)"); 273 } 274 else if (format == GFXFormatR8G8B8A8) 275 { 276 AssertFatal(rowBytes == width * 4, 277 "Error, our rowbytes are incorrect for this transform... (4)"); 278 } 279 else if (format == GFXFormatR5G6B5) 280 { 281 AssertFatal(rowBytes == width * 2, 282 "Error, our rowbytes are incorrect for this transform... (2)"); 283 } 284 285 // actually allocate the bitmap space... 286 bitmap->allocateBitmap(width, height, 287 false, // don't extrude miplevels... 288 format); // use determined format... 289 290 // Set up the row pointers... 291 png_bytep* rowPointers = new png_bytep[ height ]; 292 U8* pBase = (U8*)bitmap->getBits(); 293 294 for (U32 i = 0; i < height; i++) 295 rowPointers[i] = pBase + (i * rowBytes); 296 297 // And actually read the image! 298 png_read_image(png_ptr, rowPointers); 299 300 // We're outta here, destroy the png structs, and release the lock 301 // as quickly as possible... 302 //png_read_end(png_ptr, end_info); 303 delete [] rowPointers; 304 png_read_end(png_ptr, NULL); 305 png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); 306 307 // Ok, the image is read in, now we need to finish up the initialization, 308 // which means: setting up the detailing members, init'ing the palette 309 // key, etc... 310 // 311 // actually, all of that was handled by allocateBitmap, so we're outta here 312 // 313 314 // Check this bitmap for transparency 315 bitmap->checkForTransparency(); 316 317 FrameAllocator::setWaterMark(prevWaterMark); 318 319 return true; 320} 321 322//-------------------------------------------------------------------------- 323static bool _writePNG(GBitmap *bitmap, Stream &stream, U32 compressionLevel, U32 strategy, U32 filter) 324{ 325 GFXFormat format = bitmap->getFormat(); 326 327 // ONLY RGB bitmap writing supported at this time! 328 AssertFatal( format == GFXFormatR8G8B8 || 329 format == GFXFormatR8G8B8A8 || 330 format == GFXFormatR8G8B8X8 || 331 format == GFXFormatA8 || 332 format == GFXFormatR5G6B5 || 333 format == GFXFormatR8G8B8A8_LINEAR_FORCE, "_writePNG: ONLY RGB bitmap writing supported at this time."); 334 335 if ( format != GFXFormatR8G8B8 && 336 format != GFXFormatR8G8B8A8 && 337 format != GFXFormatR8G8B8X8 && 338 format != GFXFormatA8 && 339 format != GFXFormatR5G6B5 && format != GFXFormatR8G8B8A8_LINEAR_FORCE) 340 return false; 341 342 png_structp png_ptr = png_create_write_struct_2(PNG_LIBPNG_VER_STRING, 343 NULL, 344 pngFatalErrorFn, 345 pngWarningFn, 346 NULL, 347 pngMallocFn, 348 pngFreeFn); 349 if (png_ptr == NULL) 350 return (false); 351 352 png_infop info_ptr = png_create_info_struct(png_ptr); 353 if (info_ptr == NULL) 354 { 355 png_destroy_write_struct(&png_ptr, (png_infopp)NULL); 356 return false; 357 } 358 359 png_set_write_fn(png_ptr, &stream, pngWriteDataFn, pngFlushDataFn); 360 361 // Set the compression level and image filters 362 png_set_compression_window_bits(png_ptr, 15); 363 png_set_compression_level(png_ptr, compressionLevel); 364 png_set_filter(png_ptr, 0, filter); 365 366 // Set the image information here. Width and height are up to 2^31, 367 // bit_depth is one of 1, 2, 4, 8, or 16, but valid values also depend on 368 // the color_type selected. color_type is one of PNG_COLOR_TYPE_GRAY, 369 // PNG_COLOR_TYPE_GRAY_ALPHA, PNG_COLOR_TYPE_PALETTE, PNG_COLOR_TYPE_RGB, 370 // or PNG_COLOR_TYPE_RGB_ALPHA. interlace is either PNG_INTERLACE_NONE or 371 // PNG_INTERLACE_ADAM7, and the compression_type and filter_type MUST 372 // currently be PNG_COMPRESSION_TYPE_BASE and PNG_FILTER_TYPE_BASE. REQUIRED 373 374 U32 width = bitmap->getWidth(); 375 U32 height = bitmap->getHeight(); 376 377 if (format == GFXFormatR8G8B8) 378 { 379 png_set_IHDR(png_ptr, info_ptr, 380 width, height, // the width & height 381 8, PNG_COLOR_TYPE_RGB, // bit_depth, color_type, 382 NULL, // no interlace 383 NULL, // compression type 384 NULL); // filter type 385 } 386 else if (format == GFXFormatR8G8B8A8 || format == GFXFormatR8G8B8X8 || format == GFXFormatR8G8B8A8_LINEAR_FORCE) 387 { 388 png_set_IHDR(png_ptr, info_ptr, 389 width, height, // the width & height 390 8, PNG_COLOR_TYPE_RGB_ALPHA, // bit_depth, color_type, 391 NULL, // no interlace 392 NULL, // compression type 393 NULL); // filter type 394 } 395 else if (format == GFXFormatA8) 396 { 397 png_set_IHDR(png_ptr, info_ptr, 398 width, height, // the width & height 399 8, PNG_COLOR_TYPE_GRAY, // bit_depth, color_type, 400 NULL, // no interlace 401 NULL, // compression type 402 NULL); // filter type 403 } 404 else if (format == GFXFormatR5G6B5) 405 { 406 png_set_IHDR(png_ptr, info_ptr, 407 width, height, // the width & height 408 16, PNG_COLOR_TYPE_GRAY, // bit_depth, color_type, 409 PNG_INTERLACE_NONE, // no interlace 410 PNG_COMPRESSION_TYPE_DEFAULT, // compression type 411 PNG_FILTER_TYPE_DEFAULT); // filter type 412 413 png_color_8_struct sigBit = { 0 }; 414 sigBit.gray = 16; 415 png_set_sBIT(png_ptr, info_ptr, &sigBit ); 416 417 png_set_swap( png_ptr ); 418 } 419 420 png_write_info(png_ptr, info_ptr); 421 FrameAllocatorMarker marker; 422 png_bytep* row_pointers = (png_bytep*)marker.alloc( height * sizeof( png_bytep ) ); 423 for (U32 i=0; i<height; i++) 424 row_pointers[i] = const_cast<png_bytep>(bitmap->getAddress(0, i)); 425 426 png_write_image(png_ptr, row_pointers); 427 428 // Write S3TC data if present... 429 // Write FXT1 data if present... 430 431 png_write_end(png_ptr, info_ptr); 432 png_destroy_write_struct(&png_ptr, (png_infopp)NULL); 433 434 return true; 435} 436 437 438//-------------------------------------------------------------------------- 439static bool sWritePNG(GBitmap *bitmap, Stream &stream, U32 compressionLevel) 440{ 441 U32 waterMark = FrameAllocator::getWaterMark(); 442 443 if ( compressionLevel < 10 ) 444 { 445 bool retVal = _writePNG(bitmap, stream, compressionLevel, 0, PNG_ALL_FILTERS); 446 FrameAllocator::setWaterMark(waterMark); 447 return retVal; 448 } 449 450 // check all our methods of compression to find the best one and use it 451 U8* buffer = new U8[1 << 22]; // 4 Megs. Should be enough... 452 MemStream* pMemStream = new MemStream(1 << 22, buffer, false, true); 453 454 const U32 zStrategies[] = { Z_DEFAULT_STRATEGY, 455 Z_FILTERED }; 456 const U32 pngFilters[] = { PNG_FILTER_NONE, 457 PNG_FILTER_SUB, 458 PNG_FILTER_UP, 459 PNG_FILTER_AVG, 460 PNG_FILTER_PAETH, 461 PNG_ALL_FILTERS }; 462 463 U32 minSize = 0xFFFFFFFF; 464 U32 bestStrategy = 0xFFFFFFFF; 465 U32 bestFilter = 0xFFFFFFFF; 466 U32 bestCLevel = 0xFFFFFFFF; 467 468 for (U32 cl = 0; cl <=9; cl++) 469 { 470 for (U32 zs = 0; zs < 2; zs++) 471 { 472 for (U32 pf = 0; pf < 6; pf++) 473 { 474 pMemStream->setPosition(0); 475 476 U32 waterMarkInner = FrameAllocator::getWaterMark(); 477 478 if (_writePNG(bitmap, *pMemStream, cl, zStrategies[zs], pngFilters[pf]) == false) 479 AssertFatal(false, "Handle this error!"); 480 481 FrameAllocator::setWaterMark(waterMarkInner); 482 483 if (pMemStream->getPosition() < minSize) 484 { 485 minSize = pMemStream->getPosition(); 486 bestStrategy = zs; 487 bestFilter = pf; 488 bestCLevel = cl; 489 } 490 } 491 } 492 } 493 AssertFatal(minSize != 0xFFFFFFFF, "Error, no best found?"); 494 495 delete pMemStream; 496 delete [] buffer; 497 498 499 bool retVal = _writePNG(bitmap, stream, 500 bestCLevel, 501 zStrategies[bestStrategy], 502 pngFilters[bestFilter]); 503 FrameAllocator::setWaterMark(waterMark); 504 505 return retVal; 506} 507 508//-------------------------------------------------------------------------- 509// Stores PNG stream data 510struct DeferredPNGWriterData { 511 png_structp png_ptr; 512 png_infop info_ptr; 513 U32 width; 514 U32 height; 515}; 516DeferredPNGWriter::DeferredPNGWriter() : 517 mData( NULL ), 518 mActive(false) 519{ 520 mData = new DeferredPNGWriterData(); 521} 522DeferredPNGWriter::~DeferredPNGWriter() 523{ 524 delete mData; 525} 526 527bool DeferredPNGWriter::begin( GFXFormat format, S32 width, S32 height, Stream &stream, U32 compressionLevel ) 528{ 529 // ONLY RGB bitmap writing supported at this time! 530 AssertFatal( format == GFXFormatR8G8B8 || 531 format == GFXFormatR8G8B8A8 || 532 format == GFXFormatR8G8B8X8 || 533 format == GFXFormatA8 || 534 format == GFXFormatR5G6B5, "_writePNG: ONLY RGB bitmap writing supported at this time."); 535 536 if ( format != GFXFormatR8G8B8 && 537 format != GFXFormatR8G8B8A8 && 538 format != GFXFormatR8G8B8X8 && 539 format != GFXFormatA8 && 540 format != GFXFormatR5G6B5 ) 541 return false; 542 543 mData->png_ptr = png_create_write_struct_2(PNG_LIBPNG_VER_STRING, 544 NULL, 545 pngFatalErrorFn, 546 pngWarningFn, 547 NULL, 548 pngRealMallocFn, 549 pngRealFreeFn); 550 if (mData->png_ptr == NULL) 551 return (false); 552 553 mData->info_ptr = png_create_info_struct(mData->png_ptr); 554 if (mData->info_ptr == NULL) 555 { 556 png_destroy_write_struct(&mData->png_ptr, (png_infopp)NULL); 557 return false; 558 } 559 560 png_set_write_fn(mData->png_ptr, &stream, pngWriteDataFn, pngFlushDataFn); 561 562 // Set the compression level and image filters 563 png_set_compression_window_bits(mData->png_ptr, 15); 564 png_set_compression_level(mData->png_ptr, compressionLevel); 565 png_set_filter(mData->png_ptr, 0, PNG_ALL_FILTERS); 566 567 // Set the image information here. Width and height are up to 2^31, 568 // bit_depth is one of 1, 2, 4, 8, or 16, but valid values also depend on 569 // the color_type selected. color_type is one of PNG_COLOR_TYPE_GRAY, 570 // PNG_COLOR_TYPE_GRAY_ALPHA, PNG_COLOR_TYPE_PALETTE, PNG_COLOR_TYPE_RGB, 571 // or PNG_COLOR_TYPE_RGB_ALPHA. interlace is either PNG_INTERLACE_NONE or 572 // PNG_INTERLACE_ADAM7, and the compression_type and filter_type MUST 573 // currently be PNG_COMPRESSION_TYPE_BASE and PNG_FILTER_TYPE_BASE. REQUIRED 574 575 if (format == GFXFormatR8G8B8) 576 { 577 png_set_IHDR(mData->png_ptr, mData->info_ptr, 578 width, height, // the width & height 579 8, PNG_COLOR_TYPE_RGB, // bit_depth, color_type, 580 NULL, // no interlace 581 NULL, // compression type 582 NULL); // filter type 583 } 584 else if (format == GFXFormatR8G8B8A8 || format == GFXFormatR8G8B8X8) 585 { 586 png_set_IHDR(mData->png_ptr, mData->info_ptr, 587 width, height, // the width & height 588 8, PNG_COLOR_TYPE_RGB_ALPHA, // bit_depth, color_type, 589 NULL, // no interlace 590 NULL, // compression type 591 NULL); // filter type 592 } 593 else if (format == GFXFormatA8) 594 { 595 png_set_IHDR(mData->png_ptr, mData->info_ptr, 596 width, height, // the width & height 597 8, PNG_COLOR_TYPE_GRAY, // bit_depth, color_type, 598 NULL, // no interlace 599 NULL, // compression type 600 NULL); // filter type 601 } 602 else if (format == GFXFormatR5G6B5) 603 { 604 png_set_IHDR(mData->png_ptr, mData->info_ptr, 605 width, height, // the width & height 606 16, PNG_COLOR_TYPE_GRAY, // bit_depth, color_type, 607 PNG_INTERLACE_NONE, // no interlace 608 PNG_COMPRESSION_TYPE_DEFAULT, // compression type 609 PNG_FILTER_TYPE_DEFAULT); // filter type 610 611 png_color_8_struct sigBit = { 0 }; 612 sigBit.gray = 16; 613 png_set_sBIT(mData->png_ptr, mData->info_ptr, &sigBit ); 614 615 png_set_swap( mData->png_ptr ); 616 } 617 618 png_write_info(mData->png_ptr, mData->info_ptr); 619 620 mActive = true; 621 622 return true; 623} 624void DeferredPNGWriter::append( GBitmap* bitmap, U32 rows) 625{ 626 AssertFatal(mActive, "Cannot append to an inactive DeferredPNGWriter!"); 627 628 U32 height = getMin( bitmap->getHeight(), rows); 629 630 FrameAllocatorMarker marker; 631 png_bytep* row_pointers = (png_bytep*)marker.alloc( height * sizeof( png_bytep ) ); 632 for (U32 i=0; i<height; i++) 633 row_pointers[i] = const_cast<png_bytep>(bitmap->getAddress(0, i)); 634 635 png_write_rows(mData->png_ptr, row_pointers, height); 636} 637void DeferredPNGWriter::end() 638{ 639 AssertFatal(mActive, "Cannot end an inactive DeferredPNGWriter!"); 640 641 png_write_end(mData->png_ptr, mData->info_ptr); 642 png_destroy_write_struct(&mData->png_ptr, (png_infopp)NULL); 643 644 mActive = false; 645} 646
