Colour Manipulation

Coding Critique is the place to post source code for peer review by other members of DevNetwork. Any kind of code can be posted. Code posted does not have to be limited to PHP. All members are invited to contribute constructive criticism with the goal of improving the code. Posted code should include some background information about it and what areas you specifically would like help with.

Popular code excerpts may be moved to "Code Snippets" by the moderators.

Moderator: General Moderators

Post Reply
samwho
Forum Newbie
Posts: 15
Joined: Sat Apr 03, 2010 8:06 am

Colour Manipulation

Post by samwho »

Hey, I'd like to submit a code snippet for review/comment/to help others :)

It concerns dynamic colour changing. I noticed that for websites with different colour schemes it's a little bit fiddly and annoying to define all of the colours manually in a database and after seeing Adobe's kuler (http://kuler.adobe.com/) I thought it would be cool to have a PHP implementation of this.

Some source code:

Code: Select all

	function getStrongest($r, $g, $b) {
		$strongest = max($r, $g, $b);
		if ($strongest == $r) {
			return 'r';
		}
		else if ($strongest == $g) {
			return 'g';
		}
		else if ($strongest == $b) {
			return 'b';
		}
	}
	function colourChange($colour, $red_change, $green_change, $blue_change) {
		$colour = preg_replace("/\#/","",$colour);
		if (strlen($colour) != 6) {
			return array("colour" => false, "error" => "Invalid colour value.");
		}
		$red = hexdec(substr($colour,0,2));
		$green = hexdec(substr($colour,2,2));
		$blue = hexdec(substr($colour,4,2));
		
		$red += intval($red_change);
		$green += intval($green_change);
		$blue += intval($blue_change);
		
		if ($red > 255) {
			$red = 255;
		}
		if ($red < 0) {
			$red = 0;
		}
		if ($green > 255) {
			$green = 255;
		}
		if ($green < 0) {
			$green = 0;
		}
		if ($blue > 255) {
			$blue = 255;
		}
		if ($blue < 0) {
			$blue = 0;
		}
		if (strlen(dechex($red)) == 1) {
			$new_red = "0".dechex($red);
		}
		else {
			$new_red = dechex($red);
		}
		if (strlen(dechex($green)) == 1) {
			$new_green = "0".dechex($green);
		}
		else {
			$new_green = dechex($green);
		}
		if (strlen(dechex($blue)) == 1) {
			$new_blue = "0".dechex($blue);
		}
		else {
			$new_blue = dechex($blue);
		}
		return array("colour" => "#".$new_red.$new_green.$new_blue, "error" => false);
	}
Currently it returns an array containing indices "colour" and "error". It was written like this at the time because I wanted descriptions of the errors but I realise that may not be the best way of doing it. Fairly easy to change, though :)

The way it works is quite simple, it breaks down a 3 byte hexidecimal value into its separate red, green and blue bytes and allows you to edit them with parameters passed to the function. I have found this code very useful for doing things such as slight increase/decrease in brightness for mouse overs etc.

I am open to the fact it probably isn't as efficient/well written as it could be which is why I would love some constructive criticism on it to make it a really good, useful function :)

Example of its usage:

http://lbak.co.uk/experiments/colourchange/

The first box is your initial colour, the following 3 boxes are your red, green and blue increment/decrement values (respectively). Clicking "Go!" gives you a little gradient of your desired changes as well as their output values and the "Colour Square" option is really cool. It was a proof of concept kinda thing I decided to try out that generates a colour square similar to one you would find in Photoshop when selecting a colour. It isn't perfect, I recommend you just try it with solid red, green or blue but the idea is definitely there :) Just to prove how cool this little code snippet can be (it's an AJAX based call that takes a little while to load).

Source code for the colour square and colour gradient:

Code: Select all

(CSS)
.r {
width: 4px;
height: 4px;
display: inline-block;
}
(/CSS)
                $x = 0;
		$y = 0;
		$z = 0;
		$start = preg_replace("/\#/","",$_POST['colour']);
		$strongest = getStrongest(hexdec(substr($start,0,2)),hexdec(substr($start,2,2)),hexdec(substr($start,4,2)));
		$cur_red = hexdec(substr($start,0,2));
		$cur_green = hexdec(substr($start,2,2));
		$cur_blue = hexdec(substr($start,4,2));
		$length = 64;
		if ($strongest == 'r') {
			$r = 0;
			$g = 0;
			$b = 0;
			while ($y < $length) {
				$x = 0;
				while ($x < $length) {
					$g_inc = floor((256 - $cur_green) / $length);
					$b_inc = floor((256 - $cur_blue) / $length);
					$colour = colourChange($start,$r,$g,$b);
					echo '<div class="r" style="background-color: '.$colour['colour'].'"></div>';
					$g += $g_inc;
					$b += $b_inc;
					$x++;
					//echo 'r'.$r.'g'.$g.'b'.$b;
				}
				echo '<br />';
				$y++;
				$r = -4 * $y;
				$g = -4 * $y;
				$b = -4 * $y;
			}
		}
		else if ($strongest == 'g') {
			$r = 0;
			$g = 0;
			$b = 0;
			while ($y < 64) {
				$x = 0;
				while ($x < 64) {
					$r_inc = floor((256 - $cur_red) / $length);
					$b_inc = floor((256 - $cur_blue) / $length);
					$colour = colourChange($start,$r,$g,$b);
					echo '<div style="width: 4px; height: 4px; background-color: '.$colour['colour'].'; display: inline-block;" onclick="document.getElementById(\'colour\').value = this.style.backgroundColor"></div>';
					$r += $r_inc;
					$b += $b_inc;
					$x++;
					//echo 'r'.$r.'g'.$g.'b'.$b;
				}
				echo '<br />';
				$y++;
				$r = -4 * $y;
				$g = -4 * $y;
				$b = -4 * $y;
			}
		}
		else if ($strongest == 'b') {
			$r = 0;
			$g = 0;
			$b = 0;
			while ($y < 64) {
				$x = 0;
				while ($x < 64) {
					$g_inc = floor((256 - $cur_green) / $length);
					$r_inc = floor((256 - $cur_red) / $length);
					$colour = colourChange($start,$r,$g,$b);
					echo '<div style="width: 4px; height: 4px; background-color: '.$colour['colour'].'; display: inline-block;" onclick="document.getElementById(\'colour\').value = this.style.backgroundColor"></div>';
					$g += $g_inc;
					$r += $r_inc;
					$x++;
					//echo 'r'.$r.'g'.$g.'b'.$b;
				}
				echo '<br />';
				$y++;
				$r = -4 * $y;
				$g = -4 * $y;
				$b = -4 * $y;
			}
		}
Note: Colour square may not work in IE...

PS: I apologies for the lack of comments on the colour square example but I hope it makes sense to some of the seasoned PHP coders :)

Thanks,
Sam
User avatar
kaszu
Forum Regular
Posts: 749
Joined: Wed Jul 19, 2006 7:29 am

Re: Colour Manipulation

Post by kaszu »

Looks nice, but for colors like 069 => 006699 it doesn't work :(
But to be honest I don't see where this snippet could be useful / used.
User avatar
VladSun
DevNet Master
Posts: 4313
Joined: Wed Jun 27, 2007 9:44 am
Location: Sofia, Bulgaria

Re: Colour Manipulation

Post by VladSun »

How about having a "normalize/format" function:

Code: Select all

function normalizeColour($colour)
{
    return str_pad(dechex($colour < 0 ? 0 : ($colour > 255 ? 255 : $colour)), 2, '0', STR_PAD_LEFT);
}
This way your function becomes (by using less expensive str_replace):

Code: Select all

function colourChange($colour, $red_change, $green_change, $blue_change) {
                $colour = str_replace("#","",$colour);
                if (strlen($colour) != 6) {
                        return array("colour" => false, "error" => "Invalid colour value.");
                }
                $red = hexdec(substr($colour,0,2));
                $green = hexdec(substr($colour,2,2));
                $blue = hexdec(substr($colour,4,2));
                
                $red += intval($red_change);
                $green += intval($green_change);
                $blue += intval($blue_change);
                
                $red = normalizeColour($red);
                $green = normalizeColour($green);
                $blue = normalizeColour($blue);

                return array("colour" => "#".$new_red.$new_green.$new_blue, "error" => false);
        }
Also, as kaszu noticed you should take care of "shortcut values":

Code: Select all

       }
        function colourChange($colour, $red_change, $green_change, $blue_change) {
                 $colour = str_replace("#","",$colour);
                if (strlen($colour) == 6) {
                        $bytes =2;
                }
                if (strlen($colour) == 3) {
                        $bytes = 1;
                }
                else {
                        return array("colour" => false, "error" => "Invalid colour value.");
                }

                $red = hexdec(substr($colour, 0, $bytes));
                $green = hexdec(substr($colour, $bytes, $bytes));
                $blue = hexdec(substr($colour, 2*$bytes, $bytes));
                ...
There are 10 types of people in this world, those who understand binary and those who don't
samwho
Forum Newbie
Posts: 15
Joined: Sat Apr 03, 2010 8:06 am

Re: Colour Manipulation

Post by samwho »

Huge thanks for the suggestions guys :D

Kaszu, I have developed for about 3-4 months on a text based RPG that has a little bit of trouble supporting their multiple colour schemes. This function was originally written for hover overs... Just to increase the background colour (that was pulled from a database into a $colorScheme array) by 20 in each area and thus being supported on all colour schemes :)

Uses other than that I'm not entirely sure of... I think I demonstrated some little fun things you can do with it in my example link but I haven't thought too creatively about it yet. I'm sure with the right knowledge and mathematical application you can work out complimentary colours and such :)
Post Reply