Edição de Imagem com PHP GD

Artigo que apresenta as principais funções da extensão GD para o PHP, que permite a geração dinâmica de imagens em diferentes formatos, como JPG, GIF, BMP e PNG.

Introdução

Embora PHP seja especialmente desenvolvido para geração de páginas dinâmicas (HTML), ele também possui recursos para manipular outros tipos de dados, por exemplo, imagens. Neste post, veremos como trabalhar com a biblioteca GD sem precisar criar uma matriz de cores da imagem, ou seja, trabalhar diretamente na imagem.

Criando ou abrindo uma Imagem com PHP-GD

A biblioteca GD do PHP trabalha com a imagem através de um resource (uma variável de tipo especial) e um conjunto de funções que usam este resource. O resource representa a imagem e pode ser criado com as funções com nome "imagecreate...".

Para criar um resource do zero (imagem com fundo preto) com a biblioteca GD, basta usar a função imagecreate ou imagecreatetruecolor. A primeira cria uma imagem baseada em uma palheta de cores (pode-se usar um número limitado de cores), já a segunda é mais indicada para imagens com milhões de cores.

Para criar um resource de uma imagem já existente, basta usar uma das funções com nome "imagecreatefrom...". Veja alguns exemplos:

  • imagecreatefromgif - Abre uma imagem GIF.
  • imagecreatefromjpeg - Abre uma imagem JPEG (JPG).
  • imagecreatefrompgn - Abre uma imagem PNG.
  • imagecreatefromwbmp - Abre uma imagem WBMP.
  • imagecreatefromstring - Abre uma imagem a partir do seu conteúdo binário.

Exemplos:

// Criando uma imagem truecolor, com tamanho 200px por 300px, e fundo preto
$img1 = imagecreatetruecolor(200, 300);

// Abrindo uma imagem JPG existente
$img2 = imagecreatefromjpeg('/dados/imagens/foto.jpg');

Estas funções devem retornar um resource ou false, caso ocorra alguma falha. Note que nem sempre a biblioteca GD está instalada de forma a suportar todos os tipos de imagem citados.


Obtendo o tamanho da Imagem com PHP-GD

Após criar ou abrir uma imagem e obter um resource via PHP-GD, podemos usar as funções imagesx e imagesy para obter o tamanho delas. Estas funções recebem o resource por parâmetro e devolvem um inteiro representando a largura e a altura, respectivamente. Ter a largura e a altura em mãos é fundamental para realizar operações específicas na imagem, como redimensioná-la mantendo a proporção, buscar as cores de cada pixel, etc.

Uma forma alternativa para obter o tamanho é usando a função getimagesize. Esta função não recebe um resource por parâmetro, mas sim o caminho até a imagem já existente. Ela devolve um array com 7 posições, conforme descrito:

  • Posições 0 e 1 representam a largura e altura respectivamente.
  • Posição 2 guarda o tipo da imagem, que pode ser uma das constantes de formato IMAGETYPE_... e pode ser consultado na lista de constantes da biblioteca GD.
  • Posição 3 guarda uma string com formato height="..." width="...", que pode ser usada dentro de uma tag <img> para especificar a altura e largura da imagem.
  • Posição "bits" guarda o número de bits para cada cor.
  • Posição "channels" guarda 3 para imagens RGB ou 4 para imagens RGBA (camada alpha para transparência).
  • Posição "mime" guarda o mime-type da imagem, e pode ser útil para mostrá-la dinamicamente via PHP.

Com esta função, podemos criar um algoritmo que abre uma imagem independente do tipo dela:

// Obter dados do arquivo de imagem
$dados = getimagesize($arq);

// Determinar se o tipo de imagem e' suportado
$tipo = $dados[2];
if ($tipo & imagetypes()) {
    switch ($tipo) {
    case IMG_GIF:
        $img = imagecreatefromgif($arq);
        break;
    case IMG_JPEG:
        $img = imagecreatefromjpeg($arq);
        break;
    case IMG_PNG:
        $img = imagecreatefrompng($arq);
        break;
    case IMG_WBMP:
        $img = imagecreatefromwbmp($arq);
        break;
    case IMG_XPM:
        $img = imagecreatefromxpm($arq);
        break;
    default:
        $conteudo = file_get_contents($arq);
        $img = imagecreatefromstring($conteudo);
        break;
    }
}

