From 509f45519734c05f69ba60bf3d6683cf683bcb1b Mon Sep 17 00:00:00 2001 From: stepan <stepan.sindelar@oracle.com> Date: Thu, 29 Jun 2017 16:27:36 +0200 Subject: [PATCH] FastR Grid: devices that save images support %d placeholder for filenames. --- .../r/library/fastrGrid/GridContext.java | 14 +++++-- .../r/library/fastrGrid/GridState.java | 11 +++++ .../truffle/r/library/fastrGrid/LNewPage.java | 16 +++++++- .../fastrGrid/device/FileGridDevice.java | 41 +++++++++++++++++++ .../r/library/fastrGrid/device/SVGDevice.java | 30 ++++++++++---- .../device/awt/BufferedImageDevice.java | 17 +++++++- .../library/fastrGrid/grDevices/DevCairo.java | 2 +- .../fastrGrid/grDevices/FileDevUtils.java | 38 +++++++++++++++++ .../grDevices/InitWindowedDevice.java | 4 +- 9 files changed, 154 insertions(+), 19 deletions(-) create mode 100644 com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/device/FileGridDevice.java create mode 100644 com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/grDevices/FileDevUtils.java diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/GridContext.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/GridContext.java index eafd17babd..928e050945 100644 --- a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/GridContext.java +++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/GridContext.java @@ -28,6 +28,7 @@ import java.util.ArrayList; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.r.library.fastrGrid.GridState.GridDeviceState; +import com.oracle.truffle.r.library.fastrGrid.device.FileGridDevice; import com.oracle.truffle.r.library.fastrGrid.device.GridDevice; import com.oracle.truffle.r.library.fastrGrid.device.GridDevice.DeviceCloseException; import com.oracle.truffle.r.library.fastrGrid.device.SVGDevice; @@ -59,7 +60,7 @@ public final class GridContext { private int currentDeviceIdx = 0; private GridContext() { - devices.add(new DeviceAndState(null)); + devices.add(new DeviceAndState(null, null)); } public static GridContext getContext() { @@ -86,10 +87,15 @@ public final class GridContext { } public void setCurrentDevice(String name, GridDevice currentDevice) { + assert !(currentDevice instanceof FileGridDevice) : "FileGridDevice must have filenamePattern"; + setCurrentDevice(name, currentDevice, null); + } + + public void setCurrentDevice(String name, GridDevice currentDevice, String filenamePattern) { RGridGraphicsAdapter.addDevice(name); RGridGraphicsAdapter.setCurrentDevice(name); currentDeviceIdx = this.devices.size(); - this.devices.add(new DeviceAndState(currentDevice)); + this.devices.add(new DeviceAndState(currentDevice, filenamePattern)); assert devices.size() == RGridGraphicsAdapter.getDevicesCount(); } @@ -158,9 +164,9 @@ public final class GridContext { final GridDevice device; final GridDeviceState state; - DeviceAndState(GridDevice device) { + DeviceAndState(GridDevice device, String filenamePattern) { this.device = device; - this.state = new GridDeviceState(); + this.state = new GridDeviceState(filenamePattern); } } } diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/GridState.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/GridState.java index dac9a5976c..5579a90519 100644 --- a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/GridState.java +++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/GridState.java @@ -15,6 +15,7 @@ import java.util.function.Supplier; import com.oracle.truffle.r.library.fastrGrid.device.GridColor; import com.oracle.truffle.r.library.fastrGrid.device.GridDevice; +import com.oracle.truffle.r.library.fastrGrid.grDevices.FileDevUtils; import com.oracle.truffle.r.runtime.data.RList; import com.oracle.truffle.r.runtime.data.RNull; import com.oracle.truffle.r.runtime.env.REnvironment; @@ -163,6 +164,10 @@ public final class GridState { return devState.scale; } + public String getNextPageFilename() { + return FileDevUtils.formatFilename(devState.filenamePattern, devState.pageIndex++); + } + public static final class GridPalette { public final GridColor[] colors; public final String[] colorNames; @@ -194,5 +199,11 @@ public final class GridState { private boolean isDisplayListOn = true; private RList displayList; private int displayListIndex = 0; + private int pageIndex = 2; + private String filenamePattern; + + GridDeviceState(String filenamePattern) { + this.filenamePattern = filenamePattern; + } } } diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/LNewPage.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/LNewPage.java index 1b234eda3a..27edec3641 100644 --- a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/LNewPage.java +++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/LNewPage.java @@ -14,8 +14,11 @@ package com.oracle.truffle.r.library.fastrGrid; import com.oracle.truffle.api.CompilerDirectives; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.api.frame.VirtualFrame; +import com.oracle.truffle.r.library.fastrGrid.device.FileGridDevice; import com.oracle.truffle.r.library.fastrGrid.device.GridDevice; +import com.oracle.truffle.r.library.fastrGrid.device.GridDevice.DeviceCloseException; import com.oracle.truffle.r.nodes.builtin.RExternalBuiltinNode; +import com.oracle.truffle.r.runtime.RError.Message; import com.oracle.truffle.r.runtime.data.RArgsValuesAndNames; import com.oracle.truffle.r.runtime.data.RNull; @@ -40,8 +43,17 @@ final class LNewPage extends RExternalBuiltinNode { } @TruffleBoundary - private static void openNewPage(GridDevice device) { - device.openNewPage(); + private void openNewPage(GridDevice device) { + if (device instanceof FileGridDevice) { + String path = GridContext.getContext().getGridState().getNextPageFilename(); + try { + ((FileGridDevice) device).openNewPage(path); + } catch (DeviceCloseException e) { + throw error(Message.GENERIC, "Cannot save the image. Details: " + e.getMessage()); + } + } else { + device.openNewPage(); + } } @Override diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/device/FileGridDevice.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/device/FileGridDevice.java new file mode 100644 index 0000000000..753f4d5bfc --- /dev/null +++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/device/FileGridDevice.java @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2017, 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. + */ +package com.oracle.truffle.r.library.fastrGrid.device; + +/** + * Should be implemented by devices that save their output into a file. Such devices should only + * accept complete filenames, i.e. without '%d' placeholder, and the handling of the placeholder is + * left up to the calling code. + */ +public interface FileGridDevice extends GridDevice { + /** + * Each call to {@link #openNewPage()} should save the current image (into the current path) and + * start drawing a new image into the given path, i.e. the given path becomes a new current + * path. + * + * @param filename tha path where to save the next image, will not contain '%d' symbol as its + * processing should be handled by the caller. + * @throws DeviceCloseException + */ + void openNewPage(String filename) throws DeviceCloseException; +} diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/device/SVGDevice.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/device/SVGDevice.java index 4a54283cd6..17f1667bcd 100644 --- a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/device/SVGDevice.java +++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/device/SVGDevice.java @@ -39,10 +39,10 @@ import com.oracle.truffle.r.library.fastrGrid.device.DrawingContext.GridLineJoin import com.oracle.truffle.r.runtime.RInternalError; import com.oracle.truffle.r.runtime.Utils; -public class SVGDevice implements GridDevice { +public class SVGDevice implements GridDevice, FileGridDevice { private static final DecimalFormat DECIMAL_FORMAT = new DecimalFormat("0.000"); private final StringBuilder data = new StringBuilder(1024); - private final String filename; + private String filename; private final double width; private final double height; @@ -73,14 +73,16 @@ public class SVGDevice implements GridDevice { height); } + @Override + public void openNewPage(String newFilename) throws DeviceCloseException { + saveFile(); + filename = newFilename; + openNewPage(); + } + @Override public void close() throws DeviceCloseException { - closeSVGDocument(); - try { - Files.write(Paths.get(filename), Collections.singleton(data.toString()), StandardCharsets.UTF_8); - } catch (IOException e) { - throw new DeviceCloseException(e); - } + saveFile(); } @Override @@ -181,7 +183,19 @@ public class SVGDevice implements GridDevice { data.append("' ").append(attributes).append(" />"); } + private void saveFile() throws DeviceCloseException { + closeSVGDocument(); + try { + Files.write(Paths.get(filename), Collections.singleton(data.toString()), StandardCharsets.UTF_8); + } catch (IOException e) { + throw new DeviceCloseException(e); + } + } + private void closeSVGDocument() { + if (data.length() == 0) { + return; + } if (cachedCtx != null) { // see #appendStyle append("</g>"); diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/device/awt/BufferedImageDevice.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/device/awt/BufferedImageDevice.java index 5777844618..1f95c9cfd1 100644 --- a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/device/awt/BufferedImageDevice.java +++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/device/awt/BufferedImageDevice.java @@ -32,10 +32,12 @@ import java.io.IOException; import javax.imageio.ImageIO; -public final class BufferedImageDevice extends Graphics2DDevice { +import com.oracle.truffle.r.library.fastrGrid.device.FileGridDevice; + +public final class BufferedImageDevice extends Graphics2DDevice implements FileGridDevice { private final BufferedImage image; - private final String filename; private final String fileType; + private String filename; private BufferedImageDevice(String filename, String fileType, BufferedImage image, Graphics2D graphics, int width, int height) { super(graphics, width, height, true); @@ -56,8 +58,19 @@ public final class BufferedImageDevice extends Graphics2DDevice { return new BufferedImageDevice(filename, fileType, image, graphics, width, height); } + @Override + public void openNewPage(String newFilename) throws DeviceCloseException { + saveImage(); + filename = newFilename; + openNewPage(); + } + @Override public void close() throws DeviceCloseException { + saveImage(); + } + + private void saveImage() throws DeviceCloseException { try { ImageIO.write(image, fileType, new File(filename)); } catch (IOException e) { diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/grDevices/DevCairo.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/grDevices/DevCairo.java index 45bfaf3700..5001af1fd8 100644 --- a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/grDevices/DevCairo.java +++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/grDevices/DevCairo.java @@ -48,7 +48,7 @@ public class DevCairo extends RExternalBuiltinNode { throw error(Message.INVALID_ARG_TYPE); } - GridContext.getContext().setCurrentDevice("svg", new SVGDevice(filename, witdh / 72., height / 72.)); + GridContext.getContext().setCurrentDevice("svg", new SVGDevice(FileDevUtils.formatInitialFilename(filename), witdh / 72., height / 72.), filename); return RNull.instance; } } diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/grDevices/FileDevUtils.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/grDevices/FileDevUtils.java new file mode 100644 index 0000000000..1b3d1de503 --- /dev/null +++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/grDevices/FileDevUtils.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2017, 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. + */ +package com.oracle.truffle.r.library.fastrGrid.grDevices; + +import com.oracle.truffle.api.CompilerAsserts; + +public class FileDevUtils { + public static String formatInitialFilename(String filename) { + return formatFilename(filename, 1); + } + + public static String formatFilename(String filename, int pageIndex) { + CompilerAsserts.neverPartOfCompilation(); + assert filename != null; + assert pageIndex >= 1; + return String.format(filename, pageIndex); + } +} diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/grDevices/InitWindowedDevice.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/grDevices/InitWindowedDevice.java index 49d65f6980..de15f10dcc 100644 --- a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/grDevices/InitWindowedDevice.java +++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/grDevices/InitWindowedDevice.java @@ -100,8 +100,8 @@ public final class InitWindowedDevice extends RExternalBuiltinNode { String formatName = name.substring(0, name.indexOf("::")); String filename = name.substring(name.lastIndexOf(':') + 1); try { - BufferedImageDevice device = BufferedImageDevice.open(filename, formatName, width, height); - GridContext.getContext().setCurrentDevice(formatName.toUpperCase(), device); + BufferedImageDevice device = BufferedImageDevice.open(FileDevUtils.formatInitialFilename(filename), formatName, width, height); + GridContext.getContext().setCurrentDevice(formatName.toUpperCase(), device, filename); } catch (NotSupportedImageFormatException e) { throw error(Message.GENERIC, String.format("Format '%s' is not supported.", formatName)); } -- GitLab