You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
PlexShare/fuel/core/classes/image/gd.php

552 lines
16 KiB

<?php
/**
* Part of the Fuel framework.
*
* @package Fuel
* @version 1.8
* @author Fuel Development Team
* @license MIT License
* @copyright 2010 - 2016 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);
}
elseif (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 = null, $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_fuel_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_fuel_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.
*/
protected function create_transparent_image($width, $height, $resource = null)
{
$image = imagecreatetruecolor($width, $height);
$bgcolor = $this->config['bgcolor'] == null ? '#000' : $this->config['bgcolor'];
$color = $this->create_color($image, $bgcolor, 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
*/
protected 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)
*/
protected 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);
}
}