diff --git a/android/core/src/processing/core/PImage.java b/android/core/src/processing/core/PImage.java index 1fd29b3bd..538bd1254 100644 --- a/android/core/src/processing/core/PImage.java +++ b/android/core/src/processing/core/PImage.java @@ -345,32 +345,22 @@ public class PImage implements PConstants, Cloneable { * Resize this image to a new width and height. * Use 0 for wide or high to make that dimension scale proportionally. */ - public void resize(int wide, int high) { // ignore - // Make sure that the pixels[] array is valid - loadPixels(); - - if (wide <= 0 && high <= 0) { - width = 0; // Gimme a break, don't waste my time - height = 0; - pixels = new int[0]; - bitmap = null; - - } else { - if (wide == 0) { // Use height to determine relative size - float diff = (float) high / (float) height; - wide = (int) (width * diff); - } else if (high == 0) { // Use the width to determine relative size - float diff = (float) wide / (float) width; - high = (int) (height * diff); - } - PImage temp = new PImage(wide, high, this.format); - temp.parent = parent; - temp.copy(this, 0, 0, width, height, 0, 0, wide, high); - this.width = wide; - this.height = high; - this.pixels = temp.pixels; - this.bitmap = null; + public void resize(int w, int h) { // ignore + if (w <= 0 && h <= 0) { + throw new IllegalArgumentException("width or height must be > 0 for resize"); } + + if (w == 0) { // Use height to determine relative size + float diff = (float) h / (float) height; + w = (int) (width * diff); + } else if (h == 0) { // Use the width to determine relative size + float diff = (float) w / (float) width; + h = (int) (height * diff); + } + bitmap = Bitmap.createScaledBitmap(bitmap, w, h, true); + this.width = w; + this.height = h; + // Mark the pixels array as altered updatePixels(); } diff --git a/core/src/processing/core/PImage.java b/core/src/processing/core/PImage.java index 127c41a91..160127199 100644 --- a/core/src/processing/core/PImage.java +++ b/core/src/processing/core/PImage.java @@ -24,13 +24,11 @@ package processing.core; -import java.awt.Image; +import java.awt.*; import java.awt.image.*; import java.io.*; import java.lang.reflect.Method; -import javax.imageio.ImageIO; - /** * ( begin auto-generated from PImage.xml ) @@ -125,6 +123,7 @@ public class PImage implements PConstants, Cloneable { // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . + /** for renderers that need to store info about the image */ //protected HashMap cacheMap; // protected WeakHashMap cacheMap; @@ -470,20 +469,20 @@ public class PImage implements PConstants, Cloneable { pixels = new int[width*height]; } - if (parent == null) return; - Object cache = parent.g.initCache(this); - if (cache != null) { - Method loadPixelsMethod = null; - try { - loadPixelsMethod = cache.getClass().getMethod("loadPixels", new Class[] { int[].class }); - } catch (Exception e) { - } - - if (loadPixelsMethod != null) { + if (parent != null) { + Object cache = parent.g.initCache(this); + if (cache != null) { + Method loadPixelsMethod = null; try { - loadPixelsMethod.invoke(cache, new Object[] { pixels }); - } catch (Exception e) { - e.printStackTrace(); + loadPixelsMethod = cache.getClass().getMethod("loadPixels", new Class[] { int[].class }); + } catch (Exception e) { } + + if (loadPixelsMethod != null) { + try { + loadPixelsMethod.invoke(cache, new Object[] { pixels }); + } catch (Exception e) { + e.printStackTrace(); + } } } } @@ -599,33 +598,96 @@ public class PImage implements PConstants, Cloneable { * @see PImage#get(int, int, int, int) */ public void resize(int w, int h) { // ignore - // Make sure that the pixels[] array is valid - loadPixels(); - if (w <= 0 && h <= 0) { - width = 0; // Gimme a break, don't waste my time - height = 0; - pixels = new int[0]; - - } else { - if (w == 0) { // Use height to determine relative size - float diff = (float) h / (float) height; - w = (int) (width * diff); - } else if (h == 0) { // Use the width to determine relative size - float diff = (float) w / (float) width; - h = (int) (height * diff); - } - PImage temp = new PImage(w, h, this.format); - temp.copy(this, 0, 0, width, height, 0, 0, w, h); - this.width = w; - this.height = h; - this.pixels = temp.pixels; + throw new IllegalArgumentException("width or height must be > 0 for resize"); } + + if (w == 0) { // Use height to determine relative size + float diff = (float) h / (float) height; + w = (int) (width * diff); + } else if (h == 0) { // Use the width to determine relative size + float diff = (float) w / (float) width; + h = (int) (height * diff); + } + + BufferedImage img = resizeImage((BufferedImage) getNative(), w, h); + PImage temp = new PImage(img); + + // Assume that w/h is the same as passed in + this.width = w; + this.height = h; + + // Get the resized pixel array + this.pixels = temp.pixels; + // Mark the pixels array as altered updatePixels(); } + // Adapted from getFasterScaledInstance() method from page 111 of + // "Filthy Rich Clients" by Chet Haase and Romain Guy + static private BufferedImage resizeImage(BufferedImage img, + int wide, int high) { + int type = (img.getTransparency() == Transparency.OPAQUE) ? + BufferedImage.TYPE_INT_RGB : BufferedImage.TYPE_INT_ARGB; + BufferedImage outgoing = img; + BufferedImage scratchImage = null; + Graphics2D g2 = null; + int prevW = outgoing.getWidth(); + int prevH = outgoing.getHeight(); + boolean isTranslucent = img.getTransparency() != Transparency.OPAQUE; + + // Use multi-step technique: start with original size, then scale down in + // multiple passes with drawImage() until the target size is reached + int w = img.getWidth(); + int h = img.getHeight(); + + do { + if (w > wide) { + w /= 2; + if (w < wide) { + w = wide; + } + } + if (h > high) { + h /= 2; + if (h < high) { + h = high; + } + } + if (scratchImage == null || isTranslucent) { + // Use a single scratch buffer for all iterations and then copy + // to the final, correctly-sized image before returning + scratchImage = new BufferedImage(w, h, type); + g2 = scratchImage.createGraphics(); + } + g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, + RenderingHints.VALUE_INTERPOLATION_BILINEAR); + g2.drawImage(outgoing, 0, 0, w, h, 0, 0, prevW, prevH, null); + prevW = w; + prevH = h; + outgoing = scratchImage; + } while (w != wide || h != high); + + if (g2 != null) { + g2.dispose(); + } + + // If we used a scratch buffer that is larger than our target size, + // create an image of the right size and copy the results into it + if (wide != outgoing.getWidth() || + high != outgoing.getHeight()) { + scratchImage = new BufferedImage(wide, high, type); + g2 = scratchImage.createGraphics(); + g2.drawImage(outgoing, 0, 0, null); + g2.dispose(); + outgoing = scratchImage; + } + return outgoing; + } + + ////////////////////////////////////////////////////////////// @@ -2999,7 +3061,7 @@ public class PImage implements PConstants, Cloneable { File file = new File(path); String extension = path.substring(path.lastIndexOf('.') + 1); - return ImageIO.write(bimage, extension, file); + return javax.imageio.ImageIO.write(bimage, extension, file); } catch (Exception e) { e.printStackTrace(); diff --git a/core/todo.txt b/core/todo.txt index d351dec72..1f0158f4e 100644 --- a/core/todo.txt +++ b/core/todo.txt @@ -37,6 +37,9 @@ X fix for PMatrix3D.mult() when vectors are identical X http://code.google.com/p/processing/issues/detail?id=921 A back-buffer sampler in OpenGL renderers A http://code.google.com/p/processing/issues/detail?id=1169 +X image resizing is ugly (just use java2d?) +o also deal with copy()/blend() inaccuracies +X http://code.google.com/p/processing/issues/detail?id=332 andres (cleanup) A when turning smoothing on, internal lines of shapes are visible @@ -248,8 +251,6 @@ _ createShape() with Java2D not ready to go _ loadShape() needs to live in PApplet -_ loadPixels() implementation needs to be in PApplet, not PGraphics - docs _ textureWrap() CLAMP and REPEAT now added @@ -263,10 +264,10 @@ _ also need to explain exception handling in general _ http://code.google.com/p/processing/issues/detail?id=183 2.0 FINAL / constants/hints -o bring PConstants back in line w/ previous 1.5 (can't renumber) +_ bring PConstants back in line w/ previous 1.5 (can't renumber) o consider enable("mipmaps") instead of hint(ENABLE_MIPMAPS) X clean up PConstants and move things into PGraphics that needn't be available -_ hint(DISABLE_LOADPIXELS) -> faster rendering in Java2D +_ hint(DISABLE_LOAD_PIXELS) -> faster rendering in Java2D _ getGLProfiles stuff.. can't do getGL2(), not good x-platform docs (2.0) @@ -564,6 +565,9 @@ _ don't grab pixels of java2d images unless asked _ this is the difference between a lot of loadPixels() and not _ so important to have it in before beta if that's the change _ http://code.google.com/p/processing/issues/detail?id=60 +_ loadPixels() implementation needs to be in PApplet, not PGraphics +_ this is a tricky thing to implement because of how OpenGL is handled + _ loadImage() should use the faster loading methods _ hint(DISABLE_IMAGE_CACHING) _ add a note to the loadImage() reference page @@ -578,9 +582,6 @@ _ http://code.google.com/p/processing/issues/detail?id=133 _ includes code for a slow but more accurate mode _ deprecate the blend() function -_ image resizing is ugly (just use java2d?) -_ also deal with copy()/blend() inaccuracies -_ http://code.google.com/p/processing/issues/detail?id=332 _ include option for nearest neighbor on resize _ http://code.google.com/p/processing/issues/detail?id=165 _ blend() and copy() are not pixel accurate for copy/scale