【问题标题】:How to alpha blend RGBA unsigned byte color fast?如何快速混合 RGBA 无符号字节颜色?
【发布时间】:2009-07-09 09:04:36
【问题描述】:

我正在使用 c++,我想使用以下代码进行 alpha 混合。

#define CLAMPTOBYTE(color) \
    if ((color) & (~255)) { \
        color = (BYTE)((-(color)) >> 31); \
    } else { \
        color = (BYTE)(color); \
    }
#define GET_BYTE(accessPixel, x, y, scanline, bpp) \
    ((BYTE*)((accessPixel) + (y) * (scanline) + (x) * (bpp))) 

    for (int y = top ; y < bottom; ++y)
    {
        BYTE* resultByte = GET_BYTE(resultBits, left, y, stride, bytepp);
        BYTE* srcByte = GET_BYTE(srcBits, left, y, stride, bytepp);
        BYTE* srcByteTop = GET_BYTE(srcBitsTop, left, y, stride, bytepp);
        BYTE* maskCurrent = GET_GREY(maskSrc, left, y, width);
        int alpha = 0;
        int red = 0;
        int green = 0;
        int blue = 0;
        for (int x = left; x < right; ++x)
        {
            alpha = *maskCurrent;
            red = (srcByteTop[R] * alpha + srcByte[R] * (255 - alpha)) / 255;
            green = (srcByteTop[G] * alpha + srcByte[G] * (255 - alpha)) / 255;
            blue = (srcByteTop[B] * alpha + srcByte[B] * (255 - alpha)) / 255;
            CLAMPTOBYTE(red);
            CLAMPTOBYTE(green);
            CLAMPTOBYTE(blue);
            resultByte[R] = red;
            resultByte[G] = green;
            resultByte[B] = blue;
            srcByte += bytepp;
            srcByteTop += bytepp;
            resultByte += bytepp;
            ++maskCurrent;
        }
    }

但是我发现它仍然很慢,合成两个 600 * 600 图像大约需要 40 - 60 毫秒。 有什么方法可以将速度提高到16ms以下?

任何机构可以帮助我加快这段代码的速度吗?非常感谢!

【问题讨论】:

  • 你用的是什么编译器?你为什么平台开发这个软件?您愿意使用现成的工具吗?
  • 我用的是VS2005,软件是为windows平台设计的。我愿意使用任何方法来加速这段代码。我想也许它可以加速很多
  • 如果您在我的解决方案中编码其余 SIMD 指令时遇到问题,请告诉我

标签: c++ performance


【解决方案1】:

Use SSE - 从第 131 页开始。

基本工作流程

  1. 从 src 加载 4 个像素(16 个 1 字节数字)RGBA RGBA RGBA RGBA(流式加载)

  2. 再加载 4 个要与 srcbytetop RGBx RGBx RGBx RGBx 混合的文件

  3. 做一些调配,以使 1 中的 A 项填满每个插槽,即

    xxxA xxxB xxxC xxxD -> AAAA BBBB CCCC DDDD

    在下面的解决方案中,我选择重新使用现有的“maskcurrent”数组,但是将 alpha 集成到 1 的“A”字段中将需要更少的内存负载,因此速度更快。在这种情况下,Swizzling 可能是:使用掩码选择 A、B、C、D。右移 8,或使用原始,右移 16,或再次。

  4. 将以上内容添加到每个槽中全为 -255 的向量中

  5. 乘以 1 * 4(带有 255-alpha 的源)和 2 * 3(带有 alpha 的结果)。

    您应该能够为此使用“乘以并丢弃底部 8 位”SSE2 指令。

  6. 将这两个(4 和 5)加在一起

  7. 将它们存储在其他地方(如果可能)或目的地顶部(如果必须)

