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 PROFILE_SCOPE(sReadPNG); 141 static const U32 cs_headerBytesChecked = 8; 142 143 U8 header[cs_headerBytesChecked]; 144 stream.read(cs_headerBytesChecked, header); 145 146 bool isPng = png_check_sig(header, cs_headerBytesChecked) != 0; 147 if (isPng == false) 148 { 149 AssertWarn(false, "GBitmap::readPNG: stream doesn't contain a PNG"); 150 return false; 151 } 152 153 U32 prevWaterMark = FrameAllocator::getWaterMark(); 154 png_structp png_ptr = png_create_read_struct_2(PNG_LIBPNG_VER_STRING, 155 NULL, 156 pngFatalErrorFn, 157 pngWarningFn, 158 NULL, 159 pngRealMallocFn, 160 pngRealFreeFn); 161 162 if (png_ptr == NULL) 163 { 164 FrameAllocator::setWaterMark(prevWaterMark); 165 return false; 166 } 167 168 png_infop info_ptr = png_create_info_struct(png_ptr); 169 if (info_ptr == NULL) 170 { 171 png_destroy_read_struct(&png_ptr, 172 (png_infopp)NULL, 173 (png_infopp)NULL); 174 175 FrameAllocator::setWaterMark(prevWaterMark); 176 return false; 177 } 178 179 png_infop end_info = png_create_info_struct(png_ptr); 180 if (end_info == NULL) 181 { 182 png_destroy_read_struct(&png_ptr, 183 &info_ptr, 184 (png_infopp)NULL); 185 186 FrameAllocator::setWaterMark(prevWaterMark); 187 return false; 188 } 189 190 png_set_read_fn(png_ptr, &stream, pngReadDataFn); 191 192 // Read off the info on the image. 193 png_set_sig_bytes(png_ptr, cs_headerBytesChecked); 194 png_read_info(png_ptr, info_ptr); 195 196 // OK, at this point, if we have reached it ok, then we can reset the 197 // image to accept the new data... 198 // 199 bitmap->deleteImage(); 200 201 png_uint_32 width; 202 png_uint_32 height; 203 S32 bit_depth; 204 S32 color_type; 205 206 png_get_IHDR(png_ptr, info_ptr, 207 &width, &height, // obv. 208 &bit_depth, &color_type, // obv. 209 NULL, // interlace 210 NULL, // compression_type 211 NULL); // filter_type 212 213 // First, handle the color transformations. We need this to read in the 214 // data as RGB or RGBA, _always_, with a maximal channel width of 8 bits. 215 // 216 bool transAlpha = false; 217 GFXFormat format = GFXFormatR8G8B8; 218 219 // Strip off any 16 bit info 220 // 221 if (bit_depth == 16 && color_type != PNG_COLOR_TYPE_GRAY) 222 { 223 png_set_strip_16(png_ptr); 224 } 225 226 // Expand a transparency channel into a full alpha channel... 227 // 228 if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) 229 { 230 png_set_expand(png_ptr); 231 transAlpha = true; 232 } 233 234 if (color_type == PNG_COLOR_TYPE_PALETTE) 235 { 236 png_set_expand(png_ptr); 237 format = transAlpha ? GFXFormatR8G8B8A8 : GFXFormatR8G8B8; 238 } 239 else if (color_type == PNG_COLOR_TYPE_GRAY) 240 { 241 png_set_expand(png_ptr); 242 243 if (bit_depth == 16) 244 format = GFXFormatL16; 245 else 246 format = GFXFormatA8; 247 } 248 else if (color_type == PNG_COLOR_TYPE_GRAY_ALPHA) 249 { 250 png_set_expand(png_ptr); 251 png_set_gray_to_rgb(png_ptr); 252 format = GFXFormatR8G8B8A8; 253 } 254 else if (color_type == PNG_COLOR_TYPE_RGB) 255 { 256 format = transAlpha ? GFXFormatR8G8B8A8 : GFXFormatR8G8B8; 257 png_set_expand(png_ptr); 258 } 259 else if (color_type == PNG_COLOR_TYPE_RGB_ALPHA) 260 { 261 png_set_expand(png_ptr); 262 format = GFXFormatR8G8B8A8; 263 } 264 265 // Update the info pointer with the result of the transformations 266 // above... 267 png_read_update_info(png_ptr, info_ptr); 268 269 png_uint_32 rowBytes = png_get_rowbytes(png_ptr, info_ptr); 270 if (format == GFXFormatR8G8B8) 271 { 272 AssertFatal(rowBytes == width * 3, 273 "Error, our rowbytes are incorrect for this transform... (3)"); 274 } 275 else if (format == GFXFormatR8G8B8A8) 276 { 277 AssertFatal(rowBytes == width * 4, 278 "Error, our rowbytes are incorrect for this transform... (4)"); 279 } 280 else if (format == GFXFormatL16) 281 { 282 AssertFatal(rowBytes == width * 2, 283 "Error, our rowbytes are incorrect for this transform... (2)"); 284 } 285 286 // actually allocate the bitmap space... 287 bitmap->allocateBitmap(width, height, 288 false, // don't extrude miplevels... 289 format); // use determined format... 290 291 // Set up the row pointers... 292 png_bytep* rowPointers = new png_bytep[ height ]; 293 U8* pBase = (U8*)bitmap->getBits(); 294 295 for (U32 i = 0; i < height; i++) 296 rowPointers[i] = pBase + (i * rowBytes); 297 298 // And actually read the image! 299 png_read_image(png_ptr, rowPointers); 300 301 // We're outta here, destroy the png structs, and release the lock 302 // as quickly as possible... 303 //png_read_end(png_ptr, end_info); 304 delete [] rowPointers; 305 png_read_end(png_ptr, NULL); 306 png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); 307 308 // Ok, the image is read in, now we need to finish up the initialization, 309 // which means: setting up the detailing members, init'ing the palette 310 // key, etc... 311 // 312 // actually, all of that was handled by allocateBitmap, so we're outta here 313 // 314 315 // Check this bitmap for transparency 316 bitmap->checkForTransparency(); 317 318 FrameAllocator::setWaterMark(prevWaterMark); 319 320 return true; 321} 322 323//-------------------------------------------------------------------------- 324static bool _writePNG(GBitmap *bitmap, Stream &stream, U32 compressionLevel, U32 strategy, U32 filter) 325{ 326 GFXFormat format = bitmap->getFormat(); 327 328 // ONLY RGB bitmap writing supported at this time! 329 AssertFatal( format == GFXFormatR8G8B8 || 330 format == GFXFormatR8G8B8A8 || 331 format == GFXFormatR8G8B8X8 || 332 format == GFXFormatA8 || 333 format == GFXFormatR5G6B5 || 334 format == GFXFormatR8G8B8A8_LINEAR_FORCE, "_writePNG: ONLY RGB bitmap writing supported at this time."); 335 336 if ( format != GFXFormatR8G8B8 && 337 format != GFXFormatR8G8B8A8 && 338 format != GFXFormatR8G8B8X8 && 339 format != GFXFormatA8 && 340 format != GFXFormatR5G6B5 && format != GFXFormatR8G8B8A8_LINEAR_FORCE) 341 return false; 342 343 png_structp png_ptr = png_create_write_struct_2(PNG_LIBPNG_VER_STRING, 344 NULL, 345 pngFatalErrorFn, 346 pngWarningFn, 347 NULL, 348 pngMallocFn, 349 pngFreeFn); 350 if (png_ptr == NULL) 351 return (false); 352 353 png_infop info_ptr = png_create_info_struct(png_ptr); 354 if (info_ptr == NULL) 355 { 356 png_destroy_write_struct(&png_ptr, (png_infopp)NULL); 357 return false; 358 } 359 360 png_set_write_fn(png_ptr, &stream, pngWriteDataFn, pngFlushDataFn); 361 362 // Set the compression level and image filters 363 png_set_compression_window_bits(png_ptr, 15); 364 png_set_compression_level(png_ptr, compressionLevel); 365 png_set_filter(png_ptr, 0, filter); 366 367 // Set the image information here. Width and height are up to 2^31, 368 // bit_depth is one of 1, 2, 4, 8, or 16, but valid values also depend on 369 // the color_type selected. color_type is one of PNG_COLOR_TYPE_GRAY, 370 // PNG_COLOR_TYPE_GRAY_ALPHA, PNG_COLOR_TYPE_PALETTE, PNG_COLOR_TYPE_RGB, 371 // or PNG_COLOR_TYPE_RGB_ALPHA. interlace is either PNG_INTERLACE_NONE or 372 // PNG_INTERLACE_ADAM7, and the compression_type and filter_type MUST 373 // currently be PNG_COMPRESSION_TYPE_BASE and PNG_FILTER_TYPE_BASE. REQUIRED 374 375 U32 width = bitmap->getWidth(); 376 U32 height = bitmap->getHeight(); 377 378 if (format == GFXFormatR8G8B8) 379 { 380 png_set_IHDR(png_ptr, info_ptr, 381 width, height, // the width & height 382 8, PNG_COLOR_TYPE_RGB, // bit_depth, color_type, 383 NULL, // no interlace 384 NULL, // compression type 385 NULL); // filter type 386 } 387 else if (format == GFXFormatR8G8B8A8 || format == GFXFormatR8G8B8X8 || format == GFXFormatR8G8B8A8_LINEAR_FORCE) 388 { 389 png_set_IHDR(png_ptr, info_ptr, 390 width, height, // the width & height 391 8, PNG_COLOR_TYPE_RGB_ALPHA, // bit_depth, color_type, 392 NULL, // no interlace 393 NULL, // compression type 394 NULL); // filter type 395 } 396 else if (format == GFXFormatA8) 397 { 398 png_set_IHDR(png_ptr, info_ptr, 399 width, height, // the width & height 400 8, PNG_COLOR_TYPE_GRAY, // bit_depth, color_type, 401 NULL, // no interlace 402 NULL, // compression type 403 NULL); // filter type 404 } 405 else if (format == GFXFormatR5G6B5) 406 { 407 png_set_IHDR(png_ptr, info_ptr, 408 width, height, // the width & height 409 16, PNG_COLOR_TYPE_GRAY, // bit_depth, color_type, 410 PNG_INTERLACE_NONE, // no interlace 411 PNG_COMPRESSION_TYPE_DEFAULT, // compression type 412 PNG_FILTER_TYPE_DEFAULT); // filter type 413 414 png_color_8_struct sigBit = { 0 }; 415 sigBit.gray = 16; 416 png_set_sBIT(png_ptr, info_ptr, &sigBit ); 417 418 png_set_swap( png_ptr ); 419 } 420 421 png_write_info(png_ptr, info_ptr); 422 FrameAllocatorMarker marker; 423 png_bytep* row_pointers = (png_bytep*)marker.alloc( height * sizeof( png_bytep ) ); 424 for (U32 i=0; i<height; i++) 425 row_pointers[i] = const_cast<png_bytep>(bitmap->getAddress(0, i)); 426 427 png_write_image(png_ptr, row_pointers); 428 429 // Write S3TC data if present... 430 // Write FXT1 data if present... 431 432 png_write_end(png_ptr, info_ptr); 433 png_destroy_write_struct(&png_ptr, (png_infopp)NULL); 434 435 return true; 436} 437 438 439//-------------------------------------------------------------------------- 440static bool sWritePNG(GBitmap *bitmap, Stream &stream, U32 compressionLevel) 441{ 442 U32 waterMark = FrameAllocator::getWaterMark(); 443 444 if ( compressionLevel < 10 ) 445 { 446 bool retVal = _writePNG(bitmap, stream, compressionLevel, 0, PNG_ALL_FILTERS); 447 FrameAllocator::setWaterMark(waterMark); 448 return retVal; 449 } 450 451 // check all our methods of compression to find the best one and use it 452 U8* buffer = new U8[1 << 22]; // 4 Megs. Should be enough... 453 MemStream* pMemStream = new MemStream(1 << 22, buffer, false, true); 454 455 const U32 zStrategies[] = { Z_DEFAULT_STRATEGY, 456 Z_FILTERED }; 457 const U32 pngFilters[] = { PNG_FILTER_NONE, 458 PNG_FILTER_SUB, 459 PNG_FILTER_UP, 460 PNG_FILTER_AVG, 461 PNG_FILTER_PAETH, 462 PNG_ALL_FILTERS }; 463 464 U32 minSize = 0xFFFFFFFF; 465 U32 bestStrategy = 0xFFFFFFFF; 466 U32 bestFilter = 0xFFFFFFFF; 467 U32 bestCLevel = 0xFFFFFFFF; 468 469 for (U32 cl = 0; cl <=9; cl++) 470 { 471 for (U32 zs = 0; zs < 2; zs++) 472 { 473 for (U32 pf = 0; pf < 6; pf++) 474 { 475 pMemStream->setPosition(0); 476 477 U32 waterMarkInner = FrameAllocator::getWaterMark(); 478 479 if (_writePNG(bitmap, *pMemStream, cl, zStrategies[zs], pngFilters[pf]) == false) 480 AssertFatal(false, "Handle this error!"); 481 482 FrameAllocator::setWaterMark(waterMarkInner); 483 484 if (pMemStream->getPosition() < minSize) 485 { 486 minSize = pMemStream->getPosition(); 487 bestStrategy = zs; 488 bestFilter = pf; 489 bestCLevel = cl; 490 } 491 } 492 } 493 } 494 AssertFatal(minSize != 0xFFFFFFFF, "Error, no best found?"); 495 496 delete pMemStream; 497 delete [] buffer; 498 499 500 bool retVal = _writePNG(bitmap, stream, 501 bestCLevel, 502 zStrategies[bestStrategy], 503 pngFilters[bestFilter]); 504 FrameAllocator::setWaterMark(waterMark); 505 506 return retVal; 507} 508 509//-------------------------------------------------------------------------- 510// Stores PNG stream data 511struct DeferredPNGWriterData { 512 png_structp png_ptr; 513 png_infop info_ptr; 514 U32 width; 515 U32 height; 516}; 517DeferredPNGWriter::DeferredPNGWriter() : 518 mData( NULL ), 519 mActive(false) 520{ 521 mData = new DeferredPNGWriterData(); 522} 523DeferredPNGWriter::~DeferredPNGWriter() 524{ 525 delete mData; 526} 527 528bool DeferredPNGWriter::begin( GFXFormat format, S32 width, S32 height, Stream &stream, U32 compressionLevel ) 529{ 530 // ONLY RGB bitmap writing supported at this time! 531 AssertFatal( format == GFXFormatR8G8B8 || 532 format == GFXFormatR8G8B8A8 || 533 format == GFXFormatR8G8B8X8 || 534 format == GFXFormatA8 || 535 format == GFXFormatR5G6B5, "_writePNG: ONLY RGB bitmap writing supported at this time."); 536 537 if ( format != GFXFormatR8G8B8 && 538 format != GFXFormatR8G8B8A8 && 539 format != GFXFormatR8G8B8X8 && 540 format != GFXFormatA8 && 541 format != GFXFormatR5G6B5 ) 542 return false; 543 544 mData->png_ptr = png_create_write_struct_2(PNG_LIBPNG_VER_STRING, 545 NULL, 546 pngFatalErrorFn, 547 pngWarningFn, 548 NULL, 549 pngRealMallocFn, 550 pngRealFreeFn); 551 if (mData->png_ptr == NULL) 552 return (false); 553 554 mData->info_ptr = png_create_info_struct(mData->png_ptr); 555 if (mData->info_ptr == NULL) 556 { 557 png_destroy_write_struct(&mData->png_ptr, (png_infopp)NULL); 558 return false; 559 } 560 561 png_set_write_fn(mData->png_ptr, &stream, pngWriteDataFn, pngFlushDataFn); 562 563 // Set the compression level and image filters 564 png_set_compression_window_bits(mData->png_ptr, 15); 565 png_set_compression_level(mData->png_ptr, compressionLevel); 566 png_set_filter(mData->png_ptr, 0, PNG_ALL_FILTERS); 567 568 // Set the image information here. Width and height are up to 2^31, 569 // bit_depth is one of 1, 2, 4, 8, or 16, but valid values also depend on 570 // the color_type selected. color_type is one of PNG_COLOR_TYPE_GRAY, 571 // PNG_COLOR_TYPE_GRAY_ALPHA, PNG_COLOR_TYPE_PALETTE, PNG_COLOR_TYPE_RGB, 572 // or PNG_COLOR_TYPE_RGB_ALPHA. interlace is either PNG_INTERLACE_NONE or 573 // PNG_INTERLACE_ADAM7, and the compression_type and filter_type MUST 574 // currently be PNG_COMPRESSION_TYPE_BASE and PNG_FILTER_TYPE_BASE. REQUIRED 575 576 if (format == GFXFormatR8G8B8) 577 { 578 png_set_IHDR(mData->png_ptr, mData->info_ptr, 579 width, height, // the width & height 580 8, PNG_COLOR_TYPE_RGB, // bit_depth, color_type, 581 NULL, // no interlace 582 NULL, // compression type 583 NULL); // filter type 584 } 585 else if (format == GFXFormatR8G8B8A8 || format == GFXFormatR8G8B8X8) 586 { 587 png_set_IHDR(mData->png_ptr, mData->info_ptr, 588 width, height, // the width & height 589 8, PNG_COLOR_TYPE_RGB_ALPHA, // bit_depth, color_type, 590 NULL, // no interlace 591 NULL, // compression type 592 NULL); // filter type 593 } 594 else if (format == GFXFormatA8) 595 { 596 png_set_IHDR(mData->png_ptr, mData->info_ptr, 597 width, height, // the width & height 598 8, PNG_COLOR_TYPE_GRAY, // bit_depth, color_type, 599 NULL, // no interlace 600 NULL, // compression type 601 NULL); // filter type 602 } 603 else if (format == GFXFormatR5G6B5) 604 { 605 png_set_IHDR(mData->png_ptr, mData->info_ptr, 606 width, height, // the width & height 607 16, PNG_COLOR_TYPE_GRAY, // bit_depth, color_type, 608 PNG_INTERLACE_NONE, // no interlace 609 PNG_COMPRESSION_TYPE_DEFAULT, // compression type 610 PNG_FILTER_TYPE_DEFAULT); // filter type 611 612 png_color_8_struct sigBit = { 0 }; 613 sigBit.gray = 16; 614 png_set_sBIT(mData->png_ptr, mData->info_ptr, &sigBit ); 615 616 png_set_swap( mData->png_ptr ); 617 } 618 619 png_write_info(mData->png_ptr, mData->info_ptr); 620 621 mActive = true; 622 623 return true; 624} 625void DeferredPNGWriter::append( GBitmap* bitmap, U32 rows) 626{ 627 AssertFatal(mActive, "Cannot append to an inactive DeferredPNGWriter!"); 628 629 U32 height = getMin( bitmap->getHeight(), rows); 630 631 FrameAllocatorMarker marker; 632 png_bytep* row_pointers = (png_bytep*)marker.alloc( height * sizeof( png_bytep ) ); 633 for (U32 i=0; i<height; i++) 634 row_pointers[i] = const_cast<png_bytep>(bitmap->getAddress(0, i)); 635 636 png_write_rows(mData->png_ptr, row_pointers, height); 637} 638void DeferredPNGWriter::end() 639{ 640 AssertFatal(mActive, "Cannot end an inactive DeferredPNGWriter!"); 641 642 png_write_end(mData->png_ptr, mData->info_ptr); 643 png_destroy_write_struct(&mData->png_ptr, (png_infopp)NULL); 644 645 mActive = false; 646} 647