Skip to content

Commit

Permalink
Feat:支持对使用预乘 alpha 像素格式的图像进行 alpha 混合运算 (#208)
Browse files Browse the repository at this point in the history
* feat: putimage_alphablend 支持绘制预乘 alpha 像素格式的图像
* feat: 增加 alphablend_premultiplied,支持使用预乘 alpha 的像素格式进行 alpha 混合运算
  • Loading branch information
yixy-only authored Jul 3, 2024
1 parent 6023c33 commit 4d4e7f3
Show file tree
Hide file tree
Showing 5 changed files with 281 additions and 28 deletions.
63 changes: 54 additions & 9 deletions include/ege.h
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,12 @@ enum message_mouse
typedef unsigned int color_t;
#endif

enum alpha_type
{
ALPHATYPE_STRAIGHT = 0,
ALPHATYPE_PREMULTIPLIED = 1,
};

struct ege_point
{
float x;
Expand Down Expand Up @@ -968,6 +974,8 @@ color_t EGEAPI colorblend (color_t dst, color_t src, unsigned char alpha);
color_t EGEAPI colorblend_f(color_t dst, color_t src, unsigned char alpha);
color_t EGEAPI alphablend (color_t dst, color_t src);
color_t EGEAPI alphablend (color_t dst, color_t src, unsigned char srcAlphaFactor);
color_t EGEAPI alphablend_premultiplied(color_t dst, color_t src);
color_t EGEAPI alphablend_premultiplied(color_t dst, color_t src, unsigned char srcAlphaFactor);

color_t EGEAPI getpixel (int x, int y, PCIMAGE pimg = NULL);
void EGEAPI putpixel (int x, int y, color_t color, PIMAGE pimg = NULL);
Expand Down Expand Up @@ -1324,17 +1332,54 @@ int EGEAPI putimage_transparent(
int widthSrc = 0, // width of source rectangle
int heightSrc = 0 // height of source rectangle
);

int EGEAPI putimage_alphablend(
PIMAGE imgDest, // handle to dest
PCIMAGE imgSrc, // handle to source
int xDest, // x-coord of destination upper-left corner
int yDest, // y-coord of destination upper-left corner
unsigned char alpha, // alpha
int xSrc = 0, // x-coord of source upper-left corner
int ySrc = 0, // y-coord of source upper-left corner
int widthSrc = 0, // width of source rectangle
int heightSrc = 0 // height of source rectangle
PIMAGE imgDest,
PCIMAGE imgSrc,
int xDest,
int yDest,
unsigned char alpha,
alpha_type alphaType = ALPHATYPE_STRAIGHT
);
int EGEAPI putimage_alphablend(
PIMAGE imgDest,
PCIMAGE imgSrc,
int xDest,
int yDest,
unsigned char alpha,
int xSrc,
int ySrc,
alpha_type alphaType = ALPHATYPE_STRAIGHT
);
int EGEAPI putimage_alphablend(
PIMAGE imgDest,
PCIMAGE imgSrc,
int xDest,
int yDest,
unsigned char alpha,
int xSrc,
int ySrc,
int widthSrc,
int heightSrc,
alpha_type alphaType = ALPHATYPE_STRAIGHT
);

int EGEAPI putimage_alphablend(
PIMAGE imgDest,
PCIMAGE imgSrc,
int xDest,
int yDest,
int widthDest,
int heightDest,
unsigned char alpha,
int xSrc,
int ySrc,
int widthSrc,
int heightSrc,
bool smooth = false,
alpha_type alphaType = ALPHATYPE_STRAIGHT
);

int EGEAPI putimage_alphatransparent(
PIMAGE imgDest, // handle to dest
PCIMAGE imgSrc, // handle to source
Expand Down
15 changes: 15 additions & 0 deletions src/color.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -372,4 +372,19 @@ color_t alphablend(color_t dst, color_t src, unsigned char srcAlphaFactor)
return alphablend_inline(dst, src, srcAlphaFactor);
}

color_t alphablend_premultiplied(color_t dst, color_t src)
{
return alphablend_premultiplied_inline(dst, src);
}

color_t alphablend_premultiplied(color_t dst, color_t src, unsigned char srcAlphaFactor)
{
byte alpha = DIVIDE_255_FAST(EGEGET_A(src) * srcAlphaFactor + 255/2);
byte red = DIVIDE_255_FAST(EGEGET_R(src) * srcAlphaFactor + 255/2);
byte green = DIVIDE_255_FAST(EGEGET_G(src) * srcAlphaFactor + 255/2);
byte blue = DIVIDE_255_FAST(EGEGET_B(src) * srcAlphaFactor + 255/2);

return alphablend_premultiplied_inline(dst, EGEARGB(alpha, red, green, blue));
}

} // namespace ege
22 changes: 22 additions & 0 deletions src/color.h
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,28 @@ EGE_FORCEINLINE color_t alphablend_inline(color_t dst, color_t src, byte srcAlph
return alphablend_specify_inline(dst, src, alpha);
}

/**
* @brief 将两个预乘 alpha 的 ARGB 颜色进行混合
*
* @param dst 背景色(PARGB)
* @param src 前景色(PARGB)
* @return 混合后的 PARGB 颜色
* @note 混合公式:
* A = A(src) + (1.0 - alpha) * A(dst);;
* R = R(src) + (1.0 - alpha) * R(dst);
* G = G(src) + (1.0 - alpha) * G(dst);
* B = B(src) + (1.0 - alpha) * B(dst);
*/
EGE_FORCEINLINE color_t alphablend_premultiplied_inline(color_t dst, color_t src)
{
const byte a = DIVIDE_255_FAST(255 * EGEGET_A(src) + (255 - EGEGET_A(src)) * EGEGET_A(dst));
const byte r = DIVIDE_255_FAST(255 * EGEGET_R(src) + (255 - EGEGET_A(src)) * EGEGET_R(dst));
const byte g = DIVIDE_255_FAST(255 * EGEGET_G(src) + (255 - EGEGET_A(src)) * EGEGET_G(dst));
const byte b = DIVIDE_255_FAST(255 * EGEGET_B(src) + (255 - EGEGET_A(src)) * EGEGET_B(dst));

return EGEARGB(a, r, g, b);
}

} //namespace ege


192 changes: 174 additions & 18 deletions src/image.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -977,34 +977,146 @@ int IMAGE::putimage_alphablend(PIMAGE imgDest, // handle to dest
int xSrc, // x-coord of source upper-left corner
int ySrc, // y-coord of source upper-left corner
int widthSrc, // width of source rectangle
int heightSrc // height of source rectangle
int heightSrc,// height of source rectangle
alpha_type alphaType // alpha mode(straight alpha or premultiplied alpha)
) const
{
inittest(L"IMAGE::putimage_alphablend");
const PIMAGE img = CONVERT_IMAGE(imgDest);
if (img) {
if (alpha == 0)
return grOk;

PCIMAGE imgSrc = this;
int y, x;
DWORD ddx, dsx;
DWORD * pdp, *psp;
// fix rect
fix_rect_1size(img, imgSrc, &xDest, &yDest, &xSrc, &ySrc, &widthSrc, &heightSrc);

if ((widthSrc == 0) || (heightSrc == 0))
return grOk;
// draw
pdp = img->m_pBuffer + yDest * img->m_width + xDest;
psp = imgSrc->m_pBuffer + ySrc * imgSrc->m_width + xSrc;
ddx = img->m_width - widthSrc;
dsx = imgSrc->m_width - widthSrc;

for (y = 0; y < heightSrc; ++y) {
for (x = 0; x < widthSrc; ++x, ++psp, ++pdp) {
DWORD d = *pdp, s = *psp;
*pdp = alphablend_inline(d, s, alpha);
if (alphaType == ALPHATYPE_PREMULTIPLIED) {
BLENDFUNCTION bf;
bf.BlendOp = AC_SRC_OVER;
bf.BlendFlags = 0;
bf.SourceConstantAlpha = alpha;
bf.AlphaFormat = AC_SRC_ALPHA;
// draw
dll::AlphaBlend(img->m_hDC, xDest, yDest, widthSrc, heightSrc,
imgSrc->m_hDC, xSrc, ySrc, widthSrc, heightSrc, bf);
} else {
DWORD* pdp = img->m_pBuffer + yDest * img->m_width + xDest;
DWORD* psp = imgSrc->m_pBuffer + ySrc * imgSrc->m_width + xSrc;
DWORD ddx = img->m_width - widthSrc;
DWORD dsx = imgSrc->m_width - widthSrc;

if (alpha == 0xFF) {
for (int y = 0; y < heightSrc; ++y) {
for (int x = 0; x < widthSrc; ++x, ++psp, ++pdp) {
DWORD d = *pdp, s = *psp;
*pdp = alphablend_inline(d, s);
}
pdp += ddx;
psp += dsx;
}
} else {
for (int y = 0; y < heightSrc; ++y) {
for (int x = 0; x < widthSrc; ++x, ++psp, ++pdp) {
DWORD d = *pdp, s = *psp;
*pdp = alphablend_inline(d, s, alpha);
}
pdp += ddx;
psp += dsx;
}
}
pdp += ddx;
psp += dsx;
}
}
CONVERT_IMAGE_END;
return grOk;
}

int IMAGE::putimage_alphablend(PIMAGE imgDest, // handle to dest
int xDest, // x-coord of destination upper-left corner
int yDest, // y-coord of destination upper-left corner
int widthDest, // width of source rectangle
int heightDest, // height of source rectangle
unsigned char alpha, // alpha
int xSrc, // x-coord of source upper-left corner
int ySrc, // y-coord of source upper-left corner
int widthSrc, // width of source rectangle
int heightSrc, // height of source rectangle
bool smooth,
alpha_type alphaType // alpha mode(straight alpha or premultiplied alpha)
) const
{
inittest(L"IMAGE::putimage_alphablend");
const PIMAGE img = CONVERT_IMAGE(imgDest);
if (img) {
if (alpha == 0)
return grOk;

PCIMAGE imgSrc = this;

if (widthSrc <= 0) widthSrc = imgSrc->m_width;
if (heightSrc <= 0) heightSrc = imgSrc->m_height;
if (widthDest <= 0) widthDest = widthSrc;
if (heightDest <= 0) heightDest = heightSrc;

if ((alphaType == ALPHATYPE_PREMULTIPLIED) && !smooth) {
BLENDFUNCTION bf;
bf.BlendOp = AC_SRC_OVER;
bf.BlendFlags = 0;
bf.SourceConstantAlpha = alpha;
bf.AlphaFormat = AC_SRC_ALPHA;
// draw
dll::AlphaBlend(img->m_hDC, xDest, yDest, widthDest, heightDest, imgSrc->m_hDC, xSrc, ySrc, widthSrc,
heightSrc, bf);
} else {
const viewporttype& vptDest = img->m_vpt;
const viewporttype& vptSrc = imgSrc->m_vpt;
Rect drawDest(xDest + vptDest.left, yDest + vptDest.top, widthDest, heightDest);
Rect drawSrc(xSrc + vptSrc.left, ySrc + vptSrc.top, widthSrc, heightSrc);

Gdiplus::Graphics* graphics = img->getGraphics();
Gdiplus::Matrix matrix;
graphics->GetTransform(&matrix);
graphics->ResetTransform();

if (smooth) {
graphics->SetInterpolationMode(Gdiplus::InterpolationModeHighQualityBicubic);
} else {
graphics->SetInterpolationMode(Gdiplus::InterpolationModeNearestNeighbor);
}

Gdiplus::PixelFormat pixelFormat = PixelFormat32bppARGB;

if (alphaType == ALPHATYPE_PREMULTIPLIED) {
pixelFormat = PixelFormat32bppPARGB;
}

// Create an ImageAttributes object and set its color matrix.
Gdiplus::ImageAttributes* imageAtt = NULL;

if (alpha != 0xFF) {
float scale = alpha / 255.0f;
Gdiplus::ColorMatrix colorMatrix = {
1.0f, 0.0f, 0.0f, 0.0f, 0.0f,
0.0f, 1.0f, 0.0f, 0.0f, 0.0f,
0.0f, 0.0f, 1.0f, 0.0f, 0.0f,
0.0f, 0.0f, 0.0f, scale, 0.0f,
0.0f, 0.0f, 0.0f, 0.0f, 1.0f
};
imageAtt = new Gdiplus::ImageAttributes;
imageAtt->SetColorMatrix(&colorMatrix, Gdiplus::ColorMatrixFlagsDefault, Gdiplus::ColorAdjustTypeBitmap);
}

Gdiplus::RectF rectDest((float)drawDest.x, (float)drawDest.y, (float)drawDest.width, (float)drawDest.height);
Gdiplus::RectF rectSrc((float)drawSrc.x, (float)drawSrc.y, (float)drawSrc.width, (float)drawSrc.height);

int stride = sizeof(color_t) * imgSrc->m_width;
Gdiplus::Bitmap bitmap(imgSrc->m_width, imgSrc->m_height, stride, pixelFormat, (BYTE*)imgSrc->m_pBuffer);
graphics->DrawImage(&bitmap, rectDest, rectSrc.X, rectSrc.Y, rectSrc.Width, rectSrc.Height, Gdiplus::UnitPixel, imageAtt);
graphics->SetTransform(&matrix);

delete imageAtt;
}
}
CONVERT_IMAGE_END;
Expand Down Expand Up @@ -2869,6 +2981,29 @@ int putimage_transparent(PIMAGE imgDest, // handle to dest
imgDest, xDest, yDest, transparentColor, xSrc, ySrc, widthSrc, heightSrc);
}

int EGEAPI putimage_alphablend(
PIMAGE imgDest,
PCIMAGE imgSrc,
int xDest, int yDest,
unsigned char alpha,
alpha_type alphaType
)
{
return putimage_alphablend(imgDest, imgSrc, xDest, yDest, alpha, 0, 0, 0, 0, alphaType);
}

int EGEAPI putimage_alphablend(
PIMAGE imgDest,
PCIMAGE imgSrc,
int xDest, int yDest,
unsigned char alpha,
int xSrc, int ySrc,
alpha_type alphaType
)
{
return putimage_alphablend(imgDest, imgSrc, xDest, yDest, alpha, xSrc, ySrc, 0, 0, alphaType);
}

int putimage_alphablend(PIMAGE imgDest, // handle to dest
PCIMAGE imgSrc, // handle to source
int xDest, // x-coord of destination upper-left corner
Expand All @@ -2877,12 +3012,33 @@ int putimage_alphablend(PIMAGE imgDest, // handle to dest
int xSrc, // x-coord of source upper-left corner
int ySrc, // y-coord of source upper-left corner
int widthSrc, // width of source rectangle
int heightSrc // height of source rectangle
int heightSrc, // height of source rectangle
alpha_type alphaType // alpha mode(straight alpha or premultiplied alpha)
)
{
imgSrc = CONVERT_IMAGE_CONST(imgSrc);
return imgSrc->putimage_alphablend(imgDest, xDest, yDest, alpha, xSrc, ySrc, widthSrc, heightSrc, alphaType);
}

int EGEAPI putimage_alphablend(
PIMAGE imgDest, // handle to dest
PCIMAGE imgSrc, // handle to source
int xDest, int yDest, int widthDest, int heightDest,
unsigned char alpha,
int xSrc, int ySrc, int widthSrc, int heightSrc,
bool smooth,
alpha_type alphaType
)
{
imgSrc = CONVERT_IMAGE_CONST(imgSrc);
return imgSrc->putimage_alphablend(
imgDest, xDest, yDest, alpha, xSrc, ySrc, widthSrc, heightSrc);
imgDest,
xDest, yDest, widthDest, heightDest,
alpha,
xSrc, ySrc, widthSrc, heightSrc,
smooth,
alphaType
);
}

int putimage_alphatransparent(PIMAGE imgDest, // handle to dest
Expand Down
17 changes: 16 additions & 1 deletion src/image.h
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,22 @@ class IMAGE
int xSrc = 0, // x-coord of source upper-left corner
int ySrc = 0, // y-coord of source upper-left corner
int widthSrc = 0, // width of source rectangle
int heightSrc = 0 // height of source rectangle
int heightSrc = 0, // height of source rectangle
alpha_type alphaType = ALPHATYPE_STRAIGHT // alpha mode(straight alpha or premultiplied alpha)
) const;

int putimage_alphablend(PIMAGE imgDest, // handle to dest
int xDest, // x-coord of destination upper-left corner
int yDest, // y-coord of destination upper-left corner
int widthDest, // width of source rectangle
int heightDest, // height of source rectangle
unsigned char alpha, // alpha
int xSrc, // x-coord of source upper-left corner
int ySrc, // y-coord of source upper-left corner
int widthSrc, // width of source rectangle
int heightSrc, // height of source rectangle
bool smooth = false,
alpha_type alphaType = ALPHATYPE_STRAIGHT // alpha mode(straight alpha or premultiplied alpha)
) const;

int putimage_alphatransparent(PIMAGE imgDest, // handle to dest
Expand Down

0 comments on commit 4d4e7f3

Please sign in to comment.