这里是你的起点:

    //Define your image with __declspec(align(16)) i.e char __declspec(align(16)) image[640*480]
    // so the first byte is aligned correctly for SIMD.
    // Stride must be a multiple of 16.

    for (int y = top ; y < bottom; ++y)
    {
        BYTE* resultByte = GET_BYTE(resultBits, left, y, stride, bytepp);
        BYTE* srcByte = GET_BYTE(srcBits, left, y, stride, bytepp);
        BYTE* srcByteTop = GET_BYTE(srcBitsTop, left, y, stride, bytepp);
        BYTE* maskCurrent = GET_GREY(maskSrc, left, y, width);
        for (int x = left; x < right; x += 4)
        {
            //If you can't align, use _mm_loadu_si128()
            // Step 1
            __mm128i src = _mm_load_si128(reinterpret_cast<__mm128i*>(srcByte)) 
            // Step 2
            __mm128i srcTop = _mm_load_si128(reinterpret_cast<__mm128i*>(srcByteTop)) 

            // Step 3
            // Fill the 4 positions for the first pixel with maskCurrent[0], etc
            // Could do better with shifts and so on, but this is clear
            __mm128i mask = _mm_set_epi8(maskCurrent[0],maskCurrent[0],maskCurrent[0],maskCurrent[0],
                                        maskCurrent[1],maskCurrent[1],maskCurrent[1],maskCurrent[1],
                                        maskCurrent[2],maskCurrent[2],maskCurrent[2],maskCurrent[2],
                                        maskCurrent[3],maskCurrent[3],maskCurrent[3],maskCurrent[3],
                                        ) 

            // step 4
            __mm128i maskInv = _mm_subs_epu8(_mm_set1_epu8(255), mask) 

            //Todo : Multiply, with saturate - find correct instructions for 4..6
            //note you can use Multiply and add _mm_madd_epi16

            alpha = *maskCurrent;
            red = (srcByteTop[R] * alpha + srcByte[R] * (255 - alpha)) / 255;
            green = (srcByteTop[G] * alpha + srcByte[G] * (255 - alpha)) / 255;
            blue = (srcByteTop[B] * alpha + srcByte[B] * (255 - alpha)) / 255;
            CLAMPTOBYTE(red);
            CLAMPTOBYTE(green);
            CLAMPTOBYTE(blue);
            resultByte[R] = red;
            resultByte[G] = green;
            resultByte[B] = blue;
            //----

            // Step 7 - store result.
            //Store aligned if output is aligned on 16 byte boundrary
            _mm_store_si128(reinterpret_cast<__mm128i*>(resultByte), result)
            //Slow version if you can't guarantee alignment
            //_mm_storeu_si128(reinterpret_cast<__mm128i*>(resultByte), result)

            //Move pointers forward 4 places
            srcByte += bytepp * 4;
            srcByteTop += bytepp * 4;
            resultByte += bytepp * 4;
            maskCurrent += 4;
        }
    }

要了解哪些 AMD 处理器将运行此代码(目前它使用 SSE2 指令),请参阅 Wikipedia's List of AMD Turion microprocessors。您还可以查看 Wikipedia 上的其他处理器列表,但我的研究表明,大约 4 年前的 AMD cpu 至少都支持 SSE2。

您应该期望良好的 SSE2 实现比您当前的代码运行速度快 8-16 倍。这是因为我们消除了循环中的分支,一次处理 4 个像素(或 12 个通道)并通过使用流指令提高缓存性能。作为 SSE 的替代方案,您可以通过消除用于饱和度的 if 检查来使现有代码运行得更快。除此之外,我还需要对您的工作负载运行分析器。

当然,最好的解决方案是使用硬件支持(即在 DirectX 中编码您的问题)并在显卡上完成。

【讨论】:

  • 查看我的原始帖子的编辑以解决您的问题。简短的回答 - 如果不是古老的 CPU,是的。
  • 它只能在 Windows 或其他平台上工作吗? (如果我将定义 BYTe ofc)主要问题是:SIMD 指令是否跨平台?
  • 是的,SIMD 指令需要 CPU 支持,但它们不关心操作系统(Windows 等)。编译器还需要翻译内在函数(例如_mm_set_epi8)但我相信GCC可以做到这一点。
【解决方案2】:

您始终可以同时计算红色和蓝色的 alpha。您也可以在前面提到的 SIMD 实现中使用此技巧。

unsigned int blendPreMulAlpha(unsigned int colora, unsigned int colorb, unsigned int alpha)
{
    unsigned int rb = (colora & 0xFF00FF) + ( (alpha * (colorb & 0xFF00FF)) >> 8 );
    unsigned int g = (colora & 0x00FF00) + ( (alpha * (colorb & 0x00FF00)) >> 8 );
    return (rb & 0xFF00FF) + (g & 0x00FF00);
}


unsigned int blendAlpha(unsigned int colora, unsigned int colorb, unsigned int alpha)
{
    unsigned int rb1 = ((0x100 - alpha) * (colora & 0xFF00FF)) >> 8;
    unsigned int rb2 = (alpha * (colorb & 0xFF00FF)) >> 8;
    unsigned int g1  = ((0x100 - alpha) * (colora & 0x00FF00)) >> 8;
    unsigned int g2  = (alpha * (colorb & 0x00FF00)) >> 8;
    return ((rb1 | rb2) & 0xFF00FF) + ((g1 | g2) & 0x00FF00);
}

