Refactor photo_driver to use namespaces.
Add simple UnitTest, but it is not yet very meaningful.
This commit is contained in:
parent
d70bba2806
commit
e6dadb215e
468
Zotlabs/Photo/PhotoDriver.php
Normal file
468
Zotlabs/Photo/PhotoDriver.php
Normal file
@ -0,0 +1,468 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Zotlabs\Photo;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Abstract photo driver class.
|
||||||
|
*
|
||||||
|
* Inheritance seems not to be the best design pattern for such photo drivers.
|
||||||
|
*/
|
||||||
|
abstract class PhotoDriver {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief This variable keeps the image.
|
||||||
|
*
|
||||||
|
* For GD it is a PHP image resource.
|
||||||
|
* For ImageMagick it is an \Imagick object.
|
||||||
|
*
|
||||||
|
* @var resource|\Imagick
|
||||||
|
*/
|
||||||
|
protected $image;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var integer
|
||||||
|
*/
|
||||||
|
protected $width;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var integer
|
||||||
|
*/
|
||||||
|
protected $height;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var boolean
|
||||||
|
*/
|
||||||
|
protected $valid;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief The mimetype of the image.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $type;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Supported mimetypes by the used photo driver.
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected $types;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Return an array with supported mimetypes.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
* Associative array with mimetype as key and file extension as value.
|
||||||
|
*/
|
||||||
|
abstract public function supportedTypes();
|
||||||
|
|
||||||
|
abstract protected function load($data, $type);
|
||||||
|
|
||||||
|
abstract protected function destroy();
|
||||||
|
|
||||||
|
abstract protected function setDimensions();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Return the current image.
|
||||||
|
*
|
||||||
|
* @fixme Shouldn't his method be protected, because outside of the current
|
||||||
|
* driver it makes no sense at all because of the different return values.
|
||||||
|
*
|
||||||
|
* @return boolean|resource|\Imagick
|
||||||
|
* false on failure, a PHP image resource for GD driver, an \Imagick object
|
||||||
|
* for ImageMagick driver.
|
||||||
|
*/
|
||||||
|
abstract public function getImage();
|
||||||
|
|
||||||
|
abstract public function doScaleImage($new_width, $new_height);
|
||||||
|
|
||||||
|
abstract public function rotate($degrees);
|
||||||
|
|
||||||
|
abstract public function flip($horiz = true, $vert = false);
|
||||||
|
|
||||||
|
abstract public function cropImage($max, $x, $y, $w, $h);
|
||||||
|
|
||||||
|
abstract public function cropImageRect($maxx, $maxy, $x, $y, $w, $h);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Return a binary string from the image resource.
|
||||||
|
*
|
||||||
|
* @return string A Binary String.
|
||||||
|
*/
|
||||||
|
abstract public function imageString();
|
||||||
|
|
||||||
|
abstract public function clearexif();
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief PhotoDriver constructor.
|
||||||
|
*
|
||||||
|
* @param string $data Image
|
||||||
|
* @param string $type mimetype
|
||||||
|
*/
|
||||||
|
public function __construct($data, $type = '') {
|
||||||
|
$this->types = $this->supportedTypes();
|
||||||
|
if(! array_key_exists($type, $this->types)) {
|
||||||
|
$type = 'image/jpeg';
|
||||||
|
}
|
||||||
|
$this->type = $type;
|
||||||
|
$this->valid = false;
|
||||||
|
$this->load($data, $type);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __destruct() {
|
||||||
|
if($this->is_valid())
|
||||||
|
$this->destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Is it a valid image object.
|
||||||
|
*
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
public function is_valid() {
|
||||||
|
return $this->valid;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get the width of the image.
|
||||||
|
*
|
||||||
|
* @return boolean|number Width of image in pixels, or false on failure
|
||||||
|
*/
|
||||||
|
public function getWidth() {
|
||||||
|
if(! $this->is_valid())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return $this->width;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get the height of the image.
|
||||||
|
*
|
||||||
|
* @return boolean|number Height of image in pixels, or false on failure
|
||||||
|
*/
|
||||||
|
public function getHeight() {
|
||||||
|
if(! $this->is_valid())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return $this->height;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Saves the image resource to a file in filesystem.
|
||||||
|
*
|
||||||
|
* @param string $path Path and filename where to save the image
|
||||||
|
* @return boolean False on failure, otherwise true
|
||||||
|
*/
|
||||||
|
public function saveImage($path) {
|
||||||
|
if(! $this->is_valid())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return (file_put_contents($path, $this->imageString()) ? true : false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Return mimetype of the image resource.
|
||||||
|
*
|
||||||
|
* @return boolean|string False on failure, otherwise mimetype.
|
||||||
|
*/
|
||||||
|
public function getType() {
|
||||||
|
if(! $this->is_valid())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return $this->type;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Return file extension of the image resource.
|
||||||
|
*
|
||||||
|
* @return boolean|string False on failure, otherwise file extension.
|
||||||
|
*/
|
||||||
|
public function getExt() {
|
||||||
|
if(! $this->is_valid())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return $this->types[$this->getType()];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Scale image to max pixel size in either dimension.
|
||||||
|
*
|
||||||
|
* @param int $max maximum pixel size in either dimension
|
||||||
|
* @param boolean $float_height (optional)
|
||||||
|
* If true allow height to float to any length on tall images, constraining
|
||||||
|
* only the width
|
||||||
|
* @return boolean|void false on failure, otherwise void
|
||||||
|
*/
|
||||||
|
public function scaleImage($max, $float_height = true) {
|
||||||
|
if(! $this->is_valid())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
$width = $this->width;
|
||||||
|
$height = $this->height;
|
||||||
|
|
||||||
|
$dest_width = $dest_height = 0;
|
||||||
|
|
||||||
|
if((! $width) || (! $height))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if($width > $max && $height > $max) {
|
||||||
|
|
||||||
|
// very tall image (greater than 16:9)
|
||||||
|
// constrain the width - let the height float.
|
||||||
|
|
||||||
|
if(((($height * 9) / 16) > $width) && ($float_height)) {
|
||||||
|
$dest_width = $max;
|
||||||
|
$dest_height = intval(($height * $max) / $width);
|
||||||
|
} // else constrain both dimensions
|
||||||
|
elseif($width > $height) {
|
||||||
|
$dest_width = $max;
|
||||||
|
$dest_height = intval(($height * $max) / $width);
|
||||||
|
} else {
|
||||||
|
$dest_width = intval(($width * $max) / $height);
|
||||||
|
$dest_height = $max;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if($width > $max) {
|
||||||
|
$dest_width = $max;
|
||||||
|
$dest_height = intval(($height * $max) / $width);
|
||||||
|
} else {
|
||||||
|
if($height > $max) {
|
||||||
|
|
||||||
|
// very tall image (greater than 16:9)
|
||||||
|
// but width is OK - don't do anything
|
||||||
|
|
||||||
|
if(((($height * 9) / 16) > $width) && ($float_height)) {
|
||||||
|
$dest_width = $width;
|
||||||
|
$dest_height = $height;
|
||||||
|
} else {
|
||||||
|
$dest_width = intval(($width * $max) / $height);
|
||||||
|
$dest_height = $max;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$dest_width = $width;
|
||||||
|
$dest_height = $height;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$this->doScaleImage($dest_width, $dest_height);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function scaleImageUp($min) {
|
||||||
|
if(! $this->is_valid()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$width = $this->width;
|
||||||
|
$height = $this->height;
|
||||||
|
|
||||||
|
$dest_width = $dest_height = 0;
|
||||||
|
|
||||||
|
if((! $width) || (! $height))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if($width < $min && $height < $min) {
|
||||||
|
if($width > $height) {
|
||||||
|
$dest_width = $min;
|
||||||
|
$dest_height = intval(($height * $min) / $width);
|
||||||
|
} else {
|
||||||
|
$dest_width = intval(($width * $min) / $height);
|
||||||
|
$dest_height = $min;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if($width < $min) {
|
||||||
|
$dest_width = $min;
|
||||||
|
$dest_height = intval(($height * $min) / $width);
|
||||||
|
} else {
|
||||||
|
if($height < $min) {
|
||||||
|
$dest_width = intval(($width * $min) / $height);
|
||||||
|
$dest_height = $min;
|
||||||
|
} else {
|
||||||
|
$dest_width = $width;
|
||||||
|
$dest_height = $height;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$this->doScaleImage($dest_width, $dest_height);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Scales image to a square.
|
||||||
|
*
|
||||||
|
* @param int $dim Pixel of square image
|
||||||
|
* @return boolean|void false on failure, otherwise void
|
||||||
|
*/
|
||||||
|
public function scaleImageSquare($dim) {
|
||||||
|
if(! $this->is_valid())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
$this->doScaleImage($dim, $dim);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Reads exif data from a given filename.
|
||||||
|
*
|
||||||
|
* @param string $filename
|
||||||
|
* @return boolean|array
|
||||||
|
*/
|
||||||
|
public function exif($filename) {
|
||||||
|
if((! function_exists('exif_read_data'))
|
||||||
|
|| (! in_array($this->getType(), ['image/jpeg', 'image/tiff'])))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* PHP 7.2 allows you to use a stream resource, which should reduce/avoid
|
||||||
|
* memory exhaustion on large images.
|
||||||
|
*/
|
||||||
|
|
||||||
|
if(version_compare(PHP_VERSION, '7.2.0') >= 0) {
|
||||||
|
$f = @fopen($filename, 'rb');
|
||||||
|
} else {
|
||||||
|
$f = $filename;
|
||||||
|
}
|
||||||
|
|
||||||
|
if($f) {
|
||||||
|
return @exif_read_data($f, null, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Orients current image based on exif orientation information.
|
||||||
|
*
|
||||||
|
* @param array $exif
|
||||||
|
* @return boolean true if oriented, otherwise false
|
||||||
|
*/
|
||||||
|
public function orient($exif) {
|
||||||
|
if(! ($this->is_valid() && $exif)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$ort = ((array_key_exists('IFD0', $exif)) ? $exif['IFD0']['Orientation'] : $exif['Orientation']);
|
||||||
|
|
||||||
|
if(! $ort) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch($ort) {
|
||||||
|
case 1 : // nothing
|
||||||
|
break;
|
||||||
|
case 2 : // horizontal flip
|
||||||
|
$this->flip();
|
||||||
|
break;
|
||||||
|
case 3 : // 180 rotate left
|
||||||
|
$this->rotate(180);
|
||||||
|
break;
|
||||||
|
case 4 : // vertical flip
|
||||||
|
$this->flip(false, true);
|
||||||
|
break;
|
||||||
|
case 5 : // vertical flip + 90 rotate right
|
||||||
|
$this->flip(false, true);
|
||||||
|
$this->rotate(-90);
|
||||||
|
break;
|
||||||
|
case 6 : // 90 rotate right
|
||||||
|
$this->rotate(-90);
|
||||||
|
break;
|
||||||
|
case 7 : // horizontal flip + 90 rotate right
|
||||||
|
$this->flip();
|
||||||
|
$this->rotate(-90);
|
||||||
|
break;
|
||||||
|
case 8 : // 90 rotate left
|
||||||
|
$this->rotate(90);
|
||||||
|
break;
|
||||||
|
default :
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Save photo to database.
|
||||||
|
*
|
||||||
|
* @param array $arr
|
||||||
|
* @param boolean $skipcheck (optional) default false
|
||||||
|
* @return boolean|array
|
||||||
|
*/
|
||||||
|
public function save($arr, $skipcheck = false) {
|
||||||
|
if(! ($skipcheck || $this->is_valid())) {
|
||||||
|
logger('Attempt to store invalid photo.');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$p = [];
|
||||||
|
|
||||||
|
$p['aid'] = ((intval($arr['aid'])) ? intval($arr['aid']) : 0);
|
||||||
|
$p['uid'] = ((intval($arr['uid'])) ? intval($arr['uid']) : 0);
|
||||||
|
$p['xchan'] = (($arr['xchan']) ? $arr['xchan'] : '');
|
||||||
|
$p['resource_id'] = (($arr['resource_id']) ? $arr['resource_id'] : '');
|
||||||
|
$p['filename'] = (($arr['filename']) ? $arr['filename'] : '');
|
||||||
|
$p['mimetype'] = (($arr['mimetype']) ? $arr['mimetype'] : $this->getType());
|
||||||
|
$p['album'] = (($arr['album']) ? $arr['album'] : '');
|
||||||
|
$p['imgscale'] = ((intval($arr['imgscale'])) ? intval($arr['imgscale']) : 0);
|
||||||
|
$p['allow_cid'] = (($arr['allow_cid']) ? $arr['allow_cid'] : '');
|
||||||
|
$p['allow_gid'] = (($arr['allow_gid']) ? $arr['allow_gid'] : '');
|
||||||
|
$p['deny_cid'] = (($arr['deny_cid']) ? $arr['deny_cid'] : '');
|
||||||
|
$p['deny_gid'] = (($arr['deny_gid']) ? $arr['deny_gid'] : '');
|
||||||
|
$p['edited'] = (($arr['edited']) ? $arr['edited'] : datetime_convert());
|
||||||
|
$p['title'] = (($arr['title']) ? $arr['title'] : '');
|
||||||
|
$p['description'] = (($arr['description']) ? $arr['description'] : '');
|
||||||
|
$p['photo_usage'] = intval($arr['photo_usage']);
|
||||||
|
$p['os_storage'] = intval($arr['os_storage']);
|
||||||
|
$p['os_path'] = $arr['os_path'];
|
||||||
|
$p['os_syspath'] = ((array_key_exists('os_syspath', $arr)) ? $arr['os_syspath'] : '');
|
||||||
|
$p['display_path'] = (($arr['display_path']) ? $arr['display_path'] : '');
|
||||||
|
$p['width'] = (($arr['width']) ? $arr['width'] : $this->getWidth());
|
||||||
|
$p['height'] = (($arr['height']) ? $arr['height'] : $this->getHeight());
|
||||||
|
$p['expires'] = (($arr['expires']) ? $arr['expires'] : gmdate('Y-m-d H:i:s', time() + get_config('system', 'photo_cache_time', 86400)));
|
||||||
|
|
||||||
|
if(! intval($p['imgscale']))
|
||||||
|
logger('save: ' . print_r($arr, true), LOGGER_DATA);
|
||||||
|
|
||||||
|
$x = q("select id, created from photo where resource_id = '%s' and uid = %d and xchan = '%s' and imgscale = %d limit 1", dbesc($p['resource_id']), intval($p['uid']), dbesc($p['xchan']), intval($p['imgscale']));
|
||||||
|
|
||||||
|
if($x) {
|
||||||
|
$p['created'] = (($x['created']) ? $x['created'] : $p['edited']);
|
||||||
|
$r = q("UPDATE photo set
|
||||||
|
aid = %d,
|
||||||
|
uid = %d,
|
||||||
|
xchan = '%s',
|
||||||
|
resource_id = '%s',
|
||||||
|
created = '%s',
|
||||||
|
edited = '%s',
|
||||||
|
filename = '%s',
|
||||||
|
mimetype = '%s',
|
||||||
|
album = '%s',
|
||||||
|
height = %d,
|
||||||
|
width = %d,
|
||||||
|
content = '%s',
|
||||||
|
os_storage = %d,
|
||||||
|
filesize = %d,
|
||||||
|
imgscale = %d,
|
||||||
|
photo_usage = %d,
|
||||||
|
title = '%s',
|
||||||
|
description = '%s',
|
||||||
|
os_path = '%s',
|
||||||
|
display_path = '%s',
|
||||||
|
allow_cid = '%s',
|
||||||
|
allow_gid = '%s',
|
||||||
|
deny_cid = '%s',
|
||||||
|
deny_gid = '%s',
|
||||||
|
expires = '%s'
|
||||||
|
where id = %d",
|
||||||
|
intval($p['aid']), intval($p['uid']), dbesc($p['xchan']), dbesc($p['resource_id']), dbescdate($p['created']), dbescdate($p['edited']), dbesc(basename($p['filename'])), dbesc($p['mimetype']), dbesc($p['album']), intval($p['height']), intval($p['width']), (intval($p['os_storage']) ? dbescbin($p['os_syspath']) : dbescbin($this->imageString())), intval($p['os_storage']), (intval($p['os_storage']) ? @filesize($p['os_syspath']) : strlen($this->imageString())), intval($p['imgscale']), intval($p['photo_usage']), dbesc($p['title']), dbesc($p['description']), dbesc($p['os_path']), dbesc($p['display_path']), dbesc($p['allow_cid']), dbesc($p['allow_gid']), dbesc($p['deny_cid']), dbesc($p['deny_gid']), dbescdate($p['expires']), intval($x[0]['id']));
|
||||||
|
} else {
|
||||||
|
$p['created'] = (($arr['created']) ? $arr['created'] : $p['edited']);
|
||||||
|
$r = q("INSERT INTO photo
|
||||||
|
( aid, uid, xchan, resource_id, created, edited, filename, mimetype, album, height, width, content, os_storage, filesize, imgscale, photo_usage, title, description, os_path, display_path, allow_cid, allow_gid, deny_cid, deny_gid, expires )
|
||||||
|
VALUES ( %d, %d, '%s', '%s', '%s', '%s', '%s', '%s', '%s', %d, %d, '%s', %d, %d, %d, %d, '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s' )", intval($p['aid']), intval($p['uid']), dbesc($p['xchan']), dbesc($p['resource_id']), dbescdate($p['created']), dbescdate($p['edited']), dbesc(basename($p['filename'])), dbesc($p['mimetype']), dbesc($p['album']), intval($p['height']), intval($p['width']), (intval($p['os_storage']) ? dbescbin($p['os_syspath']) : dbescbin($this->imageString())), intval($p['os_storage']), (intval($p['os_storage']) ? @filesize($p['os_syspath']) : strlen($this->imageString())), intval($p['imgscale']), intval($p['photo_usage']), dbesc($p['title']), dbesc($p['description']), dbesc($p['os_path']), dbesc($p['display_path']), dbesc($p['allow_cid']), dbesc($p['allow_gid']), dbesc($p['deny_cid']), dbesc($p['deny_gid']), dbescdate($p['expires']));
|
||||||
|
}
|
||||||
|
logger('Photo save imgscale ' . $p['imgscale'] . ' returned ' . intval($r));
|
||||||
|
|
||||||
|
return $r;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
194
Zotlabs/Photo/PhotoGd.php
Normal file
194
Zotlabs/Photo/PhotoGd.php
Normal file
@ -0,0 +1,194 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Zotlabs\Photo;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief GD photo driver.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
class PhotoGd extends PhotoDriver {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
* @see \Zotlabs\Photo\PhotoDriver::supportedTypes()
|
||||||
|
*/
|
||||||
|
public function supportedTypes() {
|
||||||
|
$t = [];
|
||||||
|
$t['image/jpeg'] = 'jpg';
|
||||||
|
if(imagetypes() & IMG_PNG)
|
||||||
|
$t['image/png'] = 'png';
|
||||||
|
if(imagetypes() & IMG_GIF)
|
||||||
|
$t['image/gif'] = 'gif';
|
||||||
|
|
||||||
|
return $t;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function load($data, $type) {
|
||||||
|
$this->valid = false;
|
||||||
|
if(! $data)
|
||||||
|
return;
|
||||||
|
|
||||||
|
$this->image = @imagecreatefromstring($data);
|
||||||
|
if($this->image !== false) {
|
||||||
|
$this->valid = true;
|
||||||
|
$this->setDimensions();
|
||||||
|
imagealphablending($this->image, false);
|
||||||
|
imagesavealpha($this->image, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function setDimensions() {
|
||||||
|
$this->width = imagesx($this->image);
|
||||||
|
$this->height = imagesy($this->image);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief GD driver does not preserve EXIF, so not need to clear it.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function clearexif() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function destroy() {
|
||||||
|
if($this->is_valid()) {
|
||||||
|
imagedestroy($this->image);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Return a PHP image resource of the current image.
|
||||||
|
*
|
||||||
|
* @see \Zotlabs\Photo\PhotoDriver::getImage()
|
||||||
|
*
|
||||||
|
* @return boolean|resource
|
||||||
|
*/
|
||||||
|
public function getImage() {
|
||||||
|
if(! $this->is_valid())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return $this->image;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function doScaleImage($dest_width, $dest_height) {
|
||||||
|
|
||||||
|
$dest = imagecreatetruecolor($dest_width, $dest_height);
|
||||||
|
$width = imagesx($this->image);
|
||||||
|
$height = imagesy($this->image);
|
||||||
|
|
||||||
|
imagealphablending($dest, false);
|
||||||
|
imagesavealpha($dest, true);
|
||||||
|
if($this->type == 'image/png')
|
||||||
|
imagefill($dest, 0, 0, imagecolorallocatealpha($dest, 0, 0, 0, 127)); // fill with alpha
|
||||||
|
|
||||||
|
imagecopyresampled($dest, $this->image, 0, 0, 0, 0, $dest_width, $dest_height, $width, $height);
|
||||||
|
if($this->image)
|
||||||
|
imagedestroy($this->image);
|
||||||
|
|
||||||
|
$this->image = $dest;
|
||||||
|
$this->setDimensions();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function rotate($degrees) {
|
||||||
|
if(! $this->is_valid())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
$this->image = imagerotate($this->image, $degrees, 0);
|
||||||
|
$this->setDimensions();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function flip($horiz = true, $vert = false) {
|
||||||
|
if(! $this->is_valid())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
$w = imagesx($this->image);
|
||||||
|
$h = imagesy($this->image);
|
||||||
|
$flipped = imagecreate($w, $h);
|
||||||
|
if($horiz) {
|
||||||
|
for($x = 0; $x < $w; $x++) {
|
||||||
|
imagecopy($flipped, $this->image, $x, 0, $w - $x - 1, 0, 1, $h);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if($vert) {
|
||||||
|
for($y = 0; $y < $h; $y++) {
|
||||||
|
imagecopy($flipped, $this->image, 0, $y, 0, $h - $y - 1, $w, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$this->image = $flipped;
|
||||||
|
$this->setDimensions(); // Shouldn't really be necessary
|
||||||
|
}
|
||||||
|
|
||||||
|
public function cropImage($max, $x, $y, $w, $h) {
|
||||||
|
if(!$this->is_valid())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
$dest = imagecreatetruecolor($max, $max);
|
||||||
|
imagealphablending($dest, false);
|
||||||
|
imagesavealpha($dest, true);
|
||||||
|
if($this->type == 'image/png')
|
||||||
|
imagefill($dest, 0, 0, imagecolorallocatealpha($dest, 0, 0, 0, 127)); // fill with alpha
|
||||||
|
|
||||||
|
imagecopyresampled($dest, $this->image, 0, 0, $x, $y, $max, $max, $w, $h);
|
||||||
|
if($this->image)
|
||||||
|
imagedestroy($this->image);
|
||||||
|
|
||||||
|
$this->image = $dest;
|
||||||
|
$this->setDimensions();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function cropImageRect($maxx, $maxy, $x, $y, $w, $h) {
|
||||||
|
if(! $this->is_valid())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
$dest = imagecreatetruecolor($maxx, $maxy);
|
||||||
|
imagealphablending($dest, false);
|
||||||
|
imagesavealpha($dest, true);
|
||||||
|
if($this->type == 'image/png')
|
||||||
|
imagefill($dest, 0, 0, imagecolorallocatealpha($dest, 0, 0, 0, 127)); // fill with alpha
|
||||||
|
|
||||||
|
imagecopyresampled($dest, $this->image, 0, 0, $x, $y, $maxx, $maxy, $w, $h);
|
||||||
|
if($this->image)
|
||||||
|
imagedestroy($this->image);
|
||||||
|
|
||||||
|
$this->image = $dest;
|
||||||
|
$this->setDimensions();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
* @see \Zotlabs\Photo\PhotoDriver::imageString()
|
||||||
|
*/
|
||||||
|
public function imageString() {
|
||||||
|
if(! $this->is_valid())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
$quality = false;
|
||||||
|
|
||||||
|
ob_start();
|
||||||
|
|
||||||
|
switch($this->getType()){
|
||||||
|
case 'image/png':
|
||||||
|
$quality = get_config('system', 'png_quality');
|
||||||
|
if((! $quality) || ($quality > 9))
|
||||||
|
$quality = PNG_QUALITY;
|
||||||
|
|
||||||
|
\imagepng($this->image, NULL, $quality);
|
||||||
|
break;
|
||||||
|
case 'image/jpeg':
|
||||||
|
// gd can lack imagejpeg(), but we verify during installation it is available
|
||||||
|
default:
|
||||||
|
$quality = get_config('system', 'jpeg_quality');
|
||||||
|
if((! $quality) || ($quality > 100))
|
||||||
|
$quality = JPEG_QUALITY;
|
||||||
|
|
||||||
|
\imagejpeg($this->image, NULL, $quality);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
$string = ob_get_contents();
|
||||||
|
ob_end_clean();
|
||||||
|
|
||||||
|
return $string;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1,45 +1,44 @@
|
|||||||
<?php /** @file */
|
<?php
|
||||||
|
|
||||||
|
namespace Zotlabs\Photo;
|
||||||
|
|
||||||
require_once('include/photo/photo_driver.php');
|
/**
|
||||||
|
* @brief ImageMagick photo driver.
|
||||||
|
*/
|
||||||
|
class PhotoImagick extends PhotoDriver {
|
||||||
|
|
||||||
|
public function supportedTypes() {
|
||||||
class photo_imagick extends photo_driver {
|
return [
|
||||||
|
|
||||||
|
|
||||||
function supportedTypes() {
|
|
||||||
return array(
|
|
||||||
'image/jpeg' => 'jpg',
|
'image/jpeg' => 'jpg',
|
||||||
'image/png' => 'png',
|
'image/png' => 'png',
|
||||||
'image/gif' => 'gif'
|
'image/gif' => 'gif',
|
||||||
);
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
public function get_FormatsMap() {
|
private function get_FormatsMap() {
|
||||||
return array(
|
return [
|
||||||
'image/jpeg' => 'JPG',
|
'image/jpeg' => 'JPG',
|
||||||
'image/png' => 'PNG',
|
'image/png' => 'PNG',
|
||||||
'image/gif' => 'GIF'
|
'image/gif' => 'GIF',
|
||||||
);
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function load($data, $type) {
|
protected function load($data, $type) {
|
||||||
$this->valid = false;
|
$this->valid = false;
|
||||||
$this->image = new Imagick();
|
$this->image = new \Imagick();
|
||||||
|
|
||||||
if(! $data)
|
if(! $data)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$this->image->readImageBlob($data);
|
$this->image->readImageBlob($data);
|
||||||
}
|
} catch(\Exception $e) {
|
||||||
catch (Exception $e) {
|
logger('Imagick readImageBlob() exception:' . print_r($e, true));
|
||||||
logger('imagick readImageBlob() exception:' . print_r($e,true));
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/*
|
||||||
* Setup the image to the format it will be saved to
|
* Setup the image to the format it will be saved to
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@ -52,19 +51,18 @@ class photo_imagick extends photo_driver {
|
|||||||
// Always coalesce, if it is not a multi-frame image it won't hurt anyway
|
// Always coalesce, if it is not a multi-frame image it won't hurt anyway
|
||||||
$this->image = $this->image->coalesceImages();
|
$this->image = $this->image->coalesceImages();
|
||||||
|
|
||||||
|
|
||||||
$this->valid = true;
|
$this->valid = true;
|
||||||
$this->setDimensions();
|
$this->setDimensions();
|
||||||
|
|
||||||
/**
|
/*
|
||||||
* setup the compression here, so we'll do it only once
|
* setup the compression here, so we'll do it only once
|
||||||
*/
|
*/
|
||||||
switch($this->getType()) {
|
switch($this->getType()) {
|
||||||
case "image/png":
|
case 'image/png':
|
||||||
$quality = get_config('system','png_quality');
|
$quality = get_config('system', 'png_quality');
|
||||||
if((! $quality) || ($quality > 9))
|
if((! $quality) || ($quality > 9))
|
||||||
$quality = PNG_QUALITY;
|
$quality = PNG_QUALITY;
|
||||||
/**
|
/*
|
||||||
* From http://www.imagemagick.org/script/command-line-options.php#quality:
|
* From http://www.imagemagick.org/script/command-line-options.php#quality:
|
||||||
*
|
*
|
||||||
* 'For the MNG and PNG image formats, the quality value sets
|
* 'For the MNG and PNG image formats, the quality value sets
|
||||||
@ -75,56 +73,64 @@ class photo_imagick extends photo_driver {
|
|||||||
$quality = $quality * 10;
|
$quality = $quality * 10;
|
||||||
$this->image->setCompressionQuality($quality);
|
$this->image->setCompressionQuality($quality);
|
||||||
break;
|
break;
|
||||||
case "image/jpeg":
|
case 'image/jpeg':
|
||||||
$quality = get_config('system','jpeg_quality');
|
$quality = get_config('system', 'jpeg_quality');
|
||||||
if((! $quality) || ($quality > 100))
|
if((! $quality) || ($quality > 100))
|
||||||
$quality = JPEG_QUALITY;
|
$quality = JPEG_QUALITY;
|
||||||
$this->image->setCompressionQuality($quality);
|
$this->image->setCompressionQuality($quality);
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function destroy() {
|
protected function destroy() {
|
||||||
if($this->is_valid()) {
|
if($this->is_valid()) {
|
||||||
$this->image->clear();
|
$this->image->clear();
|
||||||
$this->image->destroy();
|
$this->image->destroy();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected function setDimensions() {
|
||||||
public function setDimensions() {
|
|
||||||
$this->width = $this->image->getImageWidth();
|
$this->width = $this->image->getImageWidth();
|
||||||
$this->height = $this->image->getImageHeight();
|
$this->height = $this->image->getImageHeight();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Strips the image of all profiles and comments.
|
||||||
|
*
|
||||||
|
* Keep ICC profile for better colors.
|
||||||
|
*
|
||||||
|
* @see \Zotlabs\Photo\PhotoDriver::clearexif()
|
||||||
|
*/
|
||||||
public function clearexif() {
|
public function clearexif() {
|
||||||
|
$profiles = $this->image->getImageProfiles('icc', true);
|
||||||
$profiles = $this->image->getImageProfiles("icc", true);
|
|
||||||
|
|
||||||
$this->image->stripImage();
|
$this->image->stripImage();
|
||||||
|
|
||||||
if(!empty($profiles)) {
|
if(! empty($profiles)) {
|
||||||
$this->image->profileImage("icc", $profiles['icc']);
|
$this->image->profileImage('icc', $profiles['icc']);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Return a \Imagick object of the current image.
|
||||||
|
*
|
||||||
|
* @see \Zotlabs\Photo\PhotoDriver::getImage()
|
||||||
|
*
|
||||||
|
* @return boolean|\Imagick
|
||||||
|
*/
|
||||||
public function getImage() {
|
public function getImage() {
|
||||||
if(!$this->is_valid())
|
if(! $this->is_valid())
|
||||||
return FALSE;
|
return false;
|
||||||
|
|
||||||
$this->image = $this->image->deconstructImages();
|
$this->image = $this->image->deconstructImages();
|
||||||
return $this->image;
|
return $this->image;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function doScaleImage($dest_width,$dest_height) {
|
public function doScaleImage($dest_width, $dest_height) {
|
||||||
|
/*
|
||||||
/**
|
|
||||||
* If it is not animated, there will be only one iteration here,
|
* If it is not animated, there will be only one iteration here,
|
||||||
* so don't bother checking
|
* so don't bother checking
|
||||||
*/
|
*/
|
||||||
@ -132,82 +138,81 @@ class photo_imagick extends photo_driver {
|
|||||||
$this->image->setFirstIterator();
|
$this->image->setFirstIterator();
|
||||||
do {
|
do {
|
||||||
$this->image->scaleImage($dest_width, $dest_height);
|
$this->image->scaleImage($dest_width, $dest_height);
|
||||||
} while ($this->image->nextImage());
|
} while($this->image->nextImage());
|
||||||
|
|
||||||
$this->setDimensions();
|
$this->setDimensions();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function rotate($degrees) {
|
public function rotate($degrees) {
|
||||||
if(!$this->is_valid())
|
if(! $this->is_valid())
|
||||||
return FALSE;
|
return false;
|
||||||
|
|
||||||
$this->image->setFirstIterator();
|
$this->image->setFirstIterator();
|
||||||
do {
|
do {
|
||||||
// ImageMagick rotates in the opposite direction of imagerotate()
|
// ImageMagick rotates in the opposite direction of imagerotate()
|
||||||
$this->image->rotateImage(new ImagickPixel(), -$degrees);
|
$this->image->rotateImage(new \ImagickPixel(), -$degrees);
|
||||||
} while ($this->image->nextImage());
|
} while($this->image->nextImage());
|
||||||
|
|
||||||
$this->setDimensions();
|
$this->setDimensions();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function flip($horiz = true, $vert = false) {
|
public function flip($horiz = true, $vert = false) {
|
||||||
if(!$this->is_valid())
|
if(! $this->is_valid())
|
||||||
return FALSE;
|
return false;
|
||||||
|
|
||||||
$this->image->setFirstIterator();
|
$this->image->setFirstIterator();
|
||||||
do {
|
do {
|
||||||
if($horiz) $this->image->flipImage();
|
if($horiz) $this->image->flipImage();
|
||||||
if($vert) $this->image->flopImage();
|
if($vert) $this->image->flopImage();
|
||||||
} while ($this->image->nextImage());
|
} while($this->image->nextImage());
|
||||||
|
|
||||||
$this->setDimensions(); // Shouldn't really be necessary
|
$this->setDimensions(); // Shouldn't really be necessary
|
||||||
}
|
}
|
||||||
|
|
||||||
public function cropImage($max,$x,$y,$w,$h) {
|
public function cropImage($max,$x,$y,$w,$h) {
|
||||||
if(!$this->is_valid())
|
if(!$this->is_valid())
|
||||||
return FALSE;
|
return false;
|
||||||
|
|
||||||
$this->image->setFirstIterator();
|
$this->image->setFirstIterator();
|
||||||
do {
|
do {
|
||||||
$this->image->cropImage($w, $h, $x, $y);
|
$this->image->cropImage($w, $h, $x, $y);
|
||||||
/**
|
/*
|
||||||
* We need to remove the canvas,
|
* We need to remove the canvas,
|
||||||
* or the image is not resized to the crop:
|
* or the image is not resized to the crop:
|
||||||
* http://php.net/manual/en/imagick.cropimage.php#97232
|
* http://php.net/manual/en/imagick.cropimage.php#97232
|
||||||
*/
|
*/
|
||||||
$this->image->setImagePage(0, 0, 0, 0);
|
$this->image->setImagePage(0, 0, 0, 0);
|
||||||
} while ($this->image->nextImage());
|
} while($this->image->nextImage());
|
||||||
|
|
||||||
$this->doScaleImage($max,$max);
|
$this->doScaleImage($max, $max);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function cropImageRect($maxx,$maxy,$x,$y,$w,$h) {
|
public function cropImageRect($maxx, $maxy, $x, $y, $w, $h) {
|
||||||
if(!$this->is_valid())
|
if(! $this->is_valid())
|
||||||
return FALSE;
|
return false;
|
||||||
|
|
||||||
$this->image->setFirstIterator();
|
$this->image->setFirstIterator();
|
||||||
do {
|
do {
|
||||||
$this->image->cropImage($w, $h, $x, $y);
|
$this->image->cropImage($w, $h, $x, $y);
|
||||||
/**
|
/*
|
||||||
* We need to remove the canvas,
|
* We need to remove the canvas,
|
||||||
* or the image is not resized to the crop:
|
* or the image is not resized to the crop:
|
||||||
* http://php.net/manual/en/imagick.cropimage.php#97232
|
* http://php.net/manual/en/imagick.cropimage.php#97232
|
||||||
*/
|
*/
|
||||||
$this->image->setImagePage(0, 0, 0, 0);
|
$this->image->setImagePage(0, 0, 0, 0);
|
||||||
} while ($this->image->nextImage());
|
} while($this->image->nextImage());
|
||||||
|
|
||||||
$this->doScaleImage($maxx,$maxy);
|
$this->doScaleImage($maxx, $maxy);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function imageString() {
|
public function imageString() {
|
||||||
if(!$this->is_valid())
|
if(! $this->is_valid())
|
||||||
return FALSE;
|
return false;
|
||||||
|
|
||||||
/* Clean it */
|
/* Clean it */
|
||||||
$this->image = $this->image->deconstructImages();
|
$this->image = $this->image->deconstructImages();
|
||||||
|
|
||||||
return $this->image->getImagesBlob();
|
return $this->image->getImagesBlob();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
@ -1,15 +1,19 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
use Zotlabs\Photo\PhotoDriver;
|
||||||
|
use Zotlabs\Photo\PhotoGd;
|
||||||
|
use Zotlabs\Photo\PhotoImagick;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Return a photo_driver object.
|
* @brief Return a PhotoDriver object.
|
||||||
*
|
*
|
||||||
* Use this factory when manipulating images.
|
* Use this factory when manipulating images.
|
||||||
*
|
*
|
||||||
* Return a photo driver object implementing ImageMagick or GD.
|
* Return a photo driver object implementing ImageMagick or GD.
|
||||||
*
|
*
|
||||||
* @param string $data Image data
|
* @param string $data Image data
|
||||||
* @param string $type
|
* @param string $type Mimetype
|
||||||
* @return null|photo_driver
|
* @return null|PhotoDriver
|
||||||
* NULL if unsupported image type or failure, otherwise photo driver object
|
* NULL if unsupported image type or failure, otherwise photo driver object
|
||||||
*/
|
*/
|
||||||
function photo_factory($data, $type = null) {
|
function photo_factory($data, $type = null) {
|
||||||
@ -34,8 +38,7 @@ function photo_factory($data, $type = null) {
|
|||||||
$v = Imagick::getVersion();
|
$v = Imagick::getVersion();
|
||||||
preg_match('/ImageMagick ([0-9]+\.[0-9]+\.[0-9]+)/', $v['versionString'], $m);
|
preg_match('/ImageMagick ([0-9]+\.[0-9]+\.[0-9]+)/', $v['versionString'], $m);
|
||||||
if(version_compare($m[1], '6.6.7') >= 0) {
|
if(version_compare($m[1], '6.6.7') >= 0) {
|
||||||
require_once('include/photo/photo_imagick.php');
|
$ph = new PhotoImagick($data, $type);
|
||||||
$ph = new photo_imagick($data, $type);
|
|
||||||
} else {
|
} else {
|
||||||
// earlier imagick versions have issues with scaling png's
|
// earlier imagick versions have issues with scaling png's
|
||||||
// don't log this because it will just fill the logfile.
|
// don't log this because it will just fill the logfile.
|
||||||
@ -45,462 +48,13 @@ function photo_factory($data, $type = null) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if(! $ph) {
|
if(! $ph) {
|
||||||
require_once('include/photo/photo_gd.php');
|
$ph = new PhotoGd($data, $type);
|
||||||
$ph = new photo_gd($data, $type);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return $ph;
|
return $ph;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Abstract photo driver class.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
abstract class photo_driver {
|
|
||||||
|
|
||||||
protected $image;
|
|
||||||
protected $width;
|
|
||||||
protected $height;
|
|
||||||
protected $valid;
|
|
||||||
protected $type;
|
|
||||||
protected $types;
|
|
||||||
|
|
||||||
abstract function supportedTypes();
|
|
||||||
|
|
||||||
abstract function load($data,$type);
|
|
||||||
|
|
||||||
abstract function destroy();
|
|
||||||
|
|
||||||
abstract function setDimensions();
|
|
||||||
|
|
||||||
abstract function getImage();
|
|
||||||
|
|
||||||
abstract function doScaleImage($new_width,$new_height);
|
|
||||||
|
|
||||||
abstract function rotate($degrees);
|
|
||||||
|
|
||||||
abstract function flip($horiz = true, $vert = false);
|
|
||||||
|
|
||||||
abstract function cropImage($max,$x,$y,$w,$h);
|
|
||||||
|
|
||||||
abstract function cropImageRect($maxx,$maxy,$x,$y,$w,$h);
|
|
||||||
|
|
||||||
abstract function imageString();
|
|
||||||
|
|
||||||
abstract function clearexif();
|
|
||||||
|
|
||||||
public function __construct($data, $type='') {
|
|
||||||
$this->types = $this->supportedTypes();
|
|
||||||
if (! array_key_exists($type, $this->types)){
|
|
||||||
$type = 'image/jpeg';
|
|
||||||
}
|
|
||||||
$this->type = $type;
|
|
||||||
$this->valid = false;
|
|
||||||
$this->load($data, $type);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function __destruct() {
|
|
||||||
if($this->is_valid())
|
|
||||||
$this->destroy();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function is_valid() {
|
|
||||||
return $this->valid;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getWidth() {
|
|
||||||
if(!$this->is_valid())
|
|
||||||
return FALSE;
|
|
||||||
|
|
||||||
return $this->width;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getHeight() {
|
|
||||||
if(!$this->is_valid())
|
|
||||||
return FALSE;
|
|
||||||
|
|
||||||
return $this->height;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public function saveImage($path) {
|
|
||||||
if(!$this->is_valid())
|
|
||||||
return FALSE;
|
|
||||||
|
|
||||||
return (file_put_contents($path, $this->imageString()) ? true : false);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public function getType() {
|
|
||||||
if(!$this->is_valid())
|
|
||||||
return FALSE;
|
|
||||||
|
|
||||||
return $this->type;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getExt() {
|
|
||||||
if(!$this->is_valid())
|
|
||||||
return FALSE;
|
|
||||||
|
|
||||||
return $this->types[$this->getType()];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Scale image to max pixel size in either dimension.
|
|
||||||
*
|
|
||||||
* @param int $max maximum pixel size in either dimension
|
|
||||||
* @param boolean $float_height (optional)
|
|
||||||
* if true allow height to float to any length on tall images, constraining
|
|
||||||
* only the width
|
|
||||||
* @return boolean|void false on failure, otherwise void
|
|
||||||
*/
|
|
||||||
public function scaleImage($max, $float_height = true) {
|
|
||||||
if(!$this->is_valid())
|
|
||||||
return FALSE;
|
|
||||||
|
|
||||||
$width = $this->width;
|
|
||||||
$height = $this->height;
|
|
||||||
|
|
||||||
$dest_width = $dest_height = 0;
|
|
||||||
|
|
||||||
if((! $width)|| (! $height))
|
|
||||||
return FALSE;
|
|
||||||
|
|
||||||
if($width > $max && $height > $max) {
|
|
||||||
|
|
||||||
// very tall image (greater than 16:9)
|
|
||||||
// constrain the width - let the height float.
|
|
||||||
|
|
||||||
if(((($height * 9) / 16) > $width) && ($float_height)) {
|
|
||||||
$dest_width = $max;
|
|
||||||
$dest_height = intval(( $height * $max ) / $width);
|
|
||||||
}
|
|
||||||
|
|
||||||
// else constrain both dimensions
|
|
||||||
|
|
||||||
elseif($width > $height) {
|
|
||||||
$dest_width = $max;
|
|
||||||
$dest_height = intval(( $height * $max ) / $width);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
$dest_width = intval(( $width * $max ) / $height);
|
|
||||||
$dest_height = $max;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
if( $width > $max ) {
|
|
||||||
$dest_width = $max;
|
|
||||||
$dest_height = intval(( $height * $max ) / $width);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
if( $height > $max ) {
|
|
||||||
|
|
||||||
// very tall image (greater than 16:9)
|
|
||||||
// but width is OK - don't do anything
|
|
||||||
|
|
||||||
if(((($height * 9) / 16) > $width) && ($float_height)) {
|
|
||||||
$dest_width = $width;
|
|
||||||
$dest_height = $height;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
$dest_width = intval(( $width * $max ) / $height);
|
|
||||||
$dest_height = $max;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
$dest_width = $width;
|
|
||||||
$dest_height = $height;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$this->doScaleImage($dest_width,$dest_height);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function scaleImageUp($min) {
|
|
||||||
if(!$this->is_valid())
|
|
||||||
return FALSE;
|
|
||||||
|
|
||||||
$width = $this->width;
|
|
||||||
$height = $this->height;
|
|
||||||
|
|
||||||
$dest_width = $dest_height = 0;
|
|
||||||
|
|
||||||
if((! $width)|| (! $height))
|
|
||||||
return FALSE;
|
|
||||||
|
|
||||||
if($width < $min && $height < $min) {
|
|
||||||
if($width > $height) {
|
|
||||||
$dest_width = $min;
|
|
||||||
$dest_height = intval(( $height * $min ) / $width);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
$dest_width = intval(( $width * $min ) / $height);
|
|
||||||
$dest_height = $min;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
if( $width < $min ) {
|
|
||||||
$dest_width = $min;
|
|
||||||
$dest_height = intval(( $height * $min ) / $width);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
if( $height < $min ) {
|
|
||||||
$dest_width = intval(( $width * $min ) / $height);
|
|
||||||
$dest_height = $min;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
$dest_width = $width;
|
|
||||||
$dest_height = $height;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$this->doScaleImage($dest_width,$dest_height);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Scales image to a square.
|
|
||||||
*
|
|
||||||
* @param int $dim Pixel of squre image
|
|
||||||
* @return boolean|void false on failure, otherwise void
|
|
||||||
*/
|
|
||||||
public function scaleImageSquare($dim) {
|
|
||||||
if(!$this->is_valid())
|
|
||||||
return FALSE;
|
|
||||||
|
|
||||||
$this->doScaleImage($dim, $dim);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief reads exif data from filename.
|
|
||||||
*
|
|
||||||
* @param string $filename
|
|
||||||
* @return boolean|array
|
|
||||||
*/
|
|
||||||
public function exif($filename) {
|
|
||||||
|
|
||||||
if((! function_exists('exif_read_data'))
|
|
||||||
|| (! in_array($this->getType(), [ 'image/jpeg' , 'image/tiff'] ))) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* PHP 7.2 allows you to use a stream resource, which should reduce/avoid
|
|
||||||
* memory exhaustion on large images.
|
|
||||||
*/
|
|
||||||
|
|
||||||
if(version_compare(PHP_VERSION, '7.2.0') >= 0) {
|
|
||||||
$f = @fopen($filename, 'rb');
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
$f = $filename;
|
|
||||||
}
|
|
||||||
|
|
||||||
if($f) {
|
|
||||||
return @exif_read_data($f,null,true);
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Orients current image based on exif orientation information.
|
|
||||||
*
|
|
||||||
* @param array $exif
|
|
||||||
* @return boolean true if oriented, otherwise false
|
|
||||||
*/
|
|
||||||
public function orient($exif) {
|
|
||||||
|
|
||||||
if(! ($this->is_valid() && $exif)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
$ort = ((array_key_exists('IFD0', $exif)) ? $exif['IFD0']['Orientation'] : $exif['Orientation']);
|
|
||||||
|
|
||||||
if(! $ort) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch($ort) {
|
|
||||||
case 1: // nothing
|
|
||||||
break;
|
|
||||||
case 2: // horizontal flip
|
|
||||||
$this->flip();
|
|
||||||
break;
|
|
||||||
case 3: // 180 rotate left
|
|
||||||
$this->rotate(180);
|
|
||||||
break;
|
|
||||||
case 4: // vertical flip
|
|
||||||
$this->flip(false, true);
|
|
||||||
break;
|
|
||||||
case 5: // vertical flip + 90 rotate right
|
|
||||||
$this->flip(false, true);
|
|
||||||
$this->rotate(-90);
|
|
||||||
break;
|
|
||||||
case 6: // 90 rotate right
|
|
||||||
$this->rotate(-90);
|
|
||||||
break;
|
|
||||||
case 7: // horizontal flip + 90 rotate right
|
|
||||||
$this->flip();
|
|
||||||
$this->rotate(-90);
|
|
||||||
break;
|
|
||||||
case 8: // 90 rotate left
|
|
||||||
$this->rotate(90);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Save photo to database.
|
|
||||||
*
|
|
||||||
* @param array $arr
|
|
||||||
* @param boolean $skipcheck (optional) default false
|
|
||||||
* @return boolean|array
|
|
||||||
*/
|
|
||||||
public function save($arr, $skipcheck = false) {
|
|
||||||
|
|
||||||
if(! ($skipcheck || $this->is_valid())) {
|
|
||||||
logger('Attempt to store invalid photo.');
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
$p = [];
|
|
||||||
|
|
||||||
$p['aid'] = ((intval($arr['aid'])) ? intval($arr['aid']) : 0);
|
|
||||||
$p['uid'] = ((intval($arr['uid'])) ? intval($arr['uid']) : 0);
|
|
||||||
$p['xchan'] = (($arr['xchan']) ? $arr['xchan'] : '');
|
|
||||||
$p['resource_id'] = (($arr['resource_id']) ? $arr['resource_id'] : '');
|
|
||||||
$p['filename'] = (($arr['filename']) ? $arr['filename'] : '');
|
|
||||||
$p['mimetype'] = (($arr['mimetype']) ? $arr['mimetype'] : $this->getType());
|
|
||||||
$p['album'] = (($arr['album']) ? $arr['album'] : '');
|
|
||||||
$p['imgscale'] = ((intval($arr['imgscale'])) ? intval($arr['imgscale']) : 0);
|
|
||||||
$p['allow_cid'] = (($arr['allow_cid']) ? $arr['allow_cid'] : '');
|
|
||||||
$p['allow_gid'] = (($arr['allow_gid']) ? $arr['allow_gid'] : '');
|
|
||||||
$p['deny_cid'] = (($arr['deny_cid']) ? $arr['deny_cid'] : '');
|
|
||||||
$p['deny_gid'] = (($arr['deny_gid']) ? $arr['deny_gid'] : '');
|
|
||||||
$p['edited'] = (($arr['edited']) ? $arr['edited'] : datetime_convert());
|
|
||||||
$p['title'] = (($arr['title']) ? $arr['title'] : '');
|
|
||||||
$p['description'] = (($arr['description']) ? $arr['description'] : '');
|
|
||||||
$p['photo_usage'] = intval($arr['photo_usage']);
|
|
||||||
$p['os_storage'] = intval($arr['os_storage']);
|
|
||||||
$p['os_path'] = $arr['os_path'];
|
|
||||||
$p['os_syspath'] = ((array_key_exists('os_syspath',$arr)) ? $arr['os_syspath'] : '');
|
|
||||||
$p['display_path'] = (($arr['display_path']) ? $arr['display_path'] : '');
|
|
||||||
$p['width'] = (($arr['width']) ? $arr['width'] : $this->getWidth());
|
|
||||||
$p['height'] = (($arr['height']) ? $arr['height'] : $this->getHeight());
|
|
||||||
$p['expires'] = (($arr['expires']) ? $arr['expires'] : gmdate('Y-m-d H:i:s', time() + get_config('system','photo_cache_time', 86400)));
|
|
||||||
|
|
||||||
if(! intval($p['imgscale']))
|
|
||||||
logger('save: ' . print_r($arr, true), LOGGER_DATA);
|
|
||||||
|
|
||||||
$x = q("select id, created from photo where resource_id = '%s' and uid = %d and xchan = '%s' and imgscale = %d limit 1",
|
|
||||||
dbesc($p['resource_id']),
|
|
||||||
intval($p['uid']),
|
|
||||||
dbesc($p['xchan']),
|
|
||||||
intval($p['imgscale'])
|
|
||||||
);
|
|
||||||
|
|
||||||
if($x) {
|
|
||||||
$p['created'] = (($x['created']) ? $x['created'] : $p['edited']);
|
|
||||||
$r = q("UPDATE photo set
|
|
||||||
aid = %d,
|
|
||||||
uid = %d,
|
|
||||||
xchan = '%s',
|
|
||||||
resource_id = '%s',
|
|
||||||
created = '%s',
|
|
||||||
edited = '%s',
|
|
||||||
filename = '%s',
|
|
||||||
mimetype = '%s',
|
|
||||||
album = '%s',
|
|
||||||
height = %d,
|
|
||||||
width = %d,
|
|
||||||
content = '%s',
|
|
||||||
os_storage = %d,
|
|
||||||
filesize = %d,
|
|
||||||
imgscale = %d,
|
|
||||||
photo_usage = %d,
|
|
||||||
title = '%s',
|
|
||||||
description = '%s',
|
|
||||||
os_path = '%s',
|
|
||||||
display_path = '%s',
|
|
||||||
allow_cid = '%s',
|
|
||||||
allow_gid = '%s',
|
|
||||||
deny_cid = '%s',
|
|
||||||
deny_gid = '%s',
|
|
||||||
expires = '%s'
|
|
||||||
where id = %d",
|
|
||||||
|
|
||||||
intval($p['aid']),
|
|
||||||
intval($p['uid']),
|
|
||||||
dbesc($p['xchan']),
|
|
||||||
dbesc($p['resource_id']),
|
|
||||||
dbescdate($p['created']),
|
|
||||||
dbescdate($p['edited']),
|
|
||||||
dbesc(basename($p['filename'])),
|
|
||||||
dbesc($p['mimetype']),
|
|
||||||
dbesc($p['album']),
|
|
||||||
intval($p['height']),
|
|
||||||
intval($p['width']),
|
|
||||||
(intval($p['os_storage']) ? dbescbin($p['os_syspath']) : dbescbin($this->imageString())),
|
|
||||||
intval($p['os_storage']),
|
|
||||||
(intval($p['os_storage']) ? @filesize($p['os_syspath']) : strlen($this->imageString())),
|
|
||||||
intval($p['imgscale']),
|
|
||||||
intval($p['photo_usage']),
|
|
||||||
dbesc($p['title']),
|
|
||||||
dbesc($p['description']),
|
|
||||||
dbesc($p['os_path']),
|
|
||||||
dbesc($p['display_path']),
|
|
||||||
dbesc($p['allow_cid']),
|
|
||||||
dbesc($p['allow_gid']),
|
|
||||||
dbesc($p['deny_cid']),
|
|
||||||
dbesc($p['deny_gid']),
|
|
||||||
dbescdate($p['expires']),
|
|
||||||
intval($x[0]['id'])
|
|
||||||
);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
$p['created'] = (($arr['created']) ? $arr['created'] : $p['edited']);
|
|
||||||
$r = q("INSERT INTO photo
|
|
||||||
( aid, uid, xchan, resource_id, created, edited, filename, mimetype, album, height, width, content, os_storage, filesize, imgscale, photo_usage, title, description, os_path, display_path, allow_cid, allow_gid, deny_cid, deny_gid, expires )
|
|
||||||
VALUES ( %d, %d, '%s', '%s', '%s', '%s', '%s', '%s', '%s', %d, %d, '%s', %d, %d, %d, %d, '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s' )",
|
|
||||||
intval($p['aid']),
|
|
||||||
intval($p['uid']),
|
|
||||||
dbesc($p['xchan']),
|
|
||||||
dbesc($p['resource_id']),
|
|
||||||
dbescdate($p['created']),
|
|
||||||
dbescdate($p['edited']),
|
|
||||||
dbesc(basename($p['filename'])),
|
|
||||||
dbesc($p['mimetype']),
|
|
||||||
dbesc($p['album']),
|
|
||||||
intval($p['height']),
|
|
||||||
intval($p['width']),
|
|
||||||
(intval($p['os_storage']) ? dbescbin($p['os_syspath']) : dbescbin($this->imageString())),
|
|
||||||
intval($p['os_storage']),
|
|
||||||
(intval($p['os_storage']) ? @filesize($p['os_syspath']) : strlen($this->imageString())),
|
|
||||||
intval($p['imgscale']),
|
|
||||||
intval($p['photo_usage']),
|
|
||||||
dbesc($p['title']),
|
|
||||||
dbesc($p['description']),
|
|
||||||
dbesc($p['os_path']),
|
|
||||||
dbesc($p['display_path']),
|
|
||||||
dbesc($p['allow_cid']),
|
|
||||||
dbesc($p['allow_gid']),
|
|
||||||
dbesc($p['deny_cid']),
|
|
||||||
dbesc($p['deny_gid']),
|
|
||||||
dbescdate($p['expires'])
|
|
||||||
);
|
|
||||||
}
|
|
||||||
logger('Photo save ' . $p['imgscale'] . ' returned ' . intval($r));
|
|
||||||
|
|
||||||
return $r;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Guess image mimetype from filename or from Content-Type header.
|
* @brief Guess image mimetype from filename or from Content-Type header.
|
||||||
*
|
*
|
||||||
@ -519,7 +73,7 @@ function guess_image_type($filename, $headers = '') {
|
|||||||
$hdrs = [];
|
$hdrs = [];
|
||||||
$h = explode("\n", $headers);
|
$h = explode("\n", $headers);
|
||||||
foreach ($h as $l) {
|
foreach ($h as $l) {
|
||||||
list($k,$v) = array_map('trim', explode(':', trim($l), 2));
|
list($k, $v) = array_map('trim', explode(':', trim($l), 2));
|
||||||
$hdrs[strtolower($k)] = $v;
|
$hdrs[strtolower($k)] = $v;
|
||||||
}
|
}
|
||||||
logger('Curl headers: ' .var_export($hdrs, true), LOGGER_DEBUG);
|
logger('Curl headers: ' .var_export($hdrs, true), LOGGER_DEBUG);
|
||||||
@ -566,7 +120,7 @@ function guess_image_type($filename, $headers = '') {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(is_null($type) && (strpos($filename,'http') === false)) {
|
if(is_null($type) && (strpos($filename, 'http') === false)) {
|
||||||
$size = getimagesize($filename);
|
$size = getimagesize($filename);
|
||||||
$ph = photo_factory('');
|
$ph = photo_factory('');
|
||||||
$types = $ph->supportedTypes();
|
$types = $ph->supportedTypes();
|
||||||
@ -756,7 +310,7 @@ function import_xchan_photo($photo, $xchan, $thing = false, $force = false) {
|
|||||||
logger('HTTP code: ' . $result['return_code'] . '; modified: ' . $modified
|
logger('HTTP code: ' . $result['return_code'] . '; modified: ' . $modified
|
||||||
. '; failure: ' . ($photo_failure ? 'yes' : 'no') . '; URL: ' . $photo, LOGGER_DEBUG);
|
. '; failure: ' . ($photo_failure ? 'yes' : 'no') . '; URL: ' . $photo, LOGGER_DEBUG);
|
||||||
|
|
||||||
return(array($photo,$thumb,$micro,$type,$photo_failure,$modified));
|
return([$photo, $thumb, $micro, $type, $photo_failure, $modified]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1,162 +0,0 @@
|
|||||||
<?php /** @file */
|
|
||||||
|
|
||||||
|
|
||||||
require_once('include/photo/photo_driver.php');
|
|
||||||
|
|
||||||
|
|
||||||
class photo_gd extends photo_driver {
|
|
||||||
|
|
||||||
function supportedTypes() {
|
|
||||||
$t = array();
|
|
||||||
$t['image/jpeg'] ='jpg';
|
|
||||||
if (imagetypes() & IMG_PNG) $t['image/png'] = 'png';
|
|
||||||
if (imagetypes() & IMG_GIF) $t['image/gif'] = 'gif';
|
|
||||||
return $t;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
function load($data, $type) {
|
|
||||||
$this->valid = false;
|
|
||||||
if(! $data)
|
|
||||||
return;
|
|
||||||
|
|
||||||
$this->image = @imagecreatefromstring($data);
|
|
||||||
if($this->image !== FALSE) {
|
|
||||||
$this->valid = true;
|
|
||||||
$this->setDimensions();
|
|
||||||
imagealphablending($this->image, false);
|
|
||||||
imagesavealpha($this->image, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function setDimensions() {
|
|
||||||
$this->width = imagesx($this->image);
|
|
||||||
$this->height = imagesy($this->image);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public function clearexif() {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public function destroy() {
|
|
||||||
if($this->is_valid()) {
|
|
||||||
imagedestroy($this->image);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getImage() {
|
|
||||||
if(!$this->is_valid())
|
|
||||||
return FALSE;
|
|
||||||
|
|
||||||
return $this->image;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function doScaleImage($dest_width,$dest_height) {
|
|
||||||
|
|
||||||
$dest = imagecreatetruecolor( $dest_width, $dest_height );
|
|
||||||
$width = imagesx($this->image);
|
|
||||||
$height = imagesy($this->image);
|
|
||||||
|
|
||||||
imagealphablending($dest, false);
|
|
||||||
imagesavealpha($dest, true);
|
|
||||||
if ($this->type=='image/png') imagefill($dest, 0, 0, imagecolorallocatealpha($dest, 0, 0, 0, 127)); // fill with alpha
|
|
||||||
imagecopyresampled($dest, $this->image, 0, 0, 0, 0, $dest_width, $dest_height, $width, $height);
|
|
||||||
if($this->image)
|
|
||||||
imagedestroy($this->image);
|
|
||||||
$this->image = $dest;
|
|
||||||
$this->setDimensions();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function rotate($degrees) {
|
|
||||||
if(!$this->is_valid())
|
|
||||||
return FALSE;
|
|
||||||
|
|
||||||
$this->image = imagerotate($this->image,$degrees,0);
|
|
||||||
$this->setDimensions();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function flip($horiz = true, $vert = false) {
|
|
||||||
if(!$this->is_valid())
|
|
||||||
return FALSE;
|
|
||||||
|
|
||||||
$w = imagesx($this->image);
|
|
||||||
$h = imagesy($this->image);
|
|
||||||
$flipped = imagecreate($w, $h);
|
|
||||||
if($horiz) {
|
|
||||||
for ($x = 0; $x < $w; $x++) {
|
|
||||||
imagecopy($flipped, $this->image, $x, 0, $w - $x - 1, 0, 1, $h);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if($vert) {
|
|
||||||
for ($y = 0; $y < $h; $y++) {
|
|
||||||
imagecopy($flipped, $this->image, 0, $y, 0, $h - $y - 1, $w, 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$this->image = $flipped;
|
|
||||||
$this->setDimensions(); // Shouldn't really be necessary
|
|
||||||
}
|
|
||||||
|
|
||||||
public function cropImage($max,$x,$y,$w,$h) {
|
|
||||||
if(!$this->is_valid())
|
|
||||||
return FALSE;
|
|
||||||
|
|
||||||
$dest = imagecreatetruecolor( $max, $max );
|
|
||||||
imagealphablending($dest, false);
|
|
||||||
imagesavealpha($dest, true);
|
|
||||||
if ($this->type=='image/png') imagefill($dest, 0, 0, imagecolorallocatealpha($dest, 0, 0, 0, 127)); // fill with alpha
|
|
||||||
imagecopyresampled($dest, $this->image, 0, 0, $x, $y, $max, $max, $w, $h);
|
|
||||||
if($this->image)
|
|
||||||
imagedestroy($this->image);
|
|
||||||
$this->image = $dest;
|
|
||||||
$this->setDimensions();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function cropImageRect($maxx,$maxy,$x,$y,$w,$h) {
|
|
||||||
if(!$this->is_valid())
|
|
||||||
return FALSE;
|
|
||||||
|
|
||||||
$dest = imagecreatetruecolor( $maxx, $maxy );
|
|
||||||
imagealphablending($dest, false);
|
|
||||||
imagesavealpha($dest, true);
|
|
||||||
if ($this->type=='image/png') imagefill($dest, 0, 0, imagecolorallocatealpha($dest, 0, 0, 0, 127)); // fill with alpha
|
|
||||||
imagecopyresampled($dest, $this->image, 0, 0, $x, $y, $maxx, $maxy, $w, $h);
|
|
||||||
if($this->image)
|
|
||||||
imagedestroy($this->image);
|
|
||||||
$this->image = $dest;
|
|
||||||
$this->setDimensions();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public function imageString() {
|
|
||||||
if(!$this->is_valid())
|
|
||||||
return FALSE;
|
|
||||||
|
|
||||||
$quality = FALSE;
|
|
||||||
|
|
||||||
ob_start();
|
|
||||||
|
|
||||||
switch($this->getType()){
|
|
||||||
case "image/png":
|
|
||||||
$quality = get_config('system','png_quality');
|
|
||||||
if((! $quality) || ($quality > 9))
|
|
||||||
$quality = PNG_QUALITY;
|
|
||||||
imagepng($this->image,NULL, $quality);
|
|
||||||
break;
|
|
||||||
case "image/jpeg":
|
|
||||||
default:
|
|
||||||
$quality = get_config('system','jpeg_quality');
|
|
||||||
if((! $quality) || ($quality > 100))
|
|
||||||
$quality = JPEG_QUALITY;
|
|
||||||
imagejpeg($this->image,NULL,$quality);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
$string = ob_get_contents();
|
|
||||||
ob_end_clean();
|
|
||||||
|
|
||||||
return $string;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
147
tests/unit/Photo/PhotoGdTest.php
Normal file
147
tests/unit/Photo/PhotoGdTest.php
Normal file
@ -0,0 +1,147 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Zotlabs\Tests\Unit\Photo;
|
||||||
|
|
||||||
|
use Zotlabs\Photo\PhotoGd;
|
||||||
|
use phpmock\phpunit\PHPMock;
|
||||||
|
use Zotlabs\Tests\Unit\UnitTestCase;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief PhotoGd test case.
|
||||||
|
*
|
||||||
|
* These tests are not really useful yet, just some obvious behaviour.
|
||||||
|
*
|
||||||
|
* @todo Compare the actual results.
|
||||||
|
* @todo Test different image types.
|
||||||
|
*/
|
||||||
|
class PhotoGdTest extends UnitTestCase {
|
||||||
|
|
||||||
|
use PHPMock;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var PhotoGd
|
||||||
|
*/
|
||||||
|
private $photoGd;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prepares the environment before running a test.
|
||||||
|
*/
|
||||||
|
protected function setUp() {
|
||||||
|
parent::setUp();
|
||||||
|
|
||||||
|
$data = file_get_contents('images/hz-16.png');
|
||||||
|
|
||||||
|
$this->photoGd = new PhotoGd($data, 'image/png');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cleans up the environment after running a test.
|
||||||
|
*/
|
||||||
|
protected function tearDown() {
|
||||||
|
$this->photoGd = null;
|
||||||
|
|
||||||
|
parent::tearDown();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests PhotoGd->supportedTypes()
|
||||||
|
*
|
||||||
|
* Without mocking gd this check is environment dependent.
|
||||||
|
*
|
||||||
|
public function testSupportedTypes() {
|
||||||
|
$sft = $this->photoGd->supportedTypes();
|
||||||
|
|
||||||
|
$this->assertArrayHasKey('image/jpeg', $sft);
|
||||||
|
$this->assertArrayHasKey('image/gif', $sft);
|
||||||
|
$this->assertArrayHasKey('image/png', $sft);
|
||||||
|
|
||||||
|
$this->assertArrayNotHasKey('image/foo', $sft);
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests PhotoGd->clearexif()
|
||||||
|
*/
|
||||||
|
public function testClearexifIsNotImplementedInGdAndDoesNotAlterImageOrReturnSomething() {
|
||||||
|
$data_before = $this->photoGd->getImage();
|
||||||
|
$this->assertNull($this->photoGd->clearexif());
|
||||||
|
$this->assertSame($data_before, $this->photoGd->getImage());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests PhotoGd->getImage()
|
||||||
|
*/
|
||||||
|
public function testGetimageReturnsAResource() {
|
||||||
|
$res = $this->photoGd->getImage();
|
||||||
|
$this->assertIsResource($res);
|
||||||
|
$this->assertEquals('gd', get_resource_type($res));
|
||||||
|
}
|
||||||
|
public function testGetimageReturnsFalseOnFailure() {
|
||||||
|
$this->photoGd = new PhotoGd('');
|
||||||
|
$this->assertFalse($this->photoGd->getImage());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests PhotoGd->doScaleImage()
|
||||||
|
*/
|
||||||
|
public function testDoscaleImageSetsCorrectDimensions() {
|
||||||
|
$this->photoGd->doScaleImage(5, 8);
|
||||||
|
|
||||||
|
$this->assertSame(5, $this->photoGd->getWidth());
|
||||||
|
$this->assertSame(8, $this->photoGd->getHeight());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests PhotoGd->rotate()
|
||||||
|
*/
|
||||||
|
public function testRotate360DegreesCreatesANewImage() {
|
||||||
|
$data = $this->photoGd->getImage();
|
||||||
|
$this->photoGd->rotate(360);
|
||||||
|
$this->assertNotEquals($data, $this->photoGd->getImage());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests PhotoGd->flip()
|
||||||
|
*
|
||||||
|
public function testFlip() {
|
||||||
|
// TODO Auto-generated PhotoGdTest->testFlip()
|
||||||
|
$this->markTestIncomplete("flip test not implemented");
|
||||||
|
|
||||||
|
$this->photoGd->flip();
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests PhotoGd->cropImageRect()
|
||||||
|
*/
|
||||||
|
public function testCropimagerectSetsCorrectDimensions() {
|
||||||
|
$this->photoGd->cropImageRect(10, 12, 1, 2, 11, 11);
|
||||||
|
|
||||||
|
$this->assertSame(10, $this->photoGd->getWidth());
|
||||||
|
$this->assertSame(12, $this->photoGd->getHeight());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests PhotoGd->imageString()
|
||||||
|
*/
|
||||||
|
public function testImagestringReturnsABinaryString() {
|
||||||
|
// Create a stub for global function get_config()
|
||||||
|
// get_config('system', 'png_quality')
|
||||||
|
// get_config('system', 'jpeg_quality');
|
||||||
|
$gc = $this->getFunctionMock('Zotlabs\Photo', 'get_config');
|
||||||
|
$gc->expects($this->once())->willReturnCallback(
|
||||||
|
function() {
|
||||||
|
switch($this->photoGd->getType()){
|
||||||
|
case 'image/png':
|
||||||
|
return 7;
|
||||||
|
case 'image/jpeg':
|
||||||
|
default:
|
||||||
|
return 70;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->assertIsString($this->photoGd->imageString());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
39
tests/unit/includes/PhotodriverTest.php
Normal file
39
tests/unit/includes/PhotodriverTest.php
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Zotlabs\Tests\Unit\includes;
|
||||||
|
|
||||||
|
//use Zotlabs\Photo\PhotoGd;
|
||||||
|
use Zotlabs\Tests\Unit\UnitTestCase;
|
||||||
|
//use phpmock\phpunit\PHPMock;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Unit Test cases for include/photo/photo_driver.php file.
|
||||||
|
*/
|
||||||
|
class PhotodriverTest extends UnitTestCase {
|
||||||
|
//use PHPMock;
|
||||||
|
|
||||||
|
public function testPhotofactoryReturnsNullForUnsupportedType() {
|
||||||
|
// php-mock can not mock global functions which is called by a global function.
|
||||||
|
// If the calling function is in a namespace it would work.
|
||||||
|
//$logger = $this->getFunctionMock(__NAMESPACE__, 'logger');
|
||||||
|
//$logger->expects($this->once());
|
||||||
|
|
||||||
|
//$ph = \photo_factory('', 'image/bmp');
|
||||||
|
//$this->assertNull($ph);
|
||||||
|
|
||||||
|
$this->markTestIncomplete('Need to mock logger(), otherwise not unit testable.');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testPhotofactoryReturnsPhotogdIfConfigIgnore_imagickIsSet() {
|
||||||
|
// php-mock can not mock global functions which is called by a global function.
|
||||||
|
// If the calling function is in a namespace it would work.
|
||||||
|
//$gc = $this->getFunctionMock(__NAMESPACE__, 'get_config');
|
||||||
|
// simulate get_config('system', 'ignore_imagick') configured
|
||||||
|
//$gc->expects($this->once())->willReturn(1)
|
||||||
|
|
||||||
|
//$ph = \photo_factory(file_get_contents('images/hz-16.png'), 'image/png');
|
||||||
|
//$this->assertInstanceOf(PhotoGd::class, $ph);
|
||||||
|
|
||||||
|
$this->markTestIncomplete('Need to mock get_config(), otherwise not unit testable.');
|
||||||
|
}
|
||||||
|
}
|
Reference in New Issue
Block a user