PageRenderTime 56ms CodeModel.GetById 24ms RepoModel.GetById 0ms app.codeStats 1ms

/Pic.php

http://github.com/acustodioo/pic
PHP | 651 lines | 407 code | 120 blank | 124 comment | 61 complexity | 370ba15446847cf41ab243d984e65e4d MD5 | raw file
  1. <?php
  2. /**
  3. * PIC - [P]HP [I]MG [C]SS
  4. *
  5. * Com poucas linhas de código você abre, edita e salva imagens com PHP
  6. * de forma simples e rápida usando comandos CSS.
  7. *
  8. * This program is free software; you can redistribute it and/or modify
  9. * it under the terms of the GNU General Public License as published by
  10. * the Free Software Foundation; either version 2 of the License, or
  11. * (at your option) any later version.
  12. *
  13. * This program is distributed in the hope that it will be useful,
  14. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. * GNU General Public License for more details.
  17. *
  18. * You should have received a copy of the GNU General Public License
  19. * along with this program; if not, write to the Free Software
  20. * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  21. *
  22. * @copyright (C) Anderson C. Oliveira <acustodioo@gmail.com>, 2011
  23. */
  24. define('PATH_PIC_CLASS', dirname(__FILE__) . '/');
  25. class Pic {
  26. /**
  27. * Source e informações da imagem
  28. */
  29. public $img = array();
  30. /**
  31. * Caminho da imagem original
  32. */
  33. private $src = null;
  34. /**
  35. * Formatos permititos
  36. */
  37. private $mime = array(
  38. 'jpg' => 'image/jpeg',
  39. 'gif' => 'image/gif',
  40. 'png' => 'image/png',
  41. 'bmp' => 'image/x-ms-bmp'
  42. );
  43. /**
  44. * Atalhos amigáveis para os filtros
  45. */
  46. private $filters = array(
  47. 'negate' => 0, # IMG_FILTER_NEGATE
  48. 'grayscale' => 1, # IMG_FILTER_GRAYSCALE
  49. 'brightness' => 2, # IMG_FILTER_BRIGHTNESS
  50. 'contrast' => 3, # IMG_FILTER_CONTRAST
  51. 'colorize' => 4, # IMG_FILTER_COLORIZE
  52. 'edgedetect' => 5, # IMG_FILTER_EDGEDETECT
  53. 'emboss' => 6, # IMG_FILTER_EMBOSS
  54. 'gaussian-blur' => 7, # IMG_FILTER_GAUSSIAN_BLUR
  55. 'selective-blur' => 8, # IMG_FILTER_SELECTIVE_BLUR
  56. 'mean-removal' => 9, # IMG_FILTER_MEAN_REMOVAL
  57. 'smooth' => 10, # IMG_FILTER_SMOOTH
  58. 'pixelate' => 11 # IMG_FILTER_PIXELATE
  59. );
  60. public function Pic($src = null)
  61. {
  62. if ($src != null) $this->open($src);
  63. }
  64. /**
  65. *
  66. */
  67. private function position ($options = array(), $base = array()) {
  68. $pos = array('x' => 0, 'y' => 0);
  69. // Defino a posição X (left ou right)
  70. if ((isset($options['left']) and $options['left'] == 'auto')
  71. or (isset($options['right']) and $options['right'] == 'auto'))
  72. $pos['x'] = (($base['width'] - $options['width']) / 2);
  73. elseif (isset($options['left']))
  74. $pos['x'] = (int) $options['left'];
  75. elseif(isset($options['right']))
  76. $pos['x'] = ($base['width'] - $options['width'] - (int) $options['right']);
  77. // Defino a posição Y (top ou bottom)
  78. if ((isset($options['top']) and $options['top'] == 'auto')
  79. or (isset($options['bottom']) and $options['bottom'] == 'auto'))
  80. $pos['y'] = (($base['height'] - $options['height']) / 2);
  81. elseif (isset($options['top']))
  82. $pos['y'] = (int) $options['top'];
  83. elseif (isset($options['bottom']))
  84. $pos['y'] = ($base['height'] - $options['height'] - (int) $options['bottom']);
  85. return $pos;
  86. }
  87. /**
  88. * Color
  89. * @link http://www.php.net/manual/en/ref.image.php#63064
  90. */
  91. private function hexrgb ($color = null) {
  92. if (is_null($color)) return array(255, 255, 255);
  93. $color = str_replace('#', null, $color);
  94. if (strlen($color) == 3) $color .= $color;
  95. return array_map('hexdec', explode('|', wordwrap($color, 2, '|', 1)));
  96. }
  97. /**
  98. *
  99. */
  100. private function pixel (&$unid = null, $measure = null) {
  101. if (!is_numeric($unid)) {
  102. if (strpos($unid, 'px'))
  103. $unid = (int) $unid;
  104. elseif (strpos($unid, '%'))
  105. $unid = round($measure * ((float) $unid * 0.01));
  106. elseif (strpos($unid, 'em'))
  107. $unid = round($measure * (float) $unid);
  108. }
  109. }
  110. /**
  111. *
  112. */
  113. private function imagecolor ($options) {
  114. if ($options['opacity'] <= 100)
  115. $opacity = (100 - $options['opacity']) * 1.27;
  116. else
  117. $opacity = 0;
  118. $color = $this->hexrgb($options['background']);
  119. $this->img['imagecolor'] = imagecolorallocatealpha($this->img['source'],
  120. $color[0], $color[1], $color[2], $opacity);
  121. }
  122. /**
  123. *
  124. */
  125. public function background($background = '#FFF') {
  126. if ($this->img['background'] == 'transparent') {
  127. $this->img['background'] = $background;
  128. $tmp = $this->imagebase($this->img['width'], $this->img['height']);
  129. imagecopyresampled($tmp, $this->img['source'], 0, 0, 0, 0, $this->img['width'],
  130. $this->img['height'], $this->img['width'], $this->img['height']);
  131. imagedestroy($this->img['source']);
  132. $this->img['source'] = $tmp;
  133. }
  134. }
  135. /**
  136. * @link http://php.net/manual/en/function.imagesavealpha.php
  137. * @link http://stefangabos.ro/php-libraries/zebra-image/
  138. */
  139. private function imagebase($width, $height) {
  140. $base = imagecreatetruecolor($width, $height);
  141. if ($this->img['format'] == 'png' and $this->img['background'] == 'transparent') {
  142. imagealphablending($base, false);
  143. $transparent = imagecolorallocatealpha($base, 0, 0, 0, 127);
  144. imagefill($base, 0, 0, $transparent);
  145. imagesavealpha($base, true);
  146. }
  147. elseif ($this->img['format'] == 'gif' and $this->img['background'] == 'transparent'
  148. and $this->img['transparent']['color_index'] >= 0) {
  149. $transparent_color = imagecolorallocate(
  150. $base,
  151. $this->img['transparent']['color']['red'],
  152. $this->img['transparent']['color']['green'],
  153. $this->img['transparent']['color']['blue']
  154. );
  155. imagefill($base, 0, 0, $transparent_color);
  156. imagecolortransparent($base, $transparent_color);
  157. }
  158. else {
  159. $background = $this->hexrgb($this->img['background']);
  160. $background = imagecolorallocate($base, $background[0], $background[1], $background[2]);
  161. imagefill($base, 0, 0, $background);
  162. }
  163. return $base;
  164. }
  165. /**
  166. * Redimensionamento com varias opções
  167. */
  168. public function resize(array $options)
  169. {
  170. // Se definir só a largura reajusto o valor da altura para manter a proporção
  171. if (isset($options['width']) and !isset($options['height'])) {
  172. $this->pixel($options['width'], $this->img['width']);
  173. $options['height'] = floor($this->img['height'] / ($this->img['width'] / $options['width']));
  174. }
  175. // Se definir só a altura reajusto o valor da largura para manter a proporção
  176. elseif (isset($options['height']) and !isset($options['width'])) {
  177. $this->pixel($options['height'], $this->img['height']);
  178. $options['width'] = floor($this->img['width'] / ($this->img['height'] / $options['height']));
  179. }
  180. // Se os dois foram definidos redimensiono como desejado
  181. elseif (isset($options['height']) and isset($options['width'])) {
  182. $this->pixel($options['width'], $this->img['width']);
  183. $this->pixel($options['height'], $this->img['height']);
  184. }
  185. // Se nenhum foi definido, mantenho o tamanho atual
  186. else {
  187. $options['width'] = $this->img['width'];
  188. $options['height'] = $this->img['height'];
  189. }
  190. $width = $options['width'];
  191. $height = $options['height'];
  192. // Verifico se foi definido algum limiete para o canvas
  193. if (isset($options['canvas-height'])) {
  194. $this->pixel($options['canvas-height'], $this->img['height']);
  195. if ($height > $options['canvas-height']) $options['height'] = $options['canvas-height'];
  196. }
  197. elseif (isset($options['canvas-width'])) {
  198. $this->pixel($options['canvas-width'], $this->img['width']);
  199. if ($width > $options['canvas-width']) $options['width'] = $options['canvas-width'];
  200. }
  201. $pos = $this->position($options, array('width' => $width, 'height' => $height));
  202. $tmp = $this->imagebase($options['width'], $options['height']);
  203. imagecopyresampled($tmp, $this->img['source'], -$pos['x'], -$pos['y'],
  204. 0, 0, $width, $height, $this->img['width'], $this->img['height']);
  205. imagedestroy($this->img['source']);
  206. $this->img['source'] = $tmp;
  207. $this->img['width'] = $options['width'];
  208. $this->img['height'] = $options['height'];
  209. }
  210. /**
  211. * Se a imagem for maior na vertical inverte a largura com a altura, bom para fotos
  212. */
  213. public function photo ($options = array()) {
  214. $options = array_merge(array('width' => 600, 'height' => 400,
  215. 'overflow' => 'hidden'), $options);
  216. if ($this->img['width'] > $this->img['height']) {
  217. if ($options['overflow'] == 'hidden')
  218. $options['canvas-height'] = $options['height'];
  219. unset($options['height']);
  220. }
  221. elseif ($this->img['width'] < $this->img['height']) {
  222. $options = array_merge($options, array('width' => $options['height'],
  223. 'height' => $options['width']));
  224. if ($options['overflow'] == 'hidden')
  225. $options['canvas-width'] = $options['width'];
  226. unset($options['width']);
  227. }
  228. $this->resize($options);
  229. }
  230. /**
  231. *
  232. */
  233. public function flip($type = 'h'){
  234. $tmp = imagecreatetruecolor($this->img['width'], $this->img['height']);
  235. switch($type){
  236. case 'v':
  237. for($i = 0; $i < $this->img['height']; $i++)
  238. imagecopy($tmp, $this->img['source'], 0, ($this->img['height'] - $i - 1),
  239. 0, $i, $this->img['width'], 1);
  240. imagedestroy($this->img['source']);
  241. $this->img['source'] = $tmp;
  242. break;
  243. case 'h':
  244. for($i = 0; $i < $this->img['width']; $i++)
  245. imagecopy($tmp, $this->img['source'], ($this->img['width'] - $i - 1),
  246. 0, $i, 0, 1, $this->img['height']);
  247. imagedestroy($this->img['source']);
  248. $this->img['source'] = $tmp;
  249. break;
  250. case 'vh':
  251. case 'hv':
  252. $this->flip('v');
  253. $this->flip('h');
  254. break;
  255. }
  256. }
  257. /**
  258. * Corta imagem com tamanho e local especifico
  259. */
  260. public function crop($options = array()) {
  261. $options = array_merge(array('width' => $this->img['width'],
  262. 'height' => $this->img['height']), $options);
  263. $this->pixel($options['width'], $this->img['width']);
  264. $this->pixel($options['height'], $this->img['height']);
  265. $pos = $this->position($options, $this->img);
  266. $tmp = imagecreatetruecolor($options['width'], $options['height']);
  267. imagecopyresampled($tmp, $this->img['source'], 0, 0, $pos['x'], $pos['y'],
  268. $this->img['width'], $this->img['height'], $this->img['width'], $this->img['height']);
  269. imagedestroy($this->img['source']);
  270. $this->img['source'] = $tmp;
  271. $this->img['width'] = $options['width'];
  272. $this->img['height'] = $options['height'];
  273. }
  274. /**
  275. *
  276. */
  277. public function write($string = null, $options = array()) {
  278. $options = array_merge(array('color' => '#FFF', 'background' => 'transparent',
  279. 'opacity' => '100', 'font' => null, 'size' => '14', 'rotate' => 0), $options);
  280. $string = utf8_decode($string);
  281. $this->pixel($options['size'], $this->img['height']);
  282. $bbox = imagettfbbox($options['size'], 0, './' . $options['font'], $string);
  283. $options['width'] = $bbox[2];
  284. $options['height'] = $options['size'];
  285. if ($options['background'] != 'transparent')
  286. $this->geometric('rectangle', $options);
  287. $pos = $this->position($options, $this->img);
  288. $pos['y'] += $options['size'];
  289. $options['background'] = $options['color'];
  290. $this->imagecolor($options);
  291. imagettftext($this->img['source'], $options['size'], $options['rotate'],
  292. $pos['x'], $pos['y'], $this->img['imagecolor'], './' . $options['font'], $string);
  293. }
  294. /**
  295. *
  296. */
  297. public function thumbnail($options = array()) {
  298. $options = array_merge(array('width' => 90, 'height' => 90), $options);
  299. $this->pixel($options['width'], $this->img['width']);
  300. $this->pixel($options['height'], $this->img['height']);
  301. if (floor($this->img['width'] / ($this->img['height'] / $options['height'])) < $options['width']) {
  302. $options['canvas-height'] = $options['height'];
  303. unset($options['height']);
  304. } else {
  305. $options['canvas-width'] = $options['width'];
  306. unset($options['width']);
  307. }
  308. $this->resize($options);
  309. }
  310. /**
  311. * Gira a imagem
  312. */
  313. public function rotate($rotate, $options = array()) {
  314. $options = array_merge(array('background' => '#FFF', 'opacity' => '100'), $options);
  315. $this->imagecolor($options);
  316. $this->img['source'] = imagerotate($this->img['source'], $rotate,
  317. $this->img['imagecolor'], 0);
  318. unset($this->img['imagecolor']);
  319. $this->img['width'] = imagesx($this->img['source']);
  320. $this->img['height'] = imagesy($this->img['source']);
  321. }
  322. /**
  323. *
  324. */
  325. public function layer ($src = null, $options = array()) {
  326. if (is_array($src))
  327. $img = $src;
  328. else
  329. $img = $this->imagecreate($src);
  330. if (!$img) return false;
  331. $options = array_merge(array('opacity' => 100), $options);
  332. $options['width'] = $img['width'];
  333. $options['height'] = $img['height'];
  334. $pos = $this->position($options, $this->img);
  335. if ($img['format'] == 'png') {
  336. require_once PATH_PIC_CLASS . 'imagecopymerge_alpha.function.php';
  337. imagecopymerge_alpha($this->img['source'], $img['source'], $pos['x'],
  338. $pos['y'], 0, 0, $img['width'], $img['height'], $options['opacity']);
  339. }
  340. else
  341. imagecopymerge($this->img['source'], $img['source'], $pos['x'], $pos['y'],
  342. 0, 0, $img['width'], $img['height'], $options['opacity']);
  343. if (!is_array($src)) imagedestroy($img['source']);
  344. return true;
  345. }
  346. /**
  347. * Imagecreate
  348. */
  349. private function imagecreate($src) {
  350. $info = @getimagesize($src);
  351. if (!in_array($info['mime'], $this->mime))
  352. return false;
  353. $img = array();
  354. $img['width'] = $info[0];
  355. $img['height'] = $info[1];
  356. $img['format'] = str_replace(array('.', 'e'), null, image_type_to_extension($info[2]));
  357. switch ($img['format']) {
  358. case 'jpg':
  359. $img['source'] = imagecreatefromjpeg($src);
  360. $img['background'] = '#FFF';
  361. break;
  362. case 'png':
  363. $img['source'] = imagecreatefrompng($src);
  364. imagealphablending($img['source'], false);
  365. imagesavealpha($img['source'], true);
  366. $img['background'] = 'transparent';
  367. break;
  368. case 'gif':
  369. $img['source'] = imagecreatefromgif($src);
  370. $img['transparent']['color_index'] = imagecolortransparent($img['source']);
  371. if ($img['transparent']['color_index'] >= 0)
  372. $img['transparent']['color'] = imagecolorsforindex($img['source'], $img['transparent']['color_index']);
  373. $img['background'] = 'transparent';
  374. break;
  375. case 'bmp':
  376. require_once PATH_PIC_CLASS . 'imagecreatefrombmp.function.php';
  377. $img['source'] = imagecreatefrombmp($src);
  378. $img['background'] = '#FFF';
  379. break;
  380. }
  381. return $img;
  382. }
  383. /**
  384. * Abre imagem para edição
  385. */
  386. public function open($src = null) {
  387. if (!is_null($src)) $this->src = $src;
  388. if ($this->img = $this->imagecreate($src)) return true;
  389. }
  390. /**
  391. * Reabre a imagem, dando empressão de desfazer todas modificações
  392. */
  393. public function reset() {
  394. imagedestroy($this->img['source']);
  395. return $this->open($this->src);
  396. }
  397. /**
  398. *
  399. */
  400. private function filter_name($name = null) {
  401. $default = 'image.' . $this->img['format'];
  402. if(!is_null($name)) {
  403. $format = strtolower(str_replace('.', null, strrchr($name, '.')));
  404. if (isset($this->mime[$format]))
  405. $this->img['format'] = $format;
  406. else
  407. $name .= '.' . $this->img['format'];
  408. }
  409. else
  410. $name = $default;
  411. return $name;
  412. }
  413. /**
  414. * Image
  415. */
  416. private function image($save = null, $qualite = 90) {
  417. imageinterlace($this->img['source'], true);
  418. switch ($this->img['format']) {
  419. case 'jpg': return imagejpeg($this->img['source'], $save, $qualite); break;
  420. case 'png': return imagepng($this->img['source'], $save); break;
  421. case 'gif': return imagegif($this->img['source'], $save); break;
  422. case 'bmp':
  423. require_once PATH_PIC_CLASS . 'imagebmp.function.php';
  424. return imagebmp($this->img['source'], $save);
  425. break;
  426. }
  427. }
  428. /**
  429. *
  430. */
  431. public function filter ($filtertype = null, $arg1 = null, $arg2 = null, $arg3 = null, $arg4 = null) {
  432. switch ($this->filters[$filtertype]) {
  433. case IMG_FILTER_COLORIZE:
  434. imagefilter($this->img['source'], $this->filters[$filtertype], $arg1, $arg2, $arg3, $arg4);
  435. break;
  436. case IMG_FILTER_PIXELATE:
  437. imagefilter($this->img['source'], $this->filters[$filtertype], $arg1, $arg2);
  438. break;
  439. case IMG_FILTER_BRIGHTNESS:
  440. case IMG_FILTER_CONTRAST:
  441. case IMG_FILTER_SMOOTH:
  442. imagefilter($this->img['source'], $this->filters[$filtertype], $arg1);
  443. break;
  444. case IMG_FILTER_NEGATE:
  445. imagefilter($this->img['source'], $this->filters[$filtertype]);
  446. break;
  447. default:
  448. if (is_null($arg1)) $arg1 = 1;
  449. for ($i = 0; $i < $arg1; $i++)
  450. imagefilter($this->img['source'], $this->filters[$filtertype]);
  451. break;
  452. }
  453. }
  454. /**
  455. *
  456. */
  457. public function geometric($geometric = null, $options = array()) {
  458. switch ($geometric) {
  459. case 'rectangle':
  460. $options = array_merge(array('width' => '50%', 'height' => '50%',
  461. 'background' => '#FFF', 'opacity' => 100), $options);
  462. $this->pixel($options['width'], $this->img['width']);
  463. $this->pixel($options['height'], $this->img['height']);
  464. $pos = $this->position($options, $this->img);
  465. $this->imagecolor($options);
  466. imagefilledrectangle($this->img['source'], $pos['x'], $pos['y'],
  467. $pos['x'] + $options['width'], $pos['y'] + $options['height'],
  468. $this->img['imagecolor']);
  469. break;
  470. }
  471. }
  472. /**
  473. *
  474. */
  475. public function effect($effect = null) {
  476. switch ($effect) {
  477. case 'sepia':
  478. $this->filter('grayscale');
  479. $this->filter('colorize', 90, 60, 40);
  480. break;
  481. case 'drawing':
  482. $this->filter('grayscale');
  483. $this->filter('edgedetect');
  484. $this->filter('brightness', 120);
  485. break;
  486. }
  487. }
  488. /**
  489. * Mostra imagem
  490. */
  491. public function display($format = null, $qualite = 90) {
  492. if (!is_null($format)) $this->img['format'] = $format;
  493. header('Content-type: ' . $this->mime[$this->img['format']]);
  494. $this->image(null, $qualite);
  495. imagedestroy($this->img['source']);
  496. exit;
  497. }
  498. /**
  499. * Download da imagem
  500. */
  501. public function download($name = null, $qualite = 90) {
  502. $name = $this->filter_name($name);
  503. header('Content-type: ' . $this->mime[$this->img['format']]);
  504. header('Content-Disposition: attachment; filename="' . $name . '"');
  505. $this->image(null, $qualite);
  506. readfile($name);
  507. imagedestroy($this->img['source']);
  508. exit;
  509. }
  510. /**
  511. * Salva imagem
  512. */
  513. public function save($name = null, $qualite = 90, $chmod = 0777){
  514. if (is_null($qualite)) $qualite = 90;
  515. $name = $this->filter_name($name);
  516. if (!is_dir($dir = dirname($name))) mkdir($dir, $chmod, true);
  517. if ($this->image($name, $qualite)) chmod($name, $chmod);
  518. }
  519. /**
  520. * Apaga a imagem da memória
  521. */
  522. public function clear() {
  523. imagedestroy($this->img['source']);
  524. }
  525. /**
  526. * Deleta a imagem aberta pelo Pic::open
  527. */
  528. public function delete() {
  529. if (file_exists($this->src)) unlink($this->src);
  530. }
  531. }