Question

I have a question regarding the quality of the drawString function, currently I have this image: example

Basically what you see in the bottom middle is a card drawn on 125% of it's size. The cards in the background are relatively 50% and 25% size.

However what I noticed is that the quality fo the text in the description box is not as good as it should be. I agree on that the font size isn't exactly that big, but in order to fit all text in there I needed to come up with some solution and this seems to be the best for now.

The Card class:

package gui;

import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Polygon;
import java.awt.RenderingHints;
import java.awt.font.GlyphVector;
import java.awt.font.LineMetrics;
import java.awt.image.BufferedImage;
import javax.swing.JPanel;
import model.cards.Card;

/**
 *
 * @author Frank
 */
public class CardPanel extends JPanel {    
    private static final Dimension CARD_DIMENSION = new Dimension(250, 350);

    private final Card card;
    private final double scale;
    private final boolean defaultOrientation;

    private BufferedImage bufferedImage;
    private BufferedImage scaledImage;

    private BufferedImage backBufferedImage;
    private BufferedImage backScaledImage;

    public CardPanel(final Card card, final double scale) {
        this(card, scale, true);
    }

    public CardPanel(final Card card, final double scale, final boolean defaultOrientation) {
        this.card = card;
        this.scale = scale;
        this.defaultOrientation = defaultOrientation;
        createImage();
        scaleImage();
        createBackImage();
        scaleBackImage();
        this.setPreferredSize(new Dimension(scaledImage.getWidth(), scaledImage.getHeight()));
    }

    public Card getCard() {
        return card;
    }

    public double getScale() {
        return scale;
    }

