Как быстро смешать RGBA беззнаковый байтовый цвет?

Версия JavaScript немного шелушащаяся. Он получает шрифты, итерации через известные шрифты и тестирование.

Самый точный способ (хотя и для использования плагина приличия) - использовать Flash . Здесь вы можете получить список шрифтов, не тестируя их индивидуально с помощью измерений.

Вам нужно будет решить, иметь ли точный список за счет отсутствия работы на некоторых устройствах (iDevices, браузеры без плагина Flash и т. д.) или неполный список с лучшей поддержкой только с помощью JavaScript .

20
задан ideasman42 12 December 2016 в 02:35
поделиться

12 ответов

Используйте SSE - начните со страницы 131.

Базовый рабочий процесс

  1. Загрузить 4 пикселя из src (16 однобайтовых чисел) RGBA RGBA RGBA RGBA (потоковая загрузка)

  2. Загрузите еще 4, которые вы хотите смешать с srcbytetop RGBx RGBx RGBx RGBx

  3. Сделайте небольшое изменение так, чтобы член A в 1 заполнил каждый слот Ie

    xxxA xxxB xxxC xxxD -> AAAA BBBB CCCC DDDD

    In В моем решении, приведенном ниже, я решил вместо этого повторно использовать существующий массив «maskcurrent», но интеграция альфа-канала в поле «A», равное 1, потребует меньше загрузок из памяти и, следовательно, будет быстрее. Swizzling в этом случае, вероятно, будет: И с маской для выбора A, B, C, D. Сдвиг вправо 8, Или с оригиналом, сдвиг вправо 16 или снова.

  4. Добавьте указанное выше к вектору, который содержит все -255 в каждом слоте.

  5. Умножьте 1 * 4 (исходный код с 255-альфа) и 2 * 3 (результат с альфа-каналом).

    Вы должны иметь возможность использовать для этого инструкцию SSE2 «умножить и отбросить нижние 8 битов».

  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), см. Список микропроцессоров AMD Turion в Википедии . Вы также можете посмотреть другие списки процессоров в Википедии, но мое исследование показывает, что процессоры AMD примерно четырехлетней давности поддерживают как минимум SSE2.

Вы должны ожидать, что хорошая реализация SSE2 будет работать примерно в 8-16 раз быстрее, чем ваша текущая код. Это связано с тем, что мы устраняем ветки в цикле, обрабатываем сразу 4 пикселя (или 12 каналов) и улучшаем производительность кэша с помощью инструкций потоковой передачи. В качестве альтернативы SSE вы, вероятно, могли бы заставить существующий код работать намного быстрее, исключив проверки if, которые вы используете для насыщения. Помимо этого, мне нужно будет запустить профилировщик для вашей рабочей нагрузки.

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

26
ответ дан 29 November 2019 в 22:28
поделиться

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

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

1
ответ дан 29 November 2019 в 22:28
поделиться

Обновление: исправлено в мае 2013 года для лучшего подхода

  1. Пользователь вводит свое имя пользователя и нажимает «забыл пароль». Я также рекомендую вариант ввода адреса электронной почты вместо имени пользователя, потому что имена пользователей иногда тоже забываются.
  2. В системе есть таблица password_change_requests со столбцами ID , ] Время и ID пользователя . Когда новый пользователь нажимает кнопку, в таблице создается запись. Столбец Время содержит время, когда пользователь нажал кнопку «Забыли пароль». ID представляет собой строку. Создается длинная случайная строка (скажем, GUID), а затем хешируется как пароль (что само по себе является отдельной темой). Этот хеш затем используется как идентификатор в таблице.
  3. Система отправляет пользователю электронное письмо, содержащее ссылку. Ссылка также содержит исходную строку идентификатора (до хеширования). Ссылка будет примерно такой: http://www.mysite.com/forgotpassword.jsp?ID=01234567890ABCDEF . Страница Forgotpassword.jsp должна иметь возможность получать параметр ID. Извините, я не знаю Java, поэтому не могу сказать более конкретно.
  4. Когда пользователь щелкает ссылку в электронном письме, он перемещается на вашу страницу. Страница извлекает из URL-адреса ID , снова хеширует его и сверяет с таблицей. Если такая запись существует и старше, скажем, 24 часов, пользователю предлагается ввести новый пароль .
  5. Пользователь вводит новый пароль, нажимает OK и все живут долго и счастливо ... до следующего раза!
