diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/FastRGridExternalLookup.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/FastRGridExternalLookup.java index d45d0684ae3ff62b17afed69746a9404a1d5757b..016b8d9e98c8d7a83c1ede405b736cd431eb03d5 100644 --- a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/FastRGridExternalLookup.java +++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/FastRGridExternalLookup.java @@ -25,6 +25,8 @@ package com.oracle.truffle.r.library.fastrGrid; import com.oracle.truffle.r.library.fastrGrid.DisplayList.LGetDisplayListElement; import com.oracle.truffle.r.library.fastrGrid.DisplayList.LInitDisplayList; import com.oracle.truffle.r.library.fastrGrid.DisplayList.LSetDisplayListOn; +import com.oracle.truffle.r.library.fastrGrid.PaletteExternals.CPalette; +import com.oracle.truffle.r.library.fastrGrid.PaletteExternals.CPalette2; import com.oracle.truffle.r.library.fastrGrid.grDevices.DevCairo; import com.oracle.truffle.r.library.fastrGrid.grDevices.DevCurr; import com.oracle.truffle.r.library.fastrGrid.grDevices.DevHoldFlush; @@ -75,6 +77,10 @@ public final class FastRGridExternalLookup { return SavePlot.create(); case "X11": return new InitWindowedDevice(); + case "palette": + return CPalette.create(); + case "palette2": + return CPalette2.create(); default: return null; } diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/GPar.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/GPar.java index 2e46872077b1b9d058a76ababe52e67bcffe66bf..48e5ca2d58fcd3da897908e0acc74003041ee96d 100644 --- a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/GPar.java +++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/GPar.java @@ -18,6 +18,7 @@ import static com.oracle.truffle.r.library.fastrGrid.GridUtils.getDataAtMod; import java.util.Arrays; import java.util.function.Function; +import com.oracle.truffle.r.library.fastrGrid.GridState.GridPalette; import com.oracle.truffle.r.library.fastrGrid.device.DrawingContext; import com.oracle.truffle.r.library.fastrGrid.device.DrawingContextDefaults; import com.oracle.truffle.r.library.fastrGrid.device.GridColor; @@ -25,6 +26,7 @@ import com.oracle.truffle.r.library.fastrGrid.device.GridDevice; import com.oracle.truffle.r.runtime.RError; import com.oracle.truffle.r.runtime.RError.Message; import com.oracle.truffle.r.runtime.RRuntime; +import com.oracle.truffle.r.runtime.Utils; import com.oracle.truffle.r.runtime.data.RDataFactory; import com.oracle.truffle.r.runtime.data.RList; import com.oracle.truffle.r.runtime.data.RNull; @@ -265,15 +267,21 @@ public final class GPar { private GridColor getGridColor(int listIndex) { Object value = data[listIndex]; + GridColor color = getPaletteColor(value); + if (color != null) { + return color; + } + String strValue = null; if (value instanceof String) { strValue = (String) value; } else if (value instanceof RAbstractStringVector && ((RAbstractStringVector) value).getLength() > 0) { - strValue = ((RAbstractStringVector) value).getDataAt(listIndex % ((RAbstractStringVector) value).getLength()); + strValue = ((RAbstractStringVector) value).getDataAt(index % ((RAbstractStringVector) value).getLength()); } else { return GridColor.TRANSPARENT; } - GridColor color = GridColorUtils.gridColorFromString(strValue); + + color = GridColorUtils.gridColorFromString(strValue); double alpha = asDouble(data[GP_ALPHA], index); if (alpha != 1.) { int newAlpha = Math.min(255, (int) (alpha * ((color.getAlpha() / 255.0) * 255))); @@ -283,6 +291,44 @@ public final class GPar { } } + private GridColor getPaletteColor(Object colorIdIn) { + Object colorId = colorIdIn; + if (colorId instanceof RAbstractVector) { + RAbstractVector vec = (RAbstractVector) colorId; + colorId = vec.getDataAtAsObject(index % vec.getLength()); + } + int paletteIdx = RRuntime.INT_NA; + if (colorId instanceof Integer) { + paletteIdx = (int) colorId; + } else if (colorId instanceof Double && !RRuntime.isNA((Double) colorId)) { + paletteIdx = (int) (double) colorId; + } else if (colorId instanceof String && !RRuntime.isNA((String) colorId)) { + paletteIdx = paletteIdxFromString((String) colorId); + } else if (colorId instanceof Byte && !RRuntime.isNA((byte) colorId)) { + paletteIdx = (int) (byte) colorId; + } + if (RRuntime.isNA(paletteIdx)) { + return null; + } + if (paletteIdx < 0) { + throw RError.error(RError.NO_CALLER, Message.GENERIC, Utils.stringFormat("numerical color values must be >= 0, found %d", paletteIdx)); + } + if (paletteIdx == 0) { + return GridColor.TRANSPARENT; + } + GridPalette palette = GridContext.getContext().getGridState().getPalette(); + GridColor result = palette.colors[(paletteIdx - 1) % palette.colors.length]; + return result; // one based index + } + + private int paletteIdxFromString(String colorId) { + try { + return Integer.parseInt(colorId, 10); + } catch (NumberFormatException ex) { + return RRuntime.INT_NA; + } + } + private static final byte[] DASHED_LINE = new byte[]{4, 4}; private static final byte[] DOTTED_LINE = new byte[]{1, 3}; private static final byte[] DOTDASH_LINE = new byte[]{1, 3, 4, 3}; diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/GridColorUtils.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/GridColorUtils.java index 9ab71e892308256b4dad68cca67dc32ed63df0b2..1ec25bf728686609acfa0232c2515f4f09818363 100644 --- a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/GridColorUtils.java +++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/GridColorUtils.java @@ -14,15 +14,36 @@ package com.oracle.truffle.r.library.fastrGrid; import java.util.HashMap; import java.util.Locale; +import com.oracle.truffle.r.library.fastrGrid.GridState.GridPalette; import com.oracle.truffle.r.library.fastrGrid.device.GridColor; import com.oracle.truffle.r.runtime.RError; import com.oracle.truffle.r.runtime.RError.Message; public final class GridColorUtils { + + private static GridPalette defaultPalette; + private GridColorUtils() { // only static members } + public static GridPalette getDefaultPalette() { + if (defaultPalette == null) { + // Note: default palette copied from GNU R + defaultPalette = new GridPalette(new String[]{ + "black", + "red", + "green3", + "blue", + "cyan", + "magenta", + "yellow", + "grey" + }); + } + return defaultPalette; + } + /** * Converts the representation of color used within R, e.g. as value for * {@code gpar(col='value')}, to our internal representation that grid device should understand. 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 63585c18374f5b8e04e6325d8730b9afa1619a1b..20027f98003ddadbf07e640aa24cea53f7afe6b0 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 @@ -13,6 +13,7 @@ package com.oracle.truffle.r.library.fastrGrid; 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.runtime.data.RList; import com.oracle.truffle.r.runtime.data.RNull; @@ -21,6 +22,7 @@ import com.oracle.truffle.r.runtime.env.REnvironment; public final class GridState { private REnvironment gridEnv; private GridDeviceState devState; + private GridPalette palette; /** * Current grob being drawn (for determining the list of grobs to search when evaluating a @@ -87,6 +89,14 @@ public final class GridState { devState.displayListIndex = newValue; } + public GridPalette getPalette() { + return palette == null ? GridColorUtils.getDefaultPalette() : palette; + } + + public void setPalette(GridPalette palette) { + this.palette = palette; + } + public void init(REnvironment gridEnv) { this.gridEnv = gridEnv; this.currentGrob = RNull.instance; @@ -153,6 +163,28 @@ public final class GridState { return devState.scale; } + public static final class GridPalette { + public final GridColor[] colors; + public final String[] colorNames; + + public GridPalette(String[] colorNames) { + this.colorNames = colorNames; + colors = new GridColor[colorNames.length]; + for (int i = 0; i < colorNames.length; i++) { + colors[i] = GridColorUtils.gridColorFromString(colorNames[i]); + } + } + + public GridPalette(int[] colors) { + this.colors = new GridColor[colors.length]; + colorNames = new String[colors.length]; + for (int i = 0; i < colors.length; i++) { + this.colors[i] = GridColor.fromRawValue(colors[i]); + colorNames[i] = GridColorUtils.gridColorToRString(this.colors[i]); + } + } + } + static final class GridDeviceState { private boolean isDeviceInitialized = false; private RList gpar; diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/PaletteExternals.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/PaletteExternals.java new file mode 100644 index 0000000000000000000000000000000000000000..67646f37e38032e5de2d49ee72515a1d44ed6aa1 --- /dev/null +++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/PaletteExternals.java @@ -0,0 +1,121 @@ +/* + * This material is distributed under the GNU General Public License + * Version 2. You may review the terms of this license at + * http://www.gnu.org/licenses/gpl-2.0.html + * + * Copyright (c) 1997-2014, The R Core Team + * Copyright (c) 2003, The R Foundation + * Copyright (c) 2017, Oracle and/or its affiliates + * + * All rights reserved. + */ +package com.oracle.truffle.r.library.fastrGrid; + +import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.abstractVectorValue; +import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.stringValue; + +import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; +import com.oracle.truffle.api.dsl.Specialization; +import com.oracle.truffle.r.library.fastrGrid.GridState.GridPalette; +import com.oracle.truffle.r.library.fastrGrid.PaletteExternalsFactory.CPalette2NodeGen; +import com.oracle.truffle.r.library.fastrGrid.PaletteExternalsFactory.CPaletteNodeGen; +import com.oracle.truffle.r.nodes.builtin.RExternalBuiltinNode; +import com.oracle.truffle.r.runtime.RError.Message; +import com.oracle.truffle.r.runtime.RRuntime; +import com.oracle.truffle.r.runtime.data.RDataFactory; +import com.oracle.truffle.r.runtime.data.RIntVector; +import com.oracle.truffle.r.runtime.data.RStringVector; +import com.oracle.truffle.r.runtime.data.model.RAbstractIntVector; +import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector; +import com.oracle.truffle.r.runtime.data.model.RAbstractVector; + +public final class PaletteExternals { + private PaletteExternals() { + // only static members + } + + /** + * Implements external {@code C_palette} used in palette R function. + */ + public abstract static class CPalette extends RExternalBuiltinNode.Arg1 { + + static { + Casts casts = new Casts(CPalette.class); + casts.arg(0).mustBe(stringValue()); + } + + public static CPalette create() { + return CPaletteNodeGen.create(); + } + + @Specialization + @TruffleBoundary + public RStringVector updatePalette(RAbstractStringVector palette) { + GridState state = GridContext.getContext().getGridState(); + GridPalette newPalette = null; + if (palette.getLength() == 1) { + if (palette.getDataAt(0).toLowerCase().equals("default")) { + newPalette = GridColorUtils.getDefaultPalette(); + } else { + throw error(Message.GENERIC, "unknown palette (need >= 2 colors)"); + } + } else if (palette.getLength() > 1) { + newPalette = new GridPalette(palette.materialize().getDataCopy()); + } + + String[] original = state.getPalette().colorNames; + if (newPalette != null) { + // the contract is that if the argument's length is zero, we only return the palette + // and + // do not set anything. + state.setPalette(newPalette); + } + // don't know the completeness, assume the worst rather than finding out + RStringVector result = RDataFactory.createStringVector(original, RDataFactory.INCOMPLETE_VECTOR); + result.makeSharedPermanent(); + return result; + } + } + + /** + * Implements external {@code C_palette2} used only internally in grDevices, the parameter are + * colors encoded in integer already. + */ + public abstract static class CPalette2 extends RExternalBuiltinNode.Arg1 { + static { + Casts casts = new Casts(CPalette2.class); + casts.arg(0).mustBe(abstractVectorValue()); + } + + public static CPalette2 create() { + return CPalette2NodeGen.create(); + } + + @Specialization + @TruffleBoundary + public RIntVector updatePalette(RAbstractVector palette) { + GridState state = GridContext.getContext().getGridState(); + int[] newPalette = null; + if (palette.getLength() > 0) { + RAbstractIntVector data = GridUtils.asIntVector(palette); + newPalette = data.materialize().getDataCopy(); + } + RIntVector result = getResult(state); + if (newPalette != null) { + state.setPalette(new GridPalette(newPalette)); + } + return result; + } + + private static RIntVector getResult(GridState state) { + GridPalette palette = state.getPalette(); + int[] result = new int[palette.colors.length]; + boolean complete = true; + for (int i = 0; i < result.length; i++) { + result[i] = palette.colors[i].getRawValue(); + complete &= !RRuntime.isNA(result[i]); + } + return RDataFactory.createIntVector(result, complete); + } + } +} diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/device/GridColor.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/device/GridColor.java index 28abf944f1eae6088260398ccf7c9103c7118733..ec11023ea1248c6471f9a92947c56543c8f91882 100644 --- a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/device/GridColor.java +++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/device/GridColor.java @@ -33,6 +33,10 @@ public final class GridColor { private final int value; + private GridColor(int value) { + this.value = value; + } + public GridColor(int red, int green, int blue, int alpha) { value = ((alpha & 0xFF) << 24) | ((red & 0xFF) << 16) | @@ -40,6 +44,10 @@ public final class GridColor { (blue & 0xFF); } + public static GridColor fromRawValue(int value) { + return new GridColor(value); + } + public int getRed() { return (value >> 16) & 0xFF; } @@ -56,6 +64,10 @@ public final class GridColor { return (value >> 24) & 0xff; } + public int getRawValue() { + return value; + } + @Override public boolean equals(Object obj) { return obj instanceof GridColor && value == ((GridColor) obj).value; diff --git a/mx.fastr/copyrights/overrides b/mx.fastr/copyrights/overrides index 3479fff1b86814164245efe99a1b38315fe81fe1..b2505b0cb646d5b616d1a72e2a6f7d02099ab07c 100644 --- a/mx.fastr/copyrights/overrides +++ b/mx.fastr/copyrights/overrides @@ -770,6 +770,7 @@ com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/GridLine com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/ViewPortTransform.java,gnu_r_murrel_core.copyright com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/LGridDirty.java,gnu_r_murrel_core.copyright com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/GridColorUtils.java,gnu_r.copyright +com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/PaletteExternals.java,gnu_r.copyright com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/LCircle.java,gnu_r_murrel_core.copyright com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/EdgeDetection.java,gnu_r_murrel_core.copyright com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/GridTextNode.java,gnu_r_murrel_core.copyright