    @Override
    public void paintComponent(Graphics g) {
        super.paintComponent(g);
        Graphics2D g2d = (Graphics2D)g.create();

        //Graphics quality
        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        g2d.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY);
        g2d.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY);
        g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
        g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);

        g2d.setComposite(AlphaComposite.Clear);
        g2d.clearRect(0, 0, scaledImage.getWidth(), scaledImage.getHeight());

        g2d.setComposite(AlphaComposite.SrcOver);

        if (card.getFlipped()) {
            g2d.drawImage(scaledImage, 0, 0, null);
        }
        else {
            g2d.drawImage(backScaledImage, 0, 0, null);
        }

        g2d.dispose();
    }

    public BufferedImage getImage() {
        if (card.getFlipped()) {
            return scaledImage;
        }
        else {
            return backScaledImage;
        }
    }

    private void scaleImage() {
        scaledImage = new BufferedImage((int)Math.floor(scale * CARD_DIMENSION.width), (int)Math.floor(scale * CARD_DIMENSION.height), BufferedImage.TYPE_INT_ARGB);
        Graphics2D g2d = (Graphics2D)scaledImage.getGraphics();

        //Graphics quality
        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        g2d.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY);
        g2d.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY);
        g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
        g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);

        g2d.scale(scale, scale);

        g2d.drawImage(bufferedImage, 0, 0, null);

        g2d.dispose();
    }

    private void scaleBackImage() {
        backScaledImage = new BufferedImage((int)Math.floor(scale * CARD_DIMENSION.width), (int)Math.floor(scale * CARD_DIMENSION.height), BufferedImage.TYPE_INT_ARGB);
        Graphics2D g2d = (Graphics2D)backScaledImage.getGraphics();

        //Graphics quality
        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        g2d.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY);
        g2d.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY);
        g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
        g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);

        g2d.scale(scale, scale);

        g2d.drawImage(backBufferedImage, 0, 0, null);

        g2d.dispose();
    }

    private void createImage() {
        bufferedImage = new BufferedImage(CARD_DIMENSION.width, CARD_DIMENSION.height, BufferedImage.TYPE_INT_ARGB);
        final Graphics2D g2d = (Graphics2D)bufferedImage.createGraphics();

        //Graphics quality
        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        g2d.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY);
        g2d.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY);
        g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
        g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);

        if (!defaultOrientation) {
            g2d.rotate(Math.toRadians(180), bufferedImage.getWidth() / 2, bufferedImage.getHeight() / 2);
        }

        final int red = card.getColor().getRed();
        final int green = card.getColor().getGreen();
        final int blue = card.getColor().getBlue();
        final Color color = new Color(red, green, blue);

        new CustomRectangle(bufferedImage, CARD_DIMENSION.width, CARD_DIMENSION.height, 0, 0, 5, defaultOrientation) {
            @Override
            public void inBorder(final int dx, final int dy) {
                setColor(new Color(red, green, blue, 255 - Math.min(dx, dy)));
            }

            @Override
            public void outBorder(final int dx, final int dy) {
                setColor(new Color(red, green, blue, 192 - Math.min(dx, dy)));
            }
        }.draw();

        //Element
        g2d.setColor(color);
        g2d.fillPolygon(createPolygon(30, 30, 20, 20, 6));

        g2d.setColor(Color.GRAY);
        g2d.fillPolygon(createPolygon(30, 30, 15, 15, 6));

        //Name
        new CustomRectangle(bufferedImage, 140, 40, 55, 10, 5, defaultOrientation) {
            @Override
            public void inBorder(final int dx, final int dy) {
                setColor(new Color(red, green, blue, 255 - Math.min(dx, dy)));
            }

            @Override
            public void outBorder(final int dx, final int dy) {
                setColor(new Color(red, green, blue, 128 - Math.min(dx, dy)));
            }
        }.draw();

        //Display name
        double nameStringX = 55 + (140 / 2);
        double nameStringY = 10 + (40 / 2);
        drawString(g2d, card.getName(), nameStringX, nameStringY, 40, Font.PLAIN, false, 0, 0);

        //HP
        g2d.setColor(color);
        g2d.fillPolygon(createPolygon(220, 30, 20, 20, 6));

        g2d.setColor(Color.GRAY);
        g2d.fillPolygon(createPolygon(220, 30, 15, 15, 6)); 

        //Display hp
        double hpStringX = 220;
        double hpStringY = 30;
        drawString(g2d, card.getHitpoints() + "", hpStringX, hpStringY, 30, Font.PLAIN, false, 0, 0);

        //Image
        new CustomRectangle(bufferedImage, 220, 185, 15, 55, 5, defaultOrientation) {
            @Override
            public void inBorder(final int dx, final int dy) {
                setColor(new Color(red, green, blue, 255 - Math.min(dx, dy)));
            }

            @Override
            public void outBorder(final int dx, final int dy) {
                setColor(Color.GRAY);
            }
        }.draw();     

        //Description
        new CustomRectangle(bufferedImage, 220, 90, 15, 245, 5, defaultOrientation) {
            @Override
            public void inBorder(final int dx, final int dy) {
                setColor(new Color(red, green, blue, 255 - Math.min(dx, dy)));
            }

            @Override
            public void outBorder(final int dx, final int dy) {
                setColor(new Color(red, green, blue, 128 - Math.min(dx, dy)));
            }
        }.draw();  

        //Display description
        double descriptionStringX = 15 + 5 + 5;
        double descriptionStringY = 245 + 5;
        drawString(g2d, card.getDescription(), descriptionStringX, descriptionStringY, 18, Font.PLAIN, true, 220 - (2 * (5 + 5)), 30);

        //Atk
        g2d.setColor(color);
        g2d.fillPolygon(createPolygon(30, 320, 20, 20, 6));

        g2d.setColor(Color.GRAY);
        g2d.fillPolygon(createPolygon(30, 320, 15, 15, 6));

        //Display atk
        double atkStringX = 30;
        double atkStringY = 320;
        drawString(g2d, card.getAttack() + "", atkStringX, atkStringY, 30, Font.PLAIN, false, 0, 0);

        //Def
        g2d.setColor(color);
        g2d.fillPolygon(createPolygon(220, 320, 20, 20, 6));

        g2d.setColor(Color.GRAY);
        g2d.fillPolygon(createPolygon(220, 320, 15, 15, 6));

        //Display def
        double defStringX = 220;
        double defStringY = 320;
        drawString(g2d, card.getDefence() + "", defStringX, defStringY, 30, Font.PLAIN, false, 0, 0);

        g2d.dispose();
    }

    private void createBackImage() {
        backBufferedImage = new BufferedImage(CARD_DIMENSION.width, CARD_DIMENSION.height, BufferedImage.TYPE_INT_ARGB);
        final Graphics2D g2d = (Graphics2D)backBufferedImage.createGraphics();

        //Graphics quality
        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        g2d.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY);
        g2d.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY);
        g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
        g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR); 

        if (!defaultOrientation) {
            g2d.rotate(Math.toRadians(180), backBufferedImage.getWidth() / 2, backBufferedImage.getHeight() / 2);
        }

        final int red = 150;
        final int green = 75;
        final int blue = 0;

        new CustomRectangle(backBufferedImage, CARD_DIMENSION.width, CARD_DIMENSION.height, 0, 0, 5, defaultOrientation) {
            @Override
            public void inBorder(final int dx, final int dy) {
                setColor(new Color(red, green, blue, 255 - Math.min(dx, dy)));
            }

            @Override
            public void outBorder(final int dx, final int dy) {
                setColor(new Color(red, green, blue, 192 - Math.min(dx, dy)));
            }
        }.draw();

        g2d.dispose();
    }

    private Polygon createPolygon(final int xOffset, final int yOffset, final int xDiameter, final int yDiameter, final int sides) {
        Polygon polygon = new Polygon();
        for (int i = 0; i < sides; i++) {
            int px = (int)Math.round(xOffset + (xDiameter * Math.cos(i * 2 * Math.PI / sides)));
            int py = (int)Math.round(yOffset + (yDiameter * Math.sin(i * 2 * Math.PI / sides)));
            polygon.addPoint(px, py);
        }
        return polygon;
    }

    private void drawString(final Graphics2D g2d, final String string, final double xOffset, final double yOffset, final double size, final int style, final boolean description, double descriptionWidth, final double usedWidth) {
        double stringX = xOffset;
        double stringY = yOffset; 
        g2d.setFont(new Font("SansSerif"/*g2d.getFont().getFamily()*/, style, (int)Math.round(size * 0.9 * 0.5)));
        FontMetrics fontMetrics = g2d.getFontMetrics();
        LineMetrics lineMetrics = g2d.getFont().getLineMetrics(string, g2d.getFontRenderContext());
        if (!description) {
            stringX -= (fontMetrics.stringWidth(string) / 2);
            stringY += ((lineMetrics.getAscent() + lineMetrics.getDescent()) / 2) - lineMetrics.getDescent();
            g2d.setColor(Color.BLACK);
            g2d.drawString(string, (int)Math.round(stringX), (int)Math.round(stringY));
        }
        else {
            stringY += lineMetrics.getAscent();
            double wordXOffset = 0;;
            double wordYOffset = 0;
            int yCount = 0;
            for (String word : string.split(" ")) {
                int width = fontMetrics.stringWidth(word + " ");
                if (wordXOffset + width > descriptionWidth) {
                    wordXOffset = 0;
                    wordYOffset = wordYOffset + fontMetrics.getHeight();
                    yCount++;
                    if (yCount == 3) {
                        descriptionWidth -= usedWidth;
                    }
                    if (yCount >= 3) {
                        wordXOffset += usedWidth;
                    }
                }
                g2d.drawString(word, (int)Math.round(stringX + wordXOffset), (int)Math.round(stringY + wordYOffset));
                wordXOffset += width;
            }
        }
    }
}

I hope question is not too long, but what are the ways in which I could improve the quality of the text drawn on the screen?

I may change the middle card's size from 125% to 200% at some point and display it exactly centered on the screen, so it is really a must that the quality of the text is as high as possible.

Was it helpful?

Solution 2

One problem will be how you scale the card. It appears you create a BufferedImage of the card at 100% and scale that up to 125%. At which time, the text is no longer text but part of the image. You are much safer starting at the maximum resolution and working down to the thumbnails. The appearance would be best if the fonts were allowed to render at each resolution.

OTHER TIPS

Graphics2D allows to set rendering hints. Try to experiment, there are various possible options and optimal values may depend on the selected font and other details.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top