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   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