Как быстро смешать RGBA беззнаковый байтовый цвет? - PullRequest
20 голосов
/ 09 июля 2009

Я использую c ++, я хочу сделать альфа-смешивание, используя следующий код.

#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;
        }
    }

однако я считаю, что это все еще медленно, это занимает около 40 - 60 мс, когда составляется два 600 * 600 изображения. Есть ли способ улучшить скорость до 16 мс?

Может ли кто-нибудь помочь мне ускорить этот код? Большое спасибо!

Ответы [ 16 ]

2 голосов
/ 09 июля 2009

Думаю, вам поможет аппаратная поддержка. попытаться переместить логику с программного обеспечения на аппаратное обеспечение, если это возможно

2 голосов
/ 09 июля 2009

Я сделал похожий код в небезопасном C #. Есть ли причина, по которой вы не просматриваете каждый пиксель напрямую? Зачем использовать все вызовы BYTE * и GET_BYTE ()? Это, вероятно, часть проблемы со скоростью.

Как выглядит GET_GRAY?

Что еще более важно, вы уверены, что ваша платформа не предоставляет возможности альфа-смешивания? На какую платформу вы ориентируетесь? Вики сообщает мне, что следующие поддерживают его из коробки:

  • Mac OS X
  • Windows 2000, XP, Server 2003, Windows CE, Vista и Windows 7
  • Расширение XRender для системы X Window (включая современные системы Linux)
  • RISC OS Adjust
  • QNX Neutrino
  • План 9
  • Inferno
  • AmigaOS 4.1
  • BeOS, Зета и Хайку
  • Слог
  • MorphOS
1 голос
/ 09 июля 2009

Я предполагаю, что вы хотите сделать это полностью переносимым способом, без помощи графического процессора, использования проприетарной библиотеки Intel SIMD (которая может работать не так эффективно на процессорах AMD).

Поместите на место следующий расчет для RGB

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

Это более эффективный расчет.

Также используйте инструкцию сдвига влево в макросе get pixel вместо умножения на BPP.

1 голос
/ 09 июля 2009

В зависимости от целевой архитектуры, вы можете попробовать векторизовать или распараллелить функцию.

Кроме этого, попробуйте линеаризовать весь метод (т.е. без цикла в цикле) и работать с четырьмя байтами одновременно, что потеряло бы издержки на работу с одиночными байтами и упростило бы компилятору оптимизировать код.

0 голосов
/ 13 апреля 2018
; 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
0 голосов
/ 08 апреля 2017

Вот моя адаптация программной альфа-смеси, которая хорошо работает для 2 целых чисел без знака.

Мой код немного отличается, так как код выше в основном всегда предполагает, что целевая альфа равна 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);
    }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...