From: Vadim Zeitlin Date: Mon, 17 Nov 2008 13:54:42 +0000 (+0000) Subject: added the possibility to rescale the image during loading, especially useful with... X-Git-Url: https://git.saurik.com/wxWidgets.git/commitdiff_plain/36abe9d421ebb33873f012a63fbdb210ff771682 added the possibility to rescale the image during loading, especially useful with JPEGs (#8390) (change to the sample accidentally committed as r56820) git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@56821 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775 --- diff --git a/include/wx/image.h b/include/wx/image.h index b09aaf2ed1..ffb1e3777b 100644 --- a/include/wx/image.h +++ b/include/wx/image.h @@ -36,6 +36,9 @@ #define wxIMAGE_OPTION_RESOLUTIONUNIT wxString(_T("ResolutionUnit")) +#define wxIMAGE_OPTION_MAX_WIDTH wxString(_T("MaxWidth")) +#define wxIMAGE_OPTION_MAX_HEIGHT wxString(_T("MaxHeight")) + // constants used with wxIMAGE_OPTION_RESOLUTIONUNIT // // NB: don't change these values, they correspond to libjpeg constants diff --git a/interface/wx/image.h b/interface/wx/image.h index 89a1713893..06bf3ac297 100644 --- a/interface/wx/image.h +++ b/interface/wx/image.h @@ -763,6 +763,20 @@ public: If the given option is not present, the function returns 0. Use HasOption() is 0 is a possibly valid value for the option. + Options common to all formats: + @li wxIMAGE_OPTION_MAX_WIDTH and wxIMAGE_OPTION_MAX_HEIGHT: If either + of these options is specified, the loaded image will be scaled down + (preserving its aspect ratio) so that its width is less than the + max width given if it is not 0 @em and its height is less than the + max height given if it is not 0. This is typically used for loading + thumbnails and the advantage of using these options compared to + calling Rescale() after loading is that some handlers (only JPEG + one right now) support rescaling the image during loading which is + vastly more efficient than loading the entire huge image and + rescaling it later (if these options are not supported by the + handler, this is still what happens however). These options must be + set before calling LoadFile() to have any effect. + Options for wxPNGHandler: @li wxIMAGE_OPTION_PNG_FORMAT: Format for saving a PNG file. @li wxIMAGE_OPTION_PNG_BITDEPTH: Bit depth for every channel (R/G/B/A). diff --git a/src/common/image.cpp b/src/common/image.cpp index 6857a0d3da..07ae7ac0ac 100644 --- a/src/common/image.cpp +++ b/src/common/image.cpp @@ -1978,12 +1978,10 @@ void wxImage::SetPalette(const wxPalette& palette) void wxImage::SetOption(const wxString& name, const wxString& value) { - wxCHECK_RET( Ok(), wxT("invalid image") ); - AllocExclusive(); int idx = M_IMGDATA->m_optionNames.Index(name, false); - if (idx == wxNOT_FOUND) + if ( idx == wxNOT_FOUND ) { M_IMGDATA->m_optionNames.Add(name); M_IMGDATA->m_optionValues.Add(value); @@ -2004,10 +2002,11 @@ void wxImage::SetOption(const wxString& name, int value) wxString wxImage::GetOption(const wxString& name) const { - wxCHECK_MSG( Ok(), wxEmptyString, wxT("invalid image") ); + if ( !M_IMGDATA ) + return wxEmptyString; int idx = M_IMGDATA->m_optionNames.Index(name, false); - if (idx == wxNOT_FOUND) + if ( idx == wxNOT_FOUND ) return wxEmptyString; else return M_IMGDATA->m_optionValues[idx]; @@ -2020,9 +2019,8 @@ int wxImage::GetOptionInt(const wxString& name) const bool wxImage::HasOption(const wxString& name) const { - wxCHECK_MSG( Ok(), false, wxT("invalid image") ); - - return (M_IMGDATA->m_optionNames.Index(name, false) != wxNOT_FOUND); + return M_IMGDATA ? M_IMGDATA->m_optionNames.Index(name, false) != wxNOT_FOUND + : false; } // ---------------------------------------------------------------------------- @@ -2214,18 +2212,42 @@ int wxImage::GetImageCount( wxInputStream &stream, wxBitmapType type ) bool wxImage::DoLoad(wxImageHandler& handler, wxInputStream& stream, int index) { + // save the options values which can be clobbered by the handler (e.g. many + // of them call Destroy() before trying to load the file) + const unsigned maxWidth = GetOptionInt(wxIMAGE_OPTION_MAX_WIDTH), + maxHeight = GetOptionInt(wxIMAGE_OPTION_MAX_HEIGHT); + if ( !handler.LoadFile(this, stream, true/*verbose*/, index) ) return false; M_IMGDATA->m_type = handler.GetType(); + + // rescale the image to the specified size if needed + if ( maxWidth || maxHeight ) + { + const unsigned widthOrig = GetWidth(), + heightOrig = GetHeight(); + + // this uses the same (trivial) algorithm as the JPEG handler + unsigned width = widthOrig, + height = heightOrig; + while ( (maxWidth && width > maxWidth) || + (maxHeight && height > maxHeight) ) + { + width /= 2; + height /= 2; + } + + if ( width != widthOrig || height != heightOrig ) + Rescale(width, height, wxIMAGE_QUALITY_HIGH); + } + return true; } bool wxImage::LoadFile( wxInputStream& stream, wxBitmapType type, int index ) { - UnRef(); - - m_refData = new wxImageRefData; + AllocExclusive(); wxImageHandler *handler; diff --git a/src/common/imagjpeg.cpp b/src/common/imagjpeg.cpp index 8260ad683f..f14027cb78 100644 --- a/src/common/imagjpeg.cpp +++ b/src/common/imagjpeg.cpp @@ -228,11 +228,17 @@ static inline void wx_cmyk_to_rgb(unsigned char* rgb, const unsigned char* cmyk) bool wxJPEGHandler::LoadFile( wxImage *image, wxInputStream& stream, bool verbose, int WXUNUSED(index) ) { + wxCHECK_MSG( image, false, "NULL image pointer" ); + struct jpeg_decompress_struct cinfo; wx_error_mgr jerr; unsigned char *ptr; + // save this before calling Destroy() + const unsigned maxWidth = image->GetOptionInt(wxIMAGE_OPTION_MAX_WIDTH), + maxHeight = image->GetOptionInt(wxIMAGE_OPTION_MAX_HEIGHT); image->Destroy(); + cinfo.err = jpeg_std_error( &jerr ); jerr.error_exit = wx_error_exit; @@ -268,9 +274,20 @@ bool wxJPEGHandler::LoadFile( wxImage *image, wxInputStream& stream, bool verbos bytesPerPixel = 3; } + // scale the picture to fit in the specified max size if necessary + if ( maxWidth > 0 || maxHeight > 0 ) + { + unsigned& scale = cinfo.scale_denom; + while ( (maxWidth && (cinfo.image_width / scale > maxWidth)) || + (maxHeight && (cinfo.image_height / scale > maxHeight)) ) + { + scale *= 2; + } + } + jpeg_start_decompress( &cinfo ); - image->Create( cinfo.image_width, cinfo.image_height ); + image->Create( cinfo.output_width, cinfo.output_height ); if (!image->Ok()) { jpeg_finish_decompress( &cinfo ); jpeg_destroy_decompress( &cinfo );