123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118 |
- <?php
- define("PIXEL_RED", (int) pow(2,0));
- define("PIXEL_GREEN", (int) pow(2,1));
- define("PIXEL_BLUE", (int) pow(2,2));
- define("PIXEL_ALPHA", (int) pow(2,3));
- define("PIXEL_ALTERNATE", (int) pow(2,4));
- define("PIXEL_ADD", (int) pow(2,5));
- define("PIXEL_SUB", (int) pow(2,6));
- define("PIXEL_OR", (int) pow(2,7));
- define("PIXEL_AND", (int) pow(2,8));
- define("PIXEL_XOR", (int) pow(2,9));
- //define("PIXEL_FLAG", (int) pow(2,10));
- //define("PIXEL_FLAG", (int) pow(2,11));
- //define("PIXEL_FLAG", (int) pow(2,12));
- // ...
- define("PIXEL_NOISE", (int) pow(2,31));
- class Pixel {
- public $r; public $g; public $b; public $a = /* opaque */255;
- public $flags = /* none */ 0; public $bias = /* full */ 64;
- public function __construct($r, $g, $b, $alpha = NULL, $flags = 0, $bias = 0) {
- $this->r = $this->limit($r);
- $this->g = $this->limit($g);
- $this->b = $this->limit($b);
- // alpha tends to be represented [0.0 .. 1.0] rather than [0..255]
- $alpha = NULL === $alpha ?1.0 :$alpha;
- $this->setAlpha($alpha);
- $this->flags = $flags;
- // bias is up to 75% with 0.75 being "full" or "most" bias
- $this->setBias($bias);
- // process immediate flags
- Pixel::processFlags($this);
- }
- static public function processFlags(Pixel $p) {
- if(PIXEL_NOISE & $p->flags) { $p->rgbaWalk(array($p, "noise")); }
- if(PIXEL_ADD & $p->flags) $p->binFn(function($x,$y) { return $x+$y; });
- if(PIXEL_SUB & $p->flags) $p->binFn(function($x,$y) { return $x-$y; });
- if(PIXEL_OR & $p->flags) $p->binFn(function($x,$y) { return $x|$y; });
- if(PIXEL_AND & $p->flags) $p->binFn(function($x,$y) { return $x&$y; });
- if(PIXEL_XOR & $p->flags) $p->binFn(function($x,$y) { return $x^$y; });
- }
- private function binFn($fn) {
- $b = $this->bias;
- $this->rgbaWalk(function($x) use ($b, $fn) { return $fn($x,$b); });
- }
- private function rgbaWalk($callable) {
- $arr = array(
- PIXEL_RED => &$this->r,
- PIXEL_GREEN => &$this->g,
- PIXEL_BLUE => &$this->b,
- PIXEL_ALPHA => &$this->a
- );
- foreach($arr as $k=>$v) {
- $arr[$k] = ($k & $this->flags) ?call_user_func($callable, $v) :$v;
- $arr[$k] = $this->limit($arr[$k]);
- }
- }
- private function noise($v) {
- return Pixel::limit(abs($v + ((rand(0,1) ?-1 :1) * rand(0,$this->bias))));
- }
- static private function limit($x, $limit = 255) {
- return $limit > ($x = (int) round($x)) ?$x : $limit;
- }
- // alpha [0..1] where zero is transparent, and one is opaque
- public function setAlpha($f) { $this->a = $this->limit($f * 255); }
- // bias [0..0.50] where zero is none, and one half is max
- public function setBias($f) { $this->bias = $this->limit($f * 255, 192); }
- // value is a float [0..1]
- static function mix(Pixel $p1, Pixel $p2, $v1 = 0.5, $v2 = 0.5, $opts = array()) {
- $r = round(($p1->r * $v1) + ($p2->r * $v2));
- $g = round(($p1->g * $v1) + ($p2->g * $v2));
- $b = round(($p1->b * $v1) + ($p2->b * $v2));
- $a = round(($p1->a * $v1) + ($p2->a * $v2));
- // generate new pixel passing whatever options static methods provided
- if(isset($opts["flags"]) and isset($opts["bias"])) {
- return new Pixel($r, $g, $b, (float) $a/255, $opts["flags"], $opts["bias"]);
- } else if(isset($opts["flags"])) {
- return new Pixel($r, $g, $b, (float) $a/255, $opts["flags"]);
- } else {
- return new Pixel($r, $g, $b, (float) $a/255);
- }
- }
- static function generatePixelLine(Pixel $p1, Pixel $p2, $opts = array()) {
- // provide default total pixels, "t"
- $t = isset($opts["total"]) ?$opts["total"] :3;
- // first and second pixel are already provided
- $t -= 2;
- $arr = array(clone $p1);
- $toggleable = array();
- // check for alternating flag, toggle existing binary functions
- if(isset($opts["flags"]) and (PIXEL_ALTERNATE & $opts["flags"])) {
- $binaryFunctions = array(
- PIXEL_ADD,
- PIXEL_SUB,
- PIXEL_OR,
- PIXEL_XOR,
- PIXEL_AND
- );
- foreach($binaryFunctions as $v) {
- // alternate should occur immediately (second element)
- if($v & $opts["flags"]) { $toggleable[$v] = true; $opts["flags"] ^= $v; }
- }
- }
- for($i=$t;$i--;) {
- // toggle the binary operations
- foreach($toggleable as $k=>$doesntMatter) { $opts["flags"] ^= $k; }
- $arr[] = Pixel::mix($p1, $p2, ($i+1)/($t+1), ($t-$i)/($t+1), $opts);
- }
- $arr[] = clone $p2;
- return $arr;
- }
- public function __tostring() {
- return sprintf("%02x%02x%02x%02x", $this->r, $this->g, $this->b, $this->a);
- }
- }
|