Obtendo/Modificando as cores de cada pixel da Imagem via PHP-GD

Para obter as cores de cada pixel da imagem, basta percorrer toda a imagem pelos eixos X e Y, então usar a função imagecolorat para obter a cor de uma coordenada específica. Para modificar a cor de um pixel, basta usar a função imagesetpixel sobre uma coordenada e informar a cor desejada. Esta cor pode ser obtida usando a função imagecolorallocate. Veja um exemplo de como obter o RGB de cada pixel e substituí-lo pelo seu complemento de 256, ou seja, inverter as cores de uma imagem.

...
// Obter largura e altura da imagem
$w = imagesx($img);
$h = imagesy($img);

// Percorrer cada pixel da imagem
for ($y = 0; $y < $h; $y++) {
    for ($x = 0; $x < $w; $x++) {

        // Obter a cor do pixel da posicao [$x, $y]
        $cor1 = imagecolorat($img, $x, $y);
        $r1 = ($cor1 >> 16) & 0xFF;
        $g1 = ($cor1 >>  8) & 0xFF;
        $b1 = ($cor1 >>  0) & 0xFF;

        // Criar cor complementar e modificar o pixel
        $r2 = 0xFF - $r1;
        $g2 = 0xFF - $g1;
        $b2 = 0xFF - $b1;
        $cor2 = imagecolorallocate($img, $r2, $g2, $b2);

        // Modificar a cor do pixel da posicao [$x, $y]
        imagesetpixel($img, $x, $y, $cor2);
    }
}

Manipulando a Imagem com PHP-GD

Com os recursos apresentados, é possível realizar qualquer modificação na imagem, desenhar formas geométricas, redimensionar, etc. Porém, algumas destas tarefas podem exigir muitas linhas de código e algoritmos sofisticados para resolver. Felizmente, a biblioteca GD oferece algumas funções que facilitam este tipo de serviço. Veja abaixo algumas delas.

Manipulação da Imagem:

  • imagecopy: copia parte de um resource para outro resource.
  • imagecopyresampled ou imagecopyresized: copia parte de um resource para outro resorce de forma redimensionada (ampliada ou reduzida).
  • imagecopymerge: copia parte de um resource sobre outro resource, como uma marca d'água.
  • imagecopymerge: copia parte de um resource sobre outro resource, como uma marca d'água em tons de cinza.
  • imagerotate: recebe um resource e devolve outro resource com a imagem rotacionada (pode ter outro tamanho).

Desenho:

  • imageline: desenha uma linha simples ou estilizada.
  • imagerectangle: desenha um retângulo (apenas borda).
  • imagearc: desenha um arco (apenas borda).
  • imageellipse: desenha uma elipse (apenas a borda).
  • imagepolygon: desenha um polígono (apenas borda).
  • imagefilledrectangle: desenha um retângulo preenchido com uma cor
  • imagefilledarc: desenha um arco preenchido com uma cor.
  • imagefilledellipse: desenha uma elipse preenchida com uma cor.
  • imagefilledpolygon: desenha um polígono preenchido com uma cor.
  • imagefill: preenche um ponto da imagem com uma cor.
  • imagefilltoborder: preenche um ponto da imagem com uma cor até encontrar uma borda de uma cor específica.

Exemplo de desenho de um retângulo vermelho, depois duas linhas pontilhadas ligando as bordas opostas:

// Alocando a cor vermelha (#FF0000)
$cor = imagecolorallocate($img, 0xFF, 0x00, 0x00);

// Desenhando um retangulo que vai do ponto [10,10] ate [50,70] usando a cor alocada
imagerectangle($img, 10, 10, 50, 70, $cor);

// Criando uma cor estilizada:
// Vermelho + Vermelho + Transparente + Transparente
imagesetstyle($img, array($cor, $cor, IMG_COLOR_TRANSPARENT, IMG_COLOR_TRANSPARENT));

