3

I'm trying to optimize pixel depth conversion from 565 to 888 using SSE2 with the basic formula:

col8 = col5 << 3 | col5 >> 2
col8 = col6 << 2 | col6 >> 4

I take two 2x565 128-bit vectors and I'm outputing 3x888 128-bit vectors.

After some masking, shifting and OR'ing I came to the point when I have two vectors with ((blue << 8) | red)* 8-bit colors stored in 16-bit words and a similar vectors with zero-green values. Now I need to combine them into 888 output.

    BR: BR7-BR6-...-BR1-BR0
    0G: 0G7-0G7-...-0G1-0G0
                 |
                 v
  OUT1: R5-BGR4-...-BGR1-BGR0

In SSSE3 there is a _mm_shuffle_epi8() which solves my needs but would like to restrict myself to SSE2 because of the hardware range I need to support.

  • It is little endian
kyku
  • 5,892
  • 4
  • 43
  • 51
  • That second diagram isn't very clear to me, what is actually going on there? – harold Feb 15 '15 at 12:34
  • @harold, it was meant to depict packing of those two partial vectors into final result with RGB values (BGR because of little-endianness). – kyku Feb 15 '15 at 17:20

1 Answers1

5

You can refer to Google's libyuv project, which has SSE2 conversions:

https://chromium.googlesource.com/libyuv/libyuv/+/master/source/row_win.cc

 // pmul method to replicate bits.
// Math to replicate bits:
// (v << 8) | (v << 3)
// v * 256 + v * 8
// v * (256 + 8)
// G shift of 5 is incorporated, so shift is 5 + 8 and 5 + 3
// 20 instructions.
__declspec(naked)
void RGB565ToARGBRow_SSE2(const uint8* src_rgb565, uint8* dst_argb,
                          int width) {
  __asm {
    mov       eax, 0x01080108  // generate multiplier to repeat 5 bits
    movd      xmm5, eax
    pshufd    xmm5, xmm5, 0
    mov       eax, 0x20802080  // multiplier shift by 5 and then repeat 6 bits
    movd      xmm6, eax
    pshufd    xmm6, xmm6, 0
    pcmpeqb   xmm3, xmm3       // generate mask 0xf800f800 for Red
    psllw     xmm3, 11
    pcmpeqb   xmm4, xmm4       // generate mask 0x07e007e0 for Green
    psllw     xmm4, 10
    psrlw     xmm4, 5
    pcmpeqb   xmm7, xmm7       // generate mask 0xff00ff00 for Alpha
    psllw     xmm7, 8
    mov       eax, [esp + 4]   // src_rgb565
    mov       edx, [esp + 8]   // dst_argb
    mov       ecx, [esp + 12]  // width
    sub       edx, eax
    sub       edx, eax
 convertloop:
    movdqu    xmm0, [eax]   // fetch 8 pixels of bgr565
    movdqa    xmm1, xmm0
    movdqa    xmm2, xmm0
    pand      xmm1, xmm3    // R in upper 5 bits
    psllw     xmm2, 11      // B in upper 5 bits
    pmulhuw   xmm1, xmm5    // * (256 + 8)
    pmulhuw   xmm2, xmm5    // * (256 + 8)
    psllw     xmm1, 8
    por       xmm1, xmm2    // RB
    pand      xmm0, xmm4    // G in middle 6 bits
    pmulhuw   xmm0, xmm6    // << 5 * (256 + 4)
    por       xmm0, xmm7    // AG
    movdqa    xmm2, xmm1
    punpcklbw xmm1, xmm0
    punpckhbw xmm2, xmm0
    movdqu    [eax * 2 + edx], xmm1  // store 4 pixels of ARGB
    movdqu    [eax * 2 + edx + 16], xmm2  // store next 4 pixels of ARGB
    lea       eax, [eax + 16]
    sub       ecx, 8
    jg        convertloop
    ret
  }
}
huisinro
  • 1,083
  • 10
  • 15
  • 1
    I assume there's a version of that with intrinsics somewhere. Although that looks easy enough to turn into stand-alone asm that can be ported to 64bit, or for use with other compilers. It's easy enough to read, and has comments, so it has my upvote. – Peter Cordes Jun 17 '16 at 22:06
  • We are converting this to 64-bi asm, when ready, will post here too – huisinro Jun 18 '16 at 15:42
  • Note the input for this function must be at least 8 pixels, so it is at least 16 bytes array for 565. The array must be 16-byte aligned – huisinro Jun 19 '16 at 14:44