--- old/src/macosx/classes/sun/awt/CGraphicsDevice.java 2013-05-13 18:46:58.295214300 +0400 +++ new/src/macosx/classes/sun/awt/CGraphicsDevice.java 2013-05-13 18:46:58.117204100 +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); --- old/src/macosx/classes/sun/java2d/opengl/CGLGraphicsConfig.java 2013-05-13 18:46:59.935308100 +0400 +++ new/src/macosx/classes/sun/java2d/opengl/CGLGraphicsConfig.java 2013-05-13 18:46:59.757297900 +0400 @@ -515,29 +515,37 @@ @Override public int getMaxTextureWidth() { - int width; - - synchronized (totalDisplayBounds) { - if (totalDisplayBounds.width == 0) { - updateTotalDisplayBounds(); - } - width = totalDisplayBounds.width; - } - - return Math.min(width, getMaxTextureSize()); + //Temporary disable this logic and use some magic constrain. + /* + int width; + + synchronized (totalDisplayBounds) { + if (totalDisplayBounds.width == 0) { + updateTotalDisplayBounds(); + } + width = totalDisplayBounds.width; + } + + return Math.min(width, getMaxTextureSize()); + */ + return getMaxTextureSize() / (getDevice().getScaleFactor() * 2); } @Override public int getMaxTextureHeight() { - int height; - - synchronized (totalDisplayBounds) { - if (totalDisplayBounds.height == 0) { - updateTotalDisplayBounds(); - } - height = totalDisplayBounds.height; - } - - return Math.min(height, getMaxTextureSize()); + //Temporary disable this logic and use some magic constrain. + /* + int height; + + synchronized (totalDisplayBounds) { + if (totalDisplayBounds.height == 0) { + updateTotalDisplayBounds(); + } + height = totalDisplayBounds.height; + } + + return Math.min(height, getMaxTextureSize()); + */ + return getMaxTextureSize() / (getDevice().getScaleFactor() * 2); } } --- old/src/macosx/classes/sun/java2d/opengl/CGLLayer.java 2013-05-13 18:47:01.539399800 +0400 +++ new/src/macosx/classes/sun/java2d/opengl/CGLLayer.java 2013-05-13 18:47:01.354389200 +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 // ---------------------------------------------------------------------- --- old/src/macosx/classes/sun/java2d/opengl/CGLSurfaceData.java 2013-05-13 18:47:03.092488600 +0400 +++ new/src/macosx/classes/sun/java2d/opengl/CGLSurfaceData.java 2013-05-13 18:47:02.913478400 +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 --- old/src/macosx/classes/sun/lwawt/LWComponentPeer.java 2013-05-13 18:47:04.682579600 +0400 +++ new/src/macosx/classes/sun/lwawt/LWComponentPeer.java 2013-05-13 18:47:04.501569200 +0400 @@ -448,35 +448,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() { --- old/src/macosx/classes/sun/lwawt/LWWindowPeer.java 2013-05-13 18:47:06.346674800 +0400 +++ new/src/macosx/classes/sun/lwawt/LWWindowPeer.java 2013-05-13 18:47:06.161664200 +0400 @@ -656,17 +656,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(); } @@ -686,7 +685,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); @@ -961,7 +960,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; } @@ -969,11 +968,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; } @@ -981,13 +983,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; } /* @@ -1046,7 +1049,7 @@ g.setColor(nonOpaqueBackground); g.fillRect(0, 0, r.width, r.height); if (g instanceof SunGraphics2D) { - SG2DConstraint((SunGraphics2D) g, getRegion()); + ((SunGraphics2D) g).constrain(0, 0, r.width, r.height, getRegion()); } if (!isTextured()) { g.setColor(getBackground()); @@ -1062,6 +1065,7 @@ } } } + flushOnscreenGraphics(); } private void blitSurfaceData(final SurfaceData src, final SurfaceData dst) { @@ -1069,14 +1073,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); } } } --- old/src/macosx/classes/sun/lwawt/macosx/CPlatformWindow.java 2013-05-13 18:47:08.006769700 +0400 +++ new/src/macosx/classes/sun/lwawt/macosx/CPlatformWindow.java 2013-05-13 18:47:07.827759500 +0400 @@ -884,8 +884,8 @@ } } - private void flushBuffers() { - if (isVisible() && !nativeBounds.isEmpty()) { + void flushBuffers() { + if (isVisible() && !nativeBounds.isEmpty() && !isFullScreenMode) { try { LWCToolkit.invokeAndWait(new Runnable() { @Override --- old/src/macosx/native/sun/awt/CGraphicsDevice.m 2013-05-13 18:47:09.645863500 +0400 +++ new/src/macosx/native/sun/awt/CGraphicsDevice.m 2013-05-13 18:47:09.454852600 +0400 @@ -276,3 +276,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; +} --- old/src/macosx/native/sun/java2d/opengl/CGLLayer.m 2013-05-13 18:47:11.221953600 +0400 +++ new/src/macosx/native/sun/java2d/opengl/CGLLayer.m 2013-05-13 18:47:11.038943200 +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); - glViewport(0, 0, textureWidth, textureHeight); + // 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); +} --- old/src/share/classes/sun/awt/image/SurfaceManager.java 2013-05-13 18:47:12.803044100 +0400 +++ new/src/share/classes/sun/awt/image/SurfaceManager.java 2013-05-13 18:47:12.618033500 +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(); + } } --- old/src/share/classes/sun/java2d/SunGraphics2D.java 2013-05-13 18:47:14.367133500 +0400 +++ new/src/share/classes/sun/java2d/SunGraphics2D.java 2013-05-13 18:47:14.182122900 +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; /** @@ -203,13 +203,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; @@ -252,6 +254,12 @@ validateColor(); + devScale = sd.getDefaultScale(); + if (devScale != 1) { + transform.setToScale(devScale, devScale); + invalidateTransform(); + } + font = f; if (font == null) { font = defaultFont; @@ -316,38 +324,40 @@ /** * Constrain rendering for lightweight objects. - * - * REMIND: This method will back off to the "workaround" - * of using translate and clipRect if the Graphics - * to be constrained has a complex transform. The - * drawback of the workaround is that the resulting - * clip and device origin cannot be "enforced". - * - * @exception IllegalStateException If the Graphics - * to be constrained has a complex transform. */ - public void constrain(int x, int y, int w, int h) { - if ((x|y) != 0) { + public void constrain(int x, int y, int w, int h, Region region) { + if ((x | y) != 0) { translate(x, y); } - if (transformState >= TRANSFORM_TRANSLATESCALE) { + 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); + // 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 (c == constrainClip) { - // Common case to ignore - return; - } } + 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); @@ -355,6 +365,23 @@ } } + /** + * Constrain rendering for lightweight objects. + * + * REMIND: This method will back off to the "workaround" + * of using translate and clipRect if the Graphics + * to be constrained has a complex transform. The + * drawback of the workaround is that the resulting + * clip and device origin cannot be "enforced". + * + * @exception IllegalStateException If the Graphics + * to be constrained has a complex transform. + */ + @Override + public void constrain(int x, int y, int w, int h) { + constrain(x, y, w, h, null); + } + protected static ValidatePipe invalidpipe = new ValidatePipe(); /* @@ -1536,11 +1563,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(); @@ -1598,12 +1627,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; } @@ -2987,6 +3019,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. @@ -3000,8 +3063,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, @@ -3039,7 +3103,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); } @@ -3080,6 +3152,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) { @@ -3128,6 +3207,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))) { @@ -3206,6 +3290,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) { --- old/src/share/classes/sun/java2d/SurfaceData.java 2013-05-13 18:47:16.188237700 +0400 +++ new/src/share/classes/sun/java2d/SurfaceData.java 2013-05-13 18:47:16.003227100 +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; + } } --- old/src/share/classes/sun/java2d/pipe/BufferedContext.java 2013-05-13 18:47:17.821331100 +0400 +++ new/src/share/classes/sun/java2d/pipe/BufferedContext.java 2013-05-13 18:47:17.637320600 +0400 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2011, 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 @@ -95,8 +95,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; @@ -273,14 +272,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); --- old/src/share/classes/sun/java2d/pipe/DrawImage.java 2013-05-13 18:47:19.405421700 +0400 +++ new/src/share/classes/sun/java2d/pipe/DrawImage.java 2013-05-13 18:47:19.214410800 +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 --- old/src/share/classes/sun/java2d/pipe/Region.java 2013-05-13 18:47:21.050515800 +0400 +++ new/src/share/classes/sun/java2d/pipe/Region.java 2013-05-13 18:47:20.860504900 +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 2013-05-13 18:47:22.000000000 +0400 +++ new/test/java/awt/Graphics2D/FillTexturePaint/FillTexturePaint.java 2013-05-13 18:47:22.486597900 +0400 @@ -0,0 +1,96 @@ +/* + * 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.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 2013-05-13 18:47:24.000000000 +0400 +++ new/test/java/awt/Graphics2D/FlipDrawImage/FlipDrawImage.java 2013-05-13 18:47:23.701667400 +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 2013-05-13 18:47:25.000000000 +0400 +++ new/test/java/awt/Graphics2D/TransformSetGet/TransformSetGet.java 2013-05-13 18:47:24.916736900 +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 2013-05-13 18:47:26.000000000 +0400 +++ new/test/java/awt/image/DrawImage/IncorrectBounds.java 2013-05-13 18:47:26.153807700 +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 2013-05-13 18:47:27.000000000 +0400 +++ new/test/java/awt/image/DrawImage/IncorrectOffset.java 2013-05-13 18:47:27.357876500 +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; + } + } +}