blob: b89bcb1c835e3edacba739c624f204fb1268f50f [file] [log] [blame]
/*
* Copyright 2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package quality.util;
import com.sun.jna.Pointer;
import org.apache.commons.lang3.SystemUtils;
import quality.util.osx.Foundation;
import quality.util.osx.FoundationLibrary;
import quality.util.osx.ID;
import quality.util.osx.MacUtil;
import javax.imageio.ImageIO;
import javax.swing.*;
import java.awt.*;
import java.awt.color.ColorSpace;
import java.awt.image.BufferedImage;
import java.awt.image.ColorConvertOp;
import java.awt.image.Raster;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.function.Consumer;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
public class RenderUtil {
private static BufferedImage captureScreen(Window belowWindow, Rectangle rect) {
ID pool = Foundation.invoke("NSAutoreleasePool", "new");
try {
ID windowId = belowWindow != null ? MacUtil.findWindowFromJavaWindow(belowWindow) : null;
Foundation.NSRect nsRect = new Foundation.NSRect(rect.x, rect.y, rect.width, rect.height);
ID cgWindowId = windowId != null ? Foundation.invoke(windowId, "windowNumber") : ID.NIL;
int windowListOptions = cgWindowId != null
? FoundationLibrary.kCGWindowListOptionIncludingWindow
: FoundationLibrary.kCGWindowListOptionAll;
int windowImageOptions = FoundationLibrary.kCGWindowImageBestResolution |
FoundationLibrary.kCGWindowImageShouldBeOpaque;
ID cgImageRef = Foundation.cgWindowListCreateImage(
nsRect, windowListOptions, cgWindowId, windowImageOptions);
ID bitmapRep = Foundation.invoke(
Foundation.invoke("NSBitmapImageRep", "alloc"),
"initWithCGImage:", cgImageRef);
ID nsImage = Foundation.invoke(
Foundation.invoke("NSImage", "alloc"),
"init");
Foundation.invoke(nsImage, "addRepresentation:", bitmapRep);
ID data = Foundation.invoke(nsImage, "TIFFRepresentation");
ID bytes = Foundation.invoke(data, "bytes");
ID length = Foundation.invoke(data, "length");
Pointer ptr = new Pointer(bytes.longValue());
ByteBuffer byteBuffer = ptr.getByteBuffer(0, length.longValue());
Foundation.invoke(nsImage, "release");
byte[] b = new byte[byteBuffer.remaining()];
byteBuffer.get(b);
BufferedImage result = ImageIO.read(new ByteArrayInputStream(b));
if (result != null) {
ColorSpace ics = ColorSpace.getInstance(ColorSpace.CS_sRGB);
ColorConvertOp cco = new ColorConvertOp(ics, null);
return cco.filter(result, null);
}
assertNotNull("no registered ImageReader claims to be able to read the stream", result);
} catch (IOException e) {
e.printStackTrace();
assertNull("an error occurs during reading" + e.getMessage(), e);
} catch (IllegalArgumentException e) {
e.printStackTrace();
assertNull("input is null" + e.getMessage(), e);
} finally {
Foundation.invoke(pool, "release");
}
return null;
}
public static BufferedImage capture(int width, int height, Consumer<Graphics2D> painter)
throws Exception {
JFrame[] f = new JFrame[1];
Point[] p = new Point[1];
SwingUtilities.invokeAndWait(() -> {
f[0] = new JFrame();
JComponent c = new MyComponent(painter);
f[0].add(c);
c.setSize(width + 10, height + 10);
f[0].setSize(width + 100, height + 100); // giving some space
// for frame border effects,
// e.g. rounded frame
c.setLocation(50, 50);
f[0].setVisible(true);
p[0] = c.getLocationOnScreen();
});
Rectangle screenRect;
Robot r = new Robot();
while (!Color.black.equals(r.getPixelColor(p[0].x, p[0].y))) {
Thread.sleep(100);
}
screenRect = new Rectangle(p[0].x + 5, p[0].y + 5, width, height);
BufferedImage result = SystemUtils.IS_OS_MAC ?
captureScreen(f[0], screenRect) : r.createScreenCapture(screenRect);
SwingUtilities.invokeAndWait(f[0]::dispose);
return result;
}
private static class MyComponent extends JComponent {
private final Consumer<Graphics2D> painter;
private MyComponent(Consumer<Graphics2D> painter) {
this.painter = painter;
}
@Override
protected void paintComponent(Graphics g) {
g.translate(5, 5);
Shape savedClip = g.getClip();
g.clipRect(0, 0, getWidth() - 20, getHeight() - 20);
painter.accept((Graphics2D) g);
g.setClip(savedClip);
g.setColor(Color.black);
((Graphics2D) g).setStroke(new BasicStroke(10));
g.drawRect(-5, -5, getWidth() - 5, getHeight() - 5);
}
}
@SuppressWarnings("SameParameterValue")
public static void checkImage(BufferedImage image, String path, String gfName) throws Exception {
String[] testDataVariant = {
"osx_hardware_rendering", "osx_software_rendering",
"osx_sierra_rendering", "osx_lowres_rendering",
"linux_rendering", "windows_rendering"};
String testDataStr = System.getProperty("testdata");
assertNotNull("testdata property is not set", testDataStr);
File testData = new File(testDataStr, "quality" + File.separator + path);
assertTrue("Test data dir does not exist", testData.exists());
if (System.getProperty("gentestdata") == null) {
boolean failed = true;
StringBuilder failureReason = new StringBuilder();
for (String variant : testDataVariant) {
File goldenFile = new File(testData, variant + File.separator +
gfName);
if (!goldenFile.exists()) continue;
BufferedImage goldenImage = ImageIO.read(goldenFile);
assertNotNull("no registered ImageReader claims to be able to read the stream: "
+ goldenFile, goldenImage);
failed = true;
if (image.getWidth() != goldenImage.getWidth() ||
image.getHeight() != image.getHeight()) {
failureReason.append(variant).append(" : Golden image and result have different sizes\n");
continue;
}
Raster gRaster = goldenImage.getData();
Raster rRaster = image.getData();
int[] gArr = new int[3];
int[] rArr = new int[3];
failed = false;
scan:
for (int i = 0; i < gRaster.getWidth(); i++) {
for (int j = 0; j < gRaster.getHeight(); j++) {
gRaster.getPixel(i, j, gArr);
rRaster.getPixel(i, j, rArr);
assertTrue(gArr.length == rArr.length);
for (int k = 0; k < gArr.length; k++) {
if (gArr[k] != rArr[k]) {
failureReason.append(variant).append(" : Different pixels found ").
append("at (").append(i).append(",").append(j).append(")");
failed = true;
break scan;
}
}
}
}
if (!failed) break;
ImageIO.write(image, "png", new File(testData, gfName + "_" + variant));
}
if (failed) throw new RuntimeException(failureReason.toString());
} else {
ImageIO.write(image, "png", new File(testData, gfName));
}
}
}