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.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();
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
@ -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() {
|
||||
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));
|
||||
|
@ -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);
|
||||
|
@ -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<Atlas> atlases = (List<Atlas>) 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);
|
||||
});
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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];
|
||||
|
||||
|
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