bitmapPng.cpp

Engine/source/gfx/bitmap/loaders/bitmapPng.cpp

More...

Classes:

Public Defines

define

Public Variables

struct _privateRegisterPNG

Public Functions

bool
_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 )
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)
bool
sReadPNG(Stream & stream, GBitmap * bitmap)
bool
sWritePNG(GBitmap * bitmap, Stream & stream, U32 compressionLevel)

Compression levels for PNGs range from 0-9.

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