Search code examples
javabufferedimagegraphics2d

How to create custom high-def images in Java


I am creating images in Java that have fonts written on them with a transparent background. I make the fonts different colors and also different types of font styles so I need the program to be dynamic. The issue is that I am using Graphics2D and writing on a Buffered Image using g2d.drawString() and the images aren't nearly the definition I'm looking for. I've tried creating large images with large font sizes and then downscaling but that doesn't work either. I also have set all of the possible RenderingHints to highest definition. I would like the pixel density to be high enough that there isn't much of a difference if you compared it with regular text on a retina screen. Thanks.


Solution

  • To have "retina" quality images in Java, you must create and render your BufferedImage at 2 times the normal size in both dimensions (this will make the image 4 times as large, which I think is what @MadProgrammer means).

    Then, you must not downsample (or "scale") the image in Java, but instead keep the BufferedImage in full size, and only draw the image in half size to a native backed Graphics2D instance. The Graphics object passed to the paint() or paintComponent() methods of an AWT or Swing component is normally fine (while the one from BufferedImage.get/createGraphics() isn't).

    I've used code like this with success:

    @Override
    public void paintComponent(Graphics g) {
        Graphics2D g2 = (Graphics2D) g;
        AffineTransform xform = AffineTransform.getScaleInstance(.5, .5);
        g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
                            RenderingHints.VALUE_INTERPOLATION_BILINEAR);
        g2.drawImage(image, xform, null); // image being @2x or "retina" size
    }
    

    However, note that the font rendering on modern computer uses "sub pixel antialiasing" or "sub pixel rendering", which is specific to the screen device you are rendering to (see the link, but basically, the RGB pattern or "layout" differs from device to device). This means a BufferedImage usually can't use sub pixel rendering, and thus fonts will look less crisp. If you are rendering to a single LCD screen, you might be able to specify one of the RenderingHints.TEXT_ANTIALIAS_LCD_* rendering hints for better results.