1
ответ дан 29 November 2019 в 22:28
поделиться

Переместите его на графический процессор.

2
ответ дан 29 November 2019 в 22:28
поделиться

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

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

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

  • Mac OS X
  • Windows 2000, XP, Server 2003, Windows CE, Vista и Windows 7
  • Расширение XRender для системы X Window (это включает современные системы Linux)
  • RISC OS Adjust
  • QNX Neutrino
  • Plan 9
  • Inferno
  • AmigaOS 4.1
  • BeOS, Zeta и Haiku
  • Syllable
  • MorphOS
2
ответ дан 29 November 2019 в 22:28
поделиться

Точно не отвечу на вопрос, но ...

Одно дело - делать это быстро, другое - делать это правильно. Альфа-композитинг - опасный зверь, он выглядит прямолинейным и интуитивно понятным, но распространенные ошибки были широко распространены на протяжении десятилетий, и никто этого не замечал (почти)!

Самая известная и распространенная ошибка - НЕ использовать предварительно умноженную альфа . Я настоятельно рекомендую следующее: Alpha Blending for Leaves

4
ответ дан 29 November 2019 в 22:28
поделиться

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

2
ответ дан 29 November 2019 в 22:28
поделиться

Вы можете использовать 4 байта на пиксель в обоих изображениях (для выравнивания памяти), а затем использовать инструкции SSE для обработки всех каналов вместе. Искать "visual studio sse intrinsics".

3
ответ дан 29 November 2019 в 22:28
поделиться

Вы всегда можете вычислить альфу красного и синего одновременно. Вы также можете использовать этот трюк с упомянутой ранее реализацией 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 <= alpha <= 0x100

21
ответ дан 29 November 2019 в 22:28
поделиться

Основной проблемой будет плохая конструкция цикла, которая, возможно, усугубляется тем, что компилятор не может устранить CSE. Переместите реальные общие биты за пределы петель. int red не является обычным явлением, да - это должно быть внутри внутреннего цикла.

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

Для bytepp будет разрешено только ограниченное количество значений. Сделайте его параметром шаблона, а затем вызовите правильный экземпляр с переключателя. Это создаст несколько копий вашей функции, но каждую из них можно оптимизировать намного лучше.

Как уже отмечалось, зажим не требуется. В альфа-блендере вы воссоздание линейной комбинации двух изображений a [x] [y] и b [x] [y]. Поскольку 0 <= alpha <= 255, вы знаете, что каждый вывод связан с max (255 * a [x] [y], 255 * b [x] [y]). И поскольку ваш выходной диапазон совпадает с обоими входными диапазонами (0-255), это нормально.

С небольшой потерей точности вы можете вычислить (a [x] [y] * alpha * b [x] [y] * (256-альфа)) >> 8 . Битовые сдвиги часто выполняются быстрее, чем деление.

2
ответ дан 29 November 2019 в 22:28
поделиться

Вот несколько указателей.

Рассмотрите возможность использования предварительно умноженных изображений переднего плана, как описано Портером и Даффом . Помимо потенциальной скорости, вы избегаете множества потенциальных эффектов цветовой окантовки.

Уравнение композитинга изменяется с

r =  kA + (1-k)B

... на ...

r =  A + (1-k)B

В качестве альтернативы вы можете переделать стандартное уравнение, чтобы удалить одно умножить.

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

Я могу ошибаться, но я думаю, что зажим вам тоже не понадобится ...

6
ответ дан 29 November 2019 в 22:28
поделиться

Прежде всего, давайте используем правильную формулу для каждого компонента цвета

Вы начнете со следующего:

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

где t = параметр интерполяции [0..1] v0 = исходное значение цвета v1 = передать значение цвета v = выходное значение

Перестановка терминов позволяет сократить количество операций:

  v = v0 + t * (v1 - v0)

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

Для 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 немного меньше единицы.

Надеюсь, это поможет.

3
ответ дан 29 November 2019 в 22:28
поделиться
Другие вопросы по тегам:

Похожие вопросы: