【问题标题】:Java Graphics2D scale font to fill in backgroundJava Graphics2D 缩放字体填充背景
【发布时间】:2016-11-02 19:10:41
【问题描述】:

我在缩放字体以适应背景宽度时遇到问题。我有一个 1000 高度和 350 宽度的背景,当它比背景大时,我正在尝试缩放字体。 我用不同的字体做了几次测试,结果是一样的,有些字母遗漏或文本末尾有空格。

这是代码:

 import java.awt.Color;
 import java.awt.Font;
 import java.awt.FontMetrics;
 import java.awt.Graphics2D;
 import java.awt.RenderingHints;
 import java.awt.geom.AffineTransform;
 import java.awt.image.BufferedImage;
 import java.io.File;
 import java.io.IOException;
 import javax.imageio.ImageIO;

public class PruebaStackoverflow {

 public static void main(String[] args) {

    String titleText = null;
    Graphics2D g2D = null;
    Font testFont = null;

    File imageGrayBackgroundFile = new File(
            "resources/pruebaAltaResolucionGris.png");
    File destinationImageGray = new File("resources/outputTextGray.png");

    BufferedImage background = readImage(imageGrayBackgroundFile);

    titleText = "Lorem ipsum dolor sit amet asdkf sdm";
    testFont = new Font("Lucida Console", Font.PLAIN, 50);

    g2D = background.createGraphics();
    g2D.setColor(Color.BLACK);
    g2D.setFont(testFont);

    g2D.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,
            RenderingHints.VALUE_TEXT_ANTIALIAS_ON);

    g2D = scaleFontFromFontMetrics(g2D, background, titleText);

    g2D.drawString(titleText, 0, 150);
    g2D.dispose();

    writeImage(destinationImageGray, background);

}

private static Graphics2D scaleFontFromFontMetrics(Graphics2D g2D,
        BufferedImage backgroundImage, String text) {
    double xScale;
    double yScale;
    double scale;
    Integer backgroundWidth = null;
    Integer backgroundHeight = null;
    Integer textWidth = null;
    Integer textHeigth = null;

    backgroundWidth = backgroundImage.getWidth();
    backgroundHeight = backgroundImage.getHeight();

    Font f = g2D.getFont();
    FontMetrics fm = g2D.getFontMetrics(f);

    textWidth = fm.stringWidth(text);
    textHeigth = fm.getHeight();

    xScale = backgroundWidth / (double) textWidth;
    yScale = backgroundHeight / (double) textHeigth;

    if (xScale > yScale) {
        scale = yScale;
    } else {
        scale = xScale;
    }

    g2D.setFont(f.deriveFont(AffineTransform.getScaleInstance(scale, scale)));

    return g2D;
}

private static BufferedImage readImage(File sourceImage) {
    BufferedImage bufferedImage = null;
    try {
        bufferedImage = ImageIO.read(sourceImage);
    } catch (IOException e1) {
        e1.printStackTrace();
    }
    return bufferedImage;
}

private static void writeImage(File destinationImage,
        BufferedImage bufferedImage) {
    try {
        ImageIO.write(bufferedImage, "png", destinationImage);
    } catch (IOException e) {
        e.printStackTrace();
    }

    System.out.println("Image Saved");
}

}

这是缩放文本“Lorem ipsum dolor sit amet asdkf sdm” 这是用仿射变换缩放的文本。

output image with font scaled and 'm' letter missed

希望你能帮帮我,谢谢

【问题讨论】:

  • 之所以如此,是因为它选择了最接近宽度的字体 pt 大小。在这种情况下,14pt 而不是 13pt,但 13pt 不会“填充”整个宽度。您可能需要尝试使用文本创建图像,然后缩放图像。

标签: java graphics scale graphics2d drawstring


【解决方案1】:

您可以测量字符串的长度并验证它是否适合您的内容。

int lengthInPixel = graphics.getFontMetrics().stringWidth("Lorem ipsum dolor sit amet asdkf sdm")

