diff --git a/java/libraries/opengl2/src/processing/opengl2/PFontTexture.java b/java/libraries/opengl2/src/processing/opengl2/PFontTexture.java index 59660d3d8..fcd4e3cca 100644 --- a/java/libraries/opengl2/src/processing/opengl2/PFontTexture.java +++ b/java/libraries/opengl2/src/processing/opengl2/PFontTexture.java @@ -98,7 +98,10 @@ class PFontTexture implements PConstants { resize = false; } - PTexture tex = new PTexture(parent, w, h, new PTexture.Parameters(ARGB, BILINEAR)); + PTexture.Parameters par = new PTexture.Parameters(ARGB, BILINEAR); + par.wrapU = CLAMP; + par.wrapV = CLAMP; + PTexture tex = new PTexture(parent, w, h, par); if (textures == null) { textures = new PTexture[1]; @@ -180,33 +183,54 @@ class PFontTexture implements PConstants { // Adds this glyph to the opengl texture in PFont. - protected void addToTexture(int idx, PFont.Glyph glyph) { - // Converting the pixels array from the PImage into a valid RGBA array for OpenGL. - int[] rgba = new int[glyph.width * glyph.height]; + protected void addToTexture(int idx, PFont.Glyph glyph) { + // We add one pixel to avoid issues when sampling the font texture at fractional + // screen positions. I.e.: the pixel on the screen only contains half of the + // font rectangle, so it would sample half of the color from the glyph + // area in the texture, and the other half from the contiguous pixel. If the + // later contains a portion of the neighbor glyph and former doesn't, this + // would result in a shaded pixel when the correct output is blank. + // This is a consequence of putting all the glyphs in a common texture with + // bilinear sampling. + int w = 1 + glyph.width + 1; + int h = 1 + glyph.height + 1; + + // Converting the pixels array from the PImage into a valid RGBA array for OpenGL. + int[] rgba = new int[w * h]; int t = 0; int p = 0; - if (PGraphicsOpenGL2.BIG_ENDIAN) { + if (PGraphicsOpenGL2.BIG_ENDIAN) { + java.util.Arrays.fill(rgba, 0, w, 0xFFFFFF00); // Set the first row to blank pixels. + t = w; for (int y = 0; y < glyph.height; y++) { + rgba[t++] = 0xFFFFFF00; // Set the leftmost pixel in this row as blank for (int x = 0; x < glyph.width; x++) { rgba[t++] = 0xFFFFFF00 | glyph.image.pixels[p++]; - } + } + rgba[t++] = 0xFFFFFF00; // Set the rightmost pixel in this row as blank } + java.util.Arrays.fill(rgba, (h - 1) * w, h * w, 0xFFFFFF00); // Set the last row to blank pixels. } else { + java.util.Arrays.fill(rgba, 0, w, 0x00FFFFFF); // Set the first row to blank pixels. + t = w; for (int y = 0; y < glyph.height; y++) { + rgba[t++] = 0x00FFFFFF; // Set the leftmost pixel in this row as blank for (int x = 0; x < glyph.width; x++) { rgba[t++] = (glyph.image.pixels[p++] << 24) | 0x00FFFFFF; } + rgba[t++] = 0x00FFFFFF; // Set the rightmost pixel in this row as blank } + java.util.Arrays.fill(rgba, (h - 1) * w, h * w, 0x00FFFFFF); // Set the last row to blank pixels. } // Is there room for this glyph in the current line? - if (offsetX + glyph.width> textures[currentTex].glWidth) { + if (offsetX + w > textures[currentTex].glWidth) { // No room, go to the next line: offsetX = 0; offsetY += lineHeight; lineHeight = 0; } - lineHeight = Math.max(lineHeight, glyph.height); + lineHeight = Math.max(lineHeight, h); boolean resized = false; if (offsetY + lineHeight > textures[currentTex].glHeight) { @@ -237,10 +261,10 @@ class PFontTexture implements PConstants { setTexture(lastTex); } - textures[currentTex].setTexels(offsetX, offsetY, glyph.width, glyph.height, rgba); + textures[currentTex].setTexels(offsetX, offsetY, w, h, rgba); - TextureInfo tinfo = new TextureInfo(currentTex, offsetX, offsetY, glyph.width, glyph.height); - offsetX += glyph.width; + TextureInfo tinfo = new TextureInfo(currentTex, offsetX, offsetY, w, h); + offsetX += w; if (idx == glyphTexinfos.length) { TextureInfo[] temp = new TextureInfo[glyphTexinfos.length + 1]; @@ -262,18 +286,16 @@ class PFontTexture implements PConstants { public float v0, v1; public TextureInfo(int tidx, int cropX, int cropY, int cropW, int cropH) { - texIndex = tidx; - width = textures[tidx].glWidth; - height = textures[tidx].glHeight; + texIndex = tidx; crop = new int[4]; - crop[0] = cropX; - crop[1] = cropY + cropH; - crop[2] = cropW; - crop[3] = -cropH; - u0 = (float)cropX / (float)width; - u1 = u0 + (float)cropW / (float)width; - v0 = (float)cropY / (float)height; - v1 = v0 + (float)cropH / (float)height; + // The region of the texture corresponding to the glyph is surrounded by a + // 1-pixel wide border to avoid artifacts due to bilinear sampling. This is + // why the additions and subtractions to the crop values. + crop[0] = cropX + 1; + crop[1] = cropY + 1 + cropH - 2; + crop[2] = cropW - 2; + crop[3] = -cropH + 2; + updateUV(); } void updateUV() { diff --git a/java/libraries/opengl2/src/processing/opengl2/PGraphicsOpenGL2.java b/java/libraries/opengl2/src/processing/opengl2/PGraphicsOpenGL2.java index de33af568..31fceb6c4 100644 --- a/java/libraries/opengl2/src/processing/opengl2/PGraphicsOpenGL2.java +++ b/java/libraries/opengl2/src/processing/opengl2/PGraphicsOpenGL2.java @@ -1294,6 +1294,7 @@ public class PGraphicsOpenGL2 extends PGraphics { if (opengl2X) { if (primarySurface) { releaseContext(); + // TODO: we need to recreate resources here...? context.destroy(); context = null; allocate(); @@ -1310,6 +1311,7 @@ public class PGraphicsOpenGL2 extends PGraphics { if (!opengl4X) { if (primarySurface) { releaseContext(); + // TODO: we need to recreate resources here...? context.destroy(); context = null; allocate(); @@ -3403,7 +3405,7 @@ public class PGraphicsOpenGL2 extends PGraphics { // protected void textLineAlignImpl(char buffer[], int start, int stop, // float x, float y) - + /** * Implementation of actual drawing for a line of text. */ @@ -3474,7 +3476,7 @@ public class PGraphicsOpenGL2 extends PGraphics { tinfo = textTex.addToTexture(glyph); } - if (textMode == MODEL) { + if (textMode == MODEL) { float high = glyph.height / (float) textFont.getSize(); float bwidth = glyph.width / (float) textFont.getSize(); float lextent = glyph.leftExtent / (float) textFont.getSize(); @@ -3486,7 +3488,6 @@ public class PGraphicsOpenGL2 extends PGraphics { float y2 = y1 + high * textSize; textCharModelImpl(tinfo, x1, y1, x2, y2); - } else if (textMode == SCREEN) { int xx = (int) x + glyph.leftExtent; int yy = (int) y - glyph.topExtent; @@ -6773,7 +6774,9 @@ public class PGraphicsOpenGL2 extends PGraphics { } else if (hints[ENABLE_OPENGL_4X_SMOOTH]) { capabilities.setSampleBuffers(true); capabilities.setNumSamples(4); - } + } else { + capabilities.setSampleBuffers(false); + } // Getting the native window: // http://www.java-gaming.org/index.php/topic,21559.0.html