I have I problem with multiplying two registers (or just register by float constant). One register is __m128i type and contains one channel of RGBA pixel color from 16 pixels (the array with 16 pixels is sending as a parameter to CPP dll). I want to multiply this register by constant to get grayscale value for this channel and do this operation also for other channels stored in __m128i registers.
I think that a good idea to use SIMD for convert image to grayscale is to use this algorithm.
fY(R, G, B) = R x 0.29891 + G x 0.58661 + B x 0.11448
I have this following code and now it's only decomposing the image to channels and pack it together to return as an src vector. Now I need to make it for grayscale :)
The src variable is a pointer to unsigned char array.
__m128i vecSrc = _mm_loadu_si128((__m128i*) &src[srcIndex]);
__m128i maskR = _mm_setr_epi16(1, 0, 0, 0, 1, 0, 0, 0);
__m128i maskG = _mm_setr_epi16(0, 1, 0, 0, 0, 1, 0, 0);
__m128i maskB = _mm_setr_epi16(0, 0, 1, 0, 0, 0, 1, 0);
__m128i maskA = _mm_setr_epi16(0, 0, 0, 1, 0, 0, 0, 1);
// Creating factors.
const __m128i factorR = _mm_set1_epi16((short)(0.29891 * 0x10000)); //8 coefficients - R scale factor.
const __m128i factorG = _mm_set1_epi16((short)(0.58661 * 0x10000)); //8 coefficients - G scale factor.
const __m128i factorB = _mm_set1_epi16((short)(0.11448 * 0x10000)); //8 coefficients - B scale factor.
__m128i zero = _mm_setzero_si128();
// Shifting higher part of src register to lower.
__m128i vectSrcLowInHighPart = _mm_cvtepu8_epi16(vecSrc);
__m128i vectSrcHighInHighPart = _mm_unpackhi_epi8(vecSrc, zero);
// Multiply high parts of 16 x uint8 vectors by channels masks and save lower half. Getting each channels separatly (in two parts H and L)
__m128i vecR_L = _mm_mullo_epi16(vectSrcLowInHighPart, maskR);
__m128i vecG_L = _mm_mullo_epi16(vectSrcLowInHighPart, maskG);
__m128i vecB_L = _mm_mullo_epi16(vectSrcLowInHighPart, maskB);
__m128i vecA_L = _mm_mullo_epi16(vectSrcLowInHighPart, maskA);
// Multiply lower parts of 16 x uint8 vectors by channels masks and save lower half.
__m128i vecR_H = _mm_mullo_epi16(vectSrcHighInHighPart, maskR);
__m128i vecG_H = _mm_mullo_epi16(vectSrcHighInHighPart, maskG);
__m128i vecB_H = _mm_mullo_epi16(vectSrcHighInHighPart, maskB);
__m128i vecA_H = _mm_mullo_epi16(vectSrcHighInHighPart, maskA);
// Lower and high masks using to packing.
__m128i maskLo = _mm_set_epi8(0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 14, 12, 10, 8, 6, 4, 2, 0);
__m128i maskHi = _mm_set_epi8(14, 12, 10, 8, 6, 4, 2, 0, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80);
// Packed the High and Lowe part of register into one 16 x 8bit registers of each channels.
__m128i R = _mm_or_si128(_mm_shuffle_epi8(vecR_L, maskLo), _mm_shuffle_epi8(vecR_H, maskHi));
__m128i G = _mm_or_si128(_mm_shuffle_epi8(vecG_L, maskLo), _mm_shuffle_epi8(vecG_H, maskHi));
__m128i B = _mm_or_si128(_mm_shuffle_epi8(vecB_L, maskLo), _mm_shuffle_epi8(vecB_H, maskHi));
__m128i A = _mm_or_si128(_mm_shuffle_epi8(vecA_L, maskLo), _mm_shuffle_epi8(vecA_H, maskHi));
// Added all sub vectors to get in result one 128-bit vector with all edited channels.
__m128i resultVect = _mm_add_epi8(_mm_add_epi8(R, G), _mm_add_epi8(B, A));
// Put result vector into array to return as src pointer.
_mm_storel_epi64((__m128i*)&src[srcIndex], resultVect);
Thanks for help for you! It's my first program with SIMD (SSE) instructions.
__mm_cvtepi32_ps()might be useful. - Shawn__mm_cvtepi32_ps()to make your idea? - CoffeRightuint8again? Or do you actually wantfloat?) 3) From what you've written you seem to be ok with 16bit fixed-point multiplication (which really should be sufficient). Would you be fine with 8bit fixed-point multiplication as well? 4) What SSE version can you use (SSSE3 would addpmaddubsw, which could help a lot) - chtz