PageRenderTime 47ms CodeModel.GetById 16ms app.highlight 25ms RepoModel.GetById 1ms 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
 25define('PATH_PIC_CLASS', dirname(__FILE__) . '/');
 26
 27class Pic {
 28
 29    /**
 30    * Source e informações da imagem
 31    */
 32    public $img = array();
 33
 34    /**
 35    * Caminho da imagem original
 36    */
 37    private $src = null;
 38
 39    /**
 40    * Formatos permititos
 41    */
 42    private $mime = array(
 43        'jpg' => 'image/jpeg',
 44        'gif' => 'image/gif',
 45        'png' => 'image/png',
 46        'bmp' => 'image/x-ms-bmp'
 47    );
 48
 49    /**
 50    * Atalhos amigáveis para os filtros
 51    */
 52    private $filters = array(
 53        'negate' => 0,            # IMG_FILTER_NEGATE
 54        'grayscale' => 1,         # IMG_FILTER_GRAYSCALE
 55        'brightness' => 2,        # IMG_FILTER_BRIGHTNESS
 56        'contrast' => 3,          # IMG_FILTER_CONTRAST
 57        'colorize' => 4,          # IMG_FILTER_COLORIZE
 58        'edgedetect' => 5,        # IMG_FILTER_EDGEDETECT
 59        'emboss' => 6,            # IMG_FILTER_EMBOSS
 60        'gaussian-blur' => 7,     # IMG_FILTER_GAUSSIAN_BLUR
 61        'selective-blur' => 8,    # IMG_FILTER_SELECTIVE_BLUR
 62        'mean-removal' => 9,      # IMG_FILTER_MEAN_REMOVAL
 63        'smooth' => 10,           # IMG_FILTER_SMOOTH
 64        'pixelate' => 11          # IMG_FILTER_PIXELATE
 65    );
 66
 67
 68    public function Pic($src = null)
 69    {
 70        if ($src != null) $this->open($src);
 71    }
 72
 73    /**
 74    *
 75    */
 76    private function position ($options = array(), $base = array()) {
 77        $pos = array('x' => 0, 'y' => 0);
 78
 79        // Defino a posição X (left ou right)
 80        if ((isset($options['left']) and $options['left'] == 'auto')
 81            or (isset($options['right']) and $options['right'] == 'auto'))
 82            $pos['x'] = (($base['width'] - $options['width']) / 2);
 83
 84        elseif (isset($options['left']))
 85            $pos['x'] =  (int) $options['left'];
 86
 87        elseif(isset($options['right']))
 88            $pos['x'] = ($base['width'] - $options['width'] -  (int) $options['right']);
 89
 90        // Defino a posição Y (top ou bottom)
 91        if ((isset($options['top']) and $options['top'] == 'auto')
 92            or (isset($options['bottom']) and $options['bottom'] == 'auto'))
 93            $pos['y'] = (($base['height'] - $options['height']) / 2);
 94
 95        elseif (isset($options['top']))
 96            $pos['y'] =  (int) $options['top'];
 97
 98        elseif (isset($options['bottom']))
 99            $pos['y'] = ($base['height'] - $options['height'] -  (int) $options['bottom']);
100
101        return $pos;
102    }
103
104    /**
105    * Color
106    * @link http://www.php.net/manual/en/ref.image.php#63064
107    */
108    private function hexrgb ($color = null) {
109        if (is_null($color)) return array(255, 255, 255);
110        $color = str_replace('#', null, $color);
111        if (strlen($color) == 3) $color .= $color;
112        return array_map('hexdec', explode('|', wordwrap($color, 2, '|', 1)));
113    }
114
115    /**
116    *
117    */
118    private function pixel (&$unid = null, $measure = null) {
119        if (!is_numeric($unid)) {
120            if (strpos($unid, 'px'))
121                $unid = (int) $unid;
122            elseif (strpos($unid, '%'))
123                $unid = round($measure * ((float) $unid * 0.01));
124            elseif (strpos($unid, 'em'))
125                $unid = round($measure * (float) $unid);
126        }
127    }
128
129    /**
130    *
131    */
132    private function imagecolor ($options) {
133        if ($options['opacity'] <= 100)
134            $opacity = (100 - $options['opacity']) * 1.27;
135        else
136            $opacity = 0;
137
138            $color = $this->hexrgb($options['background']);
139            $this->img['imagecolor'] = imagecolorallocatealpha($this->img['source'],
140                $color[0], $color[1], $color[2], $opacity);
141    }
142
143    /**
144    *
145    */
146    public function background($background = '#FFF') {
147        if ($this->img['background'] == 'transparent') {
148
149            $this->img['background'] = $background;
150
151            $tmp = $this->imagebase($this->img['width'], $this->img['height']);
152
153            imagecopyresampled($tmp, $this->img['source'], 0, 0, 0, 0, $this->img['width'],
154                $this->img['height'], $this->img['width'], $this->img['height']);
155
156            imagedestroy($this->img['source']);
157
158            $this->img['source'] = $tmp;
159        }
160    }
161
162    /**
163    * @link http://php.net/manual/en/function.imagesavealpha.php
164    * @link http://stefangabos.ro/php-libraries/zebra-image/
165    */
166    private function imagebase($width, $height) {
167        $base = imagecreatetruecolor($width, $height);
168
169        if ($this->img['format'] == 'png' and $this->img['background'] == 'transparent') {
170            imagealphablending($base, false);
171
172            $transparent = imagecolorallocatealpha($base, 0, 0, 0, 127);
173            imagefill($base, 0, 0, $transparent);
174            imagesavealpha($base, true);
175        }
176
177        elseif ($this->img['format'] == 'gif' and $this->img['background'] == 'transparent'
178            and $this->img['transparent']['color_index'] >= 0) {
179
180            $transparent_color = imagecolorallocate(
181                $base,
182                $this->img['transparent']['color']['red'],
183                $this->img['transparent']['color']['green'],
184                $this->img['transparent']['color']['blue']
185            );
186
187            imagefill($base, 0, 0, $transparent_color);
188            imagecolortransparent($base, $transparent_color);
189        }
190
191        else {
192            $background = $this->hexrgb($this->img['background']);
193            $background = imagecolorallocate($base, $background[0], $background[1], $background[2]);
194            imagefill($base, 0, 0, $background);
195        }
196
197        return $base;
198    }
199
200    /**
201    * Redimensionamento com varias opções
202    */
203    public function resize(array $options)
204    {
205        // Se definir só a largura reajusto o valor da altura para manter a proporção
206        if (isset($options['width']) and !isset($options['height'])) {
207            $this->pixel($options['width'], $this->img['width']);
208            $options['height'] = floor($this->img['height'] / ($this->img['width'] / $options['width']));
209        }
210
211        // Se definir só a altura reajusto o valor da largura para manter a proporção
212        elseif (isset($options['height']) and !isset($options['width'])) {
213            $this->pixel($options['height'], $this->img['height']);
214            $options['width'] = floor($this->img['width'] / ($this->img['height'] / $options['height']));
215        }
216
217        // Se os dois foram definidos redimensiono como desejado
218        elseif (isset($options['height']) and isset($options['width'])) {
219            $this->pixel($options['width'], $this->img['width']);
220            $this->pixel($options['height'], $this->img['height']);
221        }
222
223        // Se nenhum foi definido, mantenho o tamanho atual
224        else {
225            $options['width'] = $this->img['width'];
226            $options['height'] = $this->img['height'];
227        }
228
229        $width = $options['width'];
230        $height = $options['height'];
231
232        // Verifico se foi definido algum limiete para o canvas
233        if (isset($options['canvas-height'])) {
234            $this->pixel($options['canvas-height'], $this->img['height']);
235            if ($height > $options['canvas-height']) $options['height'] = $options['canvas-height'];
236        }
237
238        elseif (isset($options['canvas-width'])) {
239            $this->pixel($options['canvas-width'], $this->img['width']);
240            if ($width > $options['canvas-width']) $options['width'] = $options['canvas-width'];
241        }
242
243        $pos = $this->position($options, array('width' => $width, 'height' => $height));
244        $tmp = $this->imagebase($options['width'], $options['height']);
245
246        imagecopyresampled($tmp, $this->img['source'], -$pos['x'], -$pos['y'],
247            0, 0, $width, $height, $this->img['width'], $this->img['height']);
248
249        imagedestroy($this->img['source']);
250
251        $this->img['source'] = $tmp;
252        $this->img['width'] = $options['width'];
253        $this->img['height'] = $options['height'];
254    }
255
256    /**
257    * Se a imagem for maior na vertical inverte a largura com a altura, bom para fotos
258    */
259    public function photo ($options = array()) {
260        $options = array_merge(array('width' => 600, 'height' => 400,
261            'overflow' => 'hidden'), $options);
262
263        if ($this->img['width'] > $this->img['height']) {
264            if ($options['overflow'] == 'hidden')
265                $options['canvas-height'] =  $options['height'];
266
267            unset($options['height']);
268        }
269
270        elseif ($this->img['width'] < $this->img['height']) {
271            $options = array_merge($options, array('width' => $options['height'],
272                'height' => $options['width']));
273
274            if ($options['overflow'] == 'hidden')
275                $options['canvas-width'] = $options['width'];
276
277            unset($options['width']);
278        }
279
280        $this->resize($options);
281    }
282
283    /**
284    *
285    */
286    public function flip($type = 'h'){
287        $tmp = imagecreatetruecolor($this->img['width'], $this->img['height']);
288        switch($type){
289            case 'v':
290                for($i = 0; $i < $this->img['height']; $i++)
291                    imagecopy($tmp, $this->img['source'], 0, ($this->img['height'] - $i - 1),
292                        0, $i, $this->img['width'], 1);
293
294                imagedestroy($this->img['source']);
295                $this->img['source'] = $tmp;
296            break;
297            case 'h':
298                for($i = 0; $i < $this->img['width']; $i++)
299                    imagecopy($tmp, $this->img['source'], ($this->img['width'] - $i - 1),
300                        0, $i, 0, 1, $this->img['height']);
301
302                imagedestroy($this->img['source']);
303                $this->img['source'] = $tmp;
304            break;
305            case 'vh':
306            case 'hv':
307                $this->flip('v');
308                $this->flip('h');
309            break;
310        }
311    }
312
313    /**
314    * Corta imagem com tamanho e local especifico
315    */
316    public function crop($options = array()) {
317        $options = array_merge(array('width' => $this->img['width'],
318            'height' => $this->img['height']), $options);
319
320        $this->pixel($options['width'], $this->img['width']);
321        $this->pixel($options['height'], $this->img['height']);
322
323        $pos = $this->position($options, $this->img);
324
325        $tmp = imagecreatetruecolor($options['width'], $options['height']);
326        imagecopyresampled($tmp, $this->img['source'], 0, 0, $pos['x'], $pos['y'],
327            $this->img['width'], $this->img['height'], $this->img['width'], $this->img['height']);
328        imagedestroy($this->img['source']);
329
330        $this->img['source'] = $tmp;
331        $this->img['width'] = $options['width'];
332        $this->img['height'] = $options['height'];
333    }
334
335    /**
336    *
337    */
338    public function write($string = null, $options = array()) {
339        $options = array_merge(array('color' => '#FFF', 'background' => 'transparent',
340            'opacity' => '100', 'font' => null, 'size' => '14', 'rotate' => 0), $options);
341
342        $string = utf8_decode($string);
343        $this->pixel($options['size'], $this->img['height']);
344
345        $bbox = imagettfbbox($options['size'], 0, './' . $options['font'], $string);
346        $options['width'] = $bbox[2];
347        $options['height'] = $options['size'];
348
349        if ($options['background'] != 'transparent')
350            $this->geometric('rectangle', $options);
351
352        $pos = $this->position($options, $this->img);
353        $pos['y'] += $options['size'];
354
355        $options['background'] = $options['color'];
356        $this->imagecolor($options);
357
358        imagettftext($this->img['source'], $options['size'], $options['rotate'],
359            $pos['x'], $pos['y'], $this->img['imagecolor'], './' . $options['font'], $string);
360    }
361
362    /**
363    *
364    */
365    public function thumbnail($options = array()) {
366        $options = array_merge(array('width' => 90, 'height' => 90), $options);
367
368        $this->pixel($options['width'], $this->img['width']);
369        $this->pixel($options['height'], $this->img['height']);
370
371        if (floor($this->img['width'] / ($this->img['height'] / $options['height'])) < $options['width']) {
372            $options['canvas-height'] = $options['height'];
373            unset($options['height']);
374        } else {
375            $options['canvas-width'] = $options['width'];
376            unset($options['width']);
377        }
378
379        $this->resize($options);
380    }
381
382    /**
383    * Gira a imagem
384    */
385    public function rotate($rotate, $options = array()) {
386        $options = array_merge(array('background' => '#FFF', 'opacity' => '100'), $options);
387
388        $this->imagecolor($options);
389
390        $this->img['source'] = imagerotate($this->img['source'], $rotate,
391            $this->img['imagecolor'], 0);
392
393        unset($this->img['imagecolor']);
394        $this->img['width'] = imagesx($this->img['source']);
395        $this->img['height'] = imagesy($this->img['source']);
396    }
397
398    /**
399    *
400    */
401    public function layer ($src = null, $options = array()) {
402        if (is_array($src))
403            $img = $src;
404        else
405            $img = $this->imagecreate($src);
406
407        if (!$img) return false;
408
409        $options = array_merge(array('opacity' => 100), $options);
410
411        $options['width'] = $img['width'];
412        $options['height'] = $img['height'];
413
414        $pos = $this->position($options, $this->img);
415
416        if ($img['format'] == 'png') {
417            require_once PATH_PIC_CLASS . 'imagecopymerge_alpha.function.php';
418
419            imagecopymerge_alpha($this->img['source'], $img['source'], $pos['x'],
420                $pos['y'], 0, 0, $img['width'], $img['height'], $options['opacity']);
421        }
422
423        else
424            imagecopymerge($this->img['source'], $img['source'], $pos['x'], $pos['y'],
425                0, 0, $img['width'], $img['height'], $options['opacity']);
426
427        if (!is_array($src)) imagedestroy($img['source']);
428
429        return true;
430    }
431
432    /**
433    * Imagecreate
434    */
435    private function imagecreate($src) {
436        $info = @getimagesize($src);
437
438        if (!in_array($info['mime'], $this->mime))
439            return false;
440
441        $img = array();
442
443        $img['width'] = $info[0];
444        $img['height'] = $info[1];
445
446        $img['format'] = str_replace(array('.', 'e'), null, image_type_to_extension($info[2]));
447
448        switch ($img['format']) {
449            case 'jpg':
450                $img['source'] = imagecreatefromjpeg($src);
451                $img['background'] = '#FFF';
452            break;
453            case 'png':
454                $img['source'] = imagecreatefrompng($src);
455                imagealphablending($img['source'], false);
456                imagesavealpha($img['source'], true);
457                $img['background'] = 'transparent';
458            break;
459            case 'gif':
460                $img['source'] = imagecreatefromgif($src);
461                $img['transparent']['color_index'] = imagecolortransparent($img['source']);
462
463                if ($img['transparent']['color_index'] >= 0)
464                    $img['transparent']['color'] = imagecolorsforindex($img['source'], $img['transparent']['color_index']);
465
466                $img['background'] = 'transparent';
467
468            break;
469            case 'bmp':
470                require_once PATH_PIC_CLASS . 'imagecreatefrombmp.function.php';
471                $img['source'] = imagecreatefrombmp($src);
472                $img['background'] = '#FFF';
473            break;
474        }
475
476        return $img;
477    }
478
479    /**
480    * Abre imagem para edição
481    */
482    public function open($src = null) {
483        if (!is_null($src)) $this->src = $src;
484        if ($this->img = $this->imagecreate($src)) return true;
485    }
486
487    /**
488    * Reabre a imagem, dando empressão de desfazer todas modificações
489    */
490    public function reset() {
491        imagedestroy($this->img['source']);
492        return $this->open($this->src);
493    }
494
495    /**
496    *
497    */
498    private function filter_name($name = null) {
499        $default = 'image.' . $this->img['format'];
500
501        if(!is_null($name)) {
502            $format = strtolower(str_replace('.', null, strrchr($name, '.')));
503
504            if (isset($this->mime[$format]))
505                $this->img['format'] = $format;
506            else
507                $name .= '.' . $this->img['format'];
508        }
509
510        else
511            $name = $default;
512
513        return $name;
514    }
515
516    /**
517    * Image
518    */
519    private function image($save = null, $qualite = 90) {
520        imageinterlace($this->img['source'], true);
521
522        switch ($this->img['format']) {
523            case 'jpg': return imagejpeg($this->img['source'], $save, $qualite); break;
524            case 'png': return imagepng($this->img['source'], $save); break;
525            case 'gif': return imagegif($this->img['source'], $save); break;
526            case 'bmp':
527                require_once PATH_PIC_CLASS . 'imagebmp.function.php';
528                return imagebmp($this->img['source'], $save);
529            break;
530        }
531    }
532
533    /**
534    *
535    */
536    public function filter ($filtertype = null, $arg1 = null, $arg2 = null, $arg3 = null, $arg4 = null) {
537        switch ($this->filters[$filtertype]) {
538            case IMG_FILTER_COLORIZE:
539                imagefilter($this->img['source'], $this->filters[$filtertype], $arg1, $arg2, $arg3, $arg4);
540            break;
541
542            case IMG_FILTER_PIXELATE:
543                imagefilter($this->img['source'], $this->filters[$filtertype], $arg1, $arg2);
544            break;
545
546            case IMG_FILTER_BRIGHTNESS:
547            case IMG_FILTER_CONTRAST:
548            case IMG_FILTER_SMOOTH:
549                imagefilter($this->img['source'], $this->filters[$filtertype], $arg1);
550            break;
551
552            case IMG_FILTER_NEGATE:
553                imagefilter($this->img['source'], $this->filters[$filtertype]);
554            break;
555
556            default:
557                if (is_null($arg1)) $arg1 = 1;
558
559                for ($i = 0; $i < $arg1; $i++)
560                    imagefilter($this->img['source'], $this->filters[$filtertype]);
561            break;
562        }
563    }
564
565    /**
566    *
567    */
568    public function geometric($geometric = null, $options = array()) {
569        switch ($geometric) {
570            case 'rectangle':
571                $options = array_merge(array('width' => '50%', 'height' => '50%',
572                    'background' => '#FFF', 'opacity' => 100), $options);
573
574                $this->pixel($options['width'], $this->img['width']);
575                $this->pixel($options['height'], $this->img['height']);
576
577                $pos = $this->position($options, $this->img);
578                $this->imagecolor($options);
579
580                imagefilledrectangle($this->img['source'], $pos['x'], $pos['y'],
581                    $pos['x'] + $options['width'], $pos['y'] + $options['height'],
582                    $this->img['imagecolor']);
583            break;
584        }
585    }
586
587    /**
588    *
589    */
590    public function effect($effect = null) {
591        switch ($effect) {
592            case 'sepia':
593                $this->filter('grayscale');
594                $this->filter('colorize', 90, 60, 40);
595            break;
596            case 'drawing':
597                $this->filter('grayscale');
598                $this->filter('edgedetect');
599                $this->filter('brightness', 120);
600            break;
601        }
602    }
603
604    /**
605    * Mostra imagem
606    */
607    public function display($format = null, $qualite = 90) {
608        if (!is_null($format)) $this->img['format'] = $format;
609        header('Content-type: ' . $this->mime[$this->img['format']]);
610        $this->image(null, $qualite);
611        imagedestroy($this->img['source']);
612        exit;
613    }
614
615    /**
616    * Download da imagem
617    */
618    public function download($name = null, $qualite = 90) {
619        $name = $this->filter_name($name);
620        header('Content-type: ' . $this->mime[$this->img['format']]);
621        header('Content-Disposition: attachment; filename="' . $name . '"');
622        $this->image(null, $qualite);
623        readfile($name);
624        imagedestroy($this->img['source']);
625        exit;
626    }
627
628    /**
629    * Salva imagem
630    */
631    public function save($name = null, $qualite = 90, $chmod = 0777){
632        if (is_null($qualite)) $qualite = 90;
633        $name = $this->filter_name($name);
634        if (!is_dir($dir = dirname($name))) mkdir($dir, $chmod, true);
635        if ($this->image($name, $qualite)) chmod($name, $chmod);
636    }
637
638    /**
639    * Apaga a imagem da memória
640    */
641    public function clear() {
642        imagedestroy($this->img['source']);
643    }
644
645    /**
646    * Deleta a imagem aberta pelo Pic::open
647    */
648    public function delete() {
649        if (file_exists($this->src)) unlink($this->src);
650    }
651}