Transformando Texto em Shape para uso do Stroke

10 respostas
D

Quero desenhar um contorno em volta do texto para que ele fique mais legível no fundo complexo do meu jogo.

Tentei transformar o texto em shape para então pintar o contorno usando Stroke, porém quando transformo o texto em shape ele perde muito a qualidade.

Tentei resolver usando antialiasing, o texto fica lindo, mas acho que pra usar o Stroke ficaria melhor sem o antialiasing, desde que não deformasse.

Vejam o código:

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.font.GlyphVector;

import javax.swing.JFrame;
import javax.swing.JPanel;

public class Texto extends JPanel {

	private static final long serialVersionUID = 1L;

	public void paint(Graphics g) {

		Font font = new Font("Tahoma", Font.BOLD, 12);
		String texto = "Teste de escrita em formato Shape";

		Graphics2D g2d = (Graphics2D) g;
		//g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);

		g2d.setFont(font);

		GlyphVector gv = font.createGlyphVector(g2d.getFontRenderContext(),texto);
		Shape jshape = gv.getOutline(); // Shape do texto

		g2d.setStroke(new BasicStroke(1.0f));
		g2d.translate(35, 100);

		g2d.setPaint(Color.blue);
		g2d.fill(jshape);
		g2d.setPaint(Color.black); 
		//g2d.draw(jshape); // Desenha o contorno do shape

		//Escreve o texto para comparar com o shape
		g2d.setPaint(Color.blue);
		g2d.drawString(texto, 0, 20);
	}

	public static void main(String[] a) {
		JFrame f = new JFrame();
		f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		f.setContentPane(new Texto());
		f.setSize(300,300);
		f.setVisible(true);
		f.setLocationRelativeTo(null);
	}
}

Se alguem souber outra maneira de fazer este efeito por favor me avise!

10 Respostas

D

Ainda estou com este problema.
No meu jogo, dependendo de onde o personagem estiver, o texto se mistura com as cores do fundo e fica horrível para ler. Pra piorar eu preciso escrever pequeno pois não posso ocupar muito espaço com texto.
A idéia é desenhar um contorno preto no texto como acontece em muitos outros jogos, mas a única idéia que tive até agora foi essa do código acima que não ficou legal.
Alguem tem uma idéia de como deixar o texto mais visível em fundos coloridos? Ou sabe como fazer esse contorno ficar legal?

Marky.Vasconcelos

Voce podia criar pintar um quadrado semi-transparente atraz do texto.
Assim é como eu faço em um jogo meu:

public void renderGraphics(Graphics2D surface) {
		surface.setComposite(AlphaComposite.SrcOver.derive(0.7f));
		surface.setColor(Color.black);
		surface.fillRect(0, 0, 140, 20);
		surface.setColor(Color.yellow);
		surface.drawString(text, 10, 17);
	}

E fica um visual bem legal.
Só que invés de drawString voce pode usar o seu draw pro Shape.

O segredo para ser levemente transparente é o:

surface.setComposite(AlphaComposite.SrcOver.derive(0.7f));

Quando vai liberar para vermos o que voce fez? :stuck_out_tongue:

M

Experimenta assim:

// cor que vai contornar o texto

g2d.drawString(texto, x - 1, y);

g2d.drawString(texto, x + 1, y);

g2d.drawString(texto, x, y - 1);

g2d.drawString(texto, x, y + 1);

// cor principal do texto
g2d.drawString(texto, x, y);

D

Marky, a sua idéia é boa mesmo. Eu já tinha pensado em fazer um balãozinho daqueles com o cantinho puxado mostrando quem está falando.
Derrepente junto as idéias e deixo um balão transparente.

Vou deixar essa idéia como opção e tentar mais um pouco a idéia anterior.

Cheguei a fazer um site para postar informações durante o desenvolvimento, mas como estava dando muito trabalho ficar atualizando o site para niguém ler, decidi tirar o site do ar e hoje tenho apenas um site para controle de versões e tarefas entre os desenvolvedores.

