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:
OLEGSHA 2020-08-17 22:22:13 +03:00
parent 54002475d0
commit e1ce8c2e09
14 changed files with 441 additions and 210 deletions

View File

@ -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();

View File

@ -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();
}
}

View File

@ -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() {}
}

View File

@ -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;
} }

View File

@ -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();
}
}

View File

@ -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));

View File

@ -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);

View File

@ -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);
}); });
} }

View File

@ -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);

View File

@ -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);
}
}
} }

View File

@ -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;

View File

@ -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

Binary file not shown.