// Desenhando uma linha do ponto [10,10] ate [50,70] usando cor estilizada
imageline($img, 10, 10, 50, 70, IMG_COLOR_STYLED);

// Desenhando uma linha do ponto [50,10] ate [10,70] usando cor estilizada
imageline($img, 50, 10, 10, 70, IMG_COLOR_STYLED);

Apresentando ou salvando a Imagem com PHP-GD

Para mostrar uma imagem via PHP, o script precisa especificar que o pacote HTTP que será enviado é do mime-type "image/...". Para cada tipo de imagem, existe uma função própria para devolver a imagem de um resource na forma binária. Veja um exemplo de como exibir uma imagem JPEG a partir de um resouce:

...
$qualidade = 100; // valor entre 0-100

header('Content-Type: image/jpeg');
imagejpeg($img, null, $qualidade);

Para salvar a imagem de um resource em um arquivo, basta informar o segundo parâmetro, conforme o exemplo:

...
$destino = '/tmp/imagem.jpg';
$qualidade = 100; // valor entre 0-100

imagejpeg($img, $destino, $qualidade);

As funções para geração de imagens são:

  • imagegd e imagegd2: gera uma imagem GD ou GD2.
  • imagegif: gera uma imagem GIF.
  • imagejpeg: gera uma imagem JPEG.
  • imagepng: gera uma imagem PNG.
  • imagewbmp: gera uma imagem WBMP.
  • imagexbm: gera uma imagem XBM.

Incluindo textos na Imagem com PHP-GD

Para incluir textos na imagem, é possível usar uma fonte genérica (número de 1 a 5), uma fonte TTF (True Type Font), uma fonte FreeType2 ou uma fonte PS (PostScript Type1). A vantagem das fontes TTF, FreeType2 ou PS é que elas podem suportar codificação UTF-8 e também podem ser apresentadas sob algum ângulo de inclinação. As funções para manipulação de textos em imagens são:

  • Fonte Genérica:
    • imageloadfont: carrega um arquivo de fonte.
    • imagechar: imprime um caractere horizontalmente na imagem.
    • imagecharup: imprime um caractere verticalmente na imagem.
    • imagestring: imprime uma string horizontalmente na imagem.
    • imagestringup: imprime uma string verticalmente na imagem
    • imagefontheight: obtém a altura da fonte.
    • imagefontwidth: obtém a largura da fonte.
  • Fonte FreeType2:
    • imageftbbox: obtém as posições que uma string ocuparia se usasse fonte FreeType2.
    • imagefttext: imprime uma string na imagem usando fonte FreeType2.
  • Fonte PS
    • imagepsbbox: obtém as posições que uma string ocuparia se usasse fonte PS.
    • imagepstext: imprime uma string na imagem usando fonte PS.
    • imagepsloadfont: carrega uma fonte PS.
  • Fonte TrueType
    • imagettfbbox: obtém as posições que uma string ocuparia se usasse fonte TrueType.
    • imagettftext: imprime uma string na imagem usando fonte TrueType.

Observações sobre PHP-GD

Após utilizar um resouce, é recomendável desalocá-lo da memória, para liberar recursos e evitar um estouro de memória. Para isso, basta usar a função imagedestroy passando o resource. Depois de passar uma variável para esta função, ela não deve mais ser usada.

Além disso, a biblioteca GD oferece várias outras funções úteis, por exemplo, para efeitos:

  • imageconvolution: efeito de auto-relevo ou baixo-relevo.
  • imagefilter: aplica um filtro.
  • imagegammacorrect: aplica uma correção de gamma.
  • imagelayereffect: aplica um efeito sobre a camada.

Com a biblioteca GD, é possível manipular imagens ou gerar imagens com ilustrações, diagramas ou gráficos estatísticos.

31 comentários

comentários disse...

Ola Meu caro. Estou com um grande problema para resolver e nao consegui ainda. Tenho uma umagem em PNG e preciso saber quantos pontos vermelhos ela tem. Preciso ecodificar esta imagem, representá-la em uma matriz de bytes (cada célula contendo a cor do pixel equivalente) e interar sobre esta matriz para contar os pixels vermelhos. se pude rme responder por email ficaria muito feliz robsnunesvieira@gmail.com