Mas estamos perto de lançar uma versão beta, aí com certeza aparecerão muitos erros ainda para eu vir aqui pedir ajuda de vocês hehehe

D

mochuara, pior que eu já tentei isso que você disse hehehe, foi minha primeira tentativa quando eu implantei o sistema de mensagens do jogo (faz mais de ano).
Mas não tinha dado um resultado bom não, nem lembro direito o motivo, acho que tinha ficado grosso o contorno.
Posso até testar de novo variando a fonte pra ver se encontro um resultado legal.

M

danielfroes:
mochuara, pior que eu já tentei isso que você disse hehehe, foi minha primeira tentativa quando eu implantei o sistema de mensagens do jogo (faz mais de ano).
Mas não tinha dado um resultado bom não, nem lembro direito o motivo, acho que tinha ficado grosso o contorno.
Posso até testar de novo variando a fonte pra ver se encontro um resultado legal.

O efeito é o mesmo, melhor do que isso só usando uma fonte com imagens customizadas. Mas a solução é tosca sem dúvida!

D

Gostei da idéia tosca da fonte com imagens customizadas hahahaha (tem como fazer isso?)

Eu vou fazer assim, logo que eu puder, vou fazer um teste de cada opção e coloco aqui os resultados pra compartilhar.

D

Segue uma imagem do teste que fiz hoje. O resultado foi bem satisfatório.
Pra testar com várias cores de fundo diferentes, coloquei a cor de fundo variando usando um código de um jogo antigo que nunca terminei de fazer.
Eu ia colocar um jnlp aqui mas apanhei pra fazer e desisti hehe. Mas fica aí o código comentado pra quem quiser testar.

Linha 1: Texto usando drawString
Linha 2: Texto usando Shape
Linha 3: Texto usando Shape e contorno usando Stroke.
Linha 4: Texto e contorno usando drawString, o contorno foi deslocando 1 pixel para cima, baixo e laterais.
Linha 5: Texto e contorno usando drawString, o contorno foi deslocando 1 pixel para cima, baixo, laterais e diagonais.

As 5 linhas seguintes é a mesma coisa, porém com anti-aliasing.

[img]http://img828.imageshack.us/img828/3994/textos.png[/img]

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.font.GlyphVector;

import javax.swing.JFrame;
import javax.swing.JPanel;

public class Texto extends JPanel {

	private static final long serialVersionUID = 1L;
	
	private int red=255;
	private int green=255;
	private int blue=1;
	
	public Texto() {
		variaCorFundo();
	}

	public void paint(Graphics g) {
		super.paint(g);
		
		Font font = new Font("Tahoma", Font.BOLD, 11);
		String texto = "Daniel Fróes Teste de escrita em formato Shape";

		Graphics2D g2d = (Graphics2D) g;

		g2d.setFont(font);

		g2d.setStroke(new BasicStroke(1.0f));
		g2d.translate(30, 50);
		
		//Escreve o texto para comparar com o shape
		g2d.setPaint(Color.blue);
		g2d.drawString(texto, 0, 0);

		//Desenha o texto em formato shape sem contorno
		g2d.translate(0, 20);
		GlyphVector gv = font.createGlyphVector(g2d.getFontRenderContext(),texto);
		Shape jshape = gv.getOutline(); // Shape do texto
		g2d.setPaint(Color.blue);
		g2d.fill(jshape);
		
		//Desenha o texto em formato shape com contorno (STROKE)
		g2d.translate(0, 20);
		g2d.setPaint(Color.blue);
		g2d.fill(jshape);
		g2d.setPaint(Color.black); 
		g2d.draw(jshape);

		//Desenha o texto de preto deslocando 1 pixel para cima, baixo, laterais
		g2d.translate(0, 20);
		g2d.setPaint(Color.black);
		g2d.drawString(texto, 0, -1);
		g2d.drawString(texto, 0, 1);
		g2d.drawString(texto, -1, 0);
		g2d.drawString(texto, 1, 0);
		g2d.setPaint(Color.blue);
		g2d.drawString(texto, 0, 0);
		
		//Desenha o texto de preto deslocando 1 pixel para cima, baixo, laterais e diagonais
		g2d.translate(0, 20);
		g2d.setPaint(Color.black);
		g2d.drawString(texto, 0, -1);
		g2d.drawString(texto, 0, 1);
		g2d.drawString(texto, -1, 0);
		g2d.drawString(texto, 1, 0);
		g2d.drawString(texto, -1, -1);
		g2d.drawString(texto, -1, 1);
		g2d.drawString(texto, 1, -1);
		g2d.drawString(texto, 1, 1);
		g2d.setPaint(Color.blue);
		g2d.drawString(texto, 0, 0);
		
		//Repete os desenhos anteriores com Anti-Aliasing
		g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
		
		g2d.translate(0, 40);
		
		//Escreve o texto para comparar com o shape
		g2d.setPaint(Color.blue);
		g2d.drawString(texto, 0, 0);

		//Desenha o texto em formato shape sem contorno
		g2d.translate(0, 20);
		g2d.setPaint(Color.blue);
		g2d.fill(jshape);
		
		//Desenha o texto em formato shape com contorno (STROKE)
		g2d.translate(0, 20);
		g2d.setPaint(Color.blue);
		g2d.fill(jshape);
		g2d.setPaint(Color.black); 
		g2d.draw(jshape);

		//Desenha o texto de preto deslocando 1 pixel para cima, baixo, laterais
		g2d.translate(0, 20);
		g2d.setPaint(Color.black);
		g2d.drawString(texto, 0, -1);
		g2d.drawString(texto, 0, 1);
		g2d.drawString(texto, -1, 0);
		g2d.drawString(texto, 1, 0);
		g2d.setPaint(Color.blue);
		g2d.drawString(texto, 0, 0);
		
		//Desenha o texto de preto deslocando 1 pixel para cima, baixo, laterais e diagonais
		g2d.translate(0, 20);
		g2d.setPaint(Color.black);
		g2d.drawString(texto, 0, -1);
		g2d.drawString(texto, 0, 1);
		g2d.drawString(texto, -1, 0);
		g2d.drawString(texto, 1, 0);
		g2d.drawString(texto, -1, -1);
		g2d.drawString(texto, -1, 1);
		g2d.drawString(texto, 1, -1);
		g2d.drawString(texto, 1, 1);
		g2d.setPaint(Color.blue);
		g2d.drawString(texto, 0, 0);

	}
	
	public void variaCorFundo() {
		Thread threadVariaCor = new Thread() {
			public void run() {
				while(true) {
					try {
						mudaCorFundo(1);
						Thread.sleep(10);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
			}
		};
		threadVariaCor.start();
	}
	
	public void mudaCorFundo(int quantidade) {
		for (int i=0;i<quantidade;i++) {
			if (red==255&&green<255&&blue==1) green++;
			if (red>1&&green==255&&blue==1) red--;
			if (red==1&&green==255&&blue<255) blue++;
			if (red==1&&green>1&&blue==255) green--;
			if (red<255&&green==1&&blue==255) red++;
			if (red==255&&green==1&&blue>1) blue--;
		}
		this.setBackground(new Color(red,green,blue));
	}

	public static void main(String[] a) {
		JFrame f = new JFrame();
		f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		f.setContentPane(new Texto());
		f.setSize(350,350);
		f.setVisible(true);
		f.setLocationRelativeTo(null);
	}
}
Marky.Vasconcelos

Os penultimos ficaram melhores.

D

Editei a mensagem anterior explicando como foi feita cada linha. (está comentado no código também)

Estou em dúvida entre a última e a penultima.
Será que o anti-aliasing é pesado?
Além de ter que desenhar várias vezes por segundo, para fazer o contorno da última linha eu tenho que desenhar 9 vezes.

Criado 5 de setembro de 2010
Ultima resposta 16 de set. de 2010
Respostas 10
Participantes 3