gdx-studio

#libgdx#java#desktop

git clone https://git.pyrossh.dev/gdx-studio

An IDE for creating Games using libgdx and Java supported on all platforms Android, iOS, Desktop


src_libs/com/badlogic/gdx/tools/hiero/unicodefont/GlyphPage.java
a62d533 1
/*******************************************************************************
a62d533 2
 * Copyright 2011 See AUTHORS file.
a62d533 3
 * 
a62d533 4
 * Licensed under the Apache License, Version 2.0 (the "License");
a62d533 5
 * you may not use this file except in compliance with the License.
a62d533 6
 * You may obtain a copy of the License at
a62d533 7
 * 
a62d533 8
 *   http://www.apache.org/licenses/LICENSE-2.0
a62d533 9
 * 
a62d533 10
 * Unless required by applicable law or agreed to in writing, software
a62d533 11
 * distributed under the License is distributed on an "AS IS" BASIS,
a62d533 12
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
a62d533 13
 * See the License for the specific language governing permissions and
a62d533 14
 * limitations under the License.
a62d533 15
 ******************************************************************************/
a62d533 16
a62d533 17
package com.badlogic.gdx.tools.hiero.unicodefont;
a62d533 18
a62d533 19
import java.awt.AlphaComposite;
a62d533 20
import java.awt.Graphics2D;
a62d533 21
import java.awt.RenderingHints;
a62d533 22
import java.awt.font.FontRenderContext;
a62d533 23
import java.awt.image.BufferedImage;
a62d533 24
import java.awt.image.WritableRaster;
a62d533 25
import java.nio.ByteBuffer;
a62d533 26
import java.nio.ByteOrder;
a62d533 27
import java.nio.IntBuffer;
a62d533 28
import java.util.ArrayList;
a62d533 29
import java.util.Iterator;
a62d533 30
import java.util.List;
a62d533 31
import java.util.ListIterator;
a62d533 32
a62d533 33
import org.lwjgl.opengl.GL11;
a62d533 34
import org.lwjgl.opengl.GL12;
a62d533 35
a62d533 36
import com.badlogic.gdx.graphics.Pixmap.Format;
a62d533 37
import com.badlogic.gdx.graphics.Texture;
a62d533 38
import com.badlogic.gdx.tools.hiero.unicodefont.effects.ColorEffect;
a62d533 39
import com.badlogic.gdx.tools.hiero.unicodefont.effects.Effect;
a62d533 40
a62d533 41
/** Stores a number of glyphs on a single texture.
a62d533 42
 * @author Nathan Sweet */
a62d533 43
public class GlyphPage {
a62d533 44
	private final UnicodeFont unicodeFont;
a62d533 45
	private final int pageWidth, pageHeight;
a62d533 46
	private final Texture texture;
a62d533 47
	private int pageX, pageY, rowHeight;
a62d533 48
	private boolean orderAscending;
a62d533 49
	private final List pageGlyphs = new ArrayList(32);
a62d533 50
a62d533 51
	/** @param pageWidth The width of the backing texture.
a62d533 52
	 * @param pageHeight The height of the backing texture. */
a62d533 53
	GlyphPage (UnicodeFont unicodeFont, int pageWidth, int pageHeight) {
a62d533 54
		this.unicodeFont = unicodeFont;
a62d533 55
		this.pageWidth = pageWidth;
a62d533 56
		this.pageHeight = pageHeight;
a62d533 57
a62d533 58
		texture = new Texture(pageWidth, pageHeight, Format.RGBA8888);
a62d533 59
	}
a62d533 60
a62d533 61
	/** Loads glyphs to the backing texture and sets the image on each loaded glyph. Loaded glyphs are removed from the list.
a62d533 62
	 * 
a62d533 63
	 * If this page already has glyphs and maxGlyphsToLoad is -1, then this method will return 0 if all the new glyphs don't fit.
a62d533 64
	 * This reduces texture binds when drawing since glyphs loaded at once are typically displayed together.
a62d533 65
	 * @param glyphs The glyphs to load.
a62d533 66
	 * @param maxGlyphsToLoad This is the maximum number of glyphs to load from the list. Set to -1 to attempt to load all the
a62d533 67
	 *           glyphs.
a62d533 68
	 * @return The number of glyphs that were actually loaded. */
a62d533 69
	int loadGlyphs (List glyphs, int maxGlyphsToLoad) {
a62d533 70
		if (rowHeight != 0 && maxGlyphsToLoad == -1) {
a62d533 71
			// If this page has glyphs and we are not loading incrementally, return zero if any of the glyphs don't fit.
a62d533 72
			int testX = pageX;
a62d533 73
			int testY = pageY;
a62d533 74
			int testRowHeight = rowHeight;
a62d533 75
			for (Iterator iter = getIterator(glyphs); iter.hasNext();) {
a62d533 76
				Glyph glyph = (Glyph)iter.next();
a62d533 77
				int width = glyph.getWidth();
a62d533 78
				int height = glyph.getHeight();
a62d533 79
				if (testX + width >= pageWidth) {
a62d533 80
					testX = 0;
a62d533 81
					testY += testRowHeight;
a62d533 82
					testRowHeight = height;
a62d533 83
				} else if (height > testRowHeight) {
a62d533 84
					testRowHeight = height;
a62d533 85
				}
a62d533 86
				if (testY + testRowHeight >= pageWidth) return 0;
a62d533 87
				testX += width;
a62d533 88
			}
a62d533 89
		}
a62d533 90
a62d533 91
		GL11.glColor4f(1, 1, 1, 1);
a62d533 92
		texture.bind();
a62d533 93
a62d533 94
		int i = 0;
a62d533 95
		for (Iterator iter = getIterator(glyphs); iter.hasNext();) {
a62d533 96
			Glyph glyph = (Glyph)iter.next();
a62d533 97
			int width = Math.min(MAX_GLYPH_SIZE, glyph.getWidth());
a62d533 98
			int height = Math.min(MAX_GLYPH_SIZE, glyph.getHeight());
a62d533 99
a62d533 100
			if (rowHeight == 0) {
a62d533 101
				// The first glyph always fits.
a62d533 102
				rowHeight = height;
a62d533 103
			} else {
a62d533 104
				// Wrap to the next line if needed, or break if no more fit.
a62d533 105
				if (pageX + width >= pageWidth) {
a62d533 106
					if (pageY + rowHeight + height >= pageHeight) break;
a62d533 107
					pageX = 0;
a62d533 108
					pageY += rowHeight;
a62d533 109
					rowHeight = height;
a62d533 110
				} else if (height > rowHeight) {
a62d533 111
					if (pageY + height >= pageHeight) break;
a62d533 112
					rowHeight = height;
a62d533 113
				}
a62d533 114
			}
a62d533 115
a62d533 116
			renderGlyph(glyph, width, height);
a62d533 117
			pageGlyphs.add(glyph);
a62d533 118
a62d533 119
			pageX += width;
a62d533 120
a62d533 121
			iter.remove();
a62d533 122
			i++;
a62d533 123
			if (i == maxGlyphsToLoad) {
a62d533 124
				// If loading incrementally, flip orderAscending so it won't change, since we'll probably load the rest next time.
a62d533 125
				orderAscending = !orderAscending;
a62d533 126
				break;
a62d533 127
			}
a62d533 128
		}
a62d533 129
a62d533 130
		// Every other batch of glyphs added to a page are sorted the opposite way to attempt to keep same size glyps together.
a62d533 131
		orderAscending = !orderAscending;
a62d533 132
a62d533 133
		return i;
a62d533 134
	}
a62d533 135
a62d533 136
	/** Loads a single glyph to the backing texture, if it fits. */
a62d533 137
	private void renderGlyph (Glyph glyph, int width, int height) {
a62d533 138
		// Draw the glyph to the scratch image using Java2D.
a62d533 139
		scratchGraphics.setComposite(AlphaComposite.Clear);
a62d533 140
		scratchGraphics.fillRect(0, 0, MAX_GLYPH_SIZE, MAX_GLYPH_SIZE);
a62d533 141
		scratchGraphics.setComposite(AlphaComposite.SrcOver);
a62d533 142
		if (unicodeFont.getNativeRendering()) {
a62d533 143
			for (Iterator iter = unicodeFont.getEffects().iterator(); iter.hasNext();) {
a62d533 144
				Effect effect = (Effect)iter.next();
a62d533 145
				if (effect instanceof ColorEffect) scratchGraphics.setColor(((ColorEffect)effect).getColor());
a62d533 146
			}
a62d533 147
			scratchGraphics.setColor(java.awt.Color.white);
a62d533 148
			scratchGraphics.setFont(unicodeFont.getFont());
a62d533 149
			scratchGraphics.drawString("" + (char)glyph.getCodePoint(), 0, unicodeFont.getAscent());
a62d533 150
		} else {
a62d533 151
			scratchGraphics.setColor(java.awt.Color.white);
a62d533 152
			for (Iterator iter = unicodeFont.getEffects().iterator(); iter.hasNext();)
a62d533 153
				((Effect)iter.next()).draw(scratchImage, scratchGraphics, unicodeFont, glyph);
a62d533 154
			glyph.setShape(null); // The shape will never be needed again.
a62d533 155
		}
a62d533 156
a62d533 157
		width = Math.min(width, texture.getWidth());
a62d533 158
		height = Math.min(height, texture.getHeight());
a62d533 159
a62d533 160
		WritableRaster raster = scratchImage.getRaster();
a62d533 161
		int[] row = new int[width];
a62d533 162
		for (int y = 0; y < height; y++) {
a62d533 163
			raster.getDataElements(0, y, width, 1, row);
a62d533 164
			scratchIntBuffer.put(row);
a62d533 165
		}
a62d533 166
		GL11.glTexSubImage2D(GL11.GL_TEXTURE_2D, 0, pageX, pageY, width, height, GL12.GL_BGRA, GL11.GL_UNSIGNED_BYTE,
a62d533 167
			scratchByteBuffer);
a62d533 168
		scratchIntBuffer.clear();
a62d533 169
a62d533 170
		float u = pageX / (float)texture.getWidth();
a62d533 171
		float v = pageY / (float)texture.getHeight();
a62d533 172
		float u2 = (pageX + width) / (float)texture.getWidth();
a62d533 173
		float v2 = (pageY + height) / (float)texture.getHeight();
a62d533 174
		glyph.setTexture(texture, u, v, u2, v2);
a62d533 175
	}
a62d533 176
a62d533 177
	/** Returns an iterator for the specified glyphs, sorted either ascending or descending. */
a62d533 178
	private Iterator getIterator (List glyphs) {
a62d533 179
		if (orderAscending) return glyphs.iterator();
a62d533 180
		final ListIterator iter = glyphs.listIterator(glyphs.size());
a62d533 181
		return new Iterator() {
a62d533 182
			public boolean hasNext () {
a62d533 183
				return iter.hasPrevious();
a62d533 184
			}
a62d533 185
a62d533 186
			public Object next () {
a62d533 187
				return iter.previous();
a62d533 188
			}
a62d533 189
a62d533 190
			public void remove () {
a62d533 191
				iter.remove();
a62d533 192
			}
a62d533 193
		};
a62d533 194
	}
a62d533 195
a62d533 196
	/** Returns the glyphs stored on this page. */
a62d533 197
	public List getGlyphs () {
a62d533 198
		return pageGlyphs;
a62d533 199
	}
a62d533 200
a62d533 201
	/** Returns the backing texture for this page. */
a62d533 202
	public Texture getTexture () {
a62d533 203
		return texture;
a62d533 204
	}
a62d533 205
a62d533 206
	static public final int MAX_GLYPH_SIZE = 256;
a62d533 207
a62d533 208
	static private ByteBuffer scratchByteBuffer = ByteBuffer.allocateDirect(MAX_GLYPH_SIZE * MAX_GLYPH_SIZE * 4);
a62d533 209
	static {
a62d533 210
		scratchByteBuffer.order(ByteOrder.LITTLE_ENDIAN);
a62d533 211
	}
a62d533 212
	static private IntBuffer scratchIntBuffer = scratchByteBuffer.asIntBuffer();
a62d533 213
a62d533 214
	static private BufferedImage scratchImage = new BufferedImage(MAX_GLYPH_SIZE, MAX_GLYPH_SIZE, BufferedImage.TYPE_INT_ARGB);
a62d533 215
	static Graphics2D scratchGraphics = (Graphics2D)scratchImage.getGraphics();
a62d533 216
	static {
a62d533 217
		scratchGraphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
a62d533 218
		scratchGraphics.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
a62d533 219
	}
a62d533 220
	static public FontRenderContext renderContext = scratchGraphics.getFontRenderContext();
a62d533 221
}