Add support for bilinear resize algorithm to wxImage.

Add wxIMAGE_QUALITY_BILINEAR in addition to the existing wxIMAGE_QUALITY_BICUBIC,
it is supposed to be much faster yet yield almost the same results.

Closes #11034.

git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@61791 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
This commit is contained in:
Vadim Zeitlin
2009-08-30 21:11:37 +00:00
parent 16b0c55398
commit 180f3c7461
4 changed files with 209 additions and 81 deletions

View File

@@ -372,6 +372,7 @@ All:
(file, line and function name) and id of the thread which generated it. (file, line and function name) and id of the thread which generated it.
* SetThreadActiveTarget() allows to set up thread-specific log targets. * SetThreadActiveTarget() allows to set up thread-specific log targets.
- Fix output buffer overflow in wxBase64Decode() (Eric W. Savage). - Fix output buffer overflow in wxBase64Decode() (Eric W. Savage).
- Added bilinear image resizing algorithm to wxImage (bishop).
All (GUI): All (GUI):

View File

@@ -56,10 +56,18 @@ enum wxImageResolution
}; };
// Constants for wxImage::Scale() for determining the level of quality // Constants for wxImage::Scale() for determining the level of quality
enum enum wxImageResizeQuality
{ {
wxIMAGE_QUALITY_NORMAL = 0, // different image resizing algorithms used by Scale() and Rescale()
wxIMAGE_QUALITY_HIGH = 1 wxIMAGE_QUALITY_NEAREST = 0,
wxIMAGE_QUALITY_BILINEAR = 1,
wxIMAGE_QUALITY_BICUBIC = 2,
// default quality is low (but fast)
wxIMAGE_QUALITY_NORMAL = wxIMAGE_QUALITY_NEAREST,
// highest (but best) quality
wxIMAGE_QUALITY_HIGH = wxIMAGE_QUALITY_BICUBIC
}; };
// alpha channel values: fully transparent, default threshold separating // alpha channel values: fully transparent, default threshold separating
@@ -308,10 +316,13 @@ public:
void Paste( const wxImage &image, int x, int y ); void Paste( const wxImage &image, int x, int y );
// return the new image with size width*height // return the new image with size width*height
wxImage Scale( int width, int height, int quality = wxIMAGE_QUALITY_NORMAL ) const; wxImage Scale( int width, int height,
wxImageResizeQuality quality = wxIMAGE_QUALITY_NORMAL ) const;
// box averager and bicubic filters for up/down sampling // box averager and bicubic filters for up/down sampling
wxImage ResampleNearest(int width, int height) const;
wxImage ResampleBox(int width, int height) const; wxImage ResampleBox(int width, int height) const;
wxImage ResampleBilinear(int width, int height) const;
wxImage ResampleBicubic(int width, int height) const; wxImage ResampleBicubic(int width, int height) const;
// blur the image according to the specified pixel radius // blur the image according to the specified pixel radius
@@ -322,7 +333,9 @@ public:
wxImage ShrinkBy( int xFactor , int yFactor ) const ; wxImage ShrinkBy( int xFactor , int yFactor ) const ;
// rescales the image in place // rescales the image in place
wxImage& Rescale( int width, int height, int quality = wxIMAGE_QUALITY_NORMAL ) { return *this = Scale(width, height, quality); } wxImage& Rescale( int width, int height,
wxImageResizeQuality quality = wxIMAGE_QUALITY_NORMAL )
{ return *this = Scale(width, height, quality); }
// resizes the image in place // resizes the image in place
wxImage& Resize( const wxSize& size, const wxPoint& pos, wxImage& Resize( const wxSize& size, const wxPoint& pos,

View File

@@ -23,6 +23,29 @@ enum wxImageResolution
wxIMAGE_RESOLUTION_CM = 2 wxIMAGE_RESOLUTION_CM = 2
}; };
/**
Image resize algorithm.
This is used with wxImage::Scale() and wxImage::Rescale().
*/
enum wxImageResizeQuality
{
/// Simplest and fastest algorithm.
wxIMAGE_QUALITY_NEAREST,
/// Compromise between wxIMAGE_QUALITY_NEAREST and wxIMAGE_QUALITY_BICUBIC.
wxIMAGE_QUALITY_BILINEAR,
/// Highest quality but slowest execution time.
wxIMAGE_QUALITY_BICUBIC,
/// Default image resizing algorithm used by wxImage::Scale().
wxIMAGE_QUALITY_NORMAL,
/// Best image resizing algorithm, currently same as wxIMAGE_QUALITY_BICUBIC.
wxIMAGE_QUALITY_HIGH
};
/** /**
Possible values for PNG image type option. Possible values for PNG image type option.
@@ -661,7 +684,7 @@ public:
@see Scale() @see Scale()
*/ */
wxImage& Rescale(int width, int height, wxImage& Rescale(int width, int height,
int quality = wxIMAGE_QUALITY_NORMAL); wxImageResizeQuality quality = wxIMAGE_QUALITY_NORMAL);
/** /**
Changes the size of the image in-place without scaling it by adding either a Changes the size of the image in-place without scaling it by adding either a
@@ -714,12 +737,8 @@ public:
This is also useful for scaling bitmaps in general as the only other way This is also useful for scaling bitmaps in general as the only other way
to scale bitmaps is to blit a wxMemoryDC into another wxMemoryDC. to scale bitmaps is to blit a wxMemoryDC into another wxMemoryDC.
The parameter @a quality determines what method to use for resampling the image. The parameter @a quality determines what method to use for resampling
Can be one of the following: the image, see wxImageResizeQuality documentation.
- wxIMAGE_QUALITY_NORMAL: Uses the normal default scaling method of pixel
replication
- wxIMAGE_QUALITY_HIGH: Uses bicubic and box averaging resampling methods
for upsampling and downsampling respectively
It should be noted that although using @c wxIMAGE_QUALITY_HIGH produces much nicer It should be noted that although using @c wxIMAGE_QUALITY_HIGH produces much nicer
looking results it is a slower method. Downsampling will use the box averaging looking results it is a slower method. Downsampling will use the box averaging
@@ -753,7 +772,7 @@ public:
@see Rescale() @see Rescale()
*/ */
wxImage Scale(int width, int height, wxImage Scale(int width, int height,
int quality = wxIMAGE_QUALITY_NORMAL) const; wxImageResizeQuality quality = wxIMAGE_QUALITY_NORMAL) const;
/** /**
Returns a resized version of this image without scaling it by adding either a Returns a resized version of this image without scaling it by adding either a

View File

@@ -384,7 +384,8 @@ wxImage wxImage::ShrinkBy( int xFactor , int yFactor ) const
return image; return image;
} }
wxImage wxImage::Scale( int width, int height, int quality ) const wxImage
wxImage::Scale( int width, int height, wxImageResizeQuality quality ) const
{ {
wxImage image; wxImage image;
@@ -404,30 +405,56 @@ wxImage wxImage::Scale( int width, int height, int quality ) const
if ( old_width == width && old_height == height ) if ( old_width == width && old_height == height )
return *this; return *this;
// Scale the image (...or more appropriately, resample the image) using // resample the image using either the nearest neighbourhood, bilinear or
// either the high-quality or normal method as specified // bicubic method as specified
if ( quality == wxIMAGE_QUALITY_HIGH ) switch ( quality )
{ {
// We need to check whether we are downsampling or upsampling the image case wxIMAGE_QUALITY_BICUBIC:
case wxIMAGE_QUALITY_BILINEAR:
// both of these algorithms should be used for up-sampling the
// image only, when down-sampling always use box averaging for best
// results
if ( width < old_width && height < old_height ) if ( width < old_width && height < old_height )
{
// Downsample the image using the box averaging method for best results
image = ResampleBox(width, height); image = ResampleBox(width, height);
} else if ( quality == wxIMAGE_QUALITY_BILINEAR )
else image = ResampleBilinear(width, height);
{ else if ( quality == wxIMAGE_QUALITY_BICUBIC )
// For upsampling or other random/wierd image dimensions we'll use
// a bicubic b-spline scaling method
image = ResampleBicubic(width, height); image = ResampleBicubic(width, height);
} break;
}
else // Default scaling method == simple pixel replication case wxIMAGE_QUALITY_NEAREST:
{
if ( old_width % width == 0 && old_width >= width && if ( old_width % width == 0 && old_width >= width &&
old_height % height == 0 && old_height >= height ) old_height % height == 0 && old_height >= height )
{ {
return ShrinkBy( old_width / width , old_height / height ); return ShrinkBy( old_width / width , old_height / height );
} }
image = ResampleNearest(width, height);
break;
}
// If the original image has a mask, apply the mask to the new image
if (M_IMGDATA->m_hasMask)
{
image.SetMaskColour( M_IMGDATA->m_maskRed,
M_IMGDATA->m_maskGreen,
M_IMGDATA->m_maskBlue );
}
// In case this is a cursor, make sure the hotspot is scaled accordingly:
if ( HasOption(wxIMAGE_OPTION_CUR_HOTSPOT_X) )
image.SetOption(wxIMAGE_OPTION_CUR_HOTSPOT_X,
(GetOptionInt(wxIMAGE_OPTION_CUR_HOTSPOT_X)*width)/old_width);
if ( HasOption(wxIMAGE_OPTION_CUR_HOTSPOT_Y) )
image.SetOption(wxIMAGE_OPTION_CUR_HOTSPOT_Y,
(GetOptionInt(wxIMAGE_OPTION_CUR_HOTSPOT_Y)*height)/old_height);
return image;
}
wxImage wxImage::ResampleNearest(int width, int height) const
{
wxImage image;
image.Create( width, height, false ); image.Create( width, height, false );
unsigned char *data = image.GetData(); unsigned char *data = image.GetData();
@@ -449,6 +476,8 @@ wxImage wxImage::Scale( int width, int height, int quality ) const
} }
} }
long old_height = M_IMGDATA->m_height,
old_width = M_IMGDATA->m_width;
long x_delta = (old_width<<16) / width; long x_delta = (old_width<<16) / width;
long y_delta = (old_height<<16) / height; long y_delta = (old_height<<16) / height;
@@ -476,23 +505,6 @@ wxImage wxImage::Scale( int width, int height, int quality ) const
y += y_delta; y += y_delta;
} }
}
// If the original image has a mask, apply the mask to the new image
if (M_IMGDATA->m_hasMask)
{
image.SetMaskColour( M_IMGDATA->m_maskRed,
M_IMGDATA->m_maskGreen,
M_IMGDATA->m_maskBlue );
}
// In case this is a cursor, make sure the hotspot is scaled accordingly:
if ( HasOption(wxIMAGE_OPTION_CUR_HOTSPOT_X) )
image.SetOption(wxIMAGE_OPTION_CUR_HOTSPOT_X,
(GetOptionInt(wxIMAGE_OPTION_CUR_HOTSPOT_X)*width)/old_width);
if ( HasOption(wxIMAGE_OPTION_CUR_HOTSPOT_Y) )
image.SetOption(wxIMAGE_OPTION_CUR_HOTSPOT_Y,
(GetOptionInt(wxIMAGE_OPTION_CUR_HOTSPOT_Y)*height)/old_height);
return image; return image;
} }
@@ -582,6 +594,89 @@ wxImage wxImage::ResampleBox(int width, int height) const
return ret_image; return ret_image;
} }
wxImage wxImage::ResampleBilinear(int width, int height) const
{
// This function implements a Bilinear algorithm for resampling.
wxImage ret_image(width, height, false);
unsigned char* src_data = M_IMGDATA->m_data;
unsigned char* src_alpha = M_IMGDATA->m_alpha;
unsigned char* dst_data = ret_image.GetData();
unsigned char* dst_alpha = NULL;
if ( src_alpha )
{
ret_image.SetAlpha();
dst_alpha = ret_image.GetAlpha();
}
double HFactor = double(M_IMGDATA->m_height) / height;
double WFactor = double(M_IMGDATA->m_width) / width;
int srcpixymax = M_IMGDATA->m_height - 1;
int srcpixxmax = M_IMGDATA->m_width - 1;
double srcpixy, srcpixy1, srcpixy2, dy, dy1;
double srcpixx, srcpixx1, srcpixx2, dx, dx1;
double r1, g1, b1, a1;
double r2, g2, b2, a2;
for ( int dsty = 0; dsty < height; dsty++ )
{
// We need to calculate the source pixel to interpolate from - Y-axis
srcpixy = double(dsty) * HFactor;
srcpixy1 = int(srcpixy);
srcpixy2 = ( srcpixy1 == srcpixymax ) ? srcpixy1 : srcpixy1 + 1.0;
dy = srcpixy - (int)srcpixy;
dy1 = 1.0 - dy;
for ( int dstx = 0; dstx < width; dstx++ )
{
// X-axis of pixel to interpolate from
srcpixx = double(dstx) * WFactor;
srcpixx1 = int(srcpixx);
srcpixx2 = ( srcpixx1 == srcpixxmax ) ? srcpixx1 : srcpixx1 + 1.0;
dx = srcpixx - (int)srcpixx;
dx1 = 1.0 - dx;
int x_offset1 = srcpixx1 < 0.0 ? 0 : srcpixx1 > srcpixxmax ? srcpixxmax : (int)srcpixx1;
int x_offset2 = srcpixx2 < 0.0 ? 0 : srcpixx2 > srcpixxmax ? srcpixxmax : (int)srcpixx2;
int y_offset1 = srcpixy1 < 0.0 ? 0 : srcpixy1 > srcpixymax ? srcpixymax : (int)srcpixy1;
int y_offset2 = srcpixy2 < 0.0 ? 0 : srcpixy2 > srcpixymax ? srcpixymax : (int)srcpixy2;
int src_pixel_index00 = y_offset1 * M_IMGDATA->m_width + x_offset1;
int src_pixel_index01 = y_offset1 * M_IMGDATA->m_width + x_offset2;
int src_pixel_index10 = y_offset2 * M_IMGDATA->m_width + x_offset1;
int src_pixel_index11 = y_offset2 * M_IMGDATA->m_width + x_offset2;
//first line
r1 = src_data[src_pixel_index00 * 3 + 0] * dx1 + src_data[src_pixel_index01 * 3 + 0] * dx;
g1 = src_data[src_pixel_index00 * 3 + 1] * dx1 + src_data[src_pixel_index01 * 3 + 1] * dx;
b1 = src_data[src_pixel_index00 * 3 + 2] * dx1 + src_data[src_pixel_index01 * 3 + 2] * dx;
if ( src_alpha )
a1 = src_alpha[src_pixel_index00] * dx1 + src_alpha[src_pixel_index01] * dx;
//second line
r2 = src_data[src_pixel_index10 * 3 + 0] * dx1 + src_data[src_pixel_index11 * 3 + 0] * dx;
g2 = src_data[src_pixel_index10 * 3 + 1] * dx1 + src_data[src_pixel_index11 * 3 + 1] * dx;
b2 = src_data[src_pixel_index10 * 3 + 2] * dx1 + src_data[src_pixel_index11 * 3 + 2] * dx;
if ( src_alpha )
a2 = src_alpha[src_pixel_index10] * dx1 + src_alpha[src_pixel_index11] * dx;
//result lines
dst_data[0] = r1 * dy1 + r2 * dy;
dst_data[1] = g1 * dy1 + g2 * dy;
dst_data[2] = b1 * dy1 + b2 * dy;
dst_data += 3;
if ( src_alpha )
*dst_alpha++ = a1 * dy1 + a2 * dy;
}
}
return ret_image;
}
// The following two local functions are for the B-spline weighting of the // The following two local functions are for the B-spline weighting of the
// bicubic sampling algorithm // bicubic sampling algorithm
static inline double spline_cube(double value) static inline double spline_cube(double value)