Transformando Texto em Shape para uso do Stroke

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!

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?

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:

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);

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

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.

[quote=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.[/quote]

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

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.

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.

[code]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);
}

}[/code]

Os penultimos ficaram melhores.

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.