bool wxBMPHandler::DoLoadDib(wxImage * image, int width, int height, int bpp, int ncolors, int comp, wxFileOffset bmpOffset, wxInputStream& stream, bool verbose, bool IsBmp, bool hasPalette) { wxInt32 aDword, rmask = 0, gmask = 0, bmask = 0, amask = 0; int rshift = 0, gshift = 0, bshift = 0, ashift = 0; int rbits = 0, gbits = 0, bbits = 0; wxInt32 dbuf[4]; wxInt8 bbuf[4]; wxUint8 aByte; wxUint16 aWord; // allocate space for palette if needed: BMPPalette *cmap; if ( bpp < 16 ) { cmap = new BMPPalette[ncolors]; if ( !cmap ) { if (verbose) { wxLogError(_("BMP: Couldn't allocate memory.")); } return false; } } else // no palette { cmap = NULL; } wxON_BLOCK_EXIT1(&BMPPalette::Free, cmap); // destroy existing here instead of: image->Destroy(); image->Create(width, height); unsigned char *ptr = image->GetData(); if ( !ptr ) { if ( verbose ) { wxLogError( _("BMP: Couldn't allocate memory.") ); } return false; } unsigned char *alpha; if ( bpp == 32 ) { // tell the image to allocate an alpha buffer image->SetAlpha(); alpha = image->GetAlpha(); if ( !alpha ) { if ( verbose ) { wxLogError(_("BMP: Couldn't allocate memory.")); } return false; } } else // no alpha { alpha = NULL; } // Reading the palette, if it exists: if ( bpp < 16 && ncolors != 0 ) { unsigned char* r = new unsigned char[ncolors]; unsigned char* g = new unsigned char[ncolors]; unsigned char* b = new unsigned char[ncolors]; for (int j = 0; j < ncolors; j++) { if (hasPalette) { stream.Read(bbuf, 4); cmap[j].b = bbuf[0]; cmap[j].g = bbuf[1]; cmap[j].r = bbuf[2]; r[j] = cmap[j].r; g[j] = cmap[j].g; b[j] = cmap[j].b; } else { //used in reading .ico file mask r[j] = cmap[j].r = g[j] = cmap[j].g = b[j] = cmap[j].b = ( j ? 255 : 0 ); } } #if wxUSE_PALETTE // Set the palette for the wxImage image->SetPalette(wxPalette(ncolors, r, g, b)); #endif // wxUSE_PALETTE delete[] r; delete[] g; delete[] b; } else if ( bpp == 16 || bpp == 32 ) { if ( comp == BI_BITFIELDS ) { int bit; stream.Read(dbuf, 4 * 3); rmask = wxINT32_SWAP_ON_BE(dbuf[0]); gmask = wxINT32_SWAP_ON_BE(dbuf[1]); bmask = wxINT32_SWAP_ON_BE(dbuf[2]); // find shift amount (Least significant bit of mask) for (bit = bpp-1; bit>=0; bit--) { if (bmask & (1 << bit)) bshift = bit; if (gmask & (1 << bit)) gshift = bit; if (rmask & (1 << bit)) rshift = bit; } // Find number of bits in mask (MSB-LSB+1) for (bit = 0; bit < bpp; bit++) { if (bmask & (1 << bit)) bbits = bit-bshift+1; if (gmask & (1 << bit)) gbits = bit-gshift+1; if (rmask & (1 << bit)) rbits = bit-rshift+1; } } else if ( bpp == 16 ) { rmask = 0x7C00; gmask = 0x03E0; bmask = 0x001F; rshift = 10; gshift = 5; bshift = 0; rbits = 5; gbits = 5; bbits = 5; } else if ( bpp == 32 ) { rmask = 0x00FF0000; gmask = 0x0000FF00; bmask = 0x000000FF; amask = 0xFF000000; ashift = 24; rshift = 16; gshift = 8; bshift = 0; rbits = 8; gbits = 8; bbits = 8; } } /* * Reading the image data */ if ( IsBmp ) { // NOTE: seeking a positive amount in wxFromCurrent mode allows us to // load even non-seekable streams (see wxInputStream::SeekI docs)! const wxFileOffset pos = stream.TellI(); if (pos != wxInvalidOffset && bmpOffset > pos) if (stream.SeekI(bmpOffset - pos, wxFromCurrent) == wxInvalidOffset) return false; //else: icon, just carry on } unsigned char *data = ptr; /* set the whole image to the background color */ if ( bpp < 16 && (comp == BI_RLE4 || comp == BI_RLE8) ) { for (int i = 0; i < width * height; i++) { *ptr++ = cmap[0].r; *ptr++ = cmap[0].g; *ptr++ = cmap[0].b; } ptr = data; } int linesize = ((width * bpp + 31) / 32) * 4; /* BMPs are stored upside down */ for ( int line = (height - 1); line >= 0; line-- ) { int linepos = 0; for ( int column = 0; column < width ; ) { if ( bpp < 16 ) { linepos++; aByte = stream.GetC(); if ( bpp == 1 ) { for (int bit = 0; bit < 8 && column < width; bit++) { int index = ((aByte & (0x80 >> bit)) ? 1 : 0); ptr[poffset] = cmap[index].r; ptr[poffset + 1] = cmap[index].g; ptr[poffset + 2] = cmap[index].b; column++; } } else if ( bpp == 4 ) { if ( comp == BI_RLE4 ) { wxUint8 first; first = aByte; aByte = stream.GetC(); if ( first == 0 ) { if ( aByte == 0 ) { if ( column > 0 ) column = width; } else if ( aByte == 1 ) { column = width; line = -1; } else if ( aByte == 2 ) { aByte = stream.GetC(); column += aByte; linepos = column * bpp / 4; aByte = stream.GetC(); line -= aByte; // upside down } else { int absolute = aByte; wxUint8 nibble[2] ; int readBytes = 0 ; for (int k = 0; k < absolute; k++) { if ( !(k % 2 ) ) { ++readBytes ; aByte = stream.GetC(); nibble[0] = (wxUint8)( (aByte & 0xF0) >> 4 ) ; nibble[1] = (wxUint8)( aByte & 0x0F ) ; } ptr[poffset ] = cmap[nibble[k%2]].r; ptr[poffset + 1] = cmap[nibble[k%2]].g; ptr[poffset + 2] = cmap[nibble[k%2]].b; column++; if ( k % 2 ) linepos++; } if ( readBytes & 0x01 ) aByte = stream.GetC(); } } else { wxUint8 nibble[2] ; nibble[0] = (wxUint8)( (aByte & 0xF0) >> 4 ) ; nibble[1] = (wxUint8)( aByte & 0x0F ) ; for ( int l = 0; l < first && column < width; l++ ) { ptr[poffset ] = cmap[nibble[l%2]].r; ptr[poffset + 1] = cmap[nibble[l%2]].g; ptr[poffset + 2] = cmap[nibble[l%2]].b; column++; if ( l % 2 ) linepos++; } } }
bool wxPNGHandler::LoadFile(wxImage *image, wxInputStream& stream, bool verbose, int WXUNUSED(index)) { // VZ: as this function uses setjmp() the only fool-proof error handling // method is to use goto (setjmp is not really C++ dtors friendly...) unsigned char **lines = NULL; png_infop info_ptr = (png_infop) NULL; wxPNGInfoStruct wxinfo; png_uint_32 i, width, height = 0; int bit_depth, color_type, interlace_type; wxinfo.verbose = verbose; wxinfo.stream.in = &stream; image->Destroy(); png_structp png_ptr = png_create_read_struct ( PNG_LIBPNG_VER_STRING, NULL, wx_PNG_error, wx_PNG_warning ); if (!png_ptr) goto error; // NB: please see the comment near wxPNGInfoStruct declaration for // explanation why this line is mandatory png_set_read_fn( png_ptr, &wxinfo, wx_PNG_stream_reader); info_ptr = png_create_info_struct( png_ptr ); if (!info_ptr) goto error; if (setjmp(wxinfo.jmpbuf)) goto error; png_read_info( png_ptr, info_ptr ); png_get_IHDR( png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, &interlace_type, NULL, NULL ); if (color_type == PNG_COLOR_TYPE_PALETTE) png_set_expand( png_ptr ); // Fix for Bug [ 439207 ] Monochrome PNG images come up black if (bit_depth < 8) png_set_expand( png_ptr ); png_set_strip_16( png_ptr ); png_set_packing( png_ptr ); if (png_get_valid( png_ptr, info_ptr, PNG_INFO_tRNS)) png_set_expand( png_ptr ); png_set_filler( png_ptr, 0xff, PNG_FILLER_AFTER ); image->Create((int)width, (int)height, (bool) false /* no need to init pixels */); if (!image->IsOk()) goto error; // initialize all line pointers to NULL to ensure that they can be safely // free()d if an error occurs before all of them could be allocated lines = (unsigned char **)calloc(height, sizeof(unsigned char *)); if ( !lines ) goto error; for (i = 0; i < height; i++) { if ((lines[i] = (unsigned char *)malloc( (size_t)(width * 4))) == NULL) goto error; } png_read_image( png_ptr, lines ); png_read_end( png_ptr, info_ptr ); #if wxUSE_PALETTE if (color_type == PNG_COLOR_TYPE_PALETTE) { png_colorp palette = NULL; int numPalette = 0; (void) png_get_PLTE(png_ptr, info_ptr, &palette, &numPalette); unsigned char* r = new unsigned char[numPalette]; unsigned char* g = new unsigned char[numPalette]; unsigned char* b = new unsigned char[numPalette]; for (int j = 0; j < numPalette; j++) { r[j] = palette[j].red; g[j] = palette[j].green; b[j] = palette[j].blue; } image->SetPalette(wxPalette(numPalette, r, g, b)); delete[] r; delete[] g; delete[] b; } #endif // wxUSE_PALETTE // set the image resolution if it's available png_uint_32 resX, resY; int unitType; if (png_get_pHYs(png_ptr, info_ptr, &resX, &resY, &unitType) == PNG_INFO_pHYs) { wxImageResolution res = wxIMAGE_RESOLUTION_CM; switch (unitType) { default: wxLogWarning(_("Unknown PNG resolution unit %d"), unitType); // fall through case PNG_RESOLUTION_UNKNOWN: image->SetOption(wxIMAGE_OPTION_RESOLUTIONX, resX); image->SetOption(wxIMAGE_OPTION_RESOLUTIONY, resY); res = wxIMAGE_RESOLUTION_NONE; break; case PNG_RESOLUTION_METER: /* Convert meters to centimeters. Use a string to not lose precision (converting to cm and then to inch would result in integer rounding error). If an app wants an int, GetOptionInt will convert and round down for them. */ image->SetOption(wxIMAGE_OPTION_RESOLUTIONX, wxString::FromCDouble((double) resX / 100.0, 2)); image->SetOption(wxIMAGE_OPTION_RESOLUTIONY, wxString::FromCDouble((double) resY / 100.0, 2)); break; } image->SetOption(wxIMAGE_OPTION_RESOLUTIONUNIT, res); } png_destroy_read_struct( &png_ptr, &info_ptr, (png_infopp) NULL ); // loaded successfully, now init wxImage with this data CopyDataFromPNG(image, lines, width, height, color_type); for ( i = 0; i < height; i++ ) free( lines[i] ); free( lines ); return true; error: if (verbose) { wxLogError(_("Couldn't load a PNG image - file is corrupted or not enough memory.")); } if ( image->IsOk() ) { image->Destroy(); } if ( lines ) { for ( unsigned int n = 0; n < height; n++ ) free( lines[n] ); free( lines ); } if ( png_ptr ) { if ( info_ptr ) { png_destroy_read_struct( &png_ptr, &info_ptr, (png_infopp) NULL ); free(info_ptr); } else png_destroy_read_struct( &png_ptr, (png_infopp) NULL, (png_infopp) NULL ); } return false; }
bool wxPNGHandler::LoadFile(wxImage *image, wxInputStream& stream, bool verbose, int WXUNUSED(index)) { // VZ: as this function uses setjmp() the only fool-proof error handling // method is to use goto (setjmp is not really C++ dtors friendly...) unsigned char **lines = NULL; png_infop info_ptr = (png_infop) NULL; wxPNGInfoStruct wxinfo; png_uint_32 i, width, height = 0; int bit_depth, color_type, interlace_type; wxinfo.verbose = verbose; wxinfo.stream.in = &stream; image->Destroy(); png_structp png_ptr = png_create_read_struct ( PNG_LIBPNG_VER_STRING, (voidp) NULL, wx_png_error, wx_png_warning ); if (!png_ptr) goto error; // NB: please see the comment near wxPNGInfoStruct declaration for // explanation why this line is mandatory png_set_read_fn( png_ptr, &wxinfo, wx_PNG_stream_reader); info_ptr = png_create_info_struct( png_ptr ); if (!info_ptr) goto error; if (setjmp(wxinfo.jmpbuf)) goto error; png_read_info( png_ptr, info_ptr ); png_get_IHDR( png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, &interlace_type, NULL, NULL ); if (color_type == PNG_COLOR_TYPE_PALETTE) png_set_expand( png_ptr ); // Fix for Bug [ 439207 ] Monochrome PNG images come up black if (bit_depth < 8) png_set_expand( png_ptr ); png_set_strip_16( png_ptr ); png_set_packing( png_ptr ); if (png_get_valid( png_ptr, info_ptr, PNG_INFO_tRNS)) png_set_expand( png_ptr ); png_set_filler( png_ptr, 0xff, PNG_FILLER_AFTER ); image->Create((int)width, (int)height, (bool) false /* no need to init pixels */); if (!image->Ok()) goto error; // initialize all line pointers to NULL to ensure that they can be safely // free()d if an error occurs before all of them could be allocated lines = (unsigned char **)calloc(height, sizeof(unsigned char *)); if ( !lines ) goto error; for (i = 0; i < height; i++) { if ((lines[i] = (unsigned char *)malloc( (size_t)(width * (sizeof(unsigned char) * 4)))) == NULL) goto error; } png_read_image( png_ptr, lines ); png_read_end( png_ptr, info_ptr ); #if wxUSE_PALETTE if (color_type == PNG_COLOR_TYPE_PALETTE) { const size_t ncolors = info_ptr->num_palette; unsigned char* r = new unsigned char[ncolors]; unsigned char* g = new unsigned char[ncolors]; unsigned char* b = new unsigned char[ncolors]; for (size_t j = 0; j < ncolors; j++) { r[j] = info_ptr->palette[j].red; g[j] = info_ptr->palette[j].green; b[j] = info_ptr->palette[j].blue; } image->SetPalette(wxPalette(ncolors, r, g, b)); delete[] r; delete[] g; delete[] b; } #endif // wxUSE_PALETTE png_destroy_read_struct( &png_ptr, &info_ptr, (png_infopp) NULL ); // loaded successfully, now init wxImage with this data CopyDataFromPNG(image, lines, width, height, color_type); for ( i = 0; i < height; i++ ) free( lines[i] ); free( lines ); return true; error: if (verbose) { wxLogError(_("Couldn't load a PNG image - file is corrupted or not enough memory.")); } if ( image->Ok() ) { image->Destroy(); } if ( lines ) { for ( unsigned int n = 0; n < height; n++ ) free( lines[n] ); free( lines ); } if ( png_ptr ) { if ( info_ptr ) { png_destroy_read_struct( &png_ptr, &info_ptr, (png_infopp) NULL ); free(info_ptr); } else png_destroy_read_struct( &png_ptr, (png_infopp) NULL, (png_infopp) NULL ); } return false; }
MaxPalette::MaxPalette(const wxPalette& p) { palette = wxPalette(p); }
void OnSave(wxCommandEvent& WXUNUSED(event)) { #if wxUSE_FILEDLG wxImage image = m_bitmap.ConvertToImage(); wxString savefilename = wxFileSelector( wxT("Save Image"), wxEmptyString, wxEmptyString, wxEmptyString, wxT("BMP files (*.bmp)|*.bmp|") #if wxUSE_LIBPNG wxT("PNG files (*.png)|*.png|") #endif #if wxUSE_LIBJPEG wxT("JPEG files (*.jpg)|*.jpg|") #endif #if wxUSE_GIF wxT("GIF files (*.gif)|*.gif|") #endif #if wxUSE_LIBTIFF wxT("TIFF files (*.tif)|*.tif|") #endif #if wxUSE_PCX wxT("PCX files (*.pcx)|*.pcx|") #endif #if wxUSE_XPM wxT("X PixMap files (*.xpm)|*.xpm|") #endif wxT("ICO files (*.ico)|*.ico|") wxT("CUR files (*.cur)|*.cur"), wxFD_SAVE | wxFD_OVERWRITE_PROMPT, this); if ( savefilename.empty() ) return; wxString extension; wxFileName::SplitPath(savefilename, NULL, NULL, &extension); bool saved = false; if ( extension == wxT("bmp") ) { static const int bppvalues[] = { wxBMP_1BPP, wxBMP_1BPP_BW, wxBMP_4BPP, wxBMP_8BPP, wxBMP_8BPP_GREY, wxBMP_8BPP_RED, wxBMP_8BPP_PALETTE, wxBMP_24BPP }; const wxString bppchoices[] = { wxT("1 bpp color"), wxT("1 bpp B&W"), wxT("4 bpp color"), wxT("8 bpp color"), wxT("8 bpp greyscale"), wxT("8 bpp red"), wxT("8 bpp own palette"), wxT("24 bpp") }; int bppselection = wxGetSingleChoiceIndex(wxT("Set BMP BPP"), wxT("Image sample: save file"), WXSIZEOF(bppchoices), bppchoices, this); if ( bppselection != -1 ) { int format = bppvalues[bppselection]; image.SetOption(wxIMAGE_OPTION_BMP_FORMAT, format); if ( format == wxBMP_8BPP_PALETTE ) { unsigned char *cmap = new unsigned char [256]; for ( int i = 0; i < 256; i++ ) cmap[i] = (unsigned char)i; image.SetPalette(wxPalette(256, cmap, cmap, cmap)); delete[] cmap; } } } #if wxUSE_LIBPNG else if ( extension == wxT("png") ) { static const int pngvalues[] = { wxPNG_TYPE_COLOUR, wxPNG_TYPE_COLOUR, wxPNG_TYPE_GREY, wxPNG_TYPE_GREY, wxPNG_TYPE_GREY_RED, wxPNG_TYPE_GREY_RED, }; const wxString pngchoices[] = { wxT("Colour 8bpp"), wxT("Colour 16bpp"), wxT("Grey 8bpp"), wxT("Grey 16bpp"), wxT("Grey red 8bpp"), wxT("Grey red 16bpp"), }; int sel = wxGetSingleChoiceIndex(wxT("Set PNG format"), wxT("Image sample: save file"), WXSIZEOF(pngchoices), pngchoices, this); if ( sel != -1 ) { image.SetOption(wxIMAGE_OPTION_PNG_FORMAT, pngvalues[sel]); image.SetOption(wxIMAGE_OPTION_PNG_BITDEPTH, sel % 2 ? 16 : 8); // these values are taken from OptiPNG with -o3 switch const wxString compressionChoices[] = { wxT("compression = 9, memory = 8, strategy = 0, filter = 0"), wxT("compression = 9, memory = 9, strategy = 0, filter = 0"), wxT("compression = 9, memory = 8, strategy = 1, filter = 0"), wxT("compression = 9, memory = 9, strategy = 1, filter = 0"), wxT("compression = 1, memory = 8, strategy = 2, filter = 0"), wxT("compression = 1, memory = 9, strategy = 2, filter = 0"), wxT("compression = 9, memory = 8, strategy = 0, filter = 5"), wxT("compression = 9, memory = 9, strategy = 0, filter = 5"), wxT("compression = 9, memory = 8, strategy = 1, filter = 5"), wxT("compression = 9, memory = 9, strategy = 1, filter = 5"), wxT("compression = 1, memory = 8, strategy = 2, filter = 5"), wxT("compression = 1, memory = 9, strategy = 2, filter = 5"), }; int sel = wxGetSingleChoiceIndex(wxT("Select compression option (Cancel to use default)\n"), wxT("PNG Compression Options"), WXSIZEOF(compressionChoices), compressionChoices, this); if (sel != -1) { const int zc[] = {9, 9, 9, 9, 1, 1, 9, 9, 9, 9, 1, 1}; const int zm[] = {8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9}; const int zs[] = {0, 0, 1, 1, 2, 2, 0, 0, 1, 1, 2, 2}; const int f[] = {0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8}; image.SetOption(wxIMAGE_OPTION_PNG_COMPRESSION_LEVEL , zc[sel]); image.SetOption(wxIMAGE_OPTION_PNG_COMPRESSION_MEM_LEVEL , zm[sel]); image.SetOption(wxIMAGE_OPTION_PNG_COMPRESSION_STRATEGY , zs[sel]); image.SetOption(wxIMAGE_OPTION_PNG_FILTER , f[sel]); image.SetOption(wxIMAGE_OPTION_PNG_COMPRESSION_BUFFER_SIZE, 1048576); // 1 MB } } } #endif // wxUSE_LIBPNG else if ( extension == wxT("cur") ) { image.Rescale(32,32); image.SetOption(wxIMAGE_OPTION_CUR_HOTSPOT_X, 0); image.SetOption(wxIMAGE_OPTION_CUR_HOTSPOT_Y, 0); // This shows how you can save an image with explicitly // specified image format: saved = image.SaveFile(savefilename, wxBITMAP_TYPE_CUR); } if ( !saved ) { // This one guesses image format from filename extension // (it may fail if the extension is not recognized): image.SaveFile(savefilename); } #endif // wxUSE_FILEDLG }
// This function uses wxPNGImageData to store some of its "local" variables in // order to avoid clobbering these variables by longjmp(): having them inside // the stack frame of the caller prevents this from happening. It also // "returns" its result via wxPNGImageData: use its "ok" field to check // whether loading succeeded or failed. void wxPNGImageData::DoLoadPNGFile(wxImage* image, wxPNGInfoStruct& wxinfo) { png_uint_32 width, height = 0; int bit_depth, color_type, interlace_type; image->Destroy(); png_ptr = png_create_read_struct ( PNG_LIBPNG_VER_STRING, NULL, wx_PNG_error, wx_PNG_warning ); if (!png_ptr) return; // NB: please see the comment near wxPNGInfoStruct declaration for // explanation why this line is mandatory png_set_read_fn( png_ptr, &wxinfo, wx_PNG_stream_reader); info_ptr = png_create_info_struct( png_ptr ); if (!info_ptr) return; if (setjmp(wxinfo.jmpbuf)) return; png_read_info( png_ptr, info_ptr ); png_get_IHDR( png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, &interlace_type, NULL, NULL ); if (color_type == PNG_COLOR_TYPE_PALETTE) png_set_expand( png_ptr ); // Fix for Bug [ 439207 ] Monochrome PNG images come up black if (bit_depth < 8) png_set_expand( png_ptr ); png_set_strip_16( png_ptr ); png_set_packing( png_ptr ); if (png_get_valid( png_ptr, info_ptr, PNG_INFO_tRNS)) png_set_expand( png_ptr ); png_set_filler( png_ptr, 0xff, PNG_FILLER_AFTER ); image->Create((int)width, (int)height, (bool) false /* no need to init pixels */); if (!image->IsOk()) return; if ( !Alloc(width, height) ) return; png_read_image( png_ptr, lines ); png_read_end( png_ptr, info_ptr ); #if wxUSE_PALETTE if (color_type == PNG_COLOR_TYPE_PALETTE) { png_colorp palette = NULL; int numPalette = 0; (void) png_get_PLTE(png_ptr, info_ptr, &palette, &numPalette); unsigned char* r = new unsigned char[numPalette]; unsigned char* g = new unsigned char[numPalette]; unsigned char* b = new unsigned char[numPalette]; for (int j = 0; j < numPalette; j++) { r[j] = palette[j].red; g[j] = palette[j].green; b[j] = palette[j].blue; } image->SetPalette(wxPalette(numPalette, r, g, b)); delete[] r; delete[] g; delete[] b; } #endif // wxUSE_PALETTE // set the image resolution if it's available png_uint_32 resX, resY; int unitType; if (png_get_pHYs(png_ptr, info_ptr, &resX, &resY, &unitType) == PNG_INFO_pHYs) { wxImageResolution res = wxIMAGE_RESOLUTION_CM; switch (unitType) { default: wxLogWarning(_("Unknown PNG resolution unit %d"), unitType); wxFALLTHROUGH; case PNG_RESOLUTION_UNKNOWN: image->SetOption(wxIMAGE_OPTION_RESOLUTIONX, resX); image->SetOption(wxIMAGE_OPTION_RESOLUTIONY, resY); res = wxIMAGE_RESOLUTION_NONE; break; case PNG_RESOLUTION_METER: /* Convert meters to centimeters. Use a string to not lose precision (converting to cm and then to inch would result in integer rounding error). If an app wants an int, GetOptionInt will convert and round down for them. */ image->SetOption(wxIMAGE_OPTION_RESOLUTIONX, wxString::FromCDouble((double) resX / 100.0, 2)); image->SetOption(wxIMAGE_OPTION_RESOLUTIONY, wxString::FromCDouble((double) resY / 100.0, 2)); break; } image->SetOption(wxIMAGE_OPTION_RESOLUTIONUNIT, res); } // loaded successfully, now init wxImage with this data CopyDataFromPNG(image, lines, width, height, color_type); // This will indicate to the caller that loading succeeded. ok = true; }