8041129: [OGL] surface->sw blit is extremely slow
8017626: [OGL] Translucent VolatileImages don't paint correctly
Reviewed-by: bae, flar
--- a/jdk/src/share/classes/sun/java2d/opengl/OGLBlitLoops.java Tue Jun 03 17:03:29 2014 -0700
+++ b/jdk/src/share/classes/sun/java2d/opengl/OGLBlitLoops.java Wed Jun 04 16:55:06 2014 +0400
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2014, 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
@@ -71,6 +71,8 @@
// surface->sw ops
new OGLSurfaceToSwBlit(SurfaceType.IntArgb,
OGLSurfaceData.PF_INT_ARGB),
+ new OGLSurfaceToSwBlit(SurfaceType.IntArgbPre,
+ OGLSurfaceData.PF_INT_ARGB_PRE),
// sw->surface ops
blitIntArgbPreToSurface,
@@ -505,12 +507,12 @@
}
}
-class OGLSurfaceToSwBlit extends Blit {
+final class OGLSurfaceToSwBlit extends Blit {
- private int typeval;
+ private final int typeval;
- // REMIND: destination will actually be opaque/premultiplied...
- OGLSurfaceToSwBlit(SurfaceType dstType, int typeval) {
+ // destination will actually be ArgbPre or Argb
+ OGLSurfaceToSwBlit(final SurfaceType dstType,final int typeval) {
super(OGLSurfaceData.OpenGLSurface,
CompositeType.SrcNoEa,
dstType);
--- a/jdk/src/share/native/sun/java2d/opengl/OGLBlitLoops.c Tue Jun 03 17:03:29 2014 -0700
+++ b/jdk/src/share/native/sun/java2d/opengl/OGLBlitLoops.c Wed Jun 04 16:55:06 2014 +0400
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2014, 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
@@ -34,6 +34,10 @@
#include "OGLSurfaceData.h"
#include "GraphicsPrimitiveMgr.h"
+#include <stdlib.h> // malloc
+#include <string.h> // memcpy
+#include "IntArgbPre.h"
+
extern OGLPixelFormat PixelFormats[];
/**
@@ -335,6 +339,9 @@
0, 0, sw, sh,
pf->format, pf->type,
srcInfo->rasBase);
+
+ j2d_glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0);
+ j2d_glPixelStorei(GL_UNPACK_SKIP_ROWS, 0);
}
// the texture image is "right side up", so we align the
@@ -697,6 +704,50 @@
}
/**
+ * This method makes vertical flip of the provided area of Surface and convert
+ * pixel's data from argbPre to argb format if requested.
+ */
+void flip(void *pDst, juint w, juint h, jint scanStride, jboolean convert) {
+ const size_t clippedStride = 4 * w;
+ void *tempRow = (h > 1 && !convert) ? malloc(clippedStride) : NULL;
+ juint i = 0;
+ juint step = 0;
+ // vertical flip and convert argbpre to argb if necessary
+ for (; i < h / 2; ++i) {
+ juint *r1 = PtrAddBytes(pDst, (i * scanStride));
+ juint *r2 = PtrAddBytes(pDst, (h - i - 1) * scanStride);
+ if (tempRow) {
+ // fast path
+ memcpy(tempRow, r1, clippedStride);
+ memcpy(r1, r2, clippedStride);
+ memcpy(r2, tempRow, clippedStride);
+ } else {
+ // slow path
+ for (step = 0; step < w; ++step) {
+ juint tmp = r1[step];
+ if (convert) {
+ LoadIntArgbPreTo1IntArgb(r2, 0, step, r1[step]);
+ LoadIntArgbPreTo1IntArgb(&tmp, 0, 0, r2[step]);
+ } else {
+ r1[step] = r2[step];
+ r2[step] = tmp;
+ }
+ }
+ }
+ }
+ // convert the middle line if necessary
+ if (convert && h % 2) {
+ juint *r1 = PtrAddBytes(pDst, (i * scanStride));
+ for (step = 0; step < w; ++step) {
+ LoadIntArgbPreTo1IntArgb(r1, 0, step, r1[step]);
+ }
+ }
+ if (tempRow) {
+ free(tempRow);
+ }
+}
+
+/**
* Specialized blit method for copying a native OpenGL "Surface" (pbuffer,
* window, etc.) to a system memory ("Sw") surface.
*/
@@ -758,7 +809,9 @@
width = srcInfo.bounds.x2 - srcInfo.bounds.x1;
height = srcInfo.bounds.y2 - srcInfo.bounds.y1;
- j2d_glPixelStorei(GL_PACK_SKIP_PIXELS, dstx);
+ pDst = PtrAddBytes(pDst, dstx * dstInfo.pixelStride);
+ pDst = PtrAddBytes(pDst, dsty * dstInfo.scanStride);
+
j2d_glPixelStorei(GL_PACK_ROW_LENGTH,
dstInfo.scanStride / dstInfo.pixelStride);
j2d_glPixelStorei(GL_PACK_ALIGNMENT, pf.alignment);
@@ -779,27 +832,20 @@
// this accounts for lower-left origin of the source region
srcx = srcOps->xOffset + srcx;
- srcy = srcOps->yOffset + srcOps->height - (srcy + 1);
+ srcy = srcOps->yOffset + srcOps->height - srcy - height;
- // we must read one scanline at a time because there is no way
- // to read starting at the top-left corner of the source region
- while (height > 0) {
- j2d_glPixelStorei(GL_PACK_SKIP_ROWS, dsty);
- j2d_glReadPixels(srcx, srcy, width, 1,
- pf.format, pf.type, pDst);
- srcy--;
- dsty++;
- height--;
- }
-
+ // Note that glReadPixels() is extremely slow!
+ // So we call it only once and flip the image using memcpy.
+ j2d_glReadPixels(srcx, srcy, width, height,
+ pf.format, pf.type, pDst);
+ // It was checked above that width and height are positive.
+ flip(pDst, (juint) width, (juint) height, dstInfo.scanStride,
+ !pf.isPremult && !srcOps->isOpaque);
#ifdef MACOSX
if (srcOps->isOpaque) {
j2d_glPixelTransferf(GL_ALPHA_BIAS, 0.0);
}
#endif
-
- j2d_glPixelStorei(GL_PACK_SKIP_PIXELS, 0);
- j2d_glPixelStorei(GL_PACK_SKIP_ROWS, 0);
j2d_glPixelStorei(GL_PACK_ROW_LENGTH, 0);
j2d_glPixelStorei(GL_PACK_ALIGNMENT, 4);
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/awt/image/DrawImage/IncorrectAlphaSurface2SW.java Wed Jun 04 16:55:06 2014 +0400
@@ -0,0 +1,161 @@
+/*
+ * Copyright (c) 2014, 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;
+import java.awt.image.BufferedImage;
+import java.awt.image.VolatileImage;
+import java.io.File;
+import java.io.IOException;
+
+import javax.imageio.ImageIO;
+
+import static java.awt.Transparency.TRANSLUCENT;
+import static java.awt.image.BufferedImage.TYPE_4BYTE_ABGR;
+import static java.awt.image.BufferedImage.TYPE_4BYTE_ABGR_PRE;
+import static java.awt.image.BufferedImage.TYPE_INT_ARGB;
+import static java.awt.image.BufferedImage.TYPE_INT_ARGB_PRE;
+
+/**
+ * @test
+ * @bug 8017626
+ * @summary Tests drawing transparent volatile image to transparent BI.
+ * Results of the blit compatibleImage to transparent BI used for
+ * comparison.
+ * @author Sergey Bylokhov
+ */
+public final class IncorrectAlphaSurface2SW {
+
+ private static final int[] SCALES = {1, 2, 4, 8};
+ private static final int[] SIZES = {1, 2, 3, 127, 128, 254, 255, 256};
+ private static final int[] dstTypes = {TYPE_INT_ARGB, TYPE_INT_ARGB_PRE,
+ TYPE_4BYTE_ABGR, TYPE_4BYTE_ABGR_PRE};
+ private static final int[] srcTypes = {TRANSLUCENT};
+
+
+ public static void main(final String[] args) throws IOException {
+ GraphicsEnvironment ge = GraphicsEnvironment
+ .getLocalGraphicsEnvironment();
+ GraphicsConfiguration gc = ge.getDefaultScreenDevice()
+ .getDefaultConfiguration();
+ BufferedImage destVI;
+ BufferedImage destBI;
+ BufferedImage sourceBI;
+ VolatileImage sourceVI;
+
+ for (final int s : SIZES) {
+ for (final int srcType : srcTypes) {
+ for (final int dstType : dstTypes) {
+ for (final int scale : SCALES) {
+ int sw = s * scale;
+ destVI = new BufferedImage(sw, sw, dstType);
+ destBI = new BufferedImage(sw, sw, dstType);
+ sourceBI = gc.createCompatibleImage(sw, sw, srcType);
+ sourceVI = gc.createCompatibleVolatileImage(s, s, srcType);
+
+ // draw to dest BI using compatible image
+ fill(sourceBI, s);
+ Graphics2D big = destBI.createGraphics();
+ big.setComposite(AlphaComposite.Src);
+ big.drawImage(sourceBI, 0, 0, sw, sw, null);
+ big.dispose();
+
+ // draw to dest BI using compatible image
+ fill(sourceVI, s);
+ drawVItoBI(gc, destVI, sourceVI);
+
+ validate(destVI, destBI);
+ sourceVI.flush();
+ }
+ }
+ }
+ }
+ System.out.println("Test PASSED");
+ }
+
+ private static void drawVItoBI(GraphicsConfiguration gc,
+ BufferedImage bi, VolatileImage vi) {
+ while (true) {
+ vi.validate(gc);
+ fill(vi, vi.getHeight());
+ if (vi.validate(gc) != VolatileImage.IMAGE_OK) {
+ try {
+ Thread.sleep(100);
+ } catch (final InterruptedException ignored) {
+ }
+ continue;
+ }
+
+ Graphics2D big = bi.createGraphics();
+ big.setComposite(AlphaComposite.Src);
+ big.drawImage(vi, 0, 0, bi.getWidth(), bi.getHeight(), null);
+ big.dispose();
+
+ if (vi.contentsLost()) {
+ try {
+ Thread.sleep(100);
+ } catch (final InterruptedException ignored) {
+ }
+ continue;
+ }
+ break;
+ }
+ }
+
+ private static void validate(BufferedImage bi, BufferedImage gold)
+ throws IOException {
+ for (int x = 0; x < bi.getWidth(); ++x) {
+ for (int y = 0; y < bi.getHeight(); ++y) {
+ if (gold.getRGB(x, y) != bi.getRGB(x, y)) {
+ System.err.println("Expected color = " + gold.getRGB(x, y));
+ System.err.println("Actual color = " + bi.getRGB(x, y));
+ ImageIO.write(gold, "png", new File("gold.png"));
+ ImageIO.write(bi, "png", new File("bi.png"));
+ throw new RuntimeException("Test failed.");
+ }
+ }
+ }
+ }
+
+ /**
+ * Fills the whole image using different alpha for each row.
+ *
+ * @param image to fill
+ */
+ private static void fill(final Image image, final int size) {
+ Graphics2D graphics = (Graphics2D) image.getGraphics();
+ graphics.setComposite(AlphaComposite.Src);
+ graphics.setColor(Color.GREEN);
+ graphics.fillRect(0, 0, image.getWidth(null), image.getHeight(null));
+ int row = image.getHeight(null) / size;
+ for (int i = 0; i < size; ++i) {
+ graphics.setColor(new Color(23, 127, 189, i));
+ graphics.fillRect(0, i * row, image.getWidth(null), row);
+ }
+ graphics.dispose();
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/awt/image/DrawImage/IncorrectDestinationOffset.java Wed Jun 04 16:55:06 2014 +0400
@@ -0,0 +1,164 @@
+/*
+ * Copyright (c) 2014, 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;
+import java.io.File;
+import java.io.IOException;
+
+import javax.imageio.ImageIO;
+
+/**
+ * @test
+ * @bug 8041129
+ * @summary Destination offset should be correct in case of Surface->SW blit.
+ * Destination outside of the drawing area should be untouched.
+ * @author Sergey Bylokhov
+ */
+public final class IncorrectDestinationOffset {
+
+ private static final int SIZE = 128;
+ private static final double[] SCALES = {0.25, 0.5, 1, 1.5, 2.0, 4};
+
+ public static void main(final String[] args) throws IOException {
+ GraphicsEnvironment ge = GraphicsEnvironment
+ .getLocalGraphicsEnvironment();
+ GraphicsConfiguration gc = ge.getDefaultScreenDevice()
+ .getDefaultConfiguration();
+ VolatileImage vi = gc.createCompatibleVolatileImage(SIZE, SIZE);
+ BufferedImage bi = new BufferedImage(SIZE, SIZE,
+ BufferedImage.TYPE_INT_ARGB);
+ for (double scale : SCALES) {
+ while (true) {
+ // initialize Volatile Image
+ vi.validate(gc);
+ Graphics2D g2d = vi.createGraphics();
+ g2d.setColor(Color.green);
+ g2d.fillRect(0, 0, SIZE, SIZE);
+ g2d.dispose();
+
+ if (vi.validate(gc) != VolatileImage.IMAGE_OK) {
+ try {
+ Thread.sleep(100);
+ } catch (InterruptedException ignored) {
+ }
+ continue;
+ }
+ // Draw the VolatileImage to BI with scale and offsets
+ Graphics2D g = bi.createGraphics();
+ g.setComposite(AlphaComposite.Src);
+ g.setColor(Color.RED);
+ g.fillRect(0, 0, SIZE / 2, SIZE / 2);
+ g.setColor(Color.BLUE);
+ g.fillRect(SIZE / 2, 0, SIZE / 2, SIZE / 2);
+ g.setColor(Color.ORANGE);
+ g.fillRect(0, SIZE / 2, SIZE / 2, SIZE / 2);
+ g.setColor(Color.MAGENTA);
+ g.fillRect(SIZE / 2, SIZE / 2, SIZE / 2, SIZE / 2);
+
+ int point2draw = (int) (100 * scale);
+ int size2draw = (int) (SIZE * scale);
+ g.drawImage(vi, point2draw, point2draw, size2draw, size2draw,
+ null);
+ g.dispose();
+
+ if (vi.contentsLost()) {
+ try {
+ Thread.sleep(100);
+ } catch (InterruptedException ignored) {
+ }
+ continue;
+ }
+ validate(bi, point2draw, size2draw);
+ break;
+ }
+ }
+ }
+
+ private static void validate(BufferedImage bi, int point2draw,
+ int size2draw)
+ throws IOException {
+ for (int x = 0; x < SIZE; ++x) {
+ for (int y = 0; y < SIZE; ++y) {
+ if (isInsideGreenArea(point2draw, size2draw, x, y)) {
+ if (bi.getRGB(x, y) != Color.green.getRGB()) {
+ ImageIO.write(bi, "png", new File("image.png"));
+ throw new RuntimeException("Test failed.");
+ }
+ } else {
+ if (isRedArea(x, y)) {
+ if (bi.getRGB(x, y) != Color.red.getRGB()) {
+ ImageIO.write(bi, "png", new File("image.png"));
+ throw new RuntimeException("Test failed.");
+ }
+ }
+ if (isBlueArea(x, y)) {
+ if (bi.getRGB(x, y) != Color.blue.getRGB()) {
+ ImageIO.write(bi, "png", new File("image.png"));
+ throw new RuntimeException("Test failed.");
+ }
+ }
+ if (isOrangeArea(x, y)) {
+ if (bi.getRGB(x, y) != Color.orange.getRGB()) {
+ ImageIO.write(bi, "png", new File("image.png"));
+ throw new RuntimeException("Test failed.");
+ }
+ }
+ if (isMagentaArea(x, y)) {
+ if (bi.getRGB(x, y) != Color.magenta.getRGB()) {
+ ImageIO.write(bi, "png", new File("image.png"));
+ throw new RuntimeException("Test failed.");
+ }
+ }
+ }
+ }
+ }
+ }
+
+ private static boolean isRedArea(int x, int y) {
+ return x < SIZE / 2 && y < SIZE / 2;
+ }
+
+ private static boolean isBlueArea(int x, int y) {
+ return x >= SIZE / 2 && y < SIZE / 2;
+ }
+
+ private static boolean isOrangeArea(int x, int y) {
+ return x < SIZE / 2 && y >= SIZE / 2;
+ }
+
+ private static boolean isMagentaArea(int x, int y) {
+ return x >= SIZE / 2 && y >= SIZE / 2;
+ }
+
+ private static boolean isInsideGreenArea(int point2draw, int size2draw,
+ int x, int y) {
+ return x >= point2draw && x < point2draw + size2draw && y >=
+ point2draw && y < point2draw + size2draw;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/awt/image/DrawImage/IncorrectSourceOffset.java Wed Jun 04 16:55:06 2014 +0400
@@ -0,0 +1,100 @@
+/*
+ * Copyright (c) 2014, 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;
+import java.awt.image.BufferedImage;
+import java.awt.image.VolatileImage;
+import java.io.File;
+import java.io.IOException;
+
+import javax.imageio.ImageIO;
+
+/**
+ * @test
+ * @bug 8041129
+ * @summary Tests asymmetric source offsets.
+ * @author Sergey Bylokhov
+ */
+public final class IncorrectSourceOffset {
+
+ public static void main(final String[] args) throws IOException {
+ GraphicsEnvironment ge = GraphicsEnvironment
+ .getLocalGraphicsEnvironment();
+ GraphicsConfiguration gc = ge.getDefaultScreenDevice()
+ .getDefaultConfiguration();
+ VolatileImage vi = gc.createCompatibleVolatileImage(511, 255);
+ BufferedImage bi = new BufferedImage(511, 255,
+ BufferedImage.TYPE_INT_ARGB);
+ BufferedImage gold = new BufferedImage(511, 255,
+ BufferedImage.TYPE_INT_ARGB);
+ fill(gold);
+ while (true) {
+ vi.validate(gc);
+ fill(vi);
+ if (vi.validate(gc) != VolatileImage.IMAGE_OK) {
+ try {
+ Thread.sleep(100);
+ } catch (final InterruptedException ignored) {
+ }
+ continue;
+ }
+
+ Graphics2D big = bi.createGraphics();
+ big.drawImage(vi, 7, 11, 127, 111, 7, 11, 127, 111, null);
+ big.dispose();
+ if (vi.contentsLost()) {
+ try {
+ Thread.sleep(100);
+ } catch (final InterruptedException ignored) {
+ }
+ continue;
+ }
+ break;
+ }
+
+ for (int x = 7; x < 127; ++x) {
+ for (int y = 11; y < 111; ++y) {
+ if (gold.getRGB(x, y) != bi.getRGB(x, y)) {
+ ImageIO.write(gold, "png", new File("gold.png"));
+ ImageIO.write(bi, "png", new File("bi.png"));
+ throw new RuntimeException("Test failed.");
+ }
+ }
+ }
+ }
+
+ private static void fill(Image image) {
+ Graphics2D graphics = (Graphics2D) image.getGraphics();
+ graphics.setComposite(AlphaComposite.Src);
+ for (int i = 0; i < image.getHeight(null); ++i) {
+ graphics.setColor(new Color(i, 0, 0));
+ graphics.fillRect(0, i, image.getWidth(null), 1);
+ }
+ graphics.dispose();
+ }
+}