0

【讨论】:

  • 不错的把戏。你也应该在那里添加饱和处理(现在它溢出了)
  • 溢出是故意的,在return语句中处理。
  • 它对溢出的处理相当粗鲁:环绕而不是饱和。
  • @MSalters,可能是因为宿醉,但我没有看到溢出;或者好吧,我在 rb 和 g 中看到了故意溢出,但它们在 return 语句中被掩盖了。 (只要 int 是 32 位)。
  • @JasperBekkers,你真的尝试过你的例子吗?使用 alpha=0xff(不透明),结果为 0xff80(红色完全消失,其他颜色也错误)。
【解决方案3】:

对于想要除以 255 的人,我找到了一个完美的公式:

pt->r = (r+1 + (r >> 8)) >> 8; // fast way to divide by 255

【讨论】:

  • 这可以扩展为两个 16 位字:((r+0x10001+((r>>8)&0xFF00FF))>>8) & 0xFF00FF,这允许在 ARGB 中复用 xRxB 和 AxGx 操作,类似在 RGBA 和其他变体中
  • (x+1+((x+1)&gt;&gt;8))&gt;&gt;8 // integer div 255 for [0..65790) -- 稍微好一点
  • ((x+1)*257)&gt;&gt;16 // integer div 255 for [0..65790) -- 在某些平台上可能更快的替代公式 -- 有趣的注释:Division via Multiplication
  • @nobar:使用乘法逆进行除法的标准编译器技巧也值得考虑:n/255 compiles to = asm that does (n*0x8081) &gt;&gt; 23。这也适用于所有 16 位 n。 (我刚刚注意到您的上限高于 65536)。对于 x86 SSE2,这是一个 _mm_mulhi_epu16 和一个 _mm_srli_epu16(mul, 23-16)x+1 * 257 是一个 paddw 和一个 pmulhuw,所以实际上更好(因为 mul 和 shift 可能会竞争同一个端口)。
【解决方案4】:

这里有一些提示。

考虑使用Porter and Duff 所述的预乘前景图像。除了可能更快之外,您还可以避免很多潜在的色边效应。

合成方程从

r =  kA + (1-k)B

...到...

r =  A + (1-k)B

或者,您可以修改标准方程以删除一个乘法。

r =  kA + (1-k)B
==  kA + B - kB
== k(A-B) + B

我可能是错的,但我认为你也不应该需要夹紧......

