PageRenderTime 35ms CodeModel.GetById 11ms RepoModel.GetById 0ms app.codeStats 0ms

/lib/Milk/Utils/Graphics.php

https://github.com/geekbuntu/milk
PHP | 579 lines | 480 code | 92 blank | 7 comment | 37 complexity | c91d553b04188b06b805b68b161eb897 MD5 | raw file
Possible License(s): GPL-2.0
  1. <?php
  2. namespace Milk\Utils;
  3. class Graphics {
  4. public static function resample($image, $q=90, $w=null, $h=null, $to_type=null){
  5. if (!file_exists($image))
  6. return false;
  7. //! Get image size, type and attributes
  8. list($width, $height, $type, $attr) = getimagesize($image);
  9. //! If image is invalid
  10. if ($type == null || $width == null || $height == null)
  11. return false;
  12. //! Check if we are resizing
  13. if (!$w)
  14. $w = $width;
  15. if (!$h)
  16. $h = $height;
  17. //! Calculate ratio
  18. $ratio = $width/$height;
  19. if ($w > $h)
  20. $h = $w/$ratio;
  21. else
  22. $w = $h/$ratio;
  23. if (!$to_type)
  24. $to_type = $type;
  25. $target = imagecreatetruecolor($w, $h);
  26. switch ($type) {
  27. case IMAGETYPE_JPEG:
  28. $source = imagecreatefromjpeg($image);
  29. break;
  30. case IMAGETYPE_PNG:
  31. $source = imagecreatefrompng($image);
  32. break;
  33. case IMAGETYPE_GIF:
  34. $source = imagecreatefromgif($image);
  35. break;
  36. }
  37. //! Copy source image to target, and resize if necessary
  38. imagecopyresampled(
  39. $target,
  40. $source,
  41. 0,
  42. 0,
  43. 0,
  44. 0,
  45. $w,
  46. $h,
  47. $width,
  48. $height
  49. );
  50. switch ($to_type) {
  51. case IMAGETYPE_JPEG:
  52. imagejpeg($target, $image, $q);
  53. break;
  54. case IMAGETYPE_PNG:
  55. imagepng($target, $image, $q, PNG_ALL_FILTERS);
  56. break;
  57. case IMAGETYPE_GIF:
  58. imagegif($target, $image);
  59. break;
  60. }
  61. imagedestroy($target);
  62. imagedestroy($source);
  63. }
  64. }
  65. namespace Milk\Utils\Graphics;
  66. use \StdClass;
  67. class SpriteGen {
  68. const REGEX_NAME = '/sprite:\s*(.*);/i';
  69. const REGEX_IMAGE = '/sprite-image:\s*url\((.*)\);/i';
  70. const REGEX_LAYOUT = '/sprite-layout:\s*(.*);/i';
  71. const REGEX_MARGIN = '/sprite-margin:\s*(.*);/i';
  72. const REGEX_BG = '/sprite-background:\s*(.*);/i';
  73. const REGEX_COLORS = '/sprite-colorcount:\s*(.*);/i';
  74. const REGEX_DATA = '/sprite-dataurl:\s*(.*);/i';
  75. const REGEX_SPRITE_TAG = '(?:\s+.*;\s+){1,5}';
  76. const REGEX_SPRITE_REF = 'sprite-ref:\s*';
  77. const REGEX_START_TAG = '\/\*\*\s+';
  78. const REGEX_END_TAG = '\*\/';
  79. const REGEX_BG_TAG = '/background[-image]*:\s*url\((.*)\)(\s*.*);\s*';
  80. private $files = array();
  81. private $matches = array();
  82. private $sprites = array();
  83. private $css = '';
  84. private $css_suffix = '_sprite';
  85. private $output_dir = './';
  86. public function __construct($files, $output_dir=null, $css_output_dir=null) {
  87. $this->output_dir = $output_dir;
  88. $this->css_output_dir = $css_output_dir;
  89. if (isset($files)) {
  90. $this->parseFiles($files);
  91. $this->sortImages();
  92. $this->createSprite();
  93. $this->createCSS();
  94. }
  95. }
  96. public function parseFiles($files){
  97. if (!is_array($files))
  98. $files = array();
  99. foreach ($files as $file) {
  100. if (!file_exists($file))
  101. trigger_error("$file does not exist");
  102. if (!is_readable($file))
  103. trigger_error("$file is not readable");
  104. $this->files[] = $file;
  105. $this->css .= file_get_contents($file)."\n";
  106. }
  107. $regex =
  108. '/'.
  109. self::REGEX_START_TAG.
  110. 'sprite\s*:.*;'.
  111. self::REGEX_SPRITE_TAG.
  112. self::REGEX_END_TAG.'/i';
  113. $matches = array();
  114. preg_match_all($regex, $this->css, $matches);
  115. $i=0;
  116. $matches = $matches[0];
  117. foreach($matches as $match){
  118. $this->matches[] = $match;
  119. // remove matche from css
  120. $this->css = preg_replace($regex, '', $this->css);
  121. $name = $this->parseTag(self::REGEX_NAME, $match);
  122. if (!empty($name)) {
  123. if (isset($this->sprites[$name]))
  124. continue;
  125. $url = trim($this->parseTag(self::REGEX_IMAGE, $match), '\'\""');
  126. $sprite = new StdClass();
  127. $sprite->url = $url;
  128. $sprite->filename = end(explode('/', $url));
  129. $sprite->layout = $this->parseTag(self::REGEX_LAYOUT, $match);
  130. $sprite->margin = (int)$this->parseTag(self::REGEX_MARGIN, $match);
  131. $sprite->bg = $this->parseTag(self::REGEX_BG, $match);
  132. $sprite->colors = $this->parseTag(self::REGEX_COLORS, $match);
  133. $sprite->images = $this->collectReferences($name);
  134. $sprite->type = $this->getImageTypeByExt($url);
  135. $sprite->width = 0;
  136. $sprite->height = 0;
  137. $this->sprites[$name] = $sprite;
  138. }
  139. }
  140. }
  141. private function parseTag($regex, $string) {
  142. preg_match($regex, $string, $matches);
  143. return (isset($matches[1])) ? $matches[1] : '';
  144. }
  145. private function collectReferences($name) {
  146. $regex =
  147. self::REGEX_BG_TAG.
  148. self::REGEX_START_TAG.
  149. self::REGEX_SPRITE_REF.
  150. $name.'; '.
  151. self::REGEX_END_TAG.
  152. '/i';
  153. preg_match_all($regex, $this->css, $matches);
  154. $replaces = $matches[0];
  155. $locations = $matches[1];
  156. $suffixes = $matches[2];
  157. $i = 0;
  158. $images = array();
  159. foreach ($locations as $location => $val) {
  160. $name = trim($val,'\'\""');
  161. $image = new stdClass();
  162. if (!strpos('http', $name))
  163. $name = "http:" . $name;
  164. $image->location = $name;
  165. $image->replace = $replaces[$i];
  166. $image->suffix = $suffixes[$i];
  167. $image->repeat = $this->getBGRepeat($suffixes[$i]);
  168. $image->align = $this->getBGAlign($suffixes[$i]);
  169. list($width, $height, $type, $attr) = @getimagesize($name);
  170. if ($width == null || $height == null || $type == null)
  171. continue;
  172. $image->width = $width;
  173. $image->type = $type;
  174. $image->height = $height;
  175. $images[$name] = $image;
  176. $i++;
  177. }
  178. return $images;
  179. }
  180. private function getBGRepeat($str) {
  181. $str = str_replace(';', '', $str);
  182. $str = strtolower($str);
  183. $arr = explode(' ', $str);
  184. if (in_array('repeat-x', $arr))
  185. $result = 'repeat-x';
  186. elseif (in_array('repeat-y', $arr))
  187. $result = 'repeat-y';
  188. else
  189. $result = 'no-repeat';
  190. return $result;
  191. }
  192. private function getBGAlign($str) {
  193. $str = str_replace(';', '', $str);
  194. $str = strtolower($str);
  195. $arr = explode(' ', $str);
  196. if (in_array('left', $arr))
  197. $result = 'left';
  198. elseif (in_array('right', $arr))
  199. $result = 'right';
  200. else
  201. $result = '';
  202. return $result;
  203. }
  204. private function getImageTypeByExt($file) {
  205. $file = strtolower($file);
  206. $ext = end(explode('.', $file));
  207. switch ($ext) {
  208. case 'gif':
  209. return IMAGETYPE_GIF;
  210. break;
  211. case 'jpg':
  212. case 'jpeg':
  213. return IMAGETYPE_JPEG;
  214. break;
  215. case 'png':
  216. return IMAGETYPE_PNG;
  217. break;
  218. case 'bmp':
  219. return IMAGETYPE_BMP;
  220. break;
  221. case 'swf':
  222. return IMAGETYPE_SWF;
  223. break;
  224. case 'wbmp':
  225. return IMAGETYPE_WBMP;
  226. break;
  227. case 'xbm':
  228. return IMAGETYPE_XBM;
  229. break;
  230. default:
  231. return 0;
  232. }
  233. }
  234. private function sortImages() {
  235. foreach ($this->sprites as $name => &$sprite) {
  236. $sum_width = 0;
  237. $sum_height = 0;
  238. $max_width = 0;
  239. $max_height = 0;
  240. $widthtarr = array();
  241. $heighttarr = array();
  242. $layout = $sprite->layout;
  243. $margin = $sprite->margin;
  244. if ($sprite->images) {
  245. foreach ($sprite->images as $iname => $image) {
  246. $widthtarr[$iname] = $image->width;
  247. $heighttarr[$iname] = $image->height;
  248. $sum_width += $image->width+$margin;
  249. $sum_height += $image->height+$margin;
  250. }
  251. // sprite images loop
  252. $sum_width += $margin;
  253. $sum_height += $margin;
  254. $max_width = max($widthtarr);
  255. $max_height = max($heighttarr);
  256. $min_width = min($widthtarr);
  257. $min_height = min($heighttarr);
  258. switch ($layout) {
  259. case 'horizontal':
  260. array_multisort(
  261. $heighttarr,
  262. SORT_NUMERIC,
  263. SORT_DESC,
  264. $sprite->images
  265. );
  266. $sprite->height = $max_height;
  267. $sprite->width = $sum_width;
  268. break;
  269. case 'vertical':
  270. array_multisort(
  271. $widthtarr,
  272. SORT_NUMERIC,
  273. SORT_DESC,
  274. $sprite->images
  275. );
  276. $sprite->height = $sum_height;
  277. $sprite->width = $max_width;
  278. break;
  279. case 'mixed':
  280. break;
  281. }
  282. $sum_width = $margin;
  283. $sum_height = $margin;
  284. switch ($layout) {
  285. case 'horizontal':
  286. $sum_height = 0;
  287. break;
  288. case 'vertical':
  289. $sum_width = 0;
  290. break;
  291. case 'mixed':
  292. break;
  293. }
  294. foreach ($sprite->images as $iname => $image) {
  295. switch ($image->align) {
  296. case 'left':
  297. $image->spritepos_left = 0;
  298. break;
  299. case 'right':
  300. $image->spritepos_left = $max_width-$image->width;
  301. break;
  302. }
  303. switch ($layout) {
  304. case 'horizontal':
  305. $image->spritepos_top = $sum_height;
  306. $image->spritepos_left = $sum_width;
  307. $sum_width += $image->width+$margin;
  308. break;
  309. case 'vertical':
  310. $image->spritepos_top = $sum_height;
  311. $image->spritepos_left = $sum_width;
  312. $sum_height += $image->height+$margin;
  313. break;
  314. }
  315. $sprite->images[$iname] = $image;
  316. }
  317. }
  318. }
  319. }
  320. private function createCSS() {
  321. $css = $this->css;
  322. foreach($this->sprites as $sprite) {
  323. $bgurl = 'background: url(\''.$sprite->url.'\')';
  324. if ($sprite->images) {
  325. foreach($sprite->images as $iname => $image) {
  326. $top = $image->spritepos_top;
  327. $left = $image->spritepos_left;
  328. $suffix = $image->suffix.';';
  329. $bgpos = 'background-position: -'.$left.'px -'.$top.'px;';
  330. switch ($image->align) {
  331. case 'left':
  332. $image->spritepos_left = 0;
  333. break;
  334. case 'right':
  335. $bgpos = 'background-position: right -'.$top.'px;';
  336. break;
  337. }
  338. $replace = $bgurl.$suffix.$bgpos;
  339. $css = str_replace($image->replace, $replace, $css);
  340. }
  341. }
  342. }
  343. $handle = @fopen(realpath($this->css_output_dir).'/sprites.css', "w");
  344. if (!$handle)
  345. trigger_error("Can't write to {$this->css_output_dir}sprites.css");
  346. fwrite ($handle, $css);
  347. fclose($handle);
  348. }
  349. private function createSprite() {
  350. foreach ($this->sprites as $name => $sprite) {
  351. $filename = $sprite->filename;
  352. if ($sprite->images) {
  353. switch ($sprite->type) {
  354. case IMAGETYPE_PNG:
  355. $sprite_data = imagecreatetruecolor($sprite->width, $sprite->height);
  356. imagealphablending($sprite_data, false);
  357. imagesavealpha($sprite_data, true);
  358. $transparent = imagecolorallocatealpha($sprite_data, 255, 255, 255, 127);
  359. $transparent = imagecolorallocatealpha($sprite_data, 255, 255, 255, 127);
  360. imagecolortransparent($sprite_data, $transparent);
  361. imagefilledrectangle($sprite_data, 0, 0, $sprite->width, $sprite->height, $transparent);
  362. break;
  363. case IMAGETYPE_JPEG:
  364. $sprite_data = imagecreatetruecolor($sprite->width, $sprite->height);
  365. $transparent = imagecolorallocate($sprite_data, 255, 255, 255); // white
  366. imagefilledrectangle($sprite_data, 0, 0, $sprite->width, $sprite->height, $transparent);
  367. break;
  368. case IMAGETYPE_GIF:
  369. $sprite_data = imagecreate($sprite->width, $sprite->height);
  370. break;
  371. }
  372. foreach ($sprite->images as $iname => $image) {
  373. switch ($image->type) {
  374. case IMAGETYPE_PNG:
  375. $image_data = @imagecreatefrompng($image->location);
  376. break;
  377. case IMAGETYPE_JPEG:
  378. $image_data = @imagecreatefromjpeg($image->location);
  379. break;
  380. case IMAGETYPE_GIF:
  381. $image_data = @imagecreatefromgif($image->location);
  382. break;
  383. case IMAGETYPE_SWF:
  384. $image_data = @imagecreatefromswf($image->location);
  385. break;
  386. case IMAGETYPE_WBMP:
  387. $image_data = @imagecreatefromwbmp($image->location);
  388. break;
  389. case IMAGETYPE_XBM:
  390. $image_data = @imagecreatefromxbm($image->location);
  391. break;
  392. }
  393. switch ($image->align) {
  394. case 'left':
  395. $image->spritepos_left = 0;
  396. break;
  397. case 'right':
  398. $image->spritepos_left = $sprite->width - $image->width;
  399. break;
  400. }
  401. // stretching to full width:
  402. switch ($image->repeat) {
  403. case 'repeat-x':
  404. imagecopyresampled(
  405. $sprite_data,
  406. $image_data,
  407. $image->spritepos_left,
  408. $image->spritepos_top,
  409. 0,
  410. 0,
  411. $sprite->width,
  412. $image->height,
  413. $image->width,
  414. $image->height
  415. );
  416. break;
  417. case 'repeat-y':
  418. imagecopyresampled(
  419. $sprite_data,
  420. $image_data,
  421. $image->spritepos_left,
  422. $image->spritepos_top,
  423. 0,
  424. 0,
  425. $image->width,
  426. $image->height,
  427. $image->width,
  428. $image->height
  429. );
  430. break;
  431. default:
  432. imagecopy(
  433. $sprite_data,
  434. $image_data,
  435. $image->spritepos_left,
  436. $image->spritepos_top,
  437. 0,
  438. 0,
  439. $image->width,
  440. $image->height
  441. );
  442. }
  443. imagedestroy($image_data);
  444. }
  445. $result = 0;
  446. $file = realpath($this->output_dir)."/".$sprite->filename;
  447. switch ($sprite->type) {
  448. case IMAGETYPE_GIF:
  449. $result = @imagegif($sprite_data, $file);
  450. break;
  451. case IMAGETYPE_JPEG:
  452. $result = @imagejpeg($sprite_data, $file);
  453. break;
  454. case IMAGETYPE_PNG:
  455. $result = @imagepng($sprite_data, $file);
  456. break;
  457. default:
  458. $result = @imagepng($sprite_data, $file);
  459. }
  460. imagedestroy($sprite_data);
  461. }
  462. }
  463. }
  464. }