在 Java 圖形處理中,直接使用 Graphics.drawString() 繪製的文字往往鋸齒明顯,且缺乏描邊、陰影等視覺效果。透過 Graphics2D 的渲染提示與 TextLayout,我們能實現高品質的文字繪製:抗鋸齒、描邊、填充雙重效果,適用於水印生成、圖片標註等場景。
import java.awt.*;
import java.awt.font.*;
import java.awt.geom.*;
import java.awt.image.BufferedImage;
public class HighQualityTextRenderer {
public static void drawTextWithStroke(BufferedImage image, String text,
int x, int y, Font font,
Color fillColor, Color strokeColor, float strokeWidth) {
Graphics2D g2 = image.createGraphics();
// 1. 啟用高品質渲染提示
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE);
g2.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY);
g2.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
// 2. 取得字體渲染上下文,建立 TextLayout
FontRenderContext frc = g2.getFontRenderContext();
TextLayout layout = new TextLayout(text, font, frc);
// 3. 獲取文字輪廓並平移到指定位置
Shape outline = layout.getOutline(AffineTransform.getTranslateInstance(x, y));
// 4. 先繪製描邊(外框)
g2.setStroke(new BasicStroke(strokeWidth));
g2.setColor(strokeColor);
g2.draw(outline);
// 5. 再繪製填充(內容)
g2.setColor(fillColor);
g2.fill(outline);
g2.dispose();
}
// 使用範例
public static void main(String[] args) {
BufferedImage img = new BufferedImage(400, 200, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2 = img.createGraphics();
g2.setColor(Color.WHITE);
g2.fillRect(0, 0, 400, 200);
g2.dispose();
drawTextWithStroke(img, "網址:example.com", 50, 100,
new Font("微軟正黑體", Font.BOLD, 24),
Color.BLACK, Color.WHITE, 3.0f);
// 儲存圖片...
}
}
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
開啟幾何圖形與文字的抗鋸齒,邊緣平滑不鋸齒。
TextLayout layout = new TextLayout(text, font, frc);
Shape outline = layout.getOutline(AffineTransform.getTranslateInstance(x, y));
TextLayout 封裝了文字的排版資訊,getOutline() 回傳文字的幾何輪廓,並透過 AffineTransform 移動到目標座標。
g2.draw(outline); // 先畫白色描邊
g2.fill(outline); // 再畫黑色填充
先繪製較寬的描邊,再繪製填充,形成「白邊黑字」的立體感;若反序則描邊會蓋住填充的一部分。
// 陰影效果
g2.setColor(new Color(0, 0, 0, 100));
g2.draw(outline.getBounds2D().getBounds().x + 2,
outline.getBounds2D().getBounds().y + 2);
// 雙重描邊:外層粗白、內層細黑
g2.setStroke(new BasicStroke(5f));
g2.setColor(Color.WHITE);
g2.draw(outline);
g2.setStroke(new BasicStroke(1.5f));
g2.setColor(Color.BLACK);
g2.draw(outline);
g2.setColor(Color.YELLOW);
g2.fill(outline);
TextLayout 建立較耗資源,大量文字重複繪製時建議快取 Shape 物件。g2.dispose() 釋放原生資源。BufferedImage.TYPE_INT_ARGB 保留透明通道。