1. Introdução
Na nossa jornada em aprofundarmos no estudo da plataforma Java SE começaremos a entender o pacote java.awt que possui diversos componentes relacionados ao desenvolvimento de aplicações gráficas com Java com AWT (Abstract Window Toolkit).
Neste artigo, exploraremos os conceitos de duas interfaces desse pacote a Composite e CompositeContext, veremos um exemplo de uso com um Composite nativo (AlphaComposite) e criaremos um Composite personalizado de mistura de cores para objetos gráficos.
2. Interfaces Composite e CompositeContext
A interface Composite é usada para definir como um objeto gráfico será composto sobre outro. Ela possui o método:
CompositeContext createContext(ColorModel srcColorModel, ColorModel dstColorModel, RenderingHints hints);
Esse método retorna um CompositeContext, que é responsável pela lógica real de composição pixel a pixel. O CompositeContext possui o seguinte método chave:
void compose(Raster src, Raster dstIn, WritableRaster dstOut);
Esse método define como os pixels do objeto a ser pintado (src) serão combinados com os pixels dos objetos que já foram pintados na tela (dstIn) para produzir a saída final (dstOut). Vale ressaltar que o dstIn representa todos os pixels da tela que já foram pintados, e não apenas os pixels do último objeto pintado.
3. Implementação
Exemplo com AlphaComposite
O AlphaComposite é um dos Composite nativos do Java, permitindo controlar a opacidade de desenhos. Vamos criar um exemplo simples de como utilizá-lo para desenhar formas semi-transparentes.
import java.awt.*;
public class AlphaCompositeExample extends Frame {
private double alpha = 0.5;
public static void main(String[] args) {
AlphaCompositeExample frame = new AlphaCompositeExample();
frame.setSize(200,150);
frame.setVisible(true);
}
@Override
public void paint(Graphics g) {
super.paint(g);
Graphics2D g2 = (Graphics2D) g;
g2.setColor(Color.WHITE);
g2.fillRect(0, 0, getWidth(), getHeight());
AlphaComposite alpha = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.5f);
g2.setComposite(alpha);
g2.setColor(Color.RED);
g2.fillRect(50, 50, 50, 50);
g2.setColor(Color.BLUE);
g2.fillRect(80, 80, 50, 50);
}
}Neste exemplo, criamos um AlphaComposite com 50% de opacidade (0.5f). Quando desenhamos um retângulo vermelho e um círculo azul, eles aparecem semi-transparentes, permitindo visualizar a sobreposição das formas. A Figura 1 mostra o resultado.

Criando um Composite Personalizado
Agora, vamos criar um Composite personalizado que mistura cores de forma diferenciada. Implementaremos um Composite usando a abordagem lambda do Java e implementaremos a interface CompositeContext, combinando as cores de origem e destino ao somar os valores dos pixels de origem com os pixels de destino. Com uma leve diferença, aplicaremos um fator alpha nos pixels de destino.
import java.awt.*;
import java.awt.image.BufferedImage;
import java.awt.image.Raster;
import java.awt.image.WritableRaster;
public class ColorMixCompositeExample extends Frame {
private double alpha = 0.5;
public static void main(String[] args) {
ColorMixCompositeExample frame = new ColorMixCompositeExample();
frame.setSize(200,150);
frame.setVisible(true);
}
@Override
public void paint(Graphics g) {
super.paint(g);
Graphics2D g2 = (Graphics2D) g;
g2.setColor(Color.WHITE);
g2.fillRect(0, 0, getWidth(), getHeight());
BufferedImage image = new BufferedImage(200, 150, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2d = image.createGraphics();
g2d.setComposite((srcColorModel, dstColorModel, hints) -> new CompositeContext() {
private double alpha = 0.5;
@Override
public void dispose() {
}
@Override
public void compose(Raster src, Raster dstIn, WritableRaster dstOut) {
int[] srcPixels = new int[4];
int[] dstPixels = new int[4];
int width = Math.min(src.getWidth(), dstIn.getWidth());
int height = Math.min(src.getHeight(), dstIn.getHeight());
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
src.getPixel(x, y, srcPixels);
dstIn.getPixel(x, y, dstPixels);
dstPixels[0] = (int) (srcPixels[0] + dstPixels[0] * (1 - alpha));
dstPixels[1] = (int) (srcPixels[1] + dstPixels[1] * (1 - alpha));
dstPixels[2] = (int) (srcPixels[2] + dstPixels[2] * (1 - alpha));
dstPixels[3] = 255;
dstOut.setPixel(x, y, dstPixels);
}
}
}
});
g2d.setColor(Color.RED);
g2d.fillRect(50, 50, 50, 50);
g2d.setColor(Color.BLUE);
g2d.fillRect(80, 80, 50, 50);
g2d.dispose();
g2.drawImage(image, 0,0,this);
}
}Esse exemplo cria um efeito de mistura onde o vermelho e o azul se combinam para formar uma nova cor no ponto de interseção. No AWT puro (sem Swing), o suporte para Composite é limitado e depende da implementação gráfica subjacente. Por isso utilizamos de um BufferedImage para desenhar os objetos nele usando nosso Composite customizado e depois pintar na tela. A Figura 2 mostra o resultado.
É importante entender que, cada vez que um novo objeto for pintado na tela, o método compose será chamado, e é nesse momento que os pixels serão manipulados.

3. Conclusão
O uso das interfaces Composite e CompositeContext no Java AWT é uma ferramenta poderosa para manipulação de imagens e efeitos visuais. Vimos como o AlphaComposite pode ser usado para transparência e criamos um Composite personalizado para mistura de cores.
Essa abordagem abre um leque de possibilidades para efeitos visuais sofisticados, permitindo uma maior personalização na renderização de elementos gráficos em aplicações Java.
Experimente modificar o ColorMixCompositeExample para testar diferentes algoritmos de mistura e criar seus próprios efeitos!