First Local Commit - After Clean up.
Signed-off-by: Rick Hays <rhays@haysgang.com>
This commit is contained in:
591
system/Images/Handlers/GDHandler.php
Normal file
591
system/Images/Handlers/GDHandler.php
Normal file
@@ -0,0 +1,591 @@
|
||||
<?php
|
||||
/**
|
||||
* CodeIgniter
|
||||
*
|
||||
* An open source application development framework for PHP
|
||||
*
|
||||
* This content is released under the MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2019 British Columbia Institute of Technology
|
||||
* Copyright (c) 2019 CodeIgniter Foundation
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* @package CodeIgniter
|
||||
* @author CodeIgniter Dev Team
|
||||
* @copyright 2019 CodeIgniter Foundation
|
||||
* @license https://opensource.org/licenses/MIT MIT License
|
||||
* @link https://codeigniter.com
|
||||
* @since Version 4.0.0
|
||||
* @filesource
|
||||
*/
|
||||
|
||||
namespace CodeIgniter\Images\Handlers;
|
||||
|
||||
use CodeIgniter\Images\Exceptions\ImageException;
|
||||
|
||||
/**
|
||||
* Image handler for GD package
|
||||
*/
|
||||
class GDHandler extends BaseHandler
|
||||
{
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param type $config
|
||||
* @throws type
|
||||
*/
|
||||
public function __construct($config = null)
|
||||
{
|
||||
parent::__construct($config);
|
||||
|
||||
// We should never see this, so can't test it
|
||||
// @codeCoverageIgnoreStart
|
||||
if (! extension_loaded('gd'))
|
||||
{
|
||||
throw ImageException::forMissingExtension('GD');
|
||||
}
|
||||
// @codeCoverageIgnoreEnd
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Handles the rotation of an image resource.
|
||||
* Doesn't save the image, but replaces the current resource.
|
||||
*
|
||||
* @param integer $angle
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
protected function _rotate(int $angle): bool
|
||||
{
|
||||
// Create the image handle
|
||||
$srcImg = $this->createImage();
|
||||
|
||||
// Set the background color
|
||||
// This won't work with transparent PNG files so we are
|
||||
// going to have to figure out how to determine the color
|
||||
// of the alpha channel in a future release.
|
||||
|
||||
$white = imagecolorallocate($srcImg, 255, 255, 255);
|
||||
|
||||
// Rotate it!
|
||||
$destImg = imagerotate($srcImg, $angle, $white);
|
||||
|
||||
// Kill the file handles
|
||||
imagedestroy($srcImg);
|
||||
|
||||
$this->resource = $destImg;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Flattens transparencies
|
||||
*
|
||||
* @param integer $red
|
||||
* @param integer $green
|
||||
* @param integer $blue
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function _flatten(int $red = 255, int $green = 255, int $blue = 255)
|
||||
{
|
||||
$srcImg = $this->createImage();
|
||||
|
||||
if (function_exists('imagecreatetruecolor'))
|
||||
{
|
||||
$create = 'imagecreatetruecolor';
|
||||
$copy = 'imagecopyresampled';
|
||||
}
|
||||
else
|
||||
{
|
||||
$create = 'imagecreate';
|
||||
$copy = 'imagecopyresized';
|
||||
}
|
||||
$dest = $create($this->width, $this->height);
|
||||
|
||||
$matte = imagecolorallocate($dest, $red, $green, $blue);
|
||||
|
||||
imagefilledrectangle($dest, 0, 0, $this->width, $this->height, $matte);
|
||||
imagecopy($dest, $srcImg, 0, 0, 0, 0, $this->width, $this->height);
|
||||
|
||||
// Kill the file handles
|
||||
imagedestroy($srcImg);
|
||||
|
||||
$this->resource = $dest;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Flips an image along it's vertical or horizontal axis.
|
||||
*
|
||||
* @param string $direction
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function _flip(string $direction)
|
||||
{
|
||||
$srcImg = $this->createImage();
|
||||
|
||||
$width = $this->image->origWidth;
|
||||
$height = $this->image->origHeight;
|
||||
|
||||
if ($direction === 'horizontal')
|
||||
{
|
||||
for ($i = 0; $i < $height; $i ++)
|
||||
{
|
||||
$left = 0;
|
||||
$right = $width - 1;
|
||||
|
||||
while ($left < $right)
|
||||
{
|
||||
$cl = imagecolorat($srcImg, $left, $i);
|
||||
$cr = imagecolorat($srcImg, $right, $i);
|
||||
|
||||
imagesetpixel($srcImg, $left, $i, $cr);
|
||||
imagesetpixel($srcImg, $right, $i, $cl);
|
||||
|
||||
$left ++;
|
||||
$right --;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for ($i = 0; $i < $width; $i ++)
|
||||
{
|
||||
$top = 0;
|
||||
$bottom = $height - 1;
|
||||
|
||||
while ($top < $bottom)
|
||||
{
|
||||
$ct = imagecolorat($srcImg, $i, $top);
|
||||
$cb = imagecolorat($srcImg, $i, $bottom);
|
||||
|
||||
imagesetpixel($srcImg, $i, $top, $cb);
|
||||
imagesetpixel($srcImg, $i, $bottom, $ct);
|
||||
|
||||
$top ++;
|
||||
$bottom --;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->resource = $srcImg;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Get GD version
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getVersion()
|
||||
{
|
||||
if (function_exists('gd_info'))
|
||||
{
|
||||
$gd_version = @gd_info();
|
||||
|
||||
return preg_replace('/\D/', '', $gd_version['GD Version']);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Resizes the image.
|
||||
*
|
||||
* @return boolean|\CodeIgniter\Images\Handlers\GDHandler
|
||||
*/
|
||||
public function _resize()
|
||||
{
|
||||
return $this->process('resize');
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Crops the image.
|
||||
*
|
||||
* @return boolean|\CodeIgniter\Images\Handlers\GDHandler
|
||||
*/
|
||||
public function _crop()
|
||||
{
|
||||
return $this->process('crop');
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Handles all of the grunt work of resizing, etc.
|
||||
*
|
||||
* @param string $action
|
||||
*
|
||||
* @return $this|bool
|
||||
*/
|
||||
protected function process(string $action)
|
||||
{
|
||||
$origWidth = $this->image->origWidth;
|
||||
$origHeight = $this->image->origHeight;
|
||||
|
||||
if ($action === 'crop')
|
||||
{
|
||||
// Reassign the source width/height if cropping
|
||||
$origWidth = $this->width;
|
||||
$origHeight = $this->height;
|
||||
|
||||
// Modify the "original" width/height to the new
|
||||
// values so that methods that come after have the
|
||||
// correct size to work with.
|
||||
$this->image->origHeight = $this->height;
|
||||
$this->image->origWidth = $this->width;
|
||||
}
|
||||
|
||||
// Create the image handle
|
||||
$src = $this->createImage();
|
||||
|
||||
if (function_exists('imagecreatetruecolor'))
|
||||
{
|
||||
$create = 'imagecreatetruecolor';
|
||||
$copy = 'imagecopyresampled';
|
||||
}
|
||||
else
|
||||
{
|
||||
$create = 'imagecreate';
|
||||
$copy = 'imagecopyresized';
|
||||
}
|
||||
|
||||
$dest = $create($this->width, $this->height);
|
||||
|
||||
if ($this->image->imageType === IMAGETYPE_PNG) // png we can actually preserve transparency
|
||||
{
|
||||
imagealphablending($dest, false);
|
||||
imagesavealpha($dest, true);
|
||||
}
|
||||
|
||||
$copy($dest, $src, 0, 0, $this->xAxis, $this->yAxis, $this->width, $this->height, $origWidth, $origHeight);
|
||||
|
||||
imagedestroy($src);
|
||||
$this->resource = $dest;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Saves any changes that have been made to file. If no new filename is
|
||||
* provided, the existing image is overwritten, otherwise a copy of the
|
||||
* file is made at $target.
|
||||
*
|
||||
* Example:
|
||||
* $image->resize(100, 200, true)
|
||||
* ->save();
|
||||
*
|
||||
* @param string|null $target
|
||||
* @param integer $quality
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function save(string $target = null, int $quality = 90): bool
|
||||
{
|
||||
$target = empty($target) ? $this->image->getPathname() : $target;
|
||||
|
||||
switch ($this->image->imageType)
|
||||
{
|
||||
case IMAGETYPE_GIF:
|
||||
if (! function_exists('imagegif'))
|
||||
{
|
||||
throw ImageException::forInvalidImageCreate(lang('images.gifNotSupported'));
|
||||
}
|
||||
|
||||
if (! @imagegif($this->resource, $target))
|
||||
{
|
||||
throw ImageException::forSaveFailed();
|
||||
}
|
||||
break;
|
||||
case IMAGETYPE_JPEG:
|
||||
if (! function_exists('imagejpeg'))
|
||||
{
|
||||
throw ImageException::forInvalidImageCreate(lang('images.jpgNotSupported'));
|
||||
}
|
||||
|
||||
if (! @imagejpeg($this->resource, $target, $quality))
|
||||
{
|
||||
throw ImageException::forSaveFailed();
|
||||
}
|
||||
break;
|
||||
case IMAGETYPE_PNG:
|
||||
if (! function_exists('imagepng'))
|
||||
{
|
||||
throw ImageException::forInvalidImageCreate(lang('images.pngNotSupported'));
|
||||
}
|
||||
|
||||
if (! @imagepng($this->resource, $target))
|
||||
{
|
||||
throw ImageException::forSaveFailed();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw ImageException::forInvalidImageCreate();
|
||||
break;
|
||||
}
|
||||
|
||||
imagedestroy($this->resource);
|
||||
|
||||
chmod($target, $this->filePermissions);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Create Image Resource
|
||||
*
|
||||
* This simply creates an image resource handle
|
||||
* based on the type of image being processed
|
||||
*
|
||||
* @param string $path
|
||||
* @param string $imageType
|
||||
*
|
||||
* @return resource|boolean
|
||||
*/
|
||||
protected function createImage(string $path = '', string $imageType = '')
|
||||
{
|
||||
if ($this->resource !== null)
|
||||
{
|
||||
return $this->resource;
|
||||
}
|
||||
|
||||
if ($path === '')
|
||||
{
|
||||
$path = $this->image->getPathname();
|
||||
}
|
||||
|
||||
if ($imageType === '')
|
||||
{
|
||||
$imageType = $this->image->imageType;
|
||||
}
|
||||
|
||||
switch ($imageType)
|
||||
{
|
||||
case IMAGETYPE_GIF:
|
||||
if (! function_exists('imagecreatefromgif'))
|
||||
{
|
||||
throw ImageException::forInvalidImageCreate(lang('images.gifNotSupported'));
|
||||
}
|
||||
|
||||
return imagecreatefromgif($path);
|
||||
case IMAGETYPE_JPEG:
|
||||
if (! function_exists('imagecreatefromjpeg'))
|
||||
{
|
||||
throw ImageException::forInvalidImageCreate(lang('images.jpgNotSupported'));
|
||||
}
|
||||
|
||||
return imagecreatefromjpeg($path);
|
||||
case IMAGETYPE_PNG:
|
||||
if (! function_exists('imagecreatefrompng'))
|
||||
{
|
||||
throw ImageException::forInvalidImageCreate(lang('images.pngNotSupported'));
|
||||
}
|
||||
|
||||
return imagecreatefrompng($path);
|
||||
default:
|
||||
throw ImageException::forInvalidImageCreate('Ima');
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Add text overlay to an image.
|
||||
*
|
||||
* @param string $text
|
||||
* @param array $options
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function _text(string $text, array $options = [])
|
||||
{
|
||||
// Reverse the vertical offset
|
||||
// When the image is positioned at the bottom
|
||||
// we don't want the vertical offset to push it
|
||||
// further down. We want the reverse, so we'll
|
||||
// invert the offset. Note: The horizontal
|
||||
// offset flips itself automatically
|
||||
|
||||
if ($options['vAlign'] === 'bottom')
|
||||
{
|
||||
$options['vOffset'] = $options['vOffset'] * -1;
|
||||
}
|
||||
|
||||
if ($options['hAlign'] === 'right')
|
||||
{
|
||||
$options['hOffset'] = $options['hOffset'] * -1;
|
||||
}
|
||||
|
||||
// Set font width and height
|
||||
// These are calculated differently depending on
|
||||
// whether we are using the true type font or not
|
||||
if (! empty($options['fontPath']))
|
||||
{
|
||||
if (function_exists('imagettfbbox'))
|
||||
{
|
||||
$temp = imagettfbbox($options['fontSize'], 0, $options['fontPath'], $text);
|
||||
$temp = $temp[2] - $temp[0];
|
||||
|
||||
$fontwidth = $temp / strlen($text);
|
||||
}
|
||||
else
|
||||
{
|
||||
$fontwidth = $options['fontSize'] - ($options['fontSize'] / 4);
|
||||
}
|
||||
|
||||
$fontheight = $options['fontSize'];
|
||||
}
|
||||
else
|
||||
{
|
||||
$fontwidth = imagefontwidth($options['fontSize']);
|
||||
$fontheight = imagefontheight($options['fontSize']);
|
||||
}
|
||||
|
||||
$options['fontheight'] = $fontheight;
|
||||
$options['fontwidth'] = $fontwidth;
|
||||
|
||||
// Set base X and Y axis values
|
||||
$xAxis = $options['hOffset'] + $options['padding'];
|
||||
$yAxis = $options['vOffset'] + $options['padding'];
|
||||
|
||||
// Set vertical alignment
|
||||
if ($options['vAlign'] === 'middle')
|
||||
{
|
||||
// Don't apply padding when you're in the middle of the image.
|
||||
$yAxis += ($this->image->origHeight / 2) + ($fontheight / 2) - $options['padding'];
|
||||
}
|
||||
elseif ($options['vAlign'] === 'bottom')
|
||||
{
|
||||
$yAxis = ($this->image->origHeight - $fontheight - $options['shadowOffset'] - ($fontheight / 2)) - $yAxis;
|
||||
}
|
||||
|
||||
// Set horizontal alignment
|
||||
if ($options['hAlign'] === 'right')
|
||||
{
|
||||
$xAxis += ($this->image->origWidth - ($fontwidth * strlen($text)) - $options['shadowOffset']) - (2 * $options['padding']);
|
||||
}
|
||||
elseif ($options['hAlign'] === 'center')
|
||||
{
|
||||
$xAxis += floor(($this->image->origWidth - ($fontwidth * strlen($text))) / 2);
|
||||
}
|
||||
|
||||
$options['xAxis'] = $xAxis;
|
||||
$options['yAxis'] = $yAxis;
|
||||
|
||||
if ($options['withShadow'])
|
||||
{
|
||||
// Offset from text
|
||||
$options['xShadow'] = $xAxis + $options['shadowOffset'];
|
||||
$options['yShadow'] = $yAxis + $options['shadowOffset'];
|
||||
|
||||
$this->textOverlay($text, $options, true);
|
||||
}
|
||||
|
||||
$this->textOverlay($text, $options, false);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Handler-specific method for overlaying text on an image.
|
||||
*
|
||||
* @param string $text
|
||||
* @param array $options
|
||||
* @param boolean $isShadow Whether we are drawing the dropshadow or actual text
|
||||
*/
|
||||
protected function textOverlay(string $text, array $options = [], bool $isShadow = false)
|
||||
{
|
||||
$src = $this->createImage();
|
||||
|
||||
/* Set RGB values for shadow
|
||||
*
|
||||
* Get the rest of the string and split it into 2-length
|
||||
* hex values:
|
||||
*/
|
||||
$opacity = ($options['opacity'] * 127);
|
||||
|
||||
// Allow opacity to be applied to the text
|
||||
imagealphablending($src, true);
|
||||
|
||||
$color = $isShadow ? $options['shadowColor'] : $options['color'];
|
||||
$color = str_split(substr($color, 0, 6), 2);
|
||||
$color = imagecolorclosestalpha($src, hexdec($color[0]), hexdec($color[1]), hexdec($color[2]), $opacity);
|
||||
|
||||
$xAxis = $isShadow ? $options['xShadow'] : $options['xAxis'];
|
||||
$yAxis = $isShadow ? $options['yShadow'] : $options['yAxis'];
|
||||
|
||||
// Add the shadow to the source image
|
||||
if (! empty($options['fontPath']))
|
||||
{
|
||||
// We have to add fontheight because imagettftext locates the bottom left corner, not top-left corner.
|
||||
imagettftext($src, $options['fontSize'], 0, $xAxis, $yAxis + $options['fontheight'], $color, $options['fontPath'], $text);
|
||||
}
|
||||
else
|
||||
{
|
||||
imagestring($src, $options['fontSize'], $xAxis, $yAxis, $text, $color);
|
||||
}
|
||||
|
||||
$this->resource = $src;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Return image width.
|
||||
*
|
||||
* @return integer
|
||||
*/
|
||||
public function _getWidth()
|
||||
{
|
||||
return imagesx($this->resource);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return image height.
|
||||
*
|
||||
* @return integer
|
||||
*/
|
||||
public function _getHeight()
|
||||
{
|
||||
return imagesy($this->resource);
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user