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 efd87be667163e281801070eb5c2804b37d73378..9b4b963429d62fac8895a37defd264e109ff1072 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 @@ -30,12 +30,16 @@ import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.r.library.fastrGrid.GridState.GridDeviceState; 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; +import com.oracle.truffle.r.library.fastrGrid.device.awt.BufferedImageDevice; +import com.oracle.truffle.r.library.fastrGrid.device.awt.BufferedImageDevice.NotSupportedImageFormatException; import com.oracle.truffle.r.library.fastrGrid.graphics.RGridGraphicsAdapter; import com.oracle.truffle.r.runtime.FastRConfig; import com.oracle.truffle.r.runtime.RCaller; import com.oracle.truffle.r.runtime.RError; import com.oracle.truffle.r.runtime.RError.Message; import com.oracle.truffle.r.runtime.RInternalCode; +import com.oracle.truffle.r.runtime.RInternalError; import com.oracle.truffle.r.runtime.context.RContext; import com.oracle.truffle.r.runtime.data.RFunction; import com.oracle.truffle.r.runtime.env.REnvironment; @@ -97,6 +101,14 @@ public final class GridContext { } else { setCurrentDevice(defaultDev, WindowDevice.createWindowDevice()); } + } else if (defaultDev.equals("svg")) { + setCurrentDevice(defaultDev, new SVGDevice("Rplot.svg", GridDevice.DEFAULT_WIDTH, GridDevice.DEFAULT_HEIGHT)); + } else if (defaultDev.equals("png")) { + setCurrentDevice(defaultDev, safeOpenImageDev("Rplot.png", "png")); + } else if (defaultDev.equals("bmp")) { + setCurrentDevice(defaultDev, safeOpenImageDev("Rplot.bmp", "bmp")); + } else if (defaultDev.equals("jpg") || defaultDev.equals("jpeg")) { + setCurrentDevice("jpeg", safeOpenImageDev("Rplot.jpg", "jpeg")); } else { throw RError.error(RError.NO_CALLER, Message.GENERIC, "FastR does not support device '" + defaultDev + "'."); } @@ -132,6 +144,14 @@ public final class GridContext { return RContext.getEngine().evalFunction(redrawAll, REnvironment.baseEnv().getFrame(), RCaller.topLevel, true, null, args); } + private BufferedImageDevice safeOpenImageDev(String filename, String formatName) { + try { + return BufferedImageDevice.open(filename, formatName, GridDevice.DEFAULT_WIDTH, GridDevice.DEFAULT_HEIGHT); + } catch (NotSupportedImageFormatException e) { + throw RInternalError.shouldNotReachHere("Device format " + formatName + " should be supported."); + } + } + private static final class DeviceAndState { final GridDevice device; final GridDeviceState state; diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/device/awt/Graphics2DDevice.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/device/awt/Graphics2DDevice.java index 90bf1adc20281dd8002220b8599c20c35214d8ea..e29477b675150abd208d970cd15bbcc2323942cb 100644 --- a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/device/awt/Graphics2DDevice.java +++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/device/awt/Graphics2DDevice.java @@ -182,6 +182,7 @@ public class Graphics2DDevice implements GridDevice { void setGraphics2D(Graphics2D newGraphics) { assert newGraphics != null; graphics = newGraphics; + cachedContext = null; } public Graphics2D getGraphics2D() { diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/graphics/RGridGraphicsAdapter.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/graphics/RGridGraphicsAdapter.java index f99012e978ce97b5acaeadaa66f323021627937c..bff8b3810fced4776421d6adc9092b6434ea3419 100644 --- a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/graphics/RGridGraphicsAdapter.java +++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/graphics/RGridGraphicsAdapter.java @@ -22,6 +22,7 @@ import com.oracle.truffle.r.runtime.ROptions.OptionsException; import com.oracle.truffle.r.runtime.RRuntime; import com.oracle.truffle.r.runtime.context.RContext; import com.oracle.truffle.r.runtime.data.RDataFactory; +import com.oracle.truffle.r.runtime.data.RNull; import com.oracle.truffle.r.runtime.data.RPairList; import com.oracle.truffle.r.runtime.env.REnvironment; @@ -64,9 +65,14 @@ public final class RGridGraphicsAdapter { public static void initialize() { addDevice(NULL_DEVICE); setCurrentDevice(NULL_DEVICE); - ROptions.ContextStateImpl options = RContext.getInstance().stateROptions; + RContext ctx = RContext.getInstance(); + ROptions.ContextStateImpl options = ctx.stateROptions; + if (options.getValue(DEFAULT_DEVICE_OPTION) != RNull.instance) { + return; + } + String defaultDevice = ctx.isInteractive() ? "awt" : "svg"; try { - options.setValue(DEFAULT_DEVICE_OPTION, "awt"); + options.setValue(DEFAULT_DEVICE_OPTION, defaultDevice); } catch (OptionsException e) { RError.warning(RError.NO_CALLER, Message.GENERIC, "FastR could not set the 'device' options to awt."); } diff --git a/documentation/Index.md b/documentation/Index.md index c98e248efef019be1db7917eab74484fd939a41a..393dcd24acb72e76d2e3f3c05b408a00b2986d3d 100644 --- a/documentation/Index.md +++ b/documentation/Index.md @@ -7,3 +7,5 @@ [For Developers](dev/Index.md) [Debugging](debugging.md) + +[Graphics support](graphics.md) diff --git a/documentation/graphics.md b/documentation/graphics.md new file mode 100644 index 0000000000000000000000000000000000000000..e5b13a857ae5abe437e365b04e3f3ef4a2ce4d94 --- /dev/null +++ b/documentation/graphics.md @@ -0,0 +1,88 @@ +# Introduction + +There are two main built-in R packages that provide basic graphical output +support: *graphics* and *lattice*. They both use some parts of even lower level +*grDevices* package, which allows users to change the "device" to which the +graphical output will be drawn. The probably most popular graphical packages +*lattice* and *ggplot2* are build on top of the *grid* package. + +FastR has its own implementation of the *grid* package and it also emulates +the most important R level functions from the *grDevices* package. This +implementation is purely Java based and has been tested to work with +the unmodified *lattice* package, support for unmodified *ggplot2* package +is work in progress. + +Any functions from the *graphics* package are not going to work with FastR, +namely e.g. the `plot` function. R documentation, which can be displayed by +typing `?functionName` can be used to determine if a function is from *graphics* +or *grid* package or potentially from some *grid* based higher level package. + +The longer term goal is to emulate R level *graphics* package functions by +means of *grid* package drawing primitives. + +# Examples + +The *grid* package is distributed with FastR, but not loaded by default, +therefore one has to load it using the `library` function. Following example +draws a red rectangle in the center of the screen. + +``` +library(grid) +grid.rect(width=0.5, height=0.5, gp=gpar(col='red')) +``` + +The *lattice* package must be installed using: + +``` +install.packages('lattice'); +``` + +With the *lattice* package installed, one can run the +following example showing a barchart. + +``` +library(lattice) +mtcars$cars <- rownames(mtcars) +barchart(cars~mpg, data=mtcars) +``` + +# Devices Support + +FastR supports the following devices: + +* awt: opens a new AWT window for drawing, the window can be resized and +its contents can be saved with the standard `savePlot` function. +This is the default device for interactive sessions. +In FastR, for compatibility with GNU R, *X11* device is alias for the *awt* device. + +* svg: generates SVG image, which is saved into a specified file. +The contents may be read into a character vector by FastR specific function +`grDevices:::svg.off()`. FastR produces the SVG code directly from *grid* drawing commands, +therefore the SVG code may look more SVG idiomatic and resembling the R code that created it, +but may produce visually slightly different image compared to other devices. +This device is default for non-interactive FastR sessions. + +* jpg, png, bmp: these devices create an image file of specified format. Unlike the SVG device, +these are also based on the AWT framework. + +Devices that generate file take filename as an argument. The file will be saved on disk only +once the standard `dev.off()` function is called. FastR honors the "device" option, which +may override the default device. See the documentation of *grDevices* package for more details. + +# Java Interoperability + +The `grDevices::awt` function can accept `java.awt.Graphics2D` object and width and height in AWT units, +in which case all the drawing will be done using that object. Example: + +``` +grDevices:::awt(420, 420, graphicsObj); +``` + +One possible use case is to create a Java based UI with a `JPanel` that will be +used for visualizing some data from R. Override the `JPanel`'s `paint` +method and pass the graphics object to R code using `PolyglotEngine`. +The R code can do any *grid* based visualization and it will be directly +displayed in the UI. + + +