Jump to content
  • Advertisement
Sign in to follow this  
Khatharr

Overoptimizers anonymous - Hue shifting

This topic is 2334 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

This maybe should go in the general forum, but since it deals with color spaces I'm putting it here and it can be moved if desired.

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;
}
Edited by Khatharr

Share this post


Link to post
Share on other sites
Advertisement
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

GameDev.net is your game development community. Create an account for your GameDev Portfolio and participate in the largest developer community in the games industry.

Sign me up!