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

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

Binary file not shown.