Skip to content

Instantly share code, notes, and snippets.

@blewert
Last active August 29, 2015 13:56
Show Gist options
  • Save blewert/9308577 to your computer and use it in GitHub Desktop.
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.
/**
* 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