I have spent the past few weeks working to understand and implement my own alternative to TextureRenderer and though I think I’m nearly done, I’m struggling with mipmapping still. If I use GL_GENERATE_MIPMAP the quality in a zoomed out state for text is not crisp, so I endeavored to write my own mipmap generator to use BufferedImage scaling with bicubic interpolation to give a better appearance, but even with this it does not appear crisp:
http://captiveimagination.com/download/gearloop03.jpg
The relevant source code follows. I don’t know what else I can do to make the appearance better:
public void update(GL gl, BufferedImage image, int x, int y, int width, int height, int mipmap, Component component) {
if (image.getType() != BufferedImage.TYPE_INT_ARGB_PRE) {
throw new RuntimeException("Unhandled BufferedImage format. Use TYPE_INT_ARGB_PRE. Type: " + image.getType());
}
// Create ByteBuffer for pixel data
if ((this.width != width) || (this.height != height)) {
recreate = true;
}
int imageFormat = GL.GL_RGBA;
int textureFormat = GL.GL_BGRA;
int type = GL.GL_UNSIGNED_INT_8_8_8_8_REV;
gl.glBindTexture(GL.GL_TEXTURE_2D, textureId);
gl.glDisable(GL.GL_LIGHTING);
gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MAG_FILTER, GL.GL_LINEAR);
gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MIN_FILTER, mipmap != Shape.MIPMAP_OFF ? GL.GL_LINEAR_MIPMAP_LINEAR : GL.GL_LINEAR);
gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_WRAP_S, GL.GL_CLAMP_TO_EDGE);
gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_WRAP_T, GL.GL_CLAMP_TO_EDGE);
if (mipmap == Shape.MIPMAP_AUTO) {
gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_GENERATE_MIPMAP, GL.GL_TRUE);
}
gl.glPixelStorei(GL.GL_UNPACK_ALIGNMENT, 1);
int mipmaps = (int)Math.floor(Math.log(Math.max(width, height)) / Math.log(2)) + 1;
if (recreate) {
gl.glTexImage2D(GL.GL_TEXTURE_2D, 0, imageFormat, width, height, 0, textureFormat, type, null);
}
updateSubImage(gl, image, x, y, width, height, 0, imageFormat, textureFormat, type);
if (mipmap == Shape.MIPMAP_HIGH) {
ReusableGraphic rg = ReusableGraphic.get("mipmap");
int w = width;
int h = height;
for (int i = 1; i < mipmaps; i++) {
w = Math.max(1, w / 2);
h = Math.max(1, h / 2);
if (w == 0) {
w = 1;
} else if (h == 0) {
h = 1;
}
Graphics2D g = rg.request(w, h);
try {
component.rendering.setHints(g, component);
g.drawImage(image, 0, 0, w, h, x, y, width, height, null);
g.dispose();
if (recreate) {
gl.glTexImage2D(GL.GL_TEXTURE_2D, i, imageFormat, w,h, 0, textureFormat, type, null);
}
updateSubImage(gl, rg.getImage(), x, y, w, h, i, imageFormat, textureFormat, type);
} finally {
rg.release();
}
}
}
this.width = width;
this.height = height;
recreate = false;
}
private void updateSubImage(GL gl, BufferedImage image, int x, int y, int width, int height, int level, int imageFormat, int textureFormat, int type) {
// Create ByteBuffer for pixel data
int[] data = new int[width];
WritableRaster raster = image.getRaster();
ByteBuffer buffer = ByteBuffer.allocateDirect((width * height) * 4);
buffer.order(ByteOrder.nativeOrder());
IntBuffer pixels = buffer.asIntBuffer();
for (int i = 0; i < height; i++) {
raster.getDataElements(x, y + i, width, 1, data);
pixels.put(data);
}
pixels.flip();
gl.glTexSubImage2D(GL.GL_TEXTURE_2D, level, 0, 0, width, height, textureFormat, type, pixels);
}