Ответ 1
Распаковка неподписанных пикселей с помощью SSE2
Хорошо, используя SSE2 integer intrinsics от <emmintrin.h>
, сначала загрузите вещь в младшие 32 бита регистра:
__m128i xmm0 = _mm_cvtsi32_si128(*(const int*)&pixel);
Затем сначала распакуйте эти 8-битные значения в 16-битные значения в младших 64 битах регистра, чередуя их с 0s:
xmm0 = _mm_unpacklo_epi8(xmm0, _mm_setzero_si128());
И снова распакуйте эти 16-битные значения в 32-битные значения:
xmm0 = _mm_unpacklo_epi16(xmm0, _mm_setzero_si128());
Теперь вы должны иметь каждый пиксель как 32-битное целое число в соответствующих 4 компонентах регистра SSE.
Распаковка подписанных пикселей с помощью SSE2
Я просто прочитал, что вы хотите получить эти значения в виде 32-битных подписанных целых чисел, хотя мне интересно, какой смысл имеет подписанный пиксель в [-127,127]. Но если ваши значения пикселей могут быть отрицательными, чередование с нулями не будет работать, поскольку оно делает отрицательное 8-битное число в положительное 16-битное число (таким образом, интерпретирует ваши числа как значения без знака). Отрицательное число должно быть расширено с помощью 1
вместо 0
s, но, к сожалению, это должно решаться динамически на основе компонента по компонентам, при котором SSE не так хорош.
Что вы можете сделать, это сравнить значения отрицательности и использовать полученную маску (которая, к счастью, использует 1...1
для true и 0...0
для false) как interleavand вместо нулевого регистра:
xmm0 = _mm_unpacklo_epi8(xmm0, _mm_cmplt_epi8(xmm0, _mm_setzero_si128()));
xmm0 = _mm_unpacklo_epi16(xmm0, _mm_cmplt_epi16(xmm0, _mm_setzero_si128()));
Это будет правильно расширять отрицательные числа с помощью 1
и положительных значений с помощью 0
s. Но, конечно, эти дополнительные накладные расходы (в виде, вероятно, 2-4 дополнительных инструкций SSE) необходимы только в том случае, если ваши начальные 8-битные значения пикселей могут быть отрицательными, и я все еще сомневаюсь. Но если это действительно так, вам стоит рассмотреть signed char
над char
, так как последний имеет определенную приложением подпись (таким же образом вы должны использовать unsigned char
, если это обычные значения unsigned [0,255] пикселей).
Альтернативная распаковка SSE2 с использованием сдвигов
Хотя, как выяснилось, вам не требуется преобразование с 8-битным и 32-битным символом с 8-битным, но для полноты гарольда была еще одна очень хорошая идея для расширения знака на основе SSE2, вместо использования вышеперечисленного упомянутый сравнение на основе версия. Сначала мы распаковываем 8-битные значения в верхний байт 32-битных значений вместо младшего байта. Поскольку мы не заботимся о нижних частях, мы снова используем 8-битные значения, что освобождает нас от необходимости добавления нулевого регистра и дополнительного перемещения:
xmm0 = _mm_unpacklo_epi8(xmm0, xmm0);
xmm0 = _mm_unpacklo_epi16(xmm0, xmm0);
Теперь нам просто нужно выполнить и арифметическое правое смещение верхнего байта в младший байт, что делает правильное расширение знака для отрицательных значений:
xmm0 = _mm_srai_epi32(xmm0, 24);
Это должно быть больше количества команд и регистрации, чем моя выше версия SSE2.
И поскольку он должен быть равным в подсчете команд для одного пикселя (хотя еще одна инструкция при амортизации по многим пикселям) и более эффективна в регистре (из-за отсутствия лишнего регистра нуля) по сравнению с вышеуказанным нулевым расширением, может даже использоваться для преобразования без знака в подпись, если регистры встречаются редко, но затем с логическим сдвигом (_mm_srli_epi32
) вместо арифметического сдвига.
Улучшена распаковка с помощью SSE4
Благодаря замечанию Harold, есть даже лучший вариант для первого преобразования от 8 до 32. Если у вас есть поддержка SSE4 (точнее, SSE4.1), в которой есть инструкции для полного преобразования из 4 упакованных 8-битных значений в младших 32 битах регистра в 4 32-битных значения во всем регистре, как для подписанные и неподписанные 8-битные значения:
xmm0 = _mm_cvtepu8_epi32(xmm0); //or _mm_cvtepi8_epi32 for signed 8-bit values
Упаковочные пиксели с SSE2
Что касается последующего изменения этого преобразования, сначала мы собираем подписанные 32-разрядные целые числа в 16-битные целочисленные числа и насыщаем:
xmm0 = _mm_packs_epi32(xmm0, xmm0);
Затем мы собираем эти 16-битные значения в 8-битные значения без знака с использованием насыщения:
xmm0 = _mm_packus_epi16(xmm0, xmm0);
Наконец, мы можем взять наш пиксель из нижних 32-бит регистра:
*(int*)&pixel = _mm_cvtsi128_si32(xmm0);
Из-за насыщенности весь этот процесс автоматически сопоставляет любые отрицательные значения с 0
и любыми значениями, превышающими 255
до 255
, которые обычно используются при работе с цветными пикселями.
Если вам понадобится усечение вместо насыщения при упаковывании 32-битных значений обратно в unsigned char
s, вам нужно будет сделать это самостоятельно, поскольку SSE предоставляет только насыщающие инструкции по упаковке. Но это может быть достигнуто путем простого:
xmm0 = _mm_and_si128(xmm0, _mm_set1_epi32(0xFF));
непосредственно перед вышеуказанной процедурой упаковки. Это должно составлять всего две дополнительные инструкции SSE или только 1 дополнительная инструкция при амортизации по многим пикселям.