Anyway.... Can you make it faster/better?
The goal is to write a function that accepts a 32-bit ARGB color and an signed integral number of degrees. The function returns the result of shifting the hue of the color by the number of degrees indicated.
Relevant info: http://en.wikipedia.org/wiki/Hue
Note - the forum is doing nastiness to my indentation, sorry.
//Just for fun, trying to create the fastest possible
//hue_shift function for the 32-bit ARGB color space.
//Here's what I have so far:
union uCol {
struct {
BYTE blue;
BYTE green;
BYTE red;
BYTE alpha;
};
BYTE channels[4];
UINT uint;
};
BYTE region_lut[] = {3, 4, 0xFF, 5, 2, 0xFF, 1, 0};
uCol hue_shift(uCol rgba, int degrees) {
int hue, region;
BYTE range, minim;
/* [ASM BLOCK TRANSLATION INTO C++]
int index = 0;
if(rgba.red >= rgba.green) {index += 1;}
if(rgba.red >= rgba.blue) {index += 2;}
if(rgba.green >= rgba.blue) {index += 4;}
region = region_lut[index];
*/
__asm {
pushad //push the registers to the stack
xor eax,eax //clear register a
movzx ebx,rgba.red //place the red channel in b
movzx ecx,rgba.green //green in c
movzx edx,rgba.blue //blue in d
cmp ebx,ecx //compare b and c
jl test02 //if b is less than c goto next test
or eax,1 //else set bit 1 in register a
test02: cmp ebx,edx //compare b and d
jl test03 //if b is less than d goto next test
or eax,2 //else set bit 2 in register a
test03: cmp ecx,edx //compare c and d
jl lookup //if c is less than d goto lookup
or eax,4 //else set bit 3 in register a
lookup: movzx ebx,region_lut[eax] //place the lookup value in register b
mov region,ebx //copy b to the variable 'region'
popad //restore the register values
}
switch(region) {
case 0: //RGB
range = rgba.red;
hue = rgba.green;
minim = rgba.blue;
break;
case 1: //GRB
range = rgba.green;
hue = rgba.red;
minim = rgba.blue;
break;
case 2: //GBR
range = rgba.green;
hue = rgba.blue;
minim = rgba.red;
break;
case 3: //BGR
range = rgba.blue;
hue = rgba.green;
minim = rgba.red;
break;
case 4: //BRG
range = rgba.blue;
hue = rgba.red;
minim = rgba.green;
break;
case 5: //RBG
range = rgba.red;
hue = rgba.blue;
minim = rgba.green;
break;
default:
break; //error
}
//There is no saturation, so the color is not changed.
if(range == minim) {return rgba;}
range -= minim;
hue -= minim;
if(region & 1) {hue = range - hue;}
/* [ASM BLOCK TRANSLATION INTO C++]
region += degrees / 60;
int new_hue = degrees % 60;
new_hue *= range;
new_hue /= 60;
new_hue += hue;
if(new_hue < 0) {
new_hue += range;
--region;
}
if(new_hue > range) {
new_hue -= range;
++region;
}
hue = (BYTE)new_hue;
region %= 6;
if(region < 0) {region += 6;}
range += minim;
*/
__asm {
pushad
xor edx,edx
mov eax,degrees
mov ebx,60
cdq
idiv ebx //we're dividing 'degrees' by 60
add region,eax //adding the whole number part to 'region'
movzx ecx,range
imul edx,ecx //multiplying the remainder by 'range'
mov eax,edx
xor edx,edx
cdq
idiv ebx //then dividing it by 60
add eax,hue //and adding 'hue' to it
//This section corrects the value such that it is gequal zero and
//lequal 'range' and finally sets 'hue' to the resulting value.
//If the value has to be up-stepped (have 'range' added to it)
//then 'region' is decremented. If the value has to be down-
//stepped ('range' subtracted) then 'region' is incremented.
cmp eax,0
jge geq_zero
add eax,ecx
dec region
jmp leq_rang
geq_zero: cmp eax,ecx
jle leq_rang
sub eax,ecx
inc region
leq_rang: mov hue,eax
//Here we correct 'region' to be within the range 0 to 5.
//Then just kick out the result values to memory and finish
//the block.
mov ebx,6
xor edx,edx
mov eax,region
cdq
idiv ebx
cmp edx,0
jge done
add edx,6
done: mov region,edx
add cl,minim
mov range,cl
popad
}
switch(region) {
case 0: //RGB
rgba.red = range;
rgba.green = minim + (BYTE)hue;
rgba.blue = minim;
break;
case 1: //GRB
rgba.green = range;
rgba.red = range - (BYTE)hue;
rgba.blue = minim;
break;
case 2: //GBR
rgba.green = range;
rgba.blue = minim + (BYTE)hue;
rgba.red = minim;
break;
case 3: //BGR
rgba.blue = range;
rgba.green = range - (BYTE)hue;
rgba.red = minim;
break;
case 4: //BRG
rgba.blue = range;
rgba.red = minim + (BYTE)hue;
rgba.green = minim;
break;
case 5: //RBG
rgba.red = range;
rgba.blue = range - (BYTE)hue;
rgba.green = minim;
break;
default:
break; //error
}
return rgba;
}