diff --git a/niucloud/vendor/composer/autoload_files.php b/niucloud/vendor/composer/autoload_files.php index d56d9f7a7..cc8b6f2c4 100644 --- a/niucloud/vendor/composer/autoload_files.php +++ b/niucloud/vendor/composer/autoload_files.php @@ -10,9 +10,9 @@ return array( '7b11c4dc42b3b3023073cb14e519683c' => $vendorDir . '/ralouphie/getallheaders/src/getallheaders.php', '0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => $vendorDir . '/symfony/polyfill-mbstring/bootstrap.php', 'c964ee0ededf28c96ebd9db5099ef910' => $vendorDir . '/guzzlehttp/promises/src/functions_include.php', - '37a3dc5111fe8f707ab4c132ef1dbc62' => $vendorDir . '/guzzlehttp/guzzle/src/functions_include.php', - 'a4a119a56e50fbb293281d9a48007e0e' => $vendorDir . '/symfony/polyfill-php80/bootstrap.php', '9b552a3cc426e3287cc811caefa3cf53' => $vendorDir . '/topthink/think-helper/src/helper.php', + 'a4a119a56e50fbb293281d9a48007e0e' => $vendorDir . '/symfony/polyfill-php80/bootstrap.php', + '37a3dc5111fe8f707ab4c132ef1dbc62' => $vendorDir . '/guzzlehttp/guzzle/src/functions_include.php', '35fab96057f1bf5e7aba31a8a6d5fdde' => $vendorDir . '/topthink/think-orm/stubs/load_stubs.php', 'a1105708a18b76903365ca1c4aa61b02' => $vendorDir . '/symfony/translation/Resources/functions.php', '0d59ee240a4cd96ddbb4ff164fccea4d' => $vendorDir . '/symfony/polyfill-php73/bootstrap.php', diff --git a/niucloud/vendor/composer/autoload_psr4.php b/niucloud/vendor/composer/autoload_psr4.php index 2ed5d0428..c8b1fa641 100644 --- a/niucloud/vendor/composer/autoload_psr4.php +++ b/niucloud/vendor/composer/autoload_psr4.php @@ -6,12 +6,12 @@ $vendorDir = dirname(__DIR__); $baseDir = dirname($vendorDir); return array( + 'yunwuxin\\cron\\' => array($vendorDir . '/yunwuxin/think-cron/src/cron'), 'think\\view\\driver\\' => array($vendorDir . '/topthink/think-view/src'), 'think\\trace\\' => array($vendorDir . '/topthink/think-trace/src'), 'think\\captcha\\' => array($vendorDir . '/topthink/think-captcha/src'), 'think\\app\\' => array($vendorDir . '/topthink/think-multi-app/src'), 'think\\' => array($vendorDir . '/topthink/framework/src/think', $vendorDir . '/topthink/think-helper/src', $vendorDir . '/topthink/think-image/src', $vendorDir . '/topthink/think-orm/src', $vendorDir . '/topthink/think-queue/src', $vendorDir . '/topthink/think-template/src'), - 'schedule\\' => array($vendorDir . '/yzh52521/schedule/src'), 'dh2y\\qrcode\\' => array($vendorDir . '/dh2y/think-qrcode/src'), 'core\\' => array($baseDir . '/core'), 'clagiordano\\weblibs\\configmanager\\' => array($vendorDir . '/clagiordano/weblibs-configmanager/src'), @@ -65,6 +65,7 @@ return array( 'GuzzleHttp\\Command\\Guzzle\\' => array($vendorDir . '/guzzlehttp/guzzle-services/src'), 'GuzzleHttp\\Command\\' => array($vendorDir . '/guzzlehttp/command/src'), 'GuzzleHttp\\' => array($vendorDir . '/guzzlehttp/guzzle/src'), + 'Grafika\\' => array($vendorDir . '/kosinix/grafika/src/Grafika'), 'Firebase\\JWT\\' => array($vendorDir . '/firebase/php-jwt/src'), 'Fastknife\\' => array($vendorDir . '/fastknife/ajcaptcha/src'), 'EasyWeChat\\' => array($vendorDir . '/overtrue/wechat/src'), diff --git a/niucloud/vendor/composer/autoload_static.php b/niucloud/vendor/composer/autoload_static.php index a7281c5ae..3154f0af1 100644 --- a/niucloud/vendor/composer/autoload_static.php +++ b/niucloud/vendor/composer/autoload_static.php @@ -11,9 +11,9 @@ class ComposerStaticInitf082efa3600aae2b847c3e8b4e641a4e '7b11c4dc42b3b3023073cb14e519683c' => __DIR__ . '/..' . '/ralouphie/getallheaders/src/getallheaders.php', '0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => __DIR__ . '/..' . '/symfony/polyfill-mbstring/bootstrap.php', 'c964ee0ededf28c96ebd9db5099ef910' => __DIR__ . '/..' . '/guzzlehttp/promises/src/functions_include.php', - '37a3dc5111fe8f707ab4c132ef1dbc62' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/functions_include.php', - 'a4a119a56e50fbb293281d9a48007e0e' => __DIR__ . '/..' . '/symfony/polyfill-php80/bootstrap.php', '9b552a3cc426e3287cc811caefa3cf53' => __DIR__ . '/..' . '/topthink/think-helper/src/helper.php', + 'a4a119a56e50fbb293281d9a48007e0e' => __DIR__ . '/..' . '/symfony/polyfill-php80/bootstrap.php', + '37a3dc5111fe8f707ab4c132ef1dbc62' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/functions_include.php', '35fab96057f1bf5e7aba31a8a6d5fdde' => __DIR__ . '/..' . '/topthink/think-orm/stubs/load_stubs.php', 'a1105708a18b76903365ca1c4aa61b02' => __DIR__ . '/..' . '/symfony/translation/Resources/functions.php', '0d59ee240a4cd96ddbb4ff164fccea4d' => __DIR__ . '/..' . '/symfony/polyfill-php73/bootstrap.php', @@ -39,6 +39,10 @@ class ComposerStaticInitf082efa3600aae2b847c3e8b4e641a4e ); public static $prefixLengthsPsr4 = array ( + 'y' => + array ( + 'yunwuxin\\cron\\' => 14, + ), 't' => array ( 'think\\view\\driver\\' => 18, @@ -47,10 +51,6 @@ class ComposerStaticInitf082efa3600aae2b847c3e8b4e641a4e 'think\\app\\' => 10, 'think\\' => 6, ), - 's' => - array ( - 'schedule\\' => 9, - ), 'd' => array ( 'dh2y\\qrcode\\' => 12, @@ -151,6 +151,7 @@ class ComposerStaticInitf082efa3600aae2b847c3e8b4e641a4e 'GuzzleHttp\\Command\\Guzzle\\' => 26, 'GuzzleHttp\\Command\\' => 19, 'GuzzleHttp\\' => 11, + 'Grafika\\' => 8, ), 'F' => array ( @@ -180,6 +181,10 @@ class ComposerStaticInitf082efa3600aae2b847c3e8b4e641a4e ); public static $prefixDirsPsr4 = array ( + 'yunwuxin\\cron\\' => + array ( + 0 => __DIR__ . '/..' . '/yunwuxin/think-cron/src/cron', + ), 'think\\view\\driver\\' => array ( 0 => __DIR__ . '/..' . '/topthink/think-view/src', @@ -205,10 +210,6 @@ class ComposerStaticInitf082efa3600aae2b847c3e8b4e641a4e 4 => __DIR__ . '/..' . '/topthink/think-queue/src', 5 => __DIR__ . '/..' . '/topthink/think-template/src', ), - 'schedule\\' => - array ( - 0 => __DIR__ . '/..' . '/yzh52521/schedule/src', - ), 'dh2y\\qrcode\\' => array ( 0 => __DIR__ . '/..' . '/dh2y/think-qrcode/src', @@ -422,6 +423,10 @@ class ComposerStaticInitf082efa3600aae2b847c3e8b4e641a4e array ( 0 => __DIR__ . '/..' . '/guzzlehttp/guzzle/src', ), + 'Grafika\\' => + array ( + 0 => __DIR__ . '/..' . '/kosinix/grafika/src/Grafika', + ), 'Firebase\\JWT\\' => array ( 0 => __DIR__ . '/..' . '/firebase/php-jwt/src', diff --git a/niucloud/vendor/composer/installed.json b/niucloud/vendor/composer/installed.json index 749b7202e..49e0580d5 100644 --- a/niucloud/vendor/composer/installed.json +++ b/niucloud/vendor/composer/installed.json @@ -1341,6 +1341,61 @@ ], "install-path": "../intervention/image" }, + { + "name": "kosinix/grafika", + "version": "dev-master", + "version_normalized": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/kosinix/grafika.git", + "reference": "211f61fc334b8b36616b23e8af7c5727971d96ee" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/kosinix/grafika/zipball/211f61fc334b8b36616b23e8af7c5727971d96ee", + "reference": "211f61fc334b8b36616b23e8af7c5727971d96ee", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "php": ">=5.3" + }, + "time": "2017-06-20T03:13:49+00:00", + "default-branch": true, + "type": "library", + "installation-source": "dist", + "autoload": { + "psr-4": { + "Grafika\\": "src/Grafika" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT", + "GPL-2.0+" + ], + "authors": [ + { + "name": "Nico Amarilla", + "homepage": "https://www.kosinix.com" + } + ], + "description": "An image manipulation library for PHP.", + "homepage": "http://kosinix.github.io/grafika", + "keywords": [ + "grafika" + ], + "support": { + "issues": "https://github.com/kosinix/grafika/issues", + "source": "https://github.com/kosinix/grafika/tree/2.0.8" + }, + "install-path": "../kosinix/grafika" + }, { "name": "laravel/serializable-closure", "version": "v1.2.2", @@ -5711,18 +5766,18 @@ "install-path": "../yansongda/supports" }, { - "name": "yzh52521/schedule", - "version": "v1.0.0", - "version_normalized": "1.0.0.0", + "name": "yunwuxin/think-cron", + "version": "v3.0.5", + "version_normalized": "3.0.5.0", "source": { "type": "git", - "url": "https://github.com/yzh52521/schedule.git", - "reference": "4c8f537f0c08417e785f84b8b91bf16b083cb163" + "url": "https://github.com/yunwuxin/think-cron.git", + "reference": "a5e5c679b7f5daedab9fb4bb00b641b6c4a054ca" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/yzh52521/schedule/zipball/4c8f537f0c08417e785f84b8b91bf16b083cb163", - "reference": "4c8f537f0c08417e785f84b8b91bf16b083cb163", + "url": "https://api.github.com/repos/yunwuxin/think-cron/zipball/a5e5c679b7f5daedab9fb4bb00b641b6c4a054ca", + "reference": "a5e5c679b7f5daedab9fb4bb00b641b6c4a054ca", "shasum": "", "mirrors": [ { @@ -5732,35 +5787,48 @@ ] }, "require": { - "nesbot/carbon": "^2.0", - "php": ">=7.1" + "dragonmantank/cron-expression": "^3.0", + "nesbot/carbon": "^2.28", + "symfony/process": "^4.4|^5.0", + "topthink/framework": "^6.0" }, - "time": "2020-07-02T01:34:32+00:00", + "require-dev": { + "topthink/think-swoole": "^4.0" + }, + "time": "2021-12-22T09:25:54+00:00", "type": "library", + "extra": { + "think": { + "config": { + "cron": "src/config.php" + }, + "services": [ + "yunwuxin\\cron\\Service" + ] + } + }, "installation-source": "dist", "autoload": { "psr-4": { - "schedule\\": "src/" + "yunwuxin\\cron\\": "src/cron" } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "Apache-2.0" ], - "description": "task schedule,schedule,thinkphp schedule,任务调度", - "keywords": [ - "schedule", - "task schedule", - "think-schedule", - "thinkphp", - "thinkphp5.1", - "thinkphp6" + "authors": [ + { + "name": "yunwuxin", + "email": "448901948@qq.com" + } ], + "description": "计划任务", "support": { - "issues": "https://github.com/yzh52521/schedule/issues", - "source": "https://github.com/yzh52521/schedule/tree/v1.0.0" + "issues": "https://github.com/yunwuxin/think-cron/issues", + "source": "https://github.com/yunwuxin/think-cron/tree/v3.0.5" }, - "install-path": "../yzh52521/schedule" + "install-path": "../yunwuxin/think-cron" } ], "dev": true, diff --git a/niucloud/vendor/composer/installed.php b/niucloud/vendor/composer/installed.php index 6afaec7bb..c4d2a5419 100644 --- a/niucloud/vendor/composer/installed.php +++ b/niucloud/vendor/composer/installed.php @@ -3,7 +3,7 @@ 'name' => 'topthink/think', 'pretty_version' => 'dev-master', 'version' => 'dev-master', - 'reference' => 'dc3bba8859823f8836483423b8b7bfdda3a18e7d', + 'reference' => 'bdc0c0b0594f0eff6e5ca8d8018f125190f8ee95', 'type' => 'project', 'install_path' => __DIR__ . '/../../', 'aliases' => array(), @@ -163,6 +163,17 @@ 'aliases' => array(), 'dev_requirement' => false, ), + 'kosinix/grafika' => array( + 'pretty_version' => 'dev-master', + 'version' => 'dev-master', + 'reference' => '211f61fc334b8b36616b23e8af7c5727971d96ee', + 'type' => 'library', + 'install_path' => __DIR__ . '/../kosinix/grafika', + 'aliases' => array( + 0 => '9999999-dev', + ), + 'dev_requirement' => false, + ), 'laravel/serializable-closure' => array( 'pretty_version' => 'v1.2.2', 'version' => '1.2.2.0', @@ -652,7 +663,7 @@ 'topthink/think' => array( 'pretty_version' => 'dev-master', 'version' => 'dev-master', - 'reference' => 'dc3bba8859823f8836483423b8b7bfdda3a18e7d', + 'reference' => 'bdc0c0b0594f0eff6e5ca8d8018f125190f8ee95', 'type' => 'project', 'install_path' => __DIR__ . '/../../', 'aliases' => array(), @@ -766,12 +777,12 @@ 'aliases' => array(), 'dev_requirement' => false, ), - 'yzh52521/schedule' => array( - 'pretty_version' => 'v1.0.0', - 'version' => '1.0.0.0', - 'reference' => '4c8f537f0c08417e785f84b8b91bf16b083cb163', + 'yunwuxin/think-cron' => array( + 'pretty_version' => 'v3.0.5', + 'version' => '3.0.5.0', + 'reference' => 'a5e5c679b7f5daedab9fb4bb00b641b6c4a054ca', 'type' => 'library', - 'install_path' => __DIR__ . '/../yzh52521/schedule', + 'install_path' => __DIR__ . '/../yunwuxin/think-cron', 'aliases' => array(), 'dev_requirement' => false, ), diff --git a/niucloud/vendor/kosinix/grafika/composer.json b/niucloud/vendor/kosinix/grafika/composer.json new file mode 100644 index 000000000..0f0f36eac --- /dev/null +++ b/niucloud/vendor/kosinix/grafika/composer.json @@ -0,0 +1,25 @@ +{ + "name": "kosinix/grafika", + "description": "An image manipulation library for PHP.", + "keywords": ["grafika"], + "homepage": "http://kosinix.github.io/grafika", + "type": "library", + "license": [ + "MIT", + "GPL-2.0+" + ], + "authors": [ + { + "name": "Nico Amarilla", + "homepage": "https://www.kosinix.com" + } + ], + "require": { + "php": ">=5.3" + }, + "autoload": { + "psr-4": { + "Grafika\\": "src/Grafika" + } + } +} \ No newline at end of file diff --git a/niucloud/vendor/kosinix/grafika/fonts/LiberationSans-Regular.ttf b/niucloud/vendor/kosinix/grafika/fonts/LiberationSans-Regular.ttf new file mode 100644 index 000000000..59d2e251b Binary files /dev/null and b/niucloud/vendor/kosinix/grafika/fonts/LiberationSans-Regular.ttf differ diff --git a/niucloud/vendor/kosinix/grafika/license.txt b/niucloud/vendor/kosinix/grafika/license.txt new file mode 100644 index 000000000..5e0513747 --- /dev/null +++ b/niucloud/vendor/kosinix/grafika/license.txt @@ -0,0 +1,8 @@ +The MIT License (MIT) +Copyright (c) 2016 Nico G. Amarilla + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/niucloud/vendor/kosinix/grafika/src/Grafika/Color.php b/niucloud/vendor/kosinix/grafika/src/Grafika/Color.php new file mode 100644 index 000000000..65202878b --- /dev/null +++ b/niucloud/vendor/kosinix/grafika/src/Grafika/Color.php @@ -0,0 +1,105 @@ +hexString = $hexString; // TODO: Validate hexstring + $this->alpha = $alpha; + } + + /** + * Get RGB array + * + * @return array Contains array($r, $g, $b) + */ + public function getRgb(){ + return $this->hexToRgb( $this->hexString ); + } + + /** + * Get RGBA array + * + * @return array Contains array($r, $g, $b, $a) + */ + public function getRgba(){ + $rgba = $this->hexToRgb( $this->hexString ); + $rgba[] = $this->alpha; + return $rgba; + } + + /** + * Convert hex string to RGB + * @param string $hex Hex string. Possible values: #ffffff, #fff, fff + * @return array Contains (RGB) values red, green and blue + */ + public function hexToRgb( $hex ) { + $hex = ltrim($hex, '#'); // Remove # + + if(strlen($hex) == 3) { + $r = hexdec(substr($hex,0,1).substr($hex,0,1)); + $g = hexdec(substr($hex,1,1).substr($hex,1,1)); + $b = hexdec(substr($hex,2,1).substr($hex,2,1)); + } else { + $r = hexdec(substr($hex,0,2)); + $g = hexdec(substr($hex,2,2)); + $b = hexdec(substr($hex,4,2)); + } + return array($r, $g, $b); // Returns an array with the rgb values + } + + /** + * Get hex string. + * + * @return string + */ + public function getHexString() { + return $this->hexString; + } + + /** + * Set hex string. + * + * @param string $hexString + */ + public function setHexString($hexString) { + $this->hexString = $hexString; + } + + /** + * Alpha value. + * @return float + */ + public function getAlpha() { + return $this->alpha; + } + + /** + * @param float $alpha + */ + public function setAlpha($alpha) { + $this->alpha = $alpha; + } + + +} \ No newline at end of file diff --git a/niucloud/vendor/kosinix/grafika/src/Grafika/DrawingObject/CubicBezier.php b/niucloud/vendor/kosinix/grafika/src/Grafika/DrawingObject/CubicBezier.php new file mode 100644 index 000000000..980e2b4fb --- /dev/null +++ b/niucloud/vendor/kosinix/grafika/src/Grafika/DrawingObject/CubicBezier.php @@ -0,0 +1,105 @@ +point1 = $point1; + $this->control1 = $control1; + $this->control2 = $control2; + $this->point2 = $point2; + $this->color = $color; + + } + + /** + * @return array + */ + public function getPoint1() + { + return $this->point1; + } + + /** + * @return array + */ + public function getControl1() + { + return $this->control1; + } + + /** + * @return array + */ + public function getControl2() + { + return $this->control2; + } + + /** + * @return array + */ + public function getPoint2() + { + return $this->point2; + } + + /** + * @return Color + */ + public function getColor() + { + return $this->color; + } + +} \ No newline at end of file diff --git a/niucloud/vendor/kosinix/grafika/src/Grafika/DrawingObject/Ellipse.php b/niucloud/vendor/kosinix/grafika/src/Grafika/DrawingObject/Ellipse.php new file mode 100644 index 000000000..dcea008e0 --- /dev/null +++ b/niucloud/vendor/kosinix/grafika/src/Grafika/DrawingObject/Ellipse.php @@ -0,0 +1,128 @@ +width = $width; + $this->height = $height; + $this->pos = $pos; + $this->borderSize = $borderSize; + $this->borderColor = $borderColor; + $this->fillColor = $fillColor; + } + + /** + * @return int + */ + public function getWidth() + { + return $this->width; + } + + /** + * @return int + */ + public function getHeight() + { + return $this->height; + } + + /** + * @return array + */ + public function getPos() + { + return $this->pos; + } + + /** + * @return int + */ + public function getBorderSize() + { + return $this->borderSize; + } + + /** + * @return Color + */ + public function getFillColor() + { + return $this->fillColor; + } + + /** + * @return Color + */ + public function getBorderColor() + { + return $this->borderColor; + } + + +} \ No newline at end of file diff --git a/niucloud/vendor/kosinix/grafika/src/Grafika/DrawingObject/Line.php b/niucloud/vendor/kosinix/grafika/src/Grafika/DrawingObject/Line.php new file mode 100644 index 000000000..1abfd509e --- /dev/null +++ b/niucloud/vendor/kosinix/grafika/src/Grafika/DrawingObject/Line.php @@ -0,0 +1,86 @@ +point1 = $point1; + $this->point2 = $point2; + $this->thickness = $thickness; + $this->color = $color; + } + + /** + * @return array + */ + public function getPoint1() + { + return $this->point1; + } + + /** + * @return array + */ + public function getPoint2() + { + return $this->point2; + } + + /** + * @return int + */ + public function getThickness() + { + return $this->thickness; + } + + /** + * @return Color + */ + public function getColor() + { + return $this->color; + } + +} \ No newline at end of file diff --git a/niucloud/vendor/kosinix/grafika/src/Grafika/DrawingObject/Polygon.php b/niucloud/vendor/kosinix/grafika/src/Grafika/DrawingObject/Polygon.php new file mode 100644 index 000000000..bef3b3238 --- /dev/null +++ b/niucloud/vendor/kosinix/grafika/src/Grafika/DrawingObject/Polygon.php @@ -0,0 +1,116 @@ +points = $points; + $this->borderSize = $borderSize; + $this->borderColor = $borderColor; + $this->fillColor = $fillColor; + } + + /** + * @return int + */ + public function getWidth() + { + return $this->width; + } + + /** + * @return int + */ + public function getHeight() + { + return $this->height; + } + + /** + * @return array + */ + public function getPoints() + { + return $this->points; + } + + /** + * @return int + */ + public function getBorderSize() + { + return $this->borderSize; + } + + /** + * @return Color + */ + public function getFillColor() + { + return $this->fillColor; + } + + /** + * @return Color + */ + public function getBorderColor() + { + return $this->borderColor; + } + + +} \ No newline at end of file diff --git a/niucloud/vendor/kosinix/grafika/src/Grafika/DrawingObject/QuadraticBezier.php b/niucloud/vendor/kosinix/grafika/src/Grafika/DrawingObject/QuadraticBezier.php new file mode 100644 index 000000000..cf0f04fc0 --- /dev/null +++ b/niucloud/vendor/kosinix/grafika/src/Grafika/DrawingObject/QuadraticBezier.php @@ -0,0 +1,89 @@ +point1 = $point1; + $this->control = $control; + $this->point2 = $point2; + $this->color = $color; + } + + /** + * @return array + */ + public function getPoint1() + { + return $this->point1; + } + + /** + * @return array + */ + public function getControl() + { + return $this->control; + } + + /** + * @return array + */ + public function getPoint2() + { + return $this->point2; + } + + /** + * @return Color + */ + public function getColor() + { + return $this->color; + } + +} \ No newline at end of file diff --git a/niucloud/vendor/kosinix/grafika/src/Grafika/DrawingObject/Rectangle.php b/niucloud/vendor/kosinix/grafika/src/Grafika/DrawingObject/Rectangle.php new file mode 100644 index 000000000..7715e0976 --- /dev/null +++ b/niucloud/vendor/kosinix/grafika/src/Grafika/DrawingObject/Rectangle.php @@ -0,0 +1,126 @@ +width = $width; + $this->height = $height; + $this->pos = $pos; + $this->borderSize = $borderSize; + $this->borderColor = $borderColor; + $this->fillColor = $fillColor; + } + + /** + * @return int + */ + public function getWidth() + { + return $this->width; + } + + /** + * @return int + */ + public function getHeight() + { + return $this->height; + } + + /** + * @return array + */ + public function getPos() + { + return $this->pos; + } + + /** + * @return int + */ + public function getBorderSize() + { + return $this->borderSize; + } + + /** + * @return Color + */ + public function getFillColor() + { + return $this->fillColor; + } + + /** + * @return Color + */ + public function getBorderColor() + { + return $this->borderColor; + } + + +} \ No newline at end of file diff --git a/niucloud/vendor/kosinix/grafika/src/Grafika/DrawingObjectInterface.php b/niucloud/vendor/kosinix/grafika/src/Grafika/DrawingObjectInterface.php new file mode 100644 index 000000000..09ec77162 --- /dev/null +++ b/niucloud/vendor/kosinix/grafika/src/Grafika/DrawingObjectInterface.php @@ -0,0 +1,17 @@ +getWidth(); + $height = $image->getHeight(); + $gd = $image->getCore(); + + list($x0, $y0) = $this->point1; + list($x1, $y1) = $this->control1; + list($x2, $y2) = $this->control2; + list($x3, $y3) = $this->point2; + + $this->plot($gd, $x0, $y0, $x1, $y1, $x2, $y2, $x3, $y3); + + $type = $image->getType(); + $file = $image->getImageFile(); + return new Image($gd, $file, $width, $height, $type); // Create new image with updated core + } + + protected function plot($gd, $x0, $y0, $x1, $y1, $x2, $y2, $x3, $y3) + { /* plot any cubic Bezier curve */ + $n = 0; + $i = 0; + $xc = $x0 + $x1 - $x2 - $x3; + $xa = $xc - 4 * ($x1 - $x2); + $xb = $x0 - $x1 - $x2 + $x3; + $xd = $xb + 4 * ($x1 + $x2); + $yc = $y0 + $y1 - $y2 - $y3; + $ya = $yc - 4 * ($y1 - $y2); + $yb = $y0 - $y1 - $y2 + $y3; + $yd = $yb + 4 * ($y1 + $y2); + $fx0 = $x0; + $fx1 = 0; + $fx2 = 0; + $fx3 = 0; + $fy0 = $y0; + $fy1 = 0; + $fy2 = 0; + $fy3 = 0; + $t1 = $xb * $xb - $xa * $xc; + $t2 = 0; + $t = array(); + /* sub-divide curve at gradient sign changes */ + if ($xa == 0) { /* horizontal */ + if (abs($xc) < 2 * abs($xb)) { + $t[$n++] = $xc / (2.0 * $xb); + } /* one change */ + } else { + if ($t1 > 0.0) { /* two changes */ + $t2 = sqrt($t1); + $t1 = ($xb - $t2) / $xa; + if (abs($t1) < 1.0) { + $t[$n++] = $t1; + } + $t1 = ($xb + $t2) / $xa; + if (abs($t1) < 1.0) { + $t[$n++] = $t1; + } + } + } + $t1 = $yb * $yb - $ya * $yc; + if ($ya == 0) { /* vertical */ + if (abs($yc) < 2 * abs($yb)) { + $t[$n++] = $yc / (2.0 * $yb); + } /* one change */ + } else { + if ($t1 > 0.0) { /* two changes */ + $t2 = sqrt($t1); + $t1 = ($yb - $t2) / $ya; + if (abs($t1) < 1.0) { + $t[$n++] = $t1; + } + $t1 = ($yb + $t2) / $ya; + if (abs($t1) < 1.0) { + $t[$n++] = $t1; + } + } + } + for ($i = 1; $i < $n; $i++) /* bubble sort of 4 points */ { + if (($t1 = $t[$i - 1]) > $t[$i]) { + $t[$i - 1] = $t[$i]; + $t[$i] = $t1; + $i = 0; + } + } + $t1 = -1.0; + $t[$n] = 1.0; /* begin / end point */ + for ($i = 0; $i <= $n; $i++) { /* plot each segment separately */ + $t2 = $t[$i]; /* sub-divide at $t[$i-1], $t[$i] */ + $fx1 = ($t1 * ($t1 * $xb - 2 * $xc) - $t2 * ($t1 * ($t1 * $xa - 2 * $xb) + $xc) + $xd) / 8 - $fx0; + $fy1 = ($t1 * ($t1 * $yb - 2 * $yc) - $t2 * ($t1 * ($t1 * $ya - 2 * $yb) + $yc) + $yd) / 8 - $fy0; + $fx2 = ($t2 * ($t2 * $xb - 2 * $xc) - $t1 * ($t2 * ($t2 * $xa - 2 * $xb) + $xc) + $xd) / 8 - $fx0; + $fy2 = ($t2 * ($t2 * $yb - 2 * $yc) - $t1 * ($t2 * ($t2 * $ya - 2 * $yb) + $yc) + $yd) / 8 - $fy0; + $fx0 -= $fx3 = ($t2 * ($t2 * (3 * $xb - $t2 * $xa) - 3 * $xc) + $xd) / 8; + $fy0 -= $fy3 = ($t2 * ($t2 * (3 * $yb - $t2 * $ya) - 3 * $yc) + $yd) / 8; + $x3 = floor($fx3 + 0.5); + $y3 = floor($fy3 + 0.5); /* scale bounds to int */ + if ($fx0 != 0.0) { + $fx1 *= $fx0 = ($x0 - $x3) / $fx0; + $fx2 *= $fx0; + } + if ($fy0 != 0.0) { + $fy1 *= $fy0 = ($y0 - $y3) / $fy0; + $fy2 *= $fy0; + } + if ($x0 != $x3 || $y0 != $y3) /* segment $t1 - $t2 */ { + $this->plotCubicSegment($gd, $x0, $y0, $x0 + $fx1, $y0 + $fy1, $x0 + $fx2, $y0 + $fy2, $x3, $y3); + } + $x0 = $x3; + $y0 = $y3; + $fx0 = $fx3; + $fy0 = $fy3; + $t1 = $t2; + } + } + + protected function plotCubicSegment($gd, $x0, $y0, $x1, $y1, $x2, $y2, $x3, $y3) + { /* plot limited anti-aliased cubic Bezier segment */ + $f = 0; + $fx = 0; + $fy = 0; + $leg = 1; + $sx = $x0 < $x3 ? 1 : -1; + $sy = $y0 < $y3 ? 1 : -1; /* step direction */ + + $xc = -abs($x0 + $x1 - $x2 - $x3); + $xa = $xc - 4 * $sx * ($x1 - $x2); + $xb = $sx * ($x0 - $x1 - $x2 + $x3); + $yc = -abs($y0 + $y1 - $y2 - $y3); + $ya = $yc - 4 * $sy * ($y1 - $y2); + $yb = $sy * ($y0 - $y1 - $y2 + $y3); + + $ab = 0; + $ac = 0; + $bc = 0; + $ba = 0; + $xx = 0; + $xy = 0; + $yy = 0; + $dx = 0; + $dy = 0; + $ex = 0; + $px = 0; + $py = 0; + $ed = 0; + $ip = 0; + $EP = 0.01; + /* check for curve restrains */ + /* slope P0-P1 == P2-P3 and (P0-P3 == P1-P2 or no slope change) */ + assert(($x1 - $x0) * ($x2 - $x3) < $EP && (($x3 - $x0) * ($x1 - $x2) < $EP || $xb * $xb < $xa * $xc + $EP)); + assert(($y1 - $y0) * ($y2 - $y3) < $EP && (($y3 - $y0) * ($y1 - $y2) < $EP || $yb * $yb < $ya * $yc + $EP)); + if ($xa == 0 && $ya == 0) { /* quadratic Bezier */ + $sx = floor((3 * $x1 - $x0 + 1) / 2); + $sy = floor((3 * $y1 - $y0 + 1) / 2); /* new midpoint */ + $this->plotQuadSegment($gd, $x0, $y0, $sx, $sy, $x3, $y3); + return; + } + $x1 = ($x1 - $x0) * ($x1 - $x0) + ($y1 - $y0) * ($y1 - $y0) + 1; /* line lengths */ + $x2 = ($x2 - $x3) * ($x2 - $x3) + ($y2 - $y3) * ($y2 - $y3) + 1; + do { /* loop over both ends */ + $ab = $xa * $yb - $xb * $ya; + $ac = $xa * $yc - $xc * $ya; + $bc = $xb * $yc - $xc * $yb; + $ip = 4 * $ab * $bc - $ac * $ac; /* self intersection loop at all? */ + $ex = $ab * ($ab + $ac - 3 * $bc) + $ac * $ac; /* P0 part of self-intersection loop? */ + $f = $ex > 0 ? 1 : sqrt(1 + 1024 / $x1); /* calculate resolution */ + $ab *= $f; + $ac *= $f; + $bc *= $f; + $ex *= $f * $f; /* increase resolution */ + $xy = 9 * ($ab + $ac + $bc) / 8; + $ba = 8 * ($xa - $ya);/* init differences of 1st degree */ + $dx = 27 * (8 * $ab * ($yb * $yb - $ya * $yc) + $ex * ($ya + 2 * $yb + $yc)) / 64 - $ya * $ya * ($xy - $ya); + $dy = 27 * (8 * $ab * ($xb * $xb - $xa * $xc) - $ex * ($xa + 2 * $xb + $xc)) / 64 - $xa * $xa * ($xy + $xa); + /* init differences of 2nd degree */ + $xx = 3 * (3 * $ab * (3 * $yb * $yb - $ya * $ya - 2 * $ya * $yc) - $ya * (3 * $ac * ($ya + $yb) + $ya * $ba)) / 4; + $yy = 3 * (3 * $ab * (3 * $xb * $xb - $xa * $xa - 2 * $xa * $xc) - $xa * (3 * $ac * ($xa + $xb) + $xa * $ba)) / 4; + $xy = $xa * $ya * (6 * $ab + 6 * $ac - 3 * $bc + $ba); + $ac = $ya * $ya; + $ba = $xa * $xa; + $xy = 3 * ($xy + 9 * $f * ($ba * $yb * $yc - $xb * $xc * $ac) - 18 * $xb * $yb * $ab) / 8; + if ($ex < 0) { /* negate values if inside self-intersection loop */ + $dx = -$dx; + $dy = -$dy; + $xx = -$xx; + $yy = -$yy; + $xy = -$xy; + $ac = -$ac; + $ba = -$ba; + } /* init differences of 3rd degree */ + $ab = 6 * $ya * $ac; + $ac = -6 * $xa * $ac; + $bc = 6 * $ya * $ba; + $ba = -6 * $xa * $ba; + $dx += $xy; + $ex = $dx + $dy; + $dy += $xy; /* error of 1st step */ + for ($fx = $fy = $f; $x0 != $x3 && $y0 != $y3;) { + $y1 = min($xy - $dx, $dy - $xy); + $ed = max($xy - $dx, $dy - $xy); /* approximate error distance */ + $ed = $f * ($ed + 2 * $ed * $y1 * $y1 / (4 * $ed * $ed + $y1 * $y1)); + $y1 = 255 * abs($ex - ($f - $fx + 1) * $dx - ($f - $fy + 1) * $dy + $f * $xy) / $ed; + if ($y1 < 256) { + $this->setPixel($gd, $x0, $y0, $y1 / 255); + } /* plot curve */ + $px = abs($ex - ($f - $fx + 1) * $dx + ($fy - 1) * $dy); /* pixel intensity x move */ + $py = abs($ex + ($fx - 1) * $dx - ($f - $fy + 1) * $dy); /* pixel intensity y move */ + $y2 = $y0; + do { /* move sub-steps of one pixel */ + if ($ip >= -$EP) /* intersection possible? -> check.. */ { + if ($dx + $xx > $xy || $dy + $yy < $xy) { + goto exits; + } + } /* two x or y steps */ + $y1 = 2 * $ex + $dx; /* save value for test of y step */ + if (2 * $ex + $dy > 0) { /* x sub-step */ + $fx--; + $ex += $dx += $xx; + $dy += $xy += $ac; + $yy += $bc; + $xx += $ab; + } else { + if ($y1 > 0) { + goto exits; + } + } /* tiny nearly cusp */ + if ($y1 <= 0) { /* y sub-step */ + $fy--; + $ex += $dy += $yy; + $dx += $xy += $bc; + $xx += $ac; + $yy += $ba; + } + } while ($fx > 0 && $fy > 0); /* pixel complete? */ + if (2 * $fy <= $f) { /* x+ anti-aliasing pixel */ + if ($py < $ed) { + $this->setPixel($gd, $x0 + $sx, $y0, $py / $ed); + } /* plot curve */ + $y0 += $sy; + $fy += $f; /* y step */ + } + if (2 * $fx <= $f) { /* y+ anti-aliasing pixel */ + if ($px < $ed) { + $this->setPixel($gd, $x0, $y2 + $sy, $px / $ed); + } /* plot curve */ + $x0 += $sx; + $fx += $f; /* x step */ + } + } + break; /* finish curve by line */ + exits: + if (2 * $ex < $dy && 2 * $fy <= $f + 2) { /* round x+ approximation pixel */ + if ($py < $ed) { + $this->setPixel($gd, $x0 + $sx, $y0, $py / $ed); + } /* plot curve */ + $y0 += $sy; + } + if (2 * $ex > $dx && 2 * $fx <= $f + 2) { /* round y+ approximation pixel */ + if ($px < $ed) { + $this->setPixel($gd, $x0, $y2 + $sy, $px / $ed); + } /* plot curve */ + $x0 += $sx; + } + $xx = $x0; + $x0 = $x3; + $x3 = $xx; + $sx = -$sx; + $xb = -$xb; /* swap legs */ + $yy = $y0; + $y0 = $y3; + $y3 = $yy; + $sy = -$sy; + $yb = -$yb; + $x1 = $x2; + } while ($leg--); /* try other end */ + $this->plotLine($gd, $x0, $y0, $x3, $y3); /* remaining part in case of cusp or crunode */ + } + + protected function plotQuadSegment($gd, $x0, $y0, $x1, $y1, $x2, $y2) + { /* draw an limited anti-aliased quadratic Bezier segment */ + $sx = $x2 - $x1; + $sy = $y2 - $y1; + $xx = $x0 - $x1; + $yy = $y0 - $y1; + $xy = $dx = $dy = $err = $ed = 0; + $cur = $xx * $sy - $yy * $sx; /* $curvature */ + if ($sx * (int)$sx + $sy * (int)$sy > $xx * $xx + $yy * $yy) { /* begin with longer part */ + $x2 = $x0; + $x0 = $sx + $x1; + $y2 = $y0; + $y0 = $sy + $y1; + $cur = -$cur; /* swap P0 P2 */ + } + if ($cur != 0) { /* no straight line */ + $xx += $sx; + $xx *= $sx = $x0 < $x2 ? 1 : -1; /* x step direction */ + $yy += $sy; + $yy *= $sy = $y0 < $y2 ? 1 : -1; /* y step direction */ + $xy = 2 * $xx * $yy; + $xx *= $xx; + $yy *= $yy; /* differences 2nd degree */ + if ($cur * $sx * $sy < 0) { /* negat$ed $curvature? */ + $xx = -$xx; + $yy = -$yy; + $xy = -$xy; + $cur = -$cur; + } + $dx = 4.0 * $sy * ($x1 - $x0) * $cur + $xx - $xy; /* differences 1st degree */ + $dy = 4.0 * $sx * ($y0 - $y1) * $cur + $yy - $xy; + $xx += $xx; + $yy += $yy; + $err = $dx + $dy + $xy; /* $error 1st step */ + do { + $cur = min($dx + $xy, -$xy - $dy); + $ed = max($dx + $xy, -$xy - $dy); /* approximate $error distance */ + $ed += 2 * $ed * $cur * $cur / (4 * $ed * $ed + $cur * $cur); + $this->setPixel($gd, $x0, $y0, abs($err - $dx - $dy - $xy) / $ed); /* plot $curve */ + if ($x0 == $x2 || $y0 == $y2) { + break; + } /* $curve finish$ed */ + $x1 = $x0; + $cur = $dx - $err; + $y1 = 2 * $err + $dy < 0; + if (2 * $err + $dx > 0) { /* x step */ + if ($err - $dy < $ed) { + $this->setPixel($gd, $x0, $y0 + $sy, abs($err - $dy) / $ed); + } + $x0 += $sx; + $dx -= $xy; + $err += $dy += $yy; + } + if ($y1) { /* y step */ + if ($cur < $ed) { + $this->setPixel($gd, $x1 + $sx, $y0, abs($cur) / $ed); + } + $y0 += $sy; + $dy -= $xy; + $err += $dx += $xx; + } + } while ($dy < $dx); /* gradient negates -> close curves */ + } + $this->plotLine($gd, $x0, $y0, $x2, $y2); /* plot remaining needle to end */ + } + + protected function plotLine($gd, $x0, $y0, $x1, $y1) + { /* draw a black (0) anti-aliased line on white (255) background */ + $dx = abs($x1 - $x0); + $sx = $x0 < $x1 ? 1 : -1; + $dy = -abs($y1 - $y0); + $sy = $y0 < $y1 ? 1 : -1; + $err = $dx + $dy; + $e2 = $x2 = 0; /* $error value e_xy */ + $ed = $dx - $dy == 0 ? 1 : sqrt((float)$dx * $dx + (float)$dy * $dy); + for (; ;) { /* pixel loop */ + $this->setPixel($gd, $x0, $y0, abs($err - $dx - $dy) / $ed); + $e2 = $err; + $x2 = $x0; + if (2 * $e2 + $dx >= 0) { /* x step */ + if ($x0 == $x1) { + break; + } + if ($e2 - $dy < $ed) { + $this->setPixel($gd, $x0, $y0 + $sy, ($e2 - $dy) / $ed); + } + $err += $dy; + $x0 += $sx; + } + if (2 * $e2 + $dy <= 0) { /* y step */ + if ($y0 == $y1) { + break; + } + if ($dx - $e2 < $ed) { + $this->setPixel($gd, $x2 + $sx, $y0, ($dx - $e2) / $ed); + } + $err += $dx; + $y0 += $sy; + } + } + } + + /** + * @param resource $gd + * @param int $x + * @param int $y + * @param float $ar Alpha ratio + */ + protected function setPixel($gd, $x, $y, $ar) + { + list($r, $g, $b) = $this->color->getRgb(); + $c = imagecolorallocatealpha($gd, $r, $g, $b, 127 * $ar); + imagesetpixel($gd, $x, $y, $c); + } +} \ No newline at end of file diff --git a/niucloud/vendor/kosinix/grafika/src/Grafika/Gd/DrawingObject/Ellipse.php b/niucloud/vendor/kosinix/grafika/src/Grafika/Gd/DrawingObject/Ellipse.php new file mode 100644 index 000000000..94b0c6ff5 --- /dev/null +++ b/niucloud/vendor/kosinix/grafika/src/Grafika/Gd/DrawingObject/Ellipse.php @@ -0,0 +1,42 @@ +pos; + $left = $x + $this->width / 2; + $top = $y + $this->height / 2; + + if( null !== $this->fillColor ){ + list($r, $g, $b, $alpha) = $this->fillColor->getRgba(); + $fillColorResource = imagecolorallocatealpha($image->getCore(), $r, $g, $b, Editor::gdAlpha($alpha)); + imagefilledellipse($image->getCore(), $left, $top, $this->width, $this->height, $fillColorResource); + } + // Create borders. It will be placed on top of the filled ellipse (if present) + if ( 0 < $this->getBorderSize() and null !== $this->borderColor) { // With border > 0 AND borderColor !== null + list($r, $g, $b, $alpha) = $this->borderColor->getRgba(); + $borderColorResource = imagecolorallocatealpha($image->getCore(), $r, $g, $b, Editor::gdAlpha($alpha)); + imageellipse($image->getCore(), $left, $top, $this->width, $this->height, $borderColorResource); + } + + return $image; + } +} \ No newline at end of file diff --git a/niucloud/vendor/kosinix/grafika/src/Grafika/Gd/DrawingObject/Line.php b/niucloud/vendor/kosinix/grafika/src/Grafika/Gd/DrawingObject/Line.php new file mode 100644 index 000000000..5b7c712d1 --- /dev/null +++ b/niucloud/vendor/kosinix/grafika/src/Grafika/Gd/DrawingObject/Line.php @@ -0,0 +1,36 @@ +point1; + list( $x2, $y2 ) = $this->point2; + list( $r, $g, $b ) = $this->color->getRgb(); + $color = imagecolorallocate( $image->getCore(), $r, $g, $b ); + if ( function_exists( 'imageantialias' ) ) { // Not available on some if PHP is not precompiled with it even if GD is enabled + imageantialias( $image->getCore(), true ); + } + imageline( $image->getCore(), $x1, $y1, $x2, $y2, $color ); + + return $image; + } + + +} \ No newline at end of file diff --git a/niucloud/vendor/kosinix/grafika/src/Grafika/Gd/DrawingObject/Polygon.php b/niucloud/vendor/kosinix/grafika/src/Grafika/Gd/DrawingObject/Polygon.php new file mode 100644 index 000000000..eac435db2 --- /dev/null +++ b/niucloud/vendor/kosinix/grafika/src/Grafika/Gd/DrawingObject/Polygon.php @@ -0,0 +1,64 @@ +getCore(), true); + } + + $points = $this->points(); + $count = count($this->points); + + + // Create filled polygon + if( null !== $this->fillColor){ + list($r, $g, $b, $alpha) = $this->getFillColor()->getRgba(); + $fillColorResource = imagecolorallocatealpha( + $image->getCore(), $r, $g, $b, + Editor::gdAlpha($alpha) + ); + imagefilledpolygon($image->getCore(), $points, + $count, + $fillColorResource + ); + } + + // Create polygon borders. It will be placed on top of the filled polygon (if present) + if ( 0 < $this->getBorderSize() and null !== $this->borderColor) { // With border > 0 AND borderColor !== null + list($r, $g, $b, $alpha) = $this->getBorderColor()->getRgba(); + $borderColorResource = imagecolorallocatealpha( + $image->getCore(), $r, $g, $b, + Editor::gdAlpha($alpha) + ); + imagepolygon($image->getCore(), $points, + $count, + $borderColorResource + ); + } + return $image; + } + + protected function points(){ + $points = array(); + foreach($this->points as $point){ + $points[] = $point[0]; + $points[] = $point[1]; + } + if( count($points) < 6 ){ + throw new \Exception('Polygon needs at least 3 points.'); + } + return $points; + } +} \ No newline at end of file diff --git a/niucloud/vendor/kosinix/grafika/src/Grafika/Gd/DrawingObject/QuadraticBezier.php b/niucloud/vendor/kosinix/grafika/src/Grafika/Gd/DrawingObject/QuadraticBezier.php new file mode 100644 index 000000000..97760590c --- /dev/null +++ b/niucloud/vendor/kosinix/grafika/src/Grafika/Gd/DrawingObject/QuadraticBezier.php @@ -0,0 +1,208 @@ +getWidth(); + $height = $image->getHeight(); + $gd = $image->getCore(); + + list($x0, $y0) = $this->point1; + list($x1, $y1) = $this->control; + list($x2, $y2) = $this->point2; + + $this->plot($gd, $x0, $y0, $x1, $y1, $x2, $y2); + + $type = $image->getType(); + $file = $image->getImageFile(); + return new Image($gd, $file, $width, $height, $type); // Create new image with updated core + } + + protected function plot($gd, $x0, $y0, $x1, $y1, $x2, $y2) + { + /* plot any quadratic Bezier curve */ + $x = $x0 - $x1; + $y = $y0 - $y1; + $t = $x0 - 2 * $x1 + $x2; //double + + if ((int)$x * ($x2 - $x1) > 0) { /* horizontal cut at P4? */ + if ((int)$y * ($y2 - $y1) > 0) /* vertical cut at P6 too? */ { + if (abs(($y0 - 2 * $y1 + $y2) / $t * $x) > abs($y)) { /* which first? */ + $x0 = $x2; + $x2 = $x + $x1; + $y0 = $y2; + $y2 = $y + $y1; /* swap points */ + } + } /* now horizontal cut at P4 comes first */ + $t = ($x0 - $x1) / $t; + $r = (1 - $t) * ((1 - $t) * $y0 + 2.0 * $t * $y1) + $t * $t * $y2; /* By(t=P4) */ + $t = ($x0 * $x2 - $x1 * $x1) * $t / ($x0 - $x1); /* gradient dP4/dx=0 */ + $x = floor($t + 0.5); + $y = floor($r + 0.5); + $r = ($y1 - $y0) * ($t - $x0) / ($x1 - $x0) + $y0; /* intersect P3 | P0 P1 */ + $this->plotSegment($gd, $x0, $y0, $x, floor($r + 0.5), $x, $y); + $r = ($y1 - $y2) * ($t - $x2) / ($x1 - $x2) + $y2; /* intersect P4 | P1 P2 */ + $x0 = $x1 = $x; + $y0 = $y; + $y1 = floor($r + 0.5); /* P0 = P4, P1 = P8 */ + } + if ((int)($y0 - $y1) * ($y2 - $y1) > 0) { /* vertical cut at P6? */ + $t = $y0 - 2 * $y1 + $y2; + $t = ($y0 - $y1) / $t; + $r = (1 - $t) * ((1 - $t) * $x0 + 2.0 * $t * $x1) + $t * $t * $x2; /* Bx(t=P6) */ + $t = ($y0 * $y2 - $y1 * $y1) * $t / ($y0 - $y1); /* gradient dP6/dy=0 */ + $x = floor($r + 0.5); + $y = floor($t + 0.5); + $r = ($x1 - $x0) * ($t - $y0) / ($y1 - $y0) + $x0; /* intersect P6 | P0 P1 */ + $this->plotSegment($gd, $x0, $y0, floor($r + 0.5), $y, $x, $y); + $r = ($x1 - $x2) * ($t - $y2) / ($y1 - $y2) + $x2; /* intersect P7 | P1 P2 */ + $x0 = $x; + $x1 = floor($r + 0.5); + $y0 = $y1 = $y; /* P0 = P6, P1 = P7 */ + } + $this->plotSegment($gd, $x0, $y0, $x1, $y1, $x2, $y2); /* remaining part */ + } + + /** + * Draw an limited anti-aliased quadratic Bezier segment. + * @param $gd + * @param $x0 + * @param $y0 + * @param $x1 + * @param $y1 + * @param $x2 + * @param $y2 + */ + protected function plotSegment($gd, $x0, $y0, $x1, $y1, $x2, $y2) + { + $sx = $x2 - $x1; + $sy = $y2 - $y1; + $xx = $x0 - $x1; + $yy = $y0 - $y1; + + $cur = $xx * $sy - $yy * $sx; /* $curvature */ + assert($xx * $sx <= 0 && $yy * $sy <= 0); + if ($sx * (int)$sx + $sy * (int)$sy > $xx * $xx + $yy * $yy) { /* begin with longer part */ + $x2 = $x0; + $x0 = $sx + $x1; + $y2 = $y0; + $y0 = $sy + $y1; + $cur = -$cur; /* swap P0 P2 */ + } + if ($cur != 0) { /* no straight line */ + $xx += $sx; + $xx *= $sx = $x0 < $x2 ? 1 : -1; /* x step direction */ + $yy += $sy; + $yy *= $sy = $y0 < $y2 ? 1 : -1; /* y step direction */ + $xy = 2 * $xx * $yy; + $xx *= $xx; + $yy *= $yy; /* differences 2nd degree */ + if ($cur * $sx * $sy < 0) { /* negat$ed $curvature? */ + $xx = -$xx; + $yy = -$yy; + $xy = -$xy; + $cur = -$cur; + } + $dx = 4.0 * $sy * ($x1 - $x0) * $cur + $xx - $xy; /* differences 1st degree */ + $dy = 4.0 * $sx * ($y0 - $y1) * $cur + $yy - $xy; + $xx += $xx; + $yy += $yy; + $err = $dx + $dy + $xy; /* $error 1st step */ + do { + $cur = min($dx + $xy, -$xy - $dy); + $ed = max($dx + $xy, -$xy - $dy); /* approximate $error distance */ + $ed += 2 * $ed * $cur * $cur / (4 * $ed * $ed + $cur * $cur); + $this->setPixel($gd, $x0, $y0, abs($err - $dx - $dy - $xy) / $ed); /* plot $curve */ + if ($x0 == $x2 || $y0 == $y2) { + break; + } /* $curve finish$ed */ + $x1 = $x0; + $cur = $dx - $err; + $y1 = 2 * $err + $dy < 0; + if (2 * $err + $dx > 0) { /* x step */ + if ($err - $dy < $ed) { + $this->setPixel($gd, $x0, $y0 + $sy, abs($err - $dy) / $ed); + } + $x0 += $sx; + $dx -= $xy; + $err += $dy += $yy; + } + if ($y1) { /* y step */ + if ($cur < $ed) { + $this->setPixel($gd, $x1 + $sx, $y0, abs($cur) / $ed); + } + $y0 += $sy; + $dy -= $xy; + $err += $dx += $xx; + } + } while ($dy < $dx); /* gradient negates -> close curves */ + } + $this->plotLine($gd, $x0, $y0, $x2, $y2); /* plot remaining needle to end */ + } + + protected function plotLine($gd, $x0, $y0, $x1, $y1) + { + $dx = abs($x1 - $x0); + $sx = $x0 < $x1 ? 1 : -1; + $dy = -abs($y1 - $y0); + $sy = $y0 < $y1 ? 1 : -1; + $err = $dx + $dy; + + $ed = $dx - $dy == 0 ? 1 : sqrt((float)$dx * $dx + (float)$dy * $dy); + for (; ;) { /* pixel loop */ + $this->setPixel($gd, $x0, $y0, abs($err - $dx - $dy) / $ed); + $e2 = $err; + $x2 = $x0; + if (2 * $e2 + $dx >= 0) { /* x step */ + if ($x0 == $x1) { + break; + } + if ($e2 - $dy < $ed) { + $this->setPixel($gd, $x0, $y0 + $sy, ($e2 - $dy) / $ed); + } + $err += $dy; + $x0 += $sx; + } + if (2 * $e2 + $dy <= 0) { /* y step */ + if ($y0 == $y1) { + break; + } + if ($dx - $e2 < $ed) { + $this->setPixel($gd, $x2 + $sx, $y0, ($dx - $e2) / $ed); + } + $err += $dx; + $y0 += $sy; + } + } + } + + /** + * @param resource $gd + * @param int $x + * @param int $y + * @param float $ar Alpha ratio + */ + protected function setPixel($gd, $x, $y, $ar) + { + list($r, $g, $b) = $this->color->getRgb(); + $c = imagecolorallocatealpha($gd, $r, $g, $b, 127 * $ar); + imagesetpixel($gd, $x, $y, $c); + } +} \ No newline at end of file diff --git a/niucloud/vendor/kosinix/grafika/src/Grafika/Gd/DrawingObject/Rectangle.php b/niucloud/vendor/kosinix/grafika/src/Grafika/Gd/DrawingObject/Rectangle.php new file mode 100644 index 000000000..cfbfa16c1 --- /dev/null +++ b/niucloud/vendor/kosinix/grafika/src/Grafika/Gd/DrawingObject/Rectangle.php @@ -0,0 +1,36 @@ +pos[0]; + $x2 = $x1 + $this->getWidth(); + $y1 = $this->pos[1]; + $y2 = $y1 + $this->getHeight(); + + if( null !== $this->fillColor ){ + list($r, $g, $b, $alpha) = $this->fillColor->getRgba(); + $fillColorResource = imagecolorallocatealpha($image->getCore(), $r, $g, $b, Editor::gdAlpha($alpha)); + imagefilledrectangle($image->getCore(), $x1, $y1, $x2, $y2, $fillColorResource); + } + // Create borders. It will be placed on top of the filled rectangle (if present) + if ( 0 < $this->getBorderSize() and null !== $this->borderColor) { // With border > 0 AND borderColor !== null + list($r, $g, $b, $alpha) = $this->borderColor->getRgba(); + $borderColorResource = imagecolorallocatealpha($image->getCore(), $r, $g, $b, Editor::gdAlpha($alpha)); + imagerectangle($image->getCore(), $x1, $y1, $x2, $y2, $borderColorResource); + } + + return $image; + } +} \ No newline at end of file diff --git a/niucloud/vendor/kosinix/grafika/src/Grafika/Gd/Editor.php b/niucloud/vendor/kosinix/grafika/src/Grafika/Gd/Editor.php new file mode 100644 index 000000000..1fb29a57f --- /dev/null +++ b/niucloud/vendor/kosinix/grafika/src/Grafika/Gd/Editor.php @@ -0,0 +1,1179 @@ +isAnimated()) { // Ignore animated GIF for now + return $this; + } + + $image = $filter->apply($image); + + return $this; + } + + /** + * Blend two images together with the first image as the base and the second image on top. Supports several blend modes. + * + * @param Image $image1 The base image. + * @param Image $image2 The image placed on top of the base image. + * @param string $type The blend mode. Can be: normal, multiply, overlay or screen. + * @param float $opacity The opacity of $image2. Possible values 0.0 to 1.0 where 0.0 is fully transparent and 1.0 is fully opaque. Defaults to 1.0. + * @param string $position The position of $image2 on $image1. Possible values top-left, top-center, top-right, center-left, center, center-right, bottom-left, bottom-center, bottom-right and smart. Defaults to top-left. + * @param int $offsetX Number of pixels to add to the X position of $image2. + * @param int $offsetY Number of pixels to add to the Y position of $image2. + * + * @return Editor + * @throws \Exception When added image is outside of canvas or invalid blend type + */ + public function blend(&$image1, $image2, $type='normal', $opacity = 1.0, $position = 'top-left', $offsetX = 0, $offsetY = 0 ){ + + // Turn into position object + $position = new Position($position, $offsetX, $offsetY); + + // Position is for $image2. $image1 is canvas. + list($offsetX, $offsetY) = $position->getXY($image1->getWidth(), $image1->getHeight(), $image2->getWidth(), $image2->getHeight()); + + // Check if it overlaps + if( ($offsetX >= $image1->getWidth() ) or + ($offsetX + $image2->getWidth() <= 0) or + ($offsetY >= $image1->getHeight() ) or + ($offsetY + $image2->getHeight() <= 0)){ + + throw new \Exception('Invalid blending. Image 2 is outside the canvas.'); + } + + // Loop start X + $loopStartX = 0; + $canvasStartX = $offsetX; + if($canvasStartX < 0){ + $diff = 0 - $canvasStartX; + $loopStartX += $diff; + } + + // Loop end X + $loopEndX = $image2->getWidth(); + $canvasEndX = $offsetX + $image2->getWidth(); + if($canvasEndX > $image1->getWidth()){ + $diff = $canvasEndX - $image1->getWidth(); + $loopEndX -= $diff; + } + + // Loop start Y + $loopStartY = 0; + $canvasStartY = $offsetY; + if($canvasStartY < 0){ + $diff = 0 - $canvasStartY; + $loopStartY += $diff; + } + + // Loop end Y + $loopEndY = $image2->getHeight(); + $canvasEndY = $offsetY + $image2->getHeight(); + if($canvasEndY > $image1->getHeight()){ + $diff = $canvasEndY - ($image1->getHeight()); + $loopEndY -= $diff; + } + + $w = $image1->getWidth(); + $h = $image1->getHeight(); + $gd1 = $image1->getCore(); + $gd2 = $image2->getCore(); + + $canvas = imagecreatetruecolor( $w, $h ); + imagecopy( $canvas, $gd1, 0, 0, 0, 0, $w, $h ); + + $type = strtolower( $type ); + if($type==='normal') { + if ( $opacity !== 1 ) { + $this->opacity($image2, $opacity); + } + imagecopy( $canvas, $gd2, $loopStartX + $offsetX, $loopStartY + $offsetY, 0, 0, $image2->getWidth(), $image2->getHeight()); + } else if($type==='multiply'){ + $this->_blendMultiply( $canvas, $gd1, $gd2, $loopStartY, $loopEndY, $loopStartX, $loopEndX, $offsetX, $offsetY, $opacity ); + } else if($type==='overlay'){ + $this->_blendOverlay( $canvas, $gd1, $gd2, $loopStartY, $loopEndY, $loopStartX, $loopEndX, $offsetX, $offsetY, $opacity ); + } else if($type==='screen'){ + $this->_blendScreen( $canvas, $gd1, $gd2, $loopStartY, $loopEndY, $loopStartX, $loopEndX, $offsetX, $offsetY, $opacity ); + } else { + throw new \Exception(sprintf('Invalid blend type "%s".', $type)); + } + + imagedestroy( $gd1 ); // Free resource + + $image1 = new Image( + $canvas, + $image1->getImageFile(), + $w, + $h, + $image1->getType() + ); + + return $this; + } + + /** + * Compare two images and returns a hamming distance. A value of 0 indicates a likely similar picture. A value between 1 and 10 is potentially a variation. A value greater than 10 is likely a different image. + * + * @param ImageInterface|string $image1 + * @param ImageInterface|string $image2 + * + * @return int Hamming distance. Note: This breaks the chain if you are doing fluent api calls as it does not return an Editor. + * @throws \Exception + */ + public function compare($image1, $image2) + { + + if (is_string($image1)) { // If string passed, turn it into a Image object + $image1 = Image::createFromFile($image1); + $this->flatten( $image1 ); + } + + if (is_string($image2)) { // If string passed, turn it into a Image object + $image2 = Image::createFromFile($image2); + $this->flatten( $image2 ); + } + + $hash = new DifferenceHash(); + + $bin1 = $hash->hash($image1, $this); + $bin2 = $hash->hash($image2, $this); + $str1 = str_split($bin1); + $str2 = str_split($bin2); + $distance = 0; + foreach ($str1 as $i => $char) { + if ($char !== $str2[$i]) { + $distance++; + } + } + + return $distance; + + } + + /** + * Crop the image to the given dimension and position. + * + * @param Image $image + * @param int $cropWidth Crop width in pixels. + * @param int $cropHeight Crop Height in pixels. + * @param string $position The crop position. Possible values top-left, top-center, top-right, center-left, center, center-right, bottom-left, bottom-center, bottom-right and smart. Defaults to center. + * @param int $offsetX Number of pixels to add to the X position of the crop. + * @param int $offsetY Number of pixels to add to the Y position of the crop. + * + * @return Editor + * @throws \Exception + */ + public function crop( &$image, $cropWidth, $cropHeight, $position = 'center', $offsetX = 0, $offsetY = 0) + { + + if ($image->isAnimated()) { // Ignore animated GIF for now + return $this; + } + + if ( 'smart' === $position ) { // Smart crop + list( $x, $y ) = $this->_smartCrop( $image, $cropWidth, $cropHeight ); + } else { + // Turn into an instance of Position + $position = new Position( $position, $offsetX, $offsetY ); + + // Crop position as x,y coordinates + list( $x, $y ) = $position->getXY( $image->getWidth(), $image->getHeight(), $cropWidth, $cropHeight ); + + } + + // Create blank image + $newImageResource = imagecreatetruecolor($cropWidth, $cropHeight); + + // Now crop + imagecopyresampled( + $newImageResource, // Target image + $image->getCore(), // Source image + 0, // Target x + 0, // Target y + $x, // Src x + $y, // Src y + $cropWidth, // Target width + $cropHeight, // Target height + $cropWidth, // Src width + $cropHeight // Src height + ); + + // Free memory of old resource + imagedestroy($image->getCore()); + + // Cropped image instance + $image = new Image( + $newImageResource, + $image->getImageFile(), + $cropWidth, + $cropHeight, + $image->getType() + ); + + return $this; + } + + /** + * Draw a DrawingObject on the image. See Drawing Objects section. + * + * @param Image $image + * @param DrawingObjectInterface $drawingObject + * + * @return $this + */ + public function draw( &$image, $drawingObject) + { + + if ($image->isAnimated()) { // Ignore animated GIF for now + return $this; + } + + $image = $drawingObject->draw($image); + + return $this; + } + + /** + * Compare if two images are equal. It will compare if the two images are of the same width and height. If the dimensions differ, it will return false. If the dimensions are equal, it will loop through each pixels. If one of the pixel don't match, it will return false. The pixels are compared using their RGB (Red, Green, Blue) values. + * + * @param string|ImageInterface $image1 Can be an instance of Image or string containing the file system path to image. + * @param string|ImageInterface $image2 Can be an instance of Image or string containing the file system path to image. + * + * @return bool True if equals false if not. Note: This breaks the chain if you are doing fluent api calls as it does not return an Editor. + * @throws \Exception + */ + public function equal($image1, $image2) + { + + if (is_string($image1)) { // If string passed, turn it into a Image object + $image1 = Image::createFromFile($image1); + $this->flatten( $image1 ); + } + + if (is_string($image2)) { // If string passed, turn it into a Image object + $image2 = Image::createFromFile($image2); + $this->flatten( $image2 ); + } + + // Check if image dimensions are equal + if ($image1->getWidth() !== $image2->getWidth() or $image1->getHeight() !== $image2->getHeight()) { + + return false; + + } else { + + // Loop using image1 + for ($y = 0; $y < $image1->getHeight(); $y++) { + for ($x = 0; $x < $image1->getWidth(); $x++) { + + // Get image1 pixel + $rgb1 = imagecolorat($image1->getCore(), $x, $y); + $r1 = ($rgb1 >> 16) & 0xFF; + $g1 = ($rgb1 >> 8) & 0xFF; + $b1 = $rgb1 & 0xFF; + + // Get image2 pixel + $rgb2 = imagecolorat($image2->getCore(), $x, $y); + $r2 = ($rgb2 >> 16) & 0xFF; + $g2 = ($rgb2 >> 8) & 0xFF; + $b2 = $rgb2 & 0xFF; + + // Compare pixel value + if ( + $r1 !== $r2 or + $g1 !== $g2 or + $b1 !== $b2 + ) { + return false; + } + } + } + } + + return true; + } + + /** + * Fill entire image with color. + * + * @param Image $image + * @param Color $color Color object + * @param int $x X-coordinate of start point + * @param int $y Y-coordinate of start point + * + * @return Editor + */ + public function fill( &$image, $color, $x = 0, $y = 0) + { + + if ($image->isAnimated()) { // Ignore animated GIF for now + return $this; + } + + list($r, $g, $b, $alpha) = $color->getRgba(); + + $colorResource = imagecolorallocatealpha($image->getCore(), $r, $g, $b, + $this->gdAlpha($alpha)); + imagefill($image->getCore(), $x, $y, $colorResource); + + return $this; + } + + /** + * Flatten if animated GIF. Do nothing otherwise. + * + * @param Image $image + * + * @return Editor + */ + public function flatten(&$image){ + + if($image->isAnimated()) { + $old = $image->getCore(); + $gift = new GifHelper(); + $hex = $gift->encode($image->getBlocks()); + $gd = imagecreatefromstring(pack('H*', $hex)); // Recreate resource from blocks + + imagedestroy( $old ); // Free resource + $image = new Image( + $gd, + $image->getImageFile(), + $image->getWidth(), + $image->getHeight(), + $image->getType(), + '', // blocks + false // animated + ); + } + return $this; + } + + /** + * Flip or mirrors the image. + * + * @param Image $image + * @param string $mode The type of flip: 'h' for horizontal flip or 'v' for vertical. + * + * @return Editor + * @throws \Exception + */ + public function flip(&$image, $mode){ + + $image = $this->_flip($image, $mode); + return $this; + } + + /** + * Free the image clearing resources associated with it. + * + * @param Image $image + * + * @return Editor + */ + public function free( &$image ) + { + imagedestroy($image->getCore()); + return $this; + } + + /** + * Convert alpha value of 0 - 1 to GD compatible alpha value of 0 - 127 where 0 is opaque and 127 is transparent + * + * @param float $alpha Alpha value of 0 - 1. Example: 0, 0.60, 0.9, 1 + * + * @return int + */ + public static function gdAlpha($alpha) + { + + $scale = round(127 * $alpha); + + return $invert = 127 - $scale; + } + + /** + * Checks if the editor is available on the current PHP install. + * + * @return bool True if available false if not. + */ + public function isAvailable() + { + if (false === extension_loaded('gd') || false === function_exists('gd_info')) { + return false; + } + + // On some setups GD library does not provide imagerotate() + if ( ! function_exists('imagerotate')) { + + return false; + } + + return true; + } + + /** + * Sets the image to the specified opacity level where 1.0 is fully opaque and 0.0 is fully transparent. + * Warning: This function loops thru each pixel manually which can be slow. Use sparingly. + * + * @param Image $image + * @param float $opacity + * + * @return Editor + * @throws \Exception + */ + public function opacity( &$image, $opacity ) + { + + if ($image->isAnimated()) { // Ignore animated GIF for now + return $this; + } + + // Bounds checks + $opacity = ($opacity > 1) ? 1 : $opacity; + $opacity = ($opacity < 0) ? 0 : $opacity; + + for ($y = 0; $y < $image->getHeight(); $y++) { + for ($x = 0; $x < $image->getWidth(); $x++) { + $rgb = imagecolorat($image->getCore(), $x, $y); + $alpha = ($rgb >> 24) & 0x7F; // 127 in hex. These are binary operations. + $r = ($rgb >> 16) & 0xFF; + $g = ($rgb >> 8) & 0xFF; + $b = $rgb & 0xFF; + + // Reverse alpha values from 127-0 (transparent to opaque) to 0-127 for easy math + // Previously: 0 = opaque, 127 = transparent. + // Now: 0 = transparent, 127 = opaque + $reverse = 127 - $alpha; + $reverse = round($reverse * $opacity); + + if ($alpha < 127) { // Process non transparent pixels only + imagesetpixel($image->getCore(), $x, $y, + imagecolorallocatealpha($image->getCore(), $r, $g, $b, 127 - $reverse)); + } + } + } + + return $this; + } + + /** + * Open an image file and assign Image to first parameter. + * + * @param Image $image + * @param string $imageFile + * + * @return Editor + */ + public function open(&$image, $imageFile){ + $image = Image::createFromFile( $imageFile ); + return $this; + } + + /** + * Wrapper function for the resizeXXX family of functions. Resize image given width, height and mode. + * + * @param Image $image + * @param int $newWidth Width in pixels. + * @param int $newHeight Height in pixels. + * @param string $mode Resize mode. Possible values: "exact", "exactHeight", "exactWidth", "fill", "fit". + * + * @return Editor + * @throws \Exception + */ + public function resize(&$image, $newWidth, $newHeight, $mode = 'fit') + { + /* + * Resize formula: + * ratio = w / h + * h = w / ratio + * w = h * ratio + */ + switch ($mode) { + case 'exact': + $this->resizeExact($image, $newWidth, $newHeight); + break; + case 'fill': + $this->resizeFill($image, $newWidth, $newHeight); + break; + case 'exactWidth': + $this->resizeExactWidth($image, $newWidth); + break; + case 'exactHeight': + $this->resizeExactHeight($image, $newHeight); + break; + case 'fit': + $this->resizeFit($image, $newWidth, $newHeight); + break; + default: + throw new \Exception(sprintf('Invalid resize mode "%s".', $mode)); + } + + return $this; + } + + /** + * Resize image to exact dimensions ignoring aspect ratio. Useful if you want to force exact width and height. + * + * @param Image $image + * @param int $newWidth Width in pixels. + * @param int $newHeight Height in pixels. + * + * @return Editor + */ + public function resizeExact(&$image, $newWidth, $newHeight) + { + + $this->_resize($image, $newWidth, $newHeight); + + return $this; + } + + /** + * Resize image to exact height. Width is auto calculated. Useful for creating row of images with the same height. + * + * @param Image $image + * @param int $newHeight Height in pixels. + * + * @return Editor + */ + public function resizeExactHeight(&$image, $newHeight) + { + + $width = $image->getWidth(); + $height = $image->getHeight(); + $ratio = $width / $height; + + $resizeHeight = $newHeight; + $resizeWidth = $newHeight * $ratio; + + $this->_resize($image, $resizeWidth, $resizeHeight); + + return $this; + } + + /** + * Resize image to exact width. Height is auto calculated. Useful for creating column of images with the same width. + * + * @param Image $image + * @param int $newWidth Width in pixels. + * + * @return Editor + */ + public function resizeExactWidth(&$image, $newWidth) + { + + $width = $image->getWidth(); + $height = $image->getHeight(); + $ratio = $width / $height; + + $resizeWidth = $newWidth; + $resizeHeight = round($newWidth / $ratio); + + $this->_resize($image, $resizeWidth, $resizeHeight); + + return $this; + } + + /** + * Resize image to fill all the space in the given dimension. Excess parts are cropped. + * + * @param Image $image + * @param int $newWidth Width in pixels. + * @param int $newHeight Height in pixels. + * + * @return Editor + */ + public function resizeFill(&$image, $newWidth, $newHeight) + { + $width = $image->getWidth(); + $height = $image->getHeight(); + $ratio = $width / $height; + + // Base optimum size on new width + $optimumWidth = $newWidth; + $optimumHeight = round($newWidth / $ratio); + + if (($optimumWidth < $newWidth) or ($optimumHeight < $newHeight)) { // Oops, where trying to fill and there are blank areas + // So base optimum size on height instead + $optimumWidth = $newHeight * $ratio; + $optimumHeight = $newHeight; + } + + $this->_resize($image, $optimumWidth, $optimumHeight); + $this->crop($image, $newWidth, $newHeight); // Trim excess parts + + return $this; + } + + /** + * Resize image to fit inside the given dimension. No part of the image is lost. + * + * @param Image $image + * @param int $newWidth Width in pixels. + * @param int $newHeight Height in pixels. + * + * @return Editor + */ + public function resizeFit(&$image, $newWidth, $newHeight) + { + + $width = $image->getWidth(); + $height = $image->getHeight(); + $ratio = $width / $height; + + // Try basing it on width first + $resizeWidth = $newWidth; + $resizeHeight = round($newWidth / $ratio); + + if (($resizeWidth > $newWidth) or ($resizeHeight > $newHeight)) { // Oops, either with or height does not fit + // So base on height instead + $resizeHeight = $newHeight; + $resizeWidth = $newHeight * $ratio; + } + + $this->_resize($image, $resizeWidth, $resizeHeight); + + return $this; + } + + /** + * Rotate an image counter-clockwise. + * + * @param Image $image + * @param int $angle The angle in degrees. + * @param Color|null $color The Color object containing the background color. + * + * @return EditorInterface An instance of image editor. + * @throws \Exception + */ + public function rotate(&$image, $angle, $color = null) + { + + if ($image->isAnimated()) { // Ignore animated GIF for now + return $this; + } + + $color = ($color !== null) ? $color : new Color('#000000'); + list($r, $g, $b, $alpha) = $color->getRgba(); + + $old = $image->getCore(); + $new = imagerotate($old, $angle, imagecolorallocatealpha($old, $r, $g, $b, $alpha)); + + if(false === $new){ + throw new \Exception('Error rotating image.'); + } + + imagedestroy( $old ); // Free resource + $image = new Image( $new, $image->getImageFile(), $image->getWidth(), $image->getHeight(), $image->getType() ); + + return $this; + } + + /** + * Save the image to an image format. + * + * @param Image $image + * @param string $file File path where to save the image. + * @param null|string $type Type of image. Can be null, "GIF", "PNG", or "JPEG". + * @param null|string $quality Quality of image. Applies to JPEG only. Accepts number 0 - 100 where 0 is lowest and 100 is the highest quality. Or null for default. + * @param bool|false $interlace Set to true for progressive JPEG. Applies to JPEG only. + * @param int $permission Default permission when creating non-existing target directory. + * + * @return Editor + * @throws \Exception + */ + public function save($image, $file, $type = null, $quality = null, $interlace = false, $permission = 0755) + { + + if (null === $type) { + + $type = $this->_getImageTypeFromFileName($file); // Null given, guess type from file extension + if (ImageType::UNKNOWN === $type) { + $type = $image->getType(); // 0 result, use original image type + } + } + + $targetDir = dirname($file); // $file's directory + if (false === is_dir($targetDir)) { // Check if $file's directory exist + // Create and set default perms to 0755 + if ( ! mkdir($targetDir, $permission, true)) { + throw new \Exception(sprintf('Cannot create %s', $targetDir)); + } + } + + switch (strtoupper($type)) { + case ImageType::GIF : + if($image->isAnimated()){ + $blocks = $image->getBlocks(); + $gift = new GifHelper(); + $hex = $gift->encode($blocks); + file_put_contents($file, pack('H*', $hex)); + } else { + imagegif($image->getCore(), $file); + } + + break; + + case ImageType::PNG : + // PNG is lossless and does not need compression. Although GD allow values 0-9 (0 = no compression), we leave it alone. + imagepng($image->getCore(), $file); + break; + + default: // Defaults to jpeg + $quality = ($quality === null) ? 75 : $quality; // Default to 75 (GDs default) if null. + $quality = ($quality > 100) ? 100 : $quality; + $quality = ($quality < 0) ? 0 : $quality; + imageinterlace($image->getCore(), $interlace); + imagejpeg($image->getCore(), $file, $quality); + } + + return $this; + } + + /** + * Write text to image. + * + * @param Image $image + * @param string $text The text to be written. + * @param int $size The font size. Defaults to 12. + * @param int $x The distance from the left edge of the image to the left of the text. Defaults to 0. + * @param int $y The distance from the top edge of the image to the top of the text. Defaults to 12 (equal to font size) so that the text is placed within the image. + * @param Color $color The Color object. Default text color is black. + * @param string $font Full path to font file. If blank, will default to Liberation Sans font. + * @param int $angle Angle of text from 0 - 359. Defaults to 0. + * + * @return EditorInterface + * @throws \Exception + */ + public function text(&$image, $text, $size = 12, $x = 0, $y = 0, $color = null, $font = '', $angle = 0) + { + + if ($image->isAnimated()) { // Ignore animated GIF for now + return $this; + } + + $y += $size; + + $color = ($color !== null) ? $color : new Color('#000000'); + $font = ($font !== '') ? $font : Grafika::fontsDir() . DIRECTORY_SEPARATOR . 'LiberationSans-Regular.ttf'; + + list($r, $g, $b, $alpha) = $color->getRgba(); + + $colorResource = imagecolorallocatealpha( + $image->getCore(), + $r, $g, $b, + $this->gdAlpha($alpha) + ); + + imagettftext( + $image->getCore(), + $size, + $angle, + $x, + $y, + $colorResource, + $font, + $text + ); + + return $this; + } + + /** + * @param $canvas + * @param $gd1 + * @param $gd2 + * @param $loopStartY + * @param $loopEndY + * @param $loopStartX + * @param $loopEndX + * @param $offsetX + * @param $offsetY + * + * @param $opacity + * + * @return $this + */ + private function _blendMultiply($canvas, $gd1, $gd2, $loopStartY, $loopEndY, $loopStartX, $loopEndX, $offsetX, $offsetY, $opacity){ + + for ( $y = $loopStartY; $y < $loopEndY; $y++ ) { + for ( $x = $loopStartX; $x < $loopEndX; $x++ ) { + $canvasX = $x + $offsetX; + $canvasY = $y + $offsetY; + $argb1 = imagecolorat( $gd1, $canvasX, $canvasY ); + $r1 = ( $argb1 >> 16 ) & 0xFF; + $g1 = ( $argb1 >> 8 ) & 0xFF; + $b1 = $argb1 & 0xFF; + + $argb2 = imagecolorat( $gd2, $x, $y ); + $a2 = ($argb2 >> 24) & 0x7F; // 127 in hex. These are binary operations. + $r2 = ( $argb2 >> 16 ) & 0xFF; + $g2 = ( $argb2 >> 8 ) & 0xFF; + $b2 = $argb2 & 0xFF; + + $r3 = round($r1 * $r2 / 255); + $g3 = round($g1 * $g2 / 255); + $b3 = round($b1 * $b2 / 255); + + $reverse = 127 - $a2; + $reverse = round($reverse * $opacity); + + $argb3 = imagecolorallocatealpha( $canvas, $r3, $g3, $b3, 127 - $reverse ); + imagesetpixel( $canvas, $canvasX, $canvasY, $argb3 ); + } + } + return $canvas; + } + + /** + * @param $canvas + * @param $gd1 + * @param $gd2 + * @param $loopStartY + * @param $loopEndY + * @param $loopStartX + * @param $loopEndX + * @param $offsetX + * @param $offsetY + * + * @param $opacity + * + * @return $this + */ + private function _blendOverlay($canvas, $gd1, $gd2, $loopStartY, $loopEndY, $loopStartX, $loopEndX, $offsetX, $offsetY, $opacity){ + + for ( $y = $loopStartY; $y < $loopEndY; $y++ ) { + for ( $x = $loopStartX; $x < $loopEndX; $x++ ) { + $canvasX = $x + $offsetX; + $canvasY = $y + $offsetY; + $argb1 = imagecolorat( $gd1, $canvasX, $canvasY ); + $r1 = ( $argb1 >> 16 ) & 0xFF; + $g1 = ( $argb1 >> 8 ) & 0xFF; + $b1 = $argb1 & 0xFF; + + $argb2 = imagecolorat( $gd2, $x, $y ); + $a2 = ($argb2 >> 24) & 0x7F; // 127 in hex. These are binary operations. + $r2 = ( $argb2 >> 16 ) & 0xFF; + $g2 = ( $argb2 >> 8 ) & 0xFF; + $b2 = $argb2 & 0xFF; + + $r1 /= 255; + $r2 /= 255; + if ($r1 < 0.5) { + $r3 = 2 * ($r1 * $r2); + } else { + $r3 = (1 - (2 *(1-$r1)) * (1-$r2)); + } + + $g1 /= 255; + $g2 /= 255; + if ($g1 < 0.5) { + $g3 = 2 * ($g1 * $g2); + } else { + $g3 = (1 - (2 *(1-$g1)) * (1-$g2)); + } + + $b1 /= 255; + $b2 /= 255; + if ($b1 < 0.5) { + $b3 = 2 * ($b1 * $b2); + } else { + $b3 = (1 - (2 *(1-$b1)) * (1-$b2)); + } + + $reverse = 127 - $a2; + $reverse = round($reverse * $opacity); + + $argb3 = imagecolorallocatealpha( $canvas, $r3*255, $g3*255, $b3*255, 127 - $reverse ); + imagesetpixel( $canvas, $canvasX, $canvasY, $argb3 ); + } + } + return $canvas; + } + + /** + * Calculate entropy based on histogram. + * + * @param $hist + * + * @return float|int + */ + private function _entropy($hist){ + $entropy = 0; + $hist_size = array_sum($hist['r']) + array_sum($hist['g']) + array_sum($hist['b']); + foreach($hist['r'] as $p){ + $p = $p / $hist_size; + $entropy += $p * log($p, 2); + } + foreach($hist['g'] as $p){ + $p = $p / $hist_size; + $entropy += $p * log($p, 2); + } + foreach($hist['b'] as $p){ + $p = $p / $hist_size; + $entropy += $p * log($p, 2); + } + return $entropy * -1; + } + + /** + * @param $canvas + * @param $gd1 + * @param $gd2 + * @param $loopStartY + * @param $loopEndY + * @param $loopStartX + * @param $loopEndX + * @param $offsetX + * @param $offsetY + * + * @param $opacity + * + * @return $this + */ + private function _blendScreen($canvas, $gd1, $gd2, $loopStartY, $loopEndY, $loopStartX, $loopEndX, $offsetX, $offsetY, $opacity){ + + for ( $y = $loopStartY; $y < $loopEndY; $y++ ) { + for ( $x = $loopStartX; $x < $loopEndX; $x++ ) { + $canvasX = $x + $offsetX; + $canvasY = $y + $offsetY; + $argb1 = imagecolorat( $gd1, $canvasX, $canvasY ); + $r1 = ( $argb1 >> 16 ) & 0xFF; + $g1 = ( $argb1 >> 8 ) & 0xFF; + $b1 = $argb1 & 0xFF; + + $argb2 = imagecolorat( $gd2, $x, $y ); + $a2 = ($argb2 >> 24) & 0x7F; // 127 in hex. These are binary operations. + $r2 = ( $argb2 >> 16 ) & 0xFF; + $g2 = ( $argb2 >> 8 ) & 0xFF; + $b2 = $argb2 & 0xFF; + + $r3 = 255 - ( ( 255 - $r1 ) * ( 255 - $r2 ) ) / 255; + $g3 = 255 - ( ( 255 - $g1 ) * ( 255 - $g2 ) ) / 255; + $b3 = 255 - ( ( 255 - $b1 ) * ( 255 - $b2 ) ) / 255; + + $reverse = 127 - $a2; + $reverse = round($reverse * $opacity); + + $argb3 = imagecolorallocatealpha( $canvas, $r3, $g3, $b3, 127 - $reverse ); + imagesetpixel( $canvas, $canvasX, $canvasY, $argb3 ); + } + } + return $canvas; + } + + /** + * Flips image. + * @param Image $image + * @param $mode + * + * @return Image + * @throws \Exception + */ + private function _flip($image, $mode) + { + $old = $image->getCore(); + $w = $image->getWidth(); + $h = $image->getHeight(); + if ($mode === 'h') { + $new = imagecreatetruecolor($w, $h); + for ($x = 0; $x < $w; $x++) { + imagecopy($new, $old, $w - $x - 1, 0, $x, 0, 1, $h); + } + imagedestroy($old); // Free resource + return new Image( + $new, + $image->getImageFile(), + $w, + $h, + $image->getType(), + $image->getBlocks(), + $image->isAnimated() + ); + } else if ($mode === 'v') { + $new = imagecreatetruecolor($w, $h); + for ($y = 0; $y < $h; $y++) { + imagecopy($new, $old, 0, $h - $y - 1, 0, $y, $w, 1); + } + imagedestroy($old); // Free resource + return new Image( + $new, + $image->getImageFile(), + $w, + $h, + $image->getType(), + $image->getBlocks(), + $image->isAnimated() + ); + } else { + throw new \Exception(sprintf('Unsupported mode "%s"', $mode)); + } + } + + /** + * Get image type base on file extension. + * + * @param int $imageFile File path to image. + * + * @return ImageType string Type of image. + */ + private function _getImageTypeFromFileName($imageFile) + { + $ext = strtolower((string)pathinfo($imageFile, PATHINFO_EXTENSION)); + + if ('jpg' === $ext or 'jpeg' === $ext) { + return ImageType::JPEG; + } else if ('gif' === $ext) { + return ImageType::GIF; + } else if ('png' === $ext) { + return ImageType::PNG; + } else if ('wbm' === $ext or 'wbmp' === $ext) { + return ImageType::WBMP; + } else { + return ImageType::UNKNOWN; + } + } + + /** + * Resize helper function. + * + * @param Image $image + * @param int $newWidth + * @param int $newHeight + * @param int $targetX + * @param int $targetY + * @param int $srcX + * @param int $srcY + * + */ + private function _resize(&$image, $newWidth, $newHeight, $targetX = 0, $targetY = 0, $srcX = 0, $srcY = 0) + { + +// $this->_imageCheck(); + + if ($image->isAnimated()) { // Animated GIF + $gift = new GifHelper(); + $blocks = $gift->resize($image->getBlocks(), $newWidth, $newHeight); + // Resize image instance + $image = new Image( + $image->getCore(), + $image->getImageFile(), + $newWidth, + $newHeight, + $image->getType(), + $blocks, + true + ); + } else { + + // Create blank image + $newImage = Image::createBlank($newWidth, $newHeight); + + if (ImageType::PNG === $image->getType()) { + // Preserve PNG transparency + $newImage->fullAlphaMode(true); + } + + imagecopyresampled( + $newImage->getCore(), + $image->getCore(), + $targetX, + $targetY, + $srcX, + $srcY, + $newWidth, + $newHeight, + $image->getWidth(), + $image->getHeight() + ); + + // Free memory of old resource + imagedestroy($image->getCore()); + + // Resize image instance + $image = new Image( + $newImage->getCore(), + $image->getImageFile(), + $newWidth, + $newHeight, + $image->getType() + ); + + } + } + + /** + * Crop based on entropy. + * + * @param Image $oldImage + * @param $cropW + * @param $cropH + * + * @return array + */ + private function _smartCrop($oldImage, $cropW, $cropH){ + $image = clone $oldImage; + + $this->resizeFit($image, 30, 30); + + $origW = $oldImage->getWidth(); + $origH = $oldImage->getHeight(); + $resizeW = $image->getWidth(); + $resizeH = $image->getHeight(); + + $smallCropW = round(($resizeW / $origW) * $cropW); + $smallCropH = round(($resizeH / $origH) * $cropH); + + $step = 1; + + for($y = 0; $y < $resizeH-$smallCropH; $y+=$step){ + for($x = 0; $x < $resizeW-$smallCropW; $x+=$step){ + $hist[$x.'-'.$y] = $this->_entropy($image->histogram(array(array($x, $y), array($smallCropW, $smallCropH)))); + } + if($resizeW-$smallCropW <= 0){ + $hist['0-'.$y] = $this->_entropy($image->histogram(array(array(0, 0), array($smallCropW, $smallCropH)))); + } + } + if($resizeH-$smallCropH <= 0){ + $hist['0-0'] = $this->_entropy($image->histogram(array(array(0, 0), array($smallCropW, $smallCropH)))); + } + + asort($hist); + end($hist); + $pos = key($hist); // last key + list($x, $y) = explode('-', $pos); + $x = round($x*($origW / $resizeW)); + $y = round($y*($origH / $resizeH)); + + return array($x,$y); + } +} \ No newline at end of file diff --git a/niucloud/vendor/kosinix/grafika/src/Grafika/Gd/Filter/Blur.php b/niucloud/vendor/kosinix/grafika/src/Grafika/Gd/Filter/Blur.php new file mode 100644 index 000000000..18455fad9 --- /dev/null +++ b/niucloud/vendor/kosinix/grafika/src/Grafika/Gd/Filter/Blur.php @@ -0,0 +1,40 @@ +amount = (int) $amount; + } + + /** + * @param Image $image + * + * @return Image + */ + public function apply($image) + { + for ($i=0; $i < $this->amount; $i++) { + imagefilter($image->getCore(), IMG_FILTER_GAUSSIAN_BLUR); + } + return $image; + } +} \ No newline at end of file diff --git a/niucloud/vendor/kosinix/grafika/src/Grafika/Gd/Filter/Brightness.php b/niucloud/vendor/kosinix/grafika/src/Grafika/Gd/Filter/Brightness.php new file mode 100644 index 000000000..1f3d31a93 --- /dev/null +++ b/niucloud/vendor/kosinix/grafika/src/Grafika/Gd/Filter/Brightness.php @@ -0,0 +1,39 @@ += 0 >= 100 + + /** + * Brightness constructor. + * @param int $amount The amount of brightness to apply. >= -100 and <= -1 to darken. 0 for no change. >= 1 and <= 100 to brighten. + */ + public function __construct($amount) + { + $this->amount = (int) $amount; + } + + /** + * @param Image $image + * + * @return Image + */ + public function apply( $image ) { + imagefilter($image->getCore(), IMG_FILTER_BRIGHTNESS, ($this->amount * 2.55)); + return $image; + } + +} \ No newline at end of file diff --git a/niucloud/vendor/kosinix/grafika/src/Grafika/Gd/Filter/Colorize.php b/niucloud/vendor/kosinix/grafika/src/Grafika/Gd/Filter/Colorize.php new file mode 100644 index 000000000..1f73c838f --- /dev/null +++ b/niucloud/vendor/kosinix/grafika/src/Grafika/Gd/Filter/Colorize.php @@ -0,0 +1,50 @@ += 0 >= 100 + /** + * @var int + */ + protected $green; // -100 >= 0 >= 100 + /** + * @var int + */ + protected $blue; // -100 >= 0 >= 100 + + /** + * Colorize constructor. + * @param int $red The amount of red colors. >= -100 and <= -1 to reduce. 0 for no change. >= 1 and <= 100 to add. + * @param int $green The amount of green colors. >= -100 and <= -1 to reduce. 0 for no change. >= 1 and <= 100 to add. + * @param int $blue The amount of blue colors. >= -100 and <= -1 to reduce. 0 for no change. >= 1 and <= 100 to add. + */ + public function __construct($red, $green, $blue) + { + $this->red = round($red * 2.55); + $this->green = round($green * 2.55); + $this->blue = round($blue * 2.55); + } + + /** + * @param Image $image + * + * @return Image + */ + public function apply( $image ) { + + imagefilter($image->getCore(), IMG_FILTER_COLORIZE, $this->red, $this->green, $this->blue); + return $image; + } + +} \ No newline at end of file diff --git a/niucloud/vendor/kosinix/grafika/src/Grafika/Gd/Filter/Contrast.php b/niucloud/vendor/kosinix/grafika/src/Grafika/Gd/Filter/Contrast.php new file mode 100644 index 000000000..40d6e6f85 --- /dev/null +++ b/niucloud/vendor/kosinix/grafika/src/Grafika/Gd/Filter/Contrast.php @@ -0,0 +1,38 @@ += 0 >= 100 + + /** + * Contrast constructor. + * @param int $amount The amount of contrast to apply. >= -100 and <= -1 to reduce. 0 for no change. >= 1 and <= 100 to increase. + */ + public function __construct($amount) + { + $this->amount = (int) $amount; + } + + /** + * @param Image $image + * + * @return Image + */ + public function apply( $image ) { + + imagefilter($image->getCore(), IMG_FILTER_CONTRAST, ($this->amount * -1)); + return $image; + } + +} \ No newline at end of file diff --git a/niucloud/vendor/kosinix/grafika/src/Grafika/Gd/Filter/Dither.php b/niucloud/vendor/kosinix/grafika/src/Grafika/Gd/Filter/Dither.php new file mode 100644 index 000000000..f9c9b95b9 --- /dev/null +++ b/niucloud/vendor/kosinix/grafika/src/Grafika/Gd/Filter/Dither.php @@ -0,0 +1,190 @@ +type = $type; + } + + + /** + * Apply filter. + * + * @param Image $image + * + * @return Image + * @throws \Exception + */ + public function apply( $image ) { + if ( $this->type === 'ordered' ) { + return $this->ordered( $image ); + } else if ( $this->type === 'diffusion' ) { + return $this->diffusion( $image ); + } + throw new \Exception( sprintf( 'Invalid dither type "%s".', $this->type ) ); + } + + /** + * Dither using error diffusion. + * + * @param Image $image + * + * @return Image + */ + private function diffusion( $image ){ + $pixel = array(); + + // Localize vars + $width = $image->getWidth(); + $height = $image->getHeight(); + $old = $image->getCore(); + + $new = imagecreatetruecolor($width, $height); + + for ( $y = 0; $y < $height; $y+=1 ) { + for ( $x = 0; $x < $width; $x+=1 ) { + + $color = imagecolorat( $old, $x, $y ); + $r = ($color >> 16) & 0xFF; + $g = ($color >> 8) & 0xFF; + $b = $color & 0xFF; + + $gray = round($r * 0.3 + $g * 0.59 + $b * 0.11); + + if(isset($pixel[$x][$y])){ // Add errors to color if there are + $gray += $pixel[$x][$y]; + } + + if ( $gray <= 127 ) { // Determine if black or white. Also has the benefit of clipping excess val due to adding the error + $blackOrWhite = 0; + } else { + $blackOrWhite = 255; + } + + $oldPixel = $gray; + $newPixel = $blackOrWhite; + + // Current pixel + imagesetpixel( $new, $x, $y, + imagecolorallocate( $new, + $newPixel, + $newPixel, + $newPixel + ) + ); + + $qError = $oldPixel - $newPixel; // Quantization error + + // Propagate error on neighbor pixels + if ( $x + 1 < $width ) { + $pixel[$x+1][$y] = (isset($pixel[$x+1][$y]) ? $pixel[$x+1][$y] : 0) + ($qError * (7 / 16)); + } + + if ( $x - 1 > 0 and $y + 1 < $height ) { + $pixel[$x-1][$y+1] = (isset($pixel[$x-1][$y+1]) ? $pixel[$x-1][$y+1] : 0) + ($qError * (3 / 16)); + } + + if ( $y + 1 < $height ) { + $pixel[$x][$y+1] = (isset($pixel[$x][$y+1]) ? $pixel[$x][$y+1] : 0) + ($qError * (5 / 16)); + } + + if ( $x + 1 < $width and $y + 1 < $height ) { + $pixel[$x+1][$y+1] = (isset($pixel[$x+1][$y+1]) ? $pixel[$x+1][$y+1] : 0) + ($qError * (1 / 16)); + } + + } + } + + imagedestroy($old); // Free resource + // Create new image with updated core + return new Image( + $new, + $image->getImageFile(), + $width, + $height, + $image->getType() + ); + } + + /** + * Dither by applying a threshold map. + * + * @param Image $image + * + * @return Image + */ + private function ordered( $image ) { + // Localize vars + $width = $image->getWidth(); + $height = $image->getHeight(); + $old = $image->getCore(); + + $new = imagecreatetruecolor( $width, $height ); + + $thresholdMap = array( + array( 15, 135, 45, 165 ), + array( 195, 75, 225, 105 ), + array( 60, 180, 30, 150 ), + array( 240, 120, 210, 90 ) + ); + + for ( $y = 0; $y < $height; $y += 1 ) { + for ( $x = 0; $x < $width; $x += 1 ) { + + $color = imagecolorat( $old, $x, $y ); + $r = ( $color >> 16 ) & 0xFF; + $g = ( $color >> 8 ) & 0xFF; + $b = $color & 0xFF; + + $gray = round( $r * 0.3 + $g * 0.59 + $b * 0.11 ); + + $threshold = $thresholdMap[ $x % 4 ][ $y % 4 ]; + $oldPixel = ( $gray + $threshold ) / 2; + if ( $oldPixel <= 127 ) { // Determine if black or white. Also has the benefit of clipping excess value + $newPixel = 0; + } else { + $newPixel = 255; + } + + // Current pixel + imagesetpixel( $new, $x, $y, + imagecolorallocate( $new, + $newPixel, + $newPixel, + $newPixel + ) + ); + + } + } + + imagedestroy( $old ); // Free resource + // Create new image with updated core + return new Image( + $new, + $image->getImageFile(), + $width, + $height, + $image->getType() + ); + } +} \ No newline at end of file diff --git a/niucloud/vendor/kosinix/grafika/src/Grafika/Gd/Filter/Gamma.php b/niucloud/vendor/kosinix/grafika/src/Grafika/Gd/Filter/Gamma.php new file mode 100644 index 000000000..6b7432ebc --- /dev/null +++ b/niucloud/vendor/kosinix/grafika/src/Grafika/Gd/Filter/Gamma.php @@ -0,0 +1,38 @@ += 1.0 + + /** + * Gamma constructor. + * @param float $amount The amount of gamma correction to apply. >= 1.0 + */ + public function __construct($amount) + { + $this->amount = (float) $amount; + } + + /** + * @param Image $image + * + * @return Image + */ + public function apply( $image ) { + + imagegammacorrect($image->getCore(), 1, $this->amount); + return $image; + } + +} \ No newline at end of file diff --git a/niucloud/vendor/kosinix/grafika/src/Grafika/Gd/Filter/Grayscale.php b/niucloud/vendor/kosinix/grafika/src/Grafika/Gd/Filter/Grayscale.php new file mode 100644 index 000000000..ffc0a8824 --- /dev/null +++ b/niucloud/vendor/kosinix/grafika/src/Grafika/Gd/Filter/Grayscale.php @@ -0,0 +1,23 @@ +getCore(), IMG_FILTER_GRAYSCALE); + return $image; + } + +} \ No newline at end of file diff --git a/niucloud/vendor/kosinix/grafika/src/Grafika/Gd/Filter/Invert.php b/niucloud/vendor/kosinix/grafika/src/Grafika/Gd/Filter/Invert.php new file mode 100644 index 000000000..ce79060e0 --- /dev/null +++ b/niucloud/vendor/kosinix/grafika/src/Grafika/Gd/Filter/Invert.php @@ -0,0 +1,24 @@ +getCore(), IMG_FILTER_NEGATE); + return $image; + } + +} \ No newline at end of file diff --git a/niucloud/vendor/kosinix/grafika/src/Grafika/Gd/Filter/Pixelate.php b/niucloud/vendor/kosinix/grafika/src/Grafika/Gd/Filter/Pixelate.php new file mode 100644 index 000000000..071252f18 --- /dev/null +++ b/niucloud/vendor/kosinix/grafika/src/Grafika/Gd/Filter/Pixelate.php @@ -0,0 +1,38 @@ += 1 + */ + protected $amount; + + /** + * Pixelate constructor. + * @param int $amount The size of pixelation. >= 1 + */ + public function __construct($amount) + { + $this->amount = (int) $amount; + } + + /** + * @param Image $image + * + * @return Image + */ + public function apply( $image ) { + + imagefilter($image->getCore(), IMG_FILTER_PIXELATE, $this->amount, true); + return $image; + } + +} \ No newline at end of file diff --git a/niucloud/vendor/kosinix/grafika/src/Grafika/Gd/Filter/Sharpen.php b/niucloud/vendor/kosinix/grafika/src/Grafika/Gd/Filter/Sharpen.php new file mode 100644 index 000000000..ae60cc564 --- /dev/null +++ b/niucloud/vendor/kosinix/grafika/src/Grafika/Gd/Filter/Sharpen.php @@ -0,0 +1,49 @@ += 1 to <= 100 + */ + public function __construct($amount) + { + $this->amount = (int) $amount; + } + + /** + * @param Image $image + * + * @return Image + */ + public function apply( $image ) { + $amount = $this->amount; + // build matrix + $min = $amount >= 10 ? $amount * -0.01 : 0; + $max = $amount * -0.025; + $abs = ((4 * $min + 4 * $max) * -1) + 1; + $div = 1; + $matrix = array( + array($min, $max, $min), + array($max, $abs, $max), + array($min, $max, $min) + ); + // apply the matrix + imageconvolution($image->getCore(), $matrix, $div, 0); + return $image; + } + +} \ No newline at end of file diff --git a/niucloud/vendor/kosinix/grafika/src/Grafika/Gd/Filter/Sobel.php b/niucloud/vendor/kosinix/grafika/src/Grafika/Gd/Filter/Sobel.php new file mode 100644 index 000000000..990b829df --- /dev/null +++ b/niucloud/vendor/kosinix/grafika/src/Grafika/Gd/Filter/Sobel.php @@ -0,0 +1,128 @@ +getWidth(); + $height = $image->getHeight(); + $old = $image->getCore(); + + $pixels = array(); + $new = imagecreatetruecolor($width, $height); + for ($y = 0; $y < $height; $y++) { + for ($x = 0; $x < $width; $x++) { + // row 0 + if ($x > 0 and $y > 0) { + $matrix[0][0] = $this->getColor($old, $pixels,$x - 1, $y - 1); + } else { + $matrix[0][0] = $this->getColor($old, $pixels, $x, $y); + } + + if ($y > 0) { + $matrix[1][0] = $this->getColor($old, $pixels, $x, $y - 1); + } else { + $matrix[1][0] = $this->getColor($old, $pixels, $x, $y); + } + + if ($x + 1 < $width and $y > 0) { + $matrix[2][0] = $this->getColor($old, $pixels, $x + 1, $y - 1); + } else { + $matrix[2][0] = $this->getColor($old, $pixels, $x, $y); + } + + // row 1 + if ($x > 0) { + $matrix[0][1] = $this->getColor($old, $pixels, $x - 1, $y); + } else { + $matrix[0][1] = $this->getColor($old, $pixels, $x, $y); + } + + if ($x + 1 < $width) { + $matrix[2][1] = $this->getColor($old, $pixels, $x + 1, $y); + } else { + $matrix[2][1] = $this->getColor($old, $pixels, $x, $y); + } + + // row 1 + if ($x > 0 and $y + 1 < $height) { + $matrix[0][2] = $this->getColor($old, $pixels, $x - 1, $y + 1); + } else { + $matrix[0][2] = $this->getColor($old, $pixels, $x, $y); + } + + if ($y + 1 < $height) { + $matrix[1][2] = $this->getColor($old, $pixels, $x, $y + 1); + } else { + $matrix[1][2] = $this->getColor($old, $pixels, $x, $y); + } + + if ($x + 1 < $width and $y + 1 < $height) { + $matrix[2][2] = $this->getColor($old, $pixels, $x + 1, $y + 1); + } else { + $matrix[2][2] = $this->getColor($old, $pixels, $x, $y); + } + + $edge = $this->convolve($matrix); + $edge = intval($edge / 2); + if ($edge > 255) { + $edge = 255; + } + $color = imagecolorallocate($new, $edge, $edge, $edge); + imagesetpixel($new, $x, $y, $color); + + } + } + imagedestroy($old); // Free resource + // Create and return new image with updated core + return new Image( + $new, + $image->getImageFile(), + $width, + $height, + $image->getType() + ); + } + + private function convolve($matrix) + { + $gx = $matrix[0][0] + ($matrix[2][0] * -1) + + ($matrix[0][1] * 2) + ($matrix[2][1] * -2) + + $matrix[0][2] + ($matrix[2][2] * -1); + + $gy = $matrix[0][0] + ($matrix[1][0] * 2) + $matrix[2][0] + + ($matrix[0][2] * -1) + ($matrix[1][2] * -2) + ($matrix[2][2] * -1); + + return sqrt(($gx * $gx) + ($gy * $gy)); + } + + private function getColor($gd, &$pixels, $x, $y) + { + if (isset($pixels[$x][$y])) { + return $pixels[$x][$y]; + } + $color = imagecolorat($gd, $x, $y); + $r = ($color >> 16) & 0xFF; + $g = ($color >> 8) & 0xFF; + $b = $color & 0xFF; + + return $pixels[$x][$y] = round($r * 0.3 + $g * 0.59 + $b * 0.11); // gray + } +} \ No newline at end of file diff --git a/niucloud/vendor/kosinix/grafika/src/Grafika/Gd/Helper/GifByteStream.php b/niucloud/vendor/kosinix/grafika/src/Grafika/Gd/Helper/GifByteStream.php new file mode 100644 index 000000000..31bfe4c7d --- /dev/null +++ b/niucloud/vendor/kosinix/grafika/src/Grafika/Gd/Helper/GifByteStream.php @@ -0,0 +1,123 @@ +position = 0; + $this->bytes = $bytes; + } + + /** + * Take a bite from the byte stream. + * + * @param int $size Byte size in integer. + * + * @return string + */ + public function bite($size) + { + $str = substr($this->bytes, $this->position * 2, $size * 2); + $this->position += $size; + + return $str; + } + + /** + * @param $byteString + * @param $offset + * + * @return bool|float + */ + public function find($byteString, $offset) + { + $pos = strpos($this->bytes, $byteString, $offset * 2); + if ($pos !== false) { + return $pos / 2; + } + + return false; + } + + /** + * @param int $step + */ + public function back($step = 1) + { + $this->position -= $step; + } + + /** + * @param int $step + */ + public function next($step = 1) + { + $this->position += $step; + } + + /** + * @return float + */ + public function length() + { + return strlen($this->bytes) / 2; + } + + /** + * @param $position + */ + public function setPosition($position) + { + $this->position = $position; + } + + /** + * @return int + */ + public function getPosition() + { + return $this->position; + } + + /** + * @return mixed + */ + public function getBytes() + { + return $this->bytes; + } + + /** + * @return bool + */ + public function isEnd() + { + if ($this->position > $this->length() - 1) { + return true; + } + + return false; + } +} \ No newline at end of file diff --git a/niucloud/vendor/kosinix/grafika/src/Grafika/Gd/Helper/GifHelper.php b/niucloud/vendor/kosinix/grafika/src/Grafika/Gd/Helper/GifHelper.php new file mode 100644 index 000000000..d491e00d8 --- /dev/null +++ b/niucloud/vendor/kosinix/grafika/src/Grafika/Gd/Helper/GifHelper.php @@ -0,0 +1,605 @@ +setPosition(13); + $lastPos = $bytes->getPosition(); + $gceCount = 0; + while (($lastPos = $bytes->find('21f904', $lastPos))!== false) { + $gceCount++; + if($gceCount>1){ + return true; + } + } + return false; + } + + /** + * Encode data into GIF hex string. + * + * @param array $data The array returned by decode. + * + * @return string Hex string of GIF + */ + public function encode($data){ + $hex = ''; + // header block + $hex .= $this->_fixSize($this->_asciiToHex($data['signature']),3); + $hex .= $this->_fixSize($this->_asciiToHex($data['version']),3); + + // logical screen descriptor block + $hex .= $this->_switchEndian($this->_fixSize(dechex($data['canvasWidth']), 4)); + $hex .= $this->_switchEndian($this->_fixSize(dechex($data['canvasHeight']), 4)); + $packedField = decbin($data['globalColorTableFlag']); + $packedField .= $this->_fixSize(decbin($data['colorResolution']), 3); + $packedField .= decbin($data['sortFlag']); + $packedField .= $this->_fixSize(decbin($data['sizeOfGlobalColorTable']), 3); + $hex .= $this->_fixSize(dechex(bindec($packedField)), 2); + $hex .= $this->_fixSize(dechex($data['backgroundColorIndex']), 2); + $hex .= $this->_fixSize(dechex($data['pixelAspectRatio']), 2); + + // global color table optional + if($data['globalColorTableFlag']>0) { + $hex .= $data['globalColorTable']; + } + // app ext optional + if(isset($data['applicationExtension'])){ + foreach($data['applicationExtension'] as $app){ + $hex .= '21ff0b'; + $hex .= $this->_fixSize($this->_asciiToHex($app['appId']),8); + $hex .= $this->_fixSize($this->_asciiToHex($app['appCode']),3); + foreach($app['subBlocks'] as $subBlock){ + $len = $this->_fixSize(dechex(strlen($subBlock)/2),2); + $hex .= $len.$subBlock; + } + $hex .= '00'; + } + } + + foreach($data['frames'] as $i=>$frame){ + + // graphics control optional + if(isset($frame['delayTime'])) { + $hex .= '21f904'; + $packedField = '000'; // reserved + $packedField .= $this->_fixSize(decbin($frame['disposalMethod']), 3); + $packedField .= decbin($frame['userInputFlag']); + $packedField .= decbin($frame['transparentColorFlag']); + $hex .= $this->_fixSize(dechex(bindec($packedField)), 2); + $hex .= $this->_switchEndian($this->_fixSize(dechex($frame['delayTime']), 4)); + $hex .= $this->_switchEndian($this->_fixSize(dechex($frame['transparentColorIndex']), 2)); + $hex .= '00'; + } + + //image desc + $hex .= '2c'; + $hex .= $this->_switchEndian($this->_fixSize(dechex($frame['imageLeft']), 4)); + $hex .= $this->_switchEndian($this->_fixSize(dechex($frame['imageTop']), 4)); + $hex .= $this->_switchEndian($this->_fixSize(dechex($frame['imageWidth']), 4)); + $hex .= $this->_switchEndian($this->_fixSize(dechex($frame['imageHeight']), 4)); + $packedField = decbin($frame['localColorTableFlag']); + $packedField .= decbin($frame['interlaceFlag']); + $packedField .= decbin($frame['sortFlag']); + $packedField .= '00'; // reserved + $packedField .= $this->_fixSize(decbin($frame['sizeOfLocalColorTable']), 3); + $hex .= $this->_fixSize(dechex(bindec($packedField)), 2); + + // local color table optional + if($frame['localColorTableFlag']>0){ + $hex .= $frame['localColorTable']; + } + + $hex .= $frame['imageData']; + } + $hex .= $data['trailer']; + return $hex; + } + + /** + * Decode GIF into array of data for easy use in PHP userland. + * + * @param GifByteStream $bytes Decode byte stream into array of GIF blocks. + * + * @return array Array containing GIF data + * @throws \Exception + * + */ + public function decode($bytes){ + $bytes->setPosition(0); + $blocks = $this->decodeToBlocks($bytes); + + return $this->expandBlocks($blocks); + } + + /** + * Decompose GIF into its block components. The GIF blocks are in the order that they appear in the byte stream. + * + * @param GifByteStream $bytes + * + * @return array + * @throws \Exception + */ + public function decodeToBlocks($bytes){ + $bytes->setPosition(0); + $blocks = array(); + + // Header block + $blocks['header'] = $bytes->bite(6); + + // Logical screen descriptor block + $part = $bytes->bite(2); // canvass w + $hex = $part; + $part = $bytes->bite(2); // canvass h + $hex .= $part; + $part = $bytes->bite(1); // packed field + $hex .= $part; + $bin = $this->_fixSize($this->_hexToBin($part),8); + $globalColorTableFlag = bindec(substr($bin, 0 ,1)); + $sizeOfGlobalColorTable = bindec(substr($bin, 5 ,3)); + + $part = $bytes->bite(1); // backgroundColorIndex + $hex .= $part; + $part = $bytes->bite(1); // pixelAspectRatio + $hex .= $part; + $blocks['logicalScreenDescriptor'] = $hex; + + // Global color table is optional so check its existence + if($globalColorTableFlag > 0){ + // Formula: 3 * (2^(N+1)) + $colorTableLength = 3*(pow(2,($sizeOfGlobalColorTable+1))); + $part = $bytes->bite($colorTableLength); + $blocks['globalColorTable'] = $part; + } + + + $commentC = $plainTextC = $appCount = $gce = $dc = 0; // index count + while(!$bytes->isEnd()){ + $part = $bytes->bite(1); + + if('21'===$part){ // block tests + $hex = $part; + $part = $bytes->bite(1); + if('ff'===$part) { // App extension block + $hex .= $part; + $part = $bytes->bite(1); // app name length should be 0x0b or int 11 but we check anyways + $size = hexdec($part); // turn it to int + $hex .= $part; + $part = $bytes->bite($size); // app name + $hex .= $part; + while (!$bytes->isEnd()) { // loop thru all app sub blocks + $nextSize = $bytes->bite(1); + if($nextSize !== '00'){ + $hex .= $nextSize; + $size = hexdec($nextSize); + $part = $bytes->bite($size); + $hex .= $part; + } else { + $hex .= $nextSize; + $blocks['applicationExtension-'.$appCount] = $hex; + break; + } + + } + + $appCount++; + } else if('f9'===$part){ // graphic + $hex .= $part; + $part = $bytes->bite(1); // size + $hex .= $part; + $part = $bytes->bite(1); // packed field + $hex .= $part; + $part = $bytes->bite(2); // delay time + $hex .= $part; + $part = $bytes->bite(1); // trans color index + $hex .= $part; + $part = $bytes->bite(1); // terminator + $hex .= $part; + $blocks['graphicControlExtension-'.$gce] = $hex; + $gce++; + } else if('01' === $part){ // plain text ext + $hex .= $part; + + while (!$bytes->isEnd()) { // loop thru all app sub blocks + $nextSize = $bytes->bite(1); + if($nextSize !== '00'){ + $hex .= $nextSize; + $size = hexdec($nextSize); + $part = $bytes->bite($size); + $hex .= $part; + } else { + $hex .= $nextSize; + $blocks['plainTextExtension-'.$plainTextC] = $hex; + break; + } + + } + $plainTextC++; + } else if('fe' === $part){ // comment ext + $hex .= $part; + + while (!$bytes->isEnd()) { // loop thru all app sub blocks + $nextSize = $bytes->bite(1); + if($nextSize !== '00'){ + $hex .= $nextSize; + $size = hexdec($nextSize); + $part = $bytes->bite($size); + $hex .= $part; + } else { + $hex .= $nextSize; + $blocks['commentExtension-'.$commentC] = $hex; + break; + } + + } + $commentC++; + } + } else if ('2c'===$part){ // image descriptors + $hex = $part; + $part = $bytes->bite(2); // imageLeft + $hex .= $part; + $part = $bytes->bite(2); // imageTop + $hex .= $part; + $part = $bytes->bite(2); // imageWidth + $hex .= $part; + $part = $bytes->bite(2); // imageHeight + $hex .= $part; + $part = $bytes->bite(1); // packed field + $hex .= $part; + $blocks['imageDescriptor-'.$dc] = $hex; + $bin = $this->_fixSize($this->_hexToBin($part), 8); + $localColorTableFlag = bindec(substr($bin, 0, 1)); + $sizeOfLocalColorTable = bindec(substr($bin, 5, 3)); + + //LC + if($localColorTableFlag){ + // Formula: 3 * (2^(N+1)) + $localColorTableLen = 3 * (pow(2, ($sizeOfLocalColorTable + 1))); + $part = $bytes->bite($localColorTableLen); + $blocks['localColorTable-'.$dc] = $part; + } + + // Image data + $part = $bytes->bite(1); // LZW code + $hex = $part; + while ($bytes->isEnd()===false) { + $nextSize = $bytes->bite(1); + $hex .= $nextSize; + if($nextSize !== '00') { + $subBlockLen = hexdec($nextSize); + $subBlock = $bytes->bite($subBlockLen); + $hex .= $subBlock; + } else { + $blocks['imageData-'.$dc] = $hex; + break; + } + + } + + $dc++; + + } else { + $blocks['trailer'] = $part; + break; + } + } + if($blocks['trailer']!=='3b'){ + throw new \Exception('Error decoding GIF. Stopped at '.$bytes->getPosition().'. Length is '.$bytes->length().'.'); + } + + return $blocks; + } + + /** + * Expand GIF blocks into useful info. + * + * @param array $blocks Accepts the array returned by decodeToBlocks + * + * @return array + */ + public function expandBlocks($blocks){ + + $decoded = array(); + foreach($blocks as $blockName=>$block){ + $bytes = new GifByteStream($block); + if(false !== strpos($blockName, 'header')){ + $part = $bytes->bite(3); + $decoded['signature'] = $this->_hexToAscii($part); + $part = $bytes->bite(3); + $decoded['version'] = $this->_hexToAscii($part); + } else if(false !== strpos($blockName, 'logicalScreenDescriptor')){ + $part = $bytes->bite(2); + $decoded['canvasWidth'] = hexdec($this->_switchEndian($part)); + $part = $bytes->bite(2); + $decoded['canvasHeight'] = hexdec($this->_switchEndian($part)); + $part = $bytes->bite(1); + $bin = $this->_fixSize($this->_hexToBin($part), 8); // Make sure len is correct + $decoded['globalColorTableFlag'] = bindec(substr($bin, 0 ,1)); + $decoded['colorResolution'] = bindec(substr($bin, 1 ,3)); + $decoded['sortFlag'] = bindec(substr($bin, 4 ,1)); + $decoded['sizeOfGlobalColorTable'] = bindec(substr($bin, 5 ,3)); + $part = $bytes->bite(1); + $decoded['backgroundColorIndex'] = hexdec($part); + $part = $bytes->bite(1); + $decoded['pixelAspectRatio'] = hexdec($part); + + } else if(false !== strpos($blockName, 'globalColorTable')){ + $decoded['globalColorTable'] = $block; + } else if(false !== strpos($blockName, 'applicationExtension')){ + $index = explode('-', $blockName, 2); + $index = $index[1]; + + $bytes->next(2); // Skip ext intro and label: 21 ff + $appNameSize = $bytes->bite(1); // 0x0b or 11 according to spec but we check anyways + $appNameSize = hexdec($appNameSize); + $appName = $this->_hexToAscii($bytes->bite($appNameSize)); + $subBlocks = array(); + while (!$bytes->isEnd()) { // loop thru all app sub blocks + $nextSize = $bytes->bite(1); + if($nextSize !== '00'){ + $size = hexdec($nextSize); + $subBlocks[] = $bytes->bite($size); + + } + } + if($appName==='NETSCAPE2.0'){ + $decoded['applicationExtension'][$index]['appId'] = 'NETSCAPE'; + $decoded['applicationExtension'][$index]['appCode'] = '2.0'; + $decoded['applicationExtension'][$index]['subBlocks'] = $subBlocks; + $decoded['loopCount'] = hexdec($this->_switchEndian(substr($subBlocks[0], 2, 4))); + } else { + $decoded['applicationExtension'][$index]['appId'] = substr($appName, 0, 8); + $decoded['applicationExtension'][$index]['appCode'] = substr($appName, 8, 3); + $decoded['applicationExtension'][$index]['subBlocks'] = $subBlocks; + } + } else if(false !== strpos($blockName, 'graphicControlExtension')) { + $index = explode('-', $blockName, 2); + $index = $index[1]; + + $bytes->next(3); // Skip ext intro, label, and block size which is always 4: 21 f9 04 + $part = $bytes->bite(1); // packed field + $bin = $this->_fixSize($this->_hexToBin($part), 8); // Make sure len is correct + $decoded['frames'][$index]['disposalMethod'] = bindec(substr($bin, 3 ,3)); + $decoded['frames'][$index]['userInputFlag'] = bindec(substr($bin, 6 ,1)); + $decoded['frames'][$index]['transparentColorFlag'] = bindec(substr($bin, 7 ,1)); + $part = $bytes->bite(2); + $decoded['frames'][$index]['delayTime'] = hexdec($this->_switchEndian($part)); + $part = $bytes->bite(1); + $decoded['frames'][$index]['transparentColorIndex'] = hexdec($part); + } else if(false !== strpos($blockName, 'imageDescriptor')) { + $index = explode('-', $blockName, 2); + $index = $index[1]; + + $bytes->next(1); // skip separator: 2c + $part = $bytes->bite(2); + $decoded['frames'][$index]['imageLeft'] = hexdec($this->_switchEndian($part)); + $part = $bytes->bite(2); + $decoded['frames'][$index]['imageTop'] = hexdec($this->_switchEndian($part)); + $part = $bytes->bite(2); + $decoded['frames'][$index]['imageWidth'] = hexdec($this->_switchEndian($part)); + $part = $bytes->bite(2); + $decoded['frames'][$index]['imageHeight'] = hexdec($this->_switchEndian($part)); + $part = $bytes->bite(1); // packed field + $bin = $this->_fixSize($this->_hexToBin($part), + 8); + $decoded['frames'][$index]['localColorTableFlag'] = bindec(substr($bin, 0, 1)); + $decoded['frames'][$index]['interlaceFlag'] = bindec(substr($bin, 1, 1)); + $decoded['frames'][$index]['sortFlag'] = bindec(substr($bin, 2, 1)); + $decoded['frames'][$index]['sizeOfLocalColorTable'] = bindec(substr($bin, 5, 3)); + } else if(false !== strpos($blockName, 'localColorTable')){ + $index = explode('-', $blockName, 2); + $index = $index[1]; + $decoded['frames'][$index]['localColorTable'] = $block; + } else if(false !== strpos($blockName, 'imageData')) { + $index = explode('-', $blockName, 2); + $index = $index[1]; + + $decoded['frames'][$index]['imageData'] = $block; + } else if($blockName === 'trailer') { + $decoded['trailer'] = $block; + } + unset($bytes); + } + + return $decoded; + } + + /** + * @param array $blocks The array returned by decode. + * + * @return array Array of images each containing 1 of each frames of the original image. + */ + public function splitFrames($blocks){ + $images = array(); + if (isset($blocks['frames'])){ + foreach($blocks['frames'] as $a=>$unused){ + $images[$a] = $blocks; + unset($images[$a]['frames']); // remove all frames. + foreach($blocks['frames'] as $b=>$frame){ + if($a===$b){ + $images[$a]['frames'][0] = $frame; // Re-add frames but use only 1 frame and discard others + break; + } + } + } + } + return $images; + } + + /** + * @param $blocks + * @param $newW + * @param $newH + * + * @return array $blocks + */ + public function resize($blocks, $newW, $newH){ + $images = $this->splitFrames($blocks); + + // Loop on individual images and resize them using Gd + $firstFrameGd = null; + foreach($images as $imageIndex=>$image){ + $hex = $this->encode($image); + $binaryRaw = pack('H*', $hex); + + // Utilize gd for resizing + $old = imagecreatefromstring($binaryRaw); + $width = imagesx($old); + $height = imagesy($old); + $new = imagecreatetruecolor($newW, $newH); // Create a blank image + if($firstFrameGd){ + $new = $firstFrameGd; + } + // Account for frame imageLeft and imageTop + $cX = $newW / $blocks['canvasWidth']; // change x + $dX = $image['frames'][0]['imageLeft']; + $cY = $newH / $blocks['canvasHeight']; + $dY = $image['frames'][0]['imageTop']; + + imagecopyresampled( + $new, + $old, + $dX * $cX,// dx + $dY * $cY, // dy + 0, + 0, + $image['frames'][0]['imageWidth'] * $cX, + $image['frames'][0]['imageHeight'] * $cY, + $width, + $height + ); + ob_start(); + imagegif($new); + $binaryRaw = ob_get_contents(); + ob_end_clean(); + + if($firstFrameGd===null){ + $firstFrameGd = $new; + } + + // Hex of resized + $bytes = $this->load($binaryRaw); + $hexNew = $this->decode($bytes); + + + + // Update original frames with hex from resized frames + $blocks['frames'][$imageIndex]['imageWidth'] = $hexNew['frames'][0]['imageWidth']; + $blocks['frames'][$imageIndex]['imageHeight'] = $hexNew['frames'][0]['imageHeight']; + $blocks['frames'][$imageIndex]['imageLeft'] = $hexNew['frames'][0]['imageLeft']; + $blocks['frames'][$imageIndex]['imageTop'] = $hexNew['frames'][0]['imageTop']; + $blocks['frames'][$imageIndex]['imageData'] = $hexNew['frames'][0]['imageData']; + + // We use local color tables on each frame. This will result in faster processing since we dont have to process the global color table at the cost of a larger file size. + $blocks['frames'][$imageIndex]['localColorTableFlag'] = $hexNew['globalColorTableFlag']; + $blocks['frames'][$imageIndex]['localColorTable'] = $hexNew['globalColorTable']; + $blocks['frames'][$imageIndex]['sizeOfLocalColorTable'] = $hexNew['sizeOfGlobalColorTable']; + $blocks['frames'][$imageIndex]['transparentColorFlag'] = 0; + } + // Update dimensions or else imagecreatefromgif will choke. + $blocks['canvasWidth'] = $newW; + $blocks['canvasHeight'] = $newH; + // Disable flickering bug. Also we are using localColorTable anyways. + $blocks['globalColorTableFlag'] = 0; + $blocks['globalColorTable'] = ''; + return $blocks; + } + + /** + * @param $asciiString + * + * @return string + */ + private function _asciiToHex($asciiString){ + $chars = str_split($asciiString, 1); + $string = ''; + foreach($chars as $char){ + $string .= dechex(ord($char)); + } + return $string; + } + + /** + * @param $hexString + * + * @return string + */ + private function _hexToAscii($hexString){ + $bytes = str_split($hexString, 2); + $string = ''; + foreach($bytes as $byte){ + $string .= chr(hexdec($byte)); // convert hex to dec to ascii character. See http://www.ascii.cl/ + } + return $string; + } + + /** + * @param $hexString + * + * @return string + */ + private function _hexToBin($hexString){ + return base_convert($hexString, 16, 2); + } + + /** + * @param $string + * @param $size + * @param string $char + * + * @return string + */ + private function _fixSize($string, $size, $char='0'){ + return str_pad($string, $size, $char, STR_PAD_LEFT); + } + + /** + * @param $hexString + * + * @return string + */ + private function _switchEndian($hexString) { + return implode('', array_reverse(str_split($hexString, 2))); + } +} \ No newline at end of file diff --git a/niucloud/vendor/kosinix/grafika/src/Grafika/Gd/Image.php b/niucloud/vendor/kosinix/grafika/src/Grafika/Gd/Image.php new file mode 100644 index 000000000..1b48c0d90 --- /dev/null +++ b/niucloud/vendor/kosinix/grafika/src/Grafika/Gd/Image.php @@ -0,0 +1,457 @@ +gd = $gd; + $this->imageFile = $imageFile; + $this->width = $width; + $this->height = $height; + $this->type = $type; + $this->blocks = $blocks; + $this->animated = $animated; + } + + /** + * Method called when 'clone' keyword is used. + */ + public function __clone() + { + $original = $this->gd; + $copy = imagecreatetruecolor($this->width, $this->height); + + imagecopy($copy, $original, 0, 0, 0, 0, $this->width, $this->height); + + $this->gd = $copy; + } + + /** + * Output a binary raw dump of an image in a specified format. + * + * @param string|ImageType $type Image format of the dump. + * + * @throws \Exception When unsupported type. + */ + public function blob( $type = 'PNG' ) { + + $type = strtoupper($type); + if ( ImageType::GIF == $type ) { + + imagegif( $this->gd ); + + } else if ( ImageType::JPEG == $type ) { + + imagejpeg( $this->gd ); + + } else if ( ImageType::PNG == $type ) { + + imagepng( $this->gd ); + + } else if ( ImageType::WBMP == $type ) { + + imagewbmp( $this->gd ); + + } else { + throw new \Exception( sprintf( 'File type "%s" not supported.', $type ) ); + } + } + + /** + * Create Image from image file. + * + * @param string $imageFile Path to image. + * + * @return Image + * @throws \Exception + */ + public static function createFromFile( $imageFile ) { + if ( ! file_exists( $imageFile ) ) { + throw new \Exception( sprintf( 'Could not open "%s". File does not exist.', $imageFile ) ); + } + + $type = self::_guessType( $imageFile ); + if ( ImageType::GIF == $type ) { + + return self::_createGif( $imageFile ); + + } else if ( ImageType::JPEG == $type ) { + + return self::_createJpeg( $imageFile ); + + } else if ( ImageType::PNG == $type ) { + + return self::_createPng( $imageFile ); + + } else if ( ImageType::WBMP == $type ) { + + return self::_createWbmp( $imageFile ); + + } else { + throw new \Exception( sprintf( 'Could not open "%s". File type not supported.', $imageFile ) ); + } + } + + /** + * Create an Image from a GD resource. The file type defaults to unknown. + * + * @param resource $gd GD resource. + * + * @return Image + */ + public static function createFromCore( $gd ) { + return new self( $gd, '', imagesx( $gd ), imagesy( $gd ), ImageType::UNKNOWN ); + } + + /** + * Create a blank image. + * + * @param int $width Width in pixels. + * @param int $height Height in pixels. + * + * @return Image + */ + public static function createBlank($width = 1, $height = 1){ + + return new self(imagecreatetruecolor($width, $height), '', $width, $height, ImageType::UNKNOWN); + + } + + /** + * Set the blending mode for an image. Allows transparent overlays on top of an image. + * + * @param bool $flag True to enable blending mode. + * @return self + */ + public function alphaBlendingMode( $flag ){ + imagealphablending( $this->gd, $flag ); + + return $this; + } + + /** + * Enable/Disable transparency + * + * @param bool $flag True to enable alpha mode. + * @return self + */ + public function fullAlphaMode( $flag ){ + if( true === $flag ){ + $this->alphaBlendingMode( false ); // Must be false for full alpha mode to work + } + imagesavealpha( $this->gd, $flag ); + + return $this; + } + + /** + * Returns animated flag. + * + * @return bool True if animated GIF. + */ + public function isAnimated() { + return $this->animated; + } + + /** + * Get GD resource ID. + * + * @return resource + */ + public function getCore() { + return $this->gd; + } + + /** + * Get image file path. + * + * @return string File path to image. + */ + public function getImageFile() { + return $this->imageFile; + } + + /** + * Get image width in pixels. + * + * @return int + */ + public function getWidth() { + return $this->width; + } + + /** + * Get image height in pixels. + * + * @return int + */ + public function getHeight() { + return $this->height; + } + + /** + * Get image type. + * + * @return string + */ + public function getType() { + return $this->type; + } + + /** + * Get blocks. + * + * @return string. + */ + public function getBlocks() { + return $this->blocks; + } + + /** + * Get histogram from an entire image or its sub-region. + * + * @param array|null $slice Array of slice information. array( array( 0,0), array(100,50)) means x,y is 0,0 and width,height is 100,50 + * + * @return array Returns array containing RGBA bins array('r'=>array(), 'g'=>array(), 'b'=>array(), 'a'=>array()) + */ + public function histogram($slice = null) + { + $gd = $this->getCore(); + + if(null === $slice){ + $sliceX = 0; + $sliceY = 0; + $sliceW = $this->getWidth(); + $sliceH = $this->getHeight(); + } else { + $sliceX = $slice[0][0]; + $sliceY = $slice[0][1]; + $sliceW = $slice[1][0]; + $sliceH = $slice[1][1]; + } + + $rBin = array(); + $gBin = array(); + $bBin = array(); + $aBin = array(); + for ($y = $sliceY; $y < $sliceY+$sliceH; $y++) { + for ($x = $sliceX; $x < $sliceX+$sliceW; $x++) { + $rgb = imagecolorat($gd, $x, $y); + $a = ($rgb >> 24) & 0x7F; // 127 in hex. These are binary operations. + $r = ($rgb >> 16) & 0xFF; + $g = ($rgb >> 8) & 0xFF; + $b = $rgb & 0xFF; + + if ( ! isset($rBin[$r])) { + $rBin[$r] = 1; + } else { + $rBin[$r]++; + } + + if ( ! isset($gBin[$g])) { + $gBin[$g] = 1; + } else { + $gBin[$g]++; + } + + if ( ! isset($bBin[$b])) { + $bBin[$b] = 1; + } else { + $bBin[$b]++; + } + + if ( ! isset($aBin[$a])) { + $aBin[$a] = 1; + } else { + $aBin[$a]++; + } + } + } + return array( + 'r' => $rBin, + 'g' => $gBin, + 'b' => $bBin, + 'a' => $aBin + ); + } + + /** + * Load a GIF image. + * + * @param string $imageFile + * + * @return Image + * @throws \Exception + */ + private static function _createGif( $imageFile ){ + $gift = new GifHelper(); + $bytes = $gift->open($imageFile); + $animated = $gift->isAnimated($bytes); + $blocks = ''; + if($animated){ + $blocks = $gift->decode($bytes); + } + $gd = @imagecreatefromgif( $imageFile ); + + if(!$gd){ + throw new \Exception( sprintf('Could not open "%s". Not a valid %s file.', $imageFile, ImageType::GIF) ); + } + + return new self( + $gd, + $imageFile, + imagesx( $gd ), + imagesy( $gd ), + ImageType::GIF, + $blocks, + $animated + ); + } + + /** + * Load a JPEG image. + * + * @param string $imageFile File path to image. + * + * @return Image + * @throws \Exception + */ + private static function _createJpeg( $imageFile ){ + $gd = @imagecreatefromjpeg( $imageFile ); + + if(!$gd){ + throw new \Exception( sprintf('Could not open "%s". Not a valid %s file.', $imageFile, ImageType::JPEG ) ); + } + + return new self( $gd, $imageFile, imagesx( $gd ), imagesy( $gd ), ImageType::JPEG ); + } + + /** + * Load a PNG image. + * + * @param string $imageFile File path to image. + * + * @return Image + * @throws \Exception + */ + private static function _createPng( $imageFile ){ + $gd = @imagecreatefrompng( $imageFile ); + + if(!$gd){ + throw new \Exception( sprintf('Could not open "%s". Not a valid %s file.', $imageFile, ImageType::PNG) ); + } + + $image = new self( $gd, $imageFile, imagesx( $gd ), imagesy( $gd ), ImageType::PNG ); + $image->fullAlphaMode( true ); + return $image; + } + + /** + * Load a WBMP image. + * + * @param string $imageFile + * + * @return Image + * @throws \Exception + */ + private static function _createWbmp( $imageFile ){ + $gd = @imagecreatefromwbmp( $imageFile ); + + if(!$gd){ + throw new \Exception( sprintf('Could not open "%s". Not a valid %s file.', $imageFile, ImageType::WBMP) ); + } + + return new self( $gd, $imageFile, imagesx( $gd ), imagesy( $gd ), ImageType::WBMP ); + } + + /** + * @param $imageFile + * + * @return string + */ + private static function _guessType( $imageFile ){ + // Values from http://php.net/manual/en/image.constants.php starting with IMAGETYPE_GIF. + // 0 - unknown, + // 1 - GIF, + // 2 - JPEG, + // 3 - PNG + // 15 - WBMP + list($width, $height, $type) = getimagesize( $imageFile ); + + unset($width, $height); + + if ( 1 == $type) { + + return ImageType::GIF; + + } else if ( 2 == $type) { + + return ImageType::JPEG; + + } else if ( 3 == $type) { + + return ImageType::PNG; + + } else if ( 15 == $type) { + + return ImageType::WBMP; + + } + + return ImageType::UNKNOWN; + } +} \ No newline at end of file diff --git a/niucloud/vendor/kosinix/grafika/src/Grafika/Gd/ImageHash/AverageHash.php b/niucloud/vendor/kosinix/grafika/src/Grafika/Gd/ImageHash/AverageHash.php new file mode 100644 index 000000000..328862bd3 --- /dev/null +++ b/niucloud/vendor/kosinix/grafika/src/Grafika/Gd/ImageHash/AverageHash.php @@ -0,0 +1,71 @@ +resizeExact($image, $width, $height); // Resize to exactly 8x8 + $gd = $image->getCore(); + + // Create an array of greyscale pixel values. + $pixels = array(); + for ($y = 0; $y < $height; $y++) { + for ($x = 0; $x < $width; $x++) { + $rgba = imagecolorat($gd, $x, $y); + $r = ($rgba >> 16) & 0xFF; + $g = ($rgba >> 8) & 0xFF; + $b = $rgba & 0xFF; + + $pixels[] = floor(($r + $g + $b) / 3); // Gray + } + } + + // Get the average pixel value. + $average = floor(array_sum($pixels) / count($pixels)); + // Each hash bit is set based on whether the current pixels value is above or below the average. + $hash = ''; + foreach ($pixels as $pixel) { + if ($pixel > $average) { + $hash .= '1'; + } else { + $hash .= '0'; + } + } + return $hash; + } +} \ No newline at end of file diff --git a/niucloud/vendor/kosinix/grafika/src/Grafika/Gd/ImageHash/DifferenceHash.php b/niucloud/vendor/kosinix/grafika/src/Grafika/Gd/ImageHash/DifferenceHash.php new file mode 100644 index 000000000..b95f8a97c --- /dev/null +++ b/niucloud/vendor/kosinix/grafika/src/Grafika/Gd/ImageHash/DifferenceHash.php @@ -0,0 +1,73 @@ +resizeExact($image, $width, $height); // Resize to exactly 9x8 + $gd = $image->getCore(); + + // Build hash + $hash = ''; + for ($y = 0; $y < $height; $y++) { + // Get the pixel value for the leftmost pixel. + $rgba = imagecolorat($gd, 0, $y); + $r = ($rgba >> 16) & 0xFF; + $g = ($rgba >> 8) & 0xFF; + $b = $rgba & 0xFF; + + $left = floor(($r + $g + $b) / 3); + for ($x = 1; $x < $width; $x++) { + // Get the pixel value for each pixel starting from position 1. + $rgba = imagecolorat($gd, $x, $y); + $r = ($rgba >> 16) & 0xFF; + $g = ($rgba >> 8) & 0xFF; + $b = $rgba & 0xFF; + $right = floor(($r + $g + $b) / 3); + // Each hash bit is set based on whether the left pixel is brighter than the right pixel. + if ($left > $right) { + $hash .= '1'; + } else { + $hash .= '0'; + } + // Prepare the next loop. + $left = $right; + } + } + $editor->free( $image ); + return $hash; + } +} \ No newline at end of file diff --git a/niucloud/vendor/kosinix/grafika/src/Grafika/Grafika.php b/niucloud/vendor/kosinix/grafika/src/Grafika/Grafika.php new file mode 100644 index 000000000..5489d1be3 --- /dev/null +++ b/niucloud/vendor/kosinix/grafika/src/Grafika/Grafika.php @@ -0,0 +1,394 @@ +isAvailable()) { + return $editorName; + } + } + + throw new \Exception('No supported editor.'); + } + + /** + * Creates the first available editor. + * + * @param array $editorList Array of editor list names. Use this to change the order of evaluation for editors. Default order of evaluation is Imagick then GD. + * + * @return EditorInterface + * @throws \Exception + */ + public static function createEditor($editorList = array('Imagick', 'Gd')) + { + $editorName = self::detectAvailableEditor($editorList); + if ('Imagick' === $editorName) { + return new ImagickEditor(); + } else { + return new GdEditor(); + } + } + + /** + * Create an image. + * @param string $imageFile Path to image file. + * + * @return ImageInterface + * @throws \Exception + */ + public static function createImage($imageFile) + { + $editorName = self::detectAvailableEditor(); + if ('Imagick' === $editorName) { + return ImagickImage::createFromFile($imageFile); + } else { + return GdImage::createFromFile($imageFile); + } + } + + + /** + * Create a blank image. + * + * @param int $width Width of image in pixels. + * @param int $height Height of image in pixels. + * + * @return ImageInterface + * @throws \Exception + */ + public static function createBlankImage($width = 1, $height = 1) + { + $editorName = self::detectAvailableEditor(); + if ('Imagick' === $editorName) { + return ImagickImage::createBlank($width, $height); + } else { + return GdImage::createBlank($width, $height); + } + } + + + /** + * Create a filter. Detects available editor to use. + * + * @param string $filterName The name of the filter. + * + * @return FilterInterface + * @throws \Exception + */ + public static function createFilter($filterName) + { + $editorName = self::detectAvailableEditor(); + $p = func_get_args(); + if ('Imagick' === $editorName) { + switch ($filterName){ + case 'Blur': + return new ImagickBlur( + (array_key_exists(1,$p) ? $p[1] : 1) + ); + case 'Brightness': + return new ImagickBrightness( + $p[1] + ); + case 'Colorize': + return new ImagickColorize( + $p[1], $p[2], $p[3] + ); + case 'Contrast': + return new ImagickContrast( + $p[1] + ); + case 'Dither': + return new ImagickDither( + $p[1] + ); + case 'Gamma': + return new ImagickGamma( + $p[1] + ); + case 'Grayscale': + return new ImagickGrayscale(); + case 'Invert': + return new ImagickInvert(); + case 'Pixelate': + return new ImagickPixelate( + $p[1] + ); + case 'Sharpen': + return new ImagickSharpen( + $p[1] + ); + case 'Sobel': + return new ImagickSobel(); + } + throw new \Exception('Invalid filter name.'); + } else { + switch ($filterName){ + case 'Blur': + return new GdBlur( + (array_key_exists(1,$p) ? $p[1] : 1) + ); + case 'Brightness': + return new GdBrightness( + $p[1] + ); + case 'Colorize': + return new GdColorize( + $p[1], $p[2], $p[3] + ); + case 'Contrast': + return new GdContrast( + $p[1] + ); + case 'Dither': + return new GdDither( + $p[1] + ); + case 'Gamma': + return new GdGamma( + $p[1] + ); + case 'Grayscale': + return new GdGrayscale(); + case 'Invert': + return new GdInvert(); + case 'Pixelate': + return new GdPixelate( + $p[1] + ); + case 'Sharpen': + return new GdSharpen( + $p[1] + ); + case 'Sobel': + return new GdSobel(); + } + throw new \Exception('Invalid filter name.'); + } + } + + /** + * Draws an object. Detects available editor to use. + * + * @param string $drawingObjectName The name of the DrawingObject. + * + * @return DrawingObjectInterface + * @throws \Exception + * + * We use array_key_exist() instead of isset() to be able to detect a parameter with a NULL value. + */ + public static function createDrawingObject($drawingObjectName) + { + $editorName = self::detectAvailableEditor(); + $p = func_get_args(); + if ('Imagick' === $editorName) { + switch ($drawingObjectName){ + case 'CubicBezier': + return new ImagickCubicBezier( + $p[1], + $p[2], + $p[3], + $p[4], + (array_key_exists(5,$p) ? $p[5] : '#000000') + ); + case 'Ellipse': + return new ImagickEllipse( + $p[1], + $p[2], + (array_key_exists(3,$p) ? $p[3] : array(0,0)), + (array_key_exists(4,$p) ? $p[4] : 1), + (array_key_exists(5,$p) ? $p[5] : '#000000'), + (array_key_exists(6,$p) ? $p[6] : '#FFFFFF') + ); + case 'Line': + return new ImagickLine( + $p[1], + $p[2], + (array_key_exists(3,$p) ? $p[3] : 1), + (array_key_exists(4,$p) ? $p[4] : '#000000') + ); + case 'Polygon': + return new ImagickPolygon( + $p[1], + (array_key_exists(2,$p) ? $p[2] : 1), + (array_key_exists(3,$p) ? $p[3] : '#000000'), + (array_key_exists(4,$p) ? $p[4] : '#FFFFFF') + ); + case 'Rectangle': + return new ImagickRectangle( + $p[1], + $p[2], + (array_key_exists(3,$p) ? $p[3] : array(0,0)), + (array_key_exists(4,$p) ? $p[4] : 1), + (array_key_exists(5,$p) ? $p[5] : '#000000'), + (array_key_exists(6,$p) ? $p[6] : '#FFFFFF') + ); + case 'QuadraticBezier': + return new ImagickQuadraticBezier( + $p[1], + $p[2], + $p[3], + (array_key_exists(4,$p) ? $p[4] : '#000000') + ); + + } + throw new \Exception('Invalid drawing object name.'); + } else { + switch ($drawingObjectName) { + case 'CubicBezier': + return new GdCubicBezier( + $p[1], + $p[2], + $p[3], + $p[4], + (array_key_exists(5,$p) ? $p[5] : '#000000') + ); + case 'Ellipse': + return new GdEllipse( + $p[1], + $p[2], + (array_key_exists(3,$p) ? $p[3] : array(0,0)), + (array_key_exists(4,$p) ? $p[4] : 1), + (array_key_exists(5,$p) ? $p[5] : '#000000'), + (array_key_exists(6,$p) ? $p[6] : '#FFFFFF') + ); + case 'Line': + return new GdLine( + $p[1], + $p[2], + (array_key_exists(3,$p) ? $p[3] : 1), + (array_key_exists(4,$p) ? $p[4] : '#000000') + ); + case 'Polygon': + return new GdPolygon( + $p[1], + (array_key_exists(2,$p) ? $p[2] : 1), + (array_key_exists(3,$p) ? $p[3] : '#000000'), + (array_key_exists(4,$p) ? $p[4] : '#FFFFFF') + ); + case 'Rectangle': + return new GdRectangle( + $p[1], + $p[2], + (array_key_exists(3,$p) ? $p[3] : array(0,0)), + (array_key_exists(4,$p) ? $p[4] : 1), + (array_key_exists(5,$p) ? $p[5] : '#000000'), + (array_key_exists(6,$p) ? $p[6] : '#FFFFFF') + ); + case 'QuadraticBezier': + return new GdQuadraticBezier( + $p[1], + $p[2], + $p[3], + (array_key_exists(4,$p) ? $p[4] : '#000000') + ); + } + throw new \Exception('Invalid drawing object name.'); + } + } + + +} \ No newline at end of file diff --git a/niucloud/vendor/kosinix/grafika/src/Grafika/ImageInterface.php b/niucloud/vendor/kosinix/grafika/src/Grafika/ImageInterface.php new file mode 100644 index 000000000..4ad18741c --- /dev/null +++ b/niucloud/vendor/kosinix/grafika/src/Grafika/ImageInterface.php @@ -0,0 +1,80 @@ +getWidth(); + $height = $image->getHeight(); + $imagick = $image->getCore(); + + $draw = new \ImagickDraw(); + + $strokeColor = new \ImagickPixel($this->getColor()->getHexString()); + $fillColor = new \ImagickPixel('rgba(0,0,0,0)'); + + $draw->setStrokeOpacity(1); + $draw->setStrokeColor($strokeColor); + $draw->setFillColor($fillColor); + + $points = array( + array('x'=> $this->point1[0], 'y'=> $this->point1[1]), + array('x'=> $this->control1[0], 'y'=> $this->control1[1]), + array('x'=> $this->control2[0], 'y'=> $this->control2[1]), + array('x'=> $this->point2[0], 'y'=> $this->point2[1]), + ); + $draw->bezier($points); + + // Render the draw commands in the ImagickDraw object + $imagick->drawImage($draw); + + $type = $image->getType(); + $file = $image->getImageFile(); + return new Image($imagick, $file, $width, $height, $type); // Create new image with updated core + } +} \ No newline at end of file diff --git a/niucloud/vendor/kosinix/grafika/src/Grafika/Imagick/DrawingObject/Ellipse.php b/niucloud/vendor/kosinix/grafika/src/Grafika/Imagick/DrawingObject/Ellipse.php new file mode 100644 index 000000000..d444dfb4b --- /dev/null +++ b/niucloud/vendor/kosinix/grafika/src/Grafika/Imagick/DrawingObject/Ellipse.php @@ -0,0 +1,40 @@ +getBorderColor()->getHexString()); + $fillColor = new \ImagickPixel($this->getFillColor()->getHexString()); + + $draw = new \ImagickDraw(); + $draw->setStrokeColor($strokeColor); + $draw->setFillColor($fillColor); + + $draw->setStrokeWidth($this->borderSize); + + list($x, $y) = $this->pos; + $left = $x + $this->width / 2; + $top = $y + $this->height / 2; + $draw->ellipse($left, $top, $this->width/2, $this->height/2, 0, 360); + + $image->getCore()->drawImage($draw); + + return $image; + } +} \ No newline at end of file diff --git a/niucloud/vendor/kosinix/grafika/src/Grafika/Imagick/DrawingObject/Line.php b/niucloud/vendor/kosinix/grafika/src/Grafika/Imagick/DrawingObject/Line.php new file mode 100644 index 000000000..96cad7fc5 --- /dev/null +++ b/niucloud/vendor/kosinix/grafika/src/Grafika/Imagick/DrawingObject/Line.php @@ -0,0 +1,41 @@ +getColor()->getHexString()); + + $draw = new \ImagickDraw(); + + $draw->setStrokeColor($strokeColor); + + $draw->setStrokeWidth($this->thickness); + + list($x1, $y1) = $this->point1; + list($x2, $y2) = $this->point2; + $draw->line($x1, $y1, $x2, $y2); + + $image->getCore()->drawImage($draw); + + return $image; + } + + +} \ No newline at end of file diff --git a/niucloud/vendor/kosinix/grafika/src/Grafika/Imagick/DrawingObject/Polygon.php b/niucloud/vendor/kosinix/grafika/src/Grafika/Imagick/DrawingObject/Polygon.php new file mode 100644 index 000000000..a2e3e435f --- /dev/null +++ b/niucloud/vendor/kosinix/grafika/src/Grafika/Imagick/DrawingObject/Polygon.php @@ -0,0 +1,52 @@ +setStrokeWidth($this->borderSize); + + if(null !== $this->fillColor) { + $fillColor = new \ImagickPixel( $this->fillColor->getHexString() ); + $draw->setFillColor($fillColor); + } else { + $draw->setFillOpacity(0); + } + + if(null !== $this->borderColor) { + $borderColor = new \ImagickPixel( $this->borderColor->getHexString() ); + $draw->setStrokeColor($borderColor); + } else { + $draw->setStrokeOpacity(0); + } + + $draw->polygon($this->points()); + + $image->getCore()->drawImage($draw); + + return $image; + } + + protected function points(){ + $points = array(); + foreach($this->points as $i=>$pos){ + $points[$i] = array( + 'x' => $pos[0], + 'y' => $pos[1] + ); + } + if( count($points) < 3 ){ + throw new \Exception('Polygon needs at least 3 points.'); + } + return $points; + } + +} \ No newline at end of file diff --git a/niucloud/vendor/kosinix/grafika/src/Grafika/Imagick/DrawingObject/QuadraticBezier.php b/niucloud/vendor/kosinix/grafika/src/Grafika/Imagick/DrawingObject/QuadraticBezier.php new file mode 100644 index 000000000..87bbe57c2 --- /dev/null +++ b/niucloud/vendor/kosinix/grafika/src/Grafika/Imagick/DrawingObject/QuadraticBezier.php @@ -0,0 +1,55 @@ +getWidth(); + $height = $image->getHeight(); + $imagick = $image->getCore(); + + $draw = new \ImagickDraw(); + + $strokeColor = new \ImagickPixel($this->getColor()->getHexString()); + $fillColor = new \ImagickPixel('rgba(0,0,0,0)'); + + $draw->setStrokeOpacity(1); + $draw->setStrokeColor($strokeColor); + $draw->setFillColor($fillColor); + + + + list($x1, $y1) = $this->point1; + list($x2, $y2) = $this->control; + list($x3, $y3) = $this->point2; + $draw->pathStart(); + $draw->pathMoveToAbsolute($x1, $y1); + $draw->pathCurveToQuadraticBezierAbsolute( + $x2, $y2, + $x3, $y3 + ); + $draw->pathFinish(); + + // Render the draw commands in the ImagickDraw object + $imagick->drawImage($draw); + + $type = $image->getType(); + $file = $image->getImageFile(); + return new Image($imagick, $file, $width, $height, $type); // Create new image with updated core + } +} \ No newline at end of file diff --git a/niucloud/vendor/kosinix/grafika/src/Grafika/Imagick/DrawingObject/Rectangle.php b/niucloud/vendor/kosinix/grafika/src/Grafika/Imagick/DrawingObject/Rectangle.php new file mode 100644 index 000000000..c480c6643 --- /dev/null +++ b/niucloud/vendor/kosinix/grafika/src/Grafika/Imagick/DrawingObject/Rectangle.php @@ -0,0 +1,46 @@ +setStrokeWidth($this->borderSize); + + if(null !== $this->fillColor) { + $fillColor = new \ImagickPixel( $this->fillColor->getHexString() ); + $draw->setFillColor($fillColor); + } else { + $draw->setFillOpacity(0); + } + + if(null !== $this->borderColor) { + $borderColor = new \ImagickPixel( $this->borderColor->getHexString() ); + $draw->setStrokeColor($borderColor); + } else { + $draw->setStrokeOpacity(0); + } + + + + $x1 = $this->pos[0]; + $x2 = $x1 + $this->getWidth(); + $y1 = $this->pos[1]; + $y2 = $y1 + $this->getHeight(); + + $draw->rectangle( $x1, $y1, $x2, $y2 ); + + $image->getCore()->drawImage($draw); + + return $image; + } + +} \ No newline at end of file diff --git a/niucloud/vendor/kosinix/grafika/src/Grafika/Imagick/Editor.php b/niucloud/vendor/kosinix/grafika/src/Grafika/Imagick/Editor.php new file mode 100644 index 000000000..36d698fba --- /dev/null +++ b/niucloud/vendor/kosinix/grafika/src/Grafika/Imagick/Editor.php @@ -0,0 +1,827 @@ +isAnimated()) { // Ignore animated GIF for now + return $this; + } + + $image = $filter->apply($image); + + return $this; + } + + /** + * Blend two images together with the first image as the base and the second image on top. Supports several blend modes. + * + * @param Image $image1 The base image. + * @param Image $image2 The image placed on top of the base image. + * @param string $type The blend mode. Can be: normal, multiply, overlay or screen. + * @param float $opacity The opacity of $image2. Possible values 0.0 to 1.0 where 0.0 is fully transparent and 1.0 is fully opaque. Defaults to 1.0. + * @param string $position The position of $image2 on $image1. Possible values top-left, top-center, top-right, center-left, center, center-right, bottom-left, bottom-center, bottom-right and smart. Defaults to top-left. + * @param int $offsetX Number of pixels to add to the X position of $image2. + * @param int $offsetY Number of pixels to add to the Y position of $image2. + * + * @return Editor + * @throws \Exception When added image is outside of canvas or invalid blend type + */ + public function blend(&$image1, $image2, $type='normal', $opacity = 1.0, $position = 'top-left', $offsetX = 0, $offsetY = 0 ){ + + // Turn into position object + $position = new Position($position, $offsetX, $offsetY); + + // Position is for $image2. $image1 is canvas. + list($offsetX, $offsetY) = $position->getXY($image1->getWidth(), $image1->getHeight(), $image2->getWidth(), $image2->getHeight()); + + // Check if it overlaps + if( ($offsetX >= $image1->getWidth() ) or + ($offsetX + $image2->getWidth() <= 0) or + ($offsetY >= $image1->getHeight() ) or + ($offsetY + $image2->getHeight() <= 0)){ + + throw new \Exception('Invalid blending. Image 2 is outside the canvas.'); + } + + // Loop start X + $loopStartX = 0; + $canvasStartX = $offsetX; + if($canvasStartX < 0){ + $diff = 0 - $canvasStartX; + $loopStartX += $diff; + } + + // Loop start Y + $loopStartY = 0; + $canvasStartY = $offsetY; + if($canvasStartY < 0){ + $diff = 0 - $canvasStartY; + $loopStartY += $diff; + } + + if ( $opacity !== 1 ) { + $this->opacity($image2, $opacity); + } + + $type = strtolower( $type ); + if($type==='normal') { + $image1->getCore()->compositeImage($image2->getCore(), \Imagick::COMPOSITE_OVER, $loopStartX + $offsetX, $loopStartY + $offsetY); + } else if($type==='multiply'){ + $image1->getCore()->compositeImage($image2->getCore(), \Imagick::COMPOSITE_MULTIPLY, $loopStartX + $offsetX, $loopStartY + $offsetY); + } else if($type==='overlay'){ + $image1->getCore()->compositeImage($image2->getCore(), \Imagick::COMPOSITE_OVERLAY, $loopStartX + $offsetX, $loopStartY + $offsetY); + } else if($type==='screen'){ + $image1->getCore()->compositeImage($image2->getCore(), \Imagick::COMPOSITE_SCREEN, $loopStartX + $offsetX, $loopStartY + $offsetY); + } else { + throw new \Exception(sprintf('Invalid blend type "%s".', $type)); + } + + return $this; + } + + /** + * Compare two images and returns a hamming distance. A value of 0 indicates a likely similar picture. A value between 1 and 10 is potentially a variation. A value greater than 10 is likely a different image. + * + * @param ImageInterface|string $image1 + * @param ImageInterface|string $image2 + * + * @return int Hamming distance. Note: This breaks the chain if you are doing fluent api calls as it does not return an Editor. + * @throws \Exception + */ + public function compare($image1, $image2) + { + + if (is_string($image1)) { // If string passed, turn it into a Image object + $image1 = Image::createFromFile($image1); + $this->flatten( $image1 ); + } + + if (is_string($image2)) { // If string passed, turn it into a Image object + $image2 = Image::createFromFile($image2); + $this->flatten( $image2 ); + } + + $hash = new DifferenceHash(); + + $bin1 = $hash->hash($image1, $this); + $bin2 = $hash->hash($image2, $this); + $str1 = str_split($bin1); + $str2 = str_split($bin2); + $distance = 0; + foreach ($str1 as $i => $char) { + if ($char !== $str2[$i]) { + $distance++; + } + } + + return $distance; + + } + + /** + * Crop the image to the given dimension and position. + * + * @param Image $image + * @param int $cropWidth Crop width in pixels. + * @param int $cropHeight Crop Height in pixels. + * @param string $position The crop position. Possible values top-left, top-center, top-right, center-left, center, center-right, bottom-left, bottom-center, bottom-right and smart. Defaults to center. + * @param int $offsetX Number of pixels to add to the X position of the crop. + * @param int $offsetY Number of pixels to add to the Y position of the crop. + * + * @return Editor + * @throws \Exception + */ + public function crop(&$image, $cropWidth, $cropHeight, $position = 'center', $offsetX = 0, $offsetY = 0) + { + + if ($image->isAnimated()) { // Ignore animated GIF for now + return $this; + } + + if ( 'smart' === $position ) { // Smart crop + list( $x, $y ) = $this->_smartCrop( $image, $cropWidth, $cropHeight ); + } else { + // Turn into an instance of Position + $position = new Position( $position, $offsetX, $offsetY ); + + // Crop position as x,y coordinates + list( $x, $y ) = $position->getXY( $image->getWidth(), $image->getHeight(), $cropWidth, $cropHeight ); + + } + + $image->getCore()->cropImage($cropWidth, $cropHeight, $x, $y); + + return $this; + } + + /** + * Draw a DrawingObject on the image. See Drawing Objects section. + * + * @param Image $image + * @param DrawingObjectInterface $drawingObject + * + * @return $this + */ + public function draw(&$image, $drawingObject) + { + + if ($image->isAnimated()) { // Ignore animated GIF for now + return $this; + } + + $image = $drawingObject->draw($image); + + return $this; + } + + /** + * Compare if two images are equal. It will compare if the two images are of the same width and height. If the dimensions differ, it will return false. If the dimensions are equal, it will loop through each pixels. If one of the pixel don't match, it will return false. The pixels are compared using their RGB (Red, Green, Blue) values. + * + * @param string|ImageInterface $image1 Can be an instance of Image or string containing the file system path to image. + * @param string|ImageInterface $image2 Can be an instance of Image or string containing the file system path to image. + * + * @return bool True if equals false if not. Note: This breaks the chain if you are doing fluent api calls as it does not return an Editor. + * @throws \Exception + */ + public function equal($image1, $image2) + { + + if (is_string($image1)) { // If string passed, turn it into a Image object + $image1 = Image::createFromFile($image1); + $this->flatten( $image1 ); + } + + if (is_string($image2)) { // If string passed, turn it into a Image object + $image2 = Image::createFromFile($image2); + $this->flatten( $image2 ); + } + + // Check if image dimensions are equal + if ($image1->getWidth() !== $image2->getWidth() or $image1->getHeight() !== $image2->getHeight()) { + + return false; + + } else { + + // Loop using image1 + $pixelIterator = $image1->getCore()->getPixelIterator(); + foreach ($pixelIterator as $row => $pixels) { /* Loop through pixel rows */ + foreach ($pixels as $column => $pixel) { /* Loop through the pixels in the row (columns) */ + /** + * Get image1 pixel + * @var $pixel \ImagickPixel + */ + $rgba1 = $pixel->getColor(); + + // Get image2 pixel + $rgba2 = $image2->getCore()->getImagePixelColor($column, $row)->getColor(); + + // Compare pixel value + if ( + $rgba1['r'] !== $rgba2['r'] or + $rgba1['g'] !== $rgba2['g'] or + $rgba1['b'] !== $rgba2['b'] + ) { + return false; + } + } + $pixelIterator->syncIterator(); /* Sync the iterator, this is important to do on each iteration */ + } + } + + return true; + } + + /** + * Fill entire image with color. + * + * @param Image $image + * @param Color $color Color object + * @param int $x X-coordinate of start point + * @param int $y Y-coordinate of start point + * + * @return self + */ + public function fill(&$image, $color, $x = 0, $y = 0) + { + + if ($image->isAnimated()) { // Ignore animated GIF for now + return $this; + } + + $target = $image->getCore()->getImagePixelColor($x, $y); + $image->getCore()->floodfillPaintImage($color->getHexString(), 1, $target, $x, $y, false); + + return $this; + } + + /** + * Flatten if animated GIF. Do nothing otherwise. + * + * @param Image $image + * + * @return self + */ + public function flatten(&$image){ + if($image->isAnimated()){ + $imagick = $image->getCore()->mergeImageLayers(\Imagick::LAYERMETHOD_FLATTEN); + $image = new Image( + $imagick, + $image->getImageFile(), + $image->getWidth(), + $image->getHeight(), + $image->getType(), + '', // blocks + false // animated + ); + } + return $this; + } + + /** + * Flip or mirrors the image. + * + * @param Image $image + * @param string $mode The type of flip: 'h' for horizontal flip or 'v' for vertical. + * + * @return Editor + * @throws \Exception + */ + public function flip(&$image, $mode){ + if ($mode === 'h') { + $image->getCore()->flopImage(); + } else if ($mode === 'v') { + $image->getCore()->flipImage(); + } else { + throw new \Exception(sprintf('Unsupported mode "%s"', $mode)); + } + return $this; + } + + /** + * Free the image clearing resources associated with it. + * + * @param Image $image + * + * @return Editor + */ + public function free( &$image ) + { + $image->getCore()->clear(); + return $this; + } + + /** + * Checks if the editor is available on the current PHP install. + * + * @return bool True if available false if not. + */ + public function isAvailable() + { + // First, test Imagick's extension and classes. + if (false === extension_loaded('imagick') || + false === class_exists('Imagick') || + false === class_exists('ImagickDraw') || + false === class_exists('ImagickPixel') || + false === class_exists('ImagickPixelIterator') + ) { + return false; + } + + return true; + } + + /** + * Sets the image to the specified opacity level where 1.0 is fully opaque and 0.0 is fully transparent. + * + * @param Image $image + * @param float $opacity + * + * @return self + * @throws \Exception + */ + public function opacity(&$image, $opacity) + { + + if ($image->isAnimated()) { // Ignore animated GIF for now + return $this; + } + + // Bounds checks + $opacity = ($opacity > 1) ? 1 : $opacity; + $opacity = ($opacity < 0) ? 0 : $opacity; + + $image->getCore()->setImageOpacity($opacity); + + return $this; + } + + /** + * Open an image file and assign Image to first parameter. + * + * @param Image $image + * @param string $imageFile + * + * @return Editor + */ + public function open(&$image, $imageFile){ + $image = Image::createFromFile( $imageFile ); + return $this; + } + + /** + * Wrapper function for the resizeXXX family of functions. Resize image given width, height and mode. + * + * @param Image $image + * @param int $newWidth Width in pixels. + * @param int $newHeight Height in pixels. + * @param string $mode Resize mode. Possible values: "exact", "exactHeight", "exactWidth", "fill", "fit". + * + * @return Editor + * @throws \Exception + */ + public function resize(&$image, $newWidth, $newHeight, $mode = 'fit') + { + /* + * Resize formula: + * ratio = w / h + * h = w / ratio + * w = h * ratio + */ + switch ($mode) { + case 'exact': + $this->resizeExact($image, $newWidth, $newHeight); + break; + case 'fill': + $this->resizeFill($image, $newWidth, $newHeight); + break; + case 'exactWidth': + $this->resizeExactWidth($image, $newWidth); + break; + case 'exactHeight': + $this->resizeExactHeight($image, $newHeight); + break; + case 'fit': + $this->resizeFit($image, $newWidth, $newHeight); + break; + default: + throw new \Exception(sprintf('Invalid resize mode "%s".', $mode)); + } + + return $this; + } + + /** + * Resize image to exact dimensions ignoring aspect ratio. Useful if you want to force exact width and height. + * + * @param Image $image + * @param int $newWidth Width in pixels. + * @param int $newHeight Height in pixels. + * + * @return self + */ + public function resizeExact(&$image, $newWidth, $newHeight) + { + + $this->_resize($image, $newWidth, $newHeight); + + return $this; + } + + /** + * Resize image to exact height. Width is auto calculated. Useful for creating row of images with the same height. + * + * @param Image $image + * @param int $newHeight Height in pixels. + * + * @return self + */ + public function resizeExactHeight(&$image, $newHeight) + { + + $width = $image->getWidth(); + $height = $image->getHeight(); + $ratio = $width / $height; + + $resizeHeight = $newHeight; + $resizeWidth = $newHeight * $ratio; + + $this->_resize($image, $resizeWidth, $resizeHeight); + + return $this; + } + + /** + * Resize image to exact width. Height is auto calculated. Useful for creating column of images with the same width. + * + * @param Image $image + * @param int $newWidth Width in pixels. + * + * @return self + */ + public function resizeExactWidth(&$image, $newWidth) + { + + $width = $image->getWidth(); + $height = $image->getHeight(); + $ratio = $width / $height; + + $resizeWidth = $newWidth; + $resizeHeight = round($newWidth / $ratio); + + $this->_resize($image, $resizeWidth, $resizeHeight); + + return $this; + } + + /** + * Resize image to fill all the space in the given dimension. Excess parts are cropped. + * + * @param Image $image + * @param int $newWidth Width in pixels. + * @param int $newHeight Height in pixels. + * + * @return self + */ + public function resizeFill(&$image, $newWidth, $newHeight) + { + $width = $image->getWidth(); + $height = $image->getHeight(); + $ratio = $width / $height; + + // Base optimum size on new width + $optimumWidth = $newWidth; + $optimumHeight = round($newWidth / $ratio); + + if (($optimumWidth < $newWidth) or ($optimumHeight < $newHeight)) { // Oops, where trying to fill and there are blank areas + // So base optimum size on height instead + $optimumWidth = $newHeight * $ratio; + $optimumHeight = $newHeight; + } + + $this->_resize($image, $optimumWidth, $optimumHeight); + $this->crop($image, $newWidth, $newHeight); // Trim excess parts + + return $this; + } + + /** + * Resize image to fit inside the given dimension. No part of the image is lost. + * + * @param Image $image + * @param int $newWidth Width in pixels. + * @param int $newHeight Height in pixels. + * + * @return self + */ + public function resizeFit(&$image, $newWidth, $newHeight) + { + + $width = $image->getWidth(); + $height = $image->getHeight(); + $ratio = $width / $height; + + // Try basing it on width first + $resizeWidth = $newWidth; + $resizeHeight = round($newWidth / $ratio); + + if (($resizeWidth > $newWidth) or ($resizeHeight > $newHeight)) { // Oops, either with or height does not fit + // So base on height instead + $resizeHeight = $newHeight; + $resizeWidth = $newHeight * $ratio; + } + + $this->_resize($image, $resizeWidth, $resizeHeight); + + return $this; + } + + /** + * Rotate an image counter-clockwise. + * + * @param Image $image + * @param int $angle The angle in degrees. + * @param Color|null $color The Color object containing the background color. + * + * @return EditorInterface An instance of image editor. + */ + public function rotate(&$image, $angle, $color = null) + { + + if ($image->isAnimated()) { // Ignore animated GIF for now + return $this; + } + + $color = ($color !== null) ? $color : new Color('#000000'); + list($r, $g, $b, $alpha) = $color->getRgba(); + + $image->getCore()->rotateImage(new \ImagickPixel("rgba($r, $g, $b, $alpha)"), $angle * -1); + + return $this; + } + + /** + * Save the image to an image format. + * + * @param Image $image + * @param string $file File path where to save the image. + * @param null|string $type Type of image. Can be null, "GIF", "PNG", or "JPEG". + * @param null|string $quality Quality of image. Applies to JPEG only. Accepts number 0 - 100 where 0 is lowest and 100 is the highest quality. Or null for default. + * @param bool|false $interlace Set to true for progressive JPEG. Applies to JPEG only. + * @param int $permission Default permission when creating non-existing target directory. + * + * @return Editor + * @throws \Exception + */ + public function save( $image, $file, $type = null, $quality = null, $interlace = false, $permission = 0755) + { + + if (null === $type) { + + $type = $this->_getImageTypeFromFileName($file); // Null given, guess type from file extension + if (ImageType::UNKNOWN === $type) { + $type = $image->getType(); // 0 result, use original image type + } + } + + $targetDir = dirname($file); // $file's directory + if (false === is_dir($targetDir)) { // Check if $file's directory exist + // Create and set default perms to 0755 + if ( ! mkdir($targetDir, $permission, true)) { + throw new \Exception(sprintf('Cannot create %s', $targetDir)); + } + } + + switch (strtoupper($type)) { + case ImageType::GIF : + $image->getCore()->writeImages($file, true); // Support animated image. Eg. GIF + break; + + case ImageType::PNG : + // PNG is lossless and does not need compression. Although GD allow values 0-9 (0 = no compression), we leave it alone. + $image->getCore()->setImageFormat($type); + $image->getCore()->writeImage($file); + break; + + default: // Defaults to jpeg + $quality = ($quality === null) ? 75 : $quality; // Default to 75 (GDs default) if null. + $quality = ($quality > 100) ? 100 : $quality; + $quality = ($quality <= 0) ? 1 : $quality; // Note: If 0 change it to 1. The lowest quality in Imagick is 1 whereas in GD its 0. + + if ($interlace) { + $image->getCore()->setImageInterlaceScheme(\Imagick::INTERLACE_JPEG); + } + $image->getCore()->setImageFormat($type); + $image->getCore()->setImageCompression(\Imagick::COMPRESSION_JPEG); + $image->getCore()->setImageCompressionQuality($quality); + $image->getCore()->writeImage($file); // Single frame image. Eg. JPEG + } + + return $this; + } + + /** + * Write text to image. + * + * @param Image $image + * @param string $text The text to be written. + * @param int $size The font size. Defaults to 12. + * @param int $x The distance from the left edge of the image to the left of the text. Defaults to 0. + * @param int $y The distance from the top edge of the image to the top of the text. Defaults to 12 (equal to font size) so that the text is placed within the image. + * @param Color $color The Color object. Default text color is black. + * @param string $font Full path to font file. If blank, will default to Liberation Sans font. + * @param int $angle Angle of text from 0 - 359. Defaults to 0. + * + * @return EditorInterface + * @throws \Exception + */ + public function text(&$image, $text, $size = 12, $x = 0, $y = 0, $color = null, $font = '', $angle = 0) + { + + if ($image->isAnimated()) { // Ignore animated GIF for now + return $this; + } + + $y += $size; + + $color = ($color !== null) ? $color : new Color('#000000'); + $font = ($font !== '') ? $font : Grafika::fontsDir() . DIRECTORY_SEPARATOR . 'LiberationSans-Regular.ttf'; + + list($r, $g, $b, $alpha) = $color->getRgba(); + + // Set up draw properties + $draw = new \ImagickDraw(); + // Text color + $draw->setFillColor(new \ImagickPixel("rgba($r, $g, $b, $alpha)")); + // Font properties + $draw->setFont($font); + $draw->setFontSize($size); + + // Write text + $image->getCore()->annotateImage( + $draw, + $x, + $y, + $angle, + $text + ); + + return $this; + } + + /** + * Calculate entropy based on histogram. + * + * @param array $hist Histogram returned by Image->histogram + * + * @return float|int + */ + private function _entropy($hist){ + $entropy = 0; + $hist_size = array_sum($hist['r']) + array_sum($hist['g']) + array_sum($hist['b']); + foreach($hist['r'] as $p){ + $p = $p / $hist_size; + $entropy += $p * log($p, 2); + } + foreach($hist['g'] as $p){ + $p = $p / $hist_size; + $entropy += $p * log($p, 2); + } + foreach($hist['b'] as $p){ + $p = $p / $hist_size; + $entropy += $p * log($p, 2); + } + return $entropy * -1; + } + + /** + * Crop based on entropy. + * + * @param Image $oldImage + * @param $cropW + * @param $cropH + * + * @return array + */ + private function _smartCrop($oldImage, $cropW, $cropH){ + $image = clone $oldImage; + + $this->resizeFit($image, 30, 30); + + $origW = $oldImage->getWidth(); + $origH = $oldImage->getHeight(); + $resizeW = $image->getWidth(); + $resizeH = $image->getHeight(); + + $smallCropW = round(($resizeW / $origW) * $cropW); + $smallCropH = round(($resizeH / $origH) * $cropH); + + $step = 1; + + for($y = 0; $y < $resizeH-$smallCropH; $y+=$step){ + for($x = 0; $x < $resizeW-$smallCropW; $x+=$step){ + $hist[$x.'-'.$y] = $this->_entropy($image->histogram(array(array($x, $y), array($smallCropW, $smallCropH)))); + } + if($resizeW-$smallCropW <= 0){ + $hist['0-'.$y] = $this->_entropy($image->histogram(array(array(0, 0), array($smallCropW, $smallCropH)))); + } + } + if($resizeH-$smallCropH <= 0){ + $hist['0-0'] = $this->_entropy($image->histogram(array(array(0, 0), array($smallCropW, $smallCropH)))); + } + + asort($hist); + end($hist); + $pos = key($hist); // last key + list($x, $y) = explode('-', $pos); + $x = round($x*($origW / $resizeW)); + $y = round($y*($origH / $resizeH)); + + return array($x,$y); + } + + /** + * Resize helper function. + * + * @param Image $image + * @param int $newWidth + * @param int $newHeight + * + * @return self + * @throws \Exception + */ + private function _resize(&$image, $newWidth, $newHeight) + { + + if ('GIF' == $image->getType()) { // Animated image. Eg. GIF + + $imagick = $image->getCore()->coalesceImages(); + + foreach ($imagick as $frame) { + $frame->resizeImage($newWidth, $newHeight, \Imagick::FILTER_BOX, 1, false); + $frame->setImagePage($newWidth, $newHeight, 0, 0); + } + + // Assign new image with frames + $image = new Image($imagick->deconstructImages(), $image->getImageFile(), $newWidth, $newHeight, + $image->getType()); + } else { // Single frame image. Eg. JPEG, PNG + + $image->getCore()->resizeImage($newWidth, $newHeight, \Imagick::FILTER_LANCZOS, 1, false); + // Assign new image + $image = new Image($image->getCore(), $image->getImageFile(), $newWidth, $newHeight, + $image->getType()); + } + + } + + /** + * Get image type base on file extension. + * + * @param int $imageFile File path to image. + * + * @return ImageType string Type of image. + */ + private function _getImageTypeFromFileName($imageFile) + { + $ext = strtolower((string)pathinfo($imageFile, PATHINFO_EXTENSION)); + + if ('jpg' == $ext or 'jpeg' == $ext) { + return ImageType::JPEG; + } else if ('gif' == $ext) { + return ImageType::GIF; + } else if ('png' == $ext) { + return ImageType::PNG; + } else { + return ImageType::UNKNOWN; + } + } + +} \ No newline at end of file diff --git a/niucloud/vendor/kosinix/grafika/src/Grafika/Imagick/Filter/Blur.php b/niucloud/vendor/kosinix/grafika/src/Grafika/Imagick/Filter/Blur.php new file mode 100644 index 000000000..080104442 --- /dev/null +++ b/niucloud/vendor/kosinix/grafika/src/Grafika/Imagick/Filter/Blur.php @@ -0,0 +1,37 @@ +amount = (int) $amount; + } + + /** + * @param Image $image + * + * @return Image + */ + public function apply( $image ) { + $image->getCore()->blurImage(1 * $this->amount, 0.5 * $this->amount); + return $image; + } + +} \ No newline at end of file diff --git a/niucloud/vendor/kosinix/grafika/src/Grafika/Imagick/Filter/Brightness.php b/niucloud/vendor/kosinix/grafika/src/Grafika/Imagick/Filter/Brightness.php new file mode 100644 index 000000000..a98d89daa --- /dev/null +++ b/niucloud/vendor/kosinix/grafika/src/Grafika/Imagick/Filter/Brightness.php @@ -0,0 +1,39 @@ += 0 >= 100 + + /** + * Brightness constructor. + * @param int $amount The amount of brightness to apply. >= -100 and <= -1 to darken. 0 for no change. >= 1 and <= 100 to brighten. + */ + public function __construct($amount) + { + $this->amount = (int) $amount; + } + + /** + * @param Image $image + * + * @return Image + */ + public function apply( $image ) { + $image->getCore()->modulateImage(100 + $this->amount, 100, 100); + return $image; + } + +} \ No newline at end of file diff --git a/niucloud/vendor/kosinix/grafika/src/Grafika/Imagick/Filter/Colorize.php b/niucloud/vendor/kosinix/grafika/src/Grafika/Imagick/Filter/Colorize.php new file mode 100644 index 000000000..5d4d11c57 --- /dev/null +++ b/niucloud/vendor/kosinix/grafika/src/Grafika/Imagick/Filter/Colorize.php @@ -0,0 +1,67 @@ += 0 >= 100 + /** + * @var int + */ + protected $green; // -100 >= 0 >= 100 + /** + * @var int + */ + protected $blue; // -100 >= 0 >= 100 + + /** + * Colorize constructor. + * @param int $red The amount of red colors. >= -100 and <= -1 to reduce. 0 for no change. >= 1 and <= 100 to add. + * @param int $green The amount of green colors. >= -100 and <= -1 to reduce. 0 for no change. >= 1 and <= 100 to add. + * @param int $blue The amount of blue colors. >= -100 and <= -1 to reduce. 0 for no change. >= 1 and <= 100 to add. + */ + public function __construct($red, $green, $blue) + { + $this->red = intval($red); + $this->green = intval($green); + $this->blue = intval($blue); + } + + /** + * @param Image $image + * + * @return Image + */ + public function apply( $image ) { + + // normalize colorize levels + $red = $this->normalizeLevel($this->red); + $green = $this->normalizeLevel($this->green); + $blue = $this->normalizeLevel($this->blue); + $qrange = $image->getCore()->getQuantumRange(); + + $image->getCore()->levelImage(0, $red, $qrange['quantumRangeLong'], \Imagick::CHANNEL_RED); + $image->getCore()->levelImage(0, $green, $qrange['quantumRangeLong'], \Imagick::CHANNEL_GREEN); + $image->getCore()->levelImage(0, $blue, $qrange['quantumRangeLong'], \Imagick::CHANNEL_BLUE); + + return $image; + } + + private function normalizeLevel($level) + { + if ($level > 0) { + return $level/5; + } else { + return ($level+100)/100; + } + } +} \ No newline at end of file diff --git a/niucloud/vendor/kosinix/grafika/src/Grafika/Imagick/Filter/Contrast.php b/niucloud/vendor/kosinix/grafika/src/Grafika/Imagick/Filter/Contrast.php new file mode 100644 index 000000000..149c4a1b1 --- /dev/null +++ b/niucloud/vendor/kosinix/grafika/src/Grafika/Imagick/Filter/Contrast.php @@ -0,0 +1,38 @@ += 0 >= 100 + + /** + * Contrast constructor. + * @param int $amount The amount of contrast to apply. >= -100 and <= -1 to reduce. 0 for no change. >= 1 and <= 100 to increase. + */ + public function __construct($amount) + { + $this->amount = (int) $amount; + } + + /** + * @param Image $image + * + * @return Image + */ + public function apply( $image ) { + + $image->getCore()->sigmoidalContrastImage($this->amount > 0, $this->amount / 4, 0); + return $image; + } + +} \ No newline at end of file diff --git a/niucloud/vendor/kosinix/grafika/src/Grafika/Imagick/Filter/Dither.php b/niucloud/vendor/kosinix/grafika/src/Grafika/Imagick/Filter/Dither.php new file mode 100644 index 000000000..da3952a15 --- /dev/null +++ b/niucloud/vendor/kosinix/grafika/src/Grafika/Imagick/Filter/Dither.php @@ -0,0 +1,168 @@ +type = $type; + } + + /** + * Apply filter. + * + * @param Image $image + * + * @return Image + * @throws \Exception + */ + public function apply( $image ) { + if ( $this->type === 'ordered' ) { + return $this->ordered( $image ); + } else if ( $this->type === 'diffusion' ) { + return $this->diffusion( $image ); + } + throw new \Exception( sprintf( 'Invalid dither type "%s".', $this->type ) ); + } + + /** + * Dither using error diffusion. + * + * @param Image $image + * + * @return Image + */ + private function diffusion( $image ){ + $pixels = array(); + + // Localize vars + $width = $image->getWidth(); + $height = $image->getHeight(); + + // Loop using image1 + $pixelIterator = $image->getCore()->getPixelIterator(); + foreach ($pixelIterator as $y => $rows) { /* Loop through pixel rows */ + foreach ( $rows as $x => $px ) { /* Loop through the pixels in the row (columns) */ + /** + * @var $px \ImagickPixel */ + $rgba = $px->getColor(); + + $gray = round($rgba['r'] * 0.3 + $rgba['g'] * 0.59 + $rgba['b'] * 0.11); + + if(isset($pixels[$x][$y])){ // Add errors to color if there are + $gray += $pixels[$x][$y]; + } + + if ( $gray <= 127 ) { // Determine if black or white. Also has the benefit of clipping excess val due to adding the error + $blackOrWhite = 0; + } else { + $blackOrWhite = 255; + } + + $oldPixel = $gray; + $newPixel = $blackOrWhite; + + // Current pixel + $px->setColor("rgb($newPixel,$newPixel,$newPixel)"); + + $qError = $oldPixel - $newPixel; // Quantization error + + // Propagate error on neighbor pixels + if ( $x + 1 < $width ) { + $pixels[$x+1][$y] = (isset($pixels[$x+1][$y]) ? $pixels[$x+1][$y] : 0) + ($qError * (7 / 16)); + } + + if ( $x - 1 > 0 and $y + 1 < $height ) { + $pixels[$x-1][$y+1] = (isset($pixels[$x-1][$y+1]) ? $pixels[$x-1][$y+1] : 0) + ($qError * (3 / 16)); + } + + if ( $y + 1 < $height ) { + $pixels[$x][$y+1] = (isset($pixels[$x][$y+1]) ? $pixels[$x][$y+1] : 0) + ($qError * (5 / 16)); + } + + if ( $x + 1 < $width and $y + 1 < $height ) { + $pixels[$x+1][$y+1] = (isset($pixels[$x+1][$y+1]) ? $pixels[$x+1][$y+1] : 0) + ($qError * (1 / 16)); + } + + } + $pixelIterator->syncIterator(); /* Sync the iterator, this is important to do on each iteration */ + } + + $type = $image->getType(); + $file = $image->getImageFile(); + $image = $image->getCore(); + + return new Image( $image, $file, $width, $height, $type ); // Create new image with updated core + + } + + /** + * Dither by applying a threshold map. + * + * @param Image $image + * + * @return Image + */ + private function ordered( $image ) { + + // Localize vars + $width = $image->getWidth(); + $height = $image->getHeight(); + + $thresholdMap = array( + array( 15, 135, 45, 165 ), + array( 195, 75, 225, 105 ), + array( 60, 180, 30, 150 ), + array( 240, 120, 210, 90 ) + ); + + // Loop using image1 + $pixelIterator = $image->getCore()->getPixelIterator(); + foreach ($pixelIterator as $y => $rows) { /* Loop through pixel rows */ + foreach ( $rows as $x => $px ) { /* Loop through the pixels in the row (columns) */ + /** + * @var $px \ImagickPixel */ + $rgba = $px->getColor(); + + $gray = round($rgba['r'] * 0.3 + $rgba['g'] * 0.59 + $rgba['b'] * 0.11); + + $threshold = $thresholdMap[ $x % 4 ][ $y % 4 ]; + $oldPixel = ( $gray + $threshold ) / 2; + if ( $oldPixel <= 127 ) { // Determine if black or white. Also has the benefit of clipping excess value + $newPixel = 0; + } else { + $newPixel = 255; + } + + // Current pixel + $px->setColor("rgb($newPixel,$newPixel,$newPixel)"); + + } + $pixelIterator->syncIterator(); /* Sync the iterator, this is important to do on each iteration */ + } + + $type = $image->getType(); + $file = $image->getImageFile(); + $image = $image->getCore(); + + return new Image( $image, $file, $width, $height, $type ); // Create new image with updated core + + } +} \ No newline at end of file diff --git a/niucloud/vendor/kosinix/grafika/src/Grafika/Imagick/Filter/Gamma.php b/niucloud/vendor/kosinix/grafika/src/Grafika/Imagick/Filter/Gamma.php new file mode 100644 index 000000000..bbdbae542 --- /dev/null +++ b/niucloud/vendor/kosinix/grafika/src/Grafika/Imagick/Filter/Gamma.php @@ -0,0 +1,38 @@ += 1.0 + + /** + * Gamma constructor. + * @param float $amount The amount of gamma correction to apply. >= 1.0 + */ + public function __construct($amount) + { + $this->amount = (float) $amount; + } + + /** + * @param Image $image + * + * @return Image + */ + public function apply( $image ) { + + $image->getCore()->gammaImage($this->amount); + return $image; + } + +} \ No newline at end of file diff --git a/niucloud/vendor/kosinix/grafika/src/Grafika/Imagick/Filter/Grayscale.php b/niucloud/vendor/kosinix/grafika/src/Grafika/Imagick/Filter/Grayscale.php new file mode 100644 index 000000000..5e1e2f4f1 --- /dev/null +++ b/niucloud/vendor/kosinix/grafika/src/Grafika/Imagick/Filter/Grayscale.php @@ -0,0 +1,23 @@ +getCore()->modulateImage(100, 0, 100); + return $image; + } + +} \ No newline at end of file diff --git a/niucloud/vendor/kosinix/grafika/src/Grafika/Imagick/Filter/Invert.php b/niucloud/vendor/kosinix/grafika/src/Grafika/Imagick/Filter/Invert.php new file mode 100644 index 000000000..7e9607f55 --- /dev/null +++ b/niucloud/vendor/kosinix/grafika/src/Grafika/Imagick/Filter/Invert.php @@ -0,0 +1,24 @@ +getCore()->negateImage(false); + return $image; + } + +} \ No newline at end of file diff --git a/niucloud/vendor/kosinix/grafika/src/Grafika/Imagick/Filter/Pixelate.php b/niucloud/vendor/kosinix/grafika/src/Grafika/Imagick/Filter/Pixelate.php new file mode 100644 index 000000000..0a74614c4 --- /dev/null +++ b/niucloud/vendor/kosinix/grafika/src/Grafika/Imagick/Filter/Pixelate.php @@ -0,0 +1,42 @@ += 1 + */ + protected $amount; + + /** + * Pixelate constructor. + * @param int $amount The size of pixelation. >= 1 + */ + public function __construct($amount) + { + $this->amount = (int) $amount; + } + + /** + * @param Image $image + * + * @return Image + */ + public function apply( $image ) { + + $size = $this->amount; + $width = $image->getWidth(); + $height = $image->getHeight(); + $image->getCore()->scaleImage(max(1, ($width / $size)), max(1, ($height / $size))); + $image->getCore()->scaleImage($width, $height); + return $image; + } + +} \ No newline at end of file diff --git a/niucloud/vendor/kosinix/grafika/src/Grafika/Imagick/Filter/Sharpen.php b/niucloud/vendor/kosinix/grafika/src/Grafika/Imagick/Filter/Sharpen.php new file mode 100644 index 000000000..df6ee126f --- /dev/null +++ b/niucloud/vendor/kosinix/grafika/src/Grafika/Imagick/Filter/Sharpen.php @@ -0,0 +1,37 @@ += 1 to <= 100 + */ + public function __construct($amount) + { + $this->amount = (int) $amount; + } + + /** + * @param Image $image + * + * @return Image + */ + public function apply( $image ) { + $image->getCore()->unsharpMaskImage(1, 1, $this->amount / 6.25, 0); + return $image; + } + +} \ No newline at end of file diff --git a/niucloud/vendor/kosinix/grafika/src/Grafika/Imagick/Filter/Sobel.php b/niucloud/vendor/kosinix/grafika/src/Grafika/Imagick/Filter/Sobel.php new file mode 100644 index 000000000..877bc45c7 --- /dev/null +++ b/niucloud/vendor/kosinix/grafika/src/Grafika/Imagick/Filter/Sobel.php @@ -0,0 +1,142 @@ +getWidth(); + $height = $image->getHeight(); + + // Loop + $pixelIterator = $image->getCore()->getPixelIterator(); + foreach ($pixelIterator as $y => $rows) { /* Loop through pixel rows */ + foreach ( $rows as $x => $px ) { /* Loop through the pixels in the row (columns) */ + + // row 0 + if ($x > 0 and $y > 0) { + $matrix[0][0] = $this->getColor($px, $pixels, $x - 1, $y - 1); + } else { + $matrix[0][0] = $this->getColor($px, $pixels, $x, $y); + } + + if ($y > 0) { + $matrix[1][0] = $this->getColor($px, $pixels, $x, $y - 1); + } else { + $matrix[1][0] = $this->getColor($px, $pixels, $x, $y); + } + + if ($x + 1 < $width and $y > 0) { + $matrix[2][0] = $this->getColor($px, $pixels, $x + 1, $y - 1); + } else { + $matrix[2][0] = $this->getColor($px, $pixels, $x, $y); + } + + // row 1 + if ($x > 0) { + $matrix[0][1] = $this->getColor($px, $pixels, $x - 1, $y); + } else { + $matrix[0][1] = $this->getColor($px, $pixels, $x, $y); + } + + if ($x + 1 < $width) { + $matrix[2][1] = $this->getColor($px, $pixels, $x + 1, $y); + } else { + $matrix[2][1] = $this->getColor($px, $pixels, $x, $y); + } + + // row 1 + if ($x > 0 and $y + 1 < $height) { + $matrix[0][2] = $this->getColor($px, $pixels, $x - 1, $y + 1); + } else { + $matrix[0][2] = $this->getColor($px, $pixels, $x, $y); + } + + if ($y + 1 < $height) { + $matrix[1][2] = $this->getColor($px, $pixels, $x, $y + 1); + } else { + $matrix[1][2] = $this->getColor($px, $pixels, $x, $y); + } + + if ($x + 1 < $width and $y + 1 < $height) { + $matrix[2][2] = $this->getColor($px, $pixels, $x + 1, $y + 1); + } else { + $matrix[2][2] = $this->getColor($px, $pixels, $x, $y); + } + + $edge = $this->convolve($matrix); + $edge = intval($edge / 2); + if ($edge > 255) { + $edge = 255; + } + + /** + * @var \ImagickPixel $px Current pixel. + */ + $finalPx[] = $edge; // R + $finalPx[] = $edge; // G + $finalPx[] = $edge; // B + + } + $pixelIterator->syncIterator(); /* Sync the iterator, this is important to do on each iteration */ + } + + $new = new \Imagick(); + $new->newImage($width, $height, new \ImagickPixel('black')); + /* Import the pixels into image. + width * height * strlen("RGB") must match count($pixels) */ + $new->importImagePixels(0, 0, $width, $height, "RGB", \Imagick::PIXEL_CHAR, $finalPx); + + $type = $image->getType(); + $file = $image->getImageFile(); + + return new Image( $new, $file, $width, $height, $type ); // Create new image with updated core + + } + + private function convolve($matrix) + { + $gx = $matrix[0][0] + ($matrix[2][0] * -1) + + ($matrix[0][1] * 2) + ($matrix[2][1] * -2) + + $matrix[0][2] + ($matrix[2][2] * -1); + + $gy = $matrix[0][0] + ($matrix[1][0] * 2) + $matrix[2][0] + + ($matrix[0][2] * -1) + ($matrix[1][2] * -2) + ($matrix[2][2] * -1); + + return sqrt(($gx * $gx) + ($gy * $gy)); + } + + /** + * @param \ImagickPixel $px + * @param array $pixels + * @param int $x + * @param int $y + * + * @return float + */ + private function getColor($px, &$pixels, $x, $y) + { + if (isset($pixels[$x][$y])) { + return $pixels[$x][$y]; + } + $rgba = $px->getColor(); + return $pixels[$x][$y] = round($rgba['r'] * 0.3 + $rgba['g'] * 0.59 + $rgba['b'] * 0.11); // gray + } +} \ No newline at end of file diff --git a/niucloud/vendor/kosinix/grafika/src/Grafika/Imagick/Image.php b/niucloud/vendor/kosinix/grafika/src/Grafika/Imagick/Image.php new file mode 100644 index 000000000..8055f7b6c --- /dev/null +++ b/niucloud/vendor/kosinix/grafika/src/Grafika/Imagick/Image.php @@ -0,0 +1,269 @@ +imagick = $imagick; + $this->imageFile = $imageFile; + $this->width = $width; + $this->height = $height; + $this->type = $type; + $this->animated = $animated; + } + + public function __clone() + { + $copy = clone $this->imagick; + + $this->imagick = $copy; + } + + /** + * Output a binary raw dump of an image in a specified format. + * + * @param string|ImageType $type Image format of the dump. + * + * @throws \Exception When unsupported type. + */ + public function blob( $type = 'PNG' ) { + $this->imagick->setImageFormat($type); + echo $this->imagick->getImageBlob(); + } + + /** + * @param $imageFile + * + * @return Image + * @throws \Exception + */ + public static function createFromFile( $imageFile ){ + $imageFile = realpath( $imageFile ); + + if ( ! file_exists( $imageFile ) ) { + throw new \Exception( sprintf('Could not open image file "%s"', $imageFile) ); + } + + $imagick = new \Imagick( realpath($imageFile) ); + $animated = false; + if ($imagick->getImageIterations() > 0) { + $animated = true; + } + + return new self( + $imagick, + $imageFile, + $imagick->getImageWidth(), + $imagick->getImageHeight(), + $imagick->getImageFormat(), + $animated + ); + } + + /** + * Create an Image from an instance of Imagick. + * + * @param \Imagick $imagick Instance of Imagick. + * + * @return Image + */ + public static function createFromCore( $imagick ) { + return new self( $imagick, '', $imagick->getImageWidth(), $imagick->getImageHeight(), $imagick->getImageFormat() ); + } + + /** + * Create a blank image. + * + * @param int $width Width in pixels. + * @param int $height Height in pixels. + * + * @return self + */ + public static function createBlank($width = 1, $height = 1){ + $imagick = new \Imagick(); + $imagick->newImage($width, $height, new \ImagickPixel('black')); + $imagick->setImageFormat('png'); // Default to PNG. + + return new self( $imagick, '', $imagick->getImageWidth(), $imagick->getImageHeight(), $imagick->getImageFormat()); + + } + + /** + * Get Imagick instance + * + * @return \Imagick + */ + public function getCore() { + return $this->imagick; + } + + /** + * Get image file path. + * + * @return string File path to image. + */ + public function getImageFile() { + return $this->imageFile; + } + + /** + * Get image width in pixels. + * + * @return int + */ + public function getWidth() { + return $this->width; + } + + /** + * Get image height in pixels. + * + * @return int + */ + public function getHeight() { + return $this->height; + } + + /** + * Get image type. + * + * @return string + */ + public function getType() { + return $this->type; + } + + /** + * Get histogram from an entire image or its sub-region. + * + * @param array|null $slice Array of slice information. array( array( 0,0), array(100,50)) means x,y is 0,0 and width,height is 100,50 + * + * @return array Returns array containing RGBA bins array('r'=>array(), 'g'=>array(), 'b'=>array(), 'a'=>array()) + */ + public function histogram($slice = null) + { + + if(null === $slice){ + $sliceX = 0; + $sliceY = 0; + $sliceW = $this->getWidth(); + $sliceH = $this->getHeight(); + } else { + $sliceX = $slice[0][0]; + $sliceY = $slice[0][1]; + $sliceW = $slice[1][0]; + $sliceH = $slice[1][1]; + } + + $rBin = array(); + $gBin = array(); + $bBin = array(); + $aBin = array(); + + // Loop using image + $pixelIterator = $this->getCore()->getPixelIterator(); + foreach ($pixelIterator as $y => $rows) { /* Loop through pixel rows */ + if($y >= $sliceY and $y < $sliceY+$sliceH) { + foreach ($rows as $x => $px) { /* Loop through the pixels in the row (columns) */ + if($x >= $sliceX and $x < $sliceX+$sliceW) { + /** + * @var $px \ImagickPixel */ + $pixel = $px->getColor(); + $r = $pixel['r']; + $g = $pixel['g']; + $b = $pixel['b']; + $a = $pixel['a']; + + if ( ! isset($rBin[$r])) { + $rBin[$r] = 1; + } else { + $rBin[$r]++; + } + + if ( ! isset($gBin[$g])) { + $gBin[$g] = 1; + } else { + $gBin[$g]++; + } + + if ( ! isset($bBin[$b])) { + $bBin[$b] = 1; + } else { + $bBin[$b]++; + } + + if ( ! isset($aBin[$a])) { + $aBin[$a] = 1; + } else { + $aBin[$a]++; + } + } + } + } + } + return array( + 'r' => $rBin, + 'g' => $gBin, + 'b' => $bBin, + 'a' => $aBin + ); + } + + /** + * Returns animated flag. + * + * @return bool True if animated GIF. + */ + public function isAnimated() { + return $this->animated; + } + +} \ No newline at end of file diff --git a/niucloud/vendor/kosinix/grafika/src/Grafika/Imagick/ImageHash/AverageHash.php b/niucloud/vendor/kosinix/grafika/src/Grafika/Imagick/ImageHash/AverageHash.php new file mode 100644 index 000000000..8a490cdd0 --- /dev/null +++ b/niucloud/vendor/kosinix/grafika/src/Grafika/Imagick/ImageHash/AverageHash.php @@ -0,0 +1,37 @@ +resizeExact($image, $width, $height); // Resize to exactly 9x8 + $imagick = $image->getCore(); + + // Build hash + $hash = ''; + for ($y = 0; $y < $height; $y++) { + // Get the pixel value for the leftmost pixel. + $rgba = $imagick->getImagePixelColor(0, $y)->getColor(); + + $left = floor(($rgba['r'] + $rgba['g'] + $rgba['b']) / 3); + for ($x = 1; $x < $width; $x++) { + // Get the pixel value for each pixel starting from position 1. + $rgba = $imagick->getImagePixelColor($x, $y)->getColor(); + $right = floor(($rgba['r'] + $rgba['g'] + $rgba['b']) / 3); + // Each hash bit is set based on whether the left pixel is brighter than the right pixel. + if ($left > $right) { + $hash .= '1'; + } else { + $hash .= '0'; + } + // Prepare the next loop. + $left = $right; + } + } + $editor->free( $image ); + return $hash; + } +} \ No newline at end of file diff --git a/niucloud/vendor/kosinix/grafika/src/Grafika/Position.php b/niucloud/vendor/kosinix/grafika/src/Grafika/Position.php new file mode 100644 index 000000000..bd09f4071 --- /dev/null +++ b/niucloud/vendor/kosinix/grafika/src/Grafika/Position.php @@ -0,0 +1,146 @@ +position = $position; + $this->offsetX = $offsetX; + $this->offsetY = $offsetY; + } + + /** + * Translate the textual position + offsets into x,y values. + * + * @param int $canvasWidth Width of canvas. + * @param int $canvasHeight Height of canvas. + * @param int $imageWidth Width of image/object added. + * @param int $imageHeight Height of image/object added. + * + * @return array Array of X and Y coordinates: array($x, $y). + * @throws \Exception When invalid position. + */ + public function getXY($canvasWidth, $canvasHeight, $imageWidth, $imageHeight){ + if ( self::TOP_LEFT === $this->position) { + $x = 0; + $y = 0; + } else if ( self::TOP_CENTER === $this->position) { + $x = (int)round(($canvasWidth / 2) - ($imageWidth / 2)); + $y = 0; + } else if ( self::TOP_RIGHT === $this->position) { + $x = $canvasWidth - $imageWidth; + $y = 0; + } else if ( self::CENTER_LEFT === $this->position) { + $x = 0; + $y = (int)round(($canvasHeight / 2) - ($imageHeight / 2)); + } else if ( self::CENTER_RIGHT === $this->position) { + $x = $canvasWidth - $imageWidth; + $y = (int)round(($canvasHeight / 2) - ($imageHeight / 2)); + } else if ( self::BOTTOM_LEFT === $this->position) { + $x = 0; + $y = $canvasHeight - $imageHeight; + } else if ( self::BOTTOM_CENTER === $this->position) { + $x = (int)round(($canvasWidth / 2) - ($imageWidth / 2)); + $y = $canvasHeight - $imageHeight; + } else if ( self::BOTTOM_RIGHT === $this->position) { + $x = $canvasWidth - $imageWidth; + $y = $canvasHeight - $imageHeight; + } else if ( self::CENTER === $this->position) { + $x = (int)round(($canvasWidth / 2) - ($imageWidth / 2)); + $y = (int)round(($canvasHeight / 2) - ($imageHeight / 2)); + } else { + throw new \Exception( sprintf( 'Invalid position "%s".', $this->position ) ); + } + + return array( + $x + $this->offsetX, + $y + $this->offsetY + ); + } + + /** + * @return string + */ + public function getText() { + return $this->position; + } + + /** + * @return int + */ + public function getOffsetY() { + return $this->offsetY; + } + + /** + * @return int + */ + public function getOffsetX() { + return $this->offsetX; + } + + +} \ No newline at end of file diff --git a/niucloud/vendor/kosinix/grafika/src/autoloader.php b/niucloud/vendor/kosinix/grafika/src/autoloader.php new file mode 100644 index 000000000..4d46a7d2e --- /dev/null +++ b/niucloud/vendor/kosinix/grafika/src/autoloader.php @@ -0,0 +1,10 @@ + 'think\\captcha\\CaptchaService', 1 => 'think\\app\\Service', 2 => 'think\\queue\\Service', 3 => 'think\\trace\\Service', + 4 => 'yunwuxin\\cron\\Service', ); \ No newline at end of file diff --git a/niucloud/vendor/yunwuxin/think-cron/.gitignore b/niucloud/vendor/yunwuxin/think-cron/.gitignore new file mode 100644 index 000000000..35ec8a0f0 --- /dev/null +++ b/niucloud/vendor/yunwuxin/think-cron/.gitignore @@ -0,0 +1,3 @@ +.idea/ +composer.lock +vendor/ \ No newline at end of file diff --git a/niucloud/vendor/yunwuxin/think-cron/README.md b/niucloud/vendor/yunwuxin/think-cron/README.md new file mode 100644 index 000000000..2da9a1855 --- /dev/null +++ b/niucloud/vendor/yunwuxin/think-cron/README.md @@ -0,0 +1,66 @@ +# think-cron 计划任务 + +## 安装方法 +``` +composer require yunwuxin/think-cron +``` + +## 使用方法 + +### 创建任务类 + +``` +daily(); //设置任务的周期,每天执行一次,更多的方法可以查看源代码,都有注释 + } + + /** + * 执行任务 + * @return mixed + */ + protected function execute() + { + //...具体的任务执行 + } +} + +``` + +### 配置 +> 配置文件位于 application/extra/cron.php + +``` +return [ + 'tasks' => [ + \app\task\DemoTask::class, //任务的完整类名 + ] +]; +``` + +### 任务监听 + +#### 两种方法: + +> 方法一 (推荐) + +起一个常驻进程,可以配合supervisor使用 +~~~ +php think cron:schedule +~~~ + +> 方法二 + +在系统的计划任务里添加 +~~~ +* * * * * php /path/to/think cron:run >> /dev/null 2>&1 +~~~ diff --git a/niucloud/vendor/yunwuxin/think-cron/composer.json b/niucloud/vendor/yunwuxin/think-cron/composer.json new file mode 100644 index 000000000..044d966c6 --- /dev/null +++ b/niucloud/vendor/yunwuxin/think-cron/composer.json @@ -0,0 +1,42 @@ +{ + "name": "yunwuxin/think-cron", + "description": "计划任务", + "license": "Apache-2.0", + "authors": [ + { + "name": "yunwuxin", + "email": "448901948@qq.com" + } + ], + "require": { + "topthink/framework": "^6.0", + "symfony/process": "^4.4|^5.0", + "nesbot/carbon": "^2.28", + "dragonmantank/cron-expression": "^3.0" + }, + "autoload": { + "psr-4": { + "yunwuxin\\cron\\": "src/cron" + } + }, + "extra": { + "think": { + "config": { + "cron": "src/config.php" + }, + "services": [ + "yunwuxin\\cron\\Service" + ] + } + }, + "require-dev": { + "topthink/think-swoole": "^4.0" + }, + "config": { + "preferred-install": "dist", + "platform-check": false, + "platform": { + "ext-swoole": "4.6.0" + } + } +} diff --git a/niucloud/vendor/yunwuxin/think-cron/src/config.php b/niucloud/vendor/yunwuxin/think-cron/src/config.php new file mode 100644 index 000000000..e5a24290a --- /dev/null +++ b/niucloud/vendor/yunwuxin/think-cron/src/config.php @@ -0,0 +1,5 @@ + [] +]; \ No newline at end of file diff --git a/niucloud/vendor/yzh52521/schedule/src/console/ManagesFrequencies.php b/niucloud/vendor/yunwuxin/think-cron/src/cron/ManagesFrequencies.php similarity index 59% rename from niucloud/vendor/yzh52521/schedule/src/console/ManagesFrequencies.php rename to niucloud/vendor/yunwuxin/think-cron/src/cron/ManagesFrequencies.php index 4af43a2a3..1b78ebae9 100644 --- a/niucloud/vendor/yzh52521/schedule/src/console/ManagesFrequencies.php +++ b/niucloud/vendor/yunwuxin/think-cron/src/cron/ManagesFrequencies.php @@ -1,18 +1,18 @@ expression = $expression; @@ -20,10 +20,10 @@ trait ManagesFrequencies } /** - * Schedule the event to run between start and end time. + * 设置区间时间 * - * @param string $startTime - * @param string $endTime + * @param string $startTime + * @param string $endTime * @return $this */ public function between($startTime, $endTime) @@ -32,10 +32,10 @@ trait ManagesFrequencies } /** - * Schedule the event to not run between start and end time. + * 排除区间时间 * - * @param string $startTime - * @param string $endTime + * @param string $startTime + * @param string $endTime * @return $this */ public function unlessBetween($startTime, $endTime) @@ -43,13 +43,6 @@ trait ManagesFrequencies return $this->skip($this->inTimeInterval($startTime, $endTime)); } - /** - * Schedule the event to run between start and end time. - * - * @param string $startTime - * @param string $endTime - * @return \Closure - */ private function inTimeInterval($startTime, $endTime) { return function () use ($startTime, $endTime) { @@ -62,57 +55,7 @@ trait ManagesFrequencies } /** - * Schedule the event to run every minute. - * - * @return $this - */ - public function everyMinute() - { - return $this->spliceIntoPosition(1, '*'); - } - - /** - * Schedule the event to run every five minutes. - * - * @return $this - */ - public function everyFiveMinutes() - { - return $this->spliceIntoPosition(1, '*/5'); - } - - /** - * Schedule the event to run every ten minutes. - * - * @return $this - */ - public function everyTenMinutes() - { - return $this->spliceIntoPosition(1, '*/10'); - } - - /** - * Schedule the event to run every fifteen minutes. - * - * @return $this - */ - public function everyFifteenMinutes() - { - return $this->spliceIntoPosition(1, '*/15'); - } - - /** - * Schedule the event to run every thirty minutes. - * - * @return $this - */ - public function everyThirtyMinutes() - { - return $this->spliceIntoPosition(1, '0,30'); - } - - /** - * Schedule the event to run hourly. + * 按小时执行 * * @return $this */ @@ -122,9 +65,9 @@ trait ManagesFrequencies } /** - * Schedule the event to run hourly at a given offset in the hour. + * 按小时延期执行 * - * @param int $offset + * @param int $offset * @return $this */ public function hourlyAt($offset) @@ -133,20 +76,20 @@ trait ManagesFrequencies } /** - * Schedule the event to run daily. + * 按天执行 * * @return $this */ public function daily() { return $this->spliceIntoPosition(1, 0) - ->spliceIntoPosition(2, 0); + ->spliceIntoPosition(2, 0); } /** - * Schedule the command at a given time. + * 指定时间执行 * - * @param string $time + * @param string $time * @return $this */ public function at($time) @@ -155,9 +98,9 @@ trait ManagesFrequencies } /** - * Schedule the event to run daily at a given time (10:00, 19:30, etc). + * 指定时间执行 * - * @param string $time + * @param string $time * @return $this */ public function dailyAt($time) @@ -165,26 +108,26 @@ trait ManagesFrequencies $segments = explode(':', $time); return $this->spliceIntoPosition(2, (int) $segments[0]) - ->spliceIntoPosition(1, count($segments) == 2 ? (int) $segments[1] : '0'); + ->spliceIntoPosition(1, count($segments) == 2 ? (int) $segments[1] : '0'); } /** - * Schedule the event to run twice daily. + * 每天执行两次 * - * @param int $first - * @param int $second + * @param int $first + * @param int $second * @return $this */ public function twiceDaily($first = 1, $second = 13) { - $hours = $first.','.$second; + $hours = $first . ',' . $second; return $this->spliceIntoPosition(1, 0) - ->spliceIntoPosition(2, $hours); + ->spliceIntoPosition(2, $hours); } /** - * Schedule the event to run only on weekdays. + * 工作日执行 * * @return $this */ @@ -194,7 +137,7 @@ trait ManagesFrequencies } /** - * Schedule the event to run only on weekends. + * 周末执行 * * @return $this */ @@ -204,7 +147,7 @@ trait ManagesFrequencies } /** - * Schedule the event to run only on Mondays. + * 星期一执行 * * @return $this */ @@ -214,7 +157,7 @@ trait ManagesFrequencies } /** - * Schedule the event to run only on Tuesdays. + * 星期二执行 * * @return $this */ @@ -224,7 +167,7 @@ trait ManagesFrequencies } /** - * Schedule the event to run only on Wednesdays. + * 星期三执行 * * @return $this */ @@ -234,7 +177,7 @@ trait ManagesFrequencies } /** - * Schedule the event to run only on Thursdays. + * 星期四执行 * * @return $this */ @@ -244,7 +187,7 @@ trait ManagesFrequencies } /** - * Schedule the event to run only on Fridays. + * 星期五执行 * * @return $this */ @@ -254,7 +197,7 @@ trait ManagesFrequencies } /** - * Schedule the event to run only on Saturdays. + * 星期六执行 * * @return $this */ @@ -264,7 +207,7 @@ trait ManagesFrequencies } /** - * Schedule the event to run only on Sundays. + * 星期天执行 * * @return $this */ @@ -274,22 +217,22 @@ trait ManagesFrequencies } /** - * Schedule the event to run weekly. + * 按周执行 * * @return $this */ public function weekly() { return $this->spliceIntoPosition(1, 0) - ->spliceIntoPosition(2, 0) - ->spliceIntoPosition(5, 0); + ->spliceIntoPosition(2, 0) + ->spliceIntoPosition(5, 0); } /** - * Schedule the event to run weekly on a given day and time. + * 指定每周的时间执行 * - * @param int $day - * @param string $time + * @param int $day + * @param string $time * @return $this */ public function weeklyOn($day, $time = '0:0') @@ -300,22 +243,22 @@ trait ManagesFrequencies } /** - * Schedule the event to run monthly. + * 按月执行 * * @return $this */ public function monthly() { return $this->spliceIntoPosition(1, 0) - ->spliceIntoPosition(2, 0) - ->spliceIntoPosition(3, 1); + ->spliceIntoPosition(2, 0) + ->spliceIntoPosition(3, 1); } /** - * Schedule the event to run monthly on a given day and time. + * 指定每月的执行时间 * - * @param int $day - * @param string $time + * @param int $day + * @param string $time * @return $this */ public function monthlyOn($day = 1, $time = '0:0') @@ -326,15 +269,15 @@ trait ManagesFrequencies } /** - * Schedule the event to run twice monthly. + * 每月执行两次 * - * @param int $first - * @param int $second + * @param int $first + * @param int $second * @return $this */ public function twiceMonthly($first = 1, $second = 16) { - $days = $first.','.$second; + $days = $first . ',' . $second; return $this->spliceIntoPosition(1, 0) ->spliceIntoPosition(2, 0) @@ -342,35 +285,75 @@ trait ManagesFrequencies } /** - * Schedule the event to run quarterly. + * 按季度执行 * * @return $this */ public function quarterly() { return $this->spliceIntoPosition(1, 0) - ->spliceIntoPosition(2, 0) - ->spliceIntoPosition(3, 1) - ->spliceIntoPosition(4, '1-12/3'); + ->spliceIntoPosition(2, 0) + ->spliceIntoPosition(3, 1) + ->spliceIntoPosition(4, '*/3'); } /** - * Schedule the event to run yearly. + * 按年执行 * * @return $this */ public function yearly() { return $this->spliceIntoPosition(1, 0) - ->spliceIntoPosition(2, 0) - ->spliceIntoPosition(3, 1) - ->spliceIntoPosition(4, 1); + ->spliceIntoPosition(2, 0) + ->spliceIntoPosition(3, 1) + ->spliceIntoPosition(4, 1); } /** - * Set the days of the week the command should run on. + * 每分钟执行 * - * @param array|mixed $days + * @return $this + */ + public function everyMinute() + { + return $this->spliceIntoPosition(1, '*'); + } + + /** + * 每5分钟执行 + * + * @return $this + */ + public function everyFiveMinutes() + { + return $this->spliceIntoPosition(1, '*/5'); + } + + /** + * 每10分钟执行 + * + * @return $this + */ + public function everyTenMinutes() + { + return $this->spliceIntoPosition(1, '*/10'); + } + + /** + * 每30分钟执行 + * + * @return $this + */ + public function everyThirtyMinutes() + { + return $this->spliceIntoPosition(1, '0,30'); + } + + /** + * 按周设置天执行 + * + * @param array|mixed $days * @return $this */ public function days($days) @@ -381,9 +364,9 @@ trait ManagesFrequencies } /** - * Set the timezone the date should be evaluated on. + * 设置时区 * - * @param \DateTimeZone|string $timezone + * @param string $timezone * @return $this */ public function timezone($timezone) @@ -393,19 +376,12 @@ trait ManagesFrequencies return $this; } - /** - * Splice the given value into the given position of the expression. - * - * @param int $position - * @param string $value - * @return $this - */ protected function spliceIntoPosition($position, $value) { $segments = explode(' ', $this->expression); $segments[$position - 1] = $value; - return $this->cron(implode(' ', $segments)); + return $this->expression(implode(' ', $segments)); } } diff --git a/niucloud/vendor/yunwuxin/think-cron/src/cron/Scheduler.php b/niucloud/vendor/yunwuxin/think-cron/src/cron/Scheduler.php new file mode 100644 index 000000000..29bc74963 --- /dev/null +++ b/niucloud/vendor/yunwuxin/think-cron/src/cron/Scheduler.php @@ -0,0 +1,99 @@ +app = $app; +// $this->tasks = $app->config->get('cron.tasks', []); + $this->tasks = (new CoreScheduleService())->getList(); + + $this->cache = $app->cache->store(); + } + + public function run() + { + $this->startedAt = Carbon::now(); + $file = root_path('runtime').'.schedule'; + file_put_contents($file, time()); + $taskClass = 'app\command\schedule\Schedule'; + foreach ($this->tasks as $task_value) { + + if (is_subclass_of($taskClass, Task::class)) { + /** @var Task $task */ + $task = $this->app->invokeClass($taskClass, [$task_value]); + if ($task->isDue()) { + + if (!$task->filtersPass()) { + continue; + } + + if ($task->onOneServer) { + $this->runSingleServerTask($task); + } else { + $this->runTask($task); + } + + $this->app->event->trigger(new TaskProcessed($task)); + } + } + } + } + + /** + * @param $task Task + * @return bool + */ + protected function serverShouldRun($task) + { + $key = $task->mutexName() . $this->startedAt->format('Hi'); + if ($this->cache->has($key)) { + return false; + } + $this->cache->set($key, true, 60); + return true; + } + + protected function runSingleServerTask($task) + { + if ($this->serverShouldRun($task)) { + $this->runTask($task); + } else { + $this->app->event->trigger(new TaskSkipped($task)); + } + } + + /** + * @param $task Task + */ + protected function runTask($task) + { + try { + $task->run(); + } catch (Exception $e) { + $this->app->event->trigger(new TaskFailed($task, $e)); + } + } +} diff --git a/niucloud/vendor/yunwuxin/think-cron/src/cron/Service.php b/niucloud/vendor/yunwuxin/think-cron/src/cron/Service.php new file mode 100644 index 000000000..33e383383 --- /dev/null +++ b/niucloud/vendor/yunwuxin/think-cron/src/cron/Service.php @@ -0,0 +1,30 @@ +commands([ + Run::class, + Schedule::class, + ]); + + $this->app->event->listen('swoole.init', function (Manager $manager) { + $manager->addWorker(function () use ($manager) { + Timer::tick(60 * 1000, function () use ($manager) { + $manager->runWithBarrier([$manager, 'runInSandbox'], function (Scheduler $scheduler) { + $scheduler->run(); + }); + }); + }, "cron"); + }); + } +} diff --git a/niucloud/vendor/yunwuxin/think-cron/src/cron/Task.php b/niucloud/vendor/yunwuxin/think-cron/src/cron/Task.php new file mode 100644 index 000000000..de4e08a84 --- /dev/null +++ b/niucloud/vendor/yunwuxin/think-cron/src/cron/Task.php @@ -0,0 +1,176 @@ +app = $app; +// $this->cache = $cache; + $this->cache = $app->cache->store(); + $this->vars = $vars; + $this->configure(); + } + + /** + * 是否到期执行 + * @return bool + */ + public function isDue() + { + $cronExpression = new CronExpression($this->expression); + + return $cronExpression->isDue('now', $this->timezone); + } + + /** + * 配置任务 + */ + protected function configure() + { + } + + /** + * 执行任务 + */ + protected function execute() + { + $this->app->invoke([$this, 'handle'], [], true); + } + + final public function run() + { + if ($this->withoutOverlapping && + !$this->createMutex()) { + return; + } + + register_shutdown_function(function () { + $this->removeMutex(); + }); + + try { + $this->execute(); + } finally { + $this->removeMutex(); + } + } + + /** + * 过滤 + * @return bool + */ + public function filtersPass() + { + foreach ($this->filters as $callback) { + if (!call_user_func($callback)) { + return false; + } + } + + foreach ($this->rejects as $callback) { + if (call_user_func($callback)) { + return false; + } + } + + return true; + } + + /** + * 任务标识 + */ + public function mutexName() + { + return 'task-' . sha1(static::class); + } + + protected function removeMutex() + { + return $this->cache->delete($this->mutexName()); + } + + protected function createMutex() + { + $name = $this->mutexName(); + + return $this->cache->set($name, time(), $this->expiresAt); + } + + protected function existsMutex() + { + if ($this->cache->has($this->mutexName())) { + $mutex = $this->cache->get($this->mutexName()); + return $mutex + $this->expiresAt > time(); + } + return false; + } + + public function when(Closure $callback) + { + $this->filters[] = $callback; + + return $this; + } + + public function skip(Closure $callback) + { + $this->rejects[] = $callback; + + return $this; + } + + public function withoutOverlapping($expiresAt = 1440) + { + $this->withoutOverlapping = true; + + $this->expiresAt = $expiresAt; + + return $this->skip(function () { + return $this->existsMutex(); + }); + } + + public function onOneServer() + { + $this->onOneServer = true; + + return $this; + } +} diff --git a/niucloud/vendor/yunwuxin/think-cron/src/cron/command/Run.php b/niucloud/vendor/yunwuxin/think-cron/src/cron/command/Run.php new file mode 100644 index 000000000..7b3718b6c --- /dev/null +++ b/niucloud/vendor/yunwuxin/think-cron/src/cron/command/Run.php @@ -0,0 +1,56 @@ +startedAt = Carbon::now(); + $this->setName('cron:run'); + } + + public function handle(Scheduler $scheduler) + { + $this->listenForEvents(); + + $scheduler->run(); + } + + /** + * 注册事件 + */ + protected function listenForEvents() + { + $this->app->event->listen(TaskProcessed::class, function (TaskProcessed $event) { + $this->output->writeln("Task {$event->getName()} run at " . Carbon::now()); + }); + + $this->app->event->listen(TaskSkipped::class, function (TaskSkipped $event) { + $this->output->writeln('Skipping task (has already run on another server): ' . $event->getName()); + }); + + $this->app->event->listen(TaskFailed::class, function (TaskFailed $event) { + $this->output->writeln("Task {$event->getName()} failed at " . Carbon::now()); + + /** @var Handle $handle */ + $handle = $this->app->make(Handle::class); + + $handle->renderForConsole($this->output, $event->exception); + + $handle->report($event->exception); + }); + } + +} diff --git a/niucloud/vendor/yunwuxin/think-cron/src/cron/command/Schedule.php b/niucloud/vendor/yunwuxin/think-cron/src/cron/command/Schedule.php new file mode 100644 index 000000000..f1eb8dd9c --- /dev/null +++ b/niucloud/vendor/yunwuxin/think-cron/src/cron/command/Schedule.php @@ -0,0 +1,33 @@ +setName('cron:schedule'); + } + + protected function execute(Input $input, Output $output) + { + if ('\\' == DIRECTORY_SEPARATOR) { + $command = 'start /B "Niucloud Schedule" "' . PHP_BINARY . '" think cron:run'; + } else { + $command = 'nohup "' . PHP_BINARY . '" think cron:run >> /dev/null 2>&1 &'; + } + + $process = Process::fromShellCommandline($command); + + while (true) { + $process->run(); + sleep(60); + } + } +} diff --git a/niucloud/vendor/yunwuxin/think-cron/src/cron/event/TaskEvent.php b/niucloud/vendor/yunwuxin/think-cron/src/cron/event/TaskEvent.php new file mode 100644 index 000000000..33acb322b --- /dev/null +++ b/niucloud/vendor/yunwuxin/think-cron/src/cron/event/TaskEvent.php @@ -0,0 +1,20 @@ +task = $task; + } + + public function getName() + { + return get_class($this->task); + } +} diff --git a/niucloud/vendor/yunwuxin/think-cron/src/cron/event/TaskFailed.php b/niucloud/vendor/yunwuxin/think-cron/src/cron/event/TaskFailed.php new file mode 100644 index 000000000..a56756111 --- /dev/null +++ b/niucloud/vendor/yunwuxin/think-cron/src/cron/event/TaskFailed.php @@ -0,0 +1,14 @@ +exception = $exception; + } +} diff --git a/niucloud/vendor/yunwuxin/think-cron/src/cron/event/TaskProcessed.php b/niucloud/vendor/yunwuxin/think-cron/src/cron/event/TaskProcessed.php new file mode 100644 index 000000000..44b74eac9 --- /dev/null +++ b/niucloud/vendor/yunwuxin/think-cron/src/cron/event/TaskProcessed.php @@ -0,0 +1,8 @@ +setName('schedule:run'); - } - - protected function execute(Input $input, Output $output) - { - //每天的上午十点和晚上八点执行这个命令 - $this->command('test')->twiceDaily(10, 20); - parent::execute($input, $output); - } -} -``` - -继续运行指令 -``` -php think make:command Test test -``` - -第二步,配置config/console.php文件 - -``` - [ - 'schedule:run'=>\app\command\Schedule::class, - 'test' => 'app\command\Test', - ] -]; -``` - -第三步,您应该在crontab中添加以下命令: - -``` -* * * * * php /path/to/think schedule:run >> /dev/null 2>&1 -``` - -时间表范例 -此扩展支持Laravel Schedule的所有功能,环境和维护模式除外。 -Scheduling Closures -``` -$this->call(function() -{ - // Do some task... - -})->hourly(); -``` -Running command of your application -``` -$this->command('migrate')->cron('* * * * *'); -``` -Frequent Jobs -``` -$this->command('foo')->everyFiveMinutes(); - -$this->command('foo')->everyTenMinutes(); - -$this->command('foo')->everyThirtyMinutes(); -``` -Daily Jobs -``` -$this->command('foo')->daily(); -``` -Daily Jobs At A Specific Time (24 Hour Time) - -``` -$this->command('foo')->dailyAt('15:00'); -``` -Twice Daily Jobs -``` -$this->command('foo')->twiceDaily(); -``` -Job That Runs Every Weekday -``` -$this->command('foo')->weekdays(); -``` -Weekly Jobs -``` -$this->command('foo')->weekly(); - -// Schedule weekly job for specific day (0-6) and time... -$this->command('foo')->weeklyOn(1, '8:00'); -``` - -Monthly Jobs -``` -$this->command('foo')->monthly(); -``` -Job That Runs On Specific Days -``` -$this->command('foo')->mondays(); -$this->command('foo')->tuesdays(); -$this->command('foo')->wednesdays(); -$this->command('foo')->thursdays(); -$this->command('foo')->fridays(); -$this->command('foo')->saturdays(); -$this->command('foo')->sundays(); -``` - -Only Allow Job To Run When Callback Is True -``` -$this->command('foo')->monthly()->when(function() -{ - return true; -}); -``` - diff --git a/niucloud/vendor/yzh52521/schedule/composer.json b/niucloud/vendor/yzh52521/schedule/composer.json deleted file mode 100644 index 179a3746f..000000000 --- a/niucloud/vendor/yzh52521/schedule/composer.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "name": "yzh52521/schedule", - "description": "task schedule,schedule,thinkphp schedule,任务调度", - "keywords": [ - "schedule", - "task schedule", - "thinkphp", - "thinkphp6", - "thinkphp5.1", - "think-schedule" - ], - "type": "library", - "license": "MIT", - "minimum-stability": "dev", - "require": { - "php": ">=7.1", - "nesbot/carbon": "^2.0" - }, - "autoload": { - "psr-4": { - "schedule\\": "src/" - } - } -} diff --git a/niucloud/vendor/yzh52521/schedule/src/Run.php b/niucloud/vendor/yzh52521/schedule/src/Run.php deleted file mode 100644 index 94314b18c..000000000 --- a/niucloud/vendor/yzh52521/schedule/src/Run.php +++ /dev/null @@ -1,26 +0,0 @@ -setName('schedule:run'); - } - - protected function execute(Input $input, Output $output) - { - //每天的上午十点和晚上八点执行这个命令 - $this->command('test')->twiceDaily(10, 20); - - parent::execute($input, $output); - } -} diff --git a/niucloud/vendor/yzh52521/schedule/src/console/AbstractField.php b/niucloud/vendor/yzh52521/schedule/src/console/AbstractField.php deleted file mode 100644 index 90faa46b8..000000000 --- a/niucloud/vendor/yzh52521/schedule/src/console/AbstractField.php +++ /dev/null @@ -1,148 +0,0 @@ -isIncrementsOfRanges($value)) { - return $this->isInIncrementsOfRanges($dateValue, $value); - } elseif ($this->isRange($value)) { - return $this->isInRange($dateValue, $value); - } - - return $value == '*' || $dateValue == $value; - } - - /** - * Check if a value is a range - * - * @param string $value Value to test - * - * @return bool - */ - public function isRange($value) - { - return strpos($value, '-') !== false; - } - - /** - * Check if a value is an increments of ranges - * - * @param string $value Value to test - * - * @return bool - */ - public function isIncrementsOfRanges($value) - { - return strpos($value, '/') !== false; - } - - /** - * Test if a value is within a range - * - * @param string $dateValue Set date value - * @param string $value Value to test - * - * @return bool - */ - public function isInRange($dateValue, $value) - { - $parts = array_map('trim', explode('-', $value, 2)); - - return $dateValue >= $parts[0] && $dateValue <= $parts[1]; - } - - /** - * Test if a value is within an increments of ranges (offset[-to]/step size) - * - * @param string $dateValue Set date value - * @param string $value Value to test - * - * @return bool - */ - public function isInIncrementsOfRanges($dateValue, $value) - { - $parts = array_map('trim', explode('/', $value, 2)); - $stepSize = isset($parts[1]) ? (int) $parts[1] : 0; - - if ($stepSize === 0) { - return false; - } - - if (($parts[0] == '*' || $parts[0] === '0')) { - return (int) $dateValue % $stepSize == 0; - } - - $range = explode('-', $parts[0], 2); - $offset = $range[0]; - $to = isset($range[1]) ? $range[1] : $dateValue; - // Ensure that the date value is within the range - if ($dateValue < $offset || $dateValue > $to) { - return false; - } - - if ($dateValue > $offset && 0 === $stepSize) { - return false; - } - - for ($i = $offset; $i <= $to; $i+= $stepSize) { - if ($i == $dateValue) { - return true; - } - } - - return false; - } - - /** - * Returns a range of values for the given cron expression - * - * @param string $expression The expression to evaluate - * @param int $max Maximum offset for range - * - * @return array - */ - public function getRangeForExpression($expression, $max) - { - $values = array(); - - if ($this->isRange($expression) || $this->isIncrementsOfRanges($expression)) { - if (!$this->isIncrementsOfRanges($expression)) { - list ($offset, $to) = explode('-', $expression); - $stepSize = 1; - } - else { - $range = array_map('trim', explode('/', $expression, 2)); - $stepSize = isset($range[1]) ? $range[1] : 0; - $range = $range[0]; - $range = explode('-', $range, 2); - $offset = $range[0]; - $to = isset($range[1]) ? $range[1] : $max; - } - $offset = $offset == '*' ? 0 : $offset; - for ($i = $offset; $i <= $to; $i += $stepSize) { - $values[] = $i; - } - sort($values); - } - else { - $values = array($expression); - } - - return $values; - } - -} diff --git a/niucloud/vendor/yzh52521/schedule/src/console/CallbackEvent.php b/niucloud/vendor/yzh52521/schedule/src/console/CallbackEvent.php deleted file mode 100644 index ee89c6a0c..000000000 --- a/niucloud/vendor/yzh52521/schedule/src/console/CallbackEvent.php +++ /dev/null @@ -1,104 +0,0 @@ -callback = $callback; - - $this->parameters = $parameters; - } - - /** - * Run the given event. - * - * @param \think\Container $container - * @return mixed - * - * @throws \Exception - */ - public function run(Container $container) - { - $this->callBeforeCallbacks($container); - - try { - if(is_object($this->callback)){ - $response = $container->invokeFunction($this->callback, $this->parameters); -// $response = $container->invoke([$this->callback, 'doJob'], $this->parameters); - }else if(is_array($this->callback)){ - $response = $container->invoke([$this->callback[0], $this->callback[1] ?? 'doJob'], $this->parameters); - }else{ - $response = $container->invokeFunction($this->callback, $this->parameters); - } -// $response = is_object($this->callback) -// ? $container->invoke([$this->callback, 'doJob'], $this->parameters) -// : $container->invokeFunction($this->callback, $this->parameters); - - -// $container->make($this->callback)->invokeFunction('doJob', $this->parameters); -// $response = $container->invokeFunction($this->callback, $this->parameters); - } finally { - parent::callAfterCallbacks($container); - } - - return $response; - } - public function isCallable($var, $syntaxOnly = false) - { - if(is_array($var)){ - return true; - } - if (! is_array($var)) { - return is_callable($var, $syntaxOnly); - } - - if ((! isset($var[0]) || ! isset($var[1])) || - ! is_string($var[1] ?? null)) { - return false; - } - - if ($syntaxOnly && - (is_string($var[0]) || is_object($var[0])) && - is_string($var[1])) { - return true; - } - - $class = is_object($var[0]) ? get_class($var[0]) : $var[0]; - - $method = $var[1]; - - if (! class_exists($class)) { - return false; - } - - if (method_exists($class, $method)) { - return (new \ReflectionMethod($class, $method))->isPublic(); - } - - if (is_object($var[0]) && method_exists($class, '__call')) { - return (new \ReflectionMethod($class, '__call'))->isPublic(); - } - - if (! is_object($var[0]) && method_exists($class, '__callStatic')) { - return (new \ReflectionMethod($class, '__callStatic'))->isPublic(); - } - - return false; - } - -} diff --git a/niucloud/vendor/yzh52521/schedule/src/console/Command.php b/niucloud/vendor/yzh52521/schedule/src/console/Command.php deleted file mode 100644 index 7160932ed..000000000 --- a/niucloud/vendor/yzh52521/schedule/src/console/Command.php +++ /dev/null @@ -1,84 +0,0 @@ -app = Container::getInstance(); - } - - public function call($callback, array $parameters = []) - { - $this->events[] = $event = new CallbackEvent( - $callback, $parameters - ); - - return $event; - } - - public function command($command, array $parameters = []) - { - $this->events[] = $event = new Event( - $command, $parameters - ); - - return $event; - } - - public function job($job, $data, $queue = null) - { - return $this->call(function ($data) use ($job, $queue) { - Queue::push($job, $data, $queue); - }, [ $data ]); - } - - /* - $this->command('article:pushed')->dailyAt("21:00"); - - /*$this->call(function () use ($input, $output){ - echo '-------------'; - echo 11; - })->twiceDaily(9, 20); - */ - protected function execute(Input $input, Output $output) - { - $eventsRan = false; - - foreach ($this->dueEvents($this->events) as $event) { - //foreach ($this->events as $event) { - if (! $event->filtersPass($this->app)) { - continue; - } - $event->run($this->app); - - $eventsRan = true; - } - - if (! $eventsRan) { - $output->writeln('No scheduled commands are ready to run.'); - } - } - - public function dueEvents($app) - { - return collect($this->events)->filter(function($event) use ($app){ - return $event->isDue($app); - }); - } -} diff --git a/niucloud/vendor/yzh52521/schedule/src/console/CronExpression.php b/niucloud/vendor/yzh52521/schedule/src/console/CronExpression.php deleted file mode 100644 index c80657812..000000000 --- a/niucloud/vendor/yzh52521/schedule/src/console/CronExpression.php +++ /dev/null @@ -1,390 +0,0 @@ - '0 0 1 1 *', - '@annually' => '0 0 1 1 *', - '@monthly' => '0 0 1 * *', - '@weekly' => '0 0 * * 0', - '@daily' => '0 0 * * *', - '@hourly' => '0 * * * *' - ); - - if (isset($mappings[$expression])) { - $expression = $mappings[$expression]; - } - - return new static($expression, $fieldFactory ?: new FieldFactory()); - } - - /** - * Validate a CronExpression. - * - * @param string $expression The CRON expression to validate. - * - * @return bool True if a valid CRON expression was passed. False if not. - * @see \Cron\CronExpression::factory - */ - public static function isValidExpression($expression) - { - try { - self::factory($expression); - } catch (InvalidArgumentException $e) { - return false; - } - - return true; - } - - /** - * Parse a CRON expression - * - * @param string $expression CRON expression (e.g. '8 * * * *') - * @param FieldFactory $fieldFactory Factory to create cron fields - */ - public function __construct($expression, FieldFactory $fieldFactory) - { - $this->fieldFactory = $fieldFactory; - $this->setExpression($expression); - } - - /** - * Set or change the CRON expression - * - * @param string $value CRON expression (e.g. 8 * * * *) - * - * @return CronExpression - * @throws \InvalidArgumentException if not a valid CRON expression - */ - public function setExpression($value) - { - $this->cronParts = preg_split('/\s/', $value, -1, PREG_SPLIT_NO_EMPTY); - if (count($this->cronParts) < 5) { - throw new InvalidArgumentException( - $value . ' is not a valid CRON expression' - ); - } - - foreach ($this->cronParts as $position => $part) { - $this->setPart($position, $part); - } - - return $this; - } - - /** - * Set part of the CRON expression - * - * @param int $position The position of the CRON expression to set - * @param string $value The value to set - * - * @return CronExpression - * @throws \InvalidArgumentException if the value is not valid for the part - */ - public function setPart($position, $value) - { - if (!$this->fieldFactory->getField($position)->validate($value)) { - throw new InvalidArgumentException( - 'Invalid CRON field value ' . $value . ' at position ' . $position - ); - } - - $this->cronParts[$position] = $value; - - return $this; - } - - /** - * Set max iteration count for searching next run dates - * - * @param int $maxIterationCount Max iteration count when searching for next run date - * - * @return CronExpression - */ - public function setMaxIterationCount($maxIterationCount) - { - $this->maxIterationCount = $maxIterationCount; - - return $this; - } - - /** - * Get a next run date relative to the current date or a specific date - * - * @param string|\DateTime $currentTime Relative calculation date - * @param int $nth Number of matches to skip before returning a - * matching next run date. 0, the default, will return the current - * date and time if the next run date falls on the current date and - * time. Setting this value to 1 will skip the first match and go to - * the second match. Setting this value to 2 will skip the first 2 - * matches and so on. - * @param bool $allowCurrentDate Set to TRUE to return the current date if - * it matches the cron expression. - * - * @return \DateTime - * @throws \RuntimeException on too many iterations - */ - public function getNextRunDate($currentTime = 'now', $nth = 0, $allowCurrentDate = false) - { - return $this->getRunDate($currentTime, $nth, false, $allowCurrentDate); - } - - /** - * Get a previous run date relative to the current date or a specific date - * - * @param string|\DateTime $currentTime Relative calculation date - * @param int $nth Number of matches to skip before returning - * @param bool $allowCurrentDate Set to TRUE to return the - * current date if it matches the cron expression - * - * @return \DateTime - * @throws \RuntimeException on too many iterations - * @see \Cron\CronExpression::getNextRunDate - */ - public function getPreviousRunDate($currentTime = 'now', $nth = 0, $allowCurrentDate = false) - { - return $this->getRunDate($currentTime, $nth, true, $allowCurrentDate); - } - - /** - * Get multiple run dates starting at the current date or a specific date - * - * @param int $total Set the total number of dates to calculate - * @param string|\DateTime $currentTime Relative calculation date - * @param bool $invert Set to TRUE to retrieve previous dates - * @param bool $allowCurrentDate Set to TRUE to return the - * current date if it matches the cron expression - * - * @return array Returns an array of run dates - */ - public function getMultipleRunDates($total, $currentTime = 'now', $invert = false, $allowCurrentDate = false) - { - $matches = array(); - for ($i = 0; $i < max(0, $total); $i++) { - try { - $matches[] = $this->getRunDate($currentTime, $i, $invert, $allowCurrentDate); - } catch (RuntimeException $e) { - break; - } - } - - return $matches; - } - - /** - * Get all or part of the CRON expression - * - * @param string $part Specify the part to retrieve or NULL to get the full - * cron schedule string. - * - * @return string|null Returns the CRON expression, a part of the - * CRON expression, or NULL if the part was specified but not found - */ - public function getExpression($part = null) - { - if (null === $part) { - return implode(' ', $this->cronParts); - } elseif (array_key_exists($part, $this->cronParts)) { - return $this->cronParts[$part]; - } - - return null; - } - - /** - * Helper method to output the full expression. - * - * @return string Full CRON expression - */ - public function __toString() - { - return $this->getExpression(); - } - - /** - * Determine if the cron is due to run based on the current date or a - * specific date. This method assumes that the current number of - * seconds are irrelevant, and should be called once per minute. - * - * @param string|\DateTime $currentTime Relative calculation date - * - * @return bool Returns TRUE if the cron is due to run or FALSE if not - */ - public function isDue($currentTime = 'now') - { - if ('now' === $currentTime) { - $currentDate = date('Y-m-d H:i'); - $currentTime = strtotime($currentDate); - } elseif ($currentTime instanceof DateTime) { - $currentDate = clone $currentTime; - // Ensure time in 'current' timezone is used - $currentDate->setTimezone(new DateTimeZone(date_default_timezone_get())); - $currentDate = $currentDate->format('Y-m-d H:i'); - $currentTime = strtotime($currentDate); - } elseif ($currentTime instanceof DateTimeImmutable) { - $currentDate = DateTime::createFromFormat('U', $currentTime->format('U')); - $currentDate->setTimezone(new DateTimeZone(date_default_timezone_get())); - $currentDate = $currentDate->format('Y-m-d H:i'); - $currentTime = strtotime($currentDate); - } else { - $currentTime = new DateTime($currentTime); - $currentTime->setTime($currentTime->format('H'), $currentTime->format('i'), 0); - $currentDate = $currentTime->format('Y-m-d H:i'); - $currentTime = $currentTime->getTimeStamp(); - } - - try { - return $this->getNextRunDate($currentDate, 0, true)->getTimestamp() == $currentTime; - } catch (Exception $e) { - return false; - } - } - - /** - * Get the next or previous run date of the expression relative to a date - * - * @param string|\DateTime $currentTime Relative calculation date - * @param int $nth Number of matches to skip before returning - * @param bool $invert Set to TRUE to go backwards in time - * @param bool $allowCurrentDate Set to TRUE to return the - * current date if it matches the cron expression - * - * @return \DateTime - * @throws \RuntimeException on too many iterations - */ - protected function getRunDate($currentTime = null, $nth = 0, $invert = false, $allowCurrentDate = false) - { - if ($currentTime instanceof DateTime) { - $currentDate = clone $currentTime; - } elseif ($currentTime instanceof DateTimeImmutable) { - $currentDate = DateTime::createFromFormat('U', $currentTime->format('U')); - $currentDate->setTimezone($currentTime->getTimezone()); - } else { - $currentDate = new DateTime($currentTime ?: 'now'); - $currentDate->setTimezone(new DateTimeZone(date_default_timezone_get())); - } - - $currentDate->setTime($currentDate->format('H'), $currentDate->format('i'), 0); - $nextRun = clone $currentDate; - $nth = (int) $nth; - - // We don't have to satisfy * or null fields - $parts = array(); - $fields = array(); - foreach (self::$order as $position) { - $part = $this->getExpression($position); - if (null === $part || '*' === $part) { - continue; - } - $parts[$position] = $part; - $fields[$position] = $this->fieldFactory->getField($position); - } - - // Set a hard limit to bail on an impossible date - for ($i = 0; $i < $this->maxIterationCount; $i++) { - - foreach ($parts as $position => $part) { - $satisfied = false; - // Get the field object used to validate this part - $field = $fields[$position]; - // Check if this is singular or a list - if (strpos($part, ',') === false) { - $satisfied = $field->isSatisfiedBy($nextRun, $part); - } else { - foreach (array_map('trim', explode(',', $part)) as $listPart) { - if ($field->isSatisfiedBy($nextRun, $listPart)) { - $satisfied = true; - break; - } - } - } - - // If the field is not satisfied, then start over - if (!$satisfied) { - $field->increment($nextRun, $invert, $part); - continue 2; - } - } - - // Skip this match if needed - if ((!$allowCurrentDate && $nextRun == $currentDate) || --$nth > -1) { - $this->fieldFactory->getField(0)->increment($nextRun, $invert, isset($parts[0]) ? $parts[0] : null); - continue; - } - - return $nextRun; - } - - // @codeCoverageIgnoreStart - throw new RuntimeException('Impossible CRON expression'); - // @codeCoverageIgnoreEnd - } -} diff --git a/niucloud/vendor/yzh52521/schedule/src/console/DayOfMonthField.php b/niucloud/vendor/yzh52521/schedule/src/console/DayOfMonthField.php deleted file mode 100644 index 6d17c97af..000000000 --- a/niucloud/vendor/yzh52521/schedule/src/console/DayOfMonthField.php +++ /dev/null @@ -1,173 +0,0 @@ - - */ -class DayOfMonthField extends AbstractField -{ - /** - * Get the nearest day of the week for a given day in a month - * - * @param int $currentYear Current year - * @param int $currentMonth Current month - * @param int $targetDay Target day of the month - * - * @return \DateTime Returns the nearest date - */ - private static function getNearestWeekday($currentYear, $currentMonth, $targetDay) - { - $tday = str_pad($targetDay, 2, '0', STR_PAD_LEFT); - $target = DateTime::createFromFormat('Y-m-d', "$currentYear-$currentMonth-$tday"); - $currentWeekday = (int) $target->format('N'); - - if ($currentWeekday < 6) { - return $target; - } - - $lastDayOfMonth = $target->format('t'); - - foreach (array(-1, 1, -2, 2) as $i) { - $adjusted = $targetDay + $i; - if ($adjusted > 0 && $adjusted <= $lastDayOfMonth) { - $target->setDate($currentYear, $currentMonth, $adjusted); - if ($target->format('N') < 6 && $target->format('m') == $currentMonth) { - return $target; - } - } - } - } - - public function isSatisfiedBy(DateTime $date, $value) - { - // ? states that the field value is to be skipped - if ($value == '?') { - return true; - } - - $fieldValue = $date->format('d'); - - // Check to see if this is the last day of the month - if ($value == 'L') { - return $fieldValue == $date->format('t'); - } - - // Check to see if this is the nearest weekday to a particular value - if (strpos($value, 'W')) { - // Parse the target day - $targetDay = substr($value, 0, strpos($value, 'W')); - // Find out if the current day is the nearest day of the week - return $date->format('j') == self::getNearestWeekday( - $date->format('Y'), - $date->format('m'), - $targetDay - )->format('j'); - } - - return $this->isSatisfied($date->format('d'), $value); - } - - public function increment(DateTime $date, $invert = false) - { - if ($invert) { - $date->modify('previous day'); - $date->setTime(23, 59); - } else { - $date->modify('next day'); - $date->setTime(0, 0); - } - - return $this; - } - - /** - * Validates that the value is valid for the Day of the Month field - * Days of the month can contain values of 1-31, *, L, or ? by default. This can be augmented with lists via a ',', - * ranges via a '-', or with a '[0-9]W' to specify the closest weekday. - * - * @param string $value - * @return bool - */ - public function validate($value) - { - // Allow wildcards and a single L - if ($value === '?' || $value === '*' || $value === 'L') { - return true; - } - - // If you only contain numbers and are within 1-31 - if ((bool) preg_match('/^\d{1,2}$/', $value) && ($value >= 1 && $value <= 31)) { - return true; - } - - // If you have a -, we will deal with each of your chunks - if ((bool) preg_match('/-/', $value)) { - // We cannot have a range within a list or vice versa - if ((bool) preg_match('/,/', $value)) { - return false; - } - - $chunks = explode('-', $value); - foreach ($chunks as $chunk) { - if (!$this->validate($chunk)) { - return false; - } - } - - return true; - } - - // If you have a comma, we will deal with each value - if ((bool) preg_match('/,/', $value)) { - // We cannot have a range within a list or vice versa - if ((bool) preg_match('/-/', $value)) { - return false; - } - - $chunks = explode(',', $value); - foreach ($chunks as $chunk) { - if (!$this->validate($chunk)) { - return false; - } - } - - return true; - } - - // If you contain a /, we'll deal with it - if ((bool) preg_match('/\//', $value)) { - $chunks = explode('/', $value); - foreach ($chunks as $chunk) { - if (!$this->validate($chunk)) { - return false; - } - } - return true; - } - - // If you end in W, make sure that it has a numeric in front of it - if ((bool) preg_match('/^\d{1,2}W$/', $value)) { - return true; - } - - return false; - } -} diff --git a/niucloud/vendor/yzh52521/schedule/src/console/DayOfWeekField.php b/niucloud/vendor/yzh52521/schedule/src/console/DayOfWeekField.php deleted file mode 100644 index 721243d4d..000000000 --- a/niucloud/vendor/yzh52521/schedule/src/console/DayOfWeekField.php +++ /dev/null @@ -1,141 +0,0 @@ -convertLiterals($value); - - $currentYear = $date->format('Y'); - $currentMonth = $date->format('m'); - $lastDayOfMonth = $date->format('t'); - - // Find out if this is the last specific weekday of the month - if (strpos($value, 'L')) { - $weekday = str_replace('7', '0', substr($value, 0, strpos($value, 'L'))); - $tdate = clone $date; - $tdate->setDate($currentYear, $currentMonth, $lastDayOfMonth); - while ($tdate->format('w') != $weekday) { - $tdateClone = new DateTime(); - $tdate = $tdateClone - ->setTimezone($tdate->getTimezone()) - ->setDate($currentYear, $currentMonth, --$lastDayOfMonth); - } - - return $date->format('j') == $lastDayOfMonth; - } - - // Handle # hash tokens - if (strpos($value, '#')) { - list($weekday, $nth) = explode('#', $value); - - // 0 and 7 are both Sunday, however 7 matches date('N') format ISO-8601 - if ($weekday === '0') { - $weekday = 7; - } - - // Validate the hash fields - if ($weekday < 0 || $weekday > 7) { - throw new InvalidArgumentException("Weekday must be a value between 0 and 7. {$weekday} given"); - } - if ($nth > 5) { - throw new InvalidArgumentException('There are never more than 5 of a given weekday in a month'); - } - // The current weekday must match the targeted weekday to proceed - if ($date->format('N') != $weekday) { - return false; - } - - $tdate = clone $date; - $tdate->setDate($currentYear, $currentMonth, 1); - $dayCount = 0; - $currentDay = 1; - while ($currentDay < $lastDayOfMonth + 1) { - if ($tdate->format('N') == $weekday) { - if (++$dayCount >= $nth) { - break; - } - } - $tdate->setDate($currentYear, $currentMonth, ++$currentDay); - } - - return $date->format('j') == $currentDay; - } - - // Handle day of the week values - if (strpos($value, '-')) { - $parts = explode('-', $value); - if ($parts[0] == '7') { - $parts[0] = '0'; - } elseif ($parts[1] == '0') { - $parts[1] = '7'; - } - $value = implode('-', $parts); - } - - // Test to see which Sunday to use -- 0 == 7 == Sunday - $format = in_array(7, str_split($value)) ? 'N' : 'w'; - $fieldValue = $date->format($format); - - return $this->isSatisfied($fieldValue, $value); - } - - public function increment(DateTime $date, $invert = false) - { - if ($invert) { - $date->modify('-1 day'); - $date->setTime(23, 59, 0); - } else { - $date->modify('+1 day'); - $date->setTime(0, 0, 0); - } - - return $this; - } - - public function validate($value) - { - $value = $this->convertLiterals($value); - - foreach (explode(',', $value) as $expr) { - if (!preg_match('/^(\*|[0-7](L?|#[1-5]))([\/\,\-][0-7]+)*$/', $expr)) { - return false; - } - } - - return true; - } - - private function convertLiterals($string) - { - return str_ireplace( - array('SUN', 'MON', 'TUE', 'WED', 'THU', 'FRI', 'SAT'), - range(0, 6), - $string - ); - } -} diff --git a/niucloud/vendor/yzh52521/schedule/src/console/Event.php b/niucloud/vendor/yzh52521/schedule/src/console/Event.php deleted file mode 100644 index 5471e1176..000000000 --- a/niucloud/vendor/yzh52521/schedule/src/console/Event.php +++ /dev/null @@ -1,123 +0,0 @@ -command = $command; - - $this->parameters = $parameters; - } - - public function run(Container $container) - { - $this->callBeforeCallbacks($container); - - if (strpos(\think\App::VERSION, '6.0') !== false) { - \think\facade\Console::call($this->command, $this->parameters, 'console'); - }else{ - \think\Console::call($this->command, $this->parameters, 'console'); - } - - - $this->callAfterCallbacks($container); - } - - public function filtersPass($app) - { - foreach ($this->filters as $callback) { - if (! $app->call($callback)) { - return false; - } - } - - return true; - } - - public function isDue($app) - { - return $this->expressionPasses(); - } - - public function expressionPasses() - { - $date = Carbon::now(); - - if ($this->timezone) { - $date->setTimezone($this->timezone); - } - - return CronExpression::factory($this->expression)->isDue($date->toDateTimeString()); - } - - public function when($callback) - { - $this->filters[] = is_callable($callback) ? $callback : function () use ($callback) { - return $callback; - }; - - return $this; - } - - public function before(Closure $callback) - { - $this->beforeCallbacks[] = $callback; - - return $this; - } - - - public function after(Closure $callback) - { - return $this->then($callback); - } - - public function then(Closure $callback) - { - $this->afterCallbacks[] = $callback; - - return $this; - } - - public function callBeforeCallbacks(Container $container) - { - foreach ($this->beforeCallbacks as $callback) { - $container->invokeFunction($callback); - } - } - - public function callAfterCallbacks(Container $container) - { - foreach ($this->afterCallbacks as $callback) { - $container->invokeFunction($callback); - } - } - - public function timezone($timezone) - { - $this->timezone = $timezone; - - return $this; - } -} diff --git a/niucloud/vendor/yzh52521/schedule/src/console/FieldFactory.php b/niucloud/vendor/yzh52521/schedule/src/console/FieldFactory.php deleted file mode 100644 index 7fd855be9..000000000 --- a/niucloud/vendor/yzh52521/schedule/src/console/FieldFactory.php +++ /dev/null @@ -1,58 +0,0 @@ -fields[$position])) { - switch ($position) { - case 0: - $this->fields[$position] = new MinutesField(); - break; - case 1: - $this->fields[$position] = new HoursField(); - break; - case 2: - $this->fields[$position] = new DayOfMonthField(); - break; - case 3: - $this->fields[$position] = new MonthField(); - break; - case 4: - $this->fields[$position] = new DayOfWeekField(); - break; - case 5: - $this->fields[$position] = new YearField(); - break; - default: - throw new InvalidArgumentException( - $position . ' is not a valid position' - ); - } - } - - return $this->fields[$position]; - } -} diff --git a/niucloud/vendor/yzh52521/schedule/src/console/FieldInterface.php b/niucloud/vendor/yzh52521/schedule/src/console/FieldInterface.php deleted file mode 100644 index 942fc501b..000000000 --- a/niucloud/vendor/yzh52521/schedule/src/console/FieldInterface.php +++ /dev/null @@ -1,41 +0,0 @@ -isSatisfied($date->format('H'), $value); - } - - public function increment(DateTime $date, $invert = false, $parts = null) - { - // Change timezone to UTC temporarily. This will - // allow us to go back or forwards and hour even - // if DST will be changed between the hours. - if (is_null($parts) || $parts == '*') { - $timezone = $date->getTimezone(); - $date->setTimezone(new DateTimeZone('UTC')); - if ($invert) { - $date->modify('-1 hour'); - } else { - $date->modify('+1 hour'); - } - $date->setTimezone($timezone); - - $date->setTime($date->format('H'), $invert ? 59 : 0); - return $this; - } - - $parts = strpos($parts, ',') !== false ? explode(',', $parts) : array($parts); - $hours = array(); - foreach ($parts as $part) { - $hours = array_merge($hours, $this->getRangeForExpression($part, 23)); - } - - $current_hour = $date->format('H'); - $position = $invert ? count($hours) - 1 : 0; - if (count($hours) > 1) { - for ($i = 0; $i < count($hours) - 1; $i++) { - if ((!$invert && $current_hour >= $hours[$i] && $current_hour < $hours[$i + 1]) || - ($invert && $current_hour > $hours[$i] && $current_hour <= $hours[$i + 1])) { - $position = $invert ? $i : $i + 1; - break; - } - } - } - - $hour = $hours[$position]; - if ((!$invert && $date->format('H') >= $hour) || ($invert && $date->format('H') <= $hour)) { - $date->modify(($invert ? '-' : '+') . '1 day'); - $date->setTime($invert ? 23 : 0, $invert ? 59 : 0); - } - else { - $date->setTime($hour, $invert ? 59 : 0); - } - - return $this; - } - - public function validate($value) - { - return (bool) preg_match('/^[\*,\/\-0-9]+$/', $value); - } -} diff --git a/niucloud/vendor/yzh52521/schedule/src/console/MinutesField.php b/niucloud/vendor/yzh52521/schedule/src/console/MinutesField.php deleted file mode 100644 index 0827bc996..000000000 --- a/niucloud/vendor/yzh52521/schedule/src/console/MinutesField.php +++ /dev/null @@ -1,62 +0,0 @@ -isSatisfied($date->format('i'), $value); - } - - public function increment(DateTime $date, $invert = false, $parts = null) - { - if (is_null($parts)) { - if ($invert) { - $date->modify('-1 minute'); - } else { - $date->modify('+1 minute'); - } - return $this; - } - - $parts = strpos($parts, ',') !== false ? explode(',', $parts) : array($parts); - $minutes = array(); - foreach ($parts as $part) { - $minutes = array_merge($minutes, $this->getRangeForExpression($part, 59)); - } - - $current_minute = $date->format('i'); - $position = $invert ? count($minutes) - 1 : 0; - if (count($minutes) > 1) { - for ($i = 0; $i < count($minutes) - 1; $i++) { - if ((!$invert && $current_minute >= $minutes[$i] && $current_minute < $minutes[$i + 1]) || - ($invert && $current_minute > $minutes[$i] && $current_minute <= $minutes[$i + 1])) { - $position = $invert ? $i : $i + 1; - break; - } - } - } - - if ((!$invert && $current_minute >= $minutes[$position]) || ($invert && $current_minute <= $minutes[$position])) { - $date->modify(($invert ? '-' : '+') . '1 hour'); - $date->setTime($date->format('H'), $invert ? 59 : 0); - } - else { - $date->setTime($date->format('H'), $minutes[$position]); - } - - return $this; - } - - public function validate($value) - { - return (bool) preg_match('/^[\*,\/\-0-9]+$/', $value); - } -} diff --git a/niucloud/vendor/yzh52521/schedule/src/console/MonthField.php b/niucloud/vendor/yzh52521/schedule/src/console/MonthField.php deleted file mode 100644 index 33b18deb4..000000000 --- a/niucloud/vendor/yzh52521/schedule/src/console/MonthField.php +++ /dev/null @@ -1,44 +0,0 @@ -isSatisfied($date->format('m'), $value); - } - - public function increment(DateTime $date, $invert = false) - { - if ($invert) { - $date->modify('last day of previous month'); - $date->setTime(23, 59); - } else { - $date->modify('first day of next month'); - $date->setTime(0, 0); - } - - return $this; - } - - public function validate($value) - { - return (bool) preg_match('/^[\*,\/\-0-9A-Z]+$/', $value); - } -} diff --git a/niucloud/vendor/yzh52521/schedule/src/console/YearField.php b/niucloud/vendor/yzh52521/schedule/src/console/YearField.php deleted file mode 100644 index 2d3ece687..000000000 --- a/niucloud/vendor/yzh52521/schedule/src/console/YearField.php +++ /dev/null @@ -1,37 +0,0 @@ -isSatisfied($date->format('Y'), $value); - } - - public function increment(DateTime $date, $invert = false) - { - if ($invert) { - $date->modify('-1 year'); - $date->setDate($date->format('Y'), 12, 31); - $date->setTime(23, 59, 0); - } else { - $date->modify('+1 year'); - $date->setDate($date->format('Y'), 1, 1); - $date->setTime(0, 0, 0); - } - - return $this; - } - - public function validate($value) - { - return (bool) preg_match('/^[\*,\/\-0-9]+$/', $value); - } -} diff --git a/niucloud/vendor/yzh52521/schedule/src/exceptions/Exception.php b/niucloud/vendor/yzh52521/schedule/src/exceptions/Exception.php deleted file mode 100644 index f4543a40a..000000000 --- a/niucloud/vendor/yzh52521/schedule/src/exceptions/Exception.php +++ /dev/null @@ -1,8 +0,0 @@ -