diff --git a/src/main/java/ru/windcorp/progressia/client/ClientProxy.java b/src/main/java/ru/windcorp/progressia/client/ClientProxy.java index ebbad97..9ccb063 100644 --- a/src/main/java/ru/windcorp/progressia/client/ClientProxy.java +++ b/src/main/java/ru/windcorp/progressia/client/ClientProxy.java @@ -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.flat.FlatRenderProgram; 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.gui.LayerTestGUI; import ru.windcorp.progressia.client.graphics.texture.Atlases; import ru.windcorp.progressia.client.graphics.world.LayerWorld; import ru.windcorp.progressia.client.graphics.world.WorldRenderProgram; import ru.windcorp.progressia.client.world.renders.BlockRenders; +import ru.windcorp.progressia.common.resource.ResourceManager; public class ClientProxy implements Proxy { @@ -39,7 +40,7 @@ public class ClientProxy implements Proxy { try { RenderTaskQueue.waitAndInvoke(FlatRenderProgram::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) { // TODO Auto-generated catch block e.printStackTrace(); diff --git a/src/main/java/ru/windcorp/progressia/client/graphics/font/GNUUnifont.java b/src/main/java/ru/windcorp/progressia/client/graphics/font/GNUUnifont.java new file mode 100644 index 0000000..de413ec --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/client/graphics/font/GNUUnifont.java @@ -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 textures; + + public GNUUnifont(TCharObjectMap 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(); + } + +} diff --git a/src/main/java/ru/windcorp/progressia/client/graphics/font/GNUUnifontLoader.java b/src/main/java/ru/windcorp/progressia/client/graphics/font/GNUUnifontLoader.java new file mode 100644 index 0000000..681effb --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/client/graphics/font/GNUUnifontLoader.java @@ -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 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> + createMapper() { + return Collector.of( + TCharObjectHashMap::new, + + (map, glyph) -> map.put(glyph.c, glyph.texture), + + (a, b) -> { + a.putAll(b); + return a; + }, + + Characteristics.UNORDERED + ); + } + + private GNUUnifontLoader() {} + +} diff --git a/src/main/java/ru/windcorp/progressia/client/graphics/font/SpriteTypeface.java b/src/main/java/ru/windcorp/progressia/client/graphics/font/SpriteTypeface.java index 9638615..d519125 100644 --- a/src/main/java/ru/windcorp/progressia/client/graphics/font/SpriteTypeface.java +++ b/src/main/java/ru/windcorp/progressia/client/graphics/font/SpriteTypeface.java @@ -41,6 +41,10 @@ public abstract class SpriteTypeface extends Typeface { return thickness; } + public int getInterlineBuffer() { + return getThickness(); + } + public float getItalicsSlant() { return getThickness() / (float) getHeight(); } @@ -87,7 +91,7 @@ public abstract class SpriteTypeface extends Typeface { style, align, color ); - y -= getHeight(); + y -= getHeight() + getInterlineBuffer(); currentWidth = Style.isBold(style) ? getThickness() : 0; @@ -285,7 +289,7 @@ public abstract class SpriteTypeface extends Typeface { char c = chars.charAt(i); if (c == '\n' || currentWidth + getWidth(c) > maxWidth) { - height += getHeight(); + height += getHeight() + getInterlineBuffer(); if (resultWidth < currentWidth) resultWidth = currentWidth; currentWidth = Style.isBold(style) ? getThickness() : 0; } diff --git a/src/main/java/ru/windcorp/progressia/client/graphics/font/TestTypeface.java b/src/main/java/ru/windcorp/progressia/client/graphics/font/TestTypeface.java deleted file mode 100755 index 2d5077c..0000000 --- a/src/main/java/ru/windcorp/progressia/client/graphics/font/TestTypeface.java +++ /dev/null @@ -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 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(); - } - -} diff --git a/src/main/java/ru/windcorp/progressia/client/graphics/gui/Label.java b/src/main/java/ru/windcorp/progressia/client/graphics/gui/Label.java index c70f464..1da850f 100755 --- a/src/main/java/ru/windcorp/progressia/client/graphics/gui/Label.java +++ b/src/main/java/ru/windcorp/progressia/client/graphics/gui/Label.java @@ -27,7 +27,7 @@ public class Label extends Component { public void update() { currentText = contents.get(); - currentSize = font.getSize(currentText, Integer.MAX_VALUE, null).mul(4); + currentSize = font.getSize(currentText, Integer.MAX_VALUE, null).mul(2); } @Override @@ -51,7 +51,7 @@ public class Label extends Component { protected void assembleSelf(RenderTarget target) { target.pushTransform( new Mat4().identity().translate(getX(), getY(), -1000) - .scale(4) + .scale(2) ); target.addCustomRenderer(font.assemble(currentText, Integer.MAX_VALUE)); diff --git a/src/main/java/ru/windcorp/progressia/client/graphics/gui/LayerTestGUI.java b/src/main/java/ru/windcorp/progressia/client/graphics/gui/LayerTestGUI.java index 1862eca..9d4af43 100755 --- a/src/main/java/ru/windcorp/progressia/client/graphics/gui/LayerTestGUI.java +++ b/src/main/java/ru/windcorp/progressia/client/graphics/gui/LayerTestGUI.java @@ -76,8 +76,23 @@ public class LayerTestGUI extends GUILayer { panel.addChild(new DebugComponent("Bravo", new Vec2i(200, 100), 0x44FF44)); Component charlie = new DebugComponent("Charlie", null, 0x222222); - charlie.setLayout(new LayoutAlign()); - charlie.addChild(new Label("Delta", new Font().deriveItalic().deriveBold().withAlign(0.5f).withColor(0xCCBB44).deriveShadow().deriveUnderlined().deriveStrikethru(), "BABCB\nABCBABC")); + charlie.setLayout(new LayoutVertical()); + + // 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); getRoot().addChild(panel); diff --git a/src/main/java/ru/windcorp/progressia/client/graphics/texture/Atlases.java b/src/main/java/ru/windcorp/progressia/client/graphics/texture/Atlases.java index 3ea7828..0d7c435 100644 --- a/src/main/java/ru/windcorp/progressia/client/graphics/texture/Atlases.java +++ b/src/main/java/ru/windcorp/progressia/client/graphics/texture/Atlases.java @@ -60,10 +60,10 @@ public class Atlases { int size = group.getAtlasSize(); 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 height = data.getContentHeight(); @@ -109,7 +109,7 @@ public class Atlases { return c / (float) getSize(); } - public boolean canAddSprite(TextureDataEditor data) { + public boolean canAddSprite(TextureData data) { int width = data.getContentWidth(); int height = data.getContentHeight(); @@ -137,11 +137,6 @@ public class Atlases { return true; } - public void flush() { - editor.createSnapshot(primitive.getData()); - editor.close(); - } - public AtlasGroup getGroup() { return group; } @@ -167,20 +162,22 @@ public class Atlases { } private static Sprite loadSprite(Resource resource, AtlasGroup group) { - try ( - TextureDataEditor data = - TextureLoader.loadPixels(resource, SETTINGS) - ) { - - Atlas atlas = getReadyAtlas(group, data); - return atlas.addSprite(data); + try { + TextureDataEditor data = + TextureLoader.loadPixels(resource, SETTINGS); + return loadSprite(data.getData(), group); } catch (IOException 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 atlases = (List) ATLASES.get(group); if ( @@ -201,7 +198,6 @@ public class Atlases { public static void loadAllAtlases() { ATLASES.forEach((group, atlas) -> { - atlas.flush(); RenderTaskQueue.invokeLater(atlas.getPrimitive()::load); }); } diff --git a/src/main/java/ru/windcorp/progressia/client/graphics/texture/SimpleTextures.java b/src/main/java/ru/windcorp/progressia/client/graphics/texture/SimpleTextures.java index a2f68e3..e453ff7 100644 --- a/src/main/java/ru/windcorp/progressia/client/graphics/texture/SimpleTextures.java +++ b/src/main/java/ru/windcorp/progressia/client/graphics/texture/SimpleTextures.java @@ -22,16 +22,12 @@ public class SimpleTextures { } private static Texture load(Resource resource) { - try ( - TextureDataEditor data = - TextureLoader.loadPixels(resource, SETTINGS) - ) { + try { + TextureDataEditor data = + TextureLoader.loadPixels(resource, SETTINGS); + return new SimpleTexture( - new Sprite( - new TexturePrimitive( - data.toStatic() - ) - ) + new Sprite(new TexturePrimitive(data.getData())) ); } catch (IOException e) { throw new RuntimeException(e); diff --git a/src/main/java/ru/windcorp/progressia/client/graphics/texture/TextureDataEditor.java b/src/main/java/ru/windcorp/progressia/client/graphics/texture/TextureDataEditor.java index 2cbe1c9..6c5240a 100644 --- a/src/main/java/ru/windcorp/progressia/client/graphics/texture/TextureDataEditor.java +++ b/src/main/java/ru/windcorp/progressia/client/graphics/texture/TextureDataEditor.java @@ -1,175 +1,159 @@ package ru.windcorp.progressia.client.graphics.texture; -import java.awt.Graphics2D; -import java.awt.image.BufferedImage; +import static ru.windcorp.progressia.client.graphics.texture.TextureUtils.BYTES_PER_PIXEL; -public class TextureDataEditor implements AutoCloseable { +import java.nio.ByteBuffer; + +import org.lwjgl.BufferUtils; + +public class TextureDataEditor { - protected final BufferedImage image; - protected Graphics2D graphics; - - private final int contentWidth; - private final int contentHeight; - private final TextureSettings settings; - - protected TextureDataEditor( - BufferedImage image, - int contentWidth, int contentHeight, - TextureSettings settings - ) { - this.image = image; - this.contentWidth = contentWidth; - this.contentHeight = contentHeight; - this.settings = settings; - - startEditing(); - } + protected final TextureData data; public TextureDataEditor( int bufferWidth, int bufferHeight, int contentWidth, int contentHeight, TextureSettings settings ) { - this( - TextureUtils.createCanvas( - TextureUtils.createRaster(bufferWidth, bufferHeight) - ), + this.data = new TextureData( + BufferUtils.createByteBuffer(bufferWidth * bufferHeight * 4), + bufferWidth, bufferHeight, contentWidth, contentHeight, settings ); } - public TextureDataEditor(TextureData buffer) { - this( - createImage(buffer), - buffer.getContentWidth(), - buffer.getContentHeight(), - buffer.getSettings() - ); - } - - private static BufferedImage createImage(TextureData buffer) { - return TextureUtils.createCanvas( - TextureUtils.createRaster( - buffer.getData(), - buffer.getBufferWidth(), - buffer.getBufferHeight() - ) - ); - } - - public void startEditing() { - if (isEditable()) { - 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 TextureDataEditor(TextureData data) { + this.data = data; } - public void finishEditing() { - checkEditability(); - this.graphics.dispose(); - this.graphics = null; + public TextureData getData() { + return data; } - @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"); - } + protected ByteBuffer getBuffer() { + return getData().getData(); } 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( - TextureUtils.extractBytes(image.getRaster()), - image.getWidth(), image.getHeight(), - contentWidth, contentHeight, - settings + toBuffer, + t.getBufferWidth(), t.getBufferHeight(), + t.getContentWidth(), t.getContentHeight(), + t.getSettings() ); } 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; } - - public TextureData toStatic() { - close(); - return createSnapshot(); - } - - public TextureData toStatic(TextureData data) { - close(); - return createSnapshot(data); - } public int getBufferWidth() { - return image.getWidth(); + return getData().getBufferWidth(); } public int getBufferHeight() { - return image.getHeight(); + return getData().getBufferHeight(); } public int getContentWidth() { - return contentWidth; + return getData().getContentWidth(); } public int getContentHeight() { - return contentHeight; + return getData().getContentHeight(); } public TextureSettings getSettings() { - return settings; + return getData().getSettings(); } public void draw( - BufferedImage source, + ByteBuffer src, + int srcWidth, int srcX, int srcY, int dstX, int dstY, int width, int height ) { - checkEditability(); + ByteBuffer dst = getBuffer(); - graphics.drawImage( - source, - dstX, dstY, - dstX + width, dstY + height, - srcX, srcY, - srcX + width, srcY + height, - null + int position = src.position(); + int limit = src.limit(); + + try { + + for (int line = 0; line < height; ++line) { + 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( - BufferedImage source, + TextureData source, 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( TextureDataEditor source, int srcX, int srcY, int dstX, int dstY, 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( @@ -181,5 +165,37 @@ public class TextureDataEditor implements AutoCloseable { source.getContentWidth(), source.getContentHeight() ); } + + 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); + } + } } diff --git a/src/main/java/ru/windcorp/progressia/client/graphics/texture/TextureLoader.java b/src/main/java/ru/windcorp/progressia/client/graphics/texture/TextureLoader.java index 6a82641..b7f705a 100644 --- a/src/main/java/ru/windcorp/progressia/client/graphics/texture/TextureLoader.java +++ b/src/main/java/ru/windcorp/progressia/client/graphics/texture/TextureLoader.java @@ -2,6 +2,7 @@ package ru.windcorp.progressia.client.graphics.texture; import java.awt.Graphics2D; import java.awt.image.BufferedImage; +import java.awt.image.WritableRaster; import java.io.IOException; import java.io.InputStream; @@ -23,16 +24,34 @@ public class TextureLoader { int bufferWidth = BinUtil.roundToGreaterPowerOf2(width); 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( bufferWidth, bufferHeight, width, height, settings ); - Graphics2D g = result.graphics; - g.drawImage( - readResult, - 0, 0, width, height, - 0, height, width, 0, // Flip the image - null + result.draw( + TextureUtils.extractBytes(raster), bufferWidth, + 0, 0, 0, 0, width, height ); return result; diff --git a/src/main/java/ru/windcorp/progressia/client/graphics/texture/TextureUtils.java b/src/main/java/ru/windcorp/progressia/client/graphics/texture/TextureUtils.java index 4a96a53..955c6ae 100644 --- a/src/main/java/ru/windcorp/progressia/client/graphics/texture/TextureUtils.java +++ b/src/main/java/ru/windcorp/progressia/client/graphics/texture/TextureUtils.java @@ -17,15 +17,17 @@ import org.lwjgl.BufferUtils; 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 ColorModel COLOR_MODEL = new ComponentColorModel( ColorSpace.getInstance(ColorSpace.CS_sRGB), // Use RGB - new int[] {8, 8, 8, 8}, // Use every bit + null, // Use every bit true, // Has alpha false, // Not premultiplied 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 = @@ -39,7 +41,7 @@ public class TextureUtils { DataBuffer.TYPE_BYTE, // Storage model bufferWidth, // Buffer width bufferHeight, // Buffer height - 4, // RGBA + BYTES_PER_PIXEL, // ARGB null // Location (here (0; 0)) ); } @@ -49,7 +51,7 @@ public class TextureUtils { int bufferWidth, int bufferHeight ) { - final int bands = 4; // RGBA + final int bands = BYTES_PER_PIXEL; byte[] bytes = new byte[bufferWidth * bufferHeight * bands]; diff --git a/src/main/resources/assets/textures/font_test.png b/src/main/resources/assets/textures/font_test.png deleted file mode 100755 index e80021f..0000000 Binary files a/src/main/resources/assets/textures/font_test.png and /dev/null differ diff --git a/src/main/resources/assets/unifont-13.0.03.hex.gz b/src/main/resources/assets/unifont-13.0.03.hex.gz new file mode 100644 index 0000000..1f0e96e Binary files /dev/null and b/src/main/resources/assets/unifont-13.0.03.hex.gz differ