You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于Xiaolin Wu抗锯齿算法的圆角矩形低半径像素异常排查

修复Xiaolin Wu抗锯齿圆角矩形小半径下的外部像素不透明问题

问题场景

  • 绘制小半径(如r=5)的抗锯齿圆角矩形时,圆角外部像素出现不透明异常,无法实现平滑透明的边缘效果
  • 调用参数:DrawRoundedRectangle(600, 300, 140, 140, 5, 0xEE222222)(对应RGBA(34,34,34,238))
  • 推测原因:迭代绘制8个像素导致小半径下像素冲突

原实现代码

void DrawRoundedRectangle(int cx , int cy , int width , int height , int r, uint32_t color)
{
    uint32_t alpha1 , alpha2;
    const uint32_t color2 = color & 0x00FFFFFF;
    int x = r;
    int y = 0;
    float t = 0;
    while ( x > y )
    {
        const float curDist = 1.0f - fmodf( sqrtf( static_cast<float>( r * r - y * y ) ) , 1.0 );
        if ( curDist < t )
            x--;

        alpha1 = color2 | static_cast<uint8_t>( 127 * curDist ) << 24;
        alpha2 = color2 | static_cast<uint8_t>( 127 - 127 * curDist ) << 24;

        // top left
        dot( cx - y , cy - x + 1 , alpha1 );
        dot( cx - y , cy - x - 1 , alpha2 );

        dot( cx - x + 1 , cy - y , alpha1 );
        dot( cx - x - 1 , cy - y , alpha2 );

        // bot left
        dot( cx - x + 1 , cy + y + height , alpha1 );
        dot( cx - x - 1 , cy + y + height , alpha2 );

        dot( cx - y , cy + x - 1 + height , alpha1 );
        dot( cx - y , cy + x + 1 + height , alpha2 );

        // bot right
        dot( cx + y + width , cy + x - 1 + height , alpha1 );
        dot( cx + y + width , cy + x + 1 + height , alpha2 );

        dot( cx + x - 1 + width , cy + y + height , alpha1 );
        dot( cx + x + 1 + width , cy + y + height , alpha2 );

        // top right
        dot( cx + x - 1 + width , cy - y , alpha1 );
        dot( cx + x + 1 + width , cy - y , alpha2 );

        dot( cx + y + width , cy - x + 1 , alpha1 );
        dot( cx + y + width , cy - x - 1 , alpha2 );

        // last iteration only
        if ( y == x )
        {
            dot( cx - y, cy - x , color );
            dot( cx - x , cy + y + height  , color );
            dot( cx + y + width , cy + x + height  , color );
            dot( cx + x + width , cy - y  , color );

            dot( cx - y , cy - x - 1, alpha2 );
            dot( cx - x , cy + y + height - 1, alpha2 );
            dot( cx + y + width , cy + x + height - 1, alpha2 );
            dot( cx + x + width , cy - y - 1, alpha2 );
        }
        // skip 1st iteration
        else if ( y > 0 ) 
        {
            dot( cx - y , cy - x , color );
            dot( cx - x , cy - y , color );
            dot( cx - x , cy + y + height , color );
            dot( cx - y , cy + x + height , color );
            dot( cx + y + width , cy + x + height , color );
            dot( cx + x + width , cy + y + height , color );
            dot( cx + x + width , cy - y , color );
            dot( cx + y + width , cy - x , color );
        }


        t = curDist;

        y++;
    }

    // rectangle lines
    setColor( color );
    DrawLine( cx  , cy - r + 1 , cx + width + 1, cy - r + 1 );
    DrawLine( cx - r + 1 , cy , cx - r + 1 , cy + height + 1 );
    DrawLine( cx , cy + height + r - 1 , cx + width + 1, cy + height + r - 1 );
    DrawLine( cx + width + r - 1 , cy + height + 1, cx + width + r - 1 , cy  );

    // rectangle outer shadow
    auto half_opacity = ( ( ( color & 0xFF000000 ) >> 24 ) / 2 ) << 24;
    setColor( half_opacity | color2 );
    DrawLine( cx  , cy - r , cx + width + 1, cy - r );
    DrawLine( cx - r , cy  , cx - r , cy + height + 1 );
    DrawLine( cx + 1 , cy + height + r , cx + width + 1, cy + height + r );
    DrawLine( cx + width + r , cy + height + 1, cx + width + r , cy );
}

