Created
June 8, 2010 01:24
-
-
Save Crindigo/429474 to your computer and use it in GitHub Desktop.
This file contains hidden or 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
| <?php | |
| /** | |
| * Old ass image class. Originally written in July/August 2006, may or may not work. | |
| * | |
| * 6/7/2010 | |
| * Started converting old weird comment style to PHPDoc, discovered it took too long, quit. | |
| * Replaced Omega Engine error handling with simple throw new Exception(). | |
| * | |
| * @author Steven Harris | |
| * @author Jeremy Privett | |
| * @copyright Copyright (c) 2006-2010 Steven Harris | |
| * @license http://www.opensource.org/licenses/bsd-license.php New BSD License | |
| */ | |
| /** | |
| * The image module facilitates image manipulation, along with an | |
| * encoder that stores data within images. | |
| * | |
| * The coordinate plane manipulation system allows you to alter the | |
| * origin where drawing functions are based from. For example, if you | |
| * wanted the origin in the middle, rather than the default top-left, you | |
| * can use <pre>$image->set_origin($img_width / 2, $img_height / 2);</pre> | |
| * Additionally, you can alter the angle to draw shapes on a rotated plane | |
| * with <pre>$image->set_angle( angle_in_degrees );</pre> | |
| * | |
| * The draw_* functions are merely wrappers for the original GD functions, | |
| * with the exception that the current coordinate plane transformation is | |
| * applied to them. Of course, those exceptions have exceptions. The following | |
| * included draw_* functions have limited support for plane transformations: | |
| * draw_image() | |
| * draw_ellipse() | |
| * draw_filled_ellipse() | |
| * draw_string() | |
| * draw_border(), but only because the function is meant to only affect edges. | |
| * | |
| * The effect_* functions are the fun ones, which will mostly be used | |
| * when generating captcha images. effect_ripple() is very fast and | |
| * effective for the job, though you may want to call effect_gaussian_blur() | |
| * afterwards to smooth out the edges, which will slightly increase processing time. | |
| * | |
| * Thumbnails can be generated with resize(), and if you don't mind sacrificing | |
| * processing time for quality, call effect_unsharpmask() to crispen the blurred | |
| * edges, and produce a higher-quality thumbnail. | |
| * | |
| * Watermarks can be done with draw_image() for now. | |
| */ | |
| class Crindigo_Image | |
| { | |
| /** | |
| * Holds the current X position of the origin. | |
| * @var int | |
| */ | |
| private $plane_x = 0; | |
| /** | |
| * Holds the current Y position of the origin. | |
| * @var int | |
| */ | |
| private $plane_y = 0; | |
| /** | |
| * Holds the current angle of the origin, in radians. | |
| * @var float | |
| */ | |
| private $plane_angle = 0.0; | |
| /** | |
| * Holds a stack of transformation state arrays, which include the | |
| * variables $plane_x, $plane_y, and $plane_angle. | |
| * @var array | |
| */ | |
| private $state_stack = array(); | |
| /** | |
| * Stores a list of what is enabled/disabled in terms of GD abilities. | |
| * The different keys are 'freetype', 'readgif', 'writegif', 'gif', | |
| * 'jpg', 'png', 'gd', and 'gd2'. The value is true if enabled, false if | |
| * not. 'gif' is true if 'readgif' and 'writegif' are both true. | |
| * @var array | |
| */ | |
| public $supports = array(); | |
| /** | |
| * Constructor. Retrieves GD information and stores it in the supports property. | |
| */ | |
| public function __construct() | |
| { | |
| $info = gd_info(); | |
| $this->supports['freetype'] = $info['FreeType Support']; | |
| $this->supports['readgif'] = $info['GIF Read Support']; | |
| $this->supports['writegif'] = $info['GIF Create Support']; | |
| $this->supports['gif'] = ($this->supports['readgif'] AND $this->supports['writegif']); | |
| $this->supports['jpg'] = $info['JPG Support']; | |
| $this->supports['jpeg'] = $info['JPG Support']; | |
| $this->supports['png'] = $info['PNG Support']; | |
| $this->supports['gd'] = true; | |
| $this->supports['gd2'] = true; // this file probably won't work with GD1 anyway. | |
| // besides, GD1 is like... really old. | |
| } | |
| // -------------------------------------------------------------- | |
| // Image Encoding/Decoding | |
| // | |
| /** | |
| * Creates a truecolor image, encoding $s into its pixel data. $img_width | |
| * allows you to force a certain image width, which comes in handy if you | |
| * have a small or large string. The number of pixels used equals out to | |
| * the string length divided by 3, rounded up, plus 1. If $gzip is true, | |
| * the string will be gzcompress()'d with a compression level of $gzlevel | |
| * before making an image of it. | |
| * | |
| * @param string $s The string to encode. | |
| * @param int $img_width Optional. Allows you to force the resultant image's | |
| * width. Defaults to 100. | |
| * @param bool $gzip Optional. If true, gzcompress()'s the string before | |
| * inserting it into the image. Defaults to true. | |
| * @param int $gzlevel Optional. If $gzip is true, this determines the compression | |
| * level gzcompress() uses. Defaults to 6. | |
| * @return GD image resource | |
| */ | |
| public function encode_string_to_image($s, $img_width = 100, $gzip = true, $gzlevel = 6) | |
| { | |
| if ($gzip) { | |
| $s = gzcompress($s, $gzlevel); | |
| } | |
| $len = strlen($s); | |
| $pixels = ceil($len / 3); | |
| $img_height = ceil($pixels / $img_width); | |
| $im = $this->create_true_color($img_width, $img_height); | |
| // make the first pixel 0x000001 if data is compressed, 0x000000 if not | |
| imageSetPixel($im, 0, 0, ($gzip ? 1 : 0)); | |
| $idx = -1; | |
| $x_start = 1; // skip the first pixel... | |
| for ($y = 0; $y < $img_height; $y++) { | |
| for ($x = $x_start; $x < $img_width; $x++) { | |
| $color = (ord($s[++$idx]) << 16) + (ord($s[++$idx]) << 8) + ord($s[++$idx]); | |
| imageSetPixel($im, $x, $y, $color); | |
| } | |
| // after the first line, so make x_start zero. | |
| $x_start = 0; | |
| } | |
| return $im; | |
| } | |
| /** | |
| * Given a truecolor image resource $img, this function decodes the pixel | |
| * data back into the original string which was encoded with | |
| * encode_string_to_image() | |
| * | |
| * @param gd_image_resource $img Image which contains pixel data encoded | |
| * with encode_string_to_image() | |
| * @return string | |
| */ | |
| public function decode_image_to_string(&$img) | |
| { | |
| $width = imageSX($img); | |
| $height = imageSY($img); | |
| $string = ''; | |
| $gzipped = imageColorAt($img, 0, 0); | |
| $x_start = 1; | |
| for ($y = 0; $y < $height; $y++) { | |
| for ($x = $x_start; $x < $width; $x++) { | |
| $color = imageColorAt($img, $x, $y); | |
| $part1 = chr(($color >> 16) & 0xff); | |
| $part2 = chr(($color >> 8 ) & 0xff); | |
| $part3 = chr($color & 0xff); | |
| $string .= $part1 . $part2 . $part3; | |
| } | |
| $x_start = 0; | |
| } | |
| return ($gzipped ? gzuncompress($string) : $string); | |
| } | |
| // -------------------------------------------------------------- | |
| // General Image Functions | |
| // | |
| /** | |
| * Creates a new truecolor image with the specified width and height. If the | |
| * background color $bg_color is given and nonzero, the image will be | |
| * pre-filled with the specified color. | |
| * | |
| * @param int $width Desired image width. | |
| * @param int $height Desired image height. | |
| * @param int $bg_color Optional. Background color to fill with. Defaults to 0. | |
| * @return GD resource | |
| */ | |
| public function create_true_color($width, $height, $bg_color = 0) | |
| { | |
| if (!($img = @imageCreateTrueColor($width, $height))) { | |
| throw new Exception("Could not create a new truecolor image of size $width X $height"); | |
| } | |
| if ($bg_color != 0) { | |
| imageFill($img, 0, 0, $bg_color); | |
| } | |
| return $img; | |
| } | |
| /** | |
| * Creates an image resource from the file located at | |
| * $filename. If $force_ext is given, it will override | |
| * the automatic extension check in the function. If | |
| * the file extension is jpeg or jpg, it calls | |
| * imageCreateFromJPEG. If png, imageCreateFromPNG. | |
| * If gif, imageCreateFromGIF. | |
| * If gd, imageCreateFromGD. | |
| * And if gd2, imageCreateFromGD2. | |
| * | |
| * @param string $filename Path to the file you wish to create an image | |
| * from. Can be a URL. | |
| * @param string $force_ext Optional. Allows you to force an certain file | |
| * type, regardless of the extension fetched from | |
| * the end of $filename. | |
| * @return GD resource | |
| */ | |
| public function create_from_file($filename, $force_ext = '') | |
| { | |
| if (!empty($force_ext)) { | |
| $ext = $force_ext; | |
| } | |
| else { | |
| $ext = $this->file_ext($filename); | |
| } | |
| $ext = strtolower($ext); | |
| // only need to check read privileges | |
| if ($ext == 'gif') { | |
| $check = 'readgif'; | |
| } | |
| else { | |
| $check = $ext; | |
| } | |
| // Check to see if support for this file format is disabled. | |
| if (!$check OR !$this->supports["$check"]) { | |
| throw new Exception("There is no support for reading '$ext' files."); | |
| } | |
| // choose... | |
| switch ($ext) { | |
| case 'jpeg': | |
| case 'jpg': | |
| $function = 'imageCreateFromJPEG'; | |
| break; | |
| case 'png': | |
| $function = 'imageCreateFromPNG'; | |
| break; | |
| case 'gif': | |
| $function = 'imageCreateFromGIF'; | |
| break; | |
| case 'gd': | |
| $function = 'imageCreateFromGD'; | |
| break; | |
| case 'gd2': | |
| $function = 'imageCreateFromGD2'; | |
| break; | |
| } | |
| if (!($image = $function($filename))) { | |
| throw new Exception("Could not create an image resource from $filename, with an extension $ext, using the function $function."); | |
| } | |
| // $image is valid if we're here, so return it! | |
| return $image; | |
| } | |
| /** | |
| * Saves the image $img at $filename, automatically | |
| * calling one of imagePNG, imageJPEG, imageGIF, | |
| * imageGD, or imageGD2, based on the extension. If | |
| * $force_type is given, it will override the extension | |
| * on $filename. | |
| * | |
| * @param gd_image_resource $img An image resource. | |
| * @param string $filename Filename to save the image resource as. | |
| * @param string $force_type Optional. Allows you to force a certain type | |
| * of image, regardless of the extension found | |
| * from $filename. | |
| */ | |
| public function save(&$img, $filename, $force_type = '') | |
| { | |
| if (!empty($force_type)) { | |
| $type = $force_type; | |
| } | |
| else { | |
| $type = $this->file_ext($filename); | |
| } | |
| $type = strtolower($type); | |
| if ($type == 'gif') { | |
| $check = 'writegif'; | |
| } | |
| else { | |
| $check = $type; | |
| } | |
| // Check to see if support for saving in this format is disabled. | |
| if (!$check OR !$this->supports["$check"]) { | |
| throw new Exception("There is no support for writing '$type' files."); | |
| } | |
| // time to choose... | |
| switch ($type) { | |
| case 'jpeg': | |
| case 'jpg': | |
| $function = 'imageJPEG'; | |
| break; | |
| case 'png': | |
| if (function_exists('imageSaveAlpha')) { // >= 4.3.2 | |
| imageSaveAlpha($img, true); | |
| } | |
| $function = 'imagePNG'; | |
| break; | |
| case 'gif': | |
| $function = 'imageGIF'; | |
| break; | |
| case 'gd': | |
| $function = 'imageGD'; | |
| break; | |
| case 'gd2': | |
| $function = 'imageGD2'; | |
| break; | |
| } | |
| if (!$function($img, $filename)) { | |
| throw new Exception("Could not save the image to $filename, using $function."); | |
| } | |
| } | |
| //===================================================== | |
| // @Public: output() | |
| // @Args: $img - Type: Image Resource; | |
| // Desc: Image to display. | |
| // $type - Type: String; | |
| // Desc: Image type to output as. Can be "png", | |
| // "jpg" or "jpeg", or "gif" | |
| // $do_headers - Type: Boolean; | |
| // Desc: Optional. If true, outputs the | |
| // corresponding header to make the image | |
| // work. Defaults to true. | |
| // @Author: Steven Harris | |
| // @Version: <{#spec.tag.float.version.var.replace#}> | |
| // @Desc: | |
| // Dumps the image $img to the output stream. | |
| // Currently supports png, jpg, and gif formats. If | |
| // $do_headers is true, the function will automatically | |
| // write out the Content-type header for the image | |
| // type you are saving as. | |
| //===================================================== | |
| public function output(&$img, $type, $do_headers = true) | |
| { | |
| $type = strtolower($type); | |
| if ($type == 'gif') { | |
| $check = 'writegif'; | |
| } | |
| else { | |
| $check = $type; | |
| } | |
| if (!$check OR !$this->supports["$check"]) { | |
| throw new Exception("There is no support for outputting '$type' files."); | |
| } | |
| switch ($type) { | |
| case 'jpeg': | |
| case 'jpg': | |
| $mime = 'image/jpeg'; | |
| $func = 'imageJPEG'; | |
| break; | |
| case 'png': | |
| $mime = 'image/png'; | |
| $func = 'imagePNG'; | |
| break; | |
| case 'gif': | |
| $mime = 'image/gif'; | |
| $func = 'imageGIF'; | |
| } | |
| if ($do_headers) { | |
| header("Content-type: $mime"); | |
| } | |
| if (!$func($img)) { | |
| throw new Exception("Could not output the image, using $func."); | |
| } | |
| } | |
| //===================================================== | |
| // @Public: resize() | |
| // @Args: $img - Type: Image Resource; | |
| // Desc: Image to resize. | |
| // $target_width - Type: Integer; | |
| // Desc: Optional. Specifies the maximum width | |
| // the resized image can be. | |
| // $target_height - Type: Integer; | |
| // Desc: Optional. Specifies the maximum height | |
| // the resized image can be. | |
| // @Author: Steven Harris | |
| // @Version: <{#spec.tag.float.version.var.replace#}> | |
| // @Desc: | |
| // Returns a copy of $img resized and resampled based | |
| // on the given $target_width and $target_height. If | |
| // either $target_width or $target_height are left at | |
| // 0, it will ignore that target. If both are 0, it | |
| // simply returns a copy of the image. | |
| //===================================================== | |
| public function resize(&$img, $target_width = 0, $target_height = 0) | |
| { | |
| $width = imageSX($img); | |
| $height = imageSY($img); | |
| // Not specifying any width or heights? Return a copy... | |
| if ($target_width == 0 AND $target_height == 0) { | |
| $copy = $this->create_true_color($width, $height); | |
| imageCopy($copy, $img, 0, 0, 0, 0, $width, $height); | |
| return $copy; | |
| } | |
| $x_ratio = ($target_width == 0) ? 1 : $width / $target_width; | |
| $y_ratio = ($target_height == 0) ? 1 : $height / $target_height; | |
| if ($x_ratio > $y_ratio) { | |
| $new_width = round($width / $x_ratio); | |
| $new_height = round($height / $x_ratio); | |
| } | |
| else { | |
| $new_width = round($width / $y_ratio); | |
| $new_height = round($height / $y_ratio); | |
| } | |
| $new_img = $this->create_true_color($new_width, $new_height); | |
| imageCopyResampled($new_img, $img, 0, 0, 0, 0, $new_width, $new_height, $width, $height); | |
| return $new_img; | |
| } | |
| //===================================================== | |
| // @Public: copy() | |
| // @Args: $img - Type: Image Resource; | |
| // Desc: Image to copy. | |
| // @Author: Steven Harris | |
| // @Version: <{#spec.tag.float.version.var.replace#}> | |
| // @Desc: | |
| // Returns an exact copy of the given image. For now, | |
| // this is essentially a nicer looking version of | |
| // resize($img, 0, 0) | |
| //===================================================== | |
| public function copy(&$img) | |
| { | |
| return $this->resize($img, 0, 0); | |
| } | |
| //===================================================== | |
| // @Public: destroy() | |
| // @Args: $img1 - Type: Image Resource; | |
| // Desc: Image resource to free. | |
| // $img2 | |
| // ... | |
| // @Author: Steven Harris | |
| // @Version: <{#spec.tag.float.version.var.replace#}> | |
| // @Desc: | |
| // Shortcut function that lets you free memory from | |
| // multiple images at once, by specifying multiple | |
| // image resources as arguments. | |
| //===================================================== | |
| public function destroy() | |
| { | |
| foreach (func_get_args() AS $img) { | |
| imageDestroy($img); | |
| } | |
| } | |
| // -------------------------------------------------------------- | |
| // Image State Transformation Functions | |
| // | |
| //===================================================== | |
| // @Public: move_origin() | |
| // @Args: $x - Type: Integer; | |
| // $y - Type: Integer; | |
| // @Author: Steven Harris | |
| // @Version: <{#spec.tag.float.version.var.replace#}> | |
| // @Desc: | |
| // Relatively moves the current position of the | |
| // coordinate plane's origin by $x pixels to the right, | |
| // and $y pixels down. | |
| //===================================================== | |
| public function move_origin($x, $y) | |
| { | |
| $this->plane_x = $this->plane_x + $x; | |
| $this->plane_y = $this->plane_y + $y; | |
| } | |
| //===================================================== | |
| // @Public: set_origin() | |
| // @Args: $x - Type: Integer; | |
| // $y - Type: Integer; | |
| // @Author: Steven Harris | |
| // @Version: <{#spec.tag.float.version.var.replace#}> | |
| // @Desc: Moves the coordinate plane's origin to $x, $y | |
| //===================================================== | |
| public function set_origin($x, $y) | |
| { | |
| $this->plane_x = $x; | |
| $this->plane_y = $y; | |
| } | |
| //===================================================== | |
| // @Public: move_angle() | |
| // @Args: $angle - Type: Number; | |
| // Desc: The angle to move to, in degrees. | |
| // @Author: Steven Harris | |
| // @Version: <{#spec.tag.float.version.var.replace#}> | |
| // @Desc: | |
| // Relatively adjusts the coordinate plane's angle by | |
| // $angle. | |
| //===================================================== | |
| public function move_angle($angle) | |
| { | |
| $this->plane_angle = fmod($this->plane_angle - deg2rad($angle), 2 * pi()); | |
| } | |
| //===================================================== | |
| // @Public: set_angle() | |
| // @Args: $angle - Type: Number | |
| // Desc: Angle to set to, specified in degrees. | |
| // @Author: Steven Harris | |
| // @Version: <{#spec.tag.float.version.var.replace#}> | |
| // @Desc: Sets the coordinate plane's angle to $angle. | |
| //===================================================== | |
| public function set_angle($angle) | |
| { | |
| $this->plane_angle = -1 * fmod(deg2rad($angle), 2 * pi()); | |
| } | |
| //===================================================== | |
| // @Public: save_state() | |
| // @Args: None | |
| // @Author: Steven Harris | |
| // @Version: <{#spec.tag.float.version.var.replace#}> | |
| // @Desc: | |
| // Pushes the current coordinate plane transformations | |
| // onto $state_stack. | |
| //===================================================== | |
| public function save_state() | |
| { | |
| $this->state_stack[] = array($this->plane_x, $this->plane_y, $this->plane_angle); | |
| } | |
| //===================================================== | |
| // @Public: restore_state() | |
| // @Args: None | |
| // @Author: Steven Harris | |
| // @Version: <{#spec.tag.float.version.var.replace#}> | |
| // @Desc: | |
| // Restores the previous state of the coordinate | |
| // transformation from the top of {vl $state_stack}. If | |
| // there is no previous state, the coordinate plane's | |
| // origin and angle will all be reset to zero. | |
| //===================================================== | |
| public function restore_state() | |
| { | |
| if (sizeof($this->state_stack) == 0) { | |
| $this->plane_x = $this->plane_y = $this->plane_angle = 0; | |
| $this->line_thickness = 1; | |
| return; | |
| } | |
| list($this->plane_x, $this->plane_y, $this->plane_angle) = array_pop($this->state_stack); | |
| } | |
| //===================================================== | |
| // @Private: get_image_coordinates() | |
| // @Args: $x - Type: Integer; | |
| // Desc: X coordinate on the current plane. | |
| // $y - Type: Integer; | |
| // Desc: Y coordinate on the current plane. | |
| // @Author: Steven Harris | |
| // @Version: <{#spec.tag.float.version.var.replace#}> | |
| // @Desc: | |
| // Returns the true image coordinates from the | |
| // transformed coordinates, based on the current plane | |
| // transformations. | |
| //===================================================== | |
| private function get_image_coordinates($x, $y) | |
| { | |
| if ($this->angle_is_zero()) { | |
| return array($x + $this->plane_x, $y + $this->plane_y); | |
| } | |
| $angle = atan2($y, $x) + $this->plane_angle; | |
| $length = sqrt($x*$x + $y*$y); | |
| $x = $this->plane_x + round(cos($angle) * $length); | |
| $y = $this->plane_y + round(sin($angle) * $length); | |
| return array($x, $y); | |
| } | |
| //===================================================== | |
| // @Private: angle_is_zero() | |
| // @Args: None | |
| // @Author: Steven Harris | |
| // @Version: <{#spec.tag.float.version.var.replace#}> | |
| // @Desc: | |
| // Returns true if the current plane's angle is within | |
| // 1/100000 of zero. This is mainly to account for | |
| // floating point errors. | |
| //===================================================== | |
| private function angle_is_zero() | |
| { | |
| return ($this->plane_angle > -0.00001 AND $this->plane_angle < 0.00001); | |
| } | |
| // -------------------------------------------------------------- | |
| // Drawing Functions | |
| // | |
| //===================================================== | |
| // @Public: draw_vector() | |
| // @Args: $img - Type: Image Resource; | |
| // Desc: Image to draw on. | |
| // $x - Type: Integer; | |
| // Desc: X position of the vector's origin | |
| // $y - Type: Integer; | |
| // Desc: Y position of the vector's origin | |
| // $angle - Type: Number; | |
| // Desc: Angle to draw the line. | |
| // $length - Type: Integer; | |
| // Desc: Length of the line. | |
| // $color - Type: Integer; | |
| // Desc: Color to draw the line. | |
| // @Author: Steven Harris | |
| // @Version: <{#spec.tag.float.version.var.replace#}> | |
| // @Desc: | |
| // Draws a line on the <strong>default</strong> | |
| // coordinate plane from the origin at the point | |
| // ($x, $y), at the angle $angle, with a length of | |
| // $length, and with the color $color. | |
| //===================================================== | |
| public function draw_vector(&$img, $x, $y, $angle, $length, $color) | |
| { | |
| $x2 = $x + round(cos($angle) * $length); | |
| $y2 = $y + round(sin($angle) * $length); | |
| return imageLine($img, $x, $y, $x2, $y2, $color); | |
| } | |
| //===================================================== | |
| // @Public: draw_pixel() | |
| // @Args: $img - Type: Image Resource; | |
| // Desc: Image to draw on. | |
| // $x - Type: Integer; | |
| // Desc: X position to draw pixel. | |
| // $y - Type: Integer; | |
| // Desc: Y position to draw pixel. | |
| // $color - Type: Integer; | |
| // Desc: Color of the pixel to draw. | |
| // @Author: Steven Harris | |
| // @Version: <{#spec.tag.float.version.var.replace#}> | |
| // @Desc: | |
| // Draws a point on $img at ($x, $y) with the color | |
| // $color in the current coordinate plane. | |
| //===================================================== | |
| public function draw_pixel(&$img, $x, $y, $color) | |
| { | |
| list($x, $y) = $this->get_image_coordinates($x, $y); | |
| return imageSetPixel($img, $x, $y, $color); | |
| } | |
| //===================================================== | |
| // @Public: draw_line() | |
| // @Args: $img - Type: Image Resource; | |
| // Desc: Image to draw on. | |
| // $x1 - Type: Integer; | |
| // Desc: X coordinate of first point. | |
| // $y1 - Type: Integer; | |
| // Desc: Y coordinate of first point. | |
| // $x2 - Type: Integer; | |
| // Desc: X coordinate of second point. | |
| // $y2 - Type: Integer; | |
| // Desc: Y coordinate of second point. | |
| // $color - Type: Integer; | |
| // Desc: Color of the line to draw. | |
| // @Author: Steven Harris | |
| // @Version: <{#spec.tag.float.version.var.replace#}> | |
| // @Desc: | |
| // Draws a line on $img from the point ($x1, $y1) to | |
| // ($x2, $y2) with the color $color, with respect to | |
| // the current plane transformation. | |
| //===================================================== | |
| public function draw_line(&$img, $x1, $y1, $x2, $y2, $color) | |
| { | |
| list($x1, $y1) = $this->get_image_coordinates($x1, $y1); | |
| list($x2, $y2) = $this->get_image_coordinates($x2, $y2); | |
| return imageLine($img, $x1, $y1, $x2, $y2, $color); | |
| } | |
| //===================================================== | |
| // @Public: draw_rect() | |
| // @Args: $img - Type: Image Resource; | |
| // Desc: Image to draw on. | |
| // $x1 - Type: Integer; | |
| // Desc: X coordinate of top left point. | |
| // $y1 - Type: Integer; | |
| // Desc: Y coordinate of top left point. | |
| // $x2 - Type: Integer; | |
| // Desc: X coordinate of bottom right point. | |
| // $y2 - Type: Integer; | |
| // Desc: Y coordinate of bottom right point. | |
| // $color - Type: Integer; | |
| // Desc: Color of the rectangle to draw. | |
| // @Author: Steven Harris | |
| // @Version: <{#spec.tag.float.version.var.replace#}> | |
| // @Desc: | |
| // Draws a rectangle on $img from the top-left point | |
| // ($x1, $y1) to the bottom-right point ($x2, $y2) | |
| // with the color $color, with respect to the current | |
| // plane transformation. | |
| //===================================================== | |
| public function draw_rect(&$img, $x1, $y1, $x2, $y2, $color) | |
| { | |
| // if we're on an angle, create a custom polygon | |
| if (!$this->angle_is_zero()) { | |
| $points = array($x1, $y1, $x2, $y1, $x2, $y2, $x1, $y2); | |
| return $this->draw_polygon($img, $points, 4, $color); | |
| } | |
| // otherwise, just translate and draw a basic rect | |
| list($x1, $y1) = $this->get_image_coordinates($x1, $y1); | |
| list($x2, $y2) = $this->get_image_coordinates($x2, $y2); | |
| return imageRectangle($img, $x1, $y1, $x2, $y2, $color); | |
| } | |
| //===================================================== | |
| // @Public: draw_filled_rect() | |
| // @Args: $img - Type: Image Resource; | |
| // Desc: Image to draw on. | |
| // $x1 - Type: Integer; | |
| // Desc: X coordinate of top left point. | |
| // $y1 - Type: Integer; | |
| // Desc: Y coordinate of top left point. | |
| // $x2 - Type: Integer; | |
| // Desc: X coordinate of bottom right point. | |
| // $y2 - Type: Integer; | |
| // Desc: Y coordinate of bottom right point. | |
| // $color - Type: Integer; | |
| // Desc: Fill color of the rectangle to draw. | |
| // @Author: Steven Harris | |
| // @Version: <{#spec.tag.float.version.var.replace#}> | |
| // @Desc: | |
| // Draws a filled rectangle on $img from the top-left | |
| // point ($x1, $y1) to the bottom-right point | |
| // ($x2, $y2) with the color $color, with respect to | |
| // the current plane transformation. | |
| //===================================================== | |
| public function draw_filled_rect(&$img, $x1, $y1, $x2, $y2, $color) | |
| { | |
| if (!$this->angle_is_zero()) { | |
| $points = array($x1, $y1, $x2, $y1, $x2, $y2, $x1, $y2); | |
| return $this->draw_filled_polygon($img, $points, 4, $color); | |
| } | |
| list($x1, $y1) = $this->get_image_coordinates($x1, $y1); | |
| list($x2, $y2) = $this->get_image_coordinates($x2, $y2); | |
| return imageFilledRectangle($img, $x1, $y1, $x2, $y2, $color); | |
| } | |
| //===================================================== | |
| // @Public: draw_polygon() | |
| // @Args: $img - Type: Image Resource; | |
| // Desc: Image to draw on. | |
| // $vertices - Type: Array; | |
| // Desc: Array of integers, where each pair of | |
| // two is a point of the polygon. | |
| // $num - Type: Integer; | |
| // Desc: The number of full points in $vertices. | |
| // $color - Type: Integer; | |
| // Desc: Color of the polygon to draw. | |
| // @Author: Steven Harris | |
| // @Version: <{#spec.tag.float.version.var.replace#}> | |
| // @Desc: | |
| // Draws a polygon colored with $color on $img, with | |
| // respect to the current plane transformation. | |
| // $vertices is an array of points in the format | |
| // array(x0, y0, x1, y1, x2, y2, ...); $num is the | |
| // number of points, essentially sizeof($vertices) / 2 | |
| //===================================================== | |
| public function draw_polygon(&$img, $vertices, $num, $color) | |
| { | |
| $size = $num * 2; | |
| for ($i = 0; $i < $size; $i += 2) { | |
| $xy = $this->get_image_coordinates($vertices[$i], $vertices[$i + 1]); | |
| $vertices[$i] = $xy[0]; | |
| $vertices[$i + 1] = $xy[1]; | |
| } | |
| return imagePolygon($img, $vertices, $num, $color); | |
| } | |
| //===================================================== | |
| // @Public: draw_filled_polygon() | |
| // @Args: $img - Type: Image Resource; | |
| // Desc: Image to draw on. | |
| // $vertices - Type: Array; | |
| // Desc: Array of integers, where each pair of | |
| // two is a point of the polygon. | |
| // $num - Type: Integer; | |
| // Desc: The number of full points in $vertices. | |
| // $color - Type: Integer; | |
| // Desc: Fill color of the polygon to draw. | |
| // @Author: Steven Harris | |
| // @Version: <{#spec.tag.float.version.var.replace#}> | |
| // @Desc: | |
| // Draws a filled polygon filled with $color on $img, | |
| // with respect to the current plane transformation. | |
| // $vertices is an array of points in the format | |
| // array(x0, y0, x1, y1, x2, y2, ...); $num is the | |
| // number of points, essentially sizeof($vertices) / 2 | |
| //===================================================== | |
| public function draw_filled_polygon(&$img, $vertices, $num, $color) | |
| { | |
| $size = $num * 2; | |
| for ($i = 0; $i < $size; $i += 2) { | |
| $xy = $this->get_image_coordinates($vertices[$i], $vertices[$i + 1]); | |
| $vertices[$i] = $xy[0]; | |
| $vertices[$i + 1] = $xy[1]; | |
| } | |
| return imageFilledPolygon($img, $vertices, $num, $color); | |
| } | |
| //===================================================== | |
| // @Public: draw_image() | |
| // @Args: $dest - Type: Image Resource; | |
| // Desc: Destination image, that $src will be | |
| // drawn on. | |
| // $src - Type: Image Resource; | |
| // Desc: Image that will be drawn on $dest. | |
| // $dest_x - Type: Integer; | |
| // Desc: X coordinate of where $src will be | |
| // drawn on $dest. | |
| // $dest_y - Type: Integer; | |
| // Desc: Y coordinate of where $src will be | |
| // draw on $dest. | |
| // $src_x - Type: Integer; | |
| // Desc: X coord of the top-left point of the | |
| // rectangular area to clip out of $src. | |
| // $src_y - Type: Integer; | |
| // Desc: Y coord of the top-left point of the | |
| // rectangular area to clip out of $src. | |
| // $src_w - Type: Integer; | |
| // Desc: Width of the rectangular area to clip | |
| // out of $src. | |
| // $src_h - Type: Integer; | |
| // Desc: Height of the rectangular area to clip | |
| // out of $src. | |
| // $opacity - Type: Integer; | |
| // Desc: Optional. Opacity of the drawn image. | |
| // 0 makes it invisible, 100 draws it | |
| // fully opaque. Defaults to 100. | |
| // @Author: Steven Harris | |
| // @Version: <{#spec.tag.float.version.var.replace#}> | |
| // @Desc: | |
| // Draws the image $src onto $dest, at the point | |
| // ($dest_x, $dest_y), and with respect to the current | |
| // plane transformation. You can limit a section of | |
| // $src to be copied by specifying the start point, and | |
| // width/height, with the args $src_x, $src_y, $src_w, | |
| // and $src_h. $opacity is used when you want to make | |
| // the source image partially transparent on top of the | |
| // destination. $opacity ranges from 0 to 100 - when 0, | |
| // this function does nothing; when 100, the source | |
| // image is copied with no transparency (other than a | |
| // possible background) | |
| // WARNING - This function currently ignores the | |
| // rotation aspect. It will still draw on the correct | |
| // location, but it will not be rotated. | |
| // | |
| // 6/7/2010 - Shit, really? Thought it worked on PHP 5 at least. | |
| //===================================================== | |
| public function draw_image(&$dest, &$src, $dest_x, $dest_y, $src_x, | |
| $src_y, $src_w, $src_h, $opacity = 100) | |
| { | |
| // take care of the nothing situation | |
| if ($opacity == 0) { | |
| return true; | |
| } | |
| // the simple situation | |
| // if ($this->_angle_is_zero()) { | |
| list($x, $y) = $this->get_image_coordinates($dest_x, $dest_y); | |
| return imageCopyMerge($dest, $src, $x, $y, $src_x, $src_y, $src_w, $src_h, $opacity); | |
| // } | |
| /* | |
| This is... still sorta broken, though I got image clipping and transparency | |
| to work, though transparency is only working in PHP5. NOTE to people | |
| reading this comment, I mean transparency as in when rotating. Transparency | |
| DOES WORK when there is no rotation involved. | |
| angled plane, eh? oh boy, here we go. | |
| First we need to find the maximum dimensions of the source image section's | |
| bounding rectangle. This can be done by finding the distance from | |
| the section's center to one of its corners. Multiply that by two, and | |
| you'll have the diameter of the circle the image must be contained | |
| within. Use that value to make a square image. Maybe later I'll | |
| calculate the actual bounding rect of the rotated image, but this | |
| will do for now. | |
| On the created image, the background must be set to a fully transparent | |
| color, so when it is copied over, there isn't any ugly box around | |
| the pasted image. Then, perform an imageRotate to the angle of the | |
| current coordinate plane, making the bgd_color 0x7f000000 for | |
| complete transparency. | |
| After that, we will need to calculate the offset from the original | |
| top-left of the source image, to the top left of the temp image. | |
| */ | |
| $center_x = $src_x + $src_w/2; | |
| $center_y = $src_y + $src_h/2; | |
| // distance from top left | |
| $radius = sqrt($center_x*$center_x + $center_y*$center_y); | |
| $tmp_img_dims = ceil($radius * 2); | |
| // make image now | |
| $tmp_img = $this->create_true_color($tmp_img_dims, $tmp_img_dims); | |
| imageAlphaBlending($tmp_img, false); | |
| imageFill($tmp_img, 0, 0, 0x7f000000); | |
| // we have to find the top-left of the original section and calculate the | |
| // position for the new image | |
| $x_loc = round(($tmp_img_dims / 2) - ($src_w / 2)); | |
| $y_loc = round(($tmp_img_dims / 2) - ($src_h / 2)); | |
| imageCopyMerge($tmp_img, $src, $x_loc, $y_loc, $src_x, $src_y, $src_w, $src_h, $opacity); | |
| // rotate the image now, using degrees | |
| $tmp_img = imageRotate($tmp_img, rad2deg(-1 * $this->plane_angle), 0x7f000000); | |
| // get the NEW dimensions | |
| $newx = imageSX($tmp_img); | |
| $newy = imageSY($tmp_img); | |
| // from the angle and radius, calculate the new (x,y) pair in relation to img center. | |
| // find the base angle to the top-left corner, before anything is done. | |
| $base_angle = atan2(-1 * $src_h/2, -1 * $src_w/2); | |
| $angle = $base_angle - $this->plane_angle; | |
| $x = round(cos($angle) * $radius); | |
| $y = round(sin($angle) * $radius); | |
| // get the final location of the (x,y) pair in relation to 0,0 of the tmp image | |
| $final_x = $x + round($radius); | |
| $final_y = $y + round($radius); | |
| // we need to draw the temp image back to the original at a point that will | |
| // allow the source image's top-left corner to be at the plot point | |
| // draw it at $final_x to the left and $final_y to the top to shift correctly | |
| list($plot_x, $plot_y) = $this->get_image_coordinates($dest_x, $dest_y); | |
| $return = imageCopy($dest, $tmp_img, $plot_x - $final_x, $plot_y - $final_y, | |
| 0, 0, $newx, $newy); | |
| imageDestroy($tmp_img); | |
| return $return; | |
| } | |
| //===================================================== | |
| // @Public: draw_border() | |
| // @Args: $img - Type: Image Resource; | |
| // Desc: Image to draw on. | |
| // $color - Type: Integer; | |
| // Desc: Color of the border to draw. | |
| // $thickness - Type: Integer; | |
| // Desc: Optional. Specifies the thickness of | |
| // the border to draw. Defaults to 1. | |
| // $overlap - Type: Boolean; | |
| // Desc: Optional. Specifies whether to draw the | |
| // border on top of the image (true), or | |
| // around the image (false). | |
| // Defaults to true. | |
| // @Author: Steven Harris | |
| // @Version: <{#spec.tag.float.version.var.replace#}> | |
| // @Desc: | |
| // Draws a border of color $color around $img, with a | |
| // thickness of $thickness. If $overlap is true, the | |
| // border will be drawn on top of the image, covering | |
| // the edges. If false, the entire image is preserved, | |
| // and the border is drawn on the outside, which will | |
| // result in each of the image's dimensions growing by | |
| // $thickness * 2. | |
| //===================================================== | |
| public function draw_border(&$img, $color, $thickness = 1, $overlap = true) | |
| { | |
| if ($thickness < 0) { | |
| $thickness = 0; | |
| } | |
| if ($thickness == 0) { | |
| // no change... return | |
| return; | |
| } | |
| $sx = imageSX($img) - 1; | |
| $sy = imageSY($img) - 1; | |
| if ($overlap) { | |
| // left, top, right, bottom | |
| imageFilledRectangle($img, 0, 0, $thickness - 1, $sy, $color); | |
| imageFilledRectangle($img, $thickness, 0, $sx - $thickness, $thickness - 1, $color); | |
| imageFilledRectangle($img, $sx - $thickness + 1, 0, $sx, $sy, $color); | |
| imageFilledRectangle($img, $thickness, $sy - $thickness + 1, $sx - $thickness, $sy, $color); | |
| return; | |
| } | |
| // nifty thing - let the background serve as a border! | |
| $sx++; | |
| $sy++; | |
| $tmp = $this->create_true_color($sx + $thickness*2, $sy + $thickness*2, $color); | |
| imageCopy($tmp, $img, $thickness, $thickness, 0, 0, $sx, $sy); | |
| $img = $tmp; | |
| // did not destroy $tmp, since resources are ALWAYS passed by reference | |
| // ...which almost makes me wonder why every function has &$img | |
| // ah well, no harm done I suppose. | |
| } | |
| //===================================================== | |
| // @Public: draw_arc() | |
| // @Args: $img - Type: Image Resource; | |
| // Desc: Image to draw on. | |
| // $cx - Type: Integer; | |
| // Desc: X coordinate of the arc's center point. | |
| // $cy - Type: Integer; | |
| // Desc: Y coordinate of the arc's center point. | |
| // $width - Type: Integer; | |
| // Desc: Horizontal diameter of the arc. | |
| // $height - Type: Integer; | |
| // Desc: Vertical diameter of the arc. | |
| // $start - Type: Number; | |
| // Desc: Start angle of the arc, in degrees. | |
| // $end - Type: Number; | |
| // Desc: End angle of the arc, in degrees. | |
| // $color - Type: Integer; | |
| // Desc: Color of the arc to draw. | |
| // $clockwise - Type: Boolean; | |
| // Desc: Optional. If true, the arc is drawn | |
| // clockwise. If false, it is drawn | |
| // counter-clockwise. Defaults to true. | |
| // @Author: Steven Harris | |
| // @Version: <{#spec.tag.float.version.var.replace#}> | |
| // @Desc: | |
| // Draws an elliptical arc on $img with the color | |
| // $color. The arc is centered on the point ($cx, $cy), | |
| // with a horizontal diameter of $width, and vertical | |
| // diameter of $height. The arc is drawn from the angle | |
| // $start to $end, both specified in degrees. If | |
| // $clockwise is false, it essentially reverses the | |
| // order of $start and $end in the imageArc call. The | |
| // center point is affected by the current plane | |
| // transformations, and the angle parameters are | |
| // affected by the current plane's angle. For example, | |
| // if the plane angle was 45 degrees, and you specified | |
| // $start and $end as 0 and 90, the actual arc would be | |
| // drawn from 45 to 135. | |
| //===================================================== | |
| public function draw_arc(&$img, $cx, $cy, $width, $height, $start, $end, $color, $clockwise = true) | |
| { | |
| list($x, $y) = $this->get_image_coordinates($cx, $cy); | |
| if ($clockwise) { | |
| return imageArc($img, $x, $y, $width, $height, rad2deg($this->plane_angle) - $start, | |
| rad2deg($this->plane_angle) - $end, $color); | |
| } | |
| return imageArc($img, $x, $y, $width, $height, rad2deg($this->plane_angle) - $end, | |
| rad2deg($this->plane_angle) - $start, $color); | |
| } | |
| //===================================================== | |
| // @Public: draw_filled_arc() | |
| // @Args: $img - Type: Image Resource; | |
| // Desc: Image to draw on. | |
| // $cx - Type: Integer; | |
| // Desc: X coordinate of the arc's center point. | |
| // $cy - Type: Integer; | |
| // Desc: Y coordinate of the arc's center point. | |
| // $width - Type: Integer; | |
| // Desc: Horizontal diameter of the arc. | |
| // $height - Type: Integer; | |
| // Desc: Vertical diameter of the arc. | |
| // $start - Type: Number; | |
| // Desc: Start angle of the arc, in degrees. | |
| // $end - Type: Number; | |
| // Desc: End angle of the arc, in degrees. | |
| // $color - Type: Integer; | |
| // Desc: Fill color of the arc to draw. | |
| // $clockwise - Type: Boolean; | |
| // Desc: Optional. If true, the arc is drawn | |
| // clockwise. If false, it is drawn | |
| // counter-clockwise. Defaults to true. | |
| // @Author: Steven Harris | |
| // @Version: <{#spec.tag.float.version.var.replace#}> | |
| // @Desc: | |
| // Draws a filled elliptical arc on $img with the color | |
| // $color. The arc is centered on the point ($cx, $cy), | |
| // with a horizontal diameter of $width, and vertical | |
| // diameter of $height. The arc is drawn from the angle | |
| // $start to $end, both specified in degrees. If | |
| // $clockwise is false, it essentially reverses the | |
| // order of $start and $end in the imageArc call. The | |
| // center point is affected by the current plane | |
| // transformations, and the angle parameters are | |
| // affected by the current plane's angle. For example, | |
| // if the plane angle was 45 degrees, and you specified | |
| // $start and $end as 0 and 90, the actual arc would be | |
| // drawn from 45 to 135. | |
| //===================================================== | |
| public function draw_filled_arc(&$img, $cx, $cy, $width, $height, $start, $end, $color, $clockwise = true) | |
| { | |
| list($x, $y) = $this->get_image_coordinates($cx, $cy); | |
| if ($clockwise) { | |
| return imageArc($img, $x, $y, $width, $height, rad2deg($this->plane_angle) - $start, | |
| rad2deg($this->plane_angle) - $end, $color); | |
| } | |
| return imageArc($img, $x, $y, $width, $height, rad2deg($this->plane_angle) - $end, | |
| rad2deg($this->plane_angle) - $start, $color); | |
| } | |
| //===================================================== | |
| // @Public: draw_ellipse() | |
| // @Args: $img - Type: Image Resource; | |
| // Desc: Image to draw on. | |
| // $cx - Type: Integer; | |
| // Desc: X coordinate of the ellipse's center. | |
| // $cy - Type: Integer; | |
| // Desc: Y coordinate of the ellipse's center. | |
| // $width - Type: Integer; | |
| // Desc: Horizontal diameter of the ellipse. | |
| // $height - Type: Integer; | |
| // Desc: Vertical diameter of the ellipse. | |
| // $color - Type: Integer; | |
| // Desc: Color of the ellipse to draw. | |
| // @Author: Steven Harris | |
| // @Version: <{#spec.tag.float.version.var.replace#}> | |
| // @Desc: | |
| // Draws an ellipse at ($cx, $cy) with horizontal | |
| // diameter $width and vertical diameter $height, with | |
| // the color $color. | |
| // WARNING - Like draw_image(), you currently cannot | |
| // draw a rotated ellipse. Unless you uncomment the angle_is_zero() stuff maybe? ;) | |
| //===================================================== | |
| public function draw_ellipse(&$img, $cx, $cy, $width, $height, $color) | |
| { | |
| // if ($this->_angle_is_zero()) { | |
| list($x, $y) = $this->get_image_coordinates($cx, $cy); | |
| return imageEllipse($img, $x, $y, $width, $height, $color); | |
| // } | |
| /* | |
| Here we go again, shouldn't be as complex as draw_image though. | |
| Find out the max of $width and $height, and use that as dimensions | |
| for a square image. Plot the ellipse in the middle of that image, | |
| then rotate to the desired amount. Copymerge the image over, making | |
| sure the center of the temp image lines up with the wanted destination | |
| points on the original image. | |
| Since plotting rotated images only appears to work in PHP5 so far, | |
| the rest of this code will be dead sorta. | |
| */ | |
| $tmp_size = max($width, $height); | |
| $tmp_img = $this->create_true_color($tmp_size + 1, $tmp_size + 1); | |
| // hopefully create a literal transparent background? | |
| imageAlphaBlending($tmp_img, false); | |
| imageFill($tmp_img, 0, 0, 0x7f000000); | |
| // plot ellipse | |
| imageEllipse($tmp_img, $tmp_size / 2, $tmp_size / 2, $width, $height, $color); | |
| // rotate | |
| $tmp_img = imageRotate($tmp_img, rad2deg(-1 * $this->plane_angle), 0x7f000000); | |
| // the coordinates of where the center of the image needs to be | |
| list($center_x, $center_y) = $this->_get_image_coordinates($cx, $cy); | |
| $newx = imageSX($tmp_img); | |
| $newy = imageSY($tmp_img); | |
| // the image will be plotted up and to the left of the center | |
| $plot_x = $center_x - $newx/2; | |
| $plot_y = $center_y - $newy/2; | |
| $return = imageCopy($img, $tmp_img, $plot_x, $plot_y, 0, 0, $newx, $newy); | |
| $this->destroy($tmp_img); | |
| return $return; | |
| } | |
| //===================================================== | |
| // @Public: draw_filled_ellipse() | |
| // @Args: $img - Type: Image Resource; | |
| // Desc: Image to draw on. | |
| // $cx - Type: Integer; | |
| // Desc: X coordinate of the ellipse's center. | |
| // $cy - Type: Integer; | |
| // Desc: Y coordinate of the ellipse's center. | |
| // $width - Type: Integer; | |
| // Desc: Horizontal diameter of the ellipse. | |
| // $height - Type: Integer; | |
| // Desc: Vertical diameter of the ellipse. | |
| // $color - Type: Integer; | |
| // Desc: Fill color of the ellipse to draw. | |
| // @Author: Steven Harris | |
| // @Version: <{#spec.tag.float.version.var.replace#}> | |
| // @Desc: | |
| // Draws a filled ellipse at ($cx, $cy) with horizontal | |
| // diameter $width and vertical diameter $height, with | |
| // the color $color. | |
| // WARNING - Like draw_image(), you currently cannot | |
| // draw a rotated ellipse. | |
| // Like hell I ca--oh, didn't add the angle code in here. :( | |
| //===================================================== | |
| public function draw_filled_ellipse(&$img, $cx, $cy, $width, $height, $color) | |
| { | |
| list($x, $y) = $this->get_image_coordinates($cx, $cy); | |
| return imageFilledEllipse($img, $x, $y, $width, $height, $color); | |
| // see above function for note on rotations | |
| } | |
| //===================================================== | |
| // @Public: draw_ttf_string() | |
| // @Args: $img - Type: Image Resource; | |
| // Desc: Image to draw on. | |
| // $font - Type: String; | |
| // Desc: Path to the .ttf font file, without the | |
| // ".ttf" extension. | |
| // $x - Type: Integer; | |
| // Desc: X coordinate of the lower-left point | |
| // of the first character in the string. | |
| // $y - Type: Integer; | |
| // Desc: Y coordinate of the lower-left point | |
| // of the first character in the string. | |
| // $text - Type: String; | |
| // Desc: The text to write to the image. | |
| // $color - Type: Integer; | |
| // Desc: Color of the text to draw. | |
| // $size - Type: Number; | |
| // Desc: Font size of the text to draw. | |
| // $angle - Type: Number; | |
| // Desc: Optional. Combined with the current | |
| // angle to determine the angle the text | |
| // is drawn at. Specified in degrees. | |
| // Defaults to 0. | |
| // @Author: Steven Harris | |
| // @Version: <{#spec.tag.float.version.var.replace#}> | |
| // @Desc: | |
| // Draws a Truetype Font string $text on $img, using | |
| // the font $font, color $color, font size $size, and | |
| // at the angle $angle, with respect to the current | |
| // plane tranformations. The point ($x, $y) determines | |
| // the lower-left point of the first character in the | |
| // string. The return value is a multidimensional | |
| // integer array representing the bounding box of the | |
| // drawn text, in the form of | |
| // [0] => array(lower left x, lower left y) | |
| // [1] => array(lower right x, lower right y) | |
| // [2] => array(upper right x, upper right y) | |
| // [3] => array(upper left x, upper left y) | |
| //===================================================== | |
| public function draw_ttf_string(&$img, $font, $x, $y, $text, $color, $size, $angle = 0) | |
| { | |
| // Can we even use TTF fonts? | |
| if (!$this->supports['freetype']) { | |
| throw new Exception('The GD library you have does not support TrueType fonts.'); | |
| } | |
| // no .ttf extension required | |
| $font = preg_replace('#\.ttf$#', '', $font); | |
| list($x, $y) = $this->get_image_coordinates($x, $y); | |
| // fix: 8/23/06 23:10 - for some reason I thought imagettftext took radians. | |
| $return = imageTTFText($img, $size, rad2deg($this->plane_angle) - $angle, $x, $y, $color, $font, $text); | |
| // make the return array look nicer... | |
| $ret = array(); | |
| for ($i = 0; $i < 8; $i++) { | |
| $ret[ intval($i / 2) ][ $i % 2 ] = $return[$i]; | |
| } | |
| return $ret; | |
| } | |
| //===================================================== | |
| // @Public: draw_string() | |
| // @Args: $img - Type: Image Resource; | |
| // Desc: Image to draw on. | |
| // $font - Type: Integer; | |
| // Desc: Index of the GD font to use, ranges | |
| // from 1 to 5, 1 being tiny, 5 being big. | |
| // $x - Type: Integer; | |
| // Desc: X coordinate of the top-left corner of | |
| // the first character in the string. | |
| // $y - Type: Integer; | |
| // Desc: Y coordinate of the top-left corner of | |
| // the first character in the string. | |
| // $text - Type: String; | |
| // Desc: The text to draw. | |
| // $color - Type: Integer; | |
| // Desc: Color of the text to draw. | |
| // @Author: Steven Harris | |
| // @Version: <{#spec.tag.float.version.var.replace#}> | |
| // @Desc: | |
| // Draws a string $text in a plain GD font numbered | |
| // $font, on $img, at the point ($x, $y), in the color | |
| // $color. This draws with respect to the plane | |
| // transformation, but not at an angle. If there are | |
| // any \n characters in the string, the text will be | |
| // drawn on multiple lines, split at each \n, and | |
| // spaced according to the output of | |
| // imageFontHeight($font) | |
| //===================================================== | |
| public function draw_string(&$img, $font, $x, $y, $text, $color) | |
| { | |
| list($x, $y) = $this->get_image_coordinates($x, $y); | |
| // no newlines, just output | |
| if (strpos($text, "\n") === false) { | |
| return imageString($img, $font, $x, $y, $text, $color); | |
| } | |
| // we have newlines | |
| // split up the string and go down by $height at each newline | |
| $height = imageFontHeight($font); | |
| $strings = explode("\n", $text); | |
| $test = 0; | |
| foreach ($strings AS $str) { | |
| $test += (int) imageString($img, $font, $x, $y, $str, $color); // (int) true = 1 | |
| $y += $height; | |
| } | |
| return ($test == sizeof($strings)); | |
| } | |
| // -------------------------------------------------------------- | |
| // Effect Functions | |
| // | |
| //===================================================== | |
| // @Public: effect_mosiac() | |
| // @Args: $img - Type: Image Resource; | |
| // Desc: Original image. | |
| // $block_size - Type: Integer; | |
| // Desc: Optional. Specifies the side lengths of | |
| // the magnified pixels. Defaults to 3. | |
| // @Author: Steven Harris | |
| // @Version: <{#spec.tag.float.version.var.replace#}> | |
| // @Desc: | |
| // Returns an image with a basic mosiac effect applied | |
| // to $img. $block_size specifies the side lengths of | |
| // the created pixel squares. | |
| //===================================================== | |
| public function effect_mosiac(&$img, $block_size = 3) | |
| { | |
| $sx = imageSX($img); | |
| $sy = imageSY($img); | |
| $new_img = $this->create_true_color($sx, $sy); | |
| // first we need to loop through each block... | |
| for ($x = 0; $x < $sx; $x += $block_size) { | |
| for ($y = 0; $y < $sy; $y += $block_size) { | |
| // THEN loop through the pixels in each block... | |
| $limitx = ($x + $block_size > $sx) ? $sx : $x + $block_size; | |
| $limity = ($y + $block_size > $sy) ? $sy : $y + $block_size; | |
| // the limit(x|y) vars are used in the case of the last pixels | |
| // not creating a perfect square with $block_size lengths | |
| $colors = array(); | |
| for ($px = $x; $px < $limitx; $px++) { | |
| for ($py = $y; $py < $limity; $py++) { | |
| $colors[] = imageColorAt($img, $px, $py); | |
| } | |
| } | |
| $color = $this->color_average($colors); | |
| imageFilledRectangle($new_img, $x, $y, $x + $block_size, $y + $block_size, $color); | |
| } | |
| } | |
| return $new_img; | |
| } | |
| //===================================================== | |
| // @Public: effect_scatter() | |
| // @Args: $img - Type: Image Resource; | |
| // Desc: Original image. | |
| // $distortion - Type: Integer; | |
| // Desc: Optional. Specifies the range in pixels | |
| // that pixels can be swapped between. | |
| // Defaults to 4. | |
| // @Author: Steven Harris | |
| // @Version: <{#spec.tag.float.version.var.replace#}> | |
| // @Desc: | |
| // Returns an image with pixels swapped around with a | |
| // maximum range of $distortion. | |
| //===================================================== | |
| public function effect_scatter(&$img, $distortion = 4) | |
| { | |
| $sx = imageSX($img); | |
| $sy = imageSY($img); | |
| $newimg = $this->create_true_color($sx, $sy); | |
| for ($x = 0; $x < $sx; ++$x) { | |
| for ($y = 0; $y < $sy; ++$y) { | |
| $rand_x = mt_rand(-$distortion, $distortion); | |
| $rand_y = mt_rand(-$distortion, $distortion); | |
| if ($x + $rand_x >= $sx OR $x + $rand_x < 0 OR | |
| $y + $rand_y >= $sy OR $y + $rand_y < 0) | |
| { | |
| continue; | |
| } | |
| imageSetPixel($newimg, $x, $y, imageColorAt($img, $x + $rand_x, $y + $rand_y)); | |
| imageSetPixel($newimg, $x + $rand_x, $y + $rand_y, imageColorAt($img, $x, $y)); | |
| } | |
| } | |
| return $newimg; | |
| } | |
| //===================================================== | |
| // @Public: effect_ripple() | |
| // @Args: $img - Type: Image Resource; | |
| // Desc: Original image. | |
| // $period - Type: Integer; | |
| // Desc: Optional. Specifies the length in | |
| // pixels of the sine waves. | |
| // Defaults to 50. | |
| // $amplitude - Type: Integer; | |
| // Desc: Optional. Specifies the maximum offset | |
| // obtainable by the sine functions. | |
| // Defaults to 5. | |
| // $bg_color - Type: Integer; | |
| // Desc: Optional. Specifies the color used to | |
| // fill in the blank areas that will be on | |
| // the edge. Defaults to 0 (black). | |
| // @Author: Steven Harris | |
| // @Version: <{#spec.tag.float.version.var.replace#}> | |
| // @Desc: | |
| // Returns an image with a wave effect applied | |
| // horizontally and vertically. | |
| //===================================================== | |
| public function effect_ripple(&$img, $period = 50, $amplitude = 5, $bg_color = 0) | |
| { | |
| $width = imageSX($img); | |
| $height = imageSY($img); | |
| $tmp_img = $this->create_true_color($width, $height, $bg_color); | |
| $new_img = $this->create_true_color($width, $height, $bg_color); | |
| for ($i = 0; $i < $width; $i++) { | |
| imageCopy($tmp_img, $img, $i, sin(M_PI*2 * $i/$period) * $amplitude, $i, 0, 1, $height); | |
| } | |
| for ($i = 0; $i < $height; $i++) { | |
| imageCopy($new_img, $tmp_img, sin(M_PI*-2 * $i/$period) * $amplitude, $i, 0, $i, $width, 1); | |
| } | |
| $this->destroy($tmp_img); | |
| return $new_img; | |
| } | |
| //===================================================== | |
| // @Public: effect_gaussian_blur() | |
| // @Args: $img - Type: Image Resource; | |
| // Desc: Original image. | |
| // @Author: Steven Harris | |
| // @Version: <{#spec.tag.float.version.var.replace#}> | |
| // @Desc: | |
| // Returns a gaussian blurred version of $img. Based | |
| // on a portion of the effect_unsharpmask code. | |
| //===================================================== | |
| public function effect_gaussian_blur(&$img) | |
| { | |
| /* | |
| * Based on a portion of the unsharpmask code, written by | |
| * Torstein Hønsi (see "effect_unsharpmask") | |
| */ | |
| $w = imageSX($img); | |
| $h = imageSY($img); | |
| $new_img = $this->create_true_color($w, $h); | |
| imageCopy($new_img, $img, 0, 0, 0, 0, $w, $h); | |
| // do we have access to the brilliant ImageConvolution? (PHP >= 5.1) | |
| if (function_exists('imageconvolution')) { | |
| $matrix = array( | |
| array( 1, 2, 1 ), | |
| array( 2, 4, 2 ), | |
| array( 1, 2, 1 ) | |
| ); | |
| imageConvolution($new_img, $matrix, 16, 0); | |
| return $new_img; | |
| } | |
| // no? bah, do it the hard way, then. | |
| $tmp_img = $this->create_true_color($w, $h); | |
| // Gaussian blur matrix: | |
| // | |
| // 1 2 1 | |
| // 2 4 2 | |
| // 1 2 1 | |
| // | |
| ////////////////////////////////////////////////// | |
| // Move copies of the image around one pixel at the time and merge them with weight | |
| // according to the matrix. The same matrix is simply repeated for higher radii. | |
| imageCopy ($tmp_img, $new_img, 0, 0, 1, 1, $w - 1, $h - 1); // up left | |
| imageCopyMerge($tmp_img, $new_img, 1, 1, 0, 0, $w, $h, 50); // down right | |
| imageCopyMerge($tmp_img, $new_img, 0, 1, 1, 0, $w - 1, $h, 33.33333); // down left | |
| imageCopyMerge($tmp_img, $new_img, 1, 0, 0, 1, $w, $h - 1, 25); // up right | |
| imageCopyMerge($tmp_img, $new_img, 0, 0, 1, 0, $w - 1, $h, 33.33333); // left | |
| imageCopyMerge($tmp_img, $new_img, 1, 0, 0, 0, $w, $h, 25); // right | |
| imageCopyMerge($tmp_img, $new_img, 0, 0, 0, 1, $w, $h - 1, 20 ); // up | |
| imageCopyMerge($tmp_img, $new_img, 0, 1, 0, 0, $w, $h, 16.666667); // down | |
| imageCopyMerge($tmp_img, $new_img, 0, 0, 0, 0, $w, $h, 50); // center | |
| imageCopy ($new_img, $tmp_img, 0, 0, 0, 0, $w, $h); | |
| $this->destroy($tmp_img); | |
| return $new_img; | |
| } | |
| //===================================================== | |
| // @Public: effect_unsharpmask() | |
| // @Args: $img - Type: Image Resource; | |
| // Desc: Original image. | |
| // $amount - Type: Integer; | |
| // Desc: Optional. Specifies how much of the | |
| // effect you want. 100 is "normal." | |
| // Defaults to 100. | |
| // $radius - Type: Number; | |
| // Desc: Optional. Specifies the radius of the | |
| // blurring circle of the mask. | |
| // Defaults to 0.5 | |
| // $threshold - Type: Integer; | |
| // Desc: Optional. Specifies the least | |
| // difference in color values that is | |
| // allowed between the original and the | |
| // mask. Defaults to 3. | |
| // @Author: Torstein Hønsi / modified by Steven Harris | |
| // http://vikjavev.no/computing/ump.php | |
| // @Version: <{#spec.tag.float.version.var.replace#}> | |
| // @Desc: | |
| // Applies an unsharpmask, which basically sharpens the | |
| // edges of an image. Best used on scaled-down images, | |
| // to reduce some of the blur. Be forwarned, however, | |
| // that this function can be slow and memory-intensive, | |
| // and so it should only be used on smaller images. | |
| //===================================================== | |
| public function effect_unsharpmask(&$img, $amount = 80, $radius = 0.5, $threshold = 3) | |
| { | |
| ///////////////////////////////////////////////////////////// | |
| //// | |
| //// Unsharp Mask for PHP - version 2.0 | |
| //// | |
| //// Unsharp mask algorithm by Torstein Hønsi 2003-06. | |
| //// thoensi_at_netcom_dot_no. | |
| //// Please leave this notice. | |
| //// | |
| ///////////////////////////////////////////////////////////// | |
| // $img is an image that is already created within php using | |
| // imgcreatetruecolor. No url! $img must be a truecolor image. | |
| // Attempt to calibrate the parameters to Photoshop: | |
| if ($amount > 500) { | |
| $amount = 500; | |
| } | |
| $amount *= 0.016; | |
| if ($radius > 50) { | |
| $radius = 50; | |
| } | |
| $radius *= 2; | |
| if ($threshold > 255) { | |
| $threshold = 255; | |
| } | |
| $radius = abs(round($radius)); // Only integers make sense. | |
| if ($radius == 0) { | |
| return $img; | |
| } | |
| $w = imageSX($img); | |
| $h = imageSY($img); | |
| $imgCanvas = imageCreateTrueColor($w, $h); | |
| $imgCanvas2 = imageCreateTrueColor($w, $h); | |
| $imgBlur = imageCreateTrueColor($w, $h); | |
| $imgBlur2 = imageCreateTrueColor($w, $h); | |
| imageCopy($imgCanvas, $img, 0, 0, 0, 0, $w, $h); | |
| imageCopy($imgCanvas2, $img, 0, 0, 0, 0, $w, $h); | |
| // Gaussian blur matrix: | |
| // | |
| // 1 2 1 | |
| // 2 4 2 | |
| // 1 2 1 | |
| // | |
| ////////////////////////////////////////////////// | |
| imageCopy($imgBlur, $imgCanvas, 0, 0, 0, 0, $w, $h); // background | |
| for ($i = 0; $i < $radius; $i++) { | |
| if (function_exists('imageconvolution')) { // PHP >= 5.1 | |
| $matrix = array( | |
| array( 1, 2, 1 ), | |
| array( 2, 4, 2 ), | |
| array( 1, 2, 1 ) | |
| ); | |
| imageConvolution($imgCanvas, $matrix, 16, 0); | |
| } | |
| else { | |
| // Move copies of the image around one pixel at the time and merge them with weight | |
| // according to the matrix. The same matrix is simply repeated for higher radii. | |
| imageCopy ($imgBlur, $imgCanvas, 0, 0, 1, 1, $w - 1, $h - 1); // up left | |
| imageCopyMerge($imgBlur, $imgCanvas, 1, 1, 0, 0, $w, $h, 50); // down right | |
| imageCopyMerge($imgBlur, $imgCanvas, 0, 1, 1, 0, $w - 1, $h, 33.33333); // down left | |
| imageCopyMerge($imgBlur, $imgCanvas, 1, 0, 0, 1, $w, $h - 1, 25); // up right | |
| imageCopyMerge($imgBlur, $imgCanvas, 0, 0, 1, 0, $w - 1, $h, 33.33333); // left | |
| imageCopyMerge($imgBlur, $imgCanvas, 1, 0, 0, 0, $w, $h, 25); // right | |
| imageCopyMerge($imgBlur, $imgCanvas, 0, 0, 0, 1, $w, $h - 1, 20 ); // up | |
| imageCopyMerge($imgBlur, $imgCanvas, 0, 1, 0, 0, $w, $h, 16.666667); // down | |
| imageCopyMerge($imgBlur, $imgCanvas, 0, 0, 0, 0, $w, $h, 50); // center | |
| imageCopy($imgCanvas, $imgBlur, 0, 0, 0, 0, $w, $h); | |
| // During the loop above the blurred copy darkens, possibly due to a roundoff | |
| // error. Therefore the sharp picture has to go through the same loop to | |
| // produce a similar image for comparison. This is not a good thing, as processing | |
| // time increases heavily. | |
| imageCopy ($imgBlur2, $imgCanvas2, 0, 0, 0, 0, $w, $h); | |
| imageCopyMerge($imgBlur2, $imgCanvas2, 0, 0, 0, 0, $w, $h, 50); | |
| imageCopyMerge($imgBlur2, $imgCanvas2, 0, 0, 0, 0, $w, $h, 33.33333); | |
| imageCopyMerge($imgBlur2, $imgCanvas2, 0, 0, 0, 0, $w, $h, 25); | |
| imageCopyMerge($imgBlur2, $imgCanvas2, 0, 0, 0, 0, $w, $h, 33.33333); | |
| imageCopyMerge($imgBlur2, $imgCanvas2, 0, 0, 0, 0, $w, $h, 25); | |
| imageCopyMerge($imgBlur2, $imgCanvas2, 0, 0, 0, 0, $w, $h, 20 ); | |
| imageCopyMerge($imgBlur2, $imgCanvas2, 0, 0, 0, 0, $w, $h, 16.666667); | |
| imageCopyMerge($imgBlur2, $imgCanvas2, 0, 0, 0, 0, $w, $h, 50); | |
| imageCopy($imgCanvas2, $imgBlur2, 0, 0, 0, 0, $w, $h); | |
| } | |
| } | |
| $this->destroy($imgBlur, $imgBlur2); | |
| $new_img = $this->create_true_color($w, $h); | |
| imageCopy($new_img, $img, 0, 0, 0, 0, $w, $h); | |
| // Calculate the difference between the blurred pixels and the original | |
| // and set the pixels | |
| for ($x = 0; $x < $w; $x++) { // each row | |
| for ($y = 0; $y < $h; $y++) { // each pixel | |
| $rgbOrig = imageColorAt($imgCanvas2, $x, $y); | |
| $rOrig = ($rgbOrig >> 16) & 0xFF; | |
| $gOrig = ($rgbOrig >> 8) & 0xFF; | |
| $bOrig = $rgbOrig & 0xFF; | |
| $rgbBlur = imageColorAt($imgCanvas, $x, $y); | |
| $rBlur = ($rgbBlur >> 16) & 0xFF; | |
| $gBlur = ($rgbBlur >> 8) & 0xFF; | |
| $bBlur = $rgbBlur & 0xFF; | |
| // When the masked pixels differ less from the original | |
| // than the threshold specifies, they are set to their original value. | |
| $rNew = (abs($rOrig - $rBlur) >= $threshold) | |
| ? $this->clamp(($amount * ($rOrig - $rBlur)) + $rOrig, 0, 255) | |
| : $rOrig; | |
| $gNew = (abs($gOrig - $gBlur) >= $threshold) | |
| ? $this->clamp(($amount * ($gOrig - $gBlur)) + $gOrig, 0, 255) | |
| : $gOrig; | |
| $bNew = (abs($bOrig - $bBlur) >= $threshold) | |
| ? $this->clamp(($amount * ($bOrig - $bBlur)) + $bOrig, 0, 255) | |
| : $bOrig; | |
| if (($rOrig != $rNew) || ($gOrig != $gNew) || ($bOrig != $bNew)) { | |
| $pixCol = ($rNew << 16) + ($gNew << 8) + $bNew; | |
| imageSetPixel($new_img, $x, $y, $pixCol); | |
| } | |
| } | |
| } | |
| return $new_img; | |
| } | |
| // -------------------------------------------------------------- | |
| // Color Functions | |
| // | |
| //===================================================== | |
| // @Public: color_average() | |
| // @Args: $colors - Type: Array; | |
| // Desc: Array of colors, in integer format. | |
| // Meaning 0xFF0000 = red. | |
| // @Author: Steven Harris | |
| // @Version: <{#spec.tag.float.version.var.replace#}> | |
| // @Desc: | |
| // Returns the weighted average of the given colors. | |
| //===================================================== | |
| public function color_average($colors) | |
| { | |
| $sz = sizeof($colors); | |
| $r = $g = $b = 0; | |
| for ($i = 0; $i < $sz; $i++) { | |
| $r += ($colors[$i] >> 16) & 0xff; | |
| $g += ($colors[$i] >> 8) & 0xff; | |
| $b += $colors[$i] & 0xff; | |
| } | |
| return (intval($r / $sz) << 16) | (intval($g / $sz) << 8) | intval($b / $sz); | |
| } | |
| // -------------------------------------------------------------- | |
| // Misc Functions | |
| // | |
| //===================================================== | |
| // @Private: clamp() | |
| // @Args: $val - Type: Number; | |
| // Desc: Value to clamp. | |
| // $min - Type: Number; | |
| // Desc: Minimum value $val can have. | |
| // $max - Type: Number; | |
| // Desc: Maximum value $val can have. | |
| // @Author: Steven Harris | |
| // @Version: <{#spec.tag.float.version.var.replace#}> | |
| // @Desc: Returns $val, forced between $min and $max. | |
| //===================================================== | |
| private function clamp($val, $min, $max) | |
| { | |
| return min(max($val, $min), $max); | |
| } | |
| //===================================================== | |
| // @Private: file_ext() | |
| // @Args: $name - Type: String; | |
| // Desc: A filename. | |
| // @Author: Steven Harris | |
| // @Version: <{#spec.tag.float.version.var.replace#}> | |
| // @Desc: | |
| // Returns the string after the last "." in $name. | |
| // Basically, the file extension. | |
| //===================================================== | |
| /** FIXME - This should be in the files helper, and probably with extra | |
| functionality like multiple exts (getting .tar.gz for example) | |
| */ | |
| private function file_ext($name) | |
| { | |
| return substr($name, strrpos($name, '.') + 1); | |
| } | |
| //===================================================== | |
| // @Public: load_font() | |
| // @Args: $fontfile - Type: String | |
| // Desc: Path to the GD font file. | |
| // @Author: Steven Harris | |
| // @Version: <{#spec.tag.float.version.var.replace#}> | |
| // @Desc: | |
| // Attempts to load the GD-type font located at | |
| // $fontfile, and returns the integer index of the font | |
| // if successful. If it fails, it will try creating a | |
| // temporary file with the endian order reversed, and | |
| // attempt to load that. If that fails, the function | |
| // returns false. The returned index can be used as the | |
| // second parameter of draw_string, in order to use | |
| // your custom font in an image. | |
| //===================================================== | |
| public function load_font($fontfile) | |
| { | |
| if (($index = imageLoadFont($fontfile)) !== false) { | |
| return $index; | |
| } | |
| // bah. | |
| $tmpfile = @tempnam(TMP_PATH, 'imageloadfont_'); | |
| if (!$tmpfile) { | |
| return false; | |
| } | |
| // we'll need to reverse the endian order of the 4-byte ints stored in | |
| // 0-3, 4-7, 8-11, and 12-15 | |
| $orig = @fopen($fontfile, 'rb'); | |
| $new = @fopen($tmpfile, 'wb'); | |
| fwrite($new, strrev( fread($orig, 4) ), 4); // first int - # of chars | |
| fwrite($new, strrev( fread($orig, 4) ), 4); // second int - value of first char | |
| fwrite($new, strrev( fread($orig, 4) ), 4); // third int - char pixel width | |
| fwrite($new, strrev( fread($orig, 4) ), 4); // fourth int - char pixel height | |
| $the_rest = filesize($fontfile) - 16; | |
| fwrite($new, fread($orig, $the_rest), $the_rest); | |
| fclose($orig); | |
| fclose($new); | |
| // try again, I suppose. | |
| $index = imageLoadFont($tmpfile); | |
| unlink($tmpfile); | |
| // additional checks really aren't needed, since the function would | |
| // return false anyway if imageLoadFont failed. | |
| return $index; | |
| } | |
| } | |
| ?> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment