CImage.php 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422
  1. <?php
  2. /**
  3. * FecShop file.
  4. *
  5. * @link http://www.fecshop.com/
  6. * @copyright Copyright (c) 2016 FecShop Software LLC
  7. * @license http://www.fecshop.com/license/
  8. */
  9. namespace fec\helpers;
  10. use Yii;
  11. use Imagine\Image\Box;
  12. use Imagine\Image\BoxInterface;
  13. use Imagine\Image\Color;
  14. use Imagine\Image\ImageInterface;
  15. use Imagine\Image\ImagineInterface;
  16. use Imagine\Image\ManipulatorInterface;
  17. use Imagine\Image\Point;
  18. use yii\base\InvalidConfigException;
  19. use yii\base\InvalidParamException;
  20. use yii\helpers\ArrayHelper;
  21. /**
  22. * @author Terry Zhao <2358269014@qq.com>
  23. * @since 1.0
  24. */
  25. class CImage
  26. {
  27. /**
  28. * GD2 driver definition for Imagine implementation using the GD library.
  29. */
  30. const DRIVER_GD2 = 'gd2';
  31. /**
  32. * imagick driver definition.
  33. */
  34. const DRIVER_IMAGICK = 'imagick';
  35. /**
  36. * gmagick driver definition.
  37. */
  38. const DRIVER_GMAGICK = 'gmagick';
  39. /**
  40. * @var array|string the driver to use. This can be either a single driver name or an array of driver names.
  41. * If the latter, the first available driver will be used.
  42. */
  43. public static $driver = [self::DRIVER_GMAGICK, self::DRIVER_IMAGICK, self::DRIVER_GD2];
  44. /**
  45. * @var ImagineInterface instance.
  46. */
  47. private static $_imagine;
  48. /**
  49. * @var string background color to use when creating thumbnails in `ImageInterface::THUMBNAIL_INSET` mode with
  50. * both width and height specified. Default is white.
  51. *
  52. * @since 2.0.4
  53. */
  54. public static $thumbnailBackgroundColor = 'FFF';
  55. /**
  56. * @var string background alpha (transparency) to use when creating thumbnails in `ImageInterface::THUMBNAIL_INSET`
  57. * mode with both width and height specified. Default is solid.
  58. *
  59. * @since 2.0.4
  60. */
  61. public static $thumbnailBackgroundAlpha = 100;
  62. /**
  63. * @property $imgPath|String 原来图片的绝对路径
  64. * @property $newPath|String 压缩尺寸,并加入水印后的图片保存路径。
  65. * @property $resize|Array or String 图片压缩后的宽度,[111,222];
  66. * @property $waterMark|String 水印图片的绝对路径
  67. * 生成新的带有水印的缩略图,水印图片默认放到中间位置。
  68. * newPath 要保证改路径存在并可写。如果水印图片比resize的尺寸大,则水印图片将不会被添加到生成的图片上面
  69. * $resizeWidth 和 $resizeHeight,允许其中一个设置为0,如果为0,
  70. * @return
  71. */
  72. public static function saveResizeMiddleWaterImg($imgPath, $newPath, $resize, $waterMark='', $options = []){
  73. //exit;
  74. $image = static::getImagine()->open($imgPath);
  75. $sourceBox = $image->getSize();
  76. $imgWidth = $sourceBox->getWidth();
  77. $imgHeight = $sourceBox->getHeight();
  78. if(is_array($resize)){
  79. list($resizeWidth,$resizeHeight) = $resize;
  80. }else{
  81. $resizeWidth = $resize;
  82. $resizeHeight = $resizeWidth * $imgHeight / $imgWidth;
  83. }
  84. //echo 22;
  85. //echo $resizeWidth.' && '.$resizeHeight;
  86. //exit;
  87. if(!$resizeWidth && !$resizeHeight){
  88. return false;
  89. }
  90. // 得到图片不失真情况下的缩放尺寸
  91. $imgRate = $imgWidth/$imgHeight;
  92. $resizeRate = $resizeWidth/$resizeHeight;
  93. if($imgRate >= $resizeRate){
  94. $resizeImgWidth = $resizeWidth;
  95. $resizeImgHeight = $resizeImgWidth * $imgHeight / $imgWidth;
  96. }else{
  97. $resizeImgHeight = $resizeHeight;
  98. $resizeImgWidth = $resizeImgHeight * $imgWidth / $imgHeight;
  99. }
  100. //得到背景图片
  101. $imagine = static::getImagine();
  102. //$palette = new \Imagine\Image\Palette\RGB();
  103. $size = new \Imagine\Image\Box($resizeWidth, $resizeHeight);
  104. //$color = $palette->color($backgroundColor);
  105. $gr_image = $imagine->create($size);
  106. //$gr_image->save($newPath);
  107. $image->resize(new Box($resizeImgWidth, $resizeImgHeight ));
  108. // 和空白图片合并
  109. $startX = ($resizeWidth - $resizeImgWidth)/2 ;
  110. $startY = ($resizeHeight - $resizeImgHeight)/2 ;
  111. $startX = ($startX > 0) ? $startX : 0;
  112. $startY = ($startY > 0) ? $startY : 0;
  113. $start = [$startX,$startY];
  114. $gr_image->paste($image, new Point($start[0], $start[1]));
  115. if($waterMark){
  116. $waterImage = static::getImagine()->open($waterMark);
  117. $waterSourceBox = $waterImage->getSize();
  118. $watermarkWidth = $waterSourceBox->getWidth();
  119. $watermarkHeight= $waterSourceBox->getHeight();
  120. if(($resizeWidth >= $watermarkWidth)
  121. && ($resizeHeight >= $watermarkHeight)
  122. ){
  123. $startX = ($resizeWidth - $watermarkWidth)/2 ;
  124. $startY = ($resizeHeight - $watermarkHeight)/2 ;
  125. $startX = ($startX > 0) ? $startX : 0;
  126. $startY = ($startY > 0) ? $startY : 0;
  127. $start = [$startX,$startY];
  128. $gr_image->paste($waterImage, new Point($start[0], $start[1]));
  129. }
  130. }
  131. ;
  132. $gr_image->save($newPath, $options);
  133. }
  134. # 1.保存缩略图
  135. # $oldImgFile :原来的图片路径 web 目录下面加入@webroot
  136. # $newImgFile : 新保存的图片路径
  137. # $width, $height :新图片的长宽。
  138. /*example:
  139. $oldImgFile = "@webroot/media/ebay_task/7777.jpg";
  140. $newImgFile = "@webroot/media/ebay_task/22.jpg";
  141. $width = 200;
  142. $height= 230;
  143. CImage::saveThumbnail($oldImgFile,$newImgFile ,$width, $height);
  144. 文件将被保存到新文件路径,如果文件存在,则 被覆盖
  145. */
  146. public static function saveThumbnail($oldImgFile,$newImgFile ,$resize, $mode = ManipulatorInterface::THUMBNAIL_INSET)
  147. {
  148. $width = $resize['width'];
  149. $height = $resize['height'];
  150. $imageModle = self::thumbnail($oldImgFile, $width, $height,$mode);
  151. $newImgFile = Yii::getAlias($newImgFile);
  152. $imageModle->save($newImgFile);
  153. }
  154. # 2、保存水印图片到新的路径。
  155. # 类似于 下面的 saveMiddleWaterMark ,不同的start X,Y 是自定义的
  156. # 可以通过参数的形式传递
  157. public static function saveWaterMark($waterMarkFilename,$oldImgFile,$newImgFile, array $start = [0, 0]){
  158. $imageModle = self::watermark($oldImgFile, $waterMarkFilename, $start);
  159. $newImgFile = Yii::getAlias($newImgFile);
  160. $imageModle->save($newImgFile);
  161. }
  162. # 3.保存水印图片到新的路径(水印图片在中间位置)
  163. /* #2. 使用的example
  164. $oldImgFile = "@webroot/media/ebay_task/7777.jpg";
  165. $waterMarkFilename = "@webroot/media/ebay_task/3333333.png";
  166. $newImgFile = "@webroot/media/ebay_task/11.jpg";
  167. CImage::saveMiddleWaterMark($waterMarkFilename,$oldImgFile,$newImgFile);
  168. # 作用,给一个图片加水印,水印放到地图的中间的位置。
  169. */
  170. public static function saveMiddleWaterMark($waterMarkFilename,$oldImgFile,$newImgFile){
  171. $imageModle = self::middlewatermark($oldImgFile, $waterMarkFilename, $start);
  172. $newImgFile = Yii::getAlias($newImgFile);
  173. $imageModle->save($newImgFile);
  174. }
  175. public static function middlewatermark($filename, $watermarkFilename)
  176. {
  177. $img = static::getImagine()->open(Yii::getAlias($filename));
  178. $watermark = static::getImagine()->open(Yii::getAlias($watermarkFilename));
  179. $sourceBox = $img->getSize();
  180. $imgWidth = $sourceBox->getWidth();
  181. $imgHeight = $sourceBox->getHeight();
  182. $sourceBox = $watermark->getSize();
  183. $watermarkWidth = $sourceBox->getWidth();
  184. $watermarkHeight= $sourceBox->getHeight();
  185. $startX = ($imgWidth - $watermarkWidth)/2 ;
  186. $startY = ($imgHeight - $watermarkHeight)/2 ;
  187. $startX = ($startX > 0) ? $startX : 0;
  188. $startY = ($startY > 0) ? $startY : 0;
  189. $start = [$startX,$startY];
  190. $img->paste($watermark, new Point($start[0], $start[1]));
  191. return $img;
  192. }
  193. #######################################################
  194. # 下面是yii2插件部分的代码。
  195. /**
  196. * Returns the `Imagine` object that supports various image manipulations.
  197. * @return ImagineInterface the `Imagine` object
  198. */
  199. public static function getImagine()
  200. {
  201. if (self::$_imagine === null) {
  202. self::$_imagine = static::createImagine();
  203. }
  204. return self::$_imagine;
  205. }
  206. /**
  207. * @param ImagineInterface $imagine the `Imagine` object.
  208. */
  209. public static function setImagine($imagine)
  210. {
  211. self::$_imagine = $imagine;
  212. }
  213. /**
  214. * Creates an `Imagine` object based on the specified [[driver]].
  215. * @return ImagineInterface the new `Imagine` object
  216. * @throws InvalidConfigException if [[driver]] is unknown or the system doesn't support any [[driver]].
  217. */
  218. protected static function createImagine()
  219. {
  220. foreach ((array) static::$driver as $driver) {
  221. switch ($driver) {
  222. case self::DRIVER_GMAGICK:
  223. if (class_exists('Gmagick', false)) {
  224. return new \Imagine\Gmagick\Imagine();
  225. }
  226. break;
  227. case self::DRIVER_IMAGICK:
  228. if (class_exists('Imagick', false)) {
  229. return new \Imagine\Imagick\Imagine();
  230. }
  231. break;
  232. case self::DRIVER_GD2:
  233. if (function_exists('gd_info')) {
  234. return new \Imagine\Gd\Imagine();
  235. }
  236. break;
  237. default:
  238. throw new InvalidConfigException("Unknown driver: $driver");
  239. }
  240. }
  241. throw new InvalidConfigException("Your system does not support any of these drivers: " . implode(',', (array) static::$driver));
  242. }
  243. /**
  244. * Crops an image.
  245. *
  246. * For example,
  247. *
  248. * ~~~
  249. * $obj->crop('path\to\image.jpg', 200, 200, [5, 5]);
  250. *
  251. * $point = new \Imagine\Image\Point(5, 5);
  252. * $obj->crop('path\to\image.jpg', 200, 200, $point);
  253. * ~~~
  254. *
  255. * @param string $filename the image file path or path alias.
  256. * @param integer $width the crop width
  257. * @param integer $height the crop height
  258. * @param array $start the starting point. This must be an array with two elements representing `x` and `y` coordinates.
  259. * @return ImageInterface
  260. * @throws InvalidParamException if the `$start` parameter is invalid
  261. */
  262. public static function crop($filename, $width, $height, array $start = [0, 0])
  263. {
  264. if (!isset($start[0], $start[1])) {
  265. throw new InvalidParamException('$start must be an array of two elements.');
  266. }
  267. return static::getImagine()
  268. ->open(Yii::getAlias($filename))
  269. ->copy()
  270. ->crop(new Point($start[0], $start[1]), new Box($width, $height));
  271. }
  272. /**
  273. ManipulatorInterface::THUMBNAIL_INSET
  274. ManipulatorInterface::THUMBNAIL_OUTBOUND
  275. */
  276. public static function thumbnail($filename, $width, $height, $mode = ManipulatorInterface::THUMBNAIL_INSET)
  277. {
  278. $img = static::getImagine()->open(Yii::getAlias($filename));
  279. $sourceBox = $img->getSize();
  280. $thumbnailBox = static::getThumbnailBox($sourceBox, $width, $height);
  281. if (($sourceBox->getWidth() <= $thumbnailBox->getWidth() && $sourceBox->getHeight() <= $thumbnailBox->getHeight()) || (!$thumbnailBox->getWidth() && !$thumbnailBox->getHeight())) {
  282. return $img->copy();
  283. }
  284. $img = $img->thumbnail($thumbnailBox, $mode);
  285. // create empty image to preserve aspect ratio of thumbnail
  286. $thumb = static::getImagine()->create($thumbnailBox, new Color(static::$thumbnailBackgroundColor, static::$thumbnailBackgroundAlpha));
  287. // calculate points
  288. $startX = 0;
  289. $startY = 0;
  290. if ($sourceBox->getWidth() < $width) {
  291. $startX = ceil($width - $sourceBox->getWidth()) / 2;
  292. }
  293. if ($sourceBox->getHeight() < $height) {
  294. $startY = ceil($height - $sourceBox->getHeight()) / 2;
  295. }
  296. $thumb->paste($img, new Point($startX, $startY));
  297. return $thumb;
  298. }
  299. /**
  300. * Adds a watermark to an existing image.
  301. * @param string $filename the image file path or path alias.
  302. * @param string $watermarkFilename the file path or path alias of the watermark image.
  303. * @param array $start the starting point. This must be an array with two elements representing `x` and `y` coordinates.
  304. * @return ImageInterface
  305. * @throws InvalidParamException if `$start` is invalid
  306. */
  307. public static function watermark($filename, $watermarkFilename, array $start = [0, 0])
  308. {
  309. if (!isset($start[0], $start[1])) {
  310. throw new InvalidParamException('$start must be an array of two elements.');
  311. }
  312. $img = static::getImagine()->open(Yii::getAlias($filename));
  313. $watermark = static::getImagine()->open(Yii::getAlias($watermarkFilename));
  314. $img->paste($watermark, new Point($start[0], $start[1]));
  315. return $img;
  316. }
  317. /**
  318. * Draws a text string on an existing image.
  319. * @param string $filename the image file path or path alias.
  320. * @param string $text the text to write to the image
  321. * @param string $fontFile the file path or path alias
  322. * @param array $start the starting position of the text. This must be an array with two elements representing `x` and `y` coordinates.
  323. * @param array $fontOptions the font options. The following options may be specified:
  324. *
  325. * - color: The font color. Defaults to "fff".
  326. * - size: The font size. Defaults to 12.
  327. * - angle: The angle to use to write the text. Defaults to 0.
  328. *
  329. * @return ImageInterface
  330. * @throws InvalidParamException if `$fontOptions` is invalid
  331. */
  332. public static function text($filename, $text, $fontFile, array $start = [0, 0], array $fontOptions = [])
  333. {
  334. if (!isset($start[0], $start[1])) {
  335. throw new InvalidParamException('$start must be an array of two elements.');
  336. }
  337. $fontSize = ArrayHelper::getValue($fontOptions, 'size', 12);
  338. $fontColor = ArrayHelper::getValue($fontOptions, 'color', 'fff');
  339. $fontAngle = ArrayHelper::getValue($fontOptions, 'angle', 0);
  340. $img = static::getImagine()->open(Yii::getAlias($filename));
  341. $font = static::getImagine()->font(Yii::getAlias($fontFile), $fontSize, new Color($fontColor));
  342. $img->draw()->text($text, $font, new Point($start[0], $start[1]), $fontAngle);
  343. return $img;
  344. }
  345. /**
  346. * Adds a frame around of the image. Please note that the image size will increase by `$margin` x 2.
  347. * @param string $filename the full path to the image file
  348. * @param integer $margin the frame size to add around the image
  349. * @param string $color the frame color
  350. * @param integer $alpha the alpha value of the frame.
  351. * @return ImageInterface
  352. */
  353. public static function frame($filename, $margin = 20, $color = '666', $alpha = 100)
  354. {
  355. $img = static::getImagine()->open(Yii::getAlias($filename));
  356. $size = $img->getSize();
  357. $pasteTo = new Point($margin, $margin);
  358. $padColor = new Color($color, $alpha);
  359. $box = new Box($size->getWidth() + ceil($margin * 2), $size->getHeight() + ceil($margin * 2));
  360. $image = static::getImagine()->create($box, $padColor);
  361. $image->paste($img, $pasteTo);
  362. return $image;
  363. }
  364. /**
  365. * Returns box for a thumbnail to be created. If one of the dimensions is set to `null`, another one is calculated
  366. * automatically based on width to height ratio of original image box.
  367. *
  368. * @param BoxInterface $sourceBox original image box
  369. * @param int $width thumbnail width
  370. * @param int $height thumbnail height
  371. * @return BoxInterface thumbnail box
  372. *
  373. * @since 2.0.4
  374. */
  375. protected static function getThumbnailBox(BoxInterface $sourceBox, $width, $height)
  376. {
  377. if ($width !== null && $height !== null) {
  378. return new Box($width, $height);
  379. }
  380. if ($width === null && $height === null) {
  381. throw new InvalidParamException('Width and height cannot be null at same time.');
  382. }
  383. $ratio = $sourceBox->getWidth() / $sourceBox->getHeight();
  384. if ($height === null) {
  385. $height = ceil($width / $ratio);
  386. } else {
  387. $width = ceil($height * $ratio);
  388. }
  389. return new Box($width, $height);
  390. }
  391. }