Rubens Takiguti Ribeiro (autor do blog) disse...

Olá,

Ao abrir uma imagem com imagecreatefrompng, a GD já cria internamente uma matriz de pixels, então provavelmente não vai precisar criar uma estrutura de dados matriz manualmente.

Para percorrê-la, basta caminhar de linha em linha (de zero até imagesy) e coluna em coluna (de zero até imagesx). Depois, para obter o RGB do pixel, deve usar imagecolorat.

Para determinar se a cor é vermelha, precisa primeiro definir o que será considerado vermelho. Por exemplo, na componente RGB, a componente R deve ser superior às componentes G e B (com uma determinada sobra), mas G e B devem ter valores próximos (precisa definir o nível de proximidade aceitável).

Unknown disse...

Meu caro, voc~e pode me dar uma luz de como eu poderia usar essas classes para editar uma imagem de maneira que eu digite uma frase dentro de um input type="text" e essa frase seja inserida dentro da imagem? Obrigado!

Rubens Takiguti Ribeiro (autor do blog) disse...

Ariel, para fazer isso, você cria o formulário e no script que recebe os dados do formulário, você faz o seguinte:

// Largura e Altura da imagem
$largura = 400;
$altura = 200;

// Cria a imagem
$img = imagecreatetruecolor($largura, $altura);

// Pinta o fundo da imagem com a cor branca
$branco = imagecolorallocate($img, 0xFF, 0xFF, 0xFF);
imagefill($img, 0, 0, $branco);

// Escreve o texto na imagem em preto
$tamanho_fonte = 20;
$angulo = 0;
$x = 10; $y = 30; // Canto inferior esquerdo do texto
$preto = imagecolorallocate($img, 0x00, 0x00, 0x00);
$fonte = '/caminho/para/a/fonte.ttf';

imagettftext($img, $tamanho_fonte, $angulo, $x, $y, $preto, $fonte, $_POST['texto']);

// Exibe a imagem em PNG
header('Content-Type: image/png');
imagepng($img);

// Libera a memoria
imagedestroy($img);

Unknown disse...

Obrigado! Só uma dúvida, eu crio esse script em um arquivo separado e chamo ele no <*form* action="arquivo.php" method="post"> certo?

Tudo World disse...

Olá, amigo. Gostaria de saber se tem como comprimir uma imagem, ou seja, reduzir seus bytes por pixel seu uso de FUNÇÃO, mesmo que a imagem perca qualidade. Agradeço!

Rubens Takiguti Ribeiro (autor do blog) disse...

Olá, Tudo World
Seguem algumas alternativas:

