Skip to content

Commit

Permalink
Merge branch 'image-paste-alpha'
Browse files Browse the repository at this point in the history
Add support for alpha blending to wxImage::Paste() and add unit tests
for it.

Closes wxWidgets#2056
  • Loading branch information
vadz committed Sep 24, 2020
2 parents 8ae9987 + 451ed78 commit 1f3150b
Show file tree
Hide file tree
Showing 13 changed files with 490 additions and 26 deletions.
19 changes: 16 additions & 3 deletions include/wx/image.h
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,16 @@ enum wxImageResizeQuality
wxIMAGE_QUALITY_HIGH = 4
};

// Constants for wxImage::Paste() for specifying alpha blending option.
enum wxImageAlphaBlendMode
{
// Overwrite the original alpha values with the ones being pasted.
wxIMAGE_ALPHA_BLEND_OVER = 0,

// Compose the original alpha values with the ones being pasted.
wxIMAGE_ALPHA_BLEND_COMPOSE = 1
};

// alpha channel values: fully transparent, default threshold separating
// transparent pixels from opaque for a few functions dealing with alpha and
// fully opaque
Expand Down Expand Up @@ -348,9 +358,12 @@ class WXDLLIMPEXP_CORE wxImage: public wxObject
wxImage Size( const wxSize& size, const wxPoint& pos,
int r = -1, int g = -1, int b = -1 ) const;

// pastes image into this instance and takes care of
// the mask colour and out of bounds problems
void Paste( const wxImage &image, int x, int y );
// Copy the data of the given image to the specified position of this one
// taking care of the out of bounds problems. Mask is respected, but alpha
// is simply replaced by default, use wxIMAGE_ALPHA_BLEND_COMPOSE to
// combine it with the original image alpha values if needed.
void Paste(const wxImage& image, int x, int y,
wxImageAlphaBlendMode alphaBlend = wxIMAGE_ALPHA_BLEND_OVER);

// return the new image with size width*height
wxImage Scale( int width, int height,
Expand Down
26 changes: 25 additions & 1 deletion interface/wx/image.h
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,21 @@ enum wxImageResizeQuality
wxIMAGE_QUALITY_HIGH
};


/**
Constants for wxImage::Paste() for specifying alpha blending option.
@since 3.1.5
*/
enum wxImageAlphaBlendMode
{
/// Overwrite the original alpha values with the ones being pasted.
wxIMAGE_ALPHA_BLEND_OVER = 0,

/// Compose the original alpha values with the ones being pasted.
wxIMAGE_ALPHA_BLEND_COMPOSE = 1
};

/**
Possible values for PNG image type option.
Expand Down Expand Up @@ -803,8 +818,17 @@ class wxImage : public wxObject

/**
Copy the data of the given @a image to the specified position in this image.
Takes care of the mask colour and out of bounds problems.
@param alphaBlend
This parameter (new in wx 3.1.5) determines whether the alpha values
of the original image replace (default) or are composed with the
alpha channel of this image. Notice that alpha blending overrides
the mask handling.
*/
void Paste(const wxImage& image, int x, int y);
void Paste(const wxImage& image, int x, int y,
wxImageAlphaBlendMode alphaBlend = wxIMAGE_ALPHA_BLEND_OVER);