【讨论】:

    【解决方案2】:

    根据我之前的评论,这是一个使用最接近图像宽度的字体大小的解决方案。文本被绘制到单独的图像,调整大小,然后绘制到最终图像。这一切都在createTextImage() 方法中完成。请注意,我创建了背景图像而不是使用文件。

    结果可能不如预期的那样清晰,但您可以尝试不同的算法来调整大小。希望它能给你一个起点。

    import java.awt.Color;
    import java.awt.Font;
    import java.awt.Graphics2D;
    import java.awt.Image;
    import java.awt.geom.AffineTransform;
    import java.awt.image.BufferedImage;
    import java.io.File;
    import java.io.IOException;
    import javax.imageio.ImageIO;
    
    public class Main {
    
        public static void main(String[] args) {
            String titleText = "Lorem ipsum dolor sit amet asdkf sdm";
            Font initialFont = new Font("Lucida Console", Font.PLAIN, 50);
            BufferedImage textImg = createTextImage(titleText, 350, 1000, 150,
                    initialFont, Color.BLACK, Color.GRAY);
            writeImage(new File("outputTextGray.png"), textImg);
        }
    
        private static BufferedImage createTextImage(String text, int targetWidth,
                int targetHeight, int textYOffset, Font font, Color textColor, Color bgColor) {
    
            // The final image
            BufferedImage finalImg = createBackgroundImg(targetWidth, targetHeight, bgColor);
            Graphics2D finalImgG = finalImg.createGraphics();        
            Font closestFont = scaleFont(finalImg, font, text);
            finalImgG.setFont(closestFont);
    
            // Create new image to fit text
            int textWidth = finalImgG.getFontMetrics().stringWidth(text);
            int textHeight = finalImgG.getFontMetrics().getHeight();
            BufferedImage textImg = createBackgroundImg(textWidth, textHeight * 2, bgColor);
    
            // Draw text
            Graphics2D textImgG = textImg.createGraphics();
            textImgG.setFont(closestFont);
            textImgG.setColor(textColor);
            textImgG.drawString(text, 0, textHeight);
    
            // Scale text image
            double scale = getScale(textImg.getWidth(), textImg.getHeight(),
                    targetWidth, targetHeight);
            Image resized = textImg.getScaledInstance((int) (textImg.getWidth() * scale),
                    (int) (textImg.getHeight() * scale), Image.SCALE_SMOOTH);
    
            // Draw text image onto final image
            finalImgG.drawImage(resized, 0, textYOffset, null);
            return finalImg;
        }
    
        private static Font scaleFont(BufferedImage img, Font font, String text) {
            Graphics2D g2D = img.createGraphics();
            g2D.setFont(font);
            double scale = getScale(g2D.getFontMetrics().stringWidth(text), 
                    g2D.getFontMetrics().getHeight(), img.getWidth(), 
                    img.getHeight());
            return g2D.getFont().deriveFont(AffineTransform.getScaleInstance(scale, scale));
        }
    
        private static double getScale(int width, int height, int targetWidth, int targetHeight) {
            assert width > 0 && height > 0 : "width and height must be > 0";
            double scaleX = (double) targetWidth / width;
            double scaleY = (double) targetHeight / height;
            return scaleX > scaleY ? scaleY : scaleX;
        }
    
        private static BufferedImage createBackgroundImg(int width, int height, Color color) {
            BufferedImage bufferedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
            for (int x = 0; x < bufferedImage.getWidth(); x++) {
                for (int y = 0; y < bufferedImage.getHeight(); y++) {
                    bufferedImage.setRGB(x, y, color.getRGB());
                }
            }
            return bufferedImage;
        }
    
        private static void writeImage(File destinationImage, 
                BufferedImage bufferedImage) {
            try {
                ImageIO.write(bufferedImage, "png", destinationImage);
            } catch (IOException e) {
                e.printStackTrace();
            }
            System.out.println("Image Saved");
        }
    }
    

    【讨论】:

    • 谢谢,它对我来说很好,但我还有另一个问题。如果我必须将某些图像作为背景做同样的事情,我将不得不使用透明背景进行第一次缩放操作,不是吗?还是有其他方法?
    • @SheenStvz 检查这个问题stackoverflow.com/questions/17271812/…
    【解决方案3】:

    为了使游戏屏幕适应特定分辨率,我所做的事情是在测试其最大界限后预先计算比率。像这样(我将实际代码更改为您的情况):

    double calculateScaling( BufferedImage image, String text, Font font ){
    
        //These are final to avoid accidental mending, since they are 
        //the base for our calculations.
        //Belive me, it took me a while to debug the 
        //scale calculation when I made this for my games :P
    
        /**
         * imageWidth and imageHeight are the bounds
         */
        final int imageWidth = image.getWidth();
        final int imageHeight = image.getHeight();
    
        Graphics2D g2 = image.createGraphics();
        FontMetrics fm = g2.getFontMetrics( font );
    
    
        /**
         * requestedStringWidthSize e requestedStringHeightSize are the measures needed 
         * to draw the text WITHOUT resizing.
         */
        final int requestedStringWidthSize = fm.stringWidth( text );
        final int requestedStringHeightSize = fm.getHeight();
    
    
        double stringHeightSizeToUse = imageHeight;
        double stringWidthSizeToUse;
    
        double scale = stringHeightSizeToUse/requestedStringHeightSize;
        stringWidthSizeToUse = scale*requestedStringWidthSize;
    
        /**
         * Checking if fill in height makes the text go out of bound in width,
         * if it does, it rescalates it to size it to maximum width.
         */
        if( imageWidth < ((int)(Math.rint(stringWidthSizeToUse))) ) {
            stringWidthSizeToUse = imageWidth;
    
            scale = stringWidthSizeToUse/requestedStringWidthSize;
            //stringHeightSizeToUse = scale*requestedStringHeightSize;
        }
    
        g2.dispose(); //we created this to use fontmetrics, now we don't need it.
    
        return scale;
    }
    

    在我的游戏中,我会将比例存储为 float 而不是 double 以避免在运行时进行大量计算,但对你来说这只是一个简单的缩放,对吧?

    您现在要做的就是研究代码并将其实施到您的代码中。

    我希望我有所帮助。

    祝你有美好的一天。 :)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-05-01
      • 1970-01-01
      • 2013-02-10
      • 2012-08-20
      • 2018-11-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多