I have this function for RGB blend. What I'm trying to do is put red and blue together to lessen the operations.
Here' the original code :
#define REDMASK (0xff0000)
#define GREENMASK (0x00ff00)
#define BLUEMASK (0x0000ff)
typedef unsigned int Pixel;
inline Pixel AddBlend( Pixel a_Color1, Pixel a_Color2 )
{
const unsigned int r = (a_Color1 & REDMASK) + (a_Color2 & REDMASK);
const unsigned int g = (a_Color1 & GREENMASK) + (a_Color2 & GREENMASK);
const unsigned int b = (a_Color1 & BLUEMASK) + (a_Color2 & BLUEMASK);
const unsigned r1 = (r & REDMASK) | (REDMASK * (r >> 24));
const unsigned g1 = (g & GREENMASK) | (GREENMASK * (g >> 16));
const unsigned b1 = (b & BLUEMASK) | (BLUEMASK * (b >> 8));
return (r1 + g1 + b1);
}`
And here's what I got so far. My problem is right now is that the colours are not blending correctly. What am I doing wrong here?
typedef unsigned int Pixel;
inline Pixel AddBlend( Pixel a_Color1, Pixel a_Color2 ){
const unsigned int rb = ( ( a_Color1 & 0xff00ff ) + ( a_Color2 & 0xff00ff ) );
const unsigned int g = ( a_Color1 & GREENMASK ) + ( a_Color2 & GREENMASK );
const unsigned rb1 = ( rb & 0xff00ff ) | ( 0xff00ff * ( rb >> 8 ));
const unsigned g1 = (g & GREENMASK) | (GREENMASK * (g >> 16));
return (rb1 + g1);
}
The (REDMASK * (r >> 24)) type part in the original code handles clamping values that overflow. This works with one color part, but not two. You'll need to split that into two parts, one to handle the red overflow and one for the blue. Handling the overflow for red can be done as in the original, but the blue overflow needs a little adjustment to ignore any of the red contribution.
BLUE_MASK * ((rb & 0x100) >> 8)
This results in
const unsigned rb1 = (rb & 0xff00ff) | (REDMASK * (r >> 24)) | (BLUE_MASK * ((rb & 0x100) >> 8));
Combining two colors like this works because there is a gap between red and blue that the overflow can occupy (the green bits). If you tried this with red/green or green/blue the overflow for the part stored in the lower byte would collide with the value for the part stored in the higher byte.
Related
I am trying to implement a function that blends two colors encoded with RGB565 using Alpha blending
Crgb565 = (1-a)Argb565 + a*Brgb565
Where a is the alpha parameter, and the alpha blending value of 0.0-1.0 is mapped to an unsigned char value on the range 0-32.
we can choose to use a five bit representation for a instead, thus restricting it to the range of 0-31 (effectively mapping to an alpha blending value of 0.0-0.96875).
Following code I am trying to implement, can you please suggest better way wrt less temp variable , memory optimization (number of multiplications and required memory accesses ),Is my logic for alpha bending is correct? I am not getting correct result/expected output, Seems like I am missing something, please review the code, Every suggest is appreciated, have some doubt based on alpha parameter. I have put my doubts in code comment section. Is there any way to shortening the alpha blending equations(division operation)?
=====================================================
unsigned short blend_rgb565(unsigned short A, unsigned short B, unsigned char Alpha)
{
unsigned short res = 0;
// Alpha converted from [0..255] to [0..31] (8 bit to 5 bit)
/* I want the alpha parameter (0-32), do i need to add something in Alpha before right shift?? */
Alpha = Alpha >> 3;
// Split Image A into R, G, B components
/*Do I need to take it as unsigned short or uint8_t also work fine ??*/
unsigned short A_r = A >> 11;
unsigned short A_g = (A >> 5) & ((1u << 6) - 1); // ((1u << 6) - 1) --> 00000000 00111111
unsigned short A_b = A & ((1u << 5) - 1); // ((1u << 5) - 1) --> 00000000 00011111
// Split Image B into R, G, B components
unsigned short B_r = B >> 11;
unsigned short B_g = (B >> 5) & ((1u << 6) - 1);
unsigned short B_b = B & ((1u << 5) - 1);
// Alpha blend components
/*Do I need to use 255(8 bit) instead of 32(5 bit), Why we are dividing by it , I have taken the ref from internet , but need little bit more clarification ??*/
unsigned short uiC_r = (A_r * Alpha + B_r * (32 - Alpha)) / 32;
unsigned short uiC_g = (A_g * Alpha + B_g * (32 - Alpha)) / 32;
unsigned short uiC_b = (A_b * Alpha + B_b * (32 - Alpha)) / 32;
// Pack result
res= (unsigned short) ((uiC_r << 11) | (uiC_g << 5) | uiC_b);
return res;
}
=====================
EDIT:
Adding method 2 ,is this approach is correct ?
Method 2:
// rrrrrggggggbbbbb
#define RB_MASK 63519 // 0b1111100000011111 --> hex :F81F
#define G_MASK 2016 // 0b0000011111100000 --> hex :07E0
#define RB_MUL_MASK 2032608 // 0b111110000001111100000 --> hex :1F03E0
#define G_MUL_MASK 64512 // 0b000001111110000000000 --> hex :FC00
unsigned short blend_rgb565(unsigned short A,unsigned short B,unsigned char Alpha) {
// Alpha converted from [0..255] to [0..31]
Alpha = Alpha >> 3
uint8_t beta = 32 - Alpha;
// so (0..32)*Alpha + (0..32)*beta always in 0..32
return (unsigned short)
(
(
( ( Alpha * (uint32_t)( A & RB_MASK ) + beta * (uint32_t)( B & RB_MASK )) & RB_MUL_MASK )
|
( ( Alpha * ( A & G_MASK ) + beta * ( B & G_MASK )) & G_MUL_MASK )
)
>> 5 // removing the alpha component 5 bit
);
}
It's possible to reduce the multiplies from 6 to 2 if you space out the RGB values into 2 32-bit integers before multiplying:
unsigned short blend_rgb565(unsigned short A, unsigned short B, unsigned char Alpha)
{
unsigned short res = 0;
// Alpha converted from [0..255] to [0..31] (8 bit to 5 bit)
Alpha = Alpha >> 3;
// Alpha = (Alpha + (Alpha >> 5)) >> 3; // map from 0-255 to 0-32 (if Alpha is unsigned short or larger)
// Space out A and B from RRRRRGGGGGGBBBBB to 00000RRRRR00000GGGGGG00000BBBBB
// 31 = 11111 binary
// 63 = 111111 binary
unsigned int A32 = (unsigned int)A;
unsigned int A_spaced = A32 & 31; // B
A_spaced |= (A32 & (63 << 5)) << 5; // G
A_spaced |= (A32 & (31 << 11)) << 11; // R
unsigned int B32 = (unsigned int)B;
unsigned int B_spaced = B32 & 31; // B
B_spaced |= (B32 & (63 << 5)) << 5; // G
B_spaced |= (B32 & (31 << 11)) << 11; // R
// multiply and add the alpha to give a result RRRRRrrrrrGGGGGGgggggBBBBBbbbbb,
// where RGB are the most significant bits we want to keep
unsigned int C_spaced = (A_spaced * Alpha) + (B_spaced * (32 - Alpha));
// remap back to RRRRRGGGGGBBBBB
res = (unsigned short)(((C_spaced >> 5) & 31) + ((C_spaced >> 10) & (63 << 5)) + ((C_spaced >> 16) & (31 << 11)));
return res;
}
You need to profile this to see if it is faster, it assumes that multiplications you save are slower than the extra bit-manipulations you replace them with.
can you please suggest better way wrt less temp variable
There is no advantage to remove temporary variables from the implementation. When you compile with optimizations turned on (e.g. -O2 or /O2) those temp variables will get optimized away.
Two adjustments I would make to your code:
Use uint16_t instead of unsigned short. For most platforms, it won't matter since sizeof(uint16_t)==sizeof(unsigned short), but it helps to be definitive.
No point in converting alpha from an 8-bit value to a 5-bit value. You'll get better accuracy with blending if you let alpha have the full range
Some of your bit-shifting looks weird. It might work. But I use a simpler approach.
Here's an adjustment to your implementation:
#include <stdint.h>
#define MAKE_RGB565(r, g, b) ((r << 11) | (g << 5) | (b))
uint16_t blend_rgb565(uint16_t a, uint16_t b, uint8_t Alpha)
{
const uint8_t invAlpha = 255 - Alpha;
uint16_t A_r = a >> 11;
uint16_t A_g = (a >> 5) & 0x3f;
uint16_t A_b = a & 0x1f;
uint16_t B_r = b >> 11;
uint16_t B_g = (b >> 5) & 0x3f;
uint16_t B_b = b & 0x1f;
uint32_t C_r = (A_r * invAlpha + B_r * Alpha) / 255;
uint32_t C_g = (A_g * invAlpha + B_g * Alpha) / 255;
uint32_t C_b = (A_b * invAlpha + B_b * Alpha) / 255;
return MAKE_RGB565(C_r, C_g, C_b);
}
But the bigger issue is that this function works on exactly one one pair of pixel colors. If you are invoking this function across an entire image or pair of images, the overhead of using the function call is going to be a major performance issue - even with compiler optimizations and inlining. So if you are calling this function row x col times, you should probably manually inline the code into your loop that is enumerating over every pixel on an image (or pair of images).
In the same vein as #samgak's answer, you can implement more efficiently on a 64 bits architecture by "post-masking", as follows:
rrrrrggggggbbbbb
Replicate to a long long (by shifting or mapping the long long to four shorts)
---------------- rrrrrggggggbbbbb rrrrrggggggbbbbb rrrrrggggggbbbbb
Mask out the useless bits
---------------- rrrrr----------- -----gggggg----- -----------bbbbb
Multiply by α
-----------rrrrr rrrrr----------- ggggggggggg----- ------bbbbbbbbbb
Mask out the low order bits
-----------rrrrr ---------------- gggggg---------- ------bbbbb-----
Pack
rrrrrgggggbbbbb
Another saving is possible by rewriting
(1 - α) X + α Y
as
X + α (Y - X)
(or X - α (X - Y) to avoid negatives). This spares a multiply (at the expense of a comparison).
Update:
The "saving" above cannot work because the negatives should be handled component-wise.
I want to compute the RGB values from the luminance.
The data that I know are :
the new luminance (the value that I want to apply)
the old luminance
the old RGB values.
We can compute the luminance from the RGB values like this :
uint8_t luminance = R * 0.21 + G * 0.71 + B * 0.07;
My code is :
// We create a function to set the luminance of a pixel
void jpegImage::setLuminance(uint8_t newLuminance, unsigned int x, unsigned int y) {
// If the X or Y value is out of range, we throw an error
if(x >= width) {
throw std::runtime_error("Error : in jpegImage::setLuminance : The X value is out of range");
}
else if(y >= height) {
throw std::runtime_error("Error : in jpegImage::setLuminance : The Y value is out of range");
}
// If the image is monochrome
if(pixelSize == 1) {
// We set the pixel value to the luminance
pixels[y][x] = newLuminance;
}
// Else if the image is colored, we throw an error
else if(pixelSize == 3) {
// I don't know how to proceed
// My image is stored in a std::vector<std::vector<uint8_t>> pixels;
// This is a list that contain the lines of the image
// Each line contains the RGB values of the following pixels
// For example an image with 2 columns and 3 lines
// [[R, G, B, R, G, B], [R, G, B, R, G, B], [R, G, B, R, G, B]]
// For example, the R value with x = 23, y = 12 is:
// pixels[12][23 * pixelSize];
// For example, the B value with x = 23, y = 12 is:
// pixels[12][23 * pixelSize + 2];
// (If the image is colored, the pixelSize will be 3 (R, G and B)
// (If the image is monochrome the pixelSIze will be 1 (just the luminance value)
}
}
How can I proceed ?
Thanks !
You don't need the old luminance if you have the original RGB.
Referencing https://www.fourcc.org/fccyvrgb.php for YUV to RGB conversion.
Compute U and V from original RGB:
```
V = (0.439 * R) - (0.368 * G) - (0.071 * B) + 128
U = -(0.148 * R) - (0.291 * G) + (0.439 * B) + 128
```
Y is the new luminance normalized to a value between 0 and 255
Then just convert back to RGB:
B = 1.164(Y - 16) + 2.018(U - 128)
G = 1.164(Y - 16) - 0.813(V - 128) - 0.391(U - 128)
R = 1.164(Y - 16) + 1.596(V - 128)
Make sure you clamp your computed values of each equation to be in range of 0..255. Some of these formulas can convert a YUV or RGB value to something less than 0 or higher than 255.
There's also multiple formula for converting between YUV and RGB. (Different constants). I noticed the page listed above has a different computation for Y than you cited. They are all relatively close with different precisions and adjustments. For just changing the brightness of a pixel, almost any formula will do.
Updated
I originally deleted this answer after the OP suggested it wasn't working for him. I was too busy for the last few days to investigate, but I wrote some sample code to confirm my hypothesis. At the bottom of this answer is a snippet of GDI+ based code increases the luminance of an image by a variable amount. Along with the code is an image that I tested this out on and two conversions. One at 130% brightness. Another at 170% brightness.
Here's a sample conversion
Original Image
Updated Image (at 130% Y)
Updated Image (at 170% Y)
Source:
#define CLAMP(val) {val = (val > 255) ? 255 : ((val < 0) ? 0 : val);}
void Brighten(Gdiplus::BitmapData& dataIn, Gdiplus::BitmapData& dataOut, const double YMultiplier=1.3)
{
if ( ((dataIn.PixelFormat != PixelFormat24bppRGB) && (dataIn.PixelFormat != PixelFormat32bppARGB)) ||
((dataOut.PixelFormat != PixelFormat24bppRGB) && (dataOut.PixelFormat != PixelFormat32bppARGB)))
{
return;
}
if ((dataIn.Width != dataOut.Width) || (dataIn.Height != dataOut.Height))
{
// images sizes aren't the same
return;
}
const size_t incrementIn = dataIn.PixelFormat == PixelFormat24bppRGB ? 3 : 4;
const size_t incrementOut = dataOut.PixelFormat == PixelFormat24bppRGB ? 3 : 4;
const size_t width = dataIn.Width;
const size_t height = dataIn.Height;
for (size_t y = 0; y < height; y++)
{
auto ptrRowIn = (BYTE*)(dataIn.Scan0) + (y * dataIn.Stride);
auto ptrRowOut = (BYTE*)(dataOut.Scan0) + (y * dataOut.Stride);
for (size_t x = 0; x < width; x++)
{
uint8_t B = ptrRowIn[0];
uint8_t G = ptrRowIn[1];
uint8_t R = ptrRowIn[2];
uint8_t A = (incrementIn == 3) ? 0xFF : ptrRowIn[3];
auto Y = (0.257 * R) + (0.504 * G) + (0.098 * B) + 16;
auto V = (0.439 * R) - (0.368 * G) - (0.071 * B) + 128;
auto U = -(0.148 * R) - (0.291 * G) + (0.439 * B) + 128;
Y *= YMultiplier;
auto newB = 1.164*(Y - 16) + 2.018*(U - 128);
auto newG = 1.164*(Y - 16) - 0.813*(V - 128) - 0.391*(U - 128);
auto newR = 1.164*(Y - 16) + 1.596*(V - 128);
CLAMP(newR);
CLAMP(newG);
CLAMP(newB);
ptrRowOut[0] = newB;
ptrRowOut[1] = newG;
ptrRowOut[2] = newR;
if (incrementOut == 4)
{
ptrRowOut[3] = A; // keep original alpha
}
ptrRowIn += incrementIn;
ptrRowOut += incrementOut;
}
}
}
When you and some data with a mask you get some result which is of the same size as the data/mask.
What I want to do, is to take the masked bits in the result (where there was 1 in the mask) and shift them to the right so they are next to each other and I can perform a CTZ (Count Trailing Zeroes) on them.
I didn't know how to name such a procedure so Google has failed me. The operation should preferably not be a loop solution, this has to be as fast operation as possible.
And here is an incredible image made in MS Paint.
This operation is known as compress right. It is implemented as part of BMI2 as the PEXT instruction, in Intel processors as of Haswell.
Unfortunately, without hardware support is it a quite annoying operation. Of course there is an obvious solution, just moving the bits one by one in a loop, here is the one given by Hackers Delight:
unsigned compress(unsigned x, unsigned m) {
unsigned r, s, b; // Result, shift, mask bit.
r = 0;
s = 0;
do {
b = m & 1;
r = r | ((x & b) << s);
s = s + b;
x = x >> 1;
m = m >> 1;
} while (m != 0);
return r;
}
But there is an other way, also given by Hackers Delight, which does less looping (number of iteration logarithmic in the number of bits) but more per iteration:
unsigned compress(unsigned x, unsigned m) {
unsigned mk, mp, mv, t;
int i;
x = x & m; // Clear irrelevant bits.
mk = ~m << 1; // We will count 0's to right.
for (i = 0; i < 5; i++) {
mp = mk ^ (mk << 1); // Parallel prefix.
mp = mp ^ (mp << 2);
mp = mp ^ (mp << 4);
mp = mp ^ (mp << 8);
mp = mp ^ (mp << 16);
mv = mp & m; // Bits to move.
m = m ^ mv | (mv >> (1 << i)); // Compress m.
t = x & mv;
x = x ^ t | (t >> (1 << i)); // Compress x.
mk = mk & ~mp;
}
return x;
}
Notice that a lot of the values there depend only on m. Since you only have 512 different masks, you could precompute those and simplify the code to something like this (not tested)
unsigned compress(unsigned x, int maskindex) {
unsigned t;
int i;
x = x & masks[maskindex][0];
for (i = 0; i < 5; i++) {
t = x & masks[maskindex][i + 1];
x = x ^ t | (t >> (1 << i));
}
return x;
}
Of course all of these can be turned into "not a loop" by unrolling, the second and third ways are probably more suitable for that. That's a bit of cheat however.
You can use the pack-by-multiplication technique similar to the one described here. This way you don't need any loop and can mix the bits in any order.
For example with the mask 0b10101001 == 0xA9 like above and 8-bit data abcdefgh (with a-h is the 8 bits) you can use the below expression to get 0000aceh
uint8_t compress_maskA9(uint8_t x)
{
const uint8_t mask1 = 0xA9 & 0xF0;
const uint8_t mask2 = 0xA9 & 0x0F;
return (((x & mask1)*0x03000000 >> 28) & 0x0C) | ((x & mask2)*0x50000000 >> 30);
}
In this specific case there are some overlaps of the 4 bits while adding (which incur unexpected carry) during the multiplication step, so I've split them into 2 parts, the first one extracts bit a and c, then e and h will be extracted in the latter part. There are other ways to split the bits as well, like a & h then c & e. You can see the results compared to Harold's function live on ideone
An alternate way with only one multiplication
const uint32_t X = (x << 8) | x;
return (X & 0x8821)*0x12050000 >> 28;
I got this by duplicating the bits so that they're spaced out farther, leaving enough space to avoid the carry. This is often better than splitting into 2 multiplications
If you want the result's bits reversed (i.e. heca0000) you can easily change the magic numbers accordingly
// result: he00 | 00ca;
return (((x & 0x09)*0x88000000 >> 28) & 0x0C) | (((x & 0xA0)*0x04800000) >> 30);
or you can also extract the 3 bits e, c and a at the same time, leaving h separately (as I mentioned above, there are often multiple solutions) and you need only one multiplication
return ((x & 0xA8)*0x12400000 >> 29) | (x & 0x01) << 3; // result: 0eca | h000
But there might be a better alternative like the above second snippet
const uint32_t X = (x << 8) | x;
return (X & 0x2881)*0x80290000 >> 28
Correctness check: http://ideone.com/PYUkty
For a larger number of masks you can precompute the magic numbers correspond to those masks and store them in an array so that you can look them up immediately for use. I calculated those mask by hand but you can do that automatically
Explanation
We have abcdefgh & mask1 = a0c00000. Multiply it with magic1
........................a0c00000
× 00000011000000000000000000000000 (magic1 = 0x03000000)
────────────────────────────────
a0c00000........................
+ a0c00000......................... (the leading "a" bit is outside int's range
──────────────────────────────── so it'll be truncated)
r1 = acc.............................
=> (r1 >> 28) & 0x0C = 0000ac00
Similarly we multiply abcdefgh & mask2 = 0000e00h with magic2
........................0000e00h
× 01010000000000000000000000000000 (magic2 = 0x50000000)
────────────────────────────────
e00h............................
+ 0h..............................
────────────────────────────────
r2 = eh..............................
=> (r2 >> 30) = 000000eh
Combine them together we have the expected result
((r1 >> 28) & 0x0C) | (r2 >> 30) = 0000aceh
And here's the demo for the second snippet
abcdefghabcdefgh
& 1000100000100001 (0x8821)
────────────────────────────────
a000e00000c0000h
× 00010010000001010000000000000000 (0x12050000)
────────────────────────────────
000h
00e00000c0000h
+ 0c0000h
a000e00000c0000h
────────────────────────────────
= acehe0h0c0c00h0h
& 11110000000000000000000000000000
────────────────────────────────
= aceh
For the reversed order case:
abcdefghabcdefgh
& 0010100010000001 (0x2881)
────────────────────────────────
00c0e000a000000h
x 10000000001010010000000000000000 (0x80290000)
────────────────────────────────
000a000000h
00c0e000a000000h
+ 0e000a000000h
h
────────────────────────────────
hecaea00a0h0h00h
& 11110000000000000000000000000000
────────────────────────────────
= heca
Related:
How to create a byte out of 8 bool values (and vice versa)?
Redistribute least significant bits from a 4-byte array to a nibble
I would like to write a function norm2 which computes
uint32_t norm2(uint32_t a, uint32_t b) {
return sqd( a & 0x000000FF , b & 0x000000FF )
+ sqd((a & 0x0000FF00)>> 8, (b & 0x0000FF00)>> 8)
+ sqd((a & 0x00FF0000)>>16, (b & 0x00FF0000)>> 16)
+ sqd((a & 0xFF000000)>>24, (b & 0xFF000000)>> 24);
}
uint32_t sqd(uint32_t a, uint32_t b) {
uint32_t x = (a > b) ? a - b : b - a;
return x*x;
}
What is the fastest way to do so under GCC? For example using assembler, SSE or similar.
Very simple to do the whole thing in a few instructions using SSE:
#include <immintrin.h>
#include <stdint.h>
uint32_t norm2(uint32_t a, uint32_t b) {
const __m128i vec_zero = _mm_setzero_si128();
__m128i vec_a = _mm_unpacklo_epi8(_mm_cvtsi32_si128(a), vec_zero);
__m128i vec_b = _mm_unpacklo_epi8(_mm_cvtsi32_si128(b), vec_zero);
__m128i vec_diff = _mm_sub_epi16(vec_a, vec_b);
__m128i vec_dsq = _mm_madd_epi16(vec_diff, vec_diff);
return _mm_cvtsi128_si32(_mm_hadd_epi32(vec_dsq, vec_dsq));
}
What we’re doing here is “unpacking” both a and b with a zero vector to expand the individual bytes into vectors of 16-bit integers. We then subtract them (as 16-bit integers, avoiding risk of overflow), and multiply and accumulate them (as 32-bit integers, again avoiding risk of overflow).
I don’t have GCC installed to test with, but the above generates near-optimal assembly with clang; it shouldn’t be necessary to drop into assembly for such a simple task.
If you can read in sets of 4 for a and b, this can be done most cleanly/elegantly/efficiently by operating on 4-tuples because it will more fully saturate some of the instructions, so that all parts of the computation are part of the solution. The below solution uses up to SSSE3. Of course you will be better off to pull this out of the function, initialize constants up front, and find the most efficient way to put the values into the __m128i values depending on how the surrounding code is structured.
// a, b, and out, must all point to 4 integers
void norm2x4(const unsigned *a, const unsigned *b, unsigned *out) {
// load up registers a and b, in practice this should probably not be in a function,
// initialization of zero can happen outside of a loop,
// and a and b can be loaded directly from memory into __m128i registers
__m128i const zero = _mm_setzero_si128();
__m128i alo = _mm_loadu_si128((__m128i*)a); // this can also be adapted to aligned read instructions if you ensure an aligned buffer
__m128i blo = _mm_loadu_si128((__m128i*)b);
// everything is already in the register where we need it except it
// needs to be expanded to 2-byte ints for computations to work correctly
__m128i ahi = _mm_unpackhi_epi8(alo, zero);
__m128i bhi = _mm_unpackhi_epi8(blo, zero);
alo = _mm_unpacklo_epi8(alo, zero);
blo = _mm_unpacklo_epi8(blo, zero);
alo = _mm_sub_epi16(alo, blo); // don't care if a - b, or b - a, the "wrong" one will result in a
ahi = _mm_sub_epi16(ahi, bhi); // negation the square will later correct
alo = _mm_madd_epi16(alo, alo); // perform the square, and add every two adjacent
ahi = _mm_madd_epi16(ahi, ahi);
alo = _mm_hadd_epi32(alo, ahi); // add horizontal elements; ahi now contains 4 ints which are your results
// store the result to output; this can be adapted to an aligned store if you ensure an aligned buffer
// or the individual values can be extracted directly to 32-bit registers using _mm_extract_epi32
_mm_storeu_si128((__m128i*)out, alo);
}
a branchless version (as square(-x) == square(x)):
uint32_t sqd(int32_t a, int32_t b) {
int32_t x = a - b;
return x * x;
}
uint32_t norm2(uint32_t a, uint32_t b) {
return sqd( a & 0x000000FF , b & 0x000000FF )
+ sqd((a & 0x0000FF00) >> 8, (b & 0x0000FF00) >> 8)
+ sqd((a & 0x00FF0000) >> 16, (b & 0x00FF0000) >> 16)
+ sqd((a & 0xFF000000) >> 24, (b & 0xFF000000) >> 24);
}
I'm working in C++ with an array of unsigned char representing pixels in an image. Each pixel has 3 channel (R,G,B). The image is represented linearly, sort of like
RGBRGBRGBRGB.....
How do I split each of the R,G and B, into separate arrays efficiently?
I tried:
for(int pos = 0; pos < srcWidth * srcHeight; pos++) {
int rgbPos = pos * 3;
splitChannels[0][pos] = rgbSrcData[rgbPos];
splitChannels[1][pos] = rgbSrcData[rgbPos + 1];
splitChannels[2][pos] = rgbSrcData[rgbPos + 2];
}
But this is surprisingly slow.
Thanks!
My attempt: load and store the bytes four by four. Byte scrambling will be tedious but possibly throughput will improve.
// Load 4 interleaved pixels
unsigned int RGB0= ((int*)rgbSrcData)[i];
unsigned int RGB1= ((int*)rgbSrcData)[i + 1];
unsigned int RGB2= ((int*)rgbSrcData)[i + 2];
// Rearrange and store 4 unpacked pixels
((int*)splitChannels[0])[j]=
(RGB0 & 0xFF) | (RGB0 >> 24) | (RGB1 & 0xFF0000) | ((RGB2 & 0xFF00) << 16);
((int*)splitChannels[1])[j]=
((RGB0 & 0xFF00) >> 8) | (RGB1 & 0xFF) | (RGB1 >> 24) | (RGB2 & 0xFF0000) >> 16;
((int*)splitChannels[2])[j]=
((RGB0 & 0xFF0000) >> 16) | (RGB1 & 0xFF00) | ((RGB2 & 0xFF) >> 16) | (RGB2 & 0xFF000000);
(CAUTION: not unchecked !) A shift-only version is also possible.
An SSE solution would be more complex (the stride 3 does not get along with powers of 2).
A great technique to use to make it run faster is loop unwinding.
You can read about it here: http://en.wikipedia.org/wiki/Loop_unwinding