此代码片段创建一个包含文本的图像。我将字体设置为Serif。但是,当我稍后查询结果图像的字体时,它会返回对话框。我不明白为什么会这样。
BufferedImage img = new BufferedImage(200, 100, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2d = img.createGraphics();
g2d.drawImage(img, 0, 0, 200, 100, this); // "this" refers to my custom JPanel which I am setting in the JFrame.
g2d.setPaint(Color.red);
g2d.setFont(new Font("Serif", Font.BOLD, 20));
System.out.println("from g2d object: " + g2d.getFont().getFamily()); // outputs "Serif"
String s = "Hello, world!";
FontMetrics fm = g2d.getFontMetrics();
int x = img.getWidth() - fm.stringWidth(s) - 5;
int y = fm.getHeight();
g2d.drawString(s, x, y);
g2d.dispose();
System.out.println("from image: " + img.getGraphics().getFont().getFamily()); // outputs "Dialog" (expected "Serif")
我知道Dialog是Java中的逻辑字体之一,但如果字体设置为其他字体,并且Font.getFontName()
返回给定字体的字体面,为什么它不返回Graphics2D
对象中设置的Serif?
UPDATE:在最后一个System.out.println()
之前或之后调用g2d.dispose()
没有区别。无论哪种方式,它仍然打印出对话框。
BufferedImage.getGraphics()
返回BufferedImage.createGraphics()
的结果。并且,按照这个轨迹,我们最终得到了SunGraphicsEnvironment
:的方法
/**
* Returns a Graphics2D object for rendering into the
* given BufferedImage.
* @throws NullPointerException if BufferedImage argument is null
*/
public Graphics2D createGraphics(BufferedImage img) {
if (img == null) {
throw new NullPointerException("BufferedImage cannot be null");
}
SurfaceData sd = SurfaceData.getPrimarySurfaceData(img);
return new SunGraphics2D(sd, Color.white, Color.black, defaultFont);
}
我们可以清楚地看到,它使用了defaultFont
,几乎没有连接到图像(除非getPrimarySurface()
确实更改了defaultFont
-我想不会[我找不到它正在更改](。
源代码可以在这里找到
设置Graphics
的字体不会改变BufferedImage
中的任何内容。Graphics
使用Font
在图像上绘制文本。如果从图像中获得新的Graphics
(使用getGraphics()
或createGraphics()
(,则它将具有GraphicsEnvironment
中定义的defaultFont
。
这样做:
BufferedImage img = new BufferedImage(200, 100, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2d = img.createGraphics();
g2d.drawImage(img, 0, 0, 200, 100, null); // "this" refers to my custom JPanel which I am setting in the JFrame.
g2d.setPaint(Color.red);
g2d.setFont(new Font("Serif", Font.BOLD, 20));
System.out.println("from g2d object: " + g2d.getFont().getFamily()); // outputs "Serif"
String s = "Hello, world!";
FontMetrics fm = g2d.getFontMetrics();
int x = img.getWidth() - fm.stringWidth(s) - 5;
int y = fm.getHeight();
g2d.drawString(s, x, y);
System.out.println(g2d.getFont().toString()+"-"+img.getGraphics().getFont().toString()+" from image: " + img.getGraphics().getFont().getFamily());
g2d.dispose();
System.out.println("from image: " + img.getGraphics().getFont().getFamily());
正如你所看到的,g2d保留了它钟爱的Serif符号。图像本身就是一个东西。每次在图像上调用createGraphics时,都会得到一个新东西。
每个人都在让它变得比需要的更复杂。答案很简单:Image的getGraphics()
方法总是创建一个全新的Graphics对象。来自文件:
创建用于绘制屏幕外图像的图形上下文。
仅此而已。您在一个Graphics对象中设置字体,然后创建另一个Graphic对象。当然,新的Graphics对象对第一个Graphics对象的状态一无所知。这就像期望打开一辆车的前灯会以某种方式导致所有汽车都打开前灯一样。
只需调用一次getGraphics((就可以获得预期的结果
System.out.println("from image: " + g2d.getFont().getFamily());
TL:DR字体在Graphics
或Graphics2D
中不可访问。
无论是直接调用BufferedImage#createGraphics()
还是BufferedImage#getGraphics()
,最终结果都是相同的:它们都创建了一个新的图形对象。更具体地说,前者返回一个新的Graphics
对象,而后者返回一个Graphics2D
对象。两者都是返回的具体类型的抽象,该类型是SunGraphics2D
的实例。遗憾的是,此对象没有在图形对象中存储任何与字体相关的信息集。所以基本上,我无法从图像对象中获得所需的数据。
相反,ProxyGraphics2D
和PeekGraphics
这两个抽象类Graphics2D
类的子类型确实将这些信息存储在名为mGraphics
的Graphics2D
全局变量中。该对象是通过getDelegate()
方法访问的,这显然是而不是Graphics
或Graphics2D
API的部分。不幸的是,这两个子类都是无法再访问的sun
包的一部分。检查这些类清楚地表明,像setFont()
这样的方法确实将字体保存在此委托(Graphics2D
(对象中。