--- old/src/java.desktop/windows/native/libawt/java2d/windows/GDIBlitLoops.cpp 2020-06-11 11:33:04.483157700 -0700 +++ new/src/java.desktop/windows/native/libawt/java2d/windows/GDIBlitLoops.cpp 2020-06-11 11:33:03.991762700 -0700 @@ -42,6 +42,68 @@ } BmiType; /* + * Some GDI functions functions will fail if they operate on memory which spans + * virtual allocations as used by modern garbage collectors (ie ZGC). + * So if the call to SetDIBitsToDevice fails, we will re-try it on malloced + * memory rather than the pinned Java heap memory. + * Once Microsoft fix the GDI bug, the small performance penalty of this retry + * will be gone. + */ +static void retryingSetDIBitsToDevice( + HDC hdc, + int xDest, + int yDest, + DWORD w, + DWORD h, + int xSrc, + int ySrc, + UINT StartScan, + UINT cLines, + const VOID *lpvBits, + BITMAPINFO *lpbmi, + UINT ColorUse) { + +#ifdef DEBUG_PERF + LARGE_INTEGER ts1, ts2; + QueryPerformanceCounter(&ts1); +#endif + + int ret = + SetDIBitsToDevice(hdc, xDest, yDest, w, h, + xSrc, ySrc, StartScan, cLines, lpvBits, + lpbmi, ColorUse); + + if (ret != 0 || h == 0) { +#ifdef DEBUG_PERF + QueryPerformanceCounter(&ts2); + printf("success time: %zd\n", (ts2.QuadPart-ts1.QuadPart)); +#endif + return; + } + + size_t size = lpbmi->bmiHeader.biSizeImage; + void* imageData = NULL; + try { + imageData = safe_Malloc(size); + } catch (std::bad_alloc&) { + } + if (imageData == NULL) { + return; + } + memcpy(imageData, lpvBits, size); // this is the most expensive part. + SetDIBitsToDevice(hdc, xDest, yDest, w, h, + xSrc, ySrc, StartScan, cLines, imageData, + lpbmi, ColorUse); + free(imageData); + +#ifdef DEBUG_PERF + QueryPerformanceCounter(&ts2); + printf("with retry time: %zd\n", (ts2.QuadPart-ts1.QuadPart)); +#endif + +}; + +/* * Class: sun_java2d_windows_GDIBlitLoops * Method: nativeBlit * Signature: (Lsun/java2d/SurfaceData;Lsun/java2d/SurfaceData;IIIIIIZ)V @@ -127,7 +189,6 @@ // then we can do the work much faster. This is due to a constraint // in the way DIBs are structured and parsed by GDI jboolean fastBlt = ((srcInfo.scanStride & 0x03) == 0); - bmi.bmiHeader.biSize = sizeof(bmi.bmiHeader); bmi.bmiHeader.biWidth = srcInfo.scanStride/srcInfo.pixelStride; // fastBlt copies whole image in one call; else copy line-by-line @@ -190,7 +251,7 @@ // Could also call StretchDIBits. Testing showed slight // performance advantage of SetDIBits instead, so since we // have no need of scaling, might as well use SetDIBits. - SetDIBitsToDevice(hDC, dstx, dsty, width, height, + retryingSetDIBitsToDevice(hDC, dstx, dsty, width, height, 0, 0, 0, height, rasBase, (BITMAPINFO*)&bmi, DIB_RGB_COLORS); } @@ -198,7 +259,7 @@ // Source scanlines not DWORD-aligned - copy each scanline individually for (int i = 0; i < height; i += 1) { if (::IsWindowVisible(dstOps->window)) { - SetDIBitsToDevice(hDC, dstx, dsty+i, width, 1, + retryingSetDIBitsToDevice(hDC, dstx, dsty+i, width, 1, 0, 0, 0, 1, rasBase, (BITMAPINFO*)&bmi, DIB_RGB_COLORS); rasBase = (void*)((char*)rasBase + srcInfo.scanStride); --- old/src/java.desktop/windows/native/libawt/windows/awt_Cursor.cpp 2020-06-11 11:33:07.674167300 -0700 +++ new/src/java.desktop/windows/native/libawt/windows/awt_Cursor.cpp 2020-06-11 11:33:07.189906600 -0700 @@ -373,23 +373,21 @@ int *cols = SAFE_SIZE_NEW_ARRAY2(int, nW, nH); - jint *intRasterDataPtr = NULL; + /* Copy the raster data because GDI may fail on some Java heap + * allocated memory. + */ + length = env->GetArrayLength(intRasterData); + jint *intRasterDataPtr = new jint[length]; HBITMAP hColor = NULL; try { - intRasterDataPtr = - (jint *)env->GetPrimitiveArrayCritical(intRasterData, 0); + env->GetIntArrayRegion(intRasterData, 0, length, intRasterDataPtr); hColor = create_BMP(NULL, (int *)intRasterDataPtr, nSS, nW, nH); memcpy(cols, intRasterDataPtr, nW*nH*sizeof(int)); } catch (...) { - if (intRasterDataPtr != NULL) { - env->ReleasePrimitiveArrayCritical(intRasterData, - intRasterDataPtr, 0); - } + delete[] intRasterDataPtr; throw; } - - env->ReleasePrimitiveArrayCritical(intRasterData, intRasterDataPtr, 0); - intRasterDataPtr = NULL; + delete[] intRasterDataPtr; HCURSOR hCursor = NULL; --- old/src/java.desktop/windows/native/libawt/windows/awt_TrayIcon.cpp 2020-06-11 11:33:10.792586100 -0700 +++ new/src/java.desktop/windows/native/libawt/windows/awt_TrayIcon.cpp 2020-06-11 11:33:10.272975100 -0700 @@ -1001,25 +1001,21 @@ delete[] andMaskPtr; - jint *intRasterDataPtr = NULL; + /* Copy the raster data because GDI may fail on some Java heap + * allocated memory. + */ + length = env->GetArrayLength(intRasterData); + jint *intRasterDataPtr = new jint[length]; HBITMAP hColor = NULL; try { - intRasterDataPtr = (jint *)env->GetPrimitiveArrayCritical(intRasterData, 0); - if (intRasterDataPtr == NULL) { - ::DeleteObject(hMask); - return; - } + env->GetIntArrayRegion(intRasterData, 0, length, intRasterDataPtr); hColor = AwtTrayIcon::CreateBMP(NULL, (int *)intRasterDataPtr, nSS, nW, nH); } catch (...) { - if (intRasterDataPtr != NULL) { - env->ReleasePrimitiveArrayCritical(intRasterData, intRasterDataPtr, 0); - } + delete[] intRasterDataPtr; ::DeleteObject(hMask); throw; } - - env->ReleasePrimitiveArrayCritical(intRasterData, intRasterDataPtr, 0); - intRasterDataPtr = NULL; + delete[] intRasterDataPtr; HICON hIcon = NULL; --- old/src/java.desktop/windows/native/libawt/windows/awt_PrintJob.cpp 2020-06-11 11:33:13.923697200 -0700 +++ new/src/java.desktop/windows/native/libawt/windows/awt_PrintJob.cpp 2020-06-11 11:33:13.403043700 -0700 @@ -1739,11 +1739,18 @@ // ::PatBlt(hDC, destX+1, destY+1, destWidth-2, destHeight-2, PATCOPY); // ::SelectObject(hDC, oldBrush); + /* This code is rarely used now. It used to be invoked by Java plugin browser + * printing. Today embedded frames are used only when a toolkit such as SWT + * needs to embed + */ TRY; jbyte *image = NULL; try { - image = (jbyte *)env->GetPrimitiveArrayCritical(imageArray, 0); + int length = env->GetArrayLength(imageArray); + image = new jbyte[length]; CHECK_NULL(image); + env->GetByteArrayRegion(imageArray, 0, length, image); + struct { BITMAPINFOHEADER bmiHeader; DWORD* bmiColors; @@ -1777,13 +1784,11 @@ fclose(file); #endif //DEBUG_PRINTING } catch (...) { - if (image != NULL) { - env->ReleasePrimitiveArrayCritical(imageArray, image, 0); - } + delete[] image; throw; } - env->ReleasePrimitiveArrayCritical(imageArray, image, 0); + delete[] image; CATCH_BAD_ALLOC; } @@ -2803,100 +2808,6 @@ return NULL; } -#if 0 - -/* - * Class: sun_awt_windows_WPrinterJob - * Method: drawImageIntRGB - * Signature: (J[IFFFFFFFFII)V - */ -JNIEXPORT void JNICALL Java_sun_awt_windows_WPrinterJob_drawImageIntRGB - (JNIEnv *env, jobject self, - jlong printDC, jintArray image, - jfloat destX, jfloat destY, - jfloat destWidth, jfloat destHeight, - jfloat srcX, jfloat srcY, - jfloat srcWidth, jfloat srcHeight, - jint srcBitMapWidth, jint srcBitMapHeight) { - - int result = 0; - - assert(printDC != NULL); - assert(image != NULL); - assert(srcX >= 0); - assert(srcY >= 0); - assert(srcWidth > 0); - assert(srcHeight > 0); - assert(srcBitMapWidth > 0); - assert(srcBitMapHeight > 0); - - - static int alphaMask = 0xff000000; - static int redMask = 0x00ff0000; - static int greenMask = 0x0000ff00; - static int blueMask = 0x000000ff; - - struct { - BITMAPV4HEADER header; - DWORD masks[256]; - } dib; - - - - memset(&dib,0,sizeof(dib)); - dib.header.bV4Size = sizeof(dib.header); - dib.header.bV4Width = srcBitMapWidth; - dib.header.bV4Height = -srcBitMapHeight; // Top down DIB - dib.header.bV4Planes = 1; - dib.header.bV4BitCount = 32; - dib.header.bV4V4Compression = BI_BITFIELDS; - dib.header.bV4SizeImage = 0; // It's the default size. - dib.header.bV4XPelsPerMeter = 0; - dib.header.bV4YPelsPerMeter = 0; - dib.header.bV4ClrUsed = 0; - dib.header.bV4ClrImportant = 0; - dib.header.bV4RedMask = redMask; - dib.header.bV4GreenMask = greenMask; - dib.header.bV4BlueMask = blueMask; - dib.header.bV4AlphaMask = alphaMask; - dib.masks[0] = redMask; - dib.masks[1] = greenMask; - dib.masks[2] = blueMask; - dib.masks[3] = alphaMask; - - jint *imageBits = NULL; - - try { - imageBits = (jint *)env->GetPrimitiveArrayCritical(image, 0); - - if (printDC){ - result = ::StretchDIBits( (HDC)printDC, - ROUND_TO_LONG(destX), - ROUND_TO_LONG(destY), - ROUND_TO_LONG(destWidth), - ROUND_TO_LONG(destHeight), - ROUND_TO_LONG(srcX), - ROUND_TO_LONG(srcY), - ROUND_TO_LONG(srcWidth), - ROUND_TO_LONG(srcHeight), - imageBits, - (BITMAPINFO *)&dib, - DIB_RGB_COLORS, - SRCCOPY); - - } - } catch (...) { - if (imageBits != NULL) { - env->ReleasePrimitiveArrayCritical(image, imageBits, 0); - } - throw; - } - - env->ReleasePrimitiveArrayCritical(image, imageBits, 0); - -} -#else - /* * Class: sun_awt_windows_WPrinterJob * Method: drawDIBImage @@ -2991,7 +2902,6 @@ env->ReleasePrimitiveArrayCritical(image, imageBits, 0); } -#endif /* * An utility function to print passed image byte array to @@ -3059,7 +2969,7 @@ CATCH_BAD_ALLOC; } -static FILE* outfile = NULL; + static int bitsToDevice(HDC printDC, jbyte *image, long destX, long destY, long width, long height) { int result = 0; @@ -3072,6 +2982,9 @@ /* height could be negative to indicate that this is a top-down DIB */ // assert(height > 0); + if (!printDC || height == 0) { + return result; + } struct { BITMAPINFOHEADER bmiHeader; DWORD* bmiColors; @@ -3099,11 +3012,9 @@ if (bitMapHeader.bmiHeader.biHeight < 0) { jbyte *dibImage = reverseDIB(image, width, height, 24); if (dibImage != NULL) { - bitMapHeader.bmiHeader.biWidth = ROUND_TO_LONG(width); - bitMapHeader.bmiHeader.biHeight = ROUND_TO_LONG(height); - - if (printDC){ - result = ::SetDIBitsToDevice(printDC, + bitMapHeader.bmiHeader.biWidth = ROUND_TO_LONG(width); + bitMapHeader.bmiHeader.biHeight = ROUND_TO_LONG(height); + result = ::SetDIBitsToDevice(printDC, ROUND_TO_LONG(destX), // left of dest rect ROUND_TO_LONG(destY), // top of dest rect ROUND_TO_LONG(width), // width of dest rect @@ -3115,12 +3026,9 @@ dibImage, // points to the DIB (BITMAPINFO *)&bitMapHeader, DIB_RGB_COLORS); - } - - free (dibImage); + free (dibImage); } } else { - if (printDC){ result = ::SetDIBitsToDevice(printDC, destX, // left of dest rect destY, // top of dest rect @@ -3133,9 +3041,30 @@ image, // points to the DIB (BITMAPINFO *)&bitMapHeader, DIB_RGB_COLORS); - } + if (result == 0) { + size_t size = width * height * 3; // Always 24bpp, also DWORD aligned. + void *imageData = NULL; + try { + imageData = safe_Malloc(size); + } catch (std::bad_alloc&) { + return result; + } + memcpy(imageData, image, size); + result = ::SetDIBitsToDevice(printDC, + destX, // left of dest rect + destY, // top of dest rect + width, // width of dest rect + height, // height of dest rect + 0, // left of source rect + 0, // top of source rect + 0, // line number of 1st source scan line + height, // number of source scan lines + imageData, // points to the DIB + (BITMAPINFO *)&bitMapHeader, + DIB_RGB_COLORS); + free(imageData); + } } - return result; } --- /dev/null 2020-06-11 11:33:17.000000000 -0700 +++ new/test/jdk/java/awt/Graphics2D/LargeWindowPaintTest.java 2020-06-11 11:33:16.628743900 -0700 @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2020, 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. + */ + +/* + * @test + * @bug 8240654 + * @summary Test painting a large window works + * @key headful + * @requires (os.family == "windows") + * @requires vm.gc.Z + * @run main/othervm -Dsun.java2d.uiScale=1 LargeWindowPaintTest + * @run main/othervm -Dsun.java2d.uiScale=1 -Dsun.java2d.d3d=false LargeWindowPaintTest + * @run main/othervm -XX:+UseZGC -Dsun.java2d.uiScale=1 LargeWindowPaintTest + * @run main/othervm -XX:+UseZGC -Dsun.java2d.uiScale=1 -Dsun.java2d.d3d=false LargeWindowPaintTest + */ + +import java.awt.Color; +import java.awt.Frame; +import java.awt.Graphics; +import java.awt.Rectangle; +import java.awt.Robot; + +import javax.swing.JFrame; +import javax.swing.JPanel; +import javax.swing.SwingUtilities; +import javax.swing.WindowConstants; + +public class LargeWindowPaintTest extends JPanel { + + static volatile JFrame frame = null; + static volatile LargeWindowPaintTest comp = null; + static Color color = Color.red; + + public static void main(String[] args) throws Exception { + + SwingUtilities.invokeAndWait(() -> { + frame = new JFrame("Large Window Paint Test"); + frame.add(comp = new LargeWindowPaintTest()); + frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); + frame.setExtendedState(Frame.MAXIMIZED_BOTH); + frame.setVisible(true); + }); + + Thread.sleep(2000); + Robot robot = new Robot(); + robot.setAutoDelay(500); + robot.waitForIdle(); + Rectangle r = comp.getBounds(); + System.out.println("Component bounds = " + r); + Color c = robot.getPixelColor((int)r.getWidth()-100, (int)r.getHeight()-100); + + SwingUtilities.invokeAndWait(() -> frame.dispose()); + + if (!c.equals(color)) { + throw new RuntimeException("Color was " + c + " expected " + color); + } + } + + @Override + protected void paintComponent(Graphics g) { + super.paintComponent(g); + g.setColor(color); + g.fillRect(0, 0, getSize().width, getSize().height); + }; +} --- /dev/null 2020-06-11 11:33:19.000000000 -0700 +++ new/test/jdk/java/awt/print/PrinterJob/AlphaPrintTest.java 2020-06-11 11:33:19.084174600 -0700 @@ -0,0 +1,200 @@ +/* + * Copyright (c) 2020, 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. + */ + +/* + * @test + * @bug 8240654 + * @summary Test printing alpha colors - banded printing works with ZGC. + * @key headful printer + * @requires (os.family == "windows") + * @requires vm.gc.Z + * @run main/manual/othervm -XX:+UseZGC -Dsun.java2d.d3d=false AlphaPrintTest + */ + +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Frame; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.GridLayout; + +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +import java.awt.print.PageFormat; +import java.awt.print.Printable; +import java.awt.print.PrinterException; +import java.awt.print.PrinterJob; + +import javax.swing.JButton; +import javax.swing.JFrame; +import javax.swing.JPanel; +import javax.swing.JTextArea; +import javax.swing.SwingUtilities; +import javax.swing.WindowConstants; + +public class AlphaPrintTest extends JPanel implements Printable { + + static final int W=400, H=600; + + static volatile JFrame frame = null; + static volatile AlphaPrintTest comp = null; + static Color color = Color.red; + static volatile boolean passed = false; + static volatile boolean printInvoked = false; + static volatile boolean done = false; + + public static void main(String[] args) throws Exception { + + SwingUtilities.invokeAndWait(() -> { + frame = new JFrame("Alpha Color Print Test"); + frame.setLayout(new GridLayout(1, 2)); + frame.add(comp = new AlphaPrintTest()); + frame.add(new InstructionPanel()); + frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); + frame.pack(); + frame.setVisible(true); + }); + + while (!done || !printInvoked) { + Thread.sleep(1000); + } + + SwingUtilities.invokeAndWait(() -> frame.dispose()); + + if (!passed) { + throw new RuntimeException("Test failed."); + } + } + + @Override + public Dimension getPreferredSize() { + return new Dimension(W, H); + } + + + @Override + public Dimension getMinimumSize() { + return getPreferredSize(); + } + + @Override + protected void paintComponent(Graphics g) { + super.paintComponent(g); + paintContent(g); + }; + + private void paintContent(Graphics g) { + Color c = new Color(255, 0, 0, 240); // not a solid color. + g.setColor(c); + g.drawLine(0, 0, W, H); + g.drawLine(W, 0, 0, H); + + for (int i=10; i < 150; i+=10) { + g.drawRect(i, i, W-(i*2), H-(i*2)); + } + g.drawString("Alpha Paint Test", W/2-30, H/2); + } + + public int print(Graphics g, PageFormat pf, int pageIndex) { + if (pageIndex == 0) { + Graphics2D g2d = (Graphics2D)g; + g2d.translate(pf.getImageableX(), pf.getImageableY()); + paintContent(g); + return Printable.PAGE_EXISTS; + } + return Printable.NO_SUCH_PAGE; + } + + public void doPrint() { + printInvoked = true; + PrinterJob pj = PrinterJob.getPrinterJob(); + pj.setPrintable(this); + if (pj.printDialog()) { + try { + pj.print(); + } catch (PrinterException e) { + e.printStackTrace(); + done = true; + } + } + } + + public void doClose(boolean pass) { + if (printInvoked) { + passed = pass; + done = true; + } + } +} + +class InstructionPanel extends JPanel implements ActionListener { + + static final String INSTRUCTIONS = + "You must have a printer to peform this test.\n" + + "Press the print button which will bring up a print dialog." + + "Select a suitable printer, and confirm to print. " + + "Examine the printed output. It should closely resemble the rendering in" + + " the panel to the left. If yes, press PASS, else press FAIL"; + + InstructionPanel() { + GridLayout gl1 = new GridLayout(2, 1); + setLayout(gl1); + JTextArea ta = new JTextArea(INSTRUCTIONS); + ta.setEditable(false); + ta.setLineWrap(true); + ta.setWrapStyleWord(true); + add(ta); + JPanel p = new JPanel(); + JButton print = new JButton("Print"); + JButton pass = new JButton("PASS"); + JButton fail = new JButton("FAIL"); + print.addActionListener(this); + pass.addActionListener(this); + fail.addActionListener(this); + p.add(print); + p.add(pass); + p.add(fail); + add(p); + } + @Override + public Dimension getPreferredSize() { + return new Dimension(200, 600); + } + + + @Override + public Dimension getMinimumSize() { + return getPreferredSize(); + } + public void actionPerformed(ActionEvent e) { + String cmd = e.getActionCommand(); + switch (cmd) { + case "Print" -> AlphaPrintTest.comp.doPrint(); + case "PASS" -> AlphaPrintTest.comp.doClose(true); + case "FAIL" -> AlphaPrintTest.comp.doClose(false); + } + + } + +}