diff --git a/core/src/processing/opengl/Texture.java b/core/src/processing/opengl/Texture.java index 3f096b40a..40e6b6f10 100644 --- a/core/src/processing/opengl/Texture.java +++ b/core/src/processing/opengl/Texture.java @@ -24,9 +24,11 @@ package processing.opengl; import processing.core.PApplet; import processing.core.PConstants; import processing.core.PGraphics; + import java.lang.reflect.Method; import java.nio.ByteBuffer; import java.nio.IntBuffer; +import java.util.Arrays; import java.util.LinkedList; import java.util.NoSuchElementException; @@ -100,6 +102,10 @@ public class Texture implements PConstants { protected int[] rgbaPixels = null; protected IntBuffer pixelBuffer = null; + + protected int[] edgePixels = null; + protected IntBuffer edgeBuffer = null; + protected FrameBuffer tempFbo = null; protected int pixBufUpdateCount = 0; protected int rgbaPixUpdateCount = 0; @@ -340,82 +346,20 @@ public class Texture implements PConstants { } pgl.bindTexture(glTarget, glName); + loadPixels(w * h); + convertToRGBA(pixels, format, w, h); + updatePixelBuffer(rgbaPixels); + pgl.texSubImage2D(glTarget, 0, x, y, w, h, PGL.RGBA, PGL.UNSIGNED_BYTE, + pixelBuffer); + fillEdges(x, y, w, h); + if (usingMipmaps) { if (PGraphicsOpenGL.autoMipmapGenSupported) { - // Automatic mipmap generation. - loadPixels(w * h); - convertToRGBA(pixels, format, w, h); - updatePixelBuffer(rgbaPixels); - pgl.texSubImage2D(glTarget, 0, x, y, w, h, PGL.RGBA, PGL.UNSIGNED_BYTE, - pixelBuffer); pgl.generateMipmap(glTarget); } else { - // TODO: finish manual mipmap generation, replacing Bitmap with AWT's BufferedImage, - // making it work in npot textures (embed npot tex into larger pot tex?), subregions, - // and moving GLUtils.texImage2D (originally from Android SDK) into PGL. - // Actually, this whole code should go into PGL, so the Android implementation can - // use Bitmap, and desktop use BufferedImage. - - /* - if (w != width || h != height) { - System.err.println("Sorry but I don't know how to generate mipmaps for a subregion."); - return; - } - - // Code by Mike Miller obtained from here: - // http://insanitydesign.com/wp/2009/08/01/android-opengl-es-mipmaps/ - int w0 = glWidth; - int h0 = glHeight; - int[] argbPixels = new int[w0 * h0]; - convertToARGB(pixels, argbPixels, format); - int level = 0; - int denom = 1; - - // We create a Bitmap because then we use its built-in filtered downsampling - // functionality. - Bitmap bitmap = Bitmap.createBitmap(w0, h0, Config.ARGB_8888); - bitmap.setPixels(argbPixels, 0, w0, 0, 0, w0, h0); - - while (w0 >= 1 || h0 >= 1) { - //First of all, generate the texture from our bitmap and set it to the according level - GLUtils.texImage2D(glTarget, level, bitmap, 0); - - // We are done. - if (w0 == 1 && h0 == 1) { - break; - } - - // Increase the mipmap level - level++; - denom *= 2; - - // Downsampling bitmap. We must eventually arrive to the 1x1 level, - // and if the width and height are different, there will be a few 1D - // texture levels just before. - // This update formula also allows for NPOT resolutions. - w0 = PApplet.max(1, PApplet.floor((float)glWidth / denom)); - h0 = PApplet.max(1, PApplet.floor((float)glHeight / denom)); - // (see getScaledInstance in AWT Image) - Bitmap bitmap2 = Bitmap.createScaledBitmap(bitmap, w0, h0, true); - - // Clean up - bitmap.recycle(); - bitmap = bitmap2; - } - */ - - loadPixels(w * h); - convertToRGBA(pixels, format, w, h); - updatePixelBuffer(rgbaPixels); - pgl.texSubImage2D(glTarget, 0, x, y, w, h, PGL.RGBA, PGL.UNSIGNED_BYTE, - pixelBuffer); + // TODO: finish manual mipmap generation, + // https://github.com/processing/processing/issues/3335 } - } else { - loadPixels(w * h); - convertToRGBA(pixels, format, w, h); - updatePixelBuffer(rgbaPixels); - pgl.texSubImage2D(glTarget, 0, x, y, w, h, PGL.RGBA, PGL.UNSIGNED_BYTE, - pixelBuffer); } pgl.bindTexture(glTarget, 0); @@ -472,20 +416,18 @@ public class Texture implements PConstants { } pgl.bindTexture(glTarget, glName); + pgl.texSubImage2D(glTarget, 0, x, y, w, h, PGL.RGBA, PGL.UNSIGNED_BYTE, + pixBuf); + fillEdges(x, y, w, h); + if (usingMipmaps) { if (PGraphicsOpenGL.autoMipmapGenSupported) { - pgl.texSubImage2D(glTarget, 0, x, y, w, h, PGL.RGBA, PGL.UNSIGNED_BYTE, - pixBuf); pgl.generateMipmap(glTarget); } else { - pgl.texSubImage2D(glTarget, 0, x, y, w, h, PGL.RGBA, PGL.UNSIGNED_BYTE, - pixBuf); + // TODO: finish manual mipmap generation, + // https://github.com/processing/processing/issues/3335 } - } else { - pgl.texSubImage2D(glTarget, 0, x, y, w, h, PGL.RGBA, PGL.UNSIGNED_BYTE, - pixBuf); } - pgl.bindTexture(glTarget, 0); if (enabledTex) { pgl.disableTexturing(glTarget); @@ -1504,6 +1446,44 @@ public class Texture implements PConstants { } + protected void fillEdges(int x, int y, int w, int h) { + if ((width < glWidth || height < glHeight) && (x + w == width || y + h == height)) { + if (x + w == width) { + int ew = glWidth - width; + edgePixels = new int[h * ew]; + for (int i = 0; i < h; i++) { + int c = rgbaPixels[i * w + (w - 1)]; + Arrays.fill(edgePixels, i * ew, (i + 1) * ew, c); + } + edgeBuffer = PGL.updateIntBuffer(edgeBuffer, edgePixels, true); + pgl.texSubImage2D(glTarget, 0, width, y, ew, h, PGL.RGBA, + PGL.UNSIGNED_BYTE, edgeBuffer); + } + + if (y + h == height) { + int eh = glHeight - height; + edgePixels = new int[eh * w]; + for (int i = 0; i < eh; i++) { + System.arraycopy(rgbaPixels, (h - 1) * w, edgePixels, i * w, w); + } + edgeBuffer = PGL.updateIntBuffer(edgeBuffer, edgePixels, true); + pgl.texSubImage2D(glTarget, 0, x, height, w, eh, PGL.RGBA, + PGL.UNSIGNED_BYTE, edgeBuffer); + } + + if (x + w == width && y + h == height) { + int ew = glWidth - width; + int eh = glHeight - height; + int c = rgbaPixels[w * h - 1]; + edgePixels = new int[eh * ew]; + Arrays.fill(edgePixels, 0, eh * ew, c); + edgeBuffer = PGL.updateIntBuffer(edgeBuffer, edgePixels, true); + pgl.texSubImage2D(glTarget, 0, width, height, ew, eh, PGL.RGBA, + PGL.UNSIGNED_BYTE, edgeBuffer); + } + } + } + /////////////////////////////////////////////////////////////////////////// // Parameters object