Файл: oc-includes/osclass/classes/ImageResizer.php
Строк: 240
<?php if ( ! defined('ABS_PATH')) exit('ABS_PATH is not loaded. Direct access is not allowed.');
/*
* Copyright 2014 Osclass
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* This class represents a utility to load and resize images easily.
*/
class ImageResizer {
public static function fromFile($imagePath) {
return new ImageResizer($imagePath);
}
private $im;
private $image_info;
private $ext;
private $mime;
private $_font;
private $_color;
private $_width;
private $_height;
private $_exif;
private $_watermarked = false;
private function __construct($imagePath) {
if(!file_exists($imagePath)) { throw new Exception(sprintf(__("%s does not exist!"), $imagePath)); };
if(!is_readable($imagePath)) { throw new Exception(sprintf(__("%s is not readable!"), $imagePath)); };
if(filesize($imagePath)==0) { throw new Exception(sprintf(__("%s is corrupt or broken!"), $imagePath)); };
$this->image_info = @getimagesize($imagePath);
if(osc_use_imagick()) {
$this->im = new Imagick($imagePath);
$geometry = $this->im->getImageGeometry();
$this->_width = $geometry['width'];
$this->_height = $geometry['height'];
} else {
$content = file_get_contents($imagePath);
$this->im = imagecreatefromstring($content);
$this->_width = imagesx($this->im);
$this->_height = imagesy($this->im);
$this->_exif = array();
if(@$this->image_info['mime']=='image/jpeg' && function_exists('exif_read_data')) {
$this->_exif = exif_read_data($imagePath);
}
}
switch (@$this->image_info['mime']) {
case 'image/gif':
case 'image/png':
$this->ext = 'png';
$this->mime = 'image/png';
break;
default:
$this->ext = 'jpg';
$this->mime = 'image/jpeg';
if(!osc_use_imagick()) {
$bg = imagecreatetruecolor($this->_width, $this->_height);
imagefill($bg, 0, 0, imagecolorallocatealpha($bg, 255, 255, 255, 127));
imagesavealpha($bg, true);
imagealphablending($bg, TRUE);
imagecopy($bg, $this->im, 0, 0, 0, 0, $this->_width, $this->_height);
imagedestroy($this->im);
$this->im = $bg;
}
break;
}
return $this;
}
public function __destruct() {
if(osc_use_imagick()) {
$this->im->destroy();
} else {
imagedestroy($this->im);
}
}
public function getExt() { return $this->ext; }
public function getMime() { return $this->mime; }
public function resizeTo($width, $height, $force_aspect = null, $upscale = true) {
if($force_aspect==null) {
$force_aspect = osc_force_aspect_image();
}
if(($this->_width/$this->_height)>=($width/$height)) {
if($upscale) { $newW = $width; } else { $newW = ($this->_width> $width)? $width : $this->_width; };
$newH = ceil($this->_height * ($newW / $this->_width));
if($force_aspect) { $height = $newH; }
} else {
if($upscale) { $newH = $height; } else { $newH = ($this->_height > $height)? $height : $this->_height; };
$newW = ceil($this->_width* ($newH / $this->_height));
if($force_aspect) { $width = $newW; }
}
if(osc_use_imagick()) {
$bg = new Imagick();
if($this->ext=='jpg') {
$bg->newImage($width, $height, 'white');
} else {
$bg->newImage($width, $height, 'none');
}
$this->im->thumbnailImage($width, $height, true);
$bg->compositeImage( $this->im, imagick::COMPOSITE_OVER, floor(($width-$newW)/2), floor(($height-$newH)/2));
$this->im = $bg;
} else {
$newIm = imagecreatetruecolor($width,$height);
imagealphablending($newIm, false);
$colorTransparent = imagecolorallocatealpha($newIm, 255, 255, 255, 127);
imagefill($newIm, 0, 0, $colorTransparent);
imagesavealpha($newIm, true);
imagecopyresampled($newIm, $this->im, floor(($width-$newW)/2), floor(($height-$newH)/2), 0, 0, $newW, $newH, $this->_width, $this->_height);
imagedestroy($this->im);
$this->im = $newIm;
}
$this->_width = $width;
$this->_height = $height;
return $this;
}
public function saveToFile($imagePath, $ext = null) {
if(file_exists($imagePath) && !is_writable($imagePath)) { throw new Exception("$imagePath is not writable!"); };
if($ext==null) { $ext = $this->ext; };
if($ext!='png' && $ext!='gif') { $ext = 'jpeg'; };
if(osc_use_imagick()) {
if($ext=='jpeg' && ($this->ext!='jpeg' && $this->ext!='jpg')) {
$bg = new Imagick();
$bg->newImage($this->_width, $this->_height, 'white');
$this->im->thumbnailImage($this->_width, $this->_height, true);
$bg->compositeImage( $this->im, imagick::COMPOSITE_OVER, 0, 0);
$this->im = $bg;
$this->ext = 'jpeg';
}
$this->im->setImageFileName($imagePath);
$this->im->setImageFormat($ext);
$this->im->writeImage($imagePath);
} else {
switch ($ext) {
case 'gif':
case 'png':
imagepng($this->im, $imagePath, 0);
break;
default:
if(($ext=='jpeg' && ($this->ext!='jpeg' && $this->ext!='jpg')) || $this->_watermarked) {
$this->ext = 'jpeg';
}
imagejpeg($this->im, $imagePath);
break;
}
}
}
public function autoRotate() {
if(osc_use_imagick()) {
switch($this->im->getImageOrientation()) {
case imagick::ORIENTATION_TOPRIGHT:
$this->im->flopImage();
break;
case imagick::ORIENTATION_BOTTOMRIGHT:
$this->im->rotateimage(new ImagickPixel('none'), 180); // rotate 180 degrees
break;
case imagick::ORIENTATION_BOTTOMLEFT:
$this->im->flopImage();
$this->im->rotateImage(new ImagickPixel('none'), 180);
break;
case imagick::ORIENTATION_LEFTTOP:
$this->im->flopImage();
$this->im->rotateImage(new ImagickPixel('none'), -90);
break;
case imagick::ORIENTATION_RIGHTTOP:
$this->im->rotateimage(new ImagickPixel('none'), 90); // rotate 90 degrees CW
break;
case imagick::ORIENTATION_RIGHTBOTTOM:
$this->im->flopImage();
$this->im->rotateImage(new ImagickPixel('none'), 90);
break;
case imagick::ORIENTATION_LEFTBOTTOM:
$this->im->rotateimage(new ImagickPixel('none'), -90); // rotate 90 degrees CCW
break;
default:
// DO NOTHING, THE IMAGE IS OK OR WE DON'T KNOW IF IT'S ROTATED
break;
}
} else {
if(isset($this->_exif['Orientation'])) {
switch($this->_exif['Orientation']) {
case 1:
default:
// DO NOTHING, THE IMAGE IS OK OR WE DON'T KNOW IF IT'S ROTATED
break;
case 2:
imageflip($this->im, IMG_FLIP_HORIZONTAL);
break;
case 3:
$this->im = imagerotate($this->im, 180, 0);
break;
case 4:
$this->im = imagerotate($this->im, 180, 0);
imageflip($this->im, IMG_FLIP_HORIZONTAL);
break;
case 5:
$this->im = imagerotate($this->im, 270, 0);
imageflip($this->im, IMG_FLIP_HORIZONTAL);
$aux = $this->_height;
$this->_height = $this->_width;
$this->_width = $aux;
break;
case 6:
$this->im = imagerotate($this->im, -90, 0);
$aux = $this->_height;
$this->_height = $this->_width;
$this->_width = $aux;
break;
case 7:
$this->im = imagerotate($this->im, 90, 0);
imageflip($this->im, IMG_FLIP_HORIZONTAL);
$aux = $this->_height;
$this->_height = $this->_width;
$this->_width = $aux;
break;
case 8:
$this->im = imagerotate($this->im, 90, 0);
$aux = $this->_height;
$this->_height = $this->_width;
$this->_width = $aux;
break;
}
$this->_exif['Orientation'] = 1;
}
}
return $this;
}
public function show() {
header('Content-Disposition: Attachment;filename=image.'.$this->ext);
header('Content-type: '.$this->mime);
if(osc_use_imagick()) {
} else {
switch ($this->ext) {
case 'gif':
case 'png':
imagepng($this->im);
break;
default:
imagejpeg($this->im);
break;
}
}
}
public function doWatermarkText($text, $color = 'ff0000') {
$this->_watermarked = true;
$this->_font = osc_apply_filter('watermark_font_path', LIB_PATH . "osclass/assets/fonts/Arial.ttf");
if(osc_use_imagick()) {
$draw = new ImagickDraw();
$draw->setFillColor("#".$color);
$draw->setFont($this->_font);
$draw->setFontSize( 30 );
$metrics = $this->im->queryFontMetrics($draw, $text);
switch(osc_watermark_place()) {
case 'tl':
$offset['x'] = 1;
$offset['y'] = $metrics['ascender']+1;
break;
case 'tr':
$offset['x'] = $this->_width - $metrics['textWidth']-1;
$offset['y'] = $metrics['ascender']+1;
break;
case 'bl':
$offset['x'] = 1;
$offset['y'] = $this->_height-1;
break;
case 'br':
$offset['x'] = $this->_width - $metrics['textWidth']-1;
$offset['y'] = $this->_height-1;
break;
default:
$offset['x'] = ($this->_width / 2) - ($metrics['textWidth'] / 2);
$offset['y'] = ($this->_height / 2) - ($metrics['ascender'] / 2);
break;
}
$this->im->annotateImage($draw, $offset['x'], $offset['y'], 0, $text);
} else {
imagealphablending( $this->im, true );
imagesavealpha( $this->im, true );
if($this->ext!='jpg') {
$white = imagecolorallocatealpha($this->im, 255, 255, 255, 127);
imagefill($this->im, 0, 0, $white);
}
$color = $this->_imageColorAllocateHex($color);
$offset = $this->_calculateOffset($text);
imagettftext($this->im, 20, 0, $offset['x'], $offset['y'], $color, $this->_font , html_entity_decode($text, null, "UTF-8"));
}
return $this;
}
private function _imageColorAllocateHex($hexstr) {
$red = hexdec(substr($hexstr, 0, 2));
$green = hexdec(substr($hexstr, 2, 2));
$blue = hexdec(substr($hexstr, 4, 2));
return imagecolorallocate($this->im, $red, $green, $blue);
}
private function _calculateOffset($text) {
$offset = array('x' => 0, 'y' => 0);
$bbox = $this->_calculateBBox($text);
switch( osc_watermark_place() ) {
case 'tl':
$offset['x'] = $bbox['height'];
$offset['y'] = $bbox['height'] * 1.5;
break;
case 'tr':
$offset['x'] = $this->_width - ($bbox['width'] + $bbox['height']);
$offset['y'] = $bbox['height'] * 1.5;
break;
case 'bl':
$offset['x'] = $bbox['height'];
$offset['y'] = $this->_height - $bbox['height'];
break;
case 'br':
$offset['x'] = $this->_width - ($bbox['width'] + $bbox['height']);
$offset['y'] = $this->_height - $bbox['height'];
break;
default:
$offset['x'] = ($this->_width / 2) - ($bbox['top_right']['x'] / 2);
$offset['y'] = ($this->_height / 2) - ($bbox['top_right']['y'] / 2);
break;
}
return $offset;
}
private function _calculateBBox($text) {
$bbox = imagettfbbox(
20,
0,
$this->_font,
$text
);
$bbox = array(
'bottom_left' => array(
'x' => $bbox[0],
'y' => $bbox[1]
),
'bottom_right' => array(
'x' => $bbox[2],
'y' => $bbox[3]
),
'top_right' => array(
'x' => $bbox[4],
'y' => $bbox[5]
),
'top_left' => array(
'x' => $bbox[6],
'y' => $bbox[7]
)
);
$bbox['width'] = $bbox['top_right']['x'] - $bbox['top_left']['x'];
$bbox['height'] = $bbox['bottom_left']['y'] - $bbox['top_left']['y'];
return $bbox;
}
public function doWatermarkImage()
{
$this->_watermarked = true;
$path_watermark = osc_uploads_path() . 'watermark.png';
if(osc_use_imagick()) {
$wm = new Imagick($path_watermark);
$wgeo = $wm->getImageGeometry();
switch(osc_watermark_place()) {
case 'tl':
$dest_x = 0;
$dest_y = 0;
break;
case 'tr':
$dest_x = $this->_width - $wgeo['width'];
$dest_y = 0;
break;
case 'bl':
$dest_x = 0;
$dest_y = $this->_height - $wgeo['height'];
break;
case 'br':
$dest_x = $this->_width - $wgeo['width'];
$dest_y = $this->_height - $wgeo['height'];
break;
default:
$dest_x = ($this->_width-$wgeo['width'])/2;
$dest_y = ($this->_height-$wgeo['height'])/2;
break;
}
$this->im->compositeImage( $wm, imagick::COMPOSITE_OVER, $dest_x, $dest_y );
$wm->destroy();
} else {
$watermark = imagecreatefrompng( $path_watermark );
$watermark_width = imagesx($watermark);
$watermark_height = imagesy($watermark);
switch(osc_watermark_place()) {
case 'tl':
$dest_x = 0;
$dest_y = 0;
break;
case 'tr':
$dest_x = $this->_width - $watermark_width;
$dest_y = 0;
break;
case 'bl':
$dest_x = 0;
$dest_y = $this->_height - $watermark_height;
break;
case 'br':
$dest_x = $this->_width - $watermark_width;
$dest_y = $this->_height - $watermark_height;
break;
default:
$dest_x = ($this->_width-$watermark_width)/2;
$dest_y = ($this->_height-$watermark_height)/2;
break;
}
$this->_imagecopymerge_alpha($this->im, $watermark, $dest_x, $dest_y, 0, 0, $watermark_width, $watermark_height, 100);
imagedestroy($watermark);
}
return $this;
}
private function _imagecopymerge_alpha(&$dst_im, $src_im, $dst_x, $dst_y, $src_x, $src_y, $src_w, $src_h, $pct, $trans = NULL)
{
imagealphablending( $dst_im, false );
imagesavealpha( $dst_im, true );
$dst_w = imagesx($dst_im);
$dst_h = imagesy($dst_im);
$src_x = max($src_x, 0);
$src_y = max($src_y, 0);
$dst_x = max($dst_x, 0);
$dst_y = max($dst_y, 0);
if ($dst_x + $src_w > $dst_w)
$src_w = $dst_w - $dst_x;
if ($dst_y + $src_h > $dst_h)
$src_h = $dst_h - $dst_y;
for($x_offset = 0; $x_offset < $src_w; $x_offset++) {
for($y_offset = 0; $y_offset < $src_h; $y_offset++) {
$srccolor = imagecolorsforindex($src_im, imagecolorat($src_im, $src_x + $x_offset, $src_y + $y_offset));
$dstcolor = imagecolorsforindex($dst_im, imagecolorat($dst_im, $dst_x + $x_offset, $dst_y + $y_offset));
if (is_null($trans) || ($srccolor !== $trans))
{
$src_a = $srccolor['alpha'] * $pct / 100;
// blend
$src_a = 127 - $src_a;
$dst_a = 127 - $dstcolor['alpha'];
$dst_r = ($srccolor['red'] * $src_a + $dstcolor['red'] * $dst_a * (127 - $src_a) / 127) / 127;
$dst_g = ($srccolor['green'] * $src_a + $dstcolor['green'] * $dst_a * (127 - $src_a) / 127) / 127;
$dst_b = ($srccolor['blue'] * $src_a + $dstcolor['blue'] * $dst_a * (127 - $src_a) / 127) / 127;
$dst_a = 127 - ($src_a + $dst_a * (127 - $src_a) / 127);
$color = imagecolorallocatealpha($dst_im, $dst_r, $dst_g, $dst_b, $dst_a);
if (!imagesetpixel($dst_im, $dst_x + $x_offset, $dst_y + $y_offset, $color))
return false;
imagecolordeallocate($dst_im, $color);
}
}
}
return true;
}
}
if(!function_exists('imageflip')) {
function imageflip(&$image, $x = 0, $y = 0, $width = null, $height = null)
{
if ($width < 1) $width = imagesx($image);
if ($height < 1) $height = imagesy($image);
// Truecolor provides better results, if possible.
if (function_exists('imageistruecolor') && imageistruecolor($image))
{
$tmp = imagecreatetruecolor(1, $height);
}
else
{
$tmp = imagecreate(1, $height);
}
$x2 = $x + $width - 1;
for ($i = (int) floor(($width - 1) / 2); $i >= 0; $i--)
{
// Backup right stripe.
imagecopy($tmp, $image, 0, 0, $x2 - $i, $y, 1, $height);
// Copy left stripe to the right.
imagecopy($image, $image, $x2 - $i, $y, $x + $i, $y, 1, $height);
// Copy backuped right stripe to the left.
imagecopy($image, $tmp, $x + $i, $y, 0, 0, 1, $height);
}
imagedestroy($tmp);
return $image;
}
define('IMG_FLIP_HORIZONTAL', 0);
}