changeset 1090:3440e9a39f0a

Fix RT-17409: Image Ops to enable pixel access to Image nodes. Code reviewed by Kevin, API approved by Rich.
author flar <James.Graham@oracle.com>
date Thu, 17 May 2012 16:15:50 -0700
parents 19d05a48c978
children 45a06f0a889f
files javafx-ui-common/src/com/sun/javafx/tk/DummyToolkit.java javafx-ui-common/src/com/sun/javafx/tk/ImageLoader.java javafx-ui-common/src/com/sun/javafx/tk/PlatformImage.java javafx-ui-common/src/com/sun/javafx/tk/Toolkit.java javafx-ui-common/src/javafx/scene/image/Image.java javafx-ui-common/src/javafx/scene/image/PixelFormat.java javafx-ui-common/src/javafx/scene/image/PixelReader.java javafx-ui-common/src/javafx/scene/image/PixelWriter.java javafx-ui-common/src/javafx/scene/image/WritableImage.java javafx-ui-common/src/javafx/scene/image/WritablePixelFormat.java test-stub-toolkit/src/com/sun/javafx/pgstub/StubImageLoader.java test-stub-toolkit/src/com/sun/javafx/pgstub/StubImageLoaderFactory.java test-stub-toolkit/src/com/sun/javafx/pgstub/StubPlatformImage.java test-stub-toolkit/src/com/sun/javafx/pgstub/StubToolkit.java
diffstat 14 files changed, 1372 insertions(+), 22 deletions(-) [+]
line wrap: on
line diff
--- a/javafx-ui-common/src/com/sun/javafx/tk/DummyToolkit.java	Thu May 17 17:30:50 2012 +0200
+++ b/javafx-ui-common/src/com/sun/javafx/tk/DummyToolkit.java	Thu May 17 16:15:50 2012 -0700
@@ -156,6 +156,11 @@
     }
 
     @Override
+    public PlatformImage createPlatformImage(int w, int h) {
+        throw new UnsupportedOperationException("Not supported yet.");
+    }
+
+    @Override
     public void startup(Runnable runnable) {
         throw new UnsupportedOperationException("Not supported yet.");
     }
--- a/javafx-ui-common/src/com/sun/javafx/tk/ImageLoader.java	Thu May 17 17:30:50 2012 +0200
+++ b/javafx-ui-common/src/com/sun/javafx/tk/ImageLoader.java	Thu May 17 16:15:50 2012 -0700
@@ -29,8 +29,8 @@
 public interface ImageLoader {
     public boolean getError();
     public int getFrameCount();
-    public Object[] getFrames();
-    public Object getFrame(int index);
+    public PlatformImage[] getFrames();
+    public PlatformImage getFrame(int index);
     public int getFrameDelay(int index);
     public int getWidth();
     public int getHeight();
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/javafx-ui-common/src/com/sun/javafx/tk/PlatformImage.java	Thu May 17 16:15:50 2012 -0700
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package com.sun.javafx.tk;
+
+import java.nio.Buffer;
+import javafx.scene.image.PixelFormat;
+import javafx.scene.image.WritablePixelFormat;
+
+/**
+ * Common interface to all toolkit-specific objects used to store image
+ * data for a javafx.image.Image object.
+ */
+public interface PlatformImage {
+    /**
+     * @param x X coordinate of pixel
+     * @param y Y coordinate of pixel
+     * @return the non-premultiplied pixel in integer ARGB component ordering.
+     */
+    public int getArgb(int x, int y);
+
+    /**
+     * @param x X coordinate of pixel
+     * @param y Y coordinate of pixel
+     * @param argbpre non-premultiplied pixel data to store in integer ARGB
+     *  component ordering
+     */
+    public void setArgb(int x, int y, int argb);
+
+    /**
+     * @param x X coordinate of pixel
+     * @param y Y coordinate of pixel
+     * @return the premultiplied pixel in integer ARGB component ordering.
+     */
+    public int getArgbPre(int x, int y);
+
+    /**
+     * @param x X coordinate of pixel
+     * @param y Y coordinate of pixel
+     * @param argbpre premultiplied pixel data to store in integer ARGB
+     *  component ordering
+     */
+    public void setArgbPre(int x, int y, int argbpre);
+
+    /**
+     * @return the PixelFormat to use for the pixel data transfer methods
+     * getPixels() and setPixels().
+     */
+    public PixelFormat getPlatformPixelFormat();
+
+    public boolean isWritable();
+    public PlatformImage promoteToWritableImage();
+
+    public <T extends Buffer>
+        void getPixels(int x, int y, int w, int h,
+                       T pixels, WritablePixelFormat<T> pixelformat,
+                       int scanlineBytes);
+    
+    public <T extends Buffer>
+        void setPixels(int x, int y, int w, int h,
+                       T pixels, PixelFormat<T> pixelformat,
+                       int scanlineBytes);
+}
--- a/javafx-ui-common/src/com/sun/javafx/tk/Toolkit.java	Thu May 17 17:30:50 2012 +0200
+++ b/javafx-ui-common/src/com/sun/javafx/tk/Toolkit.java	Thu May 17 16:15:50 2012 -0700
@@ -498,6 +498,8 @@
     
     public abstract ImageLoader loadPlatformImage(Object platformImage);
 
+    public abstract PlatformImage createPlatformImage(int w, int h);
+
     // Indicates the default state of smooth for ImageView and MediaView
     // Subclasses may override this to provide a platform-specific default
     public boolean getDefaultImageSmooth() { return true; }
--- a/javafx-ui-common/src/javafx/scene/image/Image.java	Thu May 17 17:30:50 2012 +0200
+++ b/javafx-ui-common/src/javafx/scene/image/Image.java	Thu May 17 16:15:50 2012 -0700
@@ -42,14 +42,19 @@
 import com.sun.javafx.runtime.async.AsyncOperation;
 import com.sun.javafx.runtime.async.AsyncOperationListener;
 import com.sun.javafx.tk.ImageLoader;
+import com.sun.javafx.tk.PlatformImage;
 import com.sun.javafx.tk.Toolkit;
 import java.net.MalformedURLException;
 import java.net.URL;
+import java.nio.Buffer;
+import java.nio.ByteBuffer;
+import java.nio.IntBuffer;
 import javafx.beans.property.ReadOnlyBooleanProperty;
 import javafx.beans.property.ReadOnlyBooleanWrapper;
 import javafx.beans.property.ReadOnlyDoubleProperty;
 import javafx.beans.property.ReadOnlyDoubleWrapper;
 import javafx.beans.property.ReadOnlyObjectProperty;
+import javafx.scene.paint.Color;
 
 /**
  * The {@code Image} class represents graphical images and is used for loading
@@ -444,7 +449,7 @@
      * @treatAsPrivate implementation detail
      * @deprecated This is an internal API that is not intended for use and will be removed in the next version
      */
-    private ObjectPropertyImpl<Object> platformImage;
+    private ObjectPropertyImpl<PlatformImage> platformImage;
 
     /**
      * @treatAsPrivate implementation detail
@@ -461,18 +466,22 @@
      * @treatAsPrivate implementation detail
      */
     @Deprecated
-    public final ReadOnlyObjectProperty<Object> impl_platformImageProperty() {
+    public final ReadOnlyObjectProperty<PlatformImage> impl_platformImageProperty() {
         return platformImagePropertyImpl();
     }
 
-    private ObjectPropertyImpl<Object> platformImagePropertyImpl() {
+    private ObjectPropertyImpl<PlatformImage> platformImagePropertyImpl() {
         if (platformImage == null) {
-            platformImage = new ObjectPropertyImpl<Object>("platformImage");
+            platformImage = new ObjectPropertyImpl<PlatformImage>("platformImage");
         }
 
         return platformImage;
     }
 
+    void pixelsDirty() {
+        platformImagePropertyImpl().fireValueChangedEvent();
+    }
+
     private final class ObjectPropertyImpl<T>
             extends ReadOnlyObjectPropertyBase<T> {
         private final String name;
@@ -637,9 +646,25 @@
         initialize(null);
     }
 
-    private Image(Object platformImage) {
+    /**
+     * Construct a new empty {@code Image} with the specified dimensions
+     * filled with transparent pixels to be used with the
+     * {@link #setArgb(int, int, int) setArgb()}
+     * and
+     * {@link #setColor(int, int, javafx.scene.paint.Color) setColor()}
+     * methods to create a completely custom image.
+     * 
+     * @param width the width of the empty image
+     * @param height the height of the empty image
+     */
+    Image(int width, int height) {
+        this(null, null, width, height, false, false, false);
+        initialize(Toolkit.getToolkit().createPlatformImage(width, height));
+    }
+
+    private Image(Object externalImage) {
         this(null, null, 0, 0, false, false, false);
-        initialize(platformImage);
+        initialize(externalImage);
     }
 
     private Image(String url, InputStream is,
@@ -679,13 +704,13 @@
 
     private ImageTask backgroundTask;
 
-    private void initialize(Object platformImage) {
+    private void initialize(Object externalImage) {
         // we need to check the original values here, because setting placeholder
         // changes platformImage, so wrong branch of if would be used
-        if (platformImage != null) {
+        if (externalImage != null) {
             // Make an image from the provided platform-specific image
             // object (e.g. a BufferedImage in the case of the Swing profile)
-            ImageLoader loader = loadPlatformImage(platformImage);
+            ImageLoader loader = loadPlatformImage(externalImage);
             finishImage(loader);
         } else if (isBackgroundLoading() && (impl_source == null)) {
             // Load image in the background.
@@ -727,7 +752,7 @@
     // Generates the animation Timeline for multiframe images.
     private void makeAnimationTimeline(ImageLoader loader) {
         // create and start the animation thread.
-        final Object[] frames = loader.getFrames();
+        final PlatformImage[] frames = loader.getFrames();
 
         timeline = new Timeline();
         timeline.setCycleCount(Timeline.INDEFINITE);
@@ -750,7 +775,7 @@
 
     private KeyFrame createPlatformImageSetKeyFrame(
             final long duration,
-            final Object platformImage) {
+            final PlatformImage platformImage) {
         return new KeyFrame(Duration.millis(duration),
                             new EventHandler<ActionEvent>() {
                                     @Override
@@ -806,7 +831,7 @@
         return new Image(image);
     }
 
-    private void setPlatformImageWH(final Object newPlatformImage,
+    private void setPlatformImageWH(final PlatformImage newPlatformImage,
                                     final double newWidth,
                                     final double newHeight) {
         if ((impl_getPlatformImage() == newPlatformImage)
@@ -834,7 +859,7 @@
         }
     }
 
-    private void storePlatformImageWH(final Object platformImage,
+    private void storePlatformImageWH(final PlatformImage platformImage,
                                       final double width,
                                       final double height) {
         platformImagePropertyImpl().store(platformImage);
@@ -1043,4 +1068,107 @@
     boolean isAnimation() {
         return timeline != null;
     }
+
+    boolean pixelsReadable() {
+        return (getProgress() >= 1.0 && !isAnimation() && !isError());
+    }
+
+    private PixelReader reader;
+    /**
+     * This method returns a {@code PixelReader} that provides access to
+     * read the pixels of the image, if the image is readable.
+     * If this method returns null then this image does not support reading
+     * at this time.
+     * This method will return null if the image is being loaded from a
+     * source and is still incomplete {the progress is still < 1.0) or if
+     * there was an error.
+     * This method may also return null for some images in a format that
+     * is not supported for reading and writing pixels to.
+     * 
+     * @return the {@code PixelReader} for reading the pixel data of the image
+     */
+    public PixelReader getPixelReader() {
+        if (!pixelsReadable()) {
+            return null;
+        }
+        if (reader == null) {
+            reader = new PixelReader() {
+                @Override
+                public PixelFormat getPixelFormat() {
+                    PlatformImage pimg = platformImage.get();
+//                    if (pimg == null) {
+//                        return null;
+//                    }
+                    return pimg.getPlatformPixelFormat();
+                }
+
+                @Override
+                public int getArgb(int x, int y) {
+                    PlatformImage pimg = platformImage.get();
+                    return pimg.getArgb(x, y);
+                }
+
+                @Override
+                public Color getColor(int x, int y) {
+                    int argb = getArgb(x, y);
+                    int a = argb >>> 24;
+                    int r = (argb >> 16) & 0xff;
+                    int g = (argb >>  8) & 0xff;
+                    int b = (argb      ) & 0xff;
+                    return Color.rgb(r, g, b, a / 255.0);
+                }
+
+                @Override
+                public <T extends Buffer>
+                    void getPixels(int x, int y, int w, int h,
+                                   WritablePixelFormat<T> pixelformat,
+                                   T buffer, int scanlineStride)
+                {
+                    PlatformImage pimg = platformImage.get();
+                    for (int j = 0; j < h; j++) {
+                        for (int i = 0; i < w; i++) {
+                            pixelformat.setArgb(buffer, i, j, scanlineStride,
+                                                pimg.getArgb(x+i, y+j));
+                        }
+                    }
+//                    checkPixelAccess(true, false).getPixels(x, y, w, h,
+//                                                            buffer, pixelformat, scanlineStride);
+                }
+
+                @Override
+                public void getPixels(int x, int y, int w, int h,
+                                    WritablePixelFormat<ByteBuffer> pixelformat,
+                                    byte buffer[], int offset, int scanlineStride)
+                {
+                    ByteBuffer bytebuf = ByteBuffer.wrap(buffer);
+                    bytebuf.position(offset);
+                    getPixels(x, y, w, h, pixelformat, bytebuf, scanlineStride);
+//                    checkPixelAccess(true, false).getPixels(x, y, w, h,
+//                                                            buffer, pixelformat, scanlineStride);
+                }
+
+                @Override
+                public void getPixels(int x, int y, int w, int h,
+                                    WritablePixelFormat<IntBuffer> pixelformat,
+                                    int buffer[], int offset, int scanlineStride)
+                {
+                    IntBuffer intbuf = IntBuffer.wrap(buffer);
+                    intbuf.position(offset);
+                    getPixels(x, y, w, h, pixelformat, intbuf, scanlineStride);
+//                    checkPixelAccess(true, false).getPixels(x, y, w, h,
+//                                                            buffer, pixelformat, scanlineStride);
+                }
+            };
+        }
+        return reader;
+    }
+
+    PlatformImage getWritablePlatformImage() {
+        PlatformImage pimg = platformImage.get();
+        if (!pimg.isWritable()) {
+            pimg = pimg.promoteToWritableImage();
+            platformImage.set(pimg);
+        }
+        return pimg;
+    }
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/javafx-ui-common/src/javafx/scene/image/PixelFormat.java	Thu May 17 16:15:50 2012 -0700
@@ -0,0 +1,261 @@
+/*
+ * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package javafx.scene.image;
+
+import java.nio.Buffer;
+import java.nio.ByteBuffer;
+import java.nio.IntBuffer;
+import java.util.Arrays;
+
+/**
+ * A {@code PixelFormat} object defines the layout of data for a pixel of
+ * a given format.
+ */
+public abstract class PixelFormat<T extends Buffer> {
+    public enum Type {
+        /**
+         * The pixels are stored in 32-bit integers with the premultiplied
+         * components stored in order, from MSb to LSb:
+         * alpha, red, green, blue.
+         */
+        INT_ARGB_PRE,
+        /**
+         * The pixels are stored in 32-bit integers with the non-premultiplied
+         * components stored in order, from MSb to LSb:
+         * alpha, red, green, blue.
+         */
+        INT_ARGB,
+        /**
+         * The pixels are stored in adjacent bytes with the premultiplied
+         * components stored in order of increasing index:
+         * blue, green, red, alpha.
+         */
+        BYTE_BGRA_PRE,
+        /**
+         * The pixels are stored in adjacent bytes with the non-premultiplied
+         * components stored in order of increasing index:
+         * blue, green, red, alpha.
+         */
+        BYTE_BGRA,
+        /**
+         * The opaque pixels are stored in adjacent bytes with the color
+         * components stored in order of increasing index:
+         * red, gree, blue.
+         */
+        BYTE_RGB,
+        /**
+         * The pixel colors are referenced by byte indices stored in the
+         * pixel array, with the byte interpreted as an unsigned index into
+         * a list of colors provided by the {@code PixelFormat} object.
+         */
+        BYTE_INDEXED,
+    }
+
+    private Type type;
+
+    PixelFormat(Type type) {
+        this.type = type;
+    }
+
+    public static WritablePixelFormat<IntBuffer> getIntArgbInstance() {
+        return WritablePixelFormat.IntArgb.INSTANCE;
+    }
+
+    public static WritablePixelFormat<IntBuffer> getIntArgbPreInstance() {
+        return WritablePixelFormat.IntArgbPre.INSTANCE;
+    }
+
+    public static WritablePixelFormat<ByteBuffer> getByteBgraInstance() {
+        return WritablePixelFormat.ByteBgra.INSTANCE;
+    }
+
+    public static WritablePixelFormat<ByteBuffer> getByteBgraPreInstance() {
+        return WritablePixelFormat.ByteBgraPre.INSTANCE;
+    }
+
+    public static PixelFormat<ByteBuffer> getByteRgbInstance() {
+        return ByteRgb.instance;
+    }
+
+    public static PixelFormat<ByteBuffer>
+        createByteIndexedPremultipliedInstance(int colors[])
+    {
+        return IndexedPixelFormat.createByte(colors, true);
+    }
+
+    public static PixelFormat<ByteBuffer>
+        createByteIndexedInstance(int colors[])
+    {
+        return IndexedPixelFormat.createByte(colors, false);
+    }
+
+    public Type getType() {
+        return type;
+    }
+
+    public abstract boolean isWritable();
+
+    public abstract boolean isPremultiplied();
+
+    static int NonPretoPre(int nonpre) {
+        int a = nonpre >>> 24;
+        if (a == 0xff) return nonpre;
+        if (a == 0x00) return 0;
+        int r = (nonpre >> 16) & 0xff;
+        int g = (nonpre >>  8) & 0xff;
+        int b = (nonpre      ) & 0xff;
+        r = (r * a) / 0xff;
+        g = (g * a) / 0xff;
+        b = (b * a) / 0xff;
+        return (a << 24) | (r << 16) | (g << 8) | b;
+    }
+
+    static int PretoNonPre(int pre) {
+        int a = pre >>> 24;
+        if (a == 0xff || a == 0x00) return pre;
+        int r = (pre >> 16) & 0xff;
+        int g = (pre >>  8) & 0xff;
+        int b = (pre      ) & 0xff;
+        int halfa = a >> 1;
+        r = (r >= a) ? 0xff : (r * 0xff + halfa) / a;
+        g = (g >= a) ? 0xff : (g * 0xff + halfa) / a;
+        b = (b >= a) ? 0xff : (b * 0xff + halfa) / a;
+        return (a << 24) | (r << 16) | (g << 8) | b;
+    }
+
+    /**
+     * Reads a 32-bit integer representation of the color from the buffer
+     * at the specified coordinates.
+     * The 32-bit integer will contain the 4 color components in separate
+     * 8-bit fields in ARGB order from the most significant byte to the least
+     * significant byte.
+     * The buffer should be positioned to the start of the pixel data such
+     * that {@code buf.get(0)} would return the pixel information for the
+     * pixel at coordinates {@code (0, 0)}.
+     * The {@scanlineStride} parameter defines the distance from the pixel
+     * data at the start of one row to the pixel data at the start of the
+     * immediately following row at the next higher Y coordinate.  Usually,
+     * {@scanlineStride} is the same as the width of the image multiplied
+     * by the number of data elements per pixel (1 for the case of the
+     * integer and indexed formats, or 3 or 4 in the case of the byte
+     * formats), but some images may have further padding between rows for
+     * alignment or other purposes.
+     * 
+     * @param buf the buffer of pixel data
+     * @param x the X coordinate of the pixel to be read
+     * @param y the Y coordinate of the pixel to be read
+     * @param scanlineStride the number of buffer elements between the
+     *        start of adjacent pixel rows in the buffer
+     * @return a 32-bit value with the color of the pixel in a format
+     *         similar to the {@code Type.INT_ARGB} pixel format
+     */
+    public abstract int getArgb(T buf, int x, int y, int scanlineStride);
+
+    static class ByteRgb extends PixelFormat<ByteBuffer> {
+        static final ByteRgb instance = new ByteRgb();
+
+        private ByteRgb() {
+            super(Type.BYTE_RGB);
+        }
+
+        @Override
+        public boolean isWritable() {
+            return true;
+        }
+
+        @Override
+        public boolean isPremultiplied() {
+            return false;
+        }
+
+        @Override
+        public int getArgb(ByteBuffer buf, int x, int y, int scanlineStride) {
+            int index = y * scanlineStride + x * 4;
+            int r = buf.get(index    ) & 0xff;
+            int g = buf.get(index + 1) & 0xff;
+            int b = buf.get(index + 2) & 0xff;
+            return (0xff << 24) | (r << 16) | (g << 8) | b;
+        }
+    }
+
+    static class IndexedPixelFormat extends PixelFormat<ByteBuffer> {
+        int precolors[];
+        int nonprecolors[];
+        boolean premult;
+
+        static PixelFormat createByte(int colors[], boolean premult) {
+            return new IndexedPixelFormat(Type.BYTE_INDEXED, premult,
+                                          Arrays.copyOf(colors, 256));
+        }
+
+        private IndexedPixelFormat(Type type, boolean premult, int colors[]) {
+            super(type);
+            if (premult) {
+                this.precolors = colors;
+            } else {
+                this.nonprecolors = colors;
+            }
+            this.premult = premult;
+        }
+
+        @Override
+        public boolean isWritable() {
+            return false;
+        }
+
+        @Override
+        public boolean isPremultiplied() {
+            return premult;
+        }
+
+        private int[] getPreColors() {
+            if (precolors == null) {
+                int colors[] = new int[nonprecolors.length];
+                for (int i = 0; i < colors.length; i++) {
+                    colors[i] = NonPretoPre(nonprecolors[i]);
+                }
+                this.precolors = colors;
+            }
+            return precolors;
+        }
+
+        private int[] getNonPreColors() {
+            if (nonprecolors == null) {
+                int colors[] = new int[precolors.length];
+                for (int i = 0; i < colors.length; i++) {
+                    colors[i] = PretoNonPre(precolors[i]);
+                }
+                this.nonprecolors = colors;
+            }
+            return nonprecolors;
+        }
+
+        @Override
+        public int getArgb(ByteBuffer buf, int x, int y, int scanlineStride) {
+            return getNonPreColors()[buf.get(y * scanlineStride + x) & 0xff];
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/javafx-ui-common/src/javafx/scene/image/PixelReader.java	Thu May 17 16:15:50 2012 -0700
@@ -0,0 +1,173 @@
+/*
+ * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package javafx.scene.image;
+
+import java.nio.Buffer;
+import java.nio.ByteBuffer;
+import java.nio.IntBuffer;
+import javafx.scene.paint.Color;
+
+/**
+ * This interface defines methods for retrieving the pixel data from an
+ * {@link Image} or other surface containing pixels.
+ */
+public interface PixelReader {
+    /**
+     * This method returns the {@code PixelFormat} in which the surface
+     * stores its pixels, or a roughly equivalent pixel format into which
+     * it can easily convert its pixels for purposes of reading them.
+     * 
+     * @return the {@code PixelFormat} that best describes the underlying
+     *         pixels
+     */
+    public PixelFormat getPixelFormat();
+
+    /**
+     * Reads a 32-bit integer representation of the color of a pixel
+     * from the specified coordinates in the surface.
+     * The 32-bit integer will contain the 4 color components in separate
+     * 8-bit fields in ARGB order from the most significant byte to the least
+     * significant byte.
+     * 
+     * @param x the X coordinate of the pixel color to read
+     * @param y the Y coordinate of the pixel color to read
+     * @return a 32-bit representation of the color in a format described
+     *         by the {@link PixelFormat.Type.BYTE_ARGB} PixelFormat type.
+     */
+    public int getArgb(int x, int y);
+
+    /**
+     * Reads the color of a pixel from the specified coordinates in the
+     * surface and returns the value as a {@link Color} object.
+     * 
+     * @param x the X coordinate of the pixel color to read
+     * @param y the Y coordinate of the pixel color to read
+     * @return the Color object representing the color of the indicated
+     *         pixel
+     */
+    public Color getColor(int x, int y);
+
+    /**
+     * Reads pixel data from a rectangular region of the surface into the
+     * specified buffer.
+     * The format to be used for pixels in the buffer is defined by the
+     * {@link PixelFormat} object and pixel format conversions will be
+     * performed as needed to store the data in the indicated format.
+     * The buffer is assumed to be positioned to the location where the
+     * first pixel data from the image pixel at location {@code (x, y)}
+     * will be stored.
+     * Pixel data for a row will be stored in adjacent locations within
+     * the buffer packed as tightly as possible for increasing X
+     * coordinates.
+     * Pixel data for adjacent rows will be stored offset from each other
+     * by the number of buffer data elements defined by
+     * {@code scanlineStride}.
+     * 
+     * @param x the X coordinate of the rectangular region to read
+     * @param y the Y coordinate of the rectangular region to read
+     * @param w the width of the rectangular region to read
+     * @param h the height of the rectangular region to read
+     * @param pixelformat the {@code PixelFormat} object defining the format
+     *        to store the pixels into buffer
+     * @param buffer a buffer of a type appropriate for the indicated
+     *        {@code PixelFormat} object
+     * @param scanlineStride the distance between the pixel data for the
+     *        start of one row of data in the buffer to the start of the
+     *        next row of data.
+     */
+    public <T extends Buffer>
+        void getPixels(int x, int y, int w, int h,
+                       WritablePixelFormat<T> pixelformat,
+                       T buffer, int scanlineStride);
+
+    /**
+     * Reads pixel data from a rectangular region of the surface into the
+     * specified byte array.
+     * The format to be used for pixels in the buffer is defined by the
+     * {@link PixelFormat} object and pixel format conversions will be
+     * performed as needed to store the data in the indicated format.
+     * The {@code pixelformat} must be a compatible
+     * {@code PixelFormat<ByteBuffer>} type.
+     * The data for the first pixel at location {@code (x, y)} will be
+     * read into the array index specified by the {@code offset} parameter.
+     * Pixel data for a row will be stored in adjacent locations within
+     * the array packed as tightly as possible for increasing X
+     * coordinates.
+     * Pixel data for adjacent rows will be stored offset from each other
+     * by the number of byte array elements defined by
+     * {@code scanlineStride}.
+     * 
+     * @param x the X coordinate of the rectangular region to read
+     * @param y the Y coordinate of the rectangular region to read
+     * @param w the width of the rectangular region to read
+     * @param h the height of the rectangular region to read
+     * @param pixelformat the {@code PixelFormat<ByteBuffer>} object
+     *        defining the byte format to store the pixels into buffer
+     * @param buffer a byte array to store the returned pixel data
+     * @param offset the offset into {@code buffer} to store the first
+     *        pixel data
+     * @param scanlineStride the distance between the pixel data for the
+     *        start of one row of data in the buffer to the start of the
+     *        next row of data
+     */
+    public void getPixels(int x, int y, int w, int h,
+                          WritablePixelFormat<ByteBuffer> pixelformat,
+                          byte buffer[], int offset, int scanlineStride);
+
+    /**
+     * Reads pixel data from a rectangular region of the surface into the
+     * specified int array.
+     * The format to be used for pixels in the buffer is defined by the
+     * {@link PixelFormat} object and pixel format conversions will be
+     * performed as needed to store the data in the indicated format.
+     * The {@code pixelformat} must be a compatible
+     * {@code PixelFormat<IntBuffer>} type.
+     * The data for the first pixel at location {@code (x, y)} will be
+     * read into the array index specified by the {@code offset} parameter.
+     * Pixel data for a row will be stored in adjacent locations within
+     * the array packed as tightly as possible for increasing X
+     * coordinates.
+     * Pixel data for adjacent rows will be stored offset from each other
+     * by the number of int array elements defined by
+     * {@code scanlineStride}.
+     * 
+     * @param x the X coordinate of the rectangular region to read
+     * @param y the Y coordinate of the rectangular region to read
+     * @param w the width of the rectangular region to read
+     * @param h the height of the rectangular region to read
+     * @param pixelformat the {@code PixelFormat<IntBuffer>} object
+     *        defining the int format to store the pixels into buffer
+     * @param buffer a int array to store the returned pixel data
+     * @param offset the offset into {@code buffer} to store the first
+     *        pixel data
+     * @param scanlineStride the distance between the pixel data for the
+     *        start of one row of data in the buffer to the start of the
+     *        next row of data
+     */
+    public void getPixels(int x, int y, int w, int h,
+                          WritablePixelFormat<IntBuffer> pixelformat,
+                          int buffer[], int offset, int scanlineStride);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/javafx-ui-common/src/javafx/scene/image/PixelWriter.java	Thu May 17 16:15:50 2012 -0700
@@ -0,0 +1,202 @@
+/*
+ * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package javafx.scene.image;
+
+import java.nio.Buffer;
+import java.nio.ByteBuffer;
+import java.nio.IntBuffer;
+import javafx.scene.paint.Color;
+
+/**
+ * This interface defines methods for writing the pixel data of a
+ * {@link WritableImage} or other surface containing writable pixels.
+ */
+public interface PixelWriter {
+    /**
+     * This method returns the {@code PixelFormat} in which the surface
+     * stores its pixels, or a roughly equivalent pixel format from which
+     * it can easily convert pixels for purposes of writing them.
+     * 
+     * @return the {@code PixelFormat} that best describes the underlying
+     *         pixels
+     */
+    public PixelFormat getPixelFormat();
+
+    /**
+     * Stores pixel data for a color into the specified coordinates of the
+     * surface.
+     * The 32-bit integer {@code argb} parameter will contain the 4 color
+     * components in separate 8-bit fields in ARGB order from the most
+     * significant byte to the least significant byte.
+     * 
+     * @param x the X coordinate of the pixel color to write
+     * @param y the Y coordinate of the pixel color to write
+     * @param argb the color information to write, specified in a format
+     *         described by the {@link PixelFormat.Type.BYTE_ARGB}
+     *         PixelFormat type.
+     */
+    public void setArgb(int x, int y, int argb);
+
+    /**
+     * Stores pixel data for a {@link Color} into the specified coordinates
+     * of the surface.
+     * 
+     * @param x the X coordinate of the pixel color to write
+     * @param y the Y coordinate of the pixel color to write
+     * @param c the Color to write
+     */
+    public void setColor(int x, int y, Color c);
+
+    /**
+     * Stores pixel data from a buffer into a rectangular region of the
+     * surface.
+     * The format of the pixels in the buffer is defined by the
+     * {@link PixelFormat} object and pixel format conversions will be
+     * performed as needed to store the data into the surface.
+     * The buffer is assumed to be positioned to the location where the
+     * first pixel data to be stored in the surface pixel at location
+     * {@code (x, y)} is located.
+     * Pixel data for a row will be read from adjacent locations within
+     * the buffer packed as tightly as possible for increasing X
+     * coordinates.
+     * Pixel data for adjacent rows will be read offset from each other
+     * by the number of buffer data elements defined by
+     * {@code scanlineStride}.
+     * 
+     * @param x the X coordinate of the rectangular region to write
+     * @param y the Y coordinate of the rectangular region to write
+     * @param w the width of the rectangular region to write
+     * @param h the height of the rectangular region to write
+     * @param pixelformat the {@code PixelFormat} object defining the format
+     *        to read the pixels from the buffer
+     * @param buffer a buffer of a type appropriate for the indicated
+     *        {@code PixelFormat} object
+     * @param scanlineStride the distance between the pixel data for the
+     *        start of one row of data in the buffer to the start of the
+     *        next row of data.
+     */
+    public <T extends Buffer>
+        void setPixels(int x, int y, int w, int h,
+                       PixelFormat<T> pixelformat,
+                       T buffer, int scanlineStride);
+
+    /**
+     * Stores pixel data from a byte array into a rectangular region of the
+     * surface.
+     * The format of the pixels in the buffer is defined by the
+     * {@link PixelFormat} object and pixel format conversions will be
+     * performed as needed to store the data into the surface.
+     * The {@code pixelformat} must be a compatible
+     * {@code PixelFormat<ByteBuffer>} type.
+     * The data for the first pixel at location {@code (x, y)} will be
+     * read from the array index specified by the {@code offset} parameter.
+     * Pixel data for a row will be read from adjacent locations within
+     * the array packed as tightly as possible for increasing X
+     * coordinates.
+     * Pixel data for adjacent rows will be read offset from each other
+     * by the number of byte array elements defined by
+     * {@code scanlineStride}.
+     * 
+     * @param x the X coordinate of the rectangular region to write
+     * @param y the Y coordinate of the rectangular region to write
+     * @param w the width of the rectangular region to write
+     * @param h the height of the rectangular region to write
+     * @param pixelformat the {@code PixelFormat<ByteBuffer>} object
+     *        defining the byte format to read the pixels from buffer
+     * @param buffer a byte array containing the pixel data to store
+     * @param offset the offset into {@code buffer} to read the first
+     *        pixel data
+     * @param scanlineStride the distance between the pixel data for the
+     *        start of one row of data in the buffer to the start of the
+     *        next row of data
+     */
+    public void setPixels(int x, int y, int w, int h,
+                          PixelFormat<ByteBuffer> pixelformat,
+                          byte buffer[], int offset, int scanlineStride);
+
+    /**
+     * Stores pixel data from an int array into a rectangular region of the
+     * surface.
+     * The format of the pixels in the buffer is defined by the
+     * {@link PixelFormat} object and pixel format conversions will be
+     * performed as needed to store the data into the surface.
+     * The {@code pixelformat} must be a compatible
+     * {@code PixelFormat<IntBuffer>} type.
+     * The data for the first pixel at location {@code (x, y)} will be
+     * read from the array index specified by the {@code offset} parameter.
+     * Pixel data for a row will be read from adjacent locations within
+     * the array packed as tightly as possible for increasing X
+     * coordinates.
+     * Pixel data for adjacent rows will be read offset from each other
+     * by the number of int array elements defined by
+     * {@code scanlineStride}.
+     * 
+     * @param x the X coordinate of the rectangular region to write
+     * @param y the Y coordinate of the rectangular region to write
+     * @param w the width of the rectangular region to write
+     * @param h the height of the rectangular region to write
+     * @param pixelformat the {@code PixelFormat<IntBuffer>} object
+     *        defining the int format to read the pixels from buffer
+     * @param buffer an int array to containing the pixel data to store
+     * @param offset the offset into {@code buffer} to read the first
+     *        pixel data
+     * @param scanlineStride the distance between the pixel data for the
+     *        start of one row of data in the buffer to the start of the
+     *        next row of data
+     */
+    public void setPixels(int x, int y, int w, int h,
+                          PixelFormat<IntBuffer> pixelformat,
+                          int buffer[], int offset, int scanlineStride);
+
+    /**
+     * Stores pixel data retrieved from a {@code PixelReader} instance
+     * into a rectangular region of the surface.
+     * The data for the pixel on the surface at {@code (dstx, dsty)}
+     * will be retrieved from the {@code reader} from its location
+     * {@code (srcx, srcy)}.
+     * This method performs an operation which is semantically equivalent to
+     * (though likely much faster than) this pseudo-code:
+     * <pre>
+     *     for (int y = 0; y < h, y++) {
+     *         for (int x = 0; x < w; x++) {
+     *             setArgb(dstx + x, dsty + y,
+     *                     reader.getArgb(srcx + x, srcy + y));
+     *         }
+     *     }
+     * </pre>
+     * 
+     * @param dstx the X coordinate of the rectangular region to write
+     * @param dsty the Y coordinate of the rectangular region to write
+     * @param w the width of the rectangular region to write
+     * @param h the height of the rectangular region to write
+     * @param reader the {@link PixelReader} used to get the pixel data
+     *        to write
+     * @param srcx the X coordinate of the data to read from {@code reader}
+     * @param srcy the Y coordinate of the data to read from {@code reader}
+     */
+    public void setPixels(int dstx, int dsty, int w, int h,
+                          PixelReader reader, int srcx, int srcy);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/javafx-ui-common/src/javafx/scene/image/WritableImage.java	Thu May 17 16:15:50 2012 -0700
@@ -0,0 +1,215 @@
+/*
+ * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package javafx.scene.image;
+
+import com.sun.javafx.tk.PlatformImage;
+import java.nio.Buffer;
+import java.nio.ByteBuffer;
+import java.nio.IntBuffer;
+import javafx.beans.property.ReadOnlyObjectProperty;
+import javafx.scene.paint.Color;
+
+/**
+ * The {@code WritableImage} class represents a custom graphical image
+ * that is constructed from pixels supplied by the application, and possibly
+ * from {@code PixelReader} objects from any number of sources, including
+ * images read from a file or URL.
+ */
+public class WritableImage extends Image {
+    /**
+     * Construct an empty image of the specified dimensions.
+     * The image will initially be filled with transparent pixels.
+     * Images constructed this way will always be readable and writable
+     * so the corresponding getPixelReader() and getPixelWriter() will
+     * always return valid objects.
+     * 
+     * @param width the desired width of the writable image
+     * @param height the desired height of the desired image
+     */
+    public WritableImage(int width, int height) {
+        super(width, height);
+    }
+
+    /**
+     * Construct an image of the specified dimensions, initialized from
+     * the indicated {@link PixelReader}.
+     * The image will initially be filled with data returned from the
+     * {@code PixelReader}.
+     * If the {@code PixelReader} accesses a surface that does not contain
+     * the necessary number of pixel rows and columns then an
+     * {@link ArrayIndexOutOfBoundsException} will be thrown.
+     * Images constructed this way will always be readable and writable
+     * so the corresponding getPixelReader() and getPixelWriter() will
+     * always return valid objects.
+     * 
+     * @param width the desired width of the writable image and the
+     *        width of the region to be read from the {@code reader}
+     * @param height the desired height of the desired image and the
+     *        width of the region to be read from the {@code reader}
+     * @throws ArrayIndexOutOfBoundsException if the {@code reader} does
+     *         not access a surface of at least the requested dimensions
+     */
+    public WritableImage(PixelReader reader, int width, int height) {
+        super(width, height);
+        getPixelWriter().setPixels(0, 0, width, height, reader, 0, 0);
+    }
+
+    /**
+     * Construct an image of the specified dimensions, initialized from
+     * the indicated region of the {@link PixelReader}.
+     * The image will initially be filled with data returned from the
+     * {@code PixelReader} for the specified region.
+     * If the {@code PixelReader} accesses a surface that does not contain
+     * the necessary number of pixel rows and columns then an
+     * {@link ArrayIndexOutOfBoundsException} will be thrown.
+     * Images constructed this way will always be readable and writable
+     * so the corresponding getPixelReader() and getPixelWriter() will
+     * always return valid objects.
+     * 
+     * @param x the X coordinate of the upper left corner of the region to
+     *        read from the {@code reader}
+     * @param y the Y coordinate of the upper left corner of the region to
+     *        read from the {@code reader}
+     * @param width the desired width of the writable image and the
+     *        width of the region to be read from the {@code reader}
+     * @param height the desired height of the desired image and the
+     *        width of the region to be read from the {@code reader}
+     * @throws ArrayIndexOutOfBoundsException if the {@code reader} does
+     *         not access a surface containing at least the indicated region
+     */
+    public WritableImage(PixelReader reader,
+                         int x, int y, int width, int height)
+    {
+        super(width, height);
+        getPixelWriter().setPixels(0, 0, width, height, reader, x, y);
+    }
+
+    @Override
+    boolean isAnimation() {
+        return true;
+    }
+
+    @Override
+    boolean pixelsReadable() {
+        return true;
+    }
+
+    private PixelWriter writer;
+    /**
+     * This method returns a {@code PixelWriter} that provides access to
+     * write the pixels of the image.
+     * 
+     * @return the {@code PixelWriter} for writing pixels to the image
+     */
+    public PixelWriter getPixelWriter() {
+        if (getProgress() < 1.0 || isError()) {
+            return null;
+        }
+        if (writer == null) {
+            writer = new PixelWriter() {
+                ReadOnlyObjectProperty<PlatformImage> pimgprop =
+                    impl_platformImageProperty();
+
+                @Override
+                public PixelFormat getPixelFormat() {
+                    PlatformImage pimg = getWritablePlatformImage();
+                    return pimg.getPlatformPixelFormat();
+                }
+
+                @Override
+                public void setArgb(int x, int y, int argb) {
+                    getWritablePlatformImage().setArgb(x, y, argb);
+                    pixelsDirty();
+                }
+
+                @Override
+                public void setColor(int x, int y, Color c) {
+                    int a = (int) Math.round(c.getOpacity() * 255);
+                    int r = (int) Math.round(c.getRed()     * 255);
+                    int g = (int) Math.round(c.getGreen()   * 255);
+                    int b = (int) Math.round(c.getBlue()    * 255);
+                    setArgb(x, y, (a << 24) | (r << 16) | (g << 8) | b);
+                }
+
+                @Override
+                public <T extends Buffer>
+                    void setPixels(int x, int y, int w, int h,
+                                   PixelFormat<T> pixelformat,
+                                   T buffer, int scanlineStride)
+                {
+                    PlatformImage pimg = getWritablePlatformImage();
+                    for (int j = 0; j < h; j++) {
+                        for (int i = 0; i < w; i++) {
+                            int argb = pixelformat.getArgb(buffer, i, j,
+                                                           scanlineStride);
+                            pimg.setArgb(x+i, y+j, argb);
+                        }
+                    }
+//                    checkPixelAccess(true, true).setPixels(x, y, w, h,
+//                                                           buffer, pixelformat, scanlineStride);
+                    pixelsDirty();
+                }
+
+                @Override
+                public void setPixels(int x, int y, int w, int h,
+                                      PixelFormat<ByteBuffer> pixelformat,
+                                      byte buffer[], int offset, int scanlineStride)
+                {
+                    ByteBuffer bytebuf = ByteBuffer.wrap(buffer);
+                    bytebuf.position(offset);
+                    setPixels(x, y, w, h, pixelformat, bytebuf, scanlineStride);
+//                    checkPixelAccess(true, false).getPixels(x, y, w, h,
+//                                                            buffer, pixelformat, scanlineStride);
+                }
+
+                @Override
+                public void setPixels(int x, int y, int w, int h,
+                                      PixelFormat<IntBuffer> pixelformat,
+                                      int buffer[], int offset, int scanlineStride)
+                {
+                    IntBuffer intbuf = IntBuffer.wrap(buffer);
+                    intbuf.position(offset);
+                    setPixels(x, y, w, h, pixelformat, intbuf, scanlineStride);
+//                    checkPixelAccess(true, false).getPixels(x, y, w, h,
+//                                                            buffer, pixelformat, scanlineStride);
+                }
+
+                @Override
+                public void setPixels(int writex, int writey, int w, int h,
+                                      PixelReader reader, int readx, int ready)
+                {
+                    for (int y = 0; y < h; y++) {
+                        for (int x = 0; x < w; x++) {
+                            setArgb(writex + x, writey + y,
+                                    reader.getArgb(readx + x, ready + y));
+                        }
+                    }
+                }
+            };
+        }
+        return writer;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/javafx-ui-common/src/javafx/scene/image/WritablePixelFormat.java	Thu May 17 16:15:50 2012 -0700
@@ -0,0 +1,215 @@
+/*
+ * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package javafx.scene.image;
+
+import java.nio.Buffer;
+import java.nio.ByteBuffer;
+import java.nio.IntBuffer;
+
+/**
+ * A {@link PixelFormat} object representing a pixel format that can store
+ * full colors and so can be used as a destination format to write pixel
+ * data from an arbitrary image.
+ */
+public abstract class WritablePixelFormat<T extends Buffer>
+    extends PixelFormat<T>
+{
+    WritablePixelFormat(Type type) {
+        super(type);
+    }
+
+    @Override
+    public boolean isWritable() {
+        return true;
+    }
+
+    /**
+     * Stores a 32-bit integer representation of the color in the buffer
+     * at the specified coordinates.
+     * The 32-bit integer will contain the 4 color components in separate
+     * 8-bit fields in ARGB order from the most significant byte to the least
+     * significant byte.
+     * The buffer should be positioned to the start of the pixel data such
+     * that {@code buf.get(0)} would return the pixel information for the
+     * pixel at coordinates {@code (0, 0)}.
+     * The {@scanlineStride} parameter defines the distance from the pixel
+     * data at the start of one row to the pixel data at the start of the
+     * immediately following row at the next higher Y coordinate.  Usually,
+     * {@scanlineStride} is the same as the width of the image multiplied
+     * by the number of data elements per pixel (1 for the case of the
+     * integer and indexed formats, or 3 or 4 in the case of the byte
+     * formats), but some images may have further padding between rows for
+     * alignment or other purposes.
+     * 
+     * @param buf the buffer of pixel data
+     * @param x the X coordinate of the pixel to be read
+     * @param y the Y coordinate of the pixel to be read
+     * @param scanlineStride the number of buffer elements between the
+     *        start of adjacent pixel rows in the buffer
+     * @param argb a 32-bit value with the color to be stored in the pixel
+     *        in a format similar to the {@code Type.INT_ARGB} pixel format
+     */
+    public abstract void setArgb(T buf, int x, int y, int scanlineStride,
+                                 int argb);
+
+    static class IntArgb extends WritablePixelFormat<IntBuffer> {
+        static final IntArgb INSTANCE = new IntArgb();
+
+        private IntArgb() {
+            super(Type.INT_ARGB);
+        }
+
+        @Override
+        public boolean isPremultiplied() {
+            return false;
+        }
+
+        @Override
+        public int getArgb(IntBuffer buf, int x, int y, int scanlineStride) {
+            return buf.get(y * scanlineStride + x);
+        }
+
+        @Override
+        public void setArgb(IntBuffer buf, int x, int y, int scanlineStride,
+                            int argb)
+        {
+            buf.put(y * scanlineStride + x, argb);
+        }
+    }
+
+    static class IntArgbPre extends WritablePixelFormat<IntBuffer> {
+        static final IntArgbPre INSTANCE = new IntArgbPre();
+
+        private IntArgbPre() {
+            super(Type.INT_ARGB_PRE);
+        }
+
+        @Override
+        public boolean isPremultiplied() {
+            return true;
+        }
+
+        @Override
+        public int getArgb(IntBuffer buf, int x, int y, int scanlineStride) {
+            return PretoNonPre(buf.get(y * scanlineStride + x));
+        }
+
+        @Override
+        public void setArgb(IntBuffer buf, int x, int y, int scanlineStride,
+                            int argb)
+        {
+            buf.put(y * scanlineStride + x, NonPretoPre(argb));
+        }
+    }
+
+    static class ByteBgra extends WritablePixelFormat<ByteBuffer> {
+        static final ByteBgra INSTANCE = new ByteBgra();
+
+        private ByteBgra() {
+            super(Type.BYTE_BGRA);
+        }
+
+        @Override
+        public boolean isPremultiplied() {
+            return false;
+        }
+
+        @Override
+        public int getArgb(ByteBuffer buf, int x, int y, int scanlineStride) {
+            int index = y * scanlineStride + x * 4;
+            int b = buf.get(index    ) & 0xff;
+            int g = buf.get(index + 1) & 0xff;
+            int r = buf.get(index + 2) & 0xff;
+            int a = buf.get(index + 3) & 0xff;
+            return (a << 24) | (r << 16) | (g << 8) | b;
+        }
+
+        @Override
+        public void setArgb(ByteBuffer buf, int x, int y, int scanlineStride,
+                            int argb)
+        {
+            int index = y * scanlineStride + x * 4;
+            buf.put(index,     (byte) (argb >> 24));
+            buf.put(index + 1, (byte) (argb >> 16));
+            buf.put(index + 2, (byte) (argb >>  8));
+            buf.put(index + 3, (byte) (argb      ));
+        }
+    }
+
+    static class ByteBgraPre extends WritablePixelFormat<ByteBuffer> {
+        static final ByteBgraPre INSTANCE = new ByteBgraPre();
+
+        private ByteBgraPre() {
+            super(Type.BYTE_BGRA_PRE);
+        }
+
+        @Override
+        public boolean isPremultiplied() {
+            return true;
+        }
+
+        @Override
+        public int getArgb(ByteBuffer buf, int x, int y, int scanlineStride) {
+            int index = y * scanlineStride + x * 4;
+            int b = buf.get(index    ) & 0xff;
+            int g = buf.get(index + 1) & 0xff;
+            int r = buf.get(index + 2) & 0xff;
+            int a = buf.get(index + 3) & 0xff;
+            if (a > 0x00 && a < 0xff) {
+                int halfa = a >> 1;
+                r = (r >= a) ? 0xff : (r * 0xff + halfa) / a;
+                g = (g >= a) ? 0xff : (g * 0xff + halfa) / a;
+                b = (b >= a) ? 0xff : (b * 0xff + halfa) / a;
+            }
+            return (a << 24) | (r << 16) | (g << 8) | b;
+        }
+
+        @Override
+        public void setArgb(ByteBuffer buf, int x, int y, int scanlineStride,
+                            int argb)
+        {
+            int index = y * scanlineStride + x * 4;
+            int a = (argb >>> 24);
+            int r, g, b;
+            if (a > 0x00) {
+                r = (argb >> 16) & 0xff;
+                g = (argb >>  8) & 0xff;
+                b = (argb      ) & 0xff;
+                if (a < 0xff) {
+                    r = (r * a) / 0xff;
+                    g = (g * a) / 0xff;
+                    b = (b * a) / 0xff;
+                }
+            } else {
+                a = r = g = b = 0;
+            }
+            buf.put(index,     (byte) a);
+            buf.put(index + 1, (byte) r);
+            buf.put(index + 2, (byte) g);
+            buf.put(index + 3, (byte) b);
+        }
+    }
+}
--- a/test-stub-toolkit/src/com/sun/javafx/pgstub/StubImageLoader.java	Thu May 17 17:30:50 2012 +0200
+++ b/test-stub-toolkit/src/com/sun/javafx/pgstub/StubImageLoader.java	Thu May 17 16:15:50 2012 -0700
@@ -26,6 +26,7 @@
 package com.sun.javafx.pgstub;
 
 import com.sun.javafx.tk.ImageLoader;
+import com.sun.javafx.tk.PlatformImage;
 
 public final class StubImageLoader implements ImageLoader {
     private final Object source;
@@ -36,7 +37,7 @@
     private final boolean preserveRatio;
     private final boolean smooth;
 
-    private final Object[] frames;
+    private final PlatformImage[] frames;
 
     public StubImageLoader(final Object source,
                            final StubPlatformImageInfo imageInfo,
@@ -52,7 +53,7 @@
         this.preserveRatio = preserveRatio;
         this.smooth = smooth;
 
-        frames = new Object[imageInfo.getFrameCount()];
+        frames = new PlatformImage[imageInfo.getFrameCount()];
         for (int i = 0; i < frames.length; ++i) {
             frames[i] = new StubPlatformImage(this, i);
         }
@@ -73,12 +74,12 @@
     }
 
     @Override
-    public Object[] getFrames() {
+    public PlatformImage[] getFrames() {
         return frames;
     }
 
     @Override
-    public Object getFrame(final int i) {
+    public PlatformImage getFrame(final int i) {
         return frames[i];
     }
 
--- a/test-stub-toolkit/src/com/sun/javafx/pgstub/StubImageLoaderFactory.java	Thu May 17 17:30:50 2012 +0200
+++ b/test-stub-toolkit/src/com/sun/javafx/pgstub/StubImageLoaderFactory.java	Thu May 17 16:15:50 2012 -0700
@@ -31,6 +31,7 @@
 import com.sun.javafx.runtime.async.AsyncOperation;
 import com.sun.javafx.runtime.async.AsyncOperationListener;
 import com.sun.javafx.tk.ImageLoader;
+import com.sun.javafx.tk.PlatformImage;
 
 public final class StubImageLoaderFactory {
     private final Map<Object, StubPlatformImageInfo> imageInfos;
@@ -50,12 +51,12 @@
                 }
 
                 @Override
-                public Object[] getFrames() {
+                public PlatformImage[] getFrames() {
                     throw new IllegalStateException();
                 }
 
                 @Override
-                public Object getFrame(int i) {
+                public PlatformImage getFrame(int i) {
                     throw new IllegalStateException();
                 }
 
--- a/test-stub-toolkit/src/com/sun/javafx/pgstub/StubPlatformImage.java	Thu May 17 17:30:50 2012 +0200
+++ b/test-stub-toolkit/src/com/sun/javafx/pgstub/StubPlatformImage.java	Thu May 17 16:15:50 2012 -0700
@@ -25,7 +25,12 @@
 
 package com.sun.javafx.pgstub;
 
-public final class StubPlatformImage {
+import com.sun.javafx.tk.PlatformImage;
+import java.nio.Buffer;
+import javafx.scene.image.PixelFormat;
+import javafx.scene.image.WritablePixelFormat;
+
+public final class StubPlatformImage implements PlatformImage {
     private final StubImageLoader imageLoader;
     private final int frame;
 
@@ -52,6 +57,57 @@
     }
 
     @Override
+    public PixelFormat getPlatformPixelFormat() {
+        throw new UnsupportedOperationException("Not supported yet.");
+    }
+
+    @Override
+    public boolean isWritable() {
+        throw new UnsupportedOperationException("Not supported yet.");
+    }
+
+    @Override
+    public PlatformImage promoteToWritableImage() {
+        throw new UnsupportedOperationException("Not supported yet.");
+    }
+
+    @Override
+    public int getArgb(int x, int y) {
+        throw new UnsupportedOperationException("Not supported yet.");
+    }
+
+    @Override
+    public int getArgbPre(int x, int y) {
+        throw new UnsupportedOperationException("Not supported yet.");
+    }
+
+    @Override
+    public void setArgb(int x, int y, int argb) {
+        throw new UnsupportedOperationException("Not supported yet.");
+    }
+
+    @Override
+    public void setArgbPre(int x, int y, int argbpre) {
+        throw new UnsupportedOperationException("Not supported yet.");
+    }
+
+    @Override
+    public <T extends Buffer> void getPixels(int x, int y, int w, int h,
+                                             T pixels,
+                                             WritablePixelFormat<T> pixelformat,
+                                             int scanlineBytes) {
+        throw new UnsupportedOperationException("Not supported yet.");
+    }
+
+    @Override
+    public <T extends Buffer> void setPixels(int x, int y, int w, int h,
+                                             T pixels,
+                                             PixelFormat<T> pixelformat,
+                                             int scanlineBytes) {
+        throw new UnsupportedOperationException("Not supported yet.");
+    }
+
+    @Override
     public String toString() {
         final StringBuilder sb = new StringBuilder();
 
--- a/test-stub-toolkit/src/com/sun/javafx/pgstub/StubToolkit.java	Thu May 17 17:30:50 2012 +0200
+++ b/test-stub-toolkit/src/com/sun/javafx/pgstub/StubToolkit.java	Thu May 17 16:15:50 2012 -0700
@@ -94,6 +94,7 @@
 import com.sun.javafx.tk.FileChooserType;
 import com.sun.javafx.tk.FontLoader;
 import com.sun.javafx.tk.ImageLoader;
+import com.sun.javafx.tk.PlatformImage;
 import com.sun.javafx.tk.ScreenConfigurationAccessor;
 import com.sun.javafx.tk.TKClipboard;
 import com.sun.javafx.tk.TKDragGestureListener;
@@ -641,6 +642,11 @@
     }
 
     @Override
+    public PlatformImage createPlatformImage(int w, int h) {
+        throw new UnsupportedOperationException("Not supported yet.");
+    }
+
+    @Override
     public void waitFor(Task t) {
         throw new UnsupportedOperationException();
     }