Я реализую функцию быстрого преобразования x888 -> 565 пикселей в pixmanв соответствии с описанным алгоритмом Intel [pdf] . Их код преобразует x888 -> 555, а я хочу преобразовать в 565. К сожалению, преобразование в 565 означает, что установлен старший бит, а это означает, что я не могу использовать инструкции пакета насыщения со знаком. Инструкция unsigned pack, packusdw, не добавлялась до SSE4.1. Я хотел бы реализовать его функциональность с помощью SSE2 или найти другой способ сделать это.
Эта функция принимает два регистра XMM, содержащих 4 32-битных пикселя каждый, и выводит один регистр XMM, содержащий 8 преобразованных пикселей RGB565.
static force_inline __m128i
pack_565_2packedx128_128 (__m128i lo, __m128i hi)
{
__m128i rb0 = _mm_and_si128 (lo, mask_565_rb);
__m128i rb1 = _mm_and_si128 (hi, mask_565_rb);
__m128i t0 = _mm_madd_epi16 (rb0, mask_565_pack_multiplier);
__m128i t1 = _mm_madd_epi16 (rb1, mask_565_pack_multiplier);
__m128i g0 = _mm_and_si128 (lo, mask_green);
__m128i g1 = _mm_and_si128 (hi, mask_green);
t0 = _mm_or_si128 (t0, g0);
t1 = _mm_or_si128 (t1, g1);
t0 = _mm_srli_epi32 (t0, 5);
t1 = _mm_srli_epi32 (t1, 5);
/* XXX: maybe there's a way to do this relatively efficiently with SSE2? */
return _mm_packus_epi32 (t0, t1);
}
Идеи, которые я придумал:
Вычитание 0x8000, _mm_packs_epi32, повторное добавление 0x8000 к каждому 565 пикселю. Я пробовал это, но я не могу заставить это работать.
t0 = _mm_sub_epi16 (t0, mask_8000);
t1 = _mm_sub_epi16 (t1, mask_8000);
t0 = _mm_packs_epi32 (t0, t1);
вернуть _mm_add_epi16 (t0, mask_8000);
Перемешивать данные вместо упаковки. Работает для MMX, но, поскольку 16-битное перемешивание SSE работает только со старшими или младшими 64-битными битами, это может привести к путанице.
Сохраните старшие биты, обнулите их, выполните пакет, а затем восстановите их. Кажется довольно грязным.
Есть ли другой (надеюсь, более эффективный) способ сделать это?