Last active
August 29, 2015 13:56
-
-
Save blewert/9308577 to your computer and use it in GitHub Desktop.
Some RGBA functions I've been writing lately in PAWN. I ended up going overboard, and thought it might be useful to some people. I'll probably end up extending this to ARGB support in the future.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* librgba.p/inc | |
* | |
* @file A file which contains some functions for managing RGBA colours. | |
* @author blewert <http://forum.sa-mp.com/member.php?u=19757> | |
* | |
*/ | |
//Pseudo-OO | |
#define LIB_NAME librgba. | |
#define RGBA::%0(%1) __RGBA_%0(%1) | |
//.. | |
//Aliases for functions and enums | |
#define __RGBA_Modify __RGBA_Set | |
#define __RGBA_To __RGBA_Convert | |
#define __RGBA_Blend __RGBA_ToNormalBlendedRGB | |
//-- | |
#define STRUCT_COLOUR_RGB STRUCT_RGB | |
#define STRUCT_COLOUR_RGBA STRUCT_RGBA | |
//.. | |
//If undefined, this won't output errors. | |
#define SHOW_ERRORS | |
//Bitflags for testing (Use by ORing them into testLevel [e.g. LIBRGBA_TEST_GET | LIBRGBA_TEST_BUILD]) | |
enum (<<= 1) | |
{ | |
LIBRGBA_TEST_GET = 1, | |
LIBRGBA_TEST_BUILD , | |
LIBRGBA_TEST_RGBABLEND , | |
LIBRGBA_TEST_RGBAMODIFY , | |
LIBRGBA_TEST_RGBATO , | |
LIBRGBA_TEST_NONE , | |
} | |
//.. | |
//Test level. Controls what to debug. | |
const testLevel = (LIBRGBA_TEST_NONE); | |
//Enums for RGB and RGBA colours. | |
enum STRUCT_COLOUR_RGB | |
{ | |
r, g, b | |
} | |
//-- | |
enum STRUCT_COLOUR_RGBA | |
{ | |
r, g, b, a | |
} | |
//.. | |
/** | |
* Builds an RGBA colour from numeric inputs. | |
* | |
* @since 22nd January 2014 | |
* | |
* @param srcR Unsigned byte which represents the red channel. | |
* @param srcG Unsigned byte which represents the green channel. | |
* @param srcB Unsigned byte which represents the blue channel. | |
* @param srcA Unsigned byte which represents the alpha channel. | |
* | |
* @return An RGBA colour, made from numeric inputs. | |
*/ | |
stock RGBA::Build(const srcR, const srcG, const srcB, const srcA) | |
{ | |
//Simply return each component shifted into their respective | |
//places, and 'glue' them together with OR. | |
return ((srcR << 24) | (srcG << 16) | (srcB << 8) | (srcA)); | |
} | |
/** | |
* Constructs an ARGB representation of RGBA colour. | |
* | |
* @since 22nd January 2014 | |
* @param input The RGBA colour to change into an ARGB colour. | |
* @return An ARGB representation of the RGBA colour passed. | |
*/ | |
stock RGBA::ToARGB(const input) | |
{ | |
//Simply grab the RRGGBB components, shift them right a byte and | |
//then 'glue' them with OR to the AA component, shifted to the | |
//beginning. This is simply a circular shift. | |
//.. | |
//0xRRGGBB00 -> 0x00RRGGBB | |
// | 0xAA000000 = 0xAARRGGBB | |
// | |
return ((input & 0xFFFFFF00) >>> 8) | ((input & 0xFF) << 24); | |
} | |
/** | |
* Constructs an RGB representation of RGBA colour. | |
* | |
* @since 22nd January 2014 | |
* @param input The RGBA colour to change into an RGB colour. | |
* @return An RGB representation of the RGBA colour passed | |
* | |
* @see RGBA::ToNormalBlendedRGB for colour blending. | |
*/ | |
stock RGBA::ToRGB(const input) | |
{ | |
//Simply return the passed colour shifted right a byte: | |
//0xRRGGBBAA >>> 8 = 0x00RRGGBB = 0xRRGGBB. | |
//.. | |
//This can also be done with (0xRRGGBBAA & 0xFFFFFF00) = 0xRRGGBB | |
return (input >>> 8); | |
//.. | |
} | |
/** | |
* Converts an RGBA colour to the format string specified. Use with 'r', 'g', 'b' and 'a', for example: | |
* | |
* RGBA::To(0xAABBCCDD, 'gbar') gives 0xBBCCDDAA | |
* RGBA::To(0xAABBCCDD, 'g' ) gives 0xBB | |
* RGBA::To(0xAABBCCDD, 'argb') gives 0xDDAABBCC | |
* | |
* @since 22nd January 2014 | |
* | |
* @param input The RGBA colour to convert. | |
* @param format A string representing the format to convert to. | |
* | |
* @return A colour in specified format. | |
*/ | |
stock RGBA::To(const input, const format[]) | |
{ | |
#if defined SHOW_ERRORS | |
{ | |
//If the string size is over 5, then things might go wrong. | |
if(strlen(format) > 5) | |
return printf("[" #LIB_NAME "RGBA::To] Destination output contains more than 5 characters. Might exceed cellmax."); | |
//If we pass empty strings, then we also might encounter some problems. | |
if(strlen(format) == 0) | |
return printf("[" #LIB_NAME "RGBA::To] Please use format specifiers. ('rgb' returns rgba -> rgb, etc.)"); | |
} | |
#endif | |
//Resulting accumulative colour to return later | |
new result; | |
for(new i = 0; i < strlen(format); i++) | |
{ | |
switch(format[i]) | |
{ | |
//Traverse through each character. | |
//.. | |
//If R, simply shift the existing colours left a byte and glue | |
//the extracted component into result. | |
case 'r', 'R': | |
result <<= 8, result |= (input >>> 24); | |
//-- | |
//Do the same for G, B and A with respect to their places: | |
case 'g', 'G': | |
result <<= 8, result |= ((input >>> 16) & 0xFF); | |
//-- | |
case 'b', 'B': | |
result <<= 8, result |= ((input >>> 8) & 0xFF); | |
//-- | |
case 'a', 'A', 'o', 'O': | |
result <<= 8, result |= (input & 0xFF); | |
//.. | |
#if defined SHOW_ERRORS | |
//We got another format specifier. Show an error if needed. | |
default: | |
return printf("[" #LIB_NAME "RGBA::To] I wasn't expecting format specifier '%c'. Please use 'r', 'g', 'b' and ('a' or 'o').", format[i]); | |
#endif | |
} | |
} | |
//Finally, return colour | |
return result; | |
} | |
/** | |
* Gets the value of a component of an RGBA colour, by a specifier. | |
* | |
* @since 22nd January 2014 | |
* | |
* @param input The RGBA colour. | |
* @param mode A character containing which component to grab ('r', 'g', 'b', 'a'). | |
* | |
* @return The value of the specified component. | |
*/ | |
stock RGBA::Get(const input, const mode = 'a') | |
{ | |
switch(mode) | |
{ | |
//Bitmask and shift colours out of input, and return them (depending on mode). Can also be done like: | |
// | |
//(input >>> 24) & 0xFF <=> (input & 0xFF000000) >>> 24 | |
//.. | |
case 'r', 'R': | |
return (input & 0xFF000000) >>> 24; | |
//-- | |
case 'g', 'G': | |
return (input & 0x00FF0000) >>> 16; | |
//-- | |
case 'b', 'B': | |
return (input & 0x0000FF00) >>> 8; | |
//-- | |
case 'a', 'A', 'o', 'O': | |
return (input & 0x000000FF); | |
//.. | |
#if defined SHOW_ERRORS | |
//Invalid mode specifier, show error. | |
default: | |
return printf("[" #LIB_NAME "Get] I wasn't expecting specifier '%c'. Please use 'r', 'g', 'b' or ('a' | 'o').", mode); | |
//.. | |
#endif | |
} | |
//Return false otherwise. | |
return false; | |
} | |
/** | |
* Modifies the specified byte of an RGBA colour passed. | |
* | |
* @since 22nd January 2014 | |
* | |
* @param input The RGBA colour to modify. | |
* @param mode The specified byte (as a character) to modify. ('r', 'g', 'b', 'a'). | |
* @param value An unsigned byte to set the specified component to. | |
* | |
* @return The RGBA colour, with modifications. | |
*/ | |
stock RGBA::Modify(const input, const mode, const value) | |
{ | |
switch(mode) | |
{ | |
//Depending on the mode, grab everything which isn't the component (inverse bitmask) | |
//and glue it to the value passed shifted left with a bitmask of the component's | |
//position: | |
// | |
//For r, with 0xEE: | |
// | |
// 0xRRGGBBAA -> (0x00GGBBAA) | |
// 0xFF000000 -> | (0xEE000000) | |
// = 0xEEGGBBAA | |
case 'r', 'R': | |
return (input & 0x00FFFFFF) | (0xFF000000 & (value << 24)); | |
//-- | |
case 'g', 'G': | |
return (input & 0xFF00FFFF) | (0x00FF0000 & (value << 16)); | |
//-- | |
case 'b', 'B': | |
return (input & 0xFFFF00FF) | (0x0000FF00 & (value << 8)); | |
//-- | |
case 'a', 'A', 'o', 'O': | |
return (input & 0xFFFFFF00) | (0x000000FF & value); | |
//.. | |
#if defined SHOW_ERRORS | |
//Invalid format specifier, return error. | |
default: | |
return printf("[" #LIB_NAME "Modify] I wasn't expecting specifier '%c'. Please use 'r', 'g', 'b' or ('a' | 'o').", mode); | |
//.. | |
#endif | |
} | |
//Return false otherwise. | |
return false; | |
} | |
/** | |
* Produces an RGB representation of an RGBA colour, by blending between | |
* the colour specified and a transparency key (background). | |
* | |
* @since 22nd January 2014 | |
* | |
* @param input Inputted RGBA colour to blend from. This is the 'top' layer. | |
* @param background Inputted RGB colour to blend to. This is the 'bottom' layer. By default, it's white. | |
* | |
* @return An RGB representation of the RGBA colour. | |
*/ | |
stock RGBA::ToNormalBlendedRGB(const input, const background = 0xFFFFFF) | |
{ | |
//<http://www.opengl.org/wiki/Blending#Blend_Equations>, see GL_FUNC_ADD | |
//.. | |
//Create some variables for triplets/bytes in RGBA/RGB input passed. | |
new backgroundTriplets[STRUCT_COLOUR_RGB]; | |
new inputTriplets [STRUCT_COLOUR_RGBA]; | |
//.. | |
//Bitmask and shift out the relevant bytes (RGB). | |
backgroundTriplets[r] = ((background & 0xFF0000) >>> 16); | |
backgroundTriplets[g] = ((background & 0x00FF00) >>> 8); | |
backgroundTriplets[b] = (background & 0x0000FF); | |
//.. | |
//Do the same for input, but note that this is RGBA - 32 bit, not 24 bit (so >>> 24) | |
inputTriplets[r] = ((input & 0xFF000000) >>> 24); | |
inputTriplets[g] = ((input & 0x00FF0000) >>> 16); | |
inputTriplets[b] = ((input & 0x0000FF00) >>> 8 ); | |
inputTriplets[a] = (input & 0x000000FF); | |
//.. | |
//Normalize opacity by dividing opacity by 255.0 (255/255 = 1.0, 127/255 = 0.5, etc) | |
new Float:opacity = (inputTriplets[a] / 255.0); | |
//Using normal blending formula, calculate the relevant blending and cast to an integer using floatround. | |
new resultR = floatround( (opacity * inputTriplets[r]) + ((1.0 - opacity) * backgroundTriplets[r]) ); | |
new resultG = floatround( (opacity * inputTriplets[g]) + ((1.0 - opacity) * backgroundTriplets[g]) ); | |
new resultB = floatround( (opacity * inputTriplets[b]) + ((1.0 - opacity) * backgroundTriplets[b]) ); | |
//.. | |
//Return the individual components "stuck" together by shifting left into their relevant places. | |
return (resultR << 16) | (resultG << 8) | (resultB); | |
} | |
#if !(testLevel & LIBRGBA_TEST_NONE) | |
public OnFilterScriptInit() | |
{ | |
#if (testLevel & LIBRGBA_TEST_GET) || (testLevel & LIBRGBA_TEST_RGBATO) || (testLevel & LIBRGBA_TEST_RGBAMODIFY) | |
new testValues[] = | |
{ | |
0xAABBCCDD, 0x11223344, 0xFF00FF00, | |
0xFFFF0000, 0x44332211, 0xFFFFFFFF, | |
0x00000000 | |
}; | |
#endif | |
#if (testLevel & LIBRGBA_TEST_GET) | |
{ | |
new const modes[] = { 'r', 'g', 'b', 'a' }; | |
for(new i = 0; i < sizeof testValues; i++) | |
{ | |
for(new j = 0; j < sizeof modes; j++) | |
{ | |
printf("RGBA::Get(0x%x, %c) -> 0x%x", testValues[i], modes[j], RGBA::Get(testValues[i], modes[j])); | |
} | |
printf("\r\n"); | |
} | |
} | |
#endif | |
#if (testLevel & LIBRGBA_TEST_RGBATO) | |
{ | |
new const formats[][] = | |
{ | |
"r", "g", "b", "a", "rgb", "argb", "grba", | |
"abrg" | |
}; | |
for(new i = 0; i < sizeof testValues; i++) | |
{ | |
for(new j = 0; j < sizeof formats; j++) | |
{ | |
printf("RGBA::To(0x%x, %s) -> 0x%x", testValues[i], formats[j], RGBA::To(testValues[i], formats[j])); | |
} | |
printf(""); | |
} | |
} | |
#endif | |
#if (testLevel & LIBRGBA_TEST_RGBAMODIFY) | |
{ | |
new const modes[] = { 'r', 'g', 'b', 'a' }; | |
new destValue = 0x7F; | |
for(new i = 0; i < sizeof testValues; i++) | |
{ | |
for(new j = 0; j < sizeof modes; j++) | |
{ | |
printf("RGBA::Modify(0x%x, %c, %d) -> 0x%x", testValues[i], modes[j], destValue, RGBA::Modify(testValues[i], modes[j], destValue)); | |
} | |
print(" "); | |
} | |
} | |
#endif | |
#if (testLevel & LIBRGBA_TEST_RGBABLEND) | |
{ | |
printf("0x%x from: 0x%x (on top of) 0x%x (%f opacity)", RGBA::ToNormalBlendedRGB(0xFF0000FF, 0xFFFFFFFF), 0xFF0000FF, 0xFFFFFFFF, 0xFF / 255.0); | |
printf("0x%x from: 0x%x (on top of) 0x%x (%f opacity)", RGBA::ToNormalBlendedRGB(0xFF0000EE, 0xFFFFFFFF), 0xFF0000EE, 0xFFFFFFFF, 0xEE / 255.0); | |
printf("0x%x from: 0x%x (on top of) 0x%x (%f opacity)", RGBA::ToNormalBlendedRGB(0xFF0000DD, 0xFFFFFFFF), 0xFF0000DD, 0xFFFFFFFF, 0xDD / 255.0); | |
printf("0x%x from: 0x%x (on top of) 0x%x (%f opacity)", RGBA::ToNormalBlendedRGB(0xFF0000CC, 0xFFFFFFFF), 0xFF0000CC, 0xFFFFFFFF, 0xCC / 255.0); | |
printf("0x%x from: 0x%x (on top of) 0x%x (%f opacity)", RGBA::ToNormalBlendedRGB(0xFF0000BB, 0xFFFFFFFF), 0xFF0000BB, 0xFFFFFFFF, 0xBB / 255.0); | |
printf("0x%x from: 0x%x (on top of) 0x%x (%f opacity)", RGBA::ToNormalBlendedRGB(0xFF0000AA, 0xFFFFFFFF), 0xFF0000AA, 0xFFFFFFFF, 0xAA / 255.0); | |
printf("0x%x from: 0x%x (on top of) 0x%x (%f opacity)", RGBA::ToNormalBlendedRGB(0xFF000099, 0xFFFFFFFF), 0xFF000099, 0xFFFFFFFF, 0x99 / 255.0); | |
printf("0x%x from: 0x%x (on top of) 0x%x (%f opacity)", RGBA::ToNormalBlendedRGB(0xFF000088, 0xFFFFFFFF), 0xFF000088, 0xFFFFFFFF, 0x88 / 255.0); | |
printf("0x%x from: 0x%x (on top of) 0x%x (%f opacity)", RGBA::ToNormalBlendedRGB(0xFF000077, 0xFFFFFFFF), 0xFF000077, 0xFFFFFFFF, 0x77 / 255.0); | |
printf("0x%x from: 0x%x (on top of) 0x%x (%f opacity)", RGBA::ToNormalBlendedRGB(0xFF000066, 0xFFFFFFFF), 0xFF000066, 0xFFFFFFFF, 0x66 / 255.0); | |
printf("0x%x from: 0x%x (on top of) 0x%x (%f opacity)", RGBA::ToNormalBlendedRGB(0xFF000055, 0xFFFFFFFF), 0xFF000055, 0xFFFFFFFF, 0x55 / 255.0); | |
printf("0x%x from: 0x%x (on top of) 0x%x (%f opacity)", RGBA::ToNormalBlendedRGB(0xFF000044, 0xFFFFFFFF), 0xFF000044, 0xFFFFFFFF, 0x44 / 255.0); | |
printf("0x%x from: 0x%x (on top of) 0x%x (%f opacity)", RGBA::ToNormalBlendedRGB(0xFF000033, 0xFFFFFFFF), 0xFF000033, 0xFFFFFFFF, 0x33 / 255.0); | |
printf("0x%x from: 0x%x (on top of) 0x%x (%f opacity)", RGBA::ToNormalBlendedRGB(0xFF000022, 0xFFFFFFFF), 0xFF000022, 0xFFFFFFFF, 0x22 / 255.0); | |
printf("0x%x from: 0x%x (on top of) 0x%x (%f opacity)", RGBA::ToNormalBlendedRGB(0xFF000011, 0xFFFFFFFF), 0xFF000011, 0xFFFFFFFF, 0x11 / 255.0); | |
printf("0x%x from: 0x%x (on top of) 0x%x (%f opacity)", RGBA::ToNormalBlendedRGB(0xFF000000, 0xFFFFFFFF), 0xFF000000, 0xFFFFFFFF, 0x00 / 255.0); | |
} | |
#endif | |
#if (testLevel & LIBRGBA_TEST_BUILD) | |
{ | |
new colours[][STRUCT_COLOUR_RGBA] = | |
{ | |
{ 255, 255, 255, 255 }, | |
{ 255, 0, 0, 127 }, | |
{ 0xAA, 0xBB, 0xCC, 0xDD }, | |
{ 0 , 255, 0, 50 } | |
}; | |
for(new i = 0; i < sizeof colours; i++) | |
printf("%5d, %5d, %5d, %5d -> 0x%x", colours[i][r], colours[i][g], colours[i][b], colours[i][a], | |
RGBA::Build(colours[i][r], colours[i][g], colours[i][b], colours[i][a])); | |
} | |
#endif | |
return 1; | |
} | |
#endif |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment