Added GNU Unifont typeface, optimized TextureDataEditor greatly
- Added GNU Unifont support - TextureDataEditor no longer uses AWT internally - Optimized Atlases slightly - SpriteTypeface adds spacing between lines - Removed TestTypeface
This commit is contained in:
parent
54002475d0
commit
e1ce8c2e09
@ -23,13 +23,14 @@ import ru.windcorp.progressia.client.graphics.backend.GraphicsBackend;
|
|||||||
import ru.windcorp.progressia.client.graphics.backend.RenderTaskQueue;
|
import ru.windcorp.progressia.client.graphics.backend.RenderTaskQueue;
|
||||||
import ru.windcorp.progressia.client.graphics.flat.FlatRenderProgram;
|
import ru.windcorp.progressia.client.graphics.flat.FlatRenderProgram;
|
||||||
import ru.windcorp.progressia.client.graphics.flat.LayerTestUI;
|
import ru.windcorp.progressia.client.graphics.flat.LayerTestUI;
|
||||||
import ru.windcorp.progressia.client.graphics.font.TestTypeface;
|
import ru.windcorp.progressia.client.graphics.font.GNUUnifontLoader;
|
||||||
import ru.windcorp.progressia.client.graphics.font.Typefaces;
|
import ru.windcorp.progressia.client.graphics.font.Typefaces;
|
||||||
import ru.windcorp.progressia.client.graphics.gui.LayerTestGUI;
|
import ru.windcorp.progressia.client.graphics.gui.LayerTestGUI;
|
||||||
import ru.windcorp.progressia.client.graphics.texture.Atlases;
|
import ru.windcorp.progressia.client.graphics.texture.Atlases;
|
||||||
import ru.windcorp.progressia.client.graphics.world.LayerWorld;
|
import ru.windcorp.progressia.client.graphics.world.LayerWorld;
|
||||||
import ru.windcorp.progressia.client.graphics.world.WorldRenderProgram;
|
import ru.windcorp.progressia.client.graphics.world.WorldRenderProgram;
|
||||||
import ru.windcorp.progressia.client.world.renders.BlockRenders;
|
import ru.windcorp.progressia.client.world.renders.BlockRenders;
|
||||||
|
import ru.windcorp.progressia.common.resource.ResourceManager;
|
||||||
|
|
||||||
public class ClientProxy implements Proxy {
|
public class ClientProxy implements Proxy {
|
||||||
|
|
||||||
@ -39,7 +40,7 @@ public class ClientProxy implements Proxy {
|
|||||||
try {
|
try {
|
||||||
RenderTaskQueue.waitAndInvoke(FlatRenderProgram::init);
|
RenderTaskQueue.waitAndInvoke(FlatRenderProgram::init);
|
||||||
RenderTaskQueue.waitAndInvoke(WorldRenderProgram::init);
|
RenderTaskQueue.waitAndInvoke(WorldRenderProgram::init);
|
||||||
RenderTaskQueue.waitAndInvoke(() -> Typefaces.setDefault(new TestTypeface()));
|
RenderTaskQueue.waitAndInvoke(() -> Typefaces.setDefault(GNUUnifontLoader.load(ResourceManager.getResource("assets/unifont-13.0.03.hex.gz"))));
|
||||||
} catch (InterruptedException e) {
|
} catch (InterruptedException e) {
|
||||||
// TODO Auto-generated catch block
|
// TODO Auto-generated catch block
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
|
@ -0,0 +1,43 @@
|
|||||||
|
package ru.windcorp.progressia.client.graphics.font;
|
||||||
|
|
||||||
|
import gnu.trove.map.TCharObjectMap;
|
||||||
|
import gnu.trove.set.TIntSet;
|
||||||
|
import gnu.trove.set.hash.TIntHashSet;
|
||||||
|
import ru.windcorp.progressia.client.graphics.flat.FlatRenderProgram;
|
||||||
|
import ru.windcorp.progressia.client.graphics.model.ShapeRenderProgram;
|
||||||
|
import ru.windcorp.progressia.client.graphics.texture.Texture;
|
||||||
|
|
||||||
|
public class GNUUnifont extends SpriteTypeface {
|
||||||
|
|
||||||
|
public static final int HEIGHT = 16;
|
||||||
|
public static final TIntSet WIDTHS = new TIntHashSet(new int[] {8, 16});
|
||||||
|
|
||||||
|
private final TCharObjectMap<Texture> textures;
|
||||||
|
|
||||||
|
public GNUUnifont(TCharObjectMap<Texture> textures) {
|
||||||
|
super("GNUUnifont", HEIGHT, 1);
|
||||||
|
this.textures = textures;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Texture getTexture(char c) {
|
||||||
|
if (!supports(c)) return textures.get('?');
|
||||||
|
return textures.get(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ShapeRenderProgram getProgram() {
|
||||||
|
return FlatRenderProgram.getDefault();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supports(char c) {
|
||||||
|
return textures.containsKey(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public float getItalicsSlant() {
|
||||||
|
return 3 * super.getItalicsSlant();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,197 @@
|
|||||||
|
package ru.windcorp.progressia.client.graphics.font;
|
||||||
|
|
||||||
|
import java.io.BufferedReader;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.io.UncheckedIOException;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.stream.Collector;
|
||||||
|
import java.util.stream.Collector.Characteristics;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
import java.util.zip.GZIPInputStream;
|
||||||
|
|
||||||
|
import gnu.trove.map.TCharObjectMap;
|
||||||
|
import gnu.trove.map.hash.TCharObjectHashMap;
|
||||||
|
import ru.windcorp.progressia.client.graphics.texture.Atlases;
|
||||||
|
import ru.windcorp.progressia.client.graphics.texture.Atlases.AtlasGroup;
|
||||||
|
import ru.windcorp.progressia.client.graphics.texture.SimpleTexture;
|
||||||
|
import ru.windcorp.progressia.client.graphics.texture.Sprite;
|
||||||
|
import ru.windcorp.progressia.client.graphics.texture.Texture;
|
||||||
|
import ru.windcorp.progressia.client.graphics.texture.TextureDataEditor;
|
||||||
|
import ru.windcorp.progressia.client.graphics.texture.TextureSettings;
|
||||||
|
import ru.windcorp.progressia.common.resource.Resource;
|
||||||
|
|
||||||
|
public class GNUUnifontLoader {
|
||||||
|
|
||||||
|
private static final AtlasGroup ATLAS_GROUP_GNU_UNIFONT =
|
||||||
|
new AtlasGroup("GNUUnifont", 1 << 12);
|
||||||
|
|
||||||
|
private static final TextureSettings TEXTURE_SETTINGS =
|
||||||
|
new TextureSettings(false);
|
||||||
|
|
||||||
|
private static final int BITS_PER_HEX_DIGIT = 4;
|
||||||
|
private static final int PREFIX_LENGTH = "0000:".length();
|
||||||
|
|
||||||
|
private static class ParsedGlyph {
|
||||||
|
final char c;
|
||||||
|
final TextureDataEditor data;
|
||||||
|
|
||||||
|
ParsedGlyph(char c, TextureDataEditor data) {
|
||||||
|
this.c = c;
|
||||||
|
this.data = data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class AtlasGlyph {
|
||||||
|
final char c;
|
||||||
|
final Texture texture;
|
||||||
|
|
||||||
|
AtlasGlyph(char c, Texture texture) {
|
||||||
|
this.c = c;
|
||||||
|
this.texture = texture;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static GNUUnifont load(Resource resource) {
|
||||||
|
try (BufferedReader reader = createReader(resource)) {
|
||||||
|
return createStream(reader)
|
||||||
|
.map(GNUUnifontLoader::parse)
|
||||||
|
.map(GNUUnifontLoader::addToAtlas)
|
||||||
|
.collect(Collectors.collectingAndThen(
|
||||||
|
createMapper(),
|
||||||
|
GNUUnifont::new
|
||||||
|
));
|
||||||
|
} catch (IOException | UncheckedIOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static BufferedReader createReader(Resource resource)
|
||||||
|
throws IOException
|
||||||
|
{
|
||||||
|
return new BufferedReader(
|
||||||
|
new InputStreamReader(
|
||||||
|
new GZIPInputStream(
|
||||||
|
resource.getInputStream()
|
||||||
|
),
|
||||||
|
StandardCharsets.UTF_8
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Stream<String> createStream(BufferedReader reader) {
|
||||||
|
return reader.lines();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ParsedGlyph parse(String declar) {
|
||||||
|
int width = getWidth(declar);
|
||||||
|
checkDeclaration(declar, width);
|
||||||
|
|
||||||
|
char c = getChar(declar);
|
||||||
|
|
||||||
|
TextureDataEditor editor = new TextureDataEditor(
|
||||||
|
width, GNUUnifont.HEIGHT,
|
||||||
|
width, GNUUnifont.HEIGHT,
|
||||||
|
TEXTURE_SETTINGS
|
||||||
|
);
|
||||||
|
|
||||||
|
for (int y = 0; y < GNUUnifont.HEIGHT; ++y) {
|
||||||
|
for (int x = 0; x < width; ++x) {
|
||||||
|
int bit = x + y * width;
|
||||||
|
|
||||||
|
editor.setPixel(
|
||||||
|
x, GNUUnifont.HEIGHT - y - 1,
|
||||||
|
getBit(declar, bit) ? 0xFFFFFFFF : 0x00000000
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new ParsedGlyph(c, editor);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static char getChar(String declar) {
|
||||||
|
int result = 0;
|
||||||
|
|
||||||
|
for (int i = 0; i < 4; ++i) {
|
||||||
|
result =
|
||||||
|
(result << BITS_PER_HEX_DIGIT) |
|
||||||
|
getHexValue(declar.charAt(i));
|
||||||
|
}
|
||||||
|
|
||||||
|
return (char) result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean getBit(String declar, int bit) {
|
||||||
|
int character = PREFIX_LENGTH + (bit / BITS_PER_HEX_DIGIT);
|
||||||
|
bit = bit % BITS_PER_HEX_DIGIT;
|
||||||
|
|
||||||
|
char c = declar.charAt(character);
|
||||||
|
int value = getHexValue(c);
|
||||||
|
|
||||||
|
return (value & (1 << (BITS_PER_HEX_DIGIT - bit - 1))) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int getWidth(String declar) {
|
||||||
|
int meaningfulChars = declar.length() - PREFIX_LENGTH;
|
||||||
|
final int charsPerColumn = GNUUnifont.HEIGHT / BITS_PER_HEX_DIGIT;
|
||||||
|
return meaningfulChars / charsPerColumn;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void checkDeclaration(String declar, int width) {
|
||||||
|
if (!GNUUnifont.WIDTHS.contains(width)) {
|
||||||
|
throw new RuntimeException("Width " + width + " is not supported (in declar \"" + declar + "\")");
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((declar.length() - PREFIX_LENGTH) % width != 0) {
|
||||||
|
throw new RuntimeException("Declar \"" + declar + "\" has invalid length");
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < declar.length(); ++i) {
|
||||||
|
if (i == BITS_PER_HEX_DIGIT) {
|
||||||
|
if (declar.charAt(i) != ':') {
|
||||||
|
throw new RuntimeException("No colon ':' found in declar \"" + declar + "\" at index 4");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
char c = declar.charAt(i);
|
||||||
|
|
||||||
|
if (!((c >= '0' && c <= '9') || (c >= 'A' && c <= 'F'))) {
|
||||||
|
throw new RuntimeException("Illegal char in declar \"" + declar + "\" at index " + i + "; expected 0-9A-F");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int getHexValue(char digit) {
|
||||||
|
if (digit < '0') throw new NumberFormatException(digit + " is not a hex digit (0-9A-F expected)");
|
||||||
|
if (digit <= '9') return digit - '0';
|
||||||
|
if (digit < 'A') throw new NumberFormatException(digit + " is not a hex digit (0-9A-F expected)");
|
||||||
|
if (digit <= 'F') return digit - 'A' + 0xA;
|
||||||
|
throw new NumberFormatException(digit + " is not a hex digit (0-9A-F expected)");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static AtlasGlyph addToAtlas(ParsedGlyph glyph) {
|
||||||
|
Sprite sprite = Atlases.loadSprite(glyph.data.getData(), ATLAS_GROUP_GNU_UNIFONT);
|
||||||
|
return new AtlasGlyph(glyph.c, new SimpleTexture(sprite));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static
|
||||||
|
Collector<AtlasGlyph, ?, TCharObjectMap<Texture>>
|
||||||
|
createMapper() {
|
||||||
|
return Collector.of(
|
||||||
|
TCharObjectHashMap<Texture>::new,
|
||||||
|
|
||||||
|
(map, glyph) -> map.put(glyph.c, glyph.texture),
|
||||||
|
|
||||||
|
(a, b) -> {
|
||||||
|
a.putAll(b);
|
||||||
|
return a;
|
||||||
|
},
|
||||||
|
|
||||||
|
Characteristics.UNORDERED
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private GNUUnifontLoader() {}
|
||||||
|
|
||||||
|
}
|
@ -41,6 +41,10 @@ public abstract class SpriteTypeface extends Typeface {
|
|||||||
return thickness;
|
return thickness;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int getInterlineBuffer() {
|
||||||
|
return getThickness();
|
||||||
|
}
|
||||||
|
|
||||||
public float getItalicsSlant() {
|
public float getItalicsSlant() {
|
||||||
return getThickness() / (float) getHeight();
|
return getThickness() / (float) getHeight();
|
||||||
}
|
}
|
||||||
@ -87,7 +91,7 @@ public abstract class SpriteTypeface extends Typeface {
|
|||||||
style, align, color
|
style, align, color
|
||||||
);
|
);
|
||||||
|
|
||||||
y -= getHeight();
|
y -= getHeight() + getInterlineBuffer();
|
||||||
|
|
||||||
currentWidth = Style.isBold(style) ? getThickness() : 0;
|
currentWidth = Style.isBold(style) ? getThickness() : 0;
|
||||||
|
|
||||||
@ -285,7 +289,7 @@ public abstract class SpriteTypeface extends Typeface {
|
|||||||
char c = chars.charAt(i);
|
char c = chars.charAt(i);
|
||||||
|
|
||||||
if (c == '\n' || currentWidth + getWidth(c) > maxWidth) {
|
if (c == '\n' || currentWidth + getWidth(c) > maxWidth) {
|
||||||
height += getHeight();
|
height += getHeight() + getInterlineBuffer();
|
||||||
if (resultWidth < currentWidth) resultWidth = currentWidth;
|
if (resultWidth < currentWidth) resultWidth = currentWidth;
|
||||||
currentWidth = Style.isBold(style) ? getThickness() : 0;
|
currentWidth = Style.isBold(style) ? getThickness() : 0;
|
||||||
}
|
}
|
||||||
|
@ -1,58 +0,0 @@
|
|||||||
package ru.windcorp.progressia.client.graphics.font;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
import glm.vec._2.Vec2;
|
|
||||||
import gnu.trove.map.TCharObjectMap;
|
|
||||||
import gnu.trove.map.hash.TCharObjectHashMap;
|
|
||||||
import ru.windcorp.progressia.client.graphics.flat.FlatRenderProgram;
|
|
||||||
import ru.windcorp.progressia.client.graphics.model.ShapeRenderProgram;
|
|
||||||
import ru.windcorp.progressia.client.graphics.texture.SimpleTexture;
|
|
||||||
import ru.windcorp.progressia.client.graphics.texture.Sprite;
|
|
||||||
import ru.windcorp.progressia.client.graphics.texture.Texture;
|
|
||||||
import ru.windcorp.progressia.client.graphics.texture.TextureLoader;
|
|
||||||
import ru.windcorp.progressia.client.graphics.texture.TexturePrimitive;
|
|
||||||
import ru.windcorp.progressia.client.graphics.texture.TextureSettings;
|
|
||||||
import ru.windcorp.progressia.common.resource.ResourceManager;
|
|
||||||
|
|
||||||
public class TestTypeface extends SpriteTypeface {
|
|
||||||
|
|
||||||
private static final TCharObjectMap<Texture> TEXTURES = new TCharObjectHashMap<>();
|
|
||||||
|
|
||||||
public TestTypeface() {
|
|
||||||
super("Test", 8, 1);
|
|
||||||
|
|
||||||
TexturePrimitive primitive = null;
|
|
||||||
try {
|
|
||||||
primitive = new TexturePrimitive(TextureLoader.loadPixels(
|
|
||||||
ResourceManager.getTextureResource("font_test"),
|
|
||||||
new TextureSettings(false)
|
|
||||||
).toStatic());
|
|
||||||
} catch (IOException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
Vec2 size = new Vec2(0.5f, 0.5f);
|
|
||||||
|
|
||||||
TEXTURES.put('A', new SimpleTexture(new Sprite(primitive, new Vec2(0, 0.5f), size)));
|
|
||||||
TEXTURES.put('B', new SimpleTexture(new Sprite(primitive, new Vec2(0.5f, 0.5f), size)));
|
|
||||||
TEXTURES.put('C', new SimpleTexture(new Sprite(primitive, new Vec2(0, 0), size)));
|
|
||||||
TEXTURES.put('D', new SimpleTexture(new Sprite(primitive, new Vec2(0.5f, 0), size)));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Texture getTexture(char c) {
|
|
||||||
return TEXTURES.get(c);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean supports(char c) {
|
|
||||||
return TEXTURES.containsKey(c);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ShapeRenderProgram getProgram() {
|
|
||||||
return FlatRenderProgram.getDefault();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -27,7 +27,7 @@ public class Label extends Component {
|
|||||||
|
|
||||||
public void update() {
|
public void update() {
|
||||||
currentText = contents.get();
|
currentText = contents.get();
|
||||||
currentSize = font.getSize(currentText, Integer.MAX_VALUE, null).mul(4);
|
currentSize = font.getSize(currentText, Integer.MAX_VALUE, null).mul(2);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -51,7 +51,7 @@ public class Label extends Component {
|
|||||||
protected void assembleSelf(RenderTarget target) {
|
protected void assembleSelf(RenderTarget target) {
|
||||||
target.pushTransform(
|
target.pushTransform(
|
||||||
new Mat4().identity().translate(getX(), getY(), -1000)
|
new Mat4().identity().translate(getX(), getY(), -1000)
|
||||||
.scale(4)
|
.scale(2)
|
||||||
);
|
);
|
||||||
|
|
||||||
target.addCustomRenderer(font.assemble(currentText, Integer.MAX_VALUE));
|
target.addCustomRenderer(font.assemble(currentText, Integer.MAX_VALUE));
|
||||||
|
@ -76,8 +76,23 @@ public class LayerTestGUI extends GUILayer {
|
|||||||
panel.addChild(new DebugComponent("Bravo", new Vec2i(200, 100), 0x44FF44));
|
panel.addChild(new DebugComponent("Bravo", new Vec2i(200, 100), 0x44FF44));
|
||||||
|
|
||||||
Component charlie = new DebugComponent("Charlie", null, 0x222222);
|
Component charlie = new DebugComponent("Charlie", null, 0x222222);
|
||||||
charlie.setLayout(new LayoutAlign());
|
charlie.setLayout(new LayoutVertical());
|
||||||
charlie.addChild(new Label("Delta", new Font().deriveItalic().deriveBold().withAlign(0.5f).withColor(0xCCBB44).deriveShadow().deriveUnderlined().deriveStrikethru(), "BABCB\nABCBABC"));
|
|
||||||
|
// These two are swapped in code due to a bug in layouts, fixing ATM
|
||||||
|
charlie.addChild(
|
||||||
|
new Label(
|
||||||
|
"Epsilon",
|
||||||
|
new Font().withColor(0x4444BB).deriveItalic(),
|
||||||
|
"Hooray?.. \u269b"
|
||||||
|
)
|
||||||
|
);
|
||||||
|
charlie.addChild(
|
||||||
|
new Label(
|
||||||
|
"Delta",
|
||||||
|
new Font().withColor(0xCCBB44).deriveShadow().deriveBold(),
|
||||||
|
"Пре-альфа!"
|
||||||
|
)
|
||||||
|
);
|
||||||
panel.addChild(charlie);
|
panel.addChild(charlie);
|
||||||
|
|
||||||
getRoot().addChild(panel);
|
getRoot().addChild(panel);
|
||||||
|
@ -60,10 +60,10 @@ public class Atlases {
|
|||||||
int size = group.getAtlasSize();
|
int size = group.getAtlasSize();
|
||||||
|
|
||||||
this.editor = new TextureDataEditor(size, size, size, size, SETTINGS);
|
this.editor = new TextureDataEditor(size, size, size, size, SETTINGS);
|
||||||
this.primitive = new TexturePrimitive(editor.createSnapshot());
|
this.primitive = new TexturePrimitive(editor.getData());
|
||||||
}
|
}
|
||||||
|
|
||||||
public Sprite addSprite(TextureDataEditor data) {
|
public Sprite addSprite(TextureData data) {
|
||||||
int width = data.getContentWidth();
|
int width = data.getContentWidth();
|
||||||
int height = data.getContentHeight();
|
int height = data.getContentHeight();
|
||||||
|
|
||||||
@ -109,7 +109,7 @@ public class Atlases {
|
|||||||
return c / (float) getSize();
|
return c / (float) getSize();
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean canAddSprite(TextureDataEditor data) {
|
public boolean canAddSprite(TextureData data) {
|
||||||
int width = data.getContentWidth();
|
int width = data.getContentWidth();
|
||||||
int height = data.getContentHeight();
|
int height = data.getContentHeight();
|
||||||
|
|
||||||
@ -137,11 +137,6 @@ public class Atlases {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void flush() {
|
|
||||||
editor.createSnapshot(primitive.getData());
|
|
||||||
editor.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
public AtlasGroup getGroup() {
|
public AtlasGroup getGroup() {
|
||||||
return group;
|
return group;
|
||||||
}
|
}
|
||||||
@ -167,20 +162,22 @@ public class Atlases {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static Sprite loadSprite(Resource resource, AtlasGroup group) {
|
private static Sprite loadSprite(Resource resource, AtlasGroup group) {
|
||||||
try (
|
try {
|
||||||
TextureDataEditor data =
|
TextureDataEditor data =
|
||||||
TextureLoader.loadPixels(resource, SETTINGS)
|
TextureLoader.loadPixels(resource, SETTINGS);
|
||||||
) {
|
|
||||||
|
|
||||||
Atlas atlas = getReadyAtlas(group, data);
|
|
||||||
return atlas.addSprite(data);
|
|
||||||
|
|
||||||
|
return loadSprite(data.getData(), group);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Atlas getReadyAtlas(AtlasGroup group, TextureDataEditor data) {
|
public static Sprite loadSprite(TextureData data, AtlasGroup group) {
|
||||||
|
Atlas atlas = getReadyAtlas(group, data);
|
||||||
|
return atlas.addSprite(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Atlas getReadyAtlas(AtlasGroup group, TextureData data) {
|
||||||
List<Atlas> atlases = (List<Atlas>) ATLASES.get(group);
|
List<Atlas> atlases = (List<Atlas>) ATLASES.get(group);
|
||||||
|
|
||||||
if (
|
if (
|
||||||
@ -201,7 +198,6 @@ public class Atlases {
|
|||||||
|
|
||||||
public static void loadAllAtlases() {
|
public static void loadAllAtlases() {
|
||||||
ATLASES.forEach((group, atlas) -> {
|
ATLASES.forEach((group, atlas) -> {
|
||||||
atlas.flush();
|
|
||||||
RenderTaskQueue.invokeLater(atlas.getPrimitive()::load);
|
RenderTaskQueue.invokeLater(atlas.getPrimitive()::load);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -22,16 +22,12 @@ public class SimpleTextures {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static Texture load(Resource resource) {
|
private static Texture load(Resource resource) {
|
||||||
try (
|
try {
|
||||||
TextureDataEditor data =
|
TextureDataEditor data =
|
||||||
TextureLoader.loadPixels(resource, SETTINGS)
|
TextureLoader.loadPixels(resource, SETTINGS);
|
||||||
) {
|
|
||||||
return new SimpleTexture(
|
return new SimpleTexture(
|
||||||
new Sprite(
|
new Sprite(new TexturePrimitive(data.getData()))
|
||||||
new TexturePrimitive(
|
|
||||||
data.toStatic()
|
|
||||||
)
|
|
||||||
)
|
|
||||||
);
|
);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
|
@ -1,166 +1,147 @@
|
|||||||
package ru.windcorp.progressia.client.graphics.texture;
|
package ru.windcorp.progressia.client.graphics.texture;
|
||||||
|
|
||||||
import java.awt.Graphics2D;
|
import static ru.windcorp.progressia.client.graphics.texture.TextureUtils.BYTES_PER_PIXEL;
|
||||||
import java.awt.image.BufferedImage;
|
|
||||||
|
|
||||||
public class TextureDataEditor implements AutoCloseable {
|
import java.nio.ByteBuffer;
|
||||||
|
|
||||||
protected final BufferedImage image;
|
import org.lwjgl.BufferUtils;
|
||||||
protected Graphics2D graphics;
|
|
||||||
|
|
||||||
private final int contentWidth;
|
public class TextureDataEditor {
|
||||||
private final int contentHeight;
|
|
||||||
private final TextureSettings settings;
|
|
||||||
|
|
||||||
protected TextureDataEditor(
|
protected final TextureData data;
|
||||||
BufferedImage image,
|
|
||||||
int contentWidth, int contentHeight,
|
|
||||||
TextureSettings settings
|
|
||||||
) {
|
|
||||||
this.image = image;
|
|
||||||
this.contentWidth = contentWidth;
|
|
||||||
this.contentHeight = contentHeight;
|
|
||||||
this.settings = settings;
|
|
||||||
|
|
||||||
startEditing();
|
|
||||||
}
|
|
||||||
|
|
||||||
public TextureDataEditor(
|
public TextureDataEditor(
|
||||||
int bufferWidth, int bufferHeight,
|
int bufferWidth, int bufferHeight,
|
||||||
int contentWidth, int contentHeight,
|
int contentWidth, int contentHeight,
|
||||||
TextureSettings settings
|
TextureSettings settings
|
||||||
) {
|
) {
|
||||||
this(
|
this.data = new TextureData(
|
||||||
TextureUtils.createCanvas(
|
BufferUtils.createByteBuffer(bufferWidth * bufferHeight * 4),
|
||||||
TextureUtils.createRaster(bufferWidth, bufferHeight)
|
bufferWidth, bufferHeight,
|
||||||
),
|
|
||||||
contentWidth, contentHeight,
|
contentWidth, contentHeight,
|
||||||
settings
|
settings
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public TextureDataEditor(TextureData buffer) {
|
public TextureDataEditor(TextureData data) {
|
||||||
this(
|
this.data = data;
|
||||||
createImage(buffer),
|
|
||||||
buffer.getContentWidth(),
|
|
||||||
buffer.getContentHeight(),
|
|
||||||
buffer.getSettings()
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static BufferedImage createImage(TextureData buffer) {
|
public TextureData getData() {
|
||||||
return TextureUtils.createCanvas(
|
return data;
|
||||||
TextureUtils.createRaster(
|
|
||||||
buffer.getData(),
|
|
||||||
buffer.getBufferWidth(),
|
|
||||||
buffer.getBufferHeight()
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void startEditing() {
|
protected ByteBuffer getBuffer() {
|
||||||
if (isEditable()) {
|
return getData().getData();
|
||||||
throw new IllegalStateException("This object is already editable");
|
|
||||||
}
|
|
||||||
|
|
||||||
this.graphics = this.image.createGraphics();
|
|
||||||
|
|
||||||
graphics.setColor(TextureUtils.CANVAS_BACKGROUND);
|
|
||||||
graphics.fillRect(0, 0, image.getWidth(), image.getHeight());
|
|
||||||
|
|
||||||
graphics.clipRect(0, 0, contentWidth, contentHeight);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void finishEditing() {
|
|
||||||
checkEditability();
|
|
||||||
this.graphics.dispose();
|
|
||||||
this.graphics = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void close() {
|
|
||||||
if (isEditable()) {
|
|
||||||
finishEditing();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isEditable() {
|
|
||||||
return this.graphics != null;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void checkEditability() {
|
|
||||||
if (!isEditable()) {
|
|
||||||
throw new IllegalStateException("This object is not editable");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public TextureData createSnapshot() {
|
public TextureData createSnapshot() {
|
||||||
|
TextureData t = getData();
|
||||||
|
|
||||||
|
ByteBuffer fromBuffer = getBuffer();
|
||||||
|
ByteBuffer toBuffer = BufferUtils.createByteBuffer(
|
||||||
|
fromBuffer.capacity()
|
||||||
|
);
|
||||||
|
|
||||||
|
copy(fromBuffer, 0, fromBuffer.capacity(), toBuffer);
|
||||||
|
toBuffer.clear();
|
||||||
|
|
||||||
return new TextureData(
|
return new TextureData(
|
||||||
TextureUtils.extractBytes(image.getRaster()),
|
toBuffer,
|
||||||
image.getWidth(), image.getHeight(),
|
t.getBufferWidth(), t.getBufferHeight(),
|
||||||
contentWidth, contentHeight,
|
t.getContentWidth(), t.getContentHeight(),
|
||||||
settings
|
t.getSettings()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public TextureData createSnapshot(TextureData output) {
|
public TextureData createSnapshot(TextureData output) {
|
||||||
TextureUtils.extractBytes(image.getRaster(), output.getData());
|
ByteBuffer src = getBuffer();
|
||||||
|
ByteBuffer dst = output.getData();
|
||||||
|
|
||||||
|
int position = dst.position();
|
||||||
|
int limit = dst.limit();
|
||||||
|
|
||||||
|
try {
|
||||||
|
copy(src, 0, src.capacity(), output.getData());
|
||||||
|
} finally {
|
||||||
|
dst.limit(dst.capacity()).position(position).limit(limit);
|
||||||
|
}
|
||||||
|
|
||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
public TextureData toStatic() {
|
|
||||||
close();
|
|
||||||
return createSnapshot();
|
|
||||||
}
|
|
||||||
|
|
||||||
public TextureData toStatic(TextureData data) {
|
|
||||||
close();
|
|
||||||
return createSnapshot(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getBufferWidth() {
|
public int getBufferWidth() {
|
||||||
return image.getWidth();
|
return getData().getBufferWidth();
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getBufferHeight() {
|
public int getBufferHeight() {
|
||||||
return image.getHeight();
|
return getData().getBufferHeight();
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getContentWidth() {
|
public int getContentWidth() {
|
||||||
return contentWidth;
|
return getData().getContentWidth();
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getContentHeight() {
|
public int getContentHeight() {
|
||||||
return contentHeight;
|
return getData().getContentHeight();
|
||||||
}
|
}
|
||||||
|
|
||||||
public TextureSettings getSettings() {
|
public TextureSettings getSettings() {
|
||||||
return settings;
|
return getData().getSettings();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void draw(
|
public void draw(
|
||||||
BufferedImage source,
|
ByteBuffer src,
|
||||||
|
int srcWidth,
|
||||||
int srcX, int srcY,
|
int srcX, int srcY,
|
||||||
int dstX, int dstY,
|
int dstX, int dstY,
|
||||||
int width, int height
|
int width, int height
|
||||||
) {
|
) {
|
||||||
checkEditability();
|
ByteBuffer dst = getBuffer();
|
||||||
|
|
||||||
graphics.drawImage(
|
int position = src.position();
|
||||||
source,
|
int limit = src.limit();
|
||||||
dstX, dstY,
|
|
||||||
dstX + width, dstY + height,
|
try {
|
||||||
srcX, srcY,
|
|
||||||
srcX + width, srcY + height,
|
for (int line = 0; line < height; ++line) {
|
||||||
null
|
src.limit(src.capacity());
|
||||||
|
|
||||||
|
position(dst, dstX, dstY + line, getBufferWidth());
|
||||||
|
position(src, srcX, srcY + line, srcWidth);
|
||||||
|
setLength(src, width);
|
||||||
|
|
||||||
|
dst.put(src);
|
||||||
|
|
||||||
|
dst.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
} finally {
|
||||||
|
src.limit(src.capacity()).position(position).limit(limit);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void draw(
|
||||||
|
TextureData source,
|
||||||
|
int srcX, int srcY,
|
||||||
|
int dstX, int dstY,
|
||||||
|
int width, int height
|
||||||
|
) {
|
||||||
|
draw(
|
||||||
|
source.getData(),
|
||||||
|
source.getBufferWidth(),
|
||||||
|
srcX, srcY, dstX, dstY, width, height
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void draw(
|
public void draw(
|
||||||
BufferedImage source,
|
TextureData source,
|
||||||
int dstX, int dstY
|
int dstX, int dstY
|
||||||
) {
|
) {
|
||||||
draw(source, 0, 0, dstX, dstY, source.getWidth(), source.getHeight());
|
draw(
|
||||||
|
source, 0, 0, dstX, dstY,
|
||||||
|
source.getContentWidth(), source.getContentHeight()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void draw(
|
public void draw(
|
||||||
@ -169,7 +150,10 @@ public class TextureDataEditor implements AutoCloseable {
|
|||||||
int dstX, int dstY,
|
int dstX, int dstY,
|
||||||
int width, int height
|
int width, int height
|
||||||
) {
|
) {
|
||||||
draw(source.image, srcX, srcY, dstX, dstY, width, height);
|
draw(
|
||||||
|
source.getData(),
|
||||||
|
srcX, srcY, dstX, dstY, width, height
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void draw(
|
public void draw(
|
||||||
@ -182,4 +166,36 @@ public class TextureDataEditor implements AutoCloseable {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setPixel(int x, int y, int color) {
|
||||||
|
ByteBuffer dst = getBuffer();
|
||||||
|
|
||||||
|
position(dst, x, y, getBufferWidth());
|
||||||
|
dst.putInt(color);
|
||||||
|
dst.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void position(ByteBuffer buffer, int x, int y, int width) {
|
||||||
|
buffer.position((y * width + x) * BYTES_PER_PIXEL);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void setLength(ByteBuffer buffer, int length) {
|
||||||
|
buffer.limit(buffer.position() + length * BYTES_PER_PIXEL);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void copy(
|
||||||
|
ByteBuffer src,
|
||||||
|
int srcStart, int srcEnd,
|
||||||
|
ByteBuffer dst
|
||||||
|
) {
|
||||||
|
int position = src.position();
|
||||||
|
int limit = src.limit();
|
||||||
|
|
||||||
|
try {
|
||||||
|
src.limit(src.capacity()).position(srcStart).limit(srcEnd);
|
||||||
|
dst.put(src);
|
||||||
|
} finally {
|
||||||
|
src.limit(src.capacity()).position(position).limit(limit);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@ package ru.windcorp.progressia.client.graphics.texture;
|
|||||||
|
|
||||||
import java.awt.Graphics2D;
|
import java.awt.Graphics2D;
|
||||||
import java.awt.image.BufferedImage;
|
import java.awt.image.BufferedImage;
|
||||||
|
import java.awt.image.WritableRaster;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
|
||||||
@ -23,16 +24,34 @@ public class TextureLoader {
|
|||||||
int bufferWidth = BinUtil.roundToGreaterPowerOf2(width);
|
int bufferWidth = BinUtil.roundToGreaterPowerOf2(width);
|
||||||
int bufferHeight = BinUtil.roundToGreaterPowerOf2(height);
|
int bufferHeight = BinUtil.roundToGreaterPowerOf2(height);
|
||||||
|
|
||||||
|
WritableRaster raster = TextureUtils.createRaster(
|
||||||
|
bufferWidth, bufferHeight
|
||||||
|
);
|
||||||
|
|
||||||
|
BufferedImage canvas = TextureUtils.createCanvas(raster);
|
||||||
|
|
||||||
|
Graphics2D g = canvas.createGraphics();
|
||||||
|
|
||||||
|
try {
|
||||||
|
g.setColor(TextureUtils.CANVAS_BACKGROUND);
|
||||||
|
g.fillRect(0, 0, bufferWidth, bufferHeight);
|
||||||
|
g.drawImage(
|
||||||
|
readResult,
|
||||||
|
0, 0, width, height,
|
||||||
|
0, height, width, 0, // Flip the image
|
||||||
|
null
|
||||||
|
);
|
||||||
|
} finally {
|
||||||
|
g.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
TextureDataEditor result = new TextureDataEditor(
|
TextureDataEditor result = new TextureDataEditor(
|
||||||
bufferWidth, bufferHeight, width, height, settings
|
bufferWidth, bufferHeight, width, height, settings
|
||||||
);
|
);
|
||||||
|
|
||||||
Graphics2D g = result.graphics;
|
result.draw(
|
||||||
g.drawImage(
|
TextureUtils.extractBytes(raster), bufferWidth,
|
||||||
readResult,
|
0, 0, 0, 0, width, height
|
||||||
0, 0, width, height,
|
|
||||||
0, height, width, 0, // Flip the image
|
|
||||||
null
|
|
||||||
);
|
);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
@ -17,15 +17,17 @@ import org.lwjgl.BufferUtils;
|
|||||||
|
|
||||||
public class TextureUtils {
|
public class TextureUtils {
|
||||||
|
|
||||||
|
public static final int BYTES_PER_PIXEL = 4; // ARGB
|
||||||
|
|
||||||
public static final Color CANVAS_BACKGROUND = new Color(0, true);
|
public static final Color CANVAS_BACKGROUND = new Color(0, true);
|
||||||
|
|
||||||
public static final ColorModel COLOR_MODEL = new ComponentColorModel(
|
public static final ColorModel COLOR_MODEL = new ComponentColorModel(
|
||||||
ColorSpace.getInstance(ColorSpace.CS_sRGB), // Use RGB
|
ColorSpace.getInstance(ColorSpace.CS_sRGB), // Use RGB
|
||||||
new int[] {8, 8, 8, 8}, // Use every bit
|
null, // Use every bit
|
||||||
true, // Has alpha
|
true, // Has alpha
|
||||||
false, // Not premultiplied
|
false, // Not premultiplied
|
||||||
Transparency.TRANSLUCENT, // Can have any alpha
|
Transparency.TRANSLUCENT, // Can have any alpha
|
||||||
DataBuffer.TYPE_BYTE // Alpha is one byte
|
DataBuffer.TYPE_BYTE // Store bytewise
|
||||||
);
|
);
|
||||||
|
|
||||||
private static final Hashtable<?, ?> BUFFERED_IMAGE_PROPERTIES =
|
private static final Hashtable<?, ?> BUFFERED_IMAGE_PROPERTIES =
|
||||||
@ -39,7 +41,7 @@ public class TextureUtils {
|
|||||||
DataBuffer.TYPE_BYTE, // Storage model
|
DataBuffer.TYPE_BYTE, // Storage model
|
||||||
bufferWidth, // Buffer width
|
bufferWidth, // Buffer width
|
||||||
bufferHeight, // Buffer height
|
bufferHeight, // Buffer height
|
||||||
4, // RGBA
|
BYTES_PER_PIXEL, // ARGB
|
||||||
null // Location (here (0; 0))
|
null // Location (here (0; 0))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -49,7 +51,7 @@ public class TextureUtils {
|
|||||||
int bufferWidth,
|
int bufferWidth,
|
||||||
int bufferHeight
|
int bufferHeight
|
||||||
) {
|
) {
|
||||||
final int bands = 4; // RGBA
|
final int bands = BYTES_PER_PIXEL;
|
||||||
|
|
||||||
byte[] bytes = new byte[bufferWidth * bufferHeight * bands];
|
byte[] bytes = new byte[bufferWidth * bufferHeight * bands];
|
||||||
|
|
||||||
|
Binary file not shown.
Before Width: | Height: | Size: 194 B |
BIN
src/main/resources/assets/unifont-13.0.03.hex.gz
Normal file
BIN
src/main/resources/assets/unifont-13.0.03.hex.gz
Normal file
Binary file not shown.
Reference in New Issue
Block a user