ImagickService.php 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815
  1. <?php
  2. /**
  3. * Created by PhpStorm.
  4. * User: Administrator
  5. * Date: 2022-06-24
  6. * Time: 12:23
  7. */
  8. namespace app\model\image;
  9. /**
  10. * 图片处理服务类
  11. * 使用php扩展服务Imagick实现
  12. * ImageMagick 官网地址 [url]http:www.imagemagick.org/script/index.php[/url]
  13. *
  14. * @author weiguang3
  15. * @since 20140403
  16. */
  17. class ImagickService
  18. {
  19. private $image = null;
  20. private $type = null;
  21. // 构造函数
  22. public function __construct()
  23. {
  24. }
  25. // 析构函数
  26. public function __destruct()
  27. {
  28. if ($this->image !== null)
  29. $this->image->destroy();
  30. }
  31. public function init()
  32. {
  33. }
  34. public function isBinary($data)
  35. {
  36. if (is_string($data)) {
  37. $mime = finfo_buffer(finfo_open(FILEINFO_MIME_TYPE), $data);
  38. return (substr($mime, 0, 4) != 'text' && $mime != 'application/x-empty');
  39. }
  40. return false;
  41. }
  42. // 载入图像
  43. public function open($path)
  44. {
  45. if($this->isBinary($path)){
  46. $this->image = new \Imagick();
  47. $this->image->readImageBlob($path);
  48. }else{
  49. $this->image = new \Imagick($path);
  50. }
  51. if ($this->image) {
  52. $this->type = strtolower($this->image->getImageFormat());
  53. }
  54. $size = $this->image->getImagePage();
  55. return $this;
  56. }
  57. public function getImageParam(){
  58. // $size = $this->image->getImagePage();
  59. $size = array(
  60. 'width' => $this->image->getImageWidth(),
  61. 'height' => $this->image->getImageHeight(),
  62. );
  63. return $size;
  64. }
  65. /**
  66. * 图片裁剪
  67. * 裁剪规则:
  68. * 1. 高度为空或为零 按宽度缩放 高度自适应
  69. * 2. 宽度为空或为零 按高度缩放 宽度自适应
  70. * 3. 宽度,高度到不为空或为零 按宽高比例等比例缩放裁剪 默认从头部居中裁剪
  71. * @param number $width
  72. * @param number $height
  73. */
  74. public function resize($width = 0, $height = 0)
  75. {
  76. if ($width == 0 && $height == 0) {
  77. return;
  78. }
  79. $color = '';// 'rgba(255,255,255,1)';
  80. $size = $this->image->getImagePage();
  81. //原始宽高
  82. $src_width = $size ['width'];
  83. $src_height = $size ['height'];
  84. //按宽度缩放 高度自适应
  85. if ($width != 0 && $height == 0) {
  86. if ($src_width > $width) {
  87. $height = intval($width * $src_height / $src_width);
  88. if ($this->type == 'gif') {
  89. $this->_resizeGif($width, $height);
  90. } else {
  91. $this->image->thumbnailImage($width, $height, true);
  92. }
  93. }
  94. return;
  95. }
  96. //按高度缩放 宽度自适应
  97. if ($width == 0 && $height != 0) {
  98. if ($src_height > $height) {
  99. $width = intval($src_width * $height / $src_height);
  100. if ($this->type == 'gif') {
  101. $this->_resizeGif($width, $height);
  102. } else {
  103. $this->image->thumbnailImage($width, $height, true);
  104. }
  105. }
  106. return;
  107. }
  108. //缩放的后的尺寸
  109. $crop_w = $width;
  110. $crop_h = $height;
  111. //缩放后裁剪的位置
  112. $crop_x = 0;
  113. $crop_y = 0;
  114. if (($src_width / $src_height) < ($width / $height)) {
  115. //宽高比例小于目标宽高比例 宽度等比例放大 按目标高度从头部截取
  116. $crop_h = intval($src_height * $width / $src_width);
  117. //从顶部裁剪 不用计算 $crop_y
  118. } else {
  119. //宽高比例大于目标宽高比例 高度等比例放大 按目标宽度居中裁剪
  120. $crop_w = intval($src_width * $height / $src_height);
  121. $crop_x = intval(($crop_w - $width) / 2);
  122. }
  123. if ($this->type == 'gif') {
  124. $this->_resizeGif($crop_w, $crop_h, true, $width, $height, $crop_x, $crop_y);
  125. } else {
  126. $this->image->thumbnailImage($crop_w, $crop_h, true);
  127. $this->image->cropImage($width, $height, $crop_x, $crop_y);
  128. }
  129. }
  130. /**
  131. * 处理gif图片 需要对每一帧图片处理
  132. * @param unknown $t_w 缩放宽
  133. * @param unknown $t_h 缩放高
  134. * @param string $isCrop 是否裁剪
  135. * @param number $c_w 裁剪宽
  136. * @param number $c_h 裁剪高
  137. * @param number $c_x 裁剪坐标 x
  138. * @param number $c_y 裁剪坐标 y
  139. */
  140. private function _resizeGif($t_w, $t_h, $isCrop = false, $c_w = 0, $c_h = 0, $c_x = 0, $c_y = 0)
  141. {
  142. $dest = new \Imagick();
  143. $color_transparent = new \ImagickPixel("transparent"); //透明色
  144. foreach ($this->image as $img) {
  145. $page = $img->getImagePage();
  146. $tmp = new Imagick();
  147. $tmp->newImage($page['width'], $page['height'], $color_transparent, 'gif');
  148. $tmp->compositeImage($img, \Imagick::COMPOSITE_OVER, $page['x'], $page['y']);
  149. $tmp->thumbnailImage($t_w, $t_h, true);
  150. if ($isCrop) {
  151. $tmp->cropImage($c_w, $c_h, $c_x, $c_y);
  152. }
  153. $dest->addImage($tmp);
  154. $dest->setImagePage($tmp->getImageWidth(), $tmp->getImageHeight(), 0, 0);
  155. $dest->setImageDelay($img->getImageDelay());
  156. $dest->setImageDispose($img->getImageDispose());
  157. }
  158. $this->image->destroy();
  159. $this->image = $dest;
  160. }
  161. /**
  162. * 更改图像大小
  163. * $fit: 适应大小方式
  164. * 'force': 把图片强制变形成 $width X $height 大小
  165. * 'scale': 按比例在安全框 $width X $height 内缩放图片, 输出缩放后图像大小 不完全等于 $width X $height
  166. * 'scale_fill': 按比例在安全框 $width X $height 内缩放图片,安全框内没有像素的地方填充色,
  167. * 使用此参数时可设置背景填充色 $bg_color = array(255,255,255)(红,绿,蓝, 透明度)
  168. * 透明度(0不透明-127完全透明)) 其它: 智能模能 缩放图像并载取图像的中间部分 $width X $height 像素大小
  169. * $fit = 'force','scale','scale_fill' 时: 输出完整图像
  170. * $fit = 图像方位值 时, 输出指定位置部分图像 字母与图像的对应关系如下:
  171. * north_west north north_east
  172. * west center east
  173. * south_west south south_east
  174. */
  175. public function resize_to($width = 100, $height = 100, $fit = 'center', $fill_color = array(255, 255, 255, 0))
  176. {
  177. switch ($fit) {
  178. case 'force' :
  179. if ($this->type == 'gif') {
  180. $image = $this->image;
  181. $canvas = new \Imagick();
  182. $images = $image->coalesceImages();
  183. foreach ($images as $frame) {
  184. $img = new \Imagick();
  185. $img->readImageBlob($frame);
  186. $img->thumbnailImage($width, $height, false);
  187. $canvas->addImage($img);
  188. $canvas->setImageDelay($img->getImageDelay());
  189. }
  190. $image->destroy();
  191. $this->image = $canvas;
  192. } else {
  193. $this->image->thumbnailImage($width, $height, false);
  194. }
  195. break;
  196. case 'scale' :
  197. if ($this->type == 'gif') {
  198. $image = $this->image;
  199. $images = $image->coalesceImages();
  200. $canvas = new \Imagick();
  201. foreach ($images as $frame) {
  202. $img = new \Imagick();
  203. $img->readImageBlob($frame);
  204. $img->thumbnailImage($width, $height, true);
  205. $canvas->addImage($img);
  206. $canvas->setImageDelay($img->getImageDelay());
  207. }
  208. $image->destroy();
  209. $this->image = $canvas;
  210. } else {
  211. $this->image->thumbnailImage($width, $height, true);
  212. }
  213. break;
  214. case 'scale_fill' :
  215. $size = $this->image->getImagePage();
  216. $src_width = $size ['width'];
  217. $src_height = $size ['height'];
  218. $x = 0;
  219. $y = 0;
  220. $dst_width = $width;
  221. $dst_height = $height;
  222. if ($src_width * $height > $src_height * $width) {
  223. $dst_height = intval($width * $src_height / $src_width);
  224. $y = intval(($height - $dst_height) / 2);
  225. } else {
  226. $dst_width = intval($height * $src_width / $src_height);
  227. $x = intval(($width - $dst_width) / 2);
  228. }
  229. $image = $this->image;
  230. $canvas = new \Imagick();
  231. $color = 'rgba(' . $fill_color [0] . ',' . $fill_color [1] . ',' . $fill_color [2] . ',' . $fill_color [3] . ')';
  232. if ($this->type == 'gif') {
  233. $images = $image->coalesceImages();
  234. foreach ($images as $frame) {
  235. $frame->thumbnailImage($width, $height, true);
  236. $draw = new \ImagickDraw();
  237. $draw->composite($frame->getImageCompose(), $x, $y, $dst_width, $dst_height, $frame);
  238. $img = new \Imagick();
  239. $img->newImage($width, $height, $color, 'gif');
  240. $img->drawImage($draw);
  241. $canvas->addImage($img);
  242. $canvas->setImageDelay($img->getImageDelay());
  243. $canvas->setImagePage($width, $height, 0, 0);
  244. }
  245. } else {
  246. $image->thumbnailImage($width, $height, true);
  247. $draw = new \ImagickDraw();
  248. $draw->composite($image->getImageCompose(), $x, $y, $dst_width, $dst_height, $image);
  249. $canvas->newImage($width, $height, $color, $this->get_type());
  250. $canvas->drawImage($draw);
  251. $canvas->setImagePage($width, $height, 0, 0);
  252. }
  253. $image->destroy();
  254. $this->image = $canvas;
  255. break;
  256. default :
  257. $size = $this->image->getImagePage();
  258. $src_width = $size ['width'];
  259. $src_height = $size ['height'];
  260. $crop_x = 0;
  261. $crop_y = 0;
  262. $crop_w = $src_width;
  263. $crop_h = $src_height;
  264. if ($src_width * $height > $src_height * $width) {
  265. $crop_w = intval($src_height * $width / $height);
  266. } else {
  267. $crop_h = intval($src_width * $height / $width);
  268. }
  269. switch ($fit) {
  270. case 'north_west' :
  271. $crop_x = 0;
  272. $crop_y = 0;
  273. break;
  274. case 'north' :
  275. $crop_x = intval(($src_width - $crop_w) / 2);
  276. $crop_y = 0;
  277. break;
  278. case 'north_east' :
  279. $crop_x = $src_width - $crop_w;
  280. $crop_y = 0;
  281. break;
  282. case 'west' :
  283. $crop_x = 0;
  284. $crop_y = intval(($src_height - $crop_h) / 2);
  285. break;
  286. case 'center' :
  287. $crop_x = intval(($src_width - $crop_w) / 2);
  288. $crop_y = intval(($src_height - $crop_h) / 2);
  289. break;
  290. case 'east' :
  291. $crop_x = $src_width - $crop_w;
  292. $crop_y = intval(($src_height - $crop_h) / 2);
  293. break;
  294. case 'south_west' :
  295. $crop_x = 0;
  296. $crop_y = $src_height - $crop_h;
  297. break;
  298. case 'south' :
  299. $crop_x = intval(($src_width - $crop_w) / 2);
  300. $crop_y = $src_height - $crop_h;
  301. break;
  302. case 'south_east' :
  303. $crop_x = $src_width - $crop_w;
  304. $crop_y = $src_height - $crop_h;
  305. break;
  306. default :
  307. $crop_x = intval(($src_width - $crop_w) / 2);
  308. $crop_y = intval(($src_height - $crop_h) / 2);
  309. }
  310. $image = $this->image;
  311. $canvas = new \Imagick();
  312. if ($this->type == 'gif') {
  313. $images = $image->coalesceImages();
  314. foreach ($images as $frame) {
  315. $img = new \Imagick();
  316. $img->readImageBlob($frame);
  317. $img->cropImage($crop_w, $crop_h, $crop_x, $crop_y);
  318. $img->thumbnailImage($width, $height, true);
  319. $canvas->addImage($img);
  320. $canvas->setImageDelay($img->getImageDelay());
  321. $canvas->setImagePage($width, $height, 0, 0);
  322. }
  323. } else {
  324. $image->cropImage($crop_w, $crop_h, $crop_x, $crop_y);
  325. $image->thumbnailImage($width, $height, true);
  326. $canvas->addImage($image);
  327. $canvas->setImagePage($width, $height, 0, 0);
  328. }
  329. $image->destroy();
  330. $this->image = $canvas;
  331. }
  332. }
  333. // 添加水印图片
  334. public function add_watermark($path, $x = 0, $y = 0)
  335. {
  336. $watermark = new \Imagick($path);
  337. $draw = new \ImagickDraw();
  338. $draw->composite($watermark->getImageCompose(), $x, $y, $watermark->getImageWidth(), $watermark->getimageheight(), $watermark);
  339. if ($this->type == 'gif') {
  340. $image = $this->image;
  341. $canvas = new \Imagick();
  342. $images = $image->coalesceImages();
  343. foreach ($image as $frame) {
  344. $img = new \Imagick();
  345. $img->readImageBlob($frame);
  346. $img->drawImage($draw);
  347. $canvas->addImage($img);
  348. $canvas->setImageDelay($img->getImageDelay());
  349. }
  350. $image->destroy();
  351. $this->image = $canvas;
  352. } else {
  353. $this->image->drawImage($draw);
  354. }
  355. }
  356. // 添加水印文字
  357. public function add_text($text, $x = 0, $y = 0, $angle = 0, $style = array())
  358. {
  359. $draw = new \ImagickDraw();
  360. if (isset ($style ['font']))
  361. $draw->setFont($style ['font']);
  362. if (isset ($style ['font_size']))
  363. $draw->setFontSize($style ['font_size']);
  364. if (isset ($style ['fill_color']))
  365. $draw->setFillColor($style ['fill_color']);
  366. $draw->setTextAlignment( 2 );
  367. if (isset ($style ['under_color']))
  368. $draw->setTextUnderColor($style ['under_color']);
  369. if ($this->type == 'gif') {
  370. foreach ($this->image as $frame) {
  371. $frame->annotateImage($draw, $x, $y, $angle, $text);
  372. }
  373. } else {
  374. $this->image->annotateImage($draw, $x, $y, $angle, $text);
  375. }
  376. }
  377. // 保存到指定路径
  378. public function save_to($path, $compress = 60)
  379. {
  380. //压缩图片质量
  381. // $this->image->setImageFormat('JPEG');
  382. $this->image->setImageCompression(\Imagick::COMPRESSION_JPEG);
  383. // $compress = $this->image->getImageCompressionQuality() * 0.60;
  384. //
  385. // if ($compress == 0) {
  386. // $compress = 60;
  387. // }
  388. $this->image->setImageCompressionQuality($compress);
  389. $this->image->stripImage();
  390. if ($this->type == 'gif') {
  391. $this->image->writeImages($path, true);
  392. } else {
  393. $this->image->writeImage($path);
  394. }
  395. }
  396. // 输出图像
  397. public function output($header = true)
  398. {
  399. if ($header)
  400. header('Content-type: ' . $this->type);
  401. echo $this->image->getImagesBlob();
  402. }
  403. public function get_width()
  404. {
  405. $size = $this->image->getImagePage();
  406. return $size ['width'];
  407. }
  408. public function get_height()
  409. {
  410. $size = $this->image->getImagePage();
  411. return $size ['height'];
  412. }
  413. // 设置图像类型, 默认与源类型一致
  414. public function set_type($type = 'png')
  415. {
  416. $this->type = $type;
  417. $this->image->setImageFormat($type);
  418. }
  419. // 获取源图像类型
  420. public function get_type()
  421. {
  422. return $this->type;
  423. }
  424. public function get_file_size()
  425. {
  426. if ($this->image) {
  427. return 0;//$this->image->getImageLength(); getImageLength not find
  428. } else {
  429. return 0;
  430. }
  431. }
  432. public function get_file_type()
  433. {
  434. if ($this->image) {
  435. return $this->image->getimagemimetype();
  436. } else {
  437. return 0;
  438. }
  439. }
  440. public function get_sha1()
  441. {
  442. if ($this->image) {
  443. return sha1($this->image->__tostring());
  444. } else {
  445. return '';
  446. }
  447. }
  448. // 当前对象是否为图片
  449. public function is_image()
  450. {
  451. if ($this->image)
  452. return true;
  453. else
  454. return false;
  455. }
  456. /*
  457. * 添加一个边框 $width: 左右边框宽度 $height: 上下边框宽度 $color: 颜色: RGB 颜色 'rgb(255,0,0)' 或 16进制颜色 '#FF0000' 或颜色单词 'white'/'red'...
  458. */
  459. public function border($width, $height, $color = 'rgb(220, 220, 220)')
  460. {
  461. $color = new \ImagickPixel();
  462. $color->setColor($color);
  463. $this->image->borderImage($color, $width, $height);
  464. }
  465. public function blur($radius, $sigma)
  466. {
  467. $this->image->blurImage($radius, $sigma);
  468. } // 模糊
  469. public function gaussian_blur($radius, $sigma)
  470. {
  471. $this->image->gaussianBlurImage($radius, $sigma);
  472. } // 高斯模糊
  473. public function motion_blur($radius, $sigma, $angle)
  474. {
  475. $this->image->motionBlurImage($radius, $sigma, $angle);
  476. } // 运动模糊
  477. public function radial_blur($radius)
  478. {
  479. $this->image->radialBlurImage($radius);
  480. } // 径向模糊
  481. public function add_noise($type = null)
  482. {
  483. $this->image->addNoiseImage($type == null ? \Imagick::NOISE_IMPULSE : $type);
  484. } // 添加噪点
  485. public function level($black_point, $gamma, $white_point)
  486. {
  487. $this->image->levelImage($black_point, $gamma, $white_point);
  488. } // 调整色阶
  489. public function modulate($brightness, $saturation, $hue)
  490. {
  491. $this->image->modulateImage($brightness, $saturation, $hue);
  492. } // 调整亮度、饱和度、色调
  493. public function charcoal($radius, $sigma)
  494. {
  495. $this->image->charcoalImage($radius, $sigma);
  496. } // 素描
  497. public function oil_paint($radius)
  498. {
  499. $this->image->oilPaintImage($radius);
  500. } // 油画效果
  501. public function flop()
  502. {
  503. $this->image->flopImage();
  504. } // 水平翻转
  505. public function flip()
  506. {
  507. $this->image->flipImage();
  508. } // 垂直翻转
  509. }