chevereto-free/app/lib/classes/class.imageresize.php

327 lines
11 KiB
PHP

<?php
/* --------------------------------------------------------------------
This file is part of Chevereto Free.
https://chevereto.com/free
(c) Rodolfo Berrios <rodolfo@chevereto.com>
For the full copyright and license information, please view the LICENSE
file that was distributed with this source code.
--------------------------------------------------------------------- */
namespace CHV;
use G;
use Exception;
class Imageresize
{
// filename => name.ext
// file => /full/path/to/name.ext
// name => name
public $resized;
public function setSource($source)
{
clearstatcache(true, $source);
$this->source = $source;
}
public function setDestination($destination)
{
$this->destination = $destination;
}
public function setFilename($name)
{
$this->filename = $name;
}
// Set options
public function setOptions($options)
{
$this->options = $options;
}
// Set individual option
public function setOption($key, $value)
{
$this->options[$key] = $value;
}
public function set_width($width)
{
$this->width = intval($width);
}
public function set_height($height)
{
$this->height = intval($height);
}
public function set_fixed()
{
$this->fixed = true;
}
/**
* Do the thing.
*
* @Exception 4xx
*/
public function exec()
{
$this->validateInput(); // Exception 1xx
// Save the source filename
$source_filename = G\get_basename_without_extension($this->source);
// Set file extension
$this->file_extension = $this->source_image_fileinfo['extension'];
// Workaround the $filename
if (!$this->filename) {
$this->filename = $source_filename;
}
// Fix file extension
if (G\get_file_extension($this->filename) == $this->resized_file_extension) {
$this->filename = G\get_basename_without_extension($this->filename);
}
// Fix the destination path
$this->destination = G\add_ending_slash($this->destination);
// Set $resized_file
$this->resized_file = $this->destination . $this->filename . '.' . $this->file_extension;
// Do the resize process
$this->resize_image();
$this->resized = [
'file' => $this->resized_file,
'filename' => G\get_filename($this->resized_file),
'name' => G\get_basename_without_extension($this->resized_file),
'fileinfo' => G\get_image_fileinfo($this->resized_file),
];
}
// @Exception 1xx
protected function validateInput()
{
$check_missing = ['source'];
missing_values_to_exception($this, 'CHV\ImageresizeException', $check_missing, 100);
if (!$this->width and !$this->height) {
throw new ImageresizeException('Missing ' . '$width and/or ' . '$height', 102);
}
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);
}
// $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);
}
// Validate $destination
if (!is_dir($this->destination)) {
// Try to create the missing directory
$old_umask = umask(0);
$make_destination = @mkdir($this->destination, 0755, true);
umask($old_umask);
if (!$make_destination) {
throw new ImageresizeException('$destination ' . $this->destination . ' is not a dir', 120);
}
}
// 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);
}
*/
// Can write $destination dir?
if (!is_writable($this->destination)) {
throw new ImageresizeException("Can't write target destination dir " . $this->destination, 122);
}
// 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);
}
if ($this->height and !is_int($this->height)) {
throw new ImageresizeException('Expecting integer value in $height, ' . gettype($this->width) . ' given', 131);
}
}
// @Exception 2xx
protected function resize_image()
{
// 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'];
// 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;
}
@ini_set('gd.jpeg_ignore_warning', 1);
$ext = $this->file_extension == 'jpg' ? 'jpeg' : $this->file_extension;
$imagecreatefrom = 'imagecreatefrom' . $ext;
$src = $imagecreatefrom($this->source);
// Invalid SRC
if (!$src) {
throw new ImageresizeException("Can't create image from source", 210);
}
if ($this->fixed) {
$source_ratio = $this->source_image_fileinfo['ratio'];
$destination_ratio = $this->width / $this->height;
// 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;
}
$target = imagecreatetruecolor($ratio_width, $ratio_height);
$x_center = $ratio_width / 2;
$y_center = $ratio_height / 2;
} 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);
}
if ($this->fixed) {
self::imagecopyresampled($target, $src, 0, 0, 0, 0, $ratio_width, $ratio_height, $imageSX, $imageSY);
$process = imagecreatetruecolor($this->width, $this->height);
// 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);
}
self::imagecopyresampled($process, $target, 0, 0, ($x_center - ($this->width / 2)), ($y_center - ($this->height / 2)), $this->width, $this->height, $this->width, $this->height);
imagedestroy($target);
} 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);
}
self::imagecopyresampled($target, $src, 0, 0, 0, 0, $this->width, $this->height, $imageSX, $imageSY);
$process = $target;
}
// 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);
}
*/
// 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;
case 'webp':
$output_image = imagewebp($process, $this->resized_file, 90);
break;
}
if (!$output_image) {
throw new ImageresizeException("Can't create final output image", 220);
}
if (!file_exists($this->resized_file)) {
throw new ImageresizeException("Can't create final output image", 230);
}
// Remove the temp files
imagedestroy($process);
imagedestroy($src);
}
// 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)
{
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;
}
}
class ImageresizeException extends Exception
{
}