2016-08-18 20:39:31 +00:00
|
|
|
<?php
|
|
|
|
|
|
|
|
/* --------------------------------------------------------------------
|
|
|
|
|
|
|
|
Chevereto
|
|
|
|
http://chevereto.com/
|
|
|
|
|
|
|
|
@author Rodolfo Berrios A. <http://rodolfoberrios.com/>
|
|
|
|
<inbox@rodolfoberrios.com>
|
|
|
|
|
|
|
|
Copyright (C) Rodolfo Berrios A. All rights reserved.
|
2018-08-16 18:51:52 +00:00
|
|
|
|
2016-08-18 20:39:31 +00:00
|
|
|
BY USING THIS SOFTWARE YOU DECLARE TO ACCEPT THE CHEVERETO EULA
|
|
|
|
http://chevereto.com/license
|
|
|
|
|
|
|
|
--------------------------------------------------------------------- */
|
|
|
|
|
|
|
|
namespace CHV;
|
|
|
|
use G, Exception;
|
|
|
|
|
|
|
|
class Imageresize {
|
|
|
|
|
|
|
|
// filename => name.ext
|
|
|
|
// file => /full/path/to/name.ext
|
|
|
|
// name => name
|
2018-08-16 18:51:52 +00:00
|
|
|
|
2016-08-18 20:39:31 +00:00
|
|
|
public $resized;
|
2018-08-16 18:51:52 +00:00
|
|
|
|
2016-08-18 20:39:31 +00:00
|
|
|
public function setSource($source) {
|
2018-08-16 18:51:52 +00:00
|
|
|
clearstatcache(TRUE, $source);
|
2016-08-18 20:39:31 +00:00
|
|
|
$this->source = $source;
|
|
|
|
}
|
2018-08-16 18:51:52 +00:00
|
|
|
|
2016-08-18 20:39:31 +00:00
|
|
|
public function setDestination($destination) {
|
|
|
|
$this->destination = $destination;
|
|
|
|
}
|
2018-08-16 18:51:52 +00:00
|
|
|
|
2016-08-18 20:39:31 +00:00
|
|
|
public function setFilename($name) {
|
|
|
|
$this->filename = $name;
|
|
|
|
}
|
2018-08-16 18:51:52 +00:00
|
|
|
|
2016-08-18 20:39:31 +00:00
|
|
|
// Set options
|
|
|
|
public function setOptions($options) {
|
|
|
|
$this->options = $options;
|
|
|
|
}
|
2018-08-16 18:51:52 +00:00
|
|
|
|
2016-08-18 20:39:31 +00:00
|
|
|
// Set individual option
|
|
|
|
public function setOption($key, $value) {
|
|
|
|
$this->options[$key] = $value;
|
|
|
|
}
|
2018-08-16 18:51:52 +00:00
|
|
|
|
2016-08-18 20:39:31 +00:00
|
|
|
public function set_width($width) {
|
|
|
|
$this->width = intval($width);
|
|
|
|
}
|
2018-08-16 18:51:52 +00:00
|
|
|
|
2016-08-18 20:39:31 +00:00
|
|
|
public function set_height($height) {
|
|
|
|
$this->height = intval($height);
|
2018-08-16 18:51:52 +00:00
|
|
|
}
|
|
|
|
|
2016-08-18 20:39:31 +00:00
|
|
|
public function set_fixed() {
|
|
|
|
$this->fixed = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Do the thing
|
|
|
|
* @Exception 4xx
|
|
|
|
*/
|
|
|
|
public function exec() {
|
2018-08-16 18:51:52 +00:00
|
|
|
|
2016-08-18 20:39:31 +00:00
|
|
|
$this->validateInput(); // Exception 1xx
|
2018-08-16 18:51:52 +00:00
|
|
|
|
2016-08-18 20:39:31 +00:00
|
|
|
// Save the source filename
|
|
|
|
$source_filename = G\get_filename_without_extension($this->source);
|
2018-08-16 18:51:52 +00:00
|
|
|
|
2016-08-18 20:39:31 +00:00
|
|
|
// Set file extension
|
|
|
|
$this->file_extension = $this->source_image_fileinfo['extension'];
|
2018-08-16 18:51:52 +00:00
|
|
|
|
2016-08-18 20:39:31 +00:00
|
|
|
// Workaround the $filename
|
|
|
|
if(!$this->filename) {
|
|
|
|
$this->filename = $source_filename;
|
|
|
|
}
|
2018-08-16 18:51:52 +00:00
|
|
|
|
2016-08-18 20:39:31 +00:00
|
|
|
// Fix file extension
|
|
|
|
if(G\get_file_extension($this->filename) == $this->resized_file_extension) {
|
|
|
|
$this->filename = G\get_filename_without_extension($this->filename);
|
|
|
|
}
|
2018-08-16 18:51:52 +00:00
|
|
|
|
2016-08-18 20:39:31 +00:00
|
|
|
// Fix the destination path
|
|
|
|
$this->destination = G\add_ending_slash($this->destination);
|
2018-08-16 18:51:52 +00:00
|
|
|
|
2016-08-18 20:39:31 +00:00
|
|
|
// Set $resized_file
|
|
|
|
$this->resized_file = $this->destination . $this->filename . '.' . $this->file_extension;
|
2018-08-16 18:51:52 +00:00
|
|
|
|
2016-08-18 20:39:31 +00:00
|
|
|
// Do the resize process
|
|
|
|
$this->resize_image();
|
2018-08-16 18:51:52 +00:00
|
|
|
|
2016-08-18 20:39:31 +00:00
|
|
|
$this->resized = [
|
|
|
|
'file' => $this->resized_file,
|
|
|
|
'filename' => G\get_filename($this->resized_file),
|
|
|
|
'name' => G\get_filename_without_extension($this->resized_file),
|
|
|
|
'fileinfo' => G\get_image_fileinfo($this->resized_file)
|
|
|
|
];
|
|
|
|
|
|
|
|
}
|
2018-08-16 18:51:52 +00:00
|
|
|
|
2016-08-18 20:39:31 +00:00
|
|
|
// @Exception 1xx
|
|
|
|
protected function validateInput() {
|
2018-08-16 18:51:52 +00:00
|
|
|
|
2016-08-18 20:39:31 +00:00
|
|
|
$check_missing = ['source'];
|
|
|
|
missing_values_to_exception($this, 'CHV\ImageresizeException', $check_missing, 100);
|
2018-08-16 18:51:52 +00:00
|
|
|
|
2016-08-18 20:39:31 +00:00
|
|
|
if(!$this->width and !$this->height) {
|
|
|
|
throw new ImageresizeException('Missing ' . '$width and/or ' . '$height', 102);
|
|
|
|
}
|
2018-08-16 18:51:52 +00:00
|
|
|
|
2016-08-18 20:39:31 +00:00
|
|
|
if(!$this->destination) {
|
|
|
|
$this->destination = G\add_ending_slash(dirname($this->source));
|
|
|
|
}
|
|
|
|
|
|
|
|
// Validate $source file
|
|
|
|
if(!file_exists($this->source)) {
|
|
|
|
throw new ImageresizeException("Source file doesn't exists", 110);
|
|
|
|
}
|
2018-08-16 18:51:52 +00:00
|
|
|
|
2016-08-18 20:39:31 +00:00
|
|
|
// $source file looks like an image?
|
|
|
|
$this->source_image_fileinfo = G\get_image_fileinfo($this->source);
|
|
|
|
if(!$this->source_image_fileinfo) {
|
|
|
|
throw new ImageresizeException("Can't get source image info", 111);
|
|
|
|
}
|
2018-08-16 18:51:52 +00:00
|
|
|
|
2016-08-18 20:39:31 +00:00
|
|
|
// Validate $destination
|
|
|
|
if(!is_dir($this->destination)) {
|
2018-08-16 18:51:52 +00:00
|
|
|
|
2016-08-18 20:39:31 +00:00
|
|
|
// Try to create the missing directory
|
|
|
|
$old_umask = umask(0);
|
|
|
|
$make_destination = @mkdir($this->destination, 0755, true);
|
|
|
|
umask($old_umask);
|
2018-08-16 18:51:52 +00:00
|
|
|
|
2016-08-18 20:39:31 +00:00
|
|
|
if(!$make_destination) {
|
|
|
|
throw new ImageresizeException('$destination ' . $this->destination . ' is not a dir', 120);
|
|
|
|
}
|
2018-08-16 18:51:52 +00:00
|
|
|
|
2016-08-18 20:39:31 +00:00
|
|
|
}
|
2018-08-16 18:51:52 +00:00
|
|
|
|
2016-08-18 20:39:31 +00:00
|
|
|
// Can read $destination dir? -> note: We only need to write and read the target file, no this dir.
|
|
|
|
/*
|
|
|
|
if(!is_readable($this->destination)) {
|
|
|
|
throw new ImageresizeException("Can't read target destination dir " . $this->destination, 121);
|
|
|
|
}
|
|
|
|
*/
|
2018-08-16 18:51:52 +00:00
|
|
|
|
2016-08-18 20:39:31 +00:00
|
|
|
// Can write $destination dir?
|
|
|
|
if(!is_writable ($this->destination)) {
|
|
|
|
throw new ImageresizeException("Can't write target destination dir " . $this->destination, 122);
|
|
|
|
}
|
2018-08-16 18:51:52 +00:00
|
|
|
|
2016-08-18 20:39:31 +00:00
|
|
|
// Validate width and height
|
|
|
|
if($this->width and !is_int($this->width)) {
|
|
|
|
throw new ImageresizeException('Expecting integer value in $width, ' . gettype($this->width) . ' given', 130);
|
|
|
|
}
|
2018-08-16 18:51:52 +00:00
|
|
|
|
2016-08-18 20:39:31 +00:00
|
|
|
if($this->height and !is_int($this->height)) {
|
|
|
|
throw new ImageresizeException('Expecting integer value in $height, ' . gettype($this->width) . ' given', 131);
|
|
|
|
}
|
2018-08-16 18:51:52 +00:00
|
|
|
|
2016-08-18 20:39:31 +00:00
|
|
|
}
|
2018-08-16 18:51:52 +00:00
|
|
|
|
2016-08-18 20:39:31 +00:00
|
|
|
// @Exception 2xx
|
|
|
|
protected function resize_image() {
|
2018-08-16 18:51:52 +00:00
|
|
|
|
2016-08-18 20:39:31 +00:00
|
|
|
// Fix the $width and $height vars
|
|
|
|
if($this->width and $this->height) {
|
|
|
|
$this->set_fixed();
|
|
|
|
} else {
|
|
|
|
if($this->fixed) {
|
|
|
|
if($this->width) {
|
|
|
|
$this->height = $this->width;
|
|
|
|
} else {
|
|
|
|
$this->width = $this->height;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if($this->width) {
|
|
|
|
$this->height = intval(round($this->width / $this->source_image_fileinfo['ratio']));
|
|
|
|
} else {
|
|
|
|
$this->width = intval(round($this->height * $this->source_image_fileinfo['ratio']));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
$imageSX = $this->source_image_fileinfo['width'];
|
|
|
|
$imageSY = $this->source_image_fileinfo['height'];
|
2018-08-16 18:51:52 +00:00
|
|
|
|
2016-08-18 20:39:31 +00:00
|
|
|
// Do we actually need to resize?
|
|
|
|
if($this->width == $imageSX and $this->height == $imageSY and !$this->options['forced']) {
|
|
|
|
@copy($this->source, $this->resized_file);
|
|
|
|
return;
|
|
|
|
}
|
2016-10-12 16:32:18 +00:00
|
|
|
@ini_set('gd.jpeg_ignore_warning', 1);
|
2016-08-18 20:39:31 +00:00
|
|
|
switch($this->file_extension) {
|
|
|
|
case 'gif':
|
|
|
|
$src = imagecreatefromgif($this->source);
|
|
|
|
break;
|
|
|
|
case 'png':
|
|
|
|
$src = imagecreatefrompng($this->source);
|
|
|
|
break;
|
|
|
|
case 'jpg':
|
|
|
|
$src = imagecreatefromjpeg($this->source);
|
|
|
|
break;
|
|
|
|
}
|
2018-08-16 18:51:52 +00:00
|
|
|
|
2016-08-18 20:39:31 +00:00
|
|
|
// Invalid SRC
|
|
|
|
if(!$src) {
|
|
|
|
throw new ImageresizeException("Can't create image from source", 210);
|
|
|
|
}
|
2018-08-16 18:51:52 +00:00
|
|
|
|
2016-08-18 20:39:31 +00:00
|
|
|
if($this->fixed) {
|
|
|
|
$source_ratio = $this->source_image_fileinfo['ratio'];
|
|
|
|
$destination_ratio = $this->width / $this->height;
|
2018-08-16 18:51:52 +00:00
|
|
|
|
2016-08-18 20:39:31 +00:00
|
|
|
// Ratio thing
|
|
|
|
if ($destination_ratio > $source_ratio) {
|
|
|
|
$ratio_height = round($this->width / $source_ratio);
|
|
|
|
$ratio_width = $this->width;
|
|
|
|
} else {
|
|
|
|
$ratio_width = round($this->height * $source_ratio);
|
|
|
|
$ratio_height = $this->height;
|
|
|
|
}
|
2018-08-16 18:51:52 +00:00
|
|
|
|
2016-08-18 20:39:31 +00:00
|
|
|
$target = imagecreatetruecolor($ratio_width, $ratio_height);
|
2018-08-16 18:51:52 +00:00
|
|
|
|
2016-08-18 20:39:31 +00:00
|
|
|
$x_center = $ratio_width / 2;
|
|
|
|
$y_center = $ratio_height / 2;
|
2018-08-16 18:51:52 +00:00
|
|
|
|
2016-08-18 20:39:31 +00:00
|
|
|
} else {
|
|
|
|
$target = imagecreatetruecolor($this->width, $this->height);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Copies SRC to TARGET
|
|
|
|
// Allocate SRC transparency
|
|
|
|
if(preg_match('/^(png|gif)$/', $this->file_extension)) {
|
|
|
|
G\image_allocate_transparency($src, $this->file_extension);
|
|
|
|
G\image_copy_transparency($src, $target);
|
|
|
|
}
|
2018-08-16 18:51:52 +00:00
|
|
|
|
2016-08-18 20:39:31 +00:00
|
|
|
if($this->fixed) {
|
2018-08-16 18:51:52 +00:00
|
|
|
|
2017-05-21 20:19:25 +00:00
|
|
|
self::imagecopyresampled($target, $src, 0, 0, 0, 0, $ratio_width, $ratio_height, $imageSX, $imageSY);
|
2016-08-18 20:39:31 +00:00
|
|
|
$process = imagecreatetruecolor($this->width, $this->height);
|
2018-08-16 18:51:52 +00:00
|
|
|
|
2016-08-18 20:39:31 +00:00
|
|
|
// Re-allocate the transparency
|
|
|
|
if($this->file_extension == 'gif') {
|
|
|
|
G\image_copy_transparency($process, $target);
|
|
|
|
G\image_copy_transparency($target, $process);
|
|
|
|
}
|
|
|
|
if($this->file_extension == 'png') {
|
|
|
|
G\image_allocate_transparency($process, $this->file_extension);
|
|
|
|
G\image_allocate_transparency($target, $this->file_extension);
|
2018-08-16 18:51:52 +00:00
|
|
|
}
|
2017-05-21 20:19:25 +00:00
|
|
|
self::imagecopyresampled($process, $target, 0, 0, ($x_center - ($this->width / 2)), ($y_center - ($this->height / 2)), $this->width, $this->height, $this->width, $this->height);
|
2016-08-18 20:39:31 +00:00
|
|
|
imagedestroy($target);
|
2018-08-16 18:51:52 +00:00
|
|
|
|
2016-08-18 20:39:31 +00:00
|
|
|
} else {
|
|
|
|
//if($this->file_extension == "gif") G\image_copy_transparency($target, $process);
|
|
|
|
if($this->file_extension == 'png') G\image_allocate_transparency($target, $this->file_extension);
|
2017-05-21 20:19:25 +00:00
|
|
|
self::imagecopyresampled($target, $src, 0, 0, 0, 0, $this->width, $this->height, $imageSX, $imageSY);
|
2016-08-18 20:39:31 +00:00
|
|
|
$process = $target;
|
|
|
|
}
|
2018-08-16 18:51:52 +00:00
|
|
|
|
2016-08-18 20:39:31 +00:00
|
|
|
// Sharpen the image just for JPG
|
|
|
|
// This needs a little more debug since GD 2.1.1 (dev)
|
|
|
|
/*
|
|
|
|
if($this->file_extension == 'jpg') {
|
|
|
|
$matrix = array(array(-1, -1, -1), array(-1, 32, -1), array(-1, -1, -1));
|
|
|
|
$divisor = array_sum(array_map('array_sum', $matrix));
|
|
|
|
imageconvolution($process, $matrix, $divisor, 0);
|
|
|
|
}
|
|
|
|
*/
|
2018-08-16 18:51:52 +00:00
|
|
|
|
2016-08-18 20:39:31 +00:00
|
|
|
// Creates the image
|
|
|
|
switch($this->file_extension) {
|
|
|
|
case 'gif':
|
|
|
|
$output_image = imagegif($process, $this->resized_file);
|
|
|
|
break;
|
|
|
|
case 'png':
|
|
|
|
$output_image = imagepng($process, $this->resized_file);
|
|
|
|
break;
|
|
|
|
case 'jpg':
|
|
|
|
$output_image = imagejpeg($process, $this->resized_file, 90);
|
|
|
|
break;
|
|
|
|
}
|
2018-08-16 18:51:52 +00:00
|
|
|
|
2016-08-18 20:39:31 +00:00
|
|
|
if(!$output_image) {
|
|
|
|
throw new ImageresizeException("Can't create final output image", 220);
|
|
|
|
}
|
2018-08-16 18:51:52 +00:00
|
|
|
|
2016-08-18 20:39:31 +00:00
|
|
|
// Remove the temp files
|
2018-08-16 18:51:52 +00:00
|
|
|
imagedestroy($process);
|
|
|
|
imagedestroy($src);
|
2016-08-18 20:39:31 +00:00
|
|
|
}
|
2018-08-16 18:51:52 +00:00
|
|
|
|
2017-05-21 20:19:25 +00:00
|
|
|
// http://stackoverflow.com/questions/12661/efficient-jpeg-image-resizing-in-php
|
|
|
|
public static function imagecopyresampled(&$dst_image, $src_image, $dst_x, $dst_y, $src_x, $src_y, $dst_w, $dst_h, $src_w, $src_h, $quality = 4) {
|
|
|
|
//return imagecopyresampled($dst_image, $src_image, $dst_x, $dst_y, $src_x, $src_y, $dst_w, $dst_h, $src_w, $src_h);
|
|
|
|
// Plug-and-Play fastimagecopyresampled function replaces much slower imagecopyresampled.
|
|
|
|
// Just include this function and change all "imagecopyresampled" references to "fastimagecopyresampled".
|
|
|
|
// Typically from 30 to 60 times faster when reducing high resolution images down to thumbnail size using the default quality setting.
|
|
|
|
// Author: Tim Eckel - Date: 09/07/07 - Version: 1.1 - Project: FreeRingers.net - Freely distributable - These comments must remain.
|
|
|
|
//
|
|
|
|
// Optional "quality" parameter (defaults is 3). Fractional values are allowed, for example 1.5. Must be greater than zero.
|
|
|
|
// Between 0 and 1 = Fast, but mosaic results, closer to 0 increases the mosaic effect.
|
|
|
|
// 1 = Up to 350 times faster. Poor results, looks very similar to imagecopyresized.
|
|
|
|
// 2 = Up to 95 times faster. Images appear a little sharp, some prefer this over a quality of 3.
|
|
|
|
// 3 = Up to 60 times faster. Will give high quality smooth results very close to imagecopyresampled, just faster.
|
|
|
|
// 4 = Up to 25 times faster. Almost identical to imagecopyresampled for most images.
|
|
|
|
// 5 = No speedup. Just uses imagecopyresampled, no advantage over imagecopyresampled.
|
|
|
|
if(empty($src_image) || empty($dst_image) || $quality <= 0) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if($quality < 5 && (($dst_w * $quality) < $src_w || ($dst_h * $quality) < $src_h)) {
|
|
|
|
$temp = imagecreatetruecolor($dst_w * $quality + 1, $dst_h * $quality + 1);
|
|
|
|
imagecopyresized($temp, $src_image, 0, 0, $src_x, $src_y, $dst_w * $quality + 1, $dst_h * $quality + 1, $src_w, $src_h);
|
|
|
|
imagecopyresampled($dst_image, $temp, $dst_x, $dst_y, 0, 0, $dst_w, $dst_h, $dst_w * $quality, $dst_h * $quality);
|
|
|
|
imagedestroy($temp);
|
|
|
|
} else {
|
|
|
|
imagecopyresampled($dst_image, $src_image, $dst_x, $dst_y, $src_x, $src_y, $dst_w, $dst_h, $src_w, $src_h);
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
2016-08-18 20:39:31 +00:00
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
class ImageresizeException extends Exception {}
|