/**
Replaces the colour specified by @e r1,g1,b1 by the colour @e r2,g2,b2.
Expand Down
85 changes: 70 additions & 15 deletions src/common/image.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1608,7 +1608,9 @@ wxImage wxImage::Size( const wxSize& size, const wxPoint& pos,
return image;
}

void wxImage::Paste( const wxImage &image, int x, int y )
void
wxImage::Paste(const wxImage & image, int x, int y,
wxImageAlphaBlendMode alphaBlend)
{
wxCHECK_RET( IsOk(), wxT("invalid image") );
wxCHECK_RET( image.IsOk(), wxT("invalid image") );
Expand Down Expand Up @@ -1639,15 +1641,18 @@ void wxImage::Paste( const wxImage &image, int x, int y )
if (width < 1) return;
if (height < 1) return;

bool copiedPixels = false;

// If we can, copy the data using memcpy() as this is the fastest way. But
// for this the image being pasted must have "compatible" mask with this
// one meaning that either it must not have one at all or it must use the
// same masked colour.
if ( !image.HasMask() ||
// for this we must not do alpha compositing and the image being pasted
// must have "compatible" mask with this one meaning that either it must
// not have one at all or it must use the same masked colour.
if (alphaBlend == wxIMAGE_ALPHA_BLEND_OVER &&
(!image.HasMask() ||
((HasMask() &&
(GetMaskRed()==image.GetMaskRed()) &&
(GetMaskGreen()==image.GetMaskGreen()) &&
(GetMaskBlue()==image.GetMaskBlue()))) )
(GetMaskBlue()==image.GetMaskBlue())))) )
{
const unsigned char* source_data = image.GetData() + 3*(xx + yy*image.GetWidth());
int source_step = image.GetWidth()*3;
Expand All @@ -1660,6 +1665,8 @@ void wxImage::Paste( const wxImage &image, int x, int y )
source_data += source_step;
target_data += target_step;
}

copiedPixels = true;
}

// Copy over the alpha channel from the original image
Expand All @@ -1668,21 +1675,69 @@ void wxImage::Paste( const wxImage &image, int x, int y )
if ( !HasAlpha() )
InitAlpha();

const unsigned char* source_data = image.GetAlpha() + xx + yy*image.GetWidth();
int source_step = image.GetWidth();
const unsigned char*
alpha_source_data = image.GetAlpha() + xx + yy * image.GetWidth();
const int source_step = image.GetWidth();

unsigned char* target_data = GetAlpha() + (x+xx) + (y+yy)*M_IMGDATA->m_width;
int target_step = M_IMGDATA->m_width;
unsigned char*
alpha_target_data = GetAlpha() + (x + xx) + (y + yy) * M_IMGDATA->m_width;
const int target_step = M_IMGDATA->m_width;

for (int j = 0; j < height; j++,
source_data += source_step,
target_data += target_step)
switch (alphaBlend)
{
memcpy( target_data, source_data, width );
case wxIMAGE_ALPHA_BLEND_OVER:
{
// Copy just the alpha values.
for (int j = 0; j < height; j++,
alpha_source_data += source_step,
alpha_target_data += target_step)
{
memcpy(alpha_target_data, alpha_source_data, width);
}
break;
}
case wxIMAGE_ALPHA_BLEND_COMPOSE:
{
const unsigned char*
source_data = image.GetData() + 3 * (xx + yy * image.GetWidth());

unsigned char*
target_data = GetData() + 3 * ((x + xx) + (y + yy) * M_IMGDATA->m_width);

// Combine the alpha values but also apply alpha blending to
// the pixels themselves while we copy them.
for (int j = 0; j < height; j++,
alpha_source_data += source_step,
alpha_target_data += target_step,
source_data += 3 * source_step,
target_data += 3 * target_step)
{
for (int i = 0; i < width; i++)
{
float source_alpha = alpha_source_data[i] / 255.0f;
float light_left = (alpha_target_data[i] / 255.0f) * (1.0f - source_alpha);
float result_alpha = source_alpha + light_left;
alpha_target_data[i] = (unsigned char)((result_alpha * 255) +0.5);
for (int c = 3 * i; c < 3 * (i + 1); c++)
{
target_data[c] =
(unsigned char)(((source_data[c] * source_alpha +
target_data[c] * light_left) /
result_alpha) + 0.5);
}
}
}

copiedPixels = true;
break;
}
}

}

if (!HasMask() && image.HasMask())
// If we hadn't copied them yet we must need to take the mask of the image
// being pasted into account.
if (!copiedPixels)
{
unsigned char r = image.GetMaskRed();
unsigned char g = image.GetMaskGreen();
Expand Down
Loading

0 comments on commit 1f3150b

Please sign in to comment.