问题根源分析

  1. Alpha值缩放逻辑错误:原代码固定用127作为Alpha基准值,未基于输入颜色的原始Alpha(238)做比例缩放,导致抗锯齿像素的Alpha范围与主颜色不匹配,小半径下重叠后边缘生硬。
  2. 像素重复覆盖:小半径迭代时,部分坐标会被多次调用dot函数绘制,若dot是直接赋值而非Alpha混合,后绘制的不透明像素会覆盖之前的半透明像素。
  3. 阴影线范围不合理:外部阴影线的绘制范围与圆角抗锯齿区域重叠,进一步加剧边缘不透明问题。

不破坏现有逻辑的修复方案

1. 修正Alpha缩放计算

基于输入颜色的原始Alpha值计算抗锯齿像素的透明度,替换原Alpha计算代码:

uint8_t original_alpha = (color >> 24) & 0xFF;
alpha1 = color2 | static_cast<uint8_t>( original_alpha * curDist ) << 24;
alpha2 = color2 | static_cast<uint8_t>( original_alpha * (1.0f - curDist) ) << 24;

2. 确保dot函数支持Alpha混合

如果dot当前是直接覆盖像素,修改为Alpha混合逻辑:

// 示例Alpha混合实现(需根据实际渲染上下文调整)
void dot(int x, int y, uint32_t color) {
    uint8_t src_r = (color >> 16) & 0xFF;
    uint8_t src_g = (color >> 8) & 0xFF;
    uint8_t src_b = color & 0xFF;
    uint8_t src_a = (color >> 24) & 0xFF;

    uint32_t dst_color = get_pixel(x, y); // 获取目标像素颜色
    uint8_t dst_r = (dst_color >> 16) & 0xFF;
    uint8_t dst_g = (dst_color >> 8) & 0xFF;
    uint8_t dst_b = dst_color & 0xFF;
    uint8_t dst_a = (dst_color >> 24) & 0xFF;

    float src_weight = src_a / 255.0f;
    float dst_weight = 1.0f - src_weight;

    uint8_t new_r = static_cast<uint8_t>(src_r * src_weight + dst_r * dst_weight);
    uint8_t new_g = static_cast<uint8_t>(src_g * src_weight + dst_g * dst_weight);
    uint8_t new_b = static_cast<uint8_t>(src_b * src_weight + dst_b * dst_weight);
    uint8_t new_a = static_cast<uint8_t>(src_a + dst_a * dst_weight);

    set_pixel(x, y, (new_a << 24) | (new_r << 16) | (new_g << 8) | new_b);
}

3. 调整阴影线绘制范围

避免阴影线与圆角抗锯齿区域重叠,修改阴影线绘制代码:

// 修正后的外部阴影线(仅绘制圆角之间的直线部分)
DrawLine( cx + r , cy - r , cx + width - r , cy - r );
DrawLine( cx - r , cy + r , cx - r , cy + height - r );
DrawLine( cx + r , cy + height + r , cx + width - r , cy + height + r );
DrawLine( cx + width + r , cy + r , cx + width + r , cy + height - r );

4. 优化循环终止条件

避免重复绘制相同坐标,将循环条件改为while(y <= x)并调整x递减逻辑:

while (y <= x) {
    const float curDist = 1.0f - fmodf( sqrtf( static_cast<float>( r * r - y * y ) ) , 1.0 );
    if (curDist < t && x > 0) {
        x--;
    }

    // ... 其余绘制逻辑不变 ...

    t = curDist;
    y++;
}

内容的提问来源于stack exchange,提问作者Svotin

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.21 10:15:33