Add alpha blending for wxImage::Paste

Add test cases for wxImage::Paste.

Closes #12458.

Co-Authored-By: Rachel Mark <kramdar@gmail.com>
This commit is contained in:
Eric Raijmakers
2020-09-23 11:56:58 +02:00
committed by Vadim Zeitlin
parent 8ae9987a29
commit 6e8da8641c
13 changed files with 490 additions and 26 deletions

View File

@@ -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") );
@@ -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;
@@ -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
@@ -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();