【讨论】:

    【解决方案5】:

    我无法发表评论,因为我没有足够的声誉,但我想说 Jasper 的版本不会溢出有效输入。 屏蔽乘法结果是必要的,因为否则红色+蓝色乘法会在绿色通道中留下位(如果您分别将红色和蓝色相乘,这也是正确的,您仍然需要屏蔽蓝色通道中的位)和绿色乘法将在蓝色通道中留下位。 如果您将组件分开,这些位会在右移时丢失,就像 alpha 混合的情况一样。 所以它们不会上溢或下溢。它们只是无用的部分,需要被屏蔽才能达到预期的效果。

    也就是说,Jasper 的版本不正确。它应该是 0xFF-alpha (255-alpha),而不是 0x100-alpha (256-alpha)。这可能不会产生可见的错误。

    我发现 Jasper 代码的改编版比我的旧 alpha 混合代码更快,这已经很不错了,目前正在我的软件渲染器项目中使用它。我使用 32 位 ARGB 像素:

    Pixel AlphaBlendPixels(Pixel p1, Pixel p2)
    {
        static const int AMASK = 0xFF000000;
        static const int RBMASK = 0x00FF00FF;
        static const int GMASK = 0x0000FF00;
        static const int AGMASK = AMASK | GMASK;
        static const int ONEALPHA = 0x01000000;
        unsigned int a = (p2 & AMASK) >> 24;
        unsigned int na = 255 - a;
        unsigned int rb = ((na * (p1 & RBMASK)) + (a * (p2 & RBMASK))) >> 8;
        unsigned int ag = (na * ((p1 & AGMASK) >> 8)) + (a * (ONEALPHA | ((p2 & GMASK) >> 8)));
        return ((rb & RBMASK) | (ag & AGMASK));
    }
    

    【讨论】:

    • 这正是我想要的。您是否确定 na = 255 - a 而不是 256 的精度,还是在这种情况下无法解决?
    • 很抱歉回复晚了,直到今晚为止,多年来一直没有对 SO 做出任何贡献,并且一直在处理旧通知。由于 c/256 并不总是等于 c/255 但 256 - a 比 255-a 更不准确,因此这两种方式都有不准确之处。通过添加丢失的最高位进行四舍五入可以减少不准确性,但也不是完全准确。我很确定获得完美准确度的唯一方法是将每个颜色通道分别除以 255,这很昂贵。 Jasper 的代码很快饱和,而我的代码趋于黑色。
    【解决方案6】:

    没有准确回答这个问题,但是......

    一件事是做的快,另一件事是做对。 Alpha 合成是一种危险的野兽,它看起来直截了当且直观,但几十年来常见错误已经普遍存在,而没有人(几乎)注意到它!

    最著名和最常见的错误是关于不使用premultiplied alpha。我强烈推荐这个:Alpha Blending for Leaves

    【讨论】:

    • 不需要使用预乘 alpha,只需确保从部分透明像素中移除背景颜色即可。移除背景颜色可能是转换为预乘 alpha 的过程的一部分,但也可以独立完成。
    【解决方案7】:

    您可以在两个图像中使用每个像素 4 个字节(用于内存对齐),然后使用 SSE 指令一起处理所有通道。搜索“visual studio sse 内在函数”。

    【讨论】:

      【解决方案8】:

      首先让我们为每个颜色分量使用正确的公式

      你从这个开始:

        v = ( 1-t ) * v0 + t * v1
      

      在哪里 t=插值参数 [0..1] v0=源颜色值 v1=传输颜色值 v=输出值

      重新洗牌,我们可以减少操作次数:

        v = v0 + t * (v1 - v0)
      

      您需要为每个颜色通道执行一次此计算(RGB 为 3 次)。

      对于 8 位无符号颜色分量,您需要使用正确的定点数学:

        i = i0 + t * ( ( i1 - i0 ) + 127 ) / 255
      

      在哪里 t = 插值参数 [0..255] i0= 源颜色值 [0..255] i1= 传输颜色值 [0..255] i = 输出颜色

      如果您忽略 +127,那么您的颜色将偏向较暗的一端。很多时候,人们使用 /256 或 >> 8 来提高速度。这是不正确的!如果除以 256,则永远无法达到纯白色 (255,255,255),因为 255/256 略小于 1。

      我希望这会有所帮助。

      【讨论】:

      • 那里有一些有趣的想法,但您确实为 / 255 付出了高昂的代价。您必须使用 t * ( ( v1 - v0 ) + 127 ) 计算中间的 16 位结果,然后除以。您确定您的公式真的比 ( 1-t ) * v0 + t * v1 简单吗?请记住,1-t 是预先计算的,并且 / 通常比 * 更昂贵
      • 该公式是数值正确公式的参考。它肯定会慢一些,但结果是准确的。了解正确答案的样子有助于确定优化结果中的错误是否可以接受。
      • 是的,i = i0 + t * ( ( i1 - i0 ) + 127 ) / 255 比你的公式更有效,整数将是(我认为): i = ( ( 255 - t ) * i0 + ( t * i1 ) ) / 255
      • PC 上的大多数图像都有 Gamma 刻录。因此,如果它的像素值是 127,那并不是介于白色和黑色之间的一半。它的实际亮度是.. powf( (c) / 255.f, gamma) .. 或大约 0.19 所以你所有假设像素亮度是线性的计算都是错误的。
      • 有了 Guilerme 的回答,这可以实现正确和快速的混合。谢谢!
      【解决方案9】:

      我认为硬件支持会对您有所帮助。如果可行,尝试将逻辑从软件转移到硬件

      【讨论】:

        【解决方案10】:

        我在不安全的 C# 中编写了类似的代码。您是否有任何理由不直接遍历每个像素?为什么要使用所有 BYTE* 和 GET_BYTE() 调用?这可能是速度问题的一部分。

        GET_GRAY 是什么样的?

        更重要的是,您确定您的平台不公开 Alpha 混合功能吗?你的目标是什么平台? Wiki 告诉我,以下支持它开箱即用:

        • Mac OS X
        • Windows 2000、XP、Server 2003、Windows CE、Vista 和 Windows 7
        • X Window 系统的 XRender 扩展(包括现代 Linux 系统)
        • RISC 操作系统调整
        • QNX 中微子
        • 计划 9
        • 地狱
        • AmigaOS 4.1
        • BeOS、Zeta 和 Haiku
        • 音节
        • MorphOS

        【讨论】:

        • 此 alpha blend 用于某种图像增强算法,而不是用于显示。所以我不能使用平台功能。谢谢!删除大部分 GET_BYTE() 似乎没用,可能是乘法运算和除法运算 255 的问题。
        • 即使您不显示图像,您仍然绝对可以使用平台功能。例如,在 Windows 上,您可以使用 GDI+ 或 .NET 包装器进行 alpha 混合,而无需显示它。我假设其他平台也类似。
        【解决方案11】:

        主要问题将是糟糕的循环构造,可能由于编译器未能消除 CSE 而变得更糟。将真正的公共位移到循环之外。 int red 并不常见,但应该在内部循环中。

        此外,红色、绿色和蓝色是独立的。如果您依次计算它们,则在计算绿色结果时无需将中间红色结果保存在寄存器中。这对于 x86 等寄存器有限的 CPU 尤其重要。

        bytepp 只允许有限数量的值。使其成为模板参数,然后从开关调用正确的实例化。这将产生您的函数的多个副本,但每个副本都可以更好地优化。

        如上所述,不需要夹紧。在 alphablending 中,您正在创建两个图像 a[x][y] 和 b[x][y] 的线性组合。由于 0

        在精度损失很小的情况下,您可以计算出(a[x][y]*alpha * b[x][y]*(256-alpha))&gt;&gt;8。位移通常比除法更快。

        【讨论】:

        • 现代 CPU 尽可能喜欢交错指令。这是因为独立工作(即在 G 处理时计算 R)非常适合现代 CPU 的流水线特性。请参阅英特尔优化手册:intel.com/Assets/PDF/manual/248966.pdf。 - 寄存器对您来说可能看起来有限,但 CPU 的实际寄存器比您使用“寄存器重命名”所想象的要多得多
        【解决方案12】:

        根据目标架构,您可以尝试对函数进行矢量化或并行化。

        除此之外,尝试线性化整个方法(即没有循环中的循环)并一次处理四倍字节,这将失去处理单个字节的开销,并且使编译器更容易优化代码。

        【讨论】:

          【解决方案13】:

          将其移至 GPU。

          【讨论】:

            【解决方案14】:

            我假设您希望以完全可移植的方式执行此操作,无需 GPU 的帮助,使用专有的英特尔 SIMD 库(在 AMD 处理器上可能无法高效运行)。

            将以下内容替换为 RGB 的计算

            R = TopR + (SourceR * alpha) >> 8;
            G = TopG + (SourceG * alpha) >> 8;
            B = TopB + (SourceB * alpha) >> 8; 
            

            这是一种更有效的计算方式。

            还可以在获取像素宏上使用左移指令,而不是乘以 BPP。

            【讨论】:

            • SSE 得到了程序员和芯片制造商的一致好评。
            【解决方案15】:

            当第一种颜色(colora,目标)也具有 alpha 通道(混合两种透明的 ARGB 颜色)时,此方法有效 alpha 在第二种颜色的 alpha 中(colorb,源)

            这会添加两个 alpha(0 = 透明,255 = 完全不透明) 这是 Jasper Bekkers 答案的修改版本。

            我用它来将透明像素艺术混合到透明屏幕上。

            Uint32 alphaBlend(unsigned int colora, unsigned int colorb) {
                unsigned int a2  = (colorb & 0xFF000000) >> 24;
                unsigned int alpha = a2;
                if (alpha == 0) return colora;
                if (alpha == 255) return colorb;
                unsigned int a1  = (colora & 0xFF000000) >> 24;
                unsigned int nalpha = 0x100 - alpha;
                unsigned int rb1 = (nalpha * (colora & 0xFF00FF)) >> 8;
                unsigned int rb2 = (alpha * (colorb & 0xFF00FF)) >> 8;
                unsigned int g1  = (nalpha * (colora & 0x00FF00)) >> 8;
                unsigned int g2  = (alpha * (colorb & 0x00FF00)) >> 8;
                unsigned int anew = a1 + a2;
                if (anew > 255) {anew = 255;}
                return ((rb1 + rb2) & 0xFF00FF) + ((g1 + g2) & 0x00FF00) + (anew << 24);
            }
            

            【讨论】:

              【解决方案16】:

              这是我对软件 alpha blend 的改编,它适用于 2 个无符号整数。

              我的代码有点不同,因为上面的代码基本上总是假设目标 alpha 为 255。

              使用一个不错的优化编译器,大多数计算应该在寄存器中,因为大多数变量的范围都很短。我还选择逐步改变结果

              另外...而不是“/ 255”,我选择了“>> 8”,可以根据需要进行更改。

              /*
                  alpha blend source and destination, either may have an alpha!!!!
              
                  Src  AAAAAAAA RRRRRRRR GGGGGGGG BBBBBBBB
                  Dest AAAAAAAA RRRRRRRR GGGGGGGG BBBBBBBB
              
                  res  AAAAAAAA RRRRRRRR GGGGGGGG BBBBBBBB
              
                  NOTE - α = αsrc + αdest(1.0-αsrc)  where α = 0.0 - 1.0
              
                  ALSO - DWORD is unsigned int so (F8000000 >> 24) = F8 not FFFFFFF8 as it would with int (signed)
                  */
              
                  inline DWORD raw_blend(const DWORD src, const DWORD dest)
                  {       
                      // setup and calculate α
              
                      DWORD src_a = src >> 24;       
                      DWORD src_a_neg = 255 - src_a;
                      DWORD dest_a = dest >> 24;
              
                      DWORD res = src_a + ((dest_a * src_a_neg) >> 8);
              
                      // setup and calculate R
              
                      DWORD src_r = (src >> 16) & 255;
                      DWORD dest_r = (dest >> 16) & 255;
              
                      res = (res << 8) | (((src_r * src_a) + (dest_r * src_a_neg)) >> 8);
              
                      // setup and calculate G
              
                      DWORD src_g = (src >> 8) & 255;
                      DWORD dest_g = (dest >> 8) & 255;
              
                      res = (res << 8) | (((src_g * src_a) + (dest_g * src_a_neg)) >> 8);
              
                      // setup and calculate B
              
                      DWORD src_b = src & 255;
                      DWORD dest_b = dest & 255;
              
                      return (res << 8) | (((src_b * src_a) + (dest_b * src_a_neg)) >> 8);
                  }
              

              【讨论】:

                【解决方案17】:
                ; In\   EAX = background color (ZRBG) 32bit (Z mean zero, always is zero)
                ; In\   EDX = foreground color (RBGA) 32bit
                ; Out\  EAX = new color
                ; free registers (R10, RDI, RSI, RSP, RBP)
                abg2:
                    mov r15b, dl                ; av
                    movzx ecx, dl
                    not ecx                     ; faster than 255 - dl
                    mov r14b, cl                ; rem
                
                    shr edx, 8
                    and edx, 0x00FFFFFF
                    mov r12d, edx
                    mov r13d, eax               ; RBGA ---> ZRGB
                
                    ; s: eax
                    ; d: edx
                
                    ;=============================red = ((s >> 16) * rem + (d >> 16) * av) >> 8;
                    mov edx, r12d
                    shr edx, 0x10
                    movzx eax, r14b
                    imul edx, eax
                    mov ecx, r13d
                    shr ecx, 0x10
                    movzx eax, r15b
                    imul eax, ecx
                    lea eax, [eax + edx]                    ; faster than add eax, edx
                    shr eax, 0x8
                    mov r9b, al
                    shl r9d, 8
                
                    ;=============================green = (((s >> 8) & 0x0000ff) * rem + ((d >> 8) & 0x0000ff) * av) >> 8;
                    mov eax, r12d
                    shr eax, 0x8
                    movzx edx, al
                    movzx eax, r14b
                    imul edx, eax
                    mov eax, r13d
                    shr eax, 0x8
                    movzx ecx, al
                    movzx eax, r15b
                    imul eax, ecx
                    lea eax, [eax, + edx]                   ; faster than add eax, edx
                    shr eax, 0x8
                    mov r9b, al
                    shl r9d, 8
                
                    ;=============================blue = ((s & 0x0000ff) * rem + (d & 0x0000ff) * av) >> 8;
                    movzx edx, r12b
                    movzx eax, r14b
                    imul edx, eax
                    movzx ecx, r13b
                    movzx eax, r15b
                    imul eax, ecx
                    lea eax, [eax + edx]                ; faster than add eax, edx
                    shr eax, 0x8
                    mov r9b, al
                
                
                    mov eax, r9d
                    ret
                

                【讨论】:

                  猜你喜欢
                  • 1970-01-01
                  • 2014-12-06
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 2011-05-07
                  • 2020-11-27
                  • 1970-01-01
                  • 1970-01-01
                  相关资源
                  最近更新 更多