+++ /dev/null
-/*
- * Jexer - Java Text User Interface
- *
- * The MIT License (MIT)
- *
- * Copyright (C) 2019 Kevin Lamonte
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- *
- * @author Kevin Lamonte [kevin.lamonte@gmail.com]
- * @version 1
- */
-package jexer.backend;
-
-import java.awt.Font;
-import java.awt.FontMetrics;
-import java.awt.Graphics2D;
-import java.awt.geom.Rectangle2D;
-import java.awt.image.BufferedImage;
-import java.io.InputStream;
-import java.io.IOException;
-import java.util.HashMap;
-
-import jexer.bits.Cell;
-
-/**
- * GlyphMaker creates glyphs as bitmaps from a font.
- */
-public class GlyphMaker {
-
- // ------------------------------------------------------------------------
- // Constants --------------------------------------------------------------
- // ------------------------------------------------------------------------
-
- /**
- * The mono font resource filename (terminus).
- */
- public static final String MONO = "terminus-ttf-4.39/TerminusTTF-Bold-4.39.ttf";
-
- /**
- * The CJK font resource filename.
- */
- public static final String CJK = "NotoSansMonoCJKhk-Regular.otf";
-
- // ------------------------------------------------------------------------
- // Variables --------------------------------------------------------------
- // ------------------------------------------------------------------------
-
- /**
- * If true, enable debug messages.
- */
- private static boolean DEBUG = false;
-
- /**
- * The instance that has the mono (default) font.
- */
- private static GlyphMaker INSTANCE_MONO;
-
- /**
- * The instance that has the CJK font.
- */
- private static GlyphMaker INSTANCE_CJK;
-
- /**
- * If true, we were successful at getting the font dimensions.
- */
- private boolean gotFontDimensions = false;
-
- /**
- * The currently selected font.
- */
- private Font font = null;
-
- /**
- * The currently selected font size in points.
- */
- private int fontSize = 16;
-
- /**
- * Width of a character cell in pixels.
- */
- private int textWidth = 1;
-
- /**
- * Height of a character cell in pixels.
- */
- private int textHeight = 1;
-
- /**
- * Width of a character cell in pixels, as reported by font.
- */
- private int fontTextWidth = 1;
-
- /**
- * Height of a character cell in pixels, as reported by font.
- */
- private int fontTextHeight = 1;
-
- /**
- * Descent of a character cell in pixels.
- */
- private int maxDescent = 0;
-
- /**
- * System-dependent Y adjustment for text in the character cell.
- */
- private int textAdjustY = 0;
-
- /**
- * System-dependent X adjustment for text in the character cell.
- */
- private int textAdjustX = 0;
-
- /**
- * System-dependent height adjustment for text in the character cell.
- */
- private int textAdjustHeight = 0;
-
- /**
- * System-dependent width adjustment for text in the character cell.
- */
- private int textAdjustWidth = 0;
-
- /**
- * A cache of previously-rendered glyphs for blinking text, when it is
- * not visible.
- */
- private HashMap<Cell, BufferedImage> glyphCacheBlink;
-
- /**
- * A cache of previously-rendered glyphs for non-blinking, or
- * blinking-and-visible, text.
- */
- private HashMap<Cell, BufferedImage> glyphCache;
-
- // ------------------------------------------------------------------------
- // Constructors -----------------------------------------------------------
- // ------------------------------------------------------------------------
-
- /**
- * Private constructor used by the static instance methods.
- *
- * @param font the font to use
- */
- private GlyphMaker(final Font font) {
- this.font = font;
- fontSize = font.getSize();
- }
-
- /**
- * Public constructor.
- *
- * @param fontName the name of the font to use
- * @param fontSize the size of font to use
- */
- public GlyphMaker(final String fontName, final int fontSize) {
- font = new Font(fontName, Font.PLAIN, fontSize);
- }
-
- // ------------------------------------------------------------------------
- // GlyphMaker -------------------------------------------------------------
- // ------------------------------------------------------------------------
-
- /**
- * Obtain the GlyphMaker instance that uses the default monospace font.
- *
- * @return the instance
- */
- public static GlyphMaker getDefault() {
-
- synchronized (GlyphMaker.class) {
- if (INSTANCE_MONO != null) {
- return INSTANCE_MONO;
- }
-
- int fallbackFontSize = 16;
- Font monoRoot = null;
- try {
- ClassLoader loader = Thread.currentThread().getContextClassLoader();
- InputStream in = loader.getResourceAsStream(MONO);
- monoRoot = Font.createFont(Font.TRUETYPE_FONT, in);
- } catch (java.awt.FontFormatException e) {
- e.printStackTrace();
- monoRoot = new Font(Font.MONOSPACED, Font.PLAIN,
- fallbackFontSize);
- } catch (java.io.IOException e) {
- e.printStackTrace();
- monoRoot = new Font(Font.MONOSPACED, Font.PLAIN,
- fallbackFontSize);
- }
- INSTANCE_MONO = new GlyphMaker(monoRoot);
- return INSTANCE_MONO;
- }
- }
-
- /**
- * Obtain the GlyphMaker instance that uses the CJK font.
- *
- * @return the instance
- */
- public static GlyphMaker getCJK() {
-
- synchronized (GlyphMaker.class) {
- if (INSTANCE_CJK != null) {
- return INSTANCE_CJK;
- }
-
- int fallbackFontSize = 16;
- Font cjkRoot = null;
- try {
- ClassLoader loader = Thread.currentThread().getContextClassLoader();
- InputStream in = loader.getResourceAsStream(CJK);
- cjkRoot = Font.createFont(Font.TRUETYPE_FONT, in);
- } catch (java.awt.FontFormatException e) {
- e.printStackTrace();
- cjkRoot = new Font(Font.MONOSPACED, Font.PLAIN,
- fallbackFontSize);
- } catch (java.io.IOException e) {
- e.printStackTrace();
- cjkRoot = new Font(Font.MONOSPACED, Font.PLAIN,
- fallbackFontSize);
- }
- INSTANCE_CJK = new GlyphMaker(cjkRoot);
- return INSTANCE_CJK;
- }
- }
-
- /**
- * Obtain the GlyphMaker instance that uses the correct font for this
- * character.
- *
- * @param ch the character
- * @return the instance
- */
- public static GlyphMaker getInstance(final int ch) {
- if (((ch >= 0x4e00) && (ch <= 0x9fff))
- || ((ch >= 0x3400) && (ch <= 0x4dbf))
- || ((ch >= 0x20000) && (ch <= 0x2ebef))
- ) {
- return getCJK();
- }
- return getDefault();
- }
-
- /**
- * Get a derived font at a specific size.
- *
- * @param fontSize the size to use
- * @return a new instance at that font size
- */
- public GlyphMaker size(final int fontSize) {
- GlyphMaker maker = new GlyphMaker(font.deriveFont(Font.PLAIN,
- fontSize));
- return maker;
- }
-
- /**
- * Get a glyph image, using the font's idea of cell width and height.
- *
- * @param cell the character to draw
- * @return the glyph as an image
- */
- public BufferedImage getImage(final Cell cell) {
- return getImage(cell, textWidth, textHeight, true);
- }
-
- /**
- * Get a glyph image.
- *
- * @param cell the character to draw
- * @param cellWidth the width of the text cell to draw into
- * @param cellHeight the height of the text cell to draw into
- * @return the glyph as an image
- */
- public BufferedImage getImage(final Cell cell, final int cellWidth,
- final int cellHeight) {
-
- return getImage(cell, cellWidth, cellHeight, true);
- }
-
- /**
- * Get a glyph image.
- *
- * @param cell the character to draw
- * @param cellWidth the width of the text cell to draw into
- * @param cellHeight the height of the text cell to draw into
- * @param blinkVisible if true, the cell is visible if it is blinking
- * @return the glyph as an image
- */
- public BufferedImage getImage(final Cell cell, final int cellWidth,
- final int cellHeight, final boolean blinkVisible) {
-
- if (gotFontDimensions == false) {
- // Lazy-load the text width/height and adjustments.
- getFontDimensions();
- }
-
- BufferedImage image = null;
- if (cell.isBlink() && !blinkVisible) {
- image = glyphCacheBlink.get(cell);
- } else {
- image = glyphCache.get(cell);
- }
- if (image != null) {
- return image;
- }
-
- // Generate glyph and draw it.
- image = new BufferedImage(cellWidth, cellHeight,
- BufferedImage.TYPE_INT_ARGB);
- Graphics2D gr2 = image.createGraphics();
- gr2.setFont(font);
-
- Cell cellColor = new Cell();
- cellColor.setTo(cell);
-
- // Check for reverse
- if (cell.isReverse()) {
- cellColor.setForeColor(cell.getBackColor());
- cellColor.setBackColor(cell.getForeColor());
- }
-
- // Draw the background rectangle, then the foreground character.
- gr2.setColor(SwingTerminal.attrToBackgroundColor(cellColor));
- gr2.fillRect(0, 0, cellWidth, cellHeight);
-
- // Handle blink and underline
- if (!cell.isBlink()
- || (cell.isBlink() && blinkVisible)
- ) {
- gr2.setColor(SwingTerminal.attrToForegroundColor(cellColor));
- char [] chars = new char[1];
- chars[0] = cell.getChar();
- gr2.drawChars(chars, 0, 1, textAdjustX,
- cellHeight - maxDescent + textAdjustY);
-
- if (cell.isUnderline()) {
- gr2.fillRect(0, cellHeight - 2, cellWidth, 2);
- }
- }
- gr2.dispose();
-
- // We need a new key that will not be mutated by invertCell().
- Cell key = new Cell();
- key.setTo(cell);
- if (cell.isBlink() && !blinkVisible) {
- glyphCacheBlink.put(key, image);
- } else {
- glyphCache.put(key, image);
- }
-
- return image;
- }
-
- /**
- * Figure out my font dimensions.
- */
- private void getFontDimensions() {
- glyphCacheBlink = new HashMap<Cell, BufferedImage>();
- glyphCache = new HashMap<Cell, BufferedImage>();
-
- BufferedImage image = new BufferedImage(fontSize * 2, fontSize * 2,
- BufferedImage.TYPE_INT_ARGB);
- Graphics2D gr = image.createGraphics();
- gr.setFont(font);
- FontMetrics fm = gr.getFontMetrics();
- maxDescent = fm.getMaxDescent();
- Rectangle2D bounds = fm.getMaxCharBounds(gr);
- int leading = fm.getLeading();
- fontTextWidth = (int)Math.round(bounds.getWidth());
- // fontTextHeight = (int)Math.round(bounds.getHeight()) - maxDescent;
-
- // This produces the same number, but works better for ugly
- // monospace.
- fontTextHeight = fm.getMaxAscent() + maxDescent - leading;
- gr.dispose();
-
- textHeight = fontTextHeight + textAdjustHeight;
- textWidth = fontTextWidth + textAdjustWidth;
-
- gotFontDimensions = true;
- }
-
-}