diff --git a/include/ege.h b/include/ege.h index 5a339c5..3ceb9a4 100644 --- a/include/ege.h +++ b/include/ege.h @@ -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; @@ -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); @@ -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 diff --git a/src/color.cpp b/src/color.cpp index d8733fa..4374a87 100644 --- a/src/color.cpp +++ b/src/color.cpp @@ -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 diff --git a/src/color.h b/src/color.h index 49e6fd9..5168e36 100644 --- a/src/color.h +++ b/src/color.h @@ -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 diff --git a/src/image.cpp b/src/image.cpp index ca1680e..c408efd 100644 --- a/src/image.cpp +++ b/src/image.cpp @@ -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; @@ -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 @@ -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 diff --git a/src/image.h b/src/image.h index e74d9fd..d7d1f65 100644 --- a/src/image.h +++ b/src/image.h @@ -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