+
+ /*
+ If saving palettised was requested but it was decided we can't use a
+ palette then reset the colour type to RGB.
+ */
+ if (!bUsePalette && iColorType == wxPNG_TYPE_PALETTE)
+ {
+ iColorType = wxPNG_TYPE_COLOUR;
+ }
+
+ bool bUseAlpha = !bUsePalette && (bHasAlpha || bHasMask);
+
+ int iPngColorType;
+
+ if (bUsePalette)
+ {
+ iPngColorType = PNG_COLOR_TYPE_PALETTE;
+ iColorType = wxPNG_TYPE_PALETTE;
+ }
+ else if ( iColorType==wxPNG_TYPE_COLOUR )
+ {
+ iPngColorType = bUseAlpha ? PNG_COLOR_TYPE_RGB_ALPHA
+ : PNG_COLOR_TYPE_RGB;
+ }
+ else
+ {
+ iPngColorType = bUseAlpha ? PNG_COLOR_TYPE_GRAY_ALPHA
+ : PNG_COLOR_TYPE_GRAY;
+ }
+
+ if (image->HasOption(wxIMAGE_OPTION_PNG_FILTER))
+ png_set_filter( png_ptr, PNG_FILTER_TYPE_BASE, image->GetOptionInt(wxIMAGE_OPTION_PNG_FILTER) );
+
+ if (image->HasOption(wxIMAGE_OPTION_PNG_COMPRESSION_LEVEL))
+ png_set_compression_level( png_ptr, image->GetOptionInt(wxIMAGE_OPTION_PNG_COMPRESSION_LEVEL) );
+
+ if (image->HasOption(wxIMAGE_OPTION_PNG_COMPRESSION_MEM_LEVEL))
+ png_set_compression_mem_level( png_ptr, image->GetOptionInt(wxIMAGE_OPTION_PNG_COMPRESSION_MEM_LEVEL) );
+
+ if (image->HasOption(wxIMAGE_OPTION_PNG_COMPRESSION_STRATEGY))
+ png_set_compression_strategy( png_ptr, image->GetOptionInt(wxIMAGE_OPTION_PNG_COMPRESSION_STRATEGY) );
+
+ if (image->HasOption(wxIMAGE_OPTION_PNG_COMPRESSION_BUFFER_SIZE))
+ png_set_compression_buffer_size( png_ptr, image->GetOptionInt(wxIMAGE_OPTION_PNG_COMPRESSION_BUFFER_SIZE) );
+
+ int iBitDepth = !bUsePalette && image->HasOption(wxIMAGE_OPTION_PNG_BITDEPTH)
+ ? image->GetOptionInt(wxIMAGE_OPTION_PNG_BITDEPTH)
+ : 8;
+
+ png_set_IHDR( png_ptr, info_ptr, image->GetWidth(), image->GetHeight(),
+ iBitDepth, iPngColorType,
+ PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE,
+ PNG_FILTER_TYPE_BASE);
+
+ int iElements;
+ png_color_8 sig_bit;
+
+ if ( iPngColorType & PNG_COLOR_MASK_COLOR )
+ {
+ sig_bit.red =
+ sig_bit.green =
+ sig_bit.blue = (png_byte)iBitDepth;
+ iElements = 3;
+ }
+ else // grey
+ {
+ sig_bit.gray = (png_byte)iBitDepth;
+ iElements = 1;
+ }
+
+ if ( bUseAlpha )
+ {
+ sig_bit.alpha = (png_byte)iBitDepth;
+ iElements++;
+ }
+
+ if ( iBitDepth == 16 )
+ iElements *= 2;
+
+ // save the image resolution if we have it
+ int resX, resY;
+ switch ( GetResolutionFromOptions(*image, &resX, &resY) )
+ {
+ case wxIMAGE_RESOLUTION_INCHES:
+ {
+ const double INCHES_IN_METER = 10000.0 / 254;
+ resX = int(resX * INCHES_IN_METER);
+ resY = int(resY * INCHES_IN_METER);
+ }
+ break;
+
+ case wxIMAGE_RESOLUTION_CM:
+ resX *= 100;
+ resY *= 100;
+ break;
+
+ case wxIMAGE_RESOLUTION_NONE:
+ break;
+
+ default:
+ wxFAIL_MSG( wxT("unsupported image resolution units") );
+ }
+
+ if ( resX && resY )
+ png_set_pHYs( png_ptr, info_ptr, resX, resY, PNG_RESOLUTION_METER );
+
+ png_set_sBIT( png_ptr, info_ptr, &sig_bit );
+ png_write_info( png_ptr, info_ptr );
+ png_set_shift( png_ptr, &sig_bit );
+ png_set_packing( png_ptr );
+
+ unsigned char *
+ data = (unsigned char *)malloc( image->GetWidth() * iElements );
+ if ( !data )
+ {
+ png_destroy_write_struct( &png_ptr, (png_infopp)NULL );
+ return false;
+ }
+
+ const unsigned char *
+ pAlpha = (const unsigned char *)(bHasAlpha ? image->GetAlpha() : NULL);
+
+ const unsigned char *pColors = image->GetData();
+
+ for (int y = 0; y != iHeight; ++y)
+ {
+ unsigned char *pData = data;
+ for (int x = 0; x != iWidth; x++)
+ {
+ png_color_8 clr;
+ clr.red = *pColors++;
+ clr.green = *pColors++;
+ clr.blue = *pColors++;
+ clr.gray = 0;
+ clr.alpha = (bUsePalette && pAlpha) ? *pAlpha++ : 0; // use with wxPNG_TYPE_PALETTE only
+
+ switch ( iColorType )
+ {
+ default:
+ wxFAIL_MSG( wxT("unknown wxPNG_TYPE_XXX") );
+ // fall through
+
+ case wxPNG_TYPE_COLOUR:
+ *pData++ = clr.red;
+ if ( iBitDepth == 16 )
+ *pData++ = 0;
+ *pData++ = clr.green;
+ if ( iBitDepth == 16 )
+ *pData++ = 0;
+ *pData++ = clr.blue;
+ if ( iBitDepth == 16 )
+ *pData++ = 0;
+ break;
+
+ case wxPNG_TYPE_GREY:
+ {
+ // where do these coefficients come from? maybe we
+ // should have image options for them as well?
+ unsigned uiColor =
+ (unsigned) (76.544*(unsigned)clr.red +
+ 150.272*(unsigned)clr.green +
+ 36.864*(unsigned)clr.blue);
+
+ *pData++ = (unsigned char)((uiColor >> 8) & 0xFF);
+ if ( iBitDepth == 16 )
+ *pData++ = (unsigned char)(uiColor & 0xFF);
+ }
+ break;
+
+ case wxPNG_TYPE_GREY_RED:
+ *pData++ = clr.red;
+ if ( iBitDepth == 16 )
+ *pData++ = 0;
+ break;
+
+ case wxPNG_TYPE_PALETTE:
+ *pData++ = (unsigned char) PaletteFind(palette, clr);
+ break;
+ }
+
+ if ( bUseAlpha )
+ {
+ unsigned char uchAlpha = 255;
+ if ( bHasAlpha )
+ uchAlpha = *pAlpha++;
+
+ if ( bHasMask )
+ {
+ if ( (clr.red == mask.red)
+ && (clr.green == mask.green)
+ && (clr.blue == mask.blue) )
+ uchAlpha = 0;
+ }
+
+ *pData++ = uchAlpha;
+ if ( iBitDepth == 16 )
+ *pData++ = 0;
+ }
+ }
+
+ png_bytep row_ptr = data;
+ png_write_rows( png_ptr, &row_ptr, 1 );
+ }
+
+ free(data);
+ png_write_end( png_ptr, info_ptr );
+ png_destroy_write_struct( &png_ptr, (png_infopp)&info_ptr );
+
+ return true;