1 - Usar a função imagetruecolortopalette (http://www.php.net/manual/en/function.imagetruecolortopalette.php) para utilizar uma palheta de cores. Dê uma olhada na documentação, mas já adianto que o último parâmetro é o número de cores máximo na sua palheta, então você pode fazer alguns testes.

2 - Salvar a imagem como jpg (imagejpeg) e especificar a taxa de qualidade da imagem no terceiro parâmetro. Isso também reduz bem o tamanho do arquivo e a imagem ainda fica razoável (pouca perda de qualidade) até a qualidade 80 ou 70, dependendo do caso.

Tudo World disse...

Olá, caro.

Sou bem iniciante no PHP por isso estou tendo dificuldade. Mas desde já, lhe pergunto se tem como você dar uma exemplo pra mim com poucas linhas de código, se possível. Agradeço !

Rubens Takiguti Ribeiro (autor do blog) disse...

Olá, Tudo World

Vou deixar m exemplo simples, que abre um arquivo "iimagem.jpg" e salva um arquivo "saida.jpg" com menos qualidade:

$img = imagecreategromjpeg('imagem.jpg');
imagejpeg($img, 'saida.jpg', 70);

Tudo World disse...

Olá, Rubens

Entendi bem o código que me passou, mas por acaso você tem algum email pra que eu possa te passar uma dúvida bem complexa ? É jogo rápido, se não te incomodar, é claro. Obrigado !

Conhece Brasília ? disse...

Olá Rubens, parabéns pelo blog!!!

Tenho uma dúvida: preciso que o usuário desenhe uma linha dentro da imagem utilizando apenas o mouse. Como seria possível isto?
Abraços

Rubens Takiguti Ribeiro (autor do blog) disse...

Olá, "Conhece Brasília?"

A biblioteca GD é útil para se gerar uma imagem pelo servidor. Porém, o PHP para web funciona na base de requisições/respostas (protocolo HTTP).

A interação com o mouse é feita inteiramente no lado do cliente, ou seja, o seu navegador é que é capaz de identificar este tipo de movimentação através de JavaScript.

Portanto, GD não é a melhor alternativa para fazer o que você está querendo. É até possível, mas bastante complexo e a performance não será das melhoras.

O ideal, na minha opinião, é que você utilize algo para gerar a imagem no lado do cliente, através de JavaScript. Por exemplo, utilizando o canvas do HTML5.

Jefmachia disse...

Olá amigo. Estou com um projeto que eu teria que digitar o nome de uma pessoa em um input type text e o php teria que editar essa imagem e jogar o nome digitado em uma determina parte da imagem e depois salvar no PC. Seria possível com essas técnicas que você passa aqui?

Rubens Takiguti Ribeiro (autor do blog) disse...

Jefmachia, não coloquei exemplo de como gerar texto, mas você pode ver um exemplo aqui (usando fonte TTF, que são bem comuns):
http://php.net/imagettfbbox

Note que você precisa ter a fonte ttf no servidor para poder usá-la.

Hamilton Trindade disse...

Ola amigo boas, estou com uma dificuldade e gostaria que vc me ajudasse. Segui o seu tutorial e deu tudo certo mas não obstante queria formatar o texto de fora a ficar centralizado na imagem. Mas usando a posição fixa não da jeito por exemplo quando tenho textos grandes, gostaria de ter algo justificado ao centro, será que tem como fazer isto? Obrigado

Rubens Takiguti Ribeiro (autor do blog) disse...

Olá, Hamilton
Não há algo nativo na GD para dispor textos de forma justificada. Porém, na própria documentação do PHP teve um cara que postou uma função para fazer isso:
http://php.net/manual/en/function.imagettftext.php#113563

Unknown disse...

Olá Rubens blz belo post, tou quebrando a cabeça aqui em relação a colocar uma imagem dentro da outra essa última em circulo redonda é possivel? Vi um outro post seu com cantos arredondados fiz na pratica blz mas a ideia seria ao inves de cantos arredondados uma imagem dentro da outra totalmente igual uma bola. vlw desde já

Rubens Takiguti Ribeiro (autor do blog) disse...

BrSim,
Para fazer isso, você pode usar uma imagem de máscara. Esse link tem um exemplo de como fazer:
http://stackoverflow.com/questions/7203160/php-gd-use-one-image-to-mask-another-image-including-transparency

Note que se você quer aplicar isso para imagens de tamanhos variados, então você precisa criar a imagem de máscara dinamicamente, ou seja, criar a imagem com as dimensões desejadas, aplicar fundo transparente, desenhar uma bola com o tamanho desejado e então seguir o fluxo como mostrado no link.

daniel disse...

Olá amigo tudo bem, depois de manipulada a imagem como faço para salvar em uma pata especifica no servidor, por exemplo na pasta images Obrigado.

Unknown disse...

Olá Rubens, tudo bem?

Eu estou gerando uma imagem com a GD, e nesta imagem coloquei várias frases, porém, para deixar mais organizado, preciso "pular linha". Mas já tentei de várias formas, e não consigo. O texto continua todo na mesma linha. Poderia me ajudar? Por favor.
Obrigada.

Rubens Takiguti Ribeiro (autor do blog) disse...

Não lembro de algo que pule linhas automaticamente com gd. Pode ser útil usar as funções wordwrap e explode para pegar linhas com um tamanho máximo, daí imprimir cada uma num ponto da imagem. Isso pode ser sufuciente para texto monoespaçado (fontes que todas letras tem mesma largura). Para maior precisão, use imageftbbox pra calcular quanto de espaço um texto vai ocupar, daí você só imprime até a palavra que tem certeza que cabe. Então calcula o texto da próxima linha e continua o processo.