X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/8ee313d2bec36438f9f93d90e68e7e4253535b47..7198c3368055d88249a338eb33b21f051f674806:/src/common/imagpng.cpp diff --git a/src/common/imagpng.cpp b/src/common/imagpng.cpp index d720b27df6..284f5d7171 100644 --- a/src/common/imagpng.cpp +++ b/src/common/imagpng.cpp @@ -43,7 +43,7 @@ // constants // ---------------------------------------------------------------------------- -// image can not have any transparent pixels at all, have only 100% opaque +// image cannot have any transparent pixels at all, have only 100% opaque // and/or 100% transparent pixels in which case a simple mask is enough to // store this information in wxImage or have a real alpha channel in which case // we need to have it in wxImage as well @@ -522,7 +522,7 @@ wxPNGHandler::LoadFile(wxImage *image, png_structp png_ptr = png_create_read_struct ( PNG_LIBPNG_VER_STRING, - (voidp) NULL, + NULL, wx_png_error, wx_png_warning ); @@ -558,7 +558,7 @@ wxPNGHandler::LoadFile(wxImage *image, image->Create((int)width, (int)height, (bool) false /* no need to init pixels */); - if (!image->Ok()) + if (!image->IsOk()) goto error; // initialize all line pointers to NULL to ensure that they can be safely @@ -579,25 +579,70 @@ wxPNGHandler::LoadFile(wxImage *image, #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]; + png_colorp palette = NULL; + int numPalette = 0; + + (void) png_get_PLTE(png_ptr, info_ptr, &palette, &numPalette); - for (size_t j = 0; j < ncolors; j++) + 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] = info_ptr->palette[j].red; - g[j] = info_ptr->palette[j].green; - b[j] = info_ptr->palette[j].blue; + r[j] = palette[j].red; + g[j] = palette[j].green; + b[j] = palette[j].blue; } - image->SetPalette(wxPalette(ncolors, r, g, b)); + 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 @@ -615,7 +660,7 @@ error: wxLogError(_("Couldn't load a PNG image - file is corrupted or not enough memory.")); } - if ( image->Ok() ) + if ( image->IsOk() ) { image->Destroy(); } @@ -642,23 +687,41 @@ error: } // ---------------------------------------------------------------------------- -// SaveFile() helpers +// SaveFile() palette helpers // ---------------------------------------------------------------------------- -static int PaletteFind(const png_color& clr, - const png_color *pal, png_uint_16 palCount) +typedef wxLongToLongHashMap PaletteMap; + +static unsigned long PaletteMakeKey(const png_color_8& clr) { - for (png_uint_16 i = 0; i < palCount; i++) - { - if ( (clr.red == pal[i].red) - && (clr.green == pal[i].green) - && (clr.blue == pal[i].blue)) - { - return i; - } - } - - return wxNOT_FOUND; + return (wxImageHistogram::MakeKey(clr.red, clr.green, clr.blue) << 8) | clr.alpha; +} + +static long PaletteFind(const PaletteMap& palette, const png_color_8& clr) +{ + unsigned long value = PaletteMakeKey(clr); + PaletteMap::const_iterator it = palette.find(value); + + return (it != palette.end()) ? it->second : wxNOT_FOUND; +} + +static long PaletteAdd(PaletteMap *palette, const png_color_8& clr) +{ + unsigned long value = PaletteMakeKey(clr); + PaletteMap::const_iterator it = palette->find(value); + size_t index; + + if (it == palette->end()) + { + index = palette->size(); + (*palette)[value] = index; + } + else + { + index = it->second; + } + + return index; } // ---------------------------------------------------------------------------- @@ -713,6 +776,9 @@ bool wxPNGHandler::SaveFile( wxImage *image, wxOutputStream& stream, bool verbos // explanation why this line is mandatory png_set_write_fn( png_ptr, &wxinfo, wx_PNG_stream_writer, NULL); + const int iHeight = image->GetHeight(); + const int iWidth = image->GetWidth(); + const bool bHasPngFormatOption = image->HasOption(wxIMAGE_OPTION_PNG_FORMAT); @@ -723,72 +789,104 @@ bool wxPNGHandler::SaveFile( wxImage *image, wxOutputStream& stream, bool verbos bool bHasAlpha = image->HasAlpha(); bool bHasMask = image->HasMask(); + bool bUsePalette = iColorType == wxPNG_TYPE_PALETTE #if wxUSE_PALETTE - /* - Only save as an indexed image if the number of palette entries does not - exceed libpng's limit (256). - We assume here that we will need an extra palette entry if there's an - alpha or mask, regardless of whether a possibly needed conversion from - alpha to a mask fails (unlikely), or whether the mask colour already - can be found in the palette (more likely). In the latter case an extra - palette entry would not be required later on and the image could actually - be saved as a palettised PNG (instead now it will be saved as true colour). - A little bit of precision is lost, but at the benefit of a lot more - simplified code. - */ - bool bUsePalette = - (!bHasPngFormatOption || iColorType == wxPNG_TYPE_PALETTE) - && image->HasPalette() - && image->GetPalette().GetColoursCount() - + ((bHasAlpha || bHasMask) ? 1 : 0) <= PNG_MAX_PALETTE_LENGTH; - - wxImage temp_image(*image); - if (bUsePalette && image->HasAlpha() && !bHasMask) + || (!bHasPngFormatOption && image->HasPalette() ) +#endif + ; + + png_color_8 mask = { 0, 0, 0, 0, 0 }; + + if (bHasMask) + { + mask.red = image->GetMaskRed(); + mask.green = image->GetMaskGreen(); + mask.blue = image->GetMaskBlue(); + } + + PaletteMap palette; + + if (bUsePalette) { - /* - Only convert alpha to mask if saving as a palettised image was - explicitly requested. We don't want to lose alpha's precision - by converting to a mask just to be able to save palettised. - */ - if (iColorType == wxPNG_TYPE_PALETTE - && temp_image.ConvertAlphaToMask()) + png_color png_rgb [PNG_MAX_PALETTE_LENGTH]; + png_byte png_trans[PNG_MAX_PALETTE_LENGTH]; + + const unsigned char *pColors = image->GetData(); + const unsigned char* pAlpha = image->GetAlpha(); + + if (bHasMask && !pAlpha) { - image = &temp_image; - bHasMask = true; - bHasAlpha = image->HasAlpha(); + // Mask must be first + PaletteAdd(&palette, mask); } - else + + for (int y = 0; y < iHeight; y++) { - bUsePalette = false; - iColorType = wxPNG_TYPE_COLOUR; + for (int x = 0; x < iWidth; x++) + { + png_color_8 rgba; + + rgba.red = *pColors++; + rgba.green = *pColors++; + rgba.blue = *pColors++; + rgba.gray = 0; + rgba.alpha = (pAlpha && !bHasMask) ? *pAlpha++ : 0; + + // save in our palette + long index = PaletteAdd(&palette, rgba); + + if (index < PNG_MAX_PALETTE_LENGTH) + { + // save in libpng's palette + png_rgb[index].red = rgba.red; + png_rgb[index].green = rgba.green; + png_rgb[index].blue = rgba.blue; + png_trans[index] = rgba.alpha; + } + else + { + bUsePalette = false; + break; + } + } } - } -#else - bool bUsePalette = false; -#endif // wxUSE_PALETTE - bool bUseAlpha = !bUsePalette && (bHasAlpha || bHasMask); + if (bUsePalette) + { + png_set_PLTE(png_ptr, info_ptr, png_rgb, palette.size()); - png_color mask; - if (bHasMask) + if (bHasMask && !pAlpha) + { + wxASSERT(PaletteFind(palette, mask) == 0); + png_trans[0] = 0; + png_set_tRNS(png_ptr, info_ptr, png_trans, 1, NULL); + } + else if (pAlpha && !bHasMask) + { + png_set_tRNS(png_ptr, info_ptr, png_trans, palette.size(), NULL); + } + } + } + + /* + 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) { - mask.red = image->GetMaskRed(); - mask.green = image->GetMaskGreen(); - mask.blue = image->GetMaskBlue(); + iColorType = wxPNG_TYPE_COLOUR; } + bool bUseAlpha = !bUsePalette && (bHasAlpha || bHasMask); int iPngColorType; -#if wxUSE_PALETTE if (bUsePalette) { iPngColorType = PNG_COLOR_TYPE_PALETTE; iColorType = wxPNG_TYPE_PALETTE; } - else -#endif // wxUSE_PALETTE - if ( iColorType==wxPNG_TYPE_COLOUR ) + else if ( iColorType==wxPNG_TYPE_COLOUR ) { iPngColorType = bUseAlpha ? PNG_COLOR_TYPE_RGB_ALPHA : PNG_COLOR_TYPE_RGB; @@ -823,56 +921,6 @@ bool wxPNGHandler::SaveFile( wxImage *image, wxOutputStream& stream, bool verbos PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); -#if wxUSE_PALETTE - if (bUsePalette) - { - const wxPalette& pal = image->GetPalette(); - const int palCount = pal.GetColoursCount(); - png_colorp palette = (png_colorp) malloc( - (palCount + 1 /*headroom for trans */) * sizeof(png_color)); - - if (!palette) - { - png_destroy_write_struct( &png_ptr, (png_infopp)NULL ); - if (verbose) - { - wxLogError(_("Couldn't save PNG image.")); - } - return false; - } - - png_uint_16 i; - for (i = 0; i < palCount; ++i) - { - pal.GetRGB(i, &palette[i].red, &palette[i].green, &palette[i].blue); - } - - png_uint_16 numPalette = palCount; - if (bHasMask) - { - int index = PaletteFind(mask, palette, numPalette); - - if (index) - { - if (index == wxNOT_FOUND) - { - numPalette++; - index = palCount; - palette[index] = mask; - } - - wxSwap(palette[0], palette[index]); - } - - png_byte trans = 0; - png_set_tRNS(png_ptr, info_ptr, &trans, 1, NULL); - } - - png_set_PLTE(png_ptr, info_ptr, palette, numPalette); - free (palette); - } -#endif // wxUSE_PALETTE - int iElements; png_color_8 sig_bit; @@ -889,7 +937,7 @@ bool wxPNGHandler::SaveFile( wxImage *image, wxOutputStream& stream, bool verbos iElements = 1; } - if ( iPngColorType & PNG_COLOR_MASK_ALPHA ) + if ( bUseAlpha ) { sig_bit.alpha = (png_byte)iBitDepth; iElements++; @@ -938,22 +986,22 @@ bool wxPNGHandler::SaveFile( wxImage *image, wxOutputStream& stream, bool verbos return false; } - unsigned char * - pAlpha = (unsigned char *)(bHasAlpha ? image->GetAlpha() : NULL); - int iHeight = image->GetHeight(); - int iWidth = image->GetWidth(); + const unsigned char * + pAlpha = (const unsigned char *)(bHasAlpha ? image->GetAlpha() : NULL); - unsigned char *pColors = image->GetData(); + 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 clr; + 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 ) { @@ -995,8 +1043,7 @@ bool wxPNGHandler::SaveFile( wxImage *image, wxOutputStream& stream, bool verbos break; case wxPNG_TYPE_PALETTE: - *pData++ = (unsigned char) PaletteFind(clr, - info_ptr->palette, info_ptr->num_palette); + *pData++ = (unsigned char) PaletteFind(palette, clr); break; }