123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551 |
- <?php
- /**
- * Part of the Fuel framework.
- *
- * @package Fuel
- * @version 1.5
- * @author Fuel Development Team
- * @license MIT License
- * @copyright 2010 - 2013 Fuel Development Team
- * @link http://fuelphp.com
- */
- namespace Fuel\Core;
- class Image_Gd extends \Image_Driver
- {
- protected $image_data = null;
- protected $accepted_extensions = array('png', 'gif', 'jpg', 'jpeg');
- protected $gdresizefunc = "imagecopyresampled";
- public function load($filename, $return_data = false, $force_extension = false)
- {
- extract(parent::load($filename, $return_data, $force_extension));
- $return = false;
- $image_extension == 'jpg' and $image_extension = 'jpeg';
- if ( ! $return_data)
- {
- $this->image_data !== null and imagedestroy($this->image_data);
- $this->image_data = null;
- }
- // Check if the function exists
- if (function_exists('imagecreatefrom'.$image_extension))
- {
- // Create a new transparent image.
- $sizes = $this->sizes($image_fullpath);
- $tmpImage = call_user_func('imagecreatefrom'.$image_extension, $image_fullpath);
- $image = $this->create_transparent_image($sizes->width, $sizes->height, $tmpImage);
- if ( ! $return_data)
- {
- $this->image_data = $image;
- $return = true;
- }
- else
- {
- $return = $image;
- }
- $this->debug('', "<strong>Loaded</strong> <code>".$image_fullpath."</code> with size of ".$sizes->width."x".$sizes->height);
- }
- else
- {
- throw new \RuntimeException("Function imagecreatefrom".$image_extension."() does not exist (Missing GD?)");
- }
- return $return_data ? $return : $this;
- }
- protected function _crop($x1, $y1, $x2, $y2)
- {
- extract(parent::_crop($x1, $y1, $x2, $y2));
- $width = $x2 - $x1;
- $height = $y2 - $y1;
- $this->debug("Cropping image ".$width."x".$height."+$x1+$y1 based on coords ($x1, $y1), ($x2, $y2)");
- $image = $this->create_transparent_image($width, $height);
- imagecopy($image, $this->image_data, 0, 0, $x1, $y1, $width, $height);
- $this->image_data = $image;
- }
- protected function _resize($width, $height = null, $keepar = true, $pad = true)
- {
- extract(parent::_resize($width, $height, $keepar, $pad));
- $sizes = $this->sizes();
- $this->debug("Resizing image to $width, $height with" . ($keepar ? '' : 'out') . " keeping AR and with" . ($pad ? '' : 'out') . " padding.");
- // Add the original image.
- $image = $this->create_transparent_image($cwidth, $cheight);
- call_user_func($this->gdresizefunc, $image, $this->image_data, $x, $y, 0, 0, $width, $height, $sizes->width, $sizes->height);
- $this->image_data = $image;
- }
- protected function _rotate($degrees)
- {
- extract(parent::_rotate($degrees));
- $degrees = 360 - $degrees;
- $bgcolor = $this->config['bgcolor'] !== null ? $this->config['bgcolor'] : '#000';
- $color = $this->create_color($this->image_data, $bgcolor, 100);
- $this->image_data = imagerotate($this->image_data, $degrees, $color, false);
- }
- protected function _watermark($filename, $position, $padding = 5)
- {
- $values = parent::_watermark($filename, $position, $padding);
- if ($values == false)
- {
- throw new \InvalidArgumentException("Watermark image not found or invalid filetype.");
- }
- else
- {
- extract($values);
- $wsizes = $this->sizes($filename);
- $sizes = $this->sizes();
- // Load the watermark preserving transparency
- $watermark = $this->load($filename, true);
- // Below is to prevent glitch in GD with negative $x coords
- if ($x < 0 || $y < 0)
- {
- $this->debug("Modifying watermark to remove negative coords.");
- // Generate a new width and height for the watermark.
- $newwidth = ($x < 0 ? $wsizes->width + $x : $wsizes->width);
- $newheight = ($y < 0 ? $wsizes->height + $y : $wsizes->height);
- // Create a transparent image the size of the new watermark.
- $tmpwatermark = $this->create_transparent_image($newwidth, $newheight);
- $this->debug("New size is $newwidth x $newheight and coords are $x , $y");
- // Call the resize function based on image format
- imagecopy(
- $tmpwatermark, $watermark, // Copy the new image into the tmp watermark
- 0, 0,
- $x < 0 ? abs($x) : 0,
- $y < 0 ? abs($y) : 0,
- $newwidth, $newheight
- );
- // Set the variables for the image_merge
- $watermark = $tmpwatermark;
- $x = $x < 0 ? 0 : $x;
- $y = $y < 0 ? 0 : $y;
- }
- // Used as a workaround for lack of alpha support in imagecopymerge.
- $this->debug("Coords for watermark are $x , $y");
- $this->image_merge($this->image_data, $watermark, $x, $y, $this->config['watermark_alpha']);
- }
- }
- protected function _flip($mode)
- {
- $sizes = (array)$this->sizes();
- $source = array_merge($sizes, array('x' => 0, 'y' => 0));
- switch ($mode)
- {
- case 'vertical':
- $source['y'] = $sizes['height'] - 1;
- $source['height'] = -$sizes['height'];
- break;
- case 'horizontal':
- $source['x'] = $sizes['width'] - 1;
- $source['width'] = -$sizes['width'];
- break;
- case 'both':
- $source['y'] = $sizes['height'] - 1;
- $source['x'] = $sizes['width'] - 1;
- $source['height'] = -$sizes['height'];
- $source['width'] = -$sizes['width'];
- break;
- default: return false;
- }
- $image = imagecreatetruecolor($sizes['width'], $sizes['height']);
- imagecopyresampled(
- $image,
- $this->image_data,
- 0,
- 0,
- $source['x'],
- $source['y'],
- $sizes['width'],
- $sizes['height'],
- $source['width'],
- $source['height']
- );
- $this->image_data = $image;
- }
- protected function _border($size, $color = null)
- {
- extract(parent::_border($size, $color));
- $sizes = $this->sizes();
- $image = $this->create_transparent_image($sizes->width + ($size * 2), $sizes->height + ($size * 2));
- $color = $this->create_color($image, $color, 100);
- $this->image_merge($image, $this->image_data, $size, $size, 100);
- for ($s = 0; $s < $size; $s++)
- {
- imagerectangle($image, $s, $s, $sizes->width + ($size * 2) - $s - 1, $sizes->height + ($size * 2) - $s - 1, $color);
- }
- $this->image_data = $image;
- }
- protected function _mask($maskimage)
- {
- extract(parent::_mask($maskimage));
- // Get size and width of image
- $sizes = $this->sizes();
- $masksizes = $this->sizes($maskimage);
- // Create new blank image
- $image = $this->create_transparent_image($sizes->width, $sizes->height);
- if (is_resource($maskimage))
- {
- $maskim = $maskimage;
- }
- else
- {
- $maskim = $this->load($maskimage, true);
- }
- $masksizes->width > $sizes->width and $masksizes->width = $sizes->width;
- $masksizes->height > $sizes->width and $masksizes->height = $sizes->height;
- // Loop through all the pixels
- for ($x = 0; $x < $masksizes->width; $x++)
- {
- for ($y = 0; $y < $masksizes->height; $y++)
- {
- $maskcolor = imagecolorat($maskim, $x, $y);
- $maskcolor = imagecolorsforindex($maskim, $maskcolor);
- $maskalpha = 127 - floor(($maskcolor['red'] + $maskcolor['green'] + $maskcolor['blue']) / 6);
- if ($maskalpha == 127)
- {
- continue;
- }
- if ($maskalpha == 0)
- {
- $ourcolor = array(
- 'red' => 0,
- 'green' => 0,
- 'blue' => 0,
- 'alpha' => 0
- );
- }
- else
- {
- $ourcolor = imagecolorat($this->image_data, $x, $y);
- $ourcolor = imagecolorsforindex($this->image_data, $ourcolor);
- }
- $ouralpha = 127 - $ourcolor['alpha'];
- if ($ouralpha == 0)
- {
- continue;
- }
- $newalpha = floor($ouralpha - (($maskalpha / 127) * $ouralpha));
- $newcolor = imagecolorallocatealpha($image, $ourcolor['red'], $ourcolor['green'], $ourcolor['blue'], 127 - $newalpha);
- imagesetpixel($image, $x, $y, $newcolor);
- }
- }
- $this->image_data = $image;
- }
- protected function _rounded($radius, $sides, $antialias)
- {
- extract(parent::_rounded($radius, $sides, $antialias));
- $tl and $this->round_corner($this->image_data, $radius, $antialias, true, true);
- $tr and $this->round_corner($this->image_data, $radius, $antialias, true, false);
- $bl and $this->round_corner($this->image_data, $radius, $antialias, false, true);
- $br and $this->round_corner($this->image_data, $radius, $antialias, false, false);
- }
- protected function _grayscale()
- {
- $sizes = $this->sizes();
- // Create the 256 color palette
- $bwpalette = array();
- for ($i = 0; $i < 256; $i++)
- {
- $bwpalette[$i] = imagecolorallocate($this->image_data, $i, $i, $i);
- }
- for ($x = 0; $x < $sizes->width; $x++)
- {
- for ($y = 0; $y < $sizes->height; $y++)
- {
- $color = imagecolorat($this->image_data, $x, $y);
- $red = ($color >> 16) & 0xFF;
- $green = ($color >> 8) & 0xFF;
- $blue = $color & 0xFF;
- // If its black or white, theres no use in setting the pixel
- if (($red == 0 && $green == 0 && $blue == 0) || ($red == 255 && $green == 255 && $blue == 255))
- {
- continue;
- }
- // Now set the color
- $shade = (($red*0.299)+($green*0.587)+($blue*0.114));
- imagesetpixel($this->image_data, $x, $y, $bwpalette[$shade]);
- }
- }
- }
- public function sizes($filename = null)
- {
- if (empty($filename) && !empty($this->image_fullpath))
- {
- $filename = $this->image_fullpath;
- }
- if ($filename == $this->image_fullpath && is_resource($this->image_data))
- {
- $width = imagesx($this->image_data);
- $height = imagesy($this->image_data);
- }
- else if (is_resource($filename))
- {
- $width = imagesx($filename);
- $height = imagesy($filename);
- }
- else
- {
- list($width, $height) = getimagesize($filename);
- }
- return (object) array('width' => $width, 'height' => $height);
- }
- public function save($filename, $permissions = null)
- {
- extract(parent::save($filename, $permissions));
- $this->run_queue();
- $this->add_background();
- $vars = array(&$this->image_data, $filename);
- $filetype = $this->image_extension;
- if ($filetype == 'jpg' || $filetype == 'jpeg')
- {
- $vars[] = $this->config['quality'];
- $filetype = 'jpeg';
- }
- elseif ($filetype == 'png')
- {
- $vars[] = floor(($this->config['quality'] / 100) * 9);
- }
- call_user_func_array('image'.$filetype, $vars);
- if ($this->config['persistence'] === false)
- {
- $this->reload();
- }
- return $this;
- }
- public function output($filetype = null)
- {
- $this->gdresizefunc = ($filetype == 'gif') ? 'imagecopyresized': $this->gdresizefunc = 'imagecopyresampled';
- extract(parent::output($filetype));
- $this->run_queue();
- $this->add_background();
- $vars = array($this->image_data, null);
- if ($filetype == 'jpg' || $filetype == 'jpeg')
- {
- $vars[] = $this->config['quality'];
- $filetype = 'jpeg';
- }
- elseif ($filetype == 'png')
- {
- $vars[] = floor(($this->config['quality'] / 100) * 9);
- }
- call_user_func_array('image'.$filetype, $vars);
- if ($this->config['persistence'] === false)
- {
- $this->reload();
- }
- return $this;
- }
- /**
- * Creates a new color usable by GD.
- *
- * @param resource $image The image to create the color from
- * @param string $hex The hex code of the color
- * @param integer $alpha The alpha of the color, 0 (trans) to 100 (opaque)
- * @return integer The color
- */
- protected function create_color(&$image, $hex, $alpha)
- {
- extract($this->create_hex_color($hex));
- // Handling alpha is different among drivers
- if ($hex == null)
- {
- $alpha = 127;
- }
- else
- {
- $alpha = 127 - floor($alpha * 1.27);
- }
- // Check if the transparency is allowed
- return imagecolorallocatealpha($image, $red, $green, $blue, $alpha);
- }
- protected function add_background()
- {
- if ($this->config['bgcolor'] != null || ($this->new_extension == 'jpg' || $this->new_extension == 'jpeg'))
- {
- $bgcolor = $this->config['bgcolor'] == null ? '#000' : $this->config['bgcolor'];
- $this->debug("Adding background color $bgcolor");
- $sizes = $this->sizes();
- $bgimg = $this->create_transparent_image($sizes->width, $sizes->height);
- $color = $this->create_color($bgimg, $bgcolor, 100);
- imagefill($bgimg, 0, 0, $color);
- $this->image_merge($bgimg, $this->image_data, 0, 0, 100);
- $this->image_data = $bgimg;
- }
- }
- /**
- * Creates a new transparent image.
- *
- * @param integer $width The width of the image.
- * @param integer $height The height of the image.
- * @param resource $resource Optionally add an image to the new transparent image.
- * @return resource Returns the image in resource form.
- */
- private function create_transparent_image($width, $height, $resource = null)
- {
- $image = imagecreatetruecolor($width, $height);
- $color = $this->create_color($image, null, 0);
- imagesavealpha($image, true);
- if ($this->image_extension == 'gif' || $this->image_extension == 'png')
- {
- // Get the current transparent color if possible...
- $transcolor = imagecolortransparent($image);
- if ($transcolor > 0)
- {
- $color = $transcolor;
- }
- imagecolortransparent($image, $color);
- }
- // Set the blending mode to false, add the bgcolor, then switch it back.
- imagealphablending($image, false);
- imagefilledrectangle($image, 0, 0, $width, $height, $color);
- imagealphablending($image, true);
- if (is_resource($resource))
- {
- imagecopy($image, $resource, 0, 0, 0, 0, $width, $height);
- }
- return $image;
- }
- /**
- * Creates a rounded corner on the image.
- *
- * @param resource $image
- * @param integer $radius
- * @param integer $antialias
- * @param boolean $top
- * @param boolean $left
- */
- private function round_corner(&$image, $radius, $antialias, $top, $left)
- {
- $this->debug("Rounding ".($top ? 'top' : 'bottom')." ".($left ? 'left' : 'right')." corner with a radius of ".$radius."px.");
- $sX = $left ? -$radius : 0;
- $sY = $top ? -$radius : 0;
- $eX = $left ? 0 : $radius;
- $eY = $top ? 0 : $radius;
- // Get this images size
- $sizes = $this->sizes();
- $offsetX = ($left ? $radius : $sizes->width - $radius - 1);
- $offsetY = ($top ? $radius : $sizes->height - $radius - 1);
- // Set the images alpha blend to false
- imagealphablending($image, false);
- // Make this color ahead time
- $transparent = $this->create_color($image, null, 0);
- for ($x = $sX; $x <= $eX; $x++)
- {
- for ($y = $sY; $y <= $eY; $y++)
- {
- $dist = sqrt(($x * $x) + ($y * $y));
- if ($dist <= $radius + $antialias)
- {
- // Decide if anything needs to be changed
- // We subtract from antialias so the transparency makes sense.
- $fromCirc = $dist - $radius;
- if ($fromCirc > 0)
- {
- if ($fromCirc == 0)
- {
- imagesetpixel($image, $x + $offsetX, $y + $offsetY, $transparent);
- }
- else
- {
- // Get color information from this spot on the image
- $rgba = imagecolorat($image, $x + $offsetX, $y + $offsetY);
- $tmpColor = imagecolorallocatealpha(
- $image,
- ($rgba >> 16) & 0xFF, // Red
- ($rgba >> 8) & 0xFF, // Green
- $rgba & 0xFF, // Blue
- (127 - (($rgba >> 24) & 0xFF)) * ($fromCirc / $antialias) // Alpha
- );
- imagesetpixel($image, $x + $offsetX, $y + $offsetY, $tmpColor);
- }
- }
- }
- else
- {
- // Clear this area out...
- imagesetpixel($image, $x + $offsetX, $y + $offsetY, $transparent);
- }
- }
- }
- // Reset alpha blending
- imagealphablending($image, true);
- }
- /**
- * Merges to images together, using a fix for transparency
- *
- * @param resource $image The bottom image
- * @param resource $watermark The image to be placed on top
- * @param integer $x The position of the watermark on the X-axis
- * @param integer $y The position of the watermark on the Y-axis
- * @param integer $alpha The transparency of the watermark, 0 (trans) to 100 (opaque)
- */
- private function image_merge(&$image, $watermark, $x, $y, $alpha)
- {
- $wsizes = $this->sizes($watermark);
- $tmpimage = $this->create_transparent_image($wsizes->width, $wsizes->height);
- imagecopy($tmpimage, $image, 0, 0, $x, $y, $wsizes->width, $wsizes->height);
- imagecopy($tmpimage, $watermark, 0, 0, 0, 0, $wsizes->width, $wsizes->height);
- imagealphablending($image, false);
- imagecopymerge($image, $tmpimage, $x, $y, 0, 0, $wsizes->width, $wsizes->height, $alpha);
- imagealphablending($image, true);
- }
- }
|