Image.php 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426
  1. <?php
  2. /**
  3. * The Feeler framework, licensed under Mit license
  4. * Author: Rick Guo
  5. */
  6. class Image{
  7. public static $typeMappings = array(
  8. "jpg" => "jpeg",
  9. );
  10. public $font;
  11. protected static $instance;
  12. function __construct(){
  13. $this->font = Yii::app()->basePath."/../css/fonts/yahei_mono.ttf";
  14. }
  15. protected static function instance(){
  16. if(!is_object(self::$instance)){
  17. self::$instance = new self();
  18. }
  19. return self::$instance;
  20. }
  21. public static function revertType(&$type){
  22. if(isset(self::$typeMappings[$type])){
  23. $type = self::$typeMappings[$type];
  24. }
  25. return true;
  26. }
  27. public static function destroy($res){
  28. return is_resource($res) ? imagedestroy($res) : false;
  29. }
  30. public static function create($width, $height, $color = array(255, 255, 255, 100)){
  31. if(!$width || !$height)
  32. return false;
  33. $src = imagecreatetruecolor($width, $height);
  34. if(!($color = self::getColor($src, $color)))
  35. return false;
  36. imagealphablending($src, true);
  37. imagefill($src, 0, 0, $color);
  38. imagesavealpha($src, true);
  39. return is_resource($src) ? $src : false;
  40. }
  41. public static function createFromFile($srcFile){
  42. if(!is_file($srcFile))
  43. return false;
  44. ($type = File::getExt($srcFile)) and self::revertType($type);
  45. switch($type){
  46. case "jpeg":
  47. $src = imagecreatefromjpeg($srcFile);
  48. break;
  49. case "png":
  50. $src = imagecreatefrompng($srcFile);
  51. break;
  52. case "gif":
  53. $src = imagecreatefromgif($srcFile);
  54. break;
  55. default:
  56. return false;
  57. break;
  58. }
  59. imagealphablending($src, true);
  60. imagesavealpha($src, true);
  61. return is_resource($src) ? $src : false;
  62. }
  63. public static function createFromFiles($files){
  64. $srcs = array();
  65. if(!Arr::isAvailable($files)){
  66. foreach($files as $file){
  67. if(is_file($file) && is_resource($src = self::createFromFile($file)))
  68. $srcs[md5_file($file)] = $src;
  69. }
  70. }
  71. return $srcs;
  72. }
  73. public static function setColor(&$src, $rgb = array()){
  74. if(!is_resource($src) || !Number::isInt($color = self::getColor($src, $rgb)))
  75. return false;
  76. imagealphablending($src, true);
  77. imagefill($src, 0, 0, $color);
  78. imagesavealpha($src, true);
  79. return true;
  80. }
  81. public static function saveAs($res, $file){
  82. if(!is_resource($res) || !$file)
  83. return false;
  84. if(!File::mkdir(File::getPath($file)))
  85. return false;
  86. $ext = File::getExt($file);
  87. switch($ext){
  88. case "jpeg":
  89. $rs = imagejpeg($res, $file);
  90. break;
  91. case "png":
  92. $rs = imagepng($res, $file);
  93. break;
  94. case "gif":
  95. $rs = imagegif($res, $file);
  96. break;
  97. default:
  98. return false;
  99. break;
  100. }
  101. return $rs ? true : false;
  102. }
  103. public static function getColor($src, $rgb){
  104. if(!Arr::isAvailable($rgb))
  105. return null;
  106. $count = count($rgb);
  107. if($count != 3 && $count != 4)
  108. return null;
  109. if($count == 4){
  110. list($r, $g, $b, $a) = $rgb;
  111. $color = imagecolorallocatealpha($src, $r, $g, $b, $a);
  112. }
  113. else if($count == 3){
  114. list($r, $g, $b) = $rgb;
  115. $color = imagecolorallocate($src, $r, $g, $b);
  116. }
  117. return $color;
  118. }
  119. public static function fillRectangle(&$src, $rectangle = array()){
  120. if(!is_resource($src) || !Arr::isAvailable($rectangle, array("w", "h", "x", "y", "color")))
  121. return false;
  122. if(!($rectangle["color"] = self::getColor($src, $rectangle["color"])))
  123. return false;
  124. return imagefilledrectangle($src, $rectangle["x"], $rectangle["y"], $rectangle["x"] + $rectangle["w"], $rectangle["y"] + $rectangle["h"], $rectangle["color"]);
  125. }
  126. public static function getImageResSize($res){
  127. $imageSize = array();
  128. $file = sys_get_temp_dir()."/".Random::uuid().".png";
  129. if(self::saveAs($res, $file)){
  130. $imageSize = getimagesize($file);
  131. $imageSize = array($imageSize[0], $imageSize[1]);
  132. File::rm($file);
  133. }
  134. return $imageSize;
  135. }
  136. private function _getStringRectangleSize($string, $font){
  137. $size = array();
  138. if($string && isset($font["size"]) && Number::isNumeric($font["size"]) && $font["size"] > 0){
  139. if(!isset($font["angle"]) || !Number::isNumeric($font["angle"]))
  140. $font["angle"] = 0;
  141. $size = imageftbbox($font["size"], $font["angle"], $this->font, $string);
  142. $size = array($size[2] - $size[0], $size[7] - $size[1]);
  143. $size = array(abs($size[0]), abs($size[1]));
  144. }
  145. return $size;
  146. }
  147. public static function getStringRectangleSize($string, $font){
  148. return self::instance()->_getStringRectangleSize($string, $font);
  149. }
  150. private function _sign(&$src, $content, $font = array()){
  151. if(!is_resource($src) || !$content || !Arr::isAvailable($font, array("size", "color", "x", "y")) ||
  152. !Number::isInt($fontColor = self::getColor($src, $font["color"])) || !($imageSize = self::getImageResSize($src)))
  153. return false;
  154. if(!($stringRectangleSize = self::getStringRectangleSize($content, $font)))
  155. return false;
  156. $fontYOffset = $stringRectangleSize[1] - 3;
  157. list($font["w"], $font["h"]) = $stringRectangleSize;
  158. $position = self::calcPosition($font, array("w" => $imageSize[0], "h" => $imageSize[1]));
  159. $position[1] += $fontYOffset;
  160. list($font["x"], $font["y"]) = $position;
  161. if(!isset($font["angle"]) || !Number::isInt($font["angle"])){
  162. $font["angle"] = 0;
  163. }
  164. $rs = imagefttext($src, $font["size"], $font["angle"], $font["x"], $font["y"], $fontColor, $this->font, $content);
  165. return $rs ? true : false;
  166. }
  167. public static function sign(&$src, $content, $font = array()){
  168. return self::instance()->_sign($src, $content, $font);
  169. }
  170. public static function zoom(&$src, $size){
  171. if(!is_resource($src) || (!Arr::isAvailable($size) && !Number::isFloat($size)))
  172. return false;
  173. $srcSize = self::getImageResSize($src);
  174. if(Number::isNumeric($size))
  175. $size = array(Number::truncateDecimal($srcSize[0] * $size), Number::truncateDecimal($srcSize[1] * $size));
  176. $res = $src;
  177. $src = self::create($size[0], $size[1]);
  178. return imagecopyresampled(
  179. $src, $res,
  180. 0, 0, 0, 0,
  181. $size[0], $size[1], $srcSize[0], $srcSize[1]
  182. );
  183. }
  184. public static function calcPosition($elementInfo, $containerInfo){
  185. if(!Arr::isAvailable($elementInfo, array("w", "h", "x", "y")) || !Arr::isAvailable($containerInfo, array("w", "h")))
  186. return array();
  187. $param = array();
  188. $param["x"] = trim($elementInfo["x"]);
  189. $param["y"] = trim($elementInfo["y"]);
  190. $param["x"] = str_replace(" ", " ", $param["x"]);
  191. $param["y"] = str_replace(" ", " ", $param["y"]);
  192. $param["x"] = explode(" ", $param["x"], 2);
  193. $param["y"] = explode(" ", $param["y"], 2);
  194. $elementInfo["x"] = 0;
  195. foreach($param["x"] as $key => $val){
  196. if(empty($val))
  197. continue;
  198. if(Number::isNumeric($val)){
  199. $elementInfo["x"] += Number::truncateDecimal($val, 1);
  200. }
  201. else{
  202. if($val == "left"){
  203. $elementInfo["x"] += 0;
  204. }
  205. else if($val == "center"){
  206. $elementInfo["x"] += Number::truncateDecimal(($containerInfo["w"] - $elementInfo["w"]) / 2, 1);
  207. }
  208. else if($val == "right"){
  209. $elementInfo["x"] += Number::truncateDecimal($containerInfo["w"] - $elementInfo["w"], 1);
  210. }
  211. }
  212. }
  213. $elementInfo["y"] = 0;
  214. foreach($param["y"] as $key => $val){
  215. if(empty($val))
  216. continue;
  217. if(Number::isNumeric($val)){
  218. $elementInfo["y"] += Number::truncateDecimal($val, 1);
  219. }
  220. else{
  221. if($val == "top"){
  222. $elementInfo["y"] += 0;
  223. }
  224. else if($val == "center"){
  225. $elementInfo["y"] += Number::truncateDecimal(($containerInfo["h"] - $elementInfo["h"]) / 2, 1);
  226. }
  227. else if($val == "bottom"){
  228. $elementInfo["y"] += Number::truncateDecimal($containerInfo["h"] - $elementInfo["h"], 1);
  229. }
  230. }
  231. }
  232. return array($elementInfo["x"], $elementInfo["y"]);
  233. }
  234. public static function crop(&$src){
  235. $params = func_get_args();
  236. if(!is_resource($src) || !isset($params[1]) || !Arr::isAvailable($params[1], array("x", "y", "w", "h")))
  237. return false;
  238. if(isset($params[2]) && !Arr::isAvailable($params[2], array("x", "y", "w", "h")))
  239. return false;
  240. $params = Arr::getVals($params, array(0, 1, 2));
  241. $imageSize = self::getImageResSize($src);
  242. if(!isset($params[2])){
  243. $vals = Arr::getVals($params[1], array(
  244. "destX" => "x",
  245. "destY" => "y",
  246. "destW" => "w",
  247. "destH" => "h",
  248. "color" => "color",
  249. ));
  250. extract($vals);
  251. return imagecopyresampled(
  252. $src, $src,
  253. 0, 0, $destX, $destY,
  254. $destW, $destH, $destW, $destH
  255. );
  256. }
  257. else{
  258. $elementInfo = Arr::getVals($params[1], array("x", "y", "w", "h"));
  259. $containerInfo = Arr::getVals($params[2], array("x", "y", "w", "h"));
  260. if(!($position = self::calcPosition(array("w" => $elementInfo["w"], "h" => $elementInfo["h"], "x" => $containerInfo["x"], "y" => $containerInfo["y"]), $containerInfo)))
  261. return false;
  262. $containerInfo["x"] = $position[0];
  263. $containerInfo["y"] = $position[1];
  264. $elementInfo = Arr::getVals($elementInfo, array(
  265. "srcX" => "x",
  266. "srcY" => "y",
  267. "srcW" => "w",
  268. "srcH" => "h",
  269. ));
  270. extract($elementInfo);
  271. $containerInfo = Arr::getVals($containerInfo, array(
  272. "destX" => "x",
  273. "destY" => "y",
  274. "destW" => "w",
  275. "destH" => "h",
  276. "color" => "color",
  277. ));
  278. extract($containerInfo);
  279. $prevSrc = $src;
  280. $src = isset($color) ? self::create($destW, $destH, $color) : self::create($destW, $destH);
  281. return $src !== false ? imagecopyresampled(
  282. $src, $prevSrc,
  283. $destX, $destY, $srcX, $srcY,
  284. $srcW, $srcH, $srcW, $srcH
  285. ) : false;
  286. }
  287. }
  288. public static function setBorder(&$src, $borderColor = array(0, 0, 0, 1), $borderSize = 1){
  289. if(!is_resource($src) || !Number::isInt($borderSize) || $borderSize < 1){
  290. return false;
  291. }
  292. if(!($imageSize = self::getImageResSize($src)))
  293. return false;
  294. $containerSize = array($borderSize * 2 + $imageSize[0], $borderSize * 2 + $imageSize[1]);
  295. return self::crop(
  296. $src,
  297. array("x" => 0, "y" => 0, "w" => $imageSize[0], "h" => $imageSize[1]),
  298. array("x" => $borderSize, "y" => $borderSize, "w" => $containerSize[0], "h" => $containerSize[1], "color" => $borderColor)
  299. );
  300. }
  301. public static function puzzle($containerInfo, $images){
  302. $params = func_get_args();
  303. if(!Arr::isAvailable($containerInfo, array("w", "h")))
  304. return false;
  305. foreach($images as $key => $image){
  306. if(!Arr::isAvailable($image, array("res", "layer_num", "x", "y", "w", "h")))
  307. unset($images[$key]);
  308. }
  309. if(!$images)
  310. return false;
  311. Arr::sortByField($images, "layer_num", "DESC");
  312. if(isset($containerInfo["bg_color"]))
  313. $dest = self::create($containerInfo["w"], $containerInfo["h"], $containerInfo["bg_color"]);
  314. else
  315. $dest = self::create($containerInfo["w"], $containerInfo["h"]);
  316. foreach($images as $image){
  317. $position = self::calcPosition($image, $containerInfo);
  318. $image["x"] = $position[0];
  319. $image["y"] = $position[1];
  320. imagecopyresampled(
  321. $dest, $image["res"],
  322. $image["x"], $image["y"],
  323. 0, 0,
  324. $image["w"], $image["h"],
  325. $image["w"], $image["h"]
  326. );
  327. }
  328. return $dest;
  329. }
  330. }