zouhuidong / HiEasyX

HiEasyX 是基于 EasyX 的扩展库,支持创建多窗口、透明抗锯齿绘图、系统 UI 组件等等。
MIT License
114 stars 21 forks source link

MixAlphaColor有个bug #5

Closed koow520530 closed 1 year ago

koow520530 commented 1 year ago
    float fRatioAlpha = ((alpha == 255) ? 1 : (alpha / 255.0f));        // 透明度比例
    float fRatioSrc = GetAValue(cSrc) / 255.0f;                         // 原像素比例
    float fRatioSrcAlpha = fRatioSrc * fRatioAlpha;                     // 原像素比例叠加透明度比例
    if (isCalculated)   fRatioSrc = fRatioAlpha;                        // 若原像素已经混合过,则设为透明度比例
    else            fRatioSrc = fRatioSrcAlpha;                     // 若原像素未混合过,则设为两个透明通道的叠加比例

    float fRatioDst = 1 - fRatioSrc;   //这条语句是否应该放在这里,src和dst的颜色分量互斥
zouhuidong commented 1 year ago

这是个好问题,很抱歉这么晚才回复你。 实际上,src 和 dst 的颜色分量不应该互斥,因为这个函数的作用是将 dst 叠加到 src 上,所以 src 的颜色分量不应该被改变。但如果 alpha 参数不为 255(不透明),则 dst 和 src 都会被再次叠上一层透明度,然后再混合到一起。

如果你觉得没问题,可以关闭这个 issue,或者我会在几个星期后把它关闭。

koow520530 commented 1 year ago

fRatioAlpha 0.5 fRatioSrc 0.5 fRatioSrcAlpha 0.25 fRatioDst 0.75 fRatioSrc 0.5 按照原始的,src+dst超过1了,两个颜色(192,96,150)和(250,0,170)紫红混合成了(19,72,197)蓝色,而且dst的alpha如果是1的话,那就没有透明效果,就是蓝色了。你可以测试一下。我是因为显示出错了,才查到这个地方可能有问题。

zouhuidong commented 1 year ago

因为我一直在学校,所以回复很迟,抱歉。

我重新检查了我的代码,确实存在你所说的问题。由于之前 cSrc 和 cDst 的颜色分量相加可能大于 1,所以导致混合后的色值中,rgb 分量可能大于 255,造成溢出。

下面是我重新写的 MixAlphaColor 函数,你可以先用它替换你的项目中旧的 MixAlphaColor 函数,测试问题是否解决,如果没有问题,我会把这个函数更新到下一个版本中。

/**
 * @brief 根据透明度混合颜色
 * @param[in] cDst              原位置像素
 * @param[in] cSrc              待绘制像素(根据其透明度混合颜色)
 * @param[in] isCalculated      待绘制像素点是否已经乘以它的透明度 <p>
 *                              例如,透明 png 图像中的像素就是已经乘过透明度的。<p>
 *                              但是一般在程序中创建的颜色是没用乘过的。<p>
 * @param[in] alpha             叠加在 src 上的透明度(默认为 255,即不叠加)
 * @return 混合后的颜色(不含透明信息)
*/
COLORREF MixAlphaColor(COLORREF cDst, COLORREF cSrc, bool isCalculated, BYTE alpha = 255);

COLORREF MixAlphaColor(COLORREF cDst, COLORREF cSrc, bool isCalculated, BYTE alpha)
{
  float fSrc = GetAValue(cSrc) / 255.0f;    // 待绘制像素的透明度
  if (alpha != 255)                     // 叠加透明度
    fSrc *= alpha / 255.0f;
  if (fSrc == 0.0f)                     // 绘制透明度为 0 时不做任何处理
    return cDst;
  float fDst = 1 - fSrc;                    // 原位置像素应乘透明度

  if (isCalculated)
  {
    return RGB(
      GetRValue(cSrc) + GetRValue(cDst) * fDst,
      GetGValue(cSrc) + GetGValue(cDst) * fDst,
      GetBValue(cSrc) + GetBValue(cDst) * fDst
    );
  }
  else
  {
    return RGB(
      GetRValue(cSrc) * fSrc + GetRValue(cDst) * fDst,
      GetGValue(cSrc) * fSrc + GetGValue(cDst) * fDst,
      GetBValue(cSrc) * fSrc + GetBValue(cDst) * fDst
    );
  }

}

另附一份测试代码(可直接编译运行)

#include "HiEasyX.h"
#include <conio.h>

int main()
{
    hiex::Window wnd(640, 480);
    hiex::AutoExit();

    hiex::Canvas canvas;
    wnd.BindCanvas(&canvas);

    canvas.Clear(true, WHITE);
    canvas.SolidRectangle(100, 100, 300, 300, true, PURPLE);

    for (byte i = 0; i < 255; i++)
    {
        canvas.SolidRectangle(200, 200, 400, 400, true, hiex::MixAlphaColor(WHITE, SET_ALPHA(RED, i), false));
        canvas.SolidRectangle(200, 200, 300, 300, true, hiex::MixAlphaColor(PURPLE, SET_ALPHA(RED, i), false));

        wnd.Redraw();
        Sleep(10);
    }

    _getch();

    wnd.CloseWindow();
    return 0;
}

不知道能不能解决你的问题,期待你的回复!

zouhuidong commented 1 year ago

此问题已在 Ver0.2.2 中修复,详见 https://github.com/zouhuidong/HiEasyX/releases/tag/Ver0.2.2