8000629: [macosx] Blurry rendering with Java 7 on Retina display
authorserb
Fri, 12 Apr 2013 15:28:49 +0400
changeset 16840 64446d4710c4
parent 16839 d0f2e97b7359
child 16841 5cd8d248ea97
8000629: [macosx] Blurry rendering with Java 7 on Retina display Reviewed-by: anthony, prr, flar
jdk/src/macosx/classes/sun/awt/CGraphicsDevice.java
jdk/src/macosx/classes/sun/java2d/opengl/CGLGraphicsConfig.java
jdk/src/macosx/classes/sun/java2d/opengl/CGLLayer.java
jdk/src/macosx/classes/sun/java2d/opengl/CGLSurfaceData.java
jdk/src/macosx/classes/sun/lwawt/LWComponentPeer.java
jdk/src/macosx/classes/sun/lwawt/LWWindowPeer.java
jdk/src/macosx/classes/sun/lwawt/macosx/CPlatformWindow.java
jdk/src/macosx/native/sun/awt/CGraphicsDevice.m
jdk/src/macosx/native/sun/java2d/opengl/CGLLayer.m
jdk/src/share/classes/sun/awt/image/SurfaceManager.java
jdk/src/share/classes/sun/java2d/SunGraphics2D.java
jdk/src/share/classes/sun/java2d/SurfaceData.java
jdk/src/share/classes/sun/java2d/pipe/BufferedContext.java
jdk/src/share/classes/sun/java2d/pipe/DrawImage.java
jdk/src/share/classes/sun/java2d/pipe/Region.java
jdk/test/java/awt/Graphics2D/FillTexturePaint/FillTexturePaint.java
jdk/test/java/awt/Graphics2D/FlipDrawImage/FlipDrawImage.java
jdk/test/java/awt/Graphics2D/TransformSetGet/TransformSetGet.java
jdk/test/java/awt/image/DrawImage/IncorrectBounds.java
jdk/test/java/awt/image/DrawImage/IncorrectOffset.java
--- a/jdk/src/macosx/classes/sun/awt/CGraphicsDevice.java	Fri Apr 12 14:33:38 2013 +0400
+++ b/jdk/src/macosx/classes/sun/awt/CGraphicsDevice.java	Fri Apr 12 15:28:49 2013 +0400
@@ -219,6 +219,12 @@
         return nativeGetDisplayModes(displayID);
     }
 
+    public int getScaleFactor() {
+        return (int) nativeGetScaleFactor(displayID);
+    }
+
+    private static native double nativeGetScaleFactor(int displayID);
+
     private static native void nativeSetDisplayMode(int displayID, int w, int h, int bpp, int refrate);
 
     private static native DisplayMode nativeGetDisplayMode(int displayID);
--- a/jdk/src/macosx/classes/sun/java2d/opengl/CGLGraphicsConfig.java	Fri Apr 12 14:33:38 2013 +0400
+++ b/jdk/src/macosx/classes/sun/java2d/opengl/CGLGraphicsConfig.java	Fri Apr 12 15:28:49 2013 +0400
@@ -441,29 +441,37 @@
 
     @Override
     public int getMaxTextureWidth() {
-        int width;
+        //Temporary disable this logic and use some magic constrain.
+        /*
+         int width;
 
-        synchronized (totalDisplayBounds) {
-            if (totalDisplayBounds.width == 0) {
-                updateTotalDisplayBounds();
-            }
-            width = totalDisplayBounds.width;
-        }
+         synchronized (totalDisplayBounds) {
+         if (totalDisplayBounds.width == 0) {
+         updateTotalDisplayBounds();
+         }
+         width = totalDisplayBounds.width;
+         }
 
-        return Math.min(width, getMaxTextureSize());
+         return Math.min(width, getMaxTextureSize());
+         */
+        return getMaxTextureSize() / (getDevice().getScaleFactor() * 2);
     }
 
     @Override
     public int getMaxTextureHeight() {
-        int height;
+        //Temporary disable this logic and use some magic constrain.
+        /*
+         int height;
 
-        synchronized (totalDisplayBounds) {
-            if (totalDisplayBounds.height == 0) {
-                updateTotalDisplayBounds();
-            }
-            height = totalDisplayBounds.height;
-        }
+         synchronized (totalDisplayBounds) {
+         if (totalDisplayBounds.height == 0) {
+         updateTotalDisplayBounds();
+         }
+         height = totalDisplayBounds.height;
+         }
 
-        return Math.min(height, getMaxTextureSize());
+         return Math.min(height, getMaxTextureSize());
+         */
+        return getMaxTextureSize() / (getDevice().getScaleFactor() * 2);
     }
 }
--- a/jdk/src/macosx/classes/sun/java2d/opengl/CGLLayer.java	Fri Apr 12 14:33:38 2013 +0400
+++ b/jdk/src/macosx/classes/sun/java2d/opengl/CGLLayer.java	Fri Apr 12 15:28:49 2013 +0400
@@ -40,11 +40,12 @@
 public class CGLLayer extends CFRetainedResource {
 
     private native long nativeCreateLayer();
-
+    private static native void nativeSetScale(long layerPtr, double scale);
     private static native void validate(long layerPtr, CGLSurfaceData cglsd);
     private static native void blitTexture(long layerPtr);
 
     private LWWindowPeer peer;
+    private int scale = 1;
 
     private SurfaceData surfaceData; // represents intermediate buffer (texture)
 
@@ -90,7 +91,7 @@
         // and blits the buffer to the layer surface (in drawInCGLContext callback)
         CGraphicsConfig gc = (CGraphicsConfig)peer.getGraphicsConfiguration();
         surfaceData = gc.createSurfaceData(this);
-
+        setScale(gc.getDevice().getScaleFactor());
         // the layer holds a reference to the buffer, which in
         // turn has a reference back to this layer
         if (surfaceData instanceof CGLSurfaceData) {
@@ -121,6 +122,13 @@
         super.dispose();
     }
 
+    private void setScale(final int _scale) {
+        if (scale != _scale) {
+            scale = _scale;
+            nativeSetScale(getPointer(), scale);
+        }
+    }
+
     // ----------------------------------------------------------------------
     // NATIVE CALLBACKS
     // ----------------------------------------------------------------------
--- a/jdk/src/macosx/classes/sun/java2d/opengl/CGLSurfaceData.java	Fri Apr 12 14:33:38 2013 +0400
+++ b/jdk/src/macosx/classes/sun/java2d/opengl/CGLSurfaceData.java	Fri Apr 12 15:28:49 2013 +0400
@@ -30,7 +30,6 @@
 import java.awt.GraphicsDevice;
 import java.awt.GraphicsEnvironment;
 import java.awt.Image;
-import java.awt.Insets;
 import java.awt.Rectangle;
 import java.awt.image.ColorModel;
 
@@ -41,6 +40,9 @@
 
 public abstract class CGLSurfaceData extends OGLSurfaceData {
 
+    protected final int scale;
+    protected final int width;
+    protected final int height;
     protected CPlatformView pView;
     private CGLGraphicsConfig graphicsConfig;
 
@@ -52,10 +54,19 @@
     protected native boolean initPbuffer(long pData, long pConfigInfo,
             boolean isOpaque, int width, int height);
 
+    protected CGLSurfaceData(CGLGraphicsConfig gc, ColorModel cm, int type,
+                             int width, int height) {
+        super(gc, cm, type);
+        // TEXTURE shouldn't be scaled, it is used for managed BufferedImages.
+        scale = type == TEXTURE ? 1 : gc.getDevice().getScaleFactor();
+        this.width = width * scale;
+        this.height = height * scale;
+    }
+
     protected CGLSurfaceData(CPlatformView pView, CGLGraphicsConfig gc,
-                             ColorModel cm, int type)
+                             ColorModel cm, int type,int width, int height)
     {
-        super(gc, cm, type);
+        this(gc, cm, type, width, height);
         this.pView = pView;
         this.graphicsConfig = gc;
 
@@ -70,9 +81,9 @@
     }
 
     protected CGLSurfaceData(CGLLayer layer, CGLGraphicsConfig gc,
-                             ColorModel cm, int type)
+                             ColorModel cm, int type,int width, int height)
     {
-        super(gc, cm, type);
+        this(gc, cm, type, width, height);
         this.graphicsConfig = gc;
 
         long pConfigInfo = gc.getNativeConfigInfo();
@@ -157,13 +168,43 @@
         // Overridden in CGLWindowSurfaceData below
     }
 
+    @Override
+    public int getDefaultScale() {
+        return scale;
+    }
+
+    @Override
+    public boolean copyArea(SunGraphics2D sg2d, int x, int y, int w, int h,
+                            int dx, int dy) {
+        final int state = sg2d.transformState;
+        if (state > SunGraphics2D.TRANSFORM_TRANSLATESCALE
+            || sg2d.compositeState >= SunGraphics2D.COMP_XOR) {
+            return false;
+        }
+        if (state <= SunGraphics2D.TRANSFORM_ANY_TRANSLATE) {
+            x += sg2d.transX;
+            y += sg2d.transY;
+        } else if (state == SunGraphics2D.TRANSFORM_TRANSLATESCALE) {
+            final double[] coords = {x, y, x + w, y + h, x + dx, y + dy};
+            sg2d.transform.transform(coords, 0, coords, 0, 3);
+            x = (int) Math.ceil(coords[0] - 0.5);
+            y = (int) Math.ceil(coords[1] - 0.5);
+            w = ((int) Math.ceil(coords[2] - 0.5)) - x;
+            h = ((int) Math.ceil(coords[3] - 0.5)) - y;
+            dx = ((int) Math.ceil(coords[4] - 0.5)) - x;
+            dy = ((int) Math.ceil(coords[5] - 0.5)) - y;
+        }
+        oglRenderPipe.copyArea(sg2d, x, y, w, h, dx, dy);
+        return true;
+    }
+
     protected native void clearWindow();
 
     public static class CGLWindowSurfaceData extends CGLSurfaceData {
 
         public CGLWindowSurfaceData(CPlatformView pView,
                 CGLGraphicsConfig gc) {
-            super(pView, gc, gc.getColorModel(), WINDOW);
+            super(pView, gc, gc.getColorModel(), WINDOW, 0, 0);
         }
 
         @Override
@@ -217,17 +258,12 @@
     public static class CGLLayerSurfaceData extends CGLSurfaceData {
 
         private CGLLayer layer;
-        private int width, height;
 
         public CGLLayerSurfaceData(CGLLayer layer, CGLGraphicsConfig gc,
                                    int width, int height) {
-            super(layer, gc, gc.getColorModel(), FBOBJECT);
-
-            this.width = width;
-            this.height = height;
+            super(layer, gc, gc.getColorModel(), FBOBJECT, width, height);
             this.layer = layer;
-
-            initSurface(width, height);
+            initSurface(this.width, this.height);
         }
 
         @Override
@@ -296,18 +332,13 @@
 
     public static class CGLOffScreenSurfaceData extends CGLSurfaceData {
         private Image offscreenImage;
-        private int width, height;
 
         public CGLOffScreenSurfaceData(CPlatformView pView,
                                        CGLGraphicsConfig gc, int width, int height, Image image,
                                        ColorModel cm, int type) {
-            super(pView, gc, cm, type);
-
-            this.width = width;
-            this.height = height;
+            super(pView, gc, cm, type, width, height);
             offscreenImage = image;
-
-            initSurface(width, height);
+            initSurface(this.width, this.height);
         }
 
         @Override
--- a/jdk/src/macosx/classes/sun/lwawt/LWComponentPeer.java	Fri Apr 12 14:33:38 2013 +0400
+++ b/jdk/src/macosx/classes/sun/lwawt/LWComponentPeer.java	Fri Apr 12 15:28:49 2013 +0400
@@ -463,35 +463,8 @@
 
     private void applyConstrain(final Graphics g) {
         final SunGraphics2D sg2d = (SunGraphics2D) g;
-        final Rectangle constr = localToWindow(getSize());
-        // translate and set rectangle constrain.
-        sg2d.constrain(constr.x, constr.y, constr.width, constr.height);
-        // set region constrain.
-        //sg2d.constrain(getVisibleRegion());
-        SG2DConstraint(sg2d, getVisibleRegion());
-    }
-
-    //TODO Move this method to SG2D?
-    void SG2DConstraint(final SunGraphics2D sg2d, Region r) {
-        sg2d.constrainX = sg2d.transX;
-        sg2d.constrainY = sg2d.transY;
-
-        Region c = sg2d.constrainClip;
-        if ((sg2d.constrainX | sg2d.constrainY) != 0) {
-            r = r.getTranslatedRegion(sg2d.constrainX, sg2d.constrainY);
-        }
-        if (c == null) {
-            c = r;
-        } else {
-            c = c.getIntersection(r);
-            if (c == sg2d.constrainClip) {
-                // Common case to ignore
-                return;
-            }
-        }
-        sg2d.constrainClip = c;
-        //validateCompClip() forced call.
-        sg2d.setDevClip(r.getLoX(), r.getLoY(), r.getWidth(), r.getHeight());
+        final Rectangle size = localToWindow(getSize());
+        sg2d.constrain(size.x, size.y, size.width, size.height, getVisibleRegion());
     }
 
     public Region getVisibleRegion() {
--- a/jdk/src/macosx/classes/sun/lwawt/LWWindowPeer.java	Fri Apr 12 14:33:38 2013 +0400
+++ b/jdk/src/macosx/classes/sun/lwawt/LWWindowPeer.java	Fri Apr 12 15:28:49 2013 +0400
@@ -580,17 +580,16 @@
         setBounds(x, y, w, h, SET_BOUNDS, false, false);
 
         // Second, update the graphics config and surface data
-        checkIfOnNewScreen();
-        if (resized) {
+        final boolean isNewDevice = updateGraphicsDevice();
+        if (resized || isNewDevice) {
             replaceSurfaceData();
-            flushOnscreenGraphics();
         }
 
         // Third, COMPONENT_MOVED/COMPONENT_RESIZED/PAINT events
         if (moved || invalid) {
             handleMove(x, y, true);
         }
-        if (resized || invalid) {
+        if (resized || invalid || isNewDevice) {
             handleResize(w, h, true);
             repaintPeer();
         }
@@ -610,7 +609,7 @@
                 }
                 if (!isTextured()) {
                     if (g instanceof SunGraphics2D) {
-                        SG2DConstraint((SunGraphics2D) g, getRegion());
+                        ((SunGraphics2D) g).constrain(0, 0, w, h, getRegion());
                     }
                     g.setColor(getBackground());
                     g.fillRect(0, 0, w, h);
@@ -922,7 +921,7 @@
             }
             // If window's graphics config is changed from the app code, the
             // config correspond to the same device as before; when the window
-            // is moved by user, graphicsDevice is updated in checkIfOnNewScreen().
+            // is moved by user, graphicsDevice is updated in notifyReshape().
             // In either case, there's nothing to do with screenOn here
             graphicsConfig = gc;
         }
@@ -930,11 +929,14 @@
         return true;
     }
 
-    private void checkIfOnNewScreen() {
+    /**
+     * Returns true if the GraphicsDevice has been changed, false otherwise.
+     */
+    public boolean updateGraphicsDevice() {
         GraphicsDevice newGraphicsDevice = platformWindow.getGraphicsDevice();
         synchronized (getStateLock()) {
             if (graphicsDevice == newGraphicsDevice) {
-                return;
+                return false;
             }
             graphicsDevice = newGraphicsDevice;
         }
@@ -942,13 +944,14 @@
         // TODO: DisplayChangedListener stuff
         final GraphicsConfiguration newGC = newGraphicsDevice.getDefaultConfiguration();
 
-        if (!setGraphicsConfig(newGC)) return;
+        if (!setGraphicsConfig(newGC)) return false;
 
         SunToolkit.executeOnEventHandlerThread(getTarget(), new Runnable() {
             public void run() {
                 AWTAccessor.getComponentAccessor().setGraphicsConfiguration(getTarget(), newGC);
             }
         });
+        return true;
     }
 
     /*
@@ -983,6 +986,7 @@
                 oldData.flush();
             }
         }
+        flushOnscreenGraphics();
     }
 
     private void blitSurfaceData(final SurfaceData src, final SurfaceData dst) {
@@ -990,14 +994,15 @@
         if (src != dst && src != null && dst != null
             && !(dst instanceof NullSurfaceData)
             && !(src instanceof NullSurfaceData)
-            && src.getSurfaceType().equals(dst.getSurfaceType())) {
-            final Rectangle size = getSize();
+            && src.getSurfaceType().equals(dst.getSurfaceType())
+            && src.getDefaultScale() == dst.getDefaultScale()) {
+            final Rectangle size = src.getBounds();
             final Blit blit = Blit.locate(src.getSurfaceType(),
                                           CompositeType.Src,
                                           dst.getSurfaceType());
             if (blit != null) {
-                blit.Blit(src, dst, AlphaComposite.Src,
-                          getRegion(), 0, 0, 0, 0, size.width, size.height);
+                blit.Blit(src, dst, AlphaComposite.Src, null, 0, 0, 0, 0,
+                          size.width, size.height);
             }
         }
     }
--- a/jdk/src/macosx/classes/sun/lwawt/macosx/CPlatformWindow.java	Fri Apr 12 14:33:38 2013 +0400
+++ b/jdk/src/macosx/classes/sun/lwawt/macosx/CPlatformWindow.java	Fri Apr 12 15:28:49 2013 +0400
@@ -860,8 +860,8 @@
         }
     }
 
-    private void flushBuffers() {
-        if (isVisible() && !nativeBounds.isEmpty()) {
+    void flushBuffers() {
+        if (isVisible() && !nativeBounds.isEmpty() && !isFullScreenMode) {
             try {
                 LWCToolkit.invokeAndWait(new Runnable() {
                     @Override
--- a/jdk/src/macosx/native/sun/awt/CGraphicsDevice.m	Fri Apr 12 14:33:38 2013 +0400
+++ b/jdk/src/macosx/native/sun/awt/CGraphicsDevice.m	Fri Apr 12 15:28:49 2013 +0400
@@ -315,3 +315,34 @@
 
     return jreturnArray;
 }
+
+/*
+ * Class:     sun_awt_CGraphicsDevice
+ * Method:    nativeGetScaleFactor
+ * Signature: (I)D
+ */
+JNIEXPORT jdouble JNICALL
+Java_sun_awt_CGraphicsDevice_nativeGetScaleFactor
+(JNIEnv *env, jclass class, jint displayID)
+{
+    __block jdouble ret = 1.0f;
+
+JNF_COCOA_ENTER(env);
+
+    [ThreadUtilities performOnMainThreadWaiting:YES block:^(){
+        NSArray *screens = [NSScreen screens];
+        for (NSScreen *screen in screens) {
+            NSDictionary *screenInfo = [screen deviceDescription];
+            NSNumber *screenID = [screenInfo objectForKey:@"NSScreenNumber"];
+            if ([screenID pointerValue] == displayID){
+                if ([screen respondsToSelector:@selector(backingScaleFactor)]) {
+                    ret = [screen backingScaleFactor];
+                }
+                break;
+            }
+        }
+    }];
+
+JNF_COCOA_EXIT(env);
+    return ret;
+}
--- a/jdk/src/macosx/native/sun/java2d/opengl/CGLLayer.m	Fri Apr 12 14:33:38 2013 +0400
+++ b/jdk/src/macosx/native/sun/java2d/opengl/CGLLayer.m	Fri Apr 12 15:28:49 2013 +0400
@@ -61,6 +61,19 @@
     //Layer backed view
     //self.needsDisplayOnBoundsChange = YES;
     //self.autoresizingMask = kCALayerWidthSizable | kCALayerHeightSizable;
+
+    //Disable CALayer's default animation
+    NSMutableDictionary * actions = [[NSMutableDictionary alloc] initWithObjectsAndKeys:
+                                    [NSNull null], @"bounds",
+                                    [NSNull null], @"contents",
+                                    [NSNull null], @"contentsScale",
+                                    [NSNull null], @"onOrderIn",
+                                    [NSNull null], @"onOrderOut",
+                                    [NSNull null], @"sublayers",
+                                    nil];
+    self.actions = actions;
+    [actions release];
+
     textureID = 0; // texture will be created by rendering pipe
     target = 0;
 
@@ -121,8 +134,12 @@
     // Set the current context to the one given to us.
     CGLSetCurrentContext(glContext);
 
+    // Should clear the whole CALayer, because it can be larger than our texture.
+    glClearColor(0.0, 0.0, 0.0, 0.0);
+    glClear(GL_COLOR_BUFFER_BIT);
+
     glViewport(0, 0, textureWidth, textureHeight);
-
+    
     JNIEnv *env = [ThreadUtilities getJNIEnv];
     static JNF_CLASS_CACHE(jc_JavaLayer, "sun/java2d/opengl/CGLLayer");
     static JNF_MEMBER_CACHE(jm_drawInCGLContext, jc_JavaLayer, "drawInCGLContext", "()V");
@@ -168,7 +185,7 @@
 // Must be called under the RQ lock.
 JNIEXPORT void JNICALL
 Java_sun_java2d_opengl_CGLLayer_validate
-(JNIEnv *env, jobject obj, jlong layerPtr, jobject surfaceData)
+(JNIEnv *env, jclass cls, jlong layerPtr, jobject surfaceData)
 {
     CGLLayer *layer = OBJC(layerPtr);
 
@@ -186,9 +203,21 @@
 // Must be called on the AppKit thread and under the RQ lock.
 JNIEXPORT void JNICALL
 Java_sun_java2d_opengl_CGLLayer_blitTexture
-(JNIEnv *env, jobject obj, jlong layerPtr)
+(JNIEnv *env, jclass cls, jlong layerPtr)
 {
     CGLLayer *layer = jlong_to_ptr(layerPtr);
 
     [layer blitTexture];
 }
+
+JNIEXPORT void JNICALL
+Java_sun_java2d_opengl_CGLLayer_nativeSetScale
+(JNIEnv *env, jclass cls, jlong layerPtr, jdouble scale)
+{
+    JNF_COCOA_ENTER(env);
+    CGLLayer *layer = jlong_to_ptr(layerPtr);
+    [ThreadUtilities performOnMainThreadWaiting:NO block:^(){
+        layer.contentsScale = scale;
+    }];
+    JNF_COCOA_EXIT(env);
+}
--- a/jdk/src/share/classes/sun/awt/image/SurfaceManager.java	Fri Apr 12 14:33:38 2013 +0400
+++ b/jdk/src/share/classes/sun/awt/image/SurfaceManager.java	Fri Apr 12 15:28:49 2013 +0400
@@ -31,6 +31,7 @@
 import java.awt.Image;
 import java.awt.ImageCapabilities;
 import java.awt.image.BufferedImage;
+import java.awt.image.VolatileImage;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.Iterator;
 import sun.java2d.SurfaceData;
@@ -287,4 +288,18 @@
             flush(true);
         }
     }
+
+    /**
+     * Returns a scale factor of the image. This is utility method, which
+     * fetches information from the SurfaceData of the image.
+     *
+     * @see SurfaceData#getDefaultScale
+     */
+    public static int getImageScale(final Image img) {
+        if (!(img instanceof VolatileImage)) {
+            return 1;
+        }
+        final SurfaceManager sm = getManager(img);
+        return sm.getPrimarySurfaceData().getDefaultScale();
+    }
 }
--- a/jdk/src/share/classes/sun/java2d/SunGraphics2D.java	Fri Apr 12 14:33:38 2013 +0400
+++ b/jdk/src/share/classes/sun/java2d/SunGraphics2D.java	Fri Apr 12 15:28:49 2013 +0400
@@ -65,6 +65,8 @@
 import java.awt.Transparency;
 import java.awt.font.GlyphVector;
 import java.awt.font.TextLayout;
+
+import sun.awt.image.SurfaceManager;
 import sun.font.FontDesignMetrics;
 import sun.font.FontUtilities;
 import sun.java2d.pipe.PixelDrawPipe;
@@ -82,14 +84,12 @@
 import sun.java2d.loops.SurfaceType;
 import sun.java2d.loops.Blit;
 import sun.java2d.loops.MaskFill;
-import sun.font.FontManager;
 import java.awt.font.FontRenderContext;
 import sun.java2d.loops.XORComposite;
 import sun.awt.ConstrainableGraphics;
 import sun.awt.SunHints;
 import java.util.Map;
 import java.util.Iterator;
-import sun.java2d.DestSurfaceProvider;
 import sun.misc.PerformanceLogger;
 
 import javax.tools.annotation.GenerateNativeHeader;
@@ -207,13 +207,15 @@
 
     public RenderingHints hints;
 
-    public Region constrainClip;                // lightweight bounds
+    public Region constrainClip;        // lightweight bounds in pixels
     public int constrainX;
     public int constrainY;
 
     public Region clipRegion;
     public Shape usrClip;
-    protected Region devClip;           // Actual physical drawable
+    protected Region devClip;           // Actual physical drawable in pixels
+
+    private final int devScale;         // Actual physical scale factor
 
     // cached state for text rendering
     private boolean validFontInfo;
@@ -256,6 +258,12 @@
 
         validateColor();
 
+        devScale = sd.getDefaultScale();
+        if (devScale != 1) {
+            transform.setToScale(devScale, devScale);
+            invalidateTransform();
+        }
+
         font = f;
         if (font == null) {
             font = defaultFont;
@@ -320,6 +328,49 @@
 
     /**
      * Constrain rendering for lightweight objects.
+     */
+    public void constrain(int x, int y, int w, int h, Region region) {
+        if ((x | y) != 0) {
+            translate(x, y);
+        }
+        if (transformState > TRANSFORM_TRANSLATESCALE) {
+            clipRect(0, 0, w, h);
+            return;
+        }
+        // changes parameters according to the current scale and translate.
+        final double scaleX = transform.getScaleX();
+        final double scaleY = transform.getScaleY();
+        x = constrainX = (int) transform.getTranslateX();
+        y = constrainY = (int) transform.getTranslateY();
+        w = Region.dimAdd(x, Region.clipScale(w, scaleX));
+        h = Region.dimAdd(y, Region.clipScale(h, scaleY));
+
+        Region c = constrainClip;
+        if (c == null) {
+            c = Region.getInstanceXYXY(x, y, w, h);
+        } else {
+            c = c.getIntersectionXYXY(x, y, w, h);
+        }
+        if (region != null) {
+            region = region.getScaledRegion(scaleX, scaleY);
+            region = region.getTranslatedRegion(x, y);
+            c = c.getIntersection(region);
+        }
+
+        if (c == constrainClip) {
+            // Common case to ignore
+            return;
+        }
+
+        constrainClip = c;
+        if (!devClip.isInsideQuickCheck(c)) {
+            devClip = devClip.getIntersection(c);
+            validateCompClip();
+        }
+    }
+
+    /**
+     * Constrain rendering for lightweight objects.
      *
      * REMIND: This method will back off to the "workaround"
      * of using translate and clipRect if the Graphics
@@ -330,33 +381,9 @@
      * @exception IllegalStateException If the Graphics
      * to be constrained has a complex transform.
      */
+    @Override
     public void constrain(int x, int y, int w, int h) {
-        if ((x|y) != 0) {
-            translate(x, y);
-        }
-        if (transformState >= TRANSFORM_TRANSLATESCALE) {
-            clipRect(0, 0, w, h);
-            return;
-        }
-        x = constrainX = transX;
-        y = constrainY = transY;
-        w = Region.dimAdd(x, w);
-        h = Region.dimAdd(y, h);
-        Region c = constrainClip;
-        if (c == null) {
-            c = Region.getInstanceXYXY(x, y, w, h);
-        } else {
-            c = c.getIntersectionXYXY(x, y, w, h);
-            if (c == constrainClip) {
-                // Common case to ignore
-                return;
-            }
-        }
-        constrainClip = c;
-        if (!devClip.isInsideQuickCheck(c)) {
-            devClip = devClip.getIntersection(c);
-            validateCompClip();
-        }
+        constrain(x, y, w, h, null);
     }
 
     protected static ValidatePipe invalidpipe = new ValidatePipe();
@@ -1540,11 +1567,13 @@
      * @see TransformChain
      * @see AffineTransform
      */
+    @Override
     public void setTransform(AffineTransform Tx) {
-        if ((constrainX|constrainY) == 0) {
+        if ((constrainX | constrainY) == 0 && devScale == 1) {
             transform.setTransform(Tx);
         } else {
-            transform.setToTranslation(constrainX, constrainY);
+            transform.setTransform(devScale, 0, 0, devScale, constrainX,
+                                   constrainY);
             transform.concatenate(Tx);
         }
         invalidateTransform();
@@ -1602,12 +1631,15 @@
      * @see #transform
      * @see #setTransform
      */
+    @Override
     public AffineTransform getTransform() {
-        if ((constrainX|constrainY) == 0) {
+        if ((constrainX | constrainY) == 0 && devScale == 1) {
             return new AffineTransform(transform);
         }
-        AffineTransform tx =
-            AffineTransform.getTranslateInstance(-constrainX, -constrainY);
+        final double invScale = 1.0 / devScale;
+        AffineTransform tx = new AffineTransform(invScale, 0, 0, invScale,
+                                                 -constrainX * invScale,
+                                                 -constrainY * invScale);
         tx.concatenate(transform);
         return tx;
     }
@@ -2991,6 +3023,37 @@
     }
 // end of text rendering methods
 
+    private static boolean isHiDPIImage(final Image img) {
+        return SurfaceManager.getImageScale(img) != 1;
+    }
+
+    private boolean drawHiDPIImage(Image img, int dx1, int dy1, int dx2,
+                                   int dy2, int sx1, int sy1, int sx2, int sy2,
+                                   Color bgcolor, ImageObserver observer) {
+        final int scale = SurfaceManager.getImageScale(img);
+        sx1 = Region.clipScale(sx1, scale);
+        sx2 = Region.clipScale(sx2, scale);
+        sy1 = Region.clipScale(sy1, scale);
+        sy2 = Region.clipScale(sy2, scale);
+        try {
+            return imagepipe.scaleImage(this, img, dx1, dy1, dx2, dy2, sx1, sy1,
+                                        sx2, sy2, bgcolor, observer);
+        } catch (InvalidPipeException e) {
+            try {
+                revalidateAll();
+                return imagepipe.scaleImage(this, img, dx1, dy1, dx2, dy2, sx1,
+                                            sy1, sx2, sy2, bgcolor, observer);
+            } catch (InvalidPipeException e2) {
+                // Still catching the exception; we are not yet ready to
+                // validate the surfaceData correctly.  Fail for now and
+                // try again next time around.
+                return false;
+            }
+        } finally {
+            surfaceData.markDirty();
+        }
+    }
+
     /**
      * Draws an image scaled to x,y,w,h in nonblocking mode with a
      * callback object.
@@ -3004,8 +3067,9 @@
      * Not part of the advertised API but a useful utility method
      * to call internally.  This is for the case where we are
      * drawing to/from given coordinates using a given width/height,
-     * but we guarantee that the weidth/height of the src and dest
-     * areas are equal (no scale needed).
+     * but we guarantee that the surfaceData's width/height of the src and dest
+     * areas are equal (no scale needed). Note that this method intentionally
+     * ignore scale factor of the source image, and copy it as is.
      */
     public boolean copyImage(Image img, int dx, int dy, int sx, int sy,
                              int width, int height, Color bgcolor,
@@ -3043,7 +3107,15 @@
         if ((width == 0) || (height == 0)) {
             return true;
         }
-        if (width == img.getWidth(null) && height == img.getHeight(null)) {
+
+        final int imgW = img.getWidth(null);
+        final int imgH = img.getHeight(null);
+        if (isHiDPIImage(img)) {
+            return drawHiDPIImage(img, x, y, x + width, y + height, 0, 0, imgW,
+                                  imgH, bg, observer);
+        }
+
+        if (width == imgW && height == imgH) {
             return copyImage(img, x, y, 0, 0, width, height, bg, observer);
         }
 
@@ -3084,6 +3156,13 @@
             return true;
         }
 
+        if (isHiDPIImage(img)) {
+            final int imgW = img.getWidth(null);
+            final int imgH = img.getHeight(null);
+            return drawHiDPIImage(img, x, y, x + imgW, y + imgH, 0, 0, imgW,
+                                  imgH, bg, observer);
+        }
+
         try {
             return imagepipe.copyImage(this, img, x, y, bg, observer);
         } catch (InvalidPipeException e) {
@@ -3132,6 +3211,11 @@
             return true;
         }
 
+        if (isHiDPIImage(img)) {
+            return drawHiDPIImage(img, dx1, dy1, dx2, dy2, sx1, sy1, sx2, sy2,
+                                  bgcolor, observer);
+        }
+
         if (((sx2 - sx1) == (dx2 - dx1)) &&
             ((sy2 - sy1) == (dy2 - dy1)))
         {
@@ -3210,6 +3294,18 @@
             return drawImage(img, 0, 0, null, observer);
         }
 
+        if (isHiDPIImage(img)) {
+            final int w = img.getWidth(null);
+            final int h = img.getHeight(null);
+            final AffineTransform tx = new AffineTransform(transform);
+            transform(xform);
+            boolean result = drawHiDPIImage(img, 0, 0, w, h, 0, 0, w, h, null,
+                                            observer);
+            transform.setTransform(tx);
+            invalidateTransform();
+            return result;
+        }
+
         try {
             return imagepipe.transformImage(this, img, xform, observer);
         } catch (InvalidPipeException e) {
--- a/jdk/src/share/classes/sun/java2d/SurfaceData.java	Fri Apr 12 14:33:38 2013 +0400
+++ b/jdk/src/share/classes/sun/java2d/SurfaceData.java	Fri Apr 12 15:28:49 2013 +0400
@@ -1057,4 +1057,14 @@
      * responsible for returning the appropriate object.
      */
     public abstract Object getDestination();
+
+    /**
+     * Returns default scale factor of the destination surface. Scale factor
+     * describes the mapping between virtual and physical coordinates of the
+     * SurfaceData. If the scale is 2 then virtual pixel coordinates need to be
+     * doubled for physical pixels.
+     */
+    public int getDefaultScale() {
+        return 1;
+    }
 }
--- a/jdk/src/share/classes/sun/java2d/pipe/BufferedContext.java	Fri Apr 12 14:33:38 2013 +0400
+++ b/jdk/src/share/classes/sun/java2d/pipe/BufferedContext.java	Fri Apr 12 15:28:49 2013 +0400
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2005, 2008, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2005, 2013, 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
@@ -99,8 +99,7 @@
     private int             validatedRGB;
     private int             validatedFlags;
     private boolean         xformInUse;
-    private int             transX;
-    private int             transY;
+    private AffineTransform transform;
 
     protected BufferedContext(RenderQueue rq) {
         this.rq = rq;
@@ -277,14 +276,11 @@
                 resetTransform();
                 xformInUse = false;
                 txChanged = true;
-            } else if (sg2d != null) {
-                if (transX != sg2d.transX || transY != sg2d.transY) {
-                    txChanged = true;
-                }
+            } else if (sg2d != null && !sg2d.transform.equals(transform)) {
+                txChanged = true;
             }
-            if (sg2d != null) {
-                transX = sg2d.transX;
-                transY = sg2d.transY;
+            if (sg2d != null && txChanged) {
+                transform = new AffineTransform(sg2d.transform);
             }
         } else {
             setTransform(xform);
--- a/jdk/src/share/classes/sun/java2d/pipe/DrawImage.java	Fri Apr 12 14:33:38 2013 +0400
+++ b/jdk/src/share/classes/sun/java2d/pipe/DrawImage.java	Fri Apr 12 15:28:49 2013 +0400
@@ -27,9 +27,7 @@
 
 import java.awt.AlphaComposite;
 import java.awt.Color;
-import java.awt.Graphics2D;
 import java.awt.Image;
-import java.awt.Rectangle;
 import java.awt.Transparency;
 import java.awt.geom.AffineTransform;
 import java.awt.geom.NoninvertibleTransformException;
@@ -38,15 +36,13 @@
 import java.awt.image.BufferedImageOp;
 import java.awt.image.ColorModel;
 import java.awt.image.DataBuffer;
-import java.awt.image.DirectColorModel;
 import java.awt.image.ImageObserver;
 import java.awt.image.IndexColorModel;
 import java.awt.image.Raster;
 import java.awt.image.VolatileImage;
-import java.awt.image.WritableRaster;
-import java.awt.image.ImagingOpException;
 import sun.awt.SunHints;
 import sun.awt.image.ImageRepresentation;
+import sun.awt.image.SurfaceManager;
 import sun.awt.image.ToolkitImage;
 import sun.java2d.InvalidPipeException;
 import sun.java2d.SunGraphics2D;
@@ -323,15 +319,17 @@
     BufferedImage makeBufferedImage(Image img, Color bgColor, int type,
                                     int sx1, int sy1, int sx2, int sy2)
     {
-        BufferedImage bimg = new BufferedImage(sx2-sx1, sy2-sy1, type);
-        Graphics2D g2d = bimg.createGraphics();
+        final int width = sx2 - sx1;
+        final int height = sy2 - sy1;
+        final BufferedImage bimg = new BufferedImage(width, height, type);
+        final SunGraphics2D g2d = (SunGraphics2D) bimg.createGraphics();
         g2d.setComposite(AlphaComposite.Src);
         if (bgColor != null) {
             g2d.setColor(bgColor);
-            g2d.fillRect(0, 0, sx2-sx1, sy2-sy1);
+            g2d.fillRect(0, 0, width, height);
             g2d.setComposite(AlphaComposite.SrcOver);
         }
-        g2d.drawImage(img, -sx1, -sy1, null);
+        g2d.copyImage(img, 0, 0, sx1, sy1, width, height, null, null);
         g2d.dispose();
         return bimg;
     }
@@ -737,8 +735,9 @@
         atfm.scale(m00, m11);
         atfm.translate(srcX-sx1, srcY-sy1);
 
-        int imgW = img.getWidth(null);
-        int imgH = img.getHeight(null);
+        final int scale = SurfaceManager.getImageScale(img);
+        final int imgW = img.getWidth(null) * scale;
+        final int imgH = img.getHeight(null) * scale;
         srcW += srcX;
         srcH += srcY;
         // Make sure we are not out of bounds
--- a/jdk/src/share/classes/sun/java2d/pipe/Region.java	Fri Apr 12 14:33:38 2013 +0400
+++ b/jdk/src/share/classes/sun/java2d/pipe/Region.java	Fri Apr 12 15:28:49 2013 +0400
@@ -131,6 +131,28 @@
         return newv;
     }
 
+    /**
+     * Multiply the scale factor {@code sv} and the value {@code v} with
+     * appropriate clipping to the bounds of Integer resolution. If the answer
+     * would be greater than {@code Integer.MAX_VALUE} then {@code
+     * Integer.MAX_VALUE} is returned. If the answer would be less than {@code
+     * Integer.MIN_VALUE} then {@code Integer.MIN_VALUE} is returned. Otherwise
+     * the multiplication is returned.
+     */
+    public static int clipScale(final int v, final double sv) {
+        if (sv == 1.0) {
+            return v;
+        }
+        final double newv = v * sv;
+        if (newv < Integer.MIN_VALUE) {
+            return Integer.MIN_VALUE;
+        }
+        if (newv > Integer.MAX_VALUE) {
+            return Integer.MAX_VALUE;
+        }
+        return (int) Math.round(newv);
+    }
+
     protected Region(int lox, int loy, int hix, int hiy) {
         this.lox = lox;
         this.loy = loy;
@@ -349,6 +371,79 @@
     }
 
     /**
+     * Returns a Region object that represents the same list of rectangles as
+     * the current Region object, scaled by the specified sx, sy factors.
+     */
+    public Region getScaledRegion(final double sx, final double sy) {
+        if (sx == 0 || sy == 0 || this == EMPTY_REGION) {
+            return EMPTY_REGION;
+        }
+        if ((sx == 1.0 && sy == 1.0) || (this == WHOLE_REGION)) {
+            return this;
+        }
+
+        int tlox = clipScale(lox, sx);
+        int tloy = clipScale(loy, sy);
+        int thix = clipScale(hix, sx);
+        int thiy = clipScale(hiy, sy);
+        Region ret = new Region(tlox, tloy, thix, thiy);
+        int bands[] = this.bands;
+        if (bands != null) {
+            int end = endIndex;
+            int newbands[] = new int[end];
+            int i = 0; // index for source bands
+            int j = 0; // index for translated newbands
+            int ncol;
+            while (i < end) {
+                int y1, y2;
+                newbands[j++] = y1   = clipScale(bands[i++], sy);
+                newbands[j++] = y2   = clipScale(bands[i++], sy);
+                newbands[j++] = ncol = bands[i++];
+                int savej = j;
+                if (y1 < y2) {
+                    while (--ncol >= 0) {
+                        int x1 = clipScale(bands[i++], sx);
+                        int x2 = clipScale(bands[i++], sx);
+                        if (x1 < x2) {
+                            newbands[j++] = x1;
+                            newbands[j++] = x2;
+                        }
+                    }
+                } else {
+                    i += ncol * 2;
+                }
+                // Did we get any non-empty bands in this row?
+                if (j > savej) {
+                    newbands[savej-1] = (j - savej) / 2;
+                } else {
+                    j = savej - 3;
+                }
+            }
+            if (j <= 5) {
+                if (j < 5) {
+                    // No rows or bands were generated...
+                    ret.lox = ret.loy = ret.hix = ret.hiy = 0;
+                } else {
+                    // Only generated one single rect in the end...
+                    ret.loy = newbands[0];
+                    ret.hiy = newbands[1];
+                    ret.lox = newbands[3];
+                    ret.hix = newbands[4];
+                }
+                // ret.endIndex and ret.bands were never initialized...
+                // ret.endIndex = 0;
+                // ret.newbands = null;
+            } else {
+                // Generated multiple bands and/or multiple rows...
+                ret.endIndex = j;
+                ret.bands = newbands;
+            }
+        }
+        return ret;
+    }
+
+
+    /**
      * Returns a Region object that represents the same list of
      * rectangles as the current Region object, translated by
      * the specified dx, dy translation factors.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/awt/Graphics2D/FillTexturePaint/FillTexturePaint.java	Fri Apr 12 15:28:49 2013 +0400
@@ -0,0 +1,98 @@
+/*
+ * Copyright (c) 2013, 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.
+ */
+
+import java.awt.AlphaComposite;
+import java.awt.Color;
+import java.awt.Graphics2D;
+import java.awt.GraphicsConfiguration;
+import java.awt.GraphicsEnvironment;
+import java.awt.Rectangle;
+import java.awt.TexturePaint;
+import java.awt.image.BufferedImage;
+import java.awt.image.VolatileImage;
+
+/**
+ * @test
+ * @bug 8000629
+ * @summary TexturePaint areas shouldn't separates.
+ * @author Sergey Bylokhov
+ */
+public class FillTexturePaint {
+
+    private static TexturePaint shape;
+    private static final int size = 400;
+
+    static {
+        BufferedImage bi = new BufferedImage(50, 50,
+                                             BufferedImage.TYPE_INT_RGB);
+        Graphics2D gi = bi.createGraphics();
+        gi.setBackground(Color.GREEN);
+        gi.clearRect(0, 0, 50, 50);
+        shape = new TexturePaint(bi, new Rectangle(0, 0, 50, 50));
+    }
+
+    public static void main(final String[] args) {
+        GraphicsEnvironment ge =
+                GraphicsEnvironment.getLocalGraphicsEnvironment();
+        GraphicsConfiguration gc =
+                ge.getDefaultScreenDevice().getDefaultConfiguration();
+        VolatileImage vi = gc.createCompatibleVolatileImage(size, size);
+        while (true) {
+            vi.validate(gc);
+            Graphics2D g2d = vi.createGraphics();
+            g2d.setComposite(AlphaComposite.Src);
+            g2d.setPaint(shape);
+            g2d.fill(new Rectangle(0, 0, size, size));
+            g2d.dispose();
+
+            if (vi.validate(gc) != VolatileImage.IMAGE_OK) {
+                try {
+                    Thread.sleep(100);
+                } catch (InterruptedException ignored) {
+                }
+                continue;
+            }
+
+            BufferedImage bi = vi.getSnapshot();
+
+            if (vi.contentsLost()) {
+                try {
+                    Thread.sleep(100);
+                } catch (InterruptedException ignored) {
+                }
+                continue;
+            }
+
+            for (int x = 0; x < size; ++x) {
+                for (int y = 0; y < size; ++y) {
+                    if (bi.getRGB(x, y) != Color.GREEN.getRGB()) {
+                        throw new RuntimeException("Test failed.");
+                    }
+                }
+            }
+            break;
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/awt/Graphics2D/FlipDrawImage/FlipDrawImage.java	Fri Apr 12 15:28:49 2013 +0400
@@ -0,0 +1,105 @@
+/*
+ * Copyright (c) 2013, 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.
+ *
+ * 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.
+ */
+
+
+import java.awt.AlphaComposite;
+import java.awt.Color;
+import java.awt.Graphics2D;
+import java.awt.GraphicsConfiguration;
+import java.awt.GraphicsEnvironment;
+import java.awt.image.BufferedImage;
+import java.awt.image.VolatileImage;
+
+/**
+ * @test
+ * @bug 8000629
+ * @author Sergey Bylokhov
+ */
+public final class FlipDrawImage {
+
+    private static final int width = 400;
+    private static final int height = 400;
+
+    public static void main(final String[] args) {
+        GraphicsEnvironment ge =
+                GraphicsEnvironment.getLocalGraphicsEnvironment();
+        GraphicsConfiguration gc =
+                ge.getDefaultScreenDevice().getDefaultConfiguration();
+        VolatileImage vi = gc.createCompatibleVolatileImage(width, height);
+        final BufferedImage bi = new BufferedImage(width, height,
+                                                   BufferedImage.TYPE_INT_ARGB);
+        while (true) {
+            vi.validate(gc);
+            Graphics2D g2d = vi.createGraphics();
+            g2d.setColor(Color.red);
+            g2d.fillRect(0, 0, width, height);
+            g2d.setColor(Color.green);
+            g2d.fillRect(0, 0, width / 2, height / 2);
+            g2d.dispose();
+
+            if (vi.validate(gc) != VolatileImage.IMAGE_OK) {
+                try {
+                    Thread.sleep(100);
+                } catch (InterruptedException ignored) {
+                }
+                continue;
+            }
+
+            Graphics2D g = bi.createGraphics();
+            g.setComposite(AlphaComposite.Src);
+            g.setColor(Color.BLUE);
+            g.fillRect(0, 0, width, height);
+            // destination width and height are flipped and scale is used.
+            g.drawImage(vi, width / 2, height / 2, -width / 2, -height / 2,
+                        null, null);
+            g.dispose();
+
+            if (vi.contentsLost()) {
+                try {
+                    Thread.sleep(100);
+                } catch (InterruptedException ignored) {
+                }
+                continue;
+            }
+
+            for (int x = 0; x < width; ++x) {
+                for (int y = 0; y < height; ++y) {
+                    if (x < width / 2 && y < height / 2) {
+                        if (x >= width / 4 && y >= height / 4) {
+                            if (bi.getRGB(x, y) != Color.green.getRGB()) {
+                                throw new RuntimeException("Test failed.");
+                            }
+                        } else if (bi.getRGB(x, y) != Color.red.getRGB()) {
+                            throw new RuntimeException("Test failed.");
+                        }
+                    } else {
+                        if (bi.getRGB(x, y) != Color.BLUE.getRGB()) {
+                            throw new RuntimeException("Test failed.");
+                        }
+                    }
+                }
+            }
+            break;
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/awt/Graphics2D/TransformSetGet/TransformSetGet.java	Fri Apr 12 15:28:49 2013 +0400
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2013, 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.
+ *
+ * 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.
+ */
+
+import java.awt.GraphicsConfiguration;
+import java.awt.GraphicsEnvironment;
+import java.awt.geom.AffineTransform;
+import java.awt.image.VolatileImage;
+
+import sun.java2d.SunGraphics2D;
+
+/**
+ * @test
+ * @bug 8000629
+ * @summary Set/get transform should work on constrained graphics.
+ * @author Sergey Bylokhov
+ */
+public class TransformSetGet {
+
+    public static void main(final String[] args) {
+        final GraphicsEnvironment ge =
+                GraphicsEnvironment.getLocalGraphicsEnvironment();
+        final GraphicsConfiguration gc =
+                ge.getDefaultScreenDevice().getDefaultConfiguration();
+        final VolatileImage vi = gc.createCompatibleVolatileImage(200, 200);
+        final SunGraphics2D sg2d = (SunGraphics2D) vi.createGraphics();
+
+        sg2d.constrain(0, 61, 100, 100);
+        final AffineTransform expected = sg2d.cloneTransform();
+        sg2d.setTransform(sg2d.getTransform());
+        final AffineTransform actual = sg2d.cloneTransform();
+        sg2d.dispose();
+        vi.flush();
+        if (!expected.equals(actual)) {
+            System.out.println("Expected = " + expected);
+            System.out.println("Actual = " + actual);
+            throw new RuntimeException("Wrong transform");
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/awt/image/DrawImage/IncorrectBounds.java	Fri Apr 12 15:28:49 2013 +0400
@@ -0,0 +1,102 @@
+/*
+ * Copyright (c) 2013, 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.
+ *
+ * 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.
+ */
+
+
+import java.awt.AlphaComposite;
+import java.awt.Color;
+import java.awt.Graphics2D;
+import java.awt.GraphicsConfiguration;
+import java.awt.GraphicsEnvironment;
+import java.awt.image.BufferedImage;
+import java.awt.image.VolatileImage;
+
+/**
+ * @test
+ * @bug 8000629
+ * @summary Temporary backbuffer in the DrawImage should not fill background
+ * outside of source image bounds.
+ * @author Sergey Bylokhov
+ */
+public final class IncorrectBounds {
+
+    private static final int width = 400;
+    private static final int height = 400;
+
+    public static void main(final String[] args) {
+        GraphicsEnvironment ge =
+                GraphicsEnvironment.getLocalGraphicsEnvironment();
+        GraphicsConfiguration gc =
+                ge.getDefaultScreenDevice().getDefaultConfiguration();
+        VolatileImage vi = gc.createCompatibleVolatileImage(width / 4,
+                                                            height / 4);
+        final BufferedImage bi = new BufferedImage(width, height,
+                                                   BufferedImage.TYPE_INT_ARGB);
+        while (true) {
+            vi.validate(gc);
+            Graphics2D g2d = vi.createGraphics();
+            g2d.setColor(Color.green);
+            g2d.fillRect(0, 0, width / 4, height / 4);
+            g2d.dispose();
+
+            if (vi.validate(gc) != VolatileImage.IMAGE_OK) {
+                try {
+                    Thread.sleep(100);
+                } catch (InterruptedException ignored) {
+                }
+                continue;
+            }
+
+            Graphics2D g = bi.createGraphics();
+            g.setComposite(AlphaComposite.Src);
+            g.setColor(Color.red);
+            g.fillRect(0, 0, width, height);
+            // Use sx and sy outside of VI bounds.
+            g.drawImage(vi, 0, 0, width / 2, height / 2, 0, 0, width * 2,
+                        height * 2, null);
+            g.dispose();
+
+            if (vi.contentsLost()) {
+                try {
+                    Thread.sleep(100);
+                } catch (InterruptedException ignored) {
+                }
+                continue;
+            }
+
+            for (int x = 0; x < width; ++x) {
+                for (int y = 0; y < height; ++y) {
+                    if (x < width / 16 && y < height / 16) {
+                        if (bi.getRGB(x, y) != Color.green.getRGB()) {
+                            throw new RuntimeException("Test failed.");
+                        }
+                    } else {
+                        if (bi.getRGB(x, y) != Color.red.getRGB()) {
+                            throw new RuntimeException("Test failed.");
+                        }
+                    }
+                }
+            }
+            break;
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/awt/image/DrawImage/IncorrectOffset.java	Fri Apr 12 15:28:49 2013 +0400
@@ -0,0 +1,93 @@
+/*
+ * Copyright (c) 2013, 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.
+ *
+ * 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.
+ */
+
+import java.awt.AlphaComposite;
+import java.awt.Color;
+import java.awt.Graphics2D;
+import java.awt.GraphicsConfiguration;
+import java.awt.GraphicsEnvironment;
+import java.awt.image.BufferedImage;
+import java.awt.image.VolatileImage;
+
+/**
+ * @test
+ * @bug 8000629
+ * @summary Temporary backbuffer in the DrawImage should have correct offset.
+ * @author Sergey Bylokhov
+ */
+public final class IncorrectOffset {
+
+    private static final int width = 400;
+    private static final int height = 400;
+
+    public static void main(final String[] args) {
+        GraphicsEnvironment ge =
+                GraphicsEnvironment.getLocalGraphicsEnvironment();
+        GraphicsConfiguration gc =
+                ge.getDefaultScreenDevice().getDefaultConfiguration();
+        VolatileImage vi = gc.createCompatibleVolatileImage(width, height);
+        BufferedImage bi = new BufferedImage(width / 4, height / 4,
+                                             BufferedImage.TYPE_INT_ARGB);
+        while (true) {
+            vi.validate(gc);
+            Graphics2D g2d = vi.createGraphics();
+            g2d.setColor(Color.black);
+            g2d.fillRect(0, 0, width, height);
+            g2d.setColor(Color.green);
+            g2d.fillRect(width / 4, height / 4, width / 2, height / 2);
+            g2d.dispose();
+
+            if (vi.validate(gc) != VolatileImage.IMAGE_OK) {
+                try {
+                    Thread.sleep(100);
+                } catch (InterruptedException ignored) {
+                }
+                continue;
+            }
+
+            Graphics2D g = bi.createGraphics();
+            g.setComposite(AlphaComposite.Src);
+            // Scale part of VI to BI. Only green area should be copied.
+            g.drawImage(vi, 0, 0, width / 4, height / 4, width / 4, height / 4,
+                        width / 4 + width / 2, height / 4 + height / 2, null);
+            g.dispose();
+
+            if (vi.contentsLost()) {
+                try {
+                    Thread.sleep(100);
+                } catch (InterruptedException ignored) {
+                }
+                continue;
+            }
+
+            for (int x = 0; x < width / 4; ++x) {
+                for (int y = 0; y < height / 4; ++y) {
+                    if (bi.getRGB(x, y) != Color.green.getRGB()) {
+                        throw new RuntimeException("Test failed.");
+                    }
+                }
+            }
+            break;
+        }
+    }
+}