diff --git a/README.md b/README.md index 55aaa792a..c01b621bd 100644 --- a/README.md +++ b/README.md @@ -19,8 +19,7 @@ The [processing-web](https://github.com/processing/processing-web/) repository contains reference, examples, and the site. (Please use that link to file issues regarding the web site, the examples, or the reference.) -The instructions for building the source [are here](https://github.com/processing/processing/wiki/Build-Instructions), -although they [need an update](https://github.com/processing/processing/issues/1629). +The instructions for building the source [are here](https://github.com/processing/processing/wiki/Build-Instructions). Someday we'll also write code style guidelines, fix all these bugs, throw together hundreds of unit tests, and solve the Israeli-Palestinian conflict. diff --git a/app/.classpath b/app/.classpath index eb8980d92..25495070c 100644 --- a/app/.classpath +++ b/app/.classpath @@ -7,13 +7,8 @@ - + - - - - - diff --git a/app/.settings/org.eclipse.jdt.core.prefs b/app/.settings/org.eclipse.jdt.core.prefs index da7176c57..60fcdf705 100644 --- a/app/.settings/org.eclipse.jdt.core.prefs +++ b/app/.settings/org.eclipse.jdt.core.prefs @@ -1,13 +1,14 @@ eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.annotation.inheritNullAnnotations=disabled org.eclipse.jdt.core.compiler.annotation.missingNonNullByDefaultAnnotation=ignore org.eclipse.jdt.core.compiler.annotation.nonnull=org.eclipse.jdt.annotation.NonNull org.eclipse.jdt.core.compiler.annotation.nonnullbydefault=org.eclipse.jdt.annotation.NonNullByDefault org.eclipse.jdt.core.compiler.annotation.nullable=org.eclipse.jdt.annotation.Nullable org.eclipse.jdt.core.compiler.annotation.nullanalysis=disabled org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled -org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6 +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.7 org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve -org.eclipse.jdt.core.compiler.compliance=1.6 +org.eclipse.jdt.core.compiler.compliance=1.7 org.eclipse.jdt.core.compiler.debug.lineNumber=generate org.eclipse.jdt.core.compiler.debug.localVariable=generate org.eclipse.jdt.core.compiler.debug.sourceFile=generate @@ -47,6 +48,7 @@ org.eclipse.jdt.core.compiler.problem.missingSynchronizedOnInheritedMethod=ignor org.eclipse.jdt.core.compiler.problem.noEffectAssignment=warning org.eclipse.jdt.core.compiler.problem.noImplicitStringConversion=warning org.eclipse.jdt.core.compiler.problem.nonExternalizedStringLiteral=ignore +org.eclipse.jdt.core.compiler.problem.nonnullParameterAnnotationDropped=warning org.eclipse.jdt.core.compiler.problem.nullAnnotationInferenceConflict=error org.eclipse.jdt.core.compiler.problem.nullReference=ignore org.eclipse.jdt.core.compiler.problem.nullSpecViolation=error @@ -67,6 +69,7 @@ org.eclipse.jdt.core.compiler.problem.specialParameterHidingField=disabled org.eclipse.jdt.core.compiler.problem.staticAccessReceiver=warning org.eclipse.jdt.core.compiler.problem.suppressOptionalErrors=disabled org.eclipse.jdt.core.compiler.problem.suppressWarnings=enabled +org.eclipse.jdt.core.compiler.problem.syntacticNullAnalysisForFields=disabled org.eclipse.jdt.core.compiler.problem.syntheticAccessEmulation=ignore org.eclipse.jdt.core.compiler.problem.typeParameterHiding=warning org.eclipse.jdt.core.compiler.problem.unavoidableGenericTypeProblems=enabled @@ -90,9 +93,10 @@ org.eclipse.jdt.core.compiler.problem.unusedParameterIncludeDocCommentReference= org.eclipse.jdt.core.compiler.problem.unusedParameterWhenImplementingAbstract=disabled org.eclipse.jdt.core.compiler.problem.unusedParameterWhenOverridingConcrete=disabled org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=warning +org.eclipse.jdt.core.compiler.problem.unusedTypeParameter=ignore org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=warning -org.eclipse.jdt.core.compiler.source=1.6 +org.eclipse.jdt.core.compiler.source=1.7 org.eclipse.jdt.core.formatter.align_type_members_on_columns=false org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression=18 org.eclipse.jdt.core.formatter.alignment_for_arguments_in_annotation=0 diff --git a/app/.settings/org.eclipse.ltk.core.refactoring.prefs b/app/.settings/org.eclipse.ltk.core.refactoring.prefs new file mode 100644 index 000000000..b196c64a3 --- /dev/null +++ b/app/.settings/org.eclipse.ltk.core.refactoring.prefs @@ -0,0 +1,2 @@ +eclipse.preferences.version=1 +org.eclipse.ltk.core.refactoring.enable.project.refactoring.history=false diff --git a/app/build.xml b/app/build.xml old mode 100644 new mode 100755 index c8d8ef937..1dc84edc3 --- a/app/build.xml +++ b/app/build.xml @@ -56,35 +56,7 @@ - - - - - + + debug="on" + nowarn="true" + compiler="org.eclipse.jdt.core.JDTCompilerAdapter"> + diff --git a/app/src/processing/app/Base.java b/app/src/processing/app/Base.java index 098e6a5db..6b4f86bce 100644 --- a/app/src/processing/app/Base.java +++ b/app/src/processing/app/Base.java @@ -35,7 +35,7 @@ import javax.swing.tree.*; import processing.app.contrib.*; import processing.core.*; - +import processing.mode.java.JavaMode; /** * The base class for the main processing application. @@ -45,10 +45,10 @@ import processing.core.*; */ public class Base { // Added accessors for 0218 because the UpdateCheck class was not properly - // updating the values, because javac was inlining the static final values. - static private final int REVISION = 221; + // updating the values, due to javac inlining the static final values. + static private final int REVISION = 228; /** This might be replaced by main() if there's a lib/version.txt file. */ - static private String VERSION_NAME = "0221"; //$NON-NLS-1$ + static private String VERSION_NAME = "0228"; //$NON-NLS-1$ /** Set true if this a proper release rather than a numbered revision. */ // static private boolean RELEASE = false; @@ -127,7 +127,11 @@ public class Base { private JMenu sketchbookMenu; private Recent recent; -// private JMenu recentMenu; + + // Used by handleOpen(), this saves the chooser to remember the directory. + // Doesn't appear to be necessary with the AWT native file dialog. + // https://github.com/processing/processing/pull/2366 + private JFileChooser openChooser; static protected File sketchbookFolder; // protected File toolsFolder; @@ -205,13 +209,11 @@ public class Base { // Prevent more than one copy of the PDE from running. SingleInstance.startServer(base); - } catch (Exception e) { + } catch (Throwable t) { // Catch-all to hopefully pick up some of the weirdness we've been // running into lately. - StringWriter sw = new StringWriter(); - e.printStackTrace(new PrintWriter(sw)); - Base.showError("We're off on the wrong foot", - "An error occurred during startup.\n" + sw, e); + showBadnessTrace("We're off on the wrong foot", + "An error occurred during startup.", t, true); } log("done creating base..."); //$NON-NLS-1$ } @@ -366,13 +368,13 @@ public class Base { } else { for (Mode m : getModeList()) { if (m.getIdentifier().equals(lastModeIdentifier)) { - logf("Setting next mode to {0}.", lastModeIdentifier); //$NON-NLS-1$ + logf("Setting next mode to %s.", lastModeIdentifier); //$NON-NLS-1$ nextMode = m; } } if (nextMode == null) { nextMode = coreModes[0]; - logf("Could not find mode {0}, using default.", lastModeIdentifier); //$NON-NLS-1$ + logf("Could not find mode %s, using default.", lastModeIdentifier); //$NON-NLS-1$ } } @@ -609,38 +611,31 @@ public class Base { Base.showWarning("Save", "Please save the sketch before changing the mode.", null); - } else { -// boolean untitled = activeEditor.untitled; - String mainPath = sketch.getMainFilePath(); - boolean wasUntitled = sketch.isUntitled(); + return; + } + nextMode = mode; - // save a mode file into this sketch folder - File sketchProps = new File(sketch.getFolder(), "sketch.properties"); //$NON-NLS-1$ - try { - Settings props = new Settings(sketchProps); - // Include the pretty name for error messages to show the user - props.set("mode", mode.getTitle()); //$NON-NLS-1$ - // Actual identifier to be used to resurrect the mode - props.set("mode.id", mode.getIdentifier()); //$NON-NLS-1$ - props.save(); - } catch (IOException e) { - e.printStackTrace(); + // If the current editor contains file extensions that the new mode can handle, then + // write a sketch.properties file with the new mode specified, and reopen. + boolean newModeCanHandleCurrentSource = true; + for (final SketchCode code: sketch.getCode()) { + if (!mode.validExtension(code.getExtension())) { + newModeCanHandleCurrentSource = false; + break; } -// PrintWriter writer = PApplet.createWriter(sketchProps); -// writer.println("mode=" + mode.getTitle()); -// writer.flush(); -// writer.close(); - -// // close this sketch -//// int[] where = activeEditor.getPlacement(); -// Rectangle bounds = activeEditor.getBounds(); -// int divider = activeEditor.getDividerLocation(); - EditorState state = activeEditor.state; + } + if (newModeCanHandleCurrentSource) { + final File props = new File(sketch.getCodeFolder(), "sketch.properties"); + saveModeSettings(props, nextMode); handleClose(activeEditor, true); - - // re-open the sketch -// /*Editor editor =*/ handleOpen(mainPath, untitled, state); - /*Editor editor =*/ handleOpen(mainPath, wasUntitled, state); + handleOpen(sketch.getMainFilePath()); + } else { + // If you're changing modes, and there's nothing in the current sketch, you probably + // don't intend to keep the old, wrong-mode editor around. + if (sketch.isUntitled()) { + handleClose(activeEditor, true); + } + handleNew(); } } } @@ -753,6 +748,10 @@ public class Base { if (!newbieFile.createNewFile()) { throw new IOException(newbieFile + " already exists."); } + + // Create sketch properties. + saveModeSettings(new File(newbieDir, "sketch.properties"), nextMode); + String path = newbieFile.getAbsolutePath(); /*Editor editor =*/ handleOpen(path, true); @@ -763,6 +762,18 @@ public class Base { } } + // Create or modify a sketch.proprties file to specify the given Mode. + private void saveModeSettings(final File sketchProps, final Mode mode) { + try { + final Settings settings = new Settings(sketchProps); + settings.set("mode", mode.getTitle()); + settings.set("mode.id", mode.getIdentifier()); + settings.save(); + } catch (IOException e) { + System.err.println("While creating " + sketchProps + ": " + e.getMessage()); + } + } + // /** // * Replace the sketch in the current window with a new untitled document. @@ -825,13 +836,17 @@ public class Base { extensions.add(mode.getDefaultExtension()); } + final String prompt = Language.text("open"); - if (Preferences.getBoolean("chooser.files.native")) { // don't use native dialogs on Linux //$NON-NLS-1$ - // get the front-most window frame for placing file dialog - FileDialog fd = new FileDialog(activeEditor, prompt, FileDialog.LOAD); + + // don't use native dialogs on Linux (or anyone else w/ override) + if (Preferences.getBoolean("chooser.files.native")) { //$NON-NLS-1$ + // use the front-most window frame for placing file dialog + FileDialog openDialog = + new FileDialog(activeEditor, prompt, FileDialog.LOAD); // Only show .pde files as eligible bachelors - fd.setFilenameFilter(new FilenameFilter() { + openDialog.setFilenameFilter(new FilenameFilter() { public boolean accept(File dir, String name) { // confirmed to be working properly [fry 110128] for (String ext : extensions) { @@ -843,20 +858,22 @@ public class Base { } }); - fd.setVisible(true); + openDialog.setVisible(true); - String directory = fd.getDirectory(); - String filename = fd.getFile(); + String directory = openDialog.getDirectory(); + String filename = openDialog.getFile(); if (filename != null) { File inputFile = new File(directory, filename); handleOpen(inputFile.getAbsolutePath()); } } else { - JFileChooser fc = new JFileChooser(); - fc.setDialogTitle(prompt); + if (openChooser == null) { + openChooser = new JFileChooser(); + } + openChooser.setDialogTitle(prompt); - fc.setFileFilter(new javax.swing.filechooser.FileFilter() { + openChooser.setFileFilter(new javax.swing.filechooser.FileFilter() { public boolean accept(File file) { // JFileChooser requires you to explicitly say yes to directories // as well (unlike the AWT chooser). Useful, but... different. @@ -876,8 +893,8 @@ public class Base { return "Processing Sketch"; } }); - if (fc.showOpenDialog(activeEditor) == JFileChooser.APPROVE_OPTION) { - handleOpen(fc.getSelectedFile().getAbsolutePath()); + if (openChooser.showOpenDialog(activeEditor) == JFileChooser.APPROVE_OPTION) { + handleOpen(openChooser.getSelectedFile().getAbsolutePath()); } } } @@ -905,115 +922,171 @@ public class Base { // protected Editor handleOpen(String path, int[] location) { // protected Editor handleOpen(String path, Rectangle bounds, int divider) { protected Editor handleOpen(String path, boolean untitled, EditorState state) { -// System.err.println("entering handleOpen " + path); - - File file = new File(path); - if (!file.exists()) return null; - - if (!Sketch.isSanitaryName(file.getName())) { - Base.showWarning("You're tricky, but not tricky enough", - file.getName() + " is not a valid name for a sketch.\n" + - "Better to stick to ASCII, no spaces, and make sure\n" + - "it doesn't start with a number.", null); - return null; - } - -// System.err.println(" editors: " + editors); - // Cycle through open windows to make sure that it's not already open. - for (Editor editor : editors) { - if (editor.getSketch().getMainFilePath().equals(path)) { - editor.toFront(); - // move back to the top of the recent list - handleRecent(editor); - return editor; - } - } - - // If the active editor window is an untitled, and un-modified document, - // just replace it with the file that's being opened. -// if (activeEditor != null) { -// Sketch activeSketch = activeEditor.sketch; -// if (activeSketch.isUntitled() && !activeSketch.isModified()) { -// // if it's an untitled, unmodified document, it can be replaced. -// // except in cases where a second blank window is being opened. -// if (!path.startsWith(untitledFolder.getAbsolutePath())) { -// activeEditor.handleOpenUnchecked(path, 0, 0, 0, 0); -// return activeEditor; -// } -// } -// } - -// Mode nextMode = nextEditorMode(); try { - File sketchFolder = new File(path).getParentFile(); - File sketchProps = new File(sketchFolder, "sketch.properties"); //$NON-NLS-1$ - if (sketchProps.exists()) { - Settings props = new Settings(sketchProps); - String modeTitle = props.get("mode"); //$NON-NLS-1$ - String modeIdentifier = props.get("mode.id"); //$NON-NLS-1$ - if (modeTitle != null && modeIdentifier != null) { -// nextMode = findMode(modeTitle); - Mode mode = findMode(modeIdentifier); - if (mode != null) { - nextMode = mode; + // System.err.println("entering handleOpen " + path); - } else { - final String msg = - "This sketch was last used in “" + modeTitle + "” mode,\n" + - "which does not appear to be installed. The sketch will\n" + - "be opened in “" + nextMode.getTitle() + "” mode instead."; - Base.showWarning("Depeche Mode", msg, null); - } + final File file = new File(path); + if (!file.exists()) { + return null; + } + + // System.err.println(" editors: " + editors); + // Cycle through open windows to make sure that it's not already open. + for (Editor editor : editors) { + if (editor.getSketch().getMainFile().equals(file)) { + editor.toFront(); + // move back to the top of the recent list + handleRecent(editor); + return editor; } } - } catch (Exception e) { - e.printStackTrace(); - } -// Editor.State state = new Editor.State(editors); - Editor editor = nextMode.createEditor(this, path, state); - if (editor == null) { - // if it's the last editor window -// if (editors.size() == 0 && defaultFileMenu == null) { - // if it's not mode[0] already, then don't go into an infinite loop - // trying to recreate a window with the default mode. - if (nextMode == coreModes[0]) { - Base.showError("Editor Problems", - "An error occurred while trying to change modes.\n" + - "We'll have to quit for now because it's an\n" + - "unfortunate bit of indigestion.", - null); - } else { - editor = coreModes[0].createEditor(this, path, state); - } - } - // Make sure that the sketch actually loaded - if (editor.getSketch() == null) { + if (!Sketch.isSanitaryName(file.getName())) { + Base.showWarning("You're tricky, but not tricky enough", + file.getName() + " is not a valid name for a sketch.\n" + + "Better to stick to ASCII, no spaces, and make sure\n" + + "it doesn't start with a number.", null); + return null; + } + + if (!nextMode.canEdit(file)) { + final Mode mode = selectMode(file); + if (mode == null) { + return null; + } + nextMode = mode; + } + +// Editor.State state = new Editor.State(editors); + Editor editor = nextMode.createEditor(this, path, state); + if (editor == null) { + // if it's the last editor window +// if (editors.size() == 0 && defaultFileMenu == null) { + // if it's not mode[0] already, then don't go into an infinite loop + // trying to recreate a window with the default mode. + if (nextMode == coreModes[0]) { + Base.showError("Editor Problems", + "An error occurred while trying to change modes.\n" + + "We'll have to quit for now because it's an\n" + + "unfortunate bit of indigestion.", + null); + } else { + editor = coreModes[0].createEditor(this, path, state); + } + } + + // Make sure that the sketch actually loaded + if (editor.getSketch() == null) { // System.err.println("sketch was null, getting out of handleOpen"); - return null; // Just walk away quietly - } + return null; // Just walk away quietly + } // editor.untitled = untitled; - editor.getSketch().setUntitled(untitled); - editors.add(editor); - handleRecent(editor); + editor.getSketch().setUntitled(untitled); + editors.add(editor); + handleRecent(editor); - // now that we're ready, show the window - // (don't do earlier, cuz we might move it based on a window being closed) - editor.setVisible(true); + // now that we're ready, show the window + // (don't do earlier, cuz we might move it based on a window being closed) + editor.setVisible(true); - return editor; + return editor; + + } catch (Throwable t) { + showBadnessTrace("Terrible News", + "A serious error occurred while " + + "trying to create a new editor window.", t, false); + nextMode = coreModes[0]; + return null; + } } -// protected Mode findMode(String title) { -// for (Mode mode : getModeList()) { -// if (mode.getTitle().equals(title)) { -// return mode; -// } -// } -// return null; -// } + private static class ModeInfo { + public final String title; + public final String id; + + public ModeInfo(String id, String title) { + this.id = id; + this.title = title; + } + } + + + private static ModeInfo modeInfoFor(final File sketch) { + final File sketchFolder = sketch.getParentFile(); + final File sketchProps = new File(sketchFolder, "sketch.properties"); + if (!sketchProps.exists()) { + return null; + } + try { + final Settings settings = new Settings(sketchProps); + final String title = settings.get("mode"); + final String id = settings.get("mode.id"); + if (title == null || id == null) { + return null; + } + return new ModeInfo(id, title); + } catch (IOException e) { + System.err.println("While trying to read " + sketchProps + ": " + + e.getMessage()); + } + return null; + } + + + private Mode promptForMode(final File sketch, final ModeInfo preferredMode) { + final String extension = + sketch.getName().substring(sketch.getName().lastIndexOf('.') + 1); + final List possibleModes = new ArrayList(); + for (final Mode mode : getModeList()) { + if (mode.canEdit(sketch)) { + possibleModes.add(mode); + } + } + if (possibleModes.size() == 1 && + possibleModes.get(0).getIdentifier() + .equals(JavaMode.class.getCanonicalName())) { + // If default mode can open it, then do so without prompting. + return possibleModes.get(0); + } + if (possibleModes.size() == 0) { + if (preferredMode == null) { + Base.showWarning("Modeless Dialog", + "I don't know how to open a sketch with the \"" + + extension + + "\"\nfile extension. You'll have to install a different" + + "\nProcessing mode for that."); + } else { + Base.showWarning("Modeless Dialog", "You'll have to install " + + preferredMode.title + " Mode " + "\nin order to open that sketch."); + } + return null; + } + final Mode[] modes = possibleModes.toArray(new Mode[possibleModes.size()]); + final String message = preferredMode == null ? + (nextMode.getTitle() + " Mode can't open ." + extension + " files, " + + "but you have one or more modes\ninstalled that can. " + + "Would you like to try one?") : + ("That's a " + preferredMode.title + " Mode sketch, " + + "but you don't have " + preferredMode.title + " installed.\n" + + "Would you like to try a different mode for opening a " + + "." + extension + " sketch?"); + return (Mode) JOptionPane.showInputDialog(null, message, "Choose Wisely", + JOptionPane.QUESTION_MESSAGE, + null, modes, modes[0]); + } + + + private Mode selectMode(final File sketch) { + final ModeInfo modeInfo = modeInfoFor(sketch); + final Mode specifiedMode = modeInfo == null ? null : findMode(modeInfo.id); + if (specifiedMode != null) { + return specifiedMode; + } + return promptForMode(sketch, modeInfo); + } + protected Mode findMode(String id) { for (Mode mode : getModeList()) { @@ -1028,6 +1101,8 @@ public class Base { /** * Close a sketch as specified by its editor window. * @param editor Editor object of the sketch to be closed. + * @param modeSwitch Whether this close is being done in the context of a + * mode switch. * @return true if succeeded in closing, false if canceled. */ public boolean handleClose(Editor editor, boolean modeSwitch) { @@ -1040,6 +1115,7 @@ public class Base { // Close the running window, avoid window boogers with multiple sketches editor.internalCloseRunner(); +// System.out.println("editors size is " + editors.size()); if (editors.size() == 1) { // For 0158, when closing the last window /and/ it was already an // untitled sketch, just give up and let the user quit. @@ -1079,6 +1155,7 @@ public class Base { // This will store the sketch count as zero editors.remove(editor); +// System.out.println("editors size now " + editors.size()); // storeSketches(); // Save out the current prefs state @@ -1592,18 +1669,29 @@ public class Base { } + // Because the Oracle JDK is 64-bit only, we lose this ability, feature, + // edge case, headache. +// /** +// * Return whether sketches will run as 32- or 64-bits. On Linux and Windows, +// * this is the bit depth of the machine, while on OS X it's determined by the +// * setting from preferences, since both 32- and 64-bit are supported. +// */ +// static public int getNativeBits() { +// if (Base.isMacOS()) { +// return Preferences.getInteger("run.options.bits"); //$NON-NLS-1$ +// } +// return nativeBits; +// } + /** - * Return whether sketches will run as 32- or 64-bits. On Linux and Windows, - * this is the bit depth of the machine, while on OS X it's determined by the - * setting from preferences, since both 32- and 64-bit are supported. + * Return whether sketches will run as 32- or 64-bits based + * on the JVM that's in use. */ static public int getNativeBits() { - if (Base.isMacOS()) { - return Preferences.getInteger("run.options.bits"); //$NON-NLS-1$ - } return nativeBits; } + /* static public String getPlatformName() { String osname = System.getProperty("os.name"); @@ -2079,10 +2167,17 @@ public class Base { } + /** + * Non-fatal error message. + */ + static public void showWarning(String title, String message) { + showWarning(title, message, null); + } + /** * Non-fatal error message with optional stack trace side dish. */ - static public void showWarning(String title, String message, Exception e) { + static public void showWarning(String title, String message, Throwable e) { if (title == null) title = "Warning"; if (commandLine) { @@ -2101,7 +2196,7 @@ public class Base { */ static public void showWarningTiered(String title, String primary, String secondary, - Exception e) { + Throwable e) { if (title == null) title = "Warning"; final String message = primary + "\n" + secondary; @@ -2175,6 +2270,35 @@ public class Base { } + /** + * Testing a new warning window that includes the stack trace. + */ + static private void showBadnessTrace(String title, String message, + Throwable t, boolean fatal) { + if (title == null) title = fatal ? "Error" : "Warning"; + + if (commandLine) { + System.err.println(title + ": " + message); + if (t != null) { + t.printStackTrace(); + } + + } else { + StringWriter sw = new StringWriter(); + t.printStackTrace(new PrintWriter(sw)); + // Necessary to replace \n with
(even if pre) otherwise Java + // treats it as a closed tag and reverts to plain formatting. + message = "" + message + "

" + + sw.toString().replaceAll("\n", "
"); + + JOptionPane.showMessageDialog(new Frame(), message, title, + fatal ? + JOptionPane.ERROR_MESSAGE : + JOptionPane.WARNING_MESSAGE); + } + } + + // ................................................................... @@ -2349,22 +2473,37 @@ public class Base { String path = Base.class.getProtectionDomain().getCodeSource().getLocation().getPath(); // Path may have URL encoding, so remove it String decodedPath = PApplet.urlDecode(path); - // The .jar file will be in the lib folder - File jarFolder = new File(decodedPath).getParentFile(); - if (jarFolder.getName().equals("lib")) { - // The main Processing installation directory. - // This works for Windows, Linux, and Apple's Java 6 on OS X. - processingRoot = jarFolder.getParentFile(); - } else if (Base.isMacOS()) { - // This works for Java 7 on OS X. - processingRoot = jarFolder; - } - if (processingRoot == null || !processingRoot.exists()) { - // Try working directory instead (user.dir, different from user.home) - Base.log("Could not find lib folder via " + - jarFolder.getAbsolutePath() + - ", switching to user.dir"); - processingRoot = new File(System.getProperty("user.dir")); + + if (decodedPath.contains("/app/bin")) { + if (Base.isMacOS()) { + processingRoot = + new File(path, "../../build/macosx/work/Processing.app/Contents/Java"); + } else if (Base.isWindows()) { + processingRoot = new File(path, "../../build/windows/work"); + } else if (Base.isLinux()) { + processingRoot = new File(path, "../../build/linux/work"); + } + } else { + // The .jar file will be in the lib folder + File jarFolder = new File(decodedPath).getParentFile(); + if (jarFolder.getName().equals("lib")) { + // The main Processing installation directory. + // This works for Windows, Linux, and Apple's Java 6 on OS X. + processingRoot = jarFolder.getParentFile(); + } else if (Base.isMacOS()) { + // This works for Java 7 on OS X. The 'lib' folder is not part of the + // classpath on OS X, and adding it creates more problems than it's + // worth. + processingRoot = jarFolder; + + } + if (processingRoot == null || !processingRoot.exists()) { + // Try working directory instead (user.dir, different from user.home) + System.err.println("Could not find lib folder via " + + jarFolder.getAbsolutePath() + + ", switching to user.dir"); + processingRoot = new File(System.getProperty("user.dir")); + } } } /* @@ -2385,6 +2524,58 @@ public class Base { } + static public File getJavaHome() { + if (isMacOS()) { + //return "Contents/PlugIns/jdk1.7.0_40.jdk/Contents/Home/jre/bin/java"; + File[] plugins = getContentFile("../PlugIns").listFiles(new FilenameFilter() { + public boolean accept(File dir, String name) { + return dir.isDirectory() && + name.endsWith(".jdk") && !name.startsWith("."); + } + }); + return new File(plugins[0], "Contents/Home/jre"); + } + // On all other platforms, it's the 'java' folder adjacent to Processing + return getContentFile("java"); + } + + + /** Get the path to the embedded Java executable. */ + static public String getJavaPath() { + String javaPath = "bin/java" + (isWindows() ? ".exe" : ""); + File javaFile = new File(getJavaHome(), javaPath); + try { + return javaFile.getCanonicalPath(); + } catch (IOException e) { + return javaFile.getAbsolutePath(); + } + /* + if (isMacOS()) { + //return "Contents/PlugIns/jdk1.7.0_40.jdk/Contents/Home/jre/bin/java"; + File[] plugins = getContentFile("../PlugIns").listFiles(new FilenameFilter() { + public boolean accept(File dir, String name) { + return name.endsWith(".jdk") && dir.isDirectory(); + } + }); + //PApplet.printArray(plugins); + File javaBinary = new File(plugins[0], "Contents/Home/jre/bin/java"); + //return getContentFile(plugins[0].getAbsolutePath() + "/Contents/Home/jre/bin/java").getAbsolutePath(); + //return getContentFile("../PlugIns/jdk1.7.0_40.jdk/Contents/Home/jre/bin/java").getAbsolutePath(); + return javaBinary.getAbsolutePath(); + + } else if (isLinux()) { + return getContentFile("java/bin/java").getAbsolutePath(); + + } else if (isWindows()) { + return getContentFile("java/bin/java.exe").getAbsolutePath(); + } + System.err.println("No appropriate platform found. " + + "Hoping that Java is in the path."); + return Base.isWindows() ? "java.exe" : "java"; + */ + } + + // /** // * Get an image associated with the current color theme. // * @deprecated @@ -2522,6 +2713,7 @@ public class Base { to = null; targetFile.setLastModified(sourceFile.lastModified()); + targetFile.setExecutable(sourceFile.canExecute()); } @@ -2594,6 +2786,28 @@ public class Base { } + static public void copyDirNative(File sourceDir, + File targetDir) throws IOException { + Process process = null; + if (Base.isMacOS() || Base.isLinux()) { + process = Runtime.getRuntime().exec(new String[] { + "cp", "-a", sourceDir.getAbsolutePath(), targetDir.getAbsolutePath() + }); + } else { + // TODO implement version that uses XCOPY here on Windows + throw new RuntimeException("Not yet implemented on Windows"); + } + try { + int result = process.waitFor(); + if (result != 0) { + throw new IOException("Error while copying (result " + result + ")"); + } + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + + /** * Delete a file or directory in a platform-specific manner. Removes a File * object (a file or directory) from the system by placing it in the Trash @@ -2986,7 +3200,7 @@ public class Base { } - static public void log(String message, Exception e) { + static public void log(String message, Throwable e) { if (DEBUG) { System.out.println(message); e.printStackTrace(); diff --git a/app/src/processing/app/ColorChooser.java b/app/src/processing/app/ColorChooser.java new file mode 100644 index 000000000..b4a6ccac8 --- /dev/null +++ b/app/src/processing/app/ColorChooser.java @@ -0,0 +1,701 @@ +/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + Part of the Processing project - http://processing.org + + Copyright (c) 2006-14 Ben Fry and Casey Reas + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License version 2 + as published by the Free Software Foundation. + + This program 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 for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +package processing.app; + +import processing.core.*; + +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Container; +import java.awt.Cursor; +import java.awt.Dimension; +import java.awt.Frame; +import java.awt.Graphics; +import java.awt.event.*; + +import javax.swing.*; +import javax.swing.border.*; +import javax.swing.event.*; +import javax.swing.text.*; + + +/** + * Generic color selector frame, pulled from the Tool object. API not really + * worked out here (what should the constructor be? how flexible?) So use with + * caution and be ready for it to break in future releases. + */ +public class ColorChooser { //extends JFrame implements DocumentListener { + + int hue, saturation, brightness; // range 360, 100, 100 + int red, green, blue; // range 256, 256, 256 + + ColorRange range; + ColorSlider slider; + + JTextField hueField, saturationField, brightnessField; + JTextField redField, greenField, blueField; + + JTextField hexField; + + JPanel colorPanel; + DocumentListener colorListener; + + JDialog window; + + +// public String getMenuTitle() { +// return "Color Selector"; +// } + + + public ColorChooser(Frame owner, boolean modal, Color initialColor, + String buttonName, ActionListener buttonListener) { + //super("Color Selector"); + window = new JDialog(owner, "Color Selector", modal); + window.getContentPane().setLayout(new BorderLayout()); + + Box box = Box.createHorizontalBox(); + box.setBorder(new EmptyBorder(12, 12, 12, 12)); + + range = new ColorRange(); + range.init(); + Box rangeBox = new Box(BoxLayout.Y_AXIS); + rangeBox.setAlignmentY(0); + rangeBox.setBorder(BorderFactory.createBevelBorder(BevelBorder.LOWERED)); + rangeBox.add(range); + box.add(rangeBox); + box.add(Box.createHorizontalStrut(10)); + + slider = new ColorSlider(); + slider.init(); + Box sliderBox = new Box(BoxLayout.Y_AXIS); + sliderBox.setAlignmentY(0); + sliderBox.setBorder(BorderFactory.createBevelBorder(BevelBorder.LOWERED)); + sliderBox.add(slider); + box.add(sliderBox); + box.add(Box.createHorizontalStrut(10)); + + box.add(createColorFields(buttonName, buttonListener)); +// System.out.println("1: " + hexField.getInsets()); + + box.add(Box.createHorizontalStrut(10)); + +// System.out.println("2: " + hexField.getInsets()); + + window.getContentPane().add(box, BorderLayout.CENTER); +// System.out.println(hexField); +// System.out.println("3: " + hexField.getInsets()); +// colorPanel.setInsets(hexField.getInsets()); + + window.pack(); + window.setResizable(false); + +// Dimension size = getSize(); +// Dimension screen = Toolkit.getScreenSize(); +// setLocation((screen.width - size.width) / 2, +// (screen.height - size.height) / 2); + window.setLocationRelativeTo(null); + + window.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE); + window.addWindowListener(new WindowAdapter() { + public void windowClosing(WindowEvent e) { + hide(); + } + }); + Toolkit.registerWindowCloseKeys(window.getRootPane(), new ActionListener() { + public void actionPerformed(ActionEvent actionEvent) { + hide(); + } + }); + + Toolkit.setIcon(window); + + colorListener = new ColorListener(); + hueField.getDocument().addDocumentListener(colorListener); + saturationField.getDocument().addDocumentListener(colorListener); + brightnessField.getDocument().addDocumentListener(colorListener); + redField.getDocument().addDocumentListener(colorListener); + greenField.getDocument().addDocumentListener(colorListener); + blueField.getDocument().addDocumentListener(colorListener); + hexField.getDocument().addDocumentListener(colorListener); + + setColor(initialColor); +// System.out.println("4: " + hexField.getInsets()); + } + + + //hexField.setText("#FFFFFF"); + + + public void show() { + window.setVisible(true); + window.setCursor(Cursor.getPredefinedCursor(Cursor.CROSSHAIR_CURSOR)); + } + + + public void hide() { + window.setVisible(false); + } + + + public Color getColor() { + return new Color(red, green, blue); + } + + + public void setColor(Color color) { + updateRGB(color.getRGB()); + } + + + public String getHexColor() { + return "#" + PApplet.hex(red, 2) + PApplet.hex(green, 2) + PApplet.hex(blue, 2); + } + + + public class ColorListener implements DocumentListener { + + public void changedUpdate(DocumentEvent e) { + //System.out.println("changed"); + } + + public void removeUpdate(DocumentEvent e) { + //System.out.println("remove"); + } + + + boolean updating; + + public void insertUpdate(DocumentEvent e) { + if (updating) return; // don't update forever recursively + updating = true; + + Document doc = e.getDocument(); + if (doc == hueField.getDocument()) { + hue = bounded(hue, hueField, 359); + updateRGB(); + updateHex(); + + } else if (doc == saturationField.getDocument()) { + saturation = bounded(saturation, saturationField, 99); + updateRGB(); + updateHex(); + + } else if (doc == brightnessField.getDocument()) { + brightness = bounded(brightness, brightnessField, 99); + updateRGB(); + updateHex(); + + } else if (doc == redField.getDocument()) { + red = bounded(red, redField, 255); + updateHSB(); + updateHex(); + + } else if (doc == greenField.getDocument()) { + green = bounded(green, greenField, 255); + updateHSB(); + updateHex(); + + } else if (doc == blueField.getDocument()) { + blue = bounded(blue, blueField, 255); + updateHSB(); + updateHex(); + + } else if (doc == hexField.getDocument()) { + String str = hexField.getText(); + if (str.startsWith("#")) { + str = str.substring(1); + } + while (str.length() < 6) { + str += "0"; + } + if (str.length() > 6) { + str = str.substring(0, 6); + } + updateRGB(Integer.parseInt(str, 16)); + updateHSB(); + } + range.redraw(); + slider.redraw(); + //colorPanel.setBackground(new Color(red, green, blue)); + colorPanel.repaint(); + updating = false; + } +} + + + /** + * Set the RGB values based on the current HSB values. + */ + protected void updateRGB() { + updateRGB(Color.HSBtoRGB(hue / 359f, + saturation / 99f, + brightness / 99f)); + } + + + /** + * Set the RGB values based on a calculated ARGB int. + * Used by both updateRGB() to set the color from the HSB values, + * and by updateHex(), to unpack the hex colors and assign them. + */ + protected void updateRGB(int rgb) { + red = (rgb >> 16) & 0xff; + green = (rgb >> 8) & 0xff; + blue = rgb & 0xff; + + redField.setText(String.valueOf(red)); + greenField.setText(String.valueOf(green)); + blueField.setText(String.valueOf(blue)); + } + + + /** + * Set the HSB values based on the current RGB values. + */ + protected void updateHSB() { + float hsb[] = new float[3]; + Color.RGBtoHSB(red, green, blue, hsb); + + hue = (int) (hsb[0] * 359.0f); + saturation = (int) (hsb[1] * 99.0f); + brightness = (int) (hsb[2] * 99.0f); + + hueField.setText(String.valueOf(hue)); + saturationField.setText(String.valueOf(saturation)); + brightnessField.setText(String.valueOf(brightness)); + } + + + protected void updateHex() { + hexField.setText(getHexColor()); + } + + + /** + * Get the bounded value for a specific range. If the value is outside + * the max, you can't edit right away, so just act as if it's already + * been bounded and return the bounded value, then fire an event to set + * it to the value that was just returned. + */ + protected int bounded(int current, final JTextField field, final int max) { + String text = field.getText(); + if (text.length() == 0) { + return 0; + } + try { + int value = Integer.parseInt(text); + if (value > max) { + SwingUtilities.invokeLater(new Runnable() { + public void run() { + field.setText(String.valueOf(max)); + } + }); + return max; + } + return value; + + } catch (NumberFormatException e) { + return current; // should not be reachable + } + } + + + protected Container createColorFields(String buttonName, ActionListener buttonListener) { + Box box = Box.createVerticalBox(); + box.setAlignmentY(0); + + final int GAP = Base.isWindows() ? 5 : 0; + final int BETWEEN = Base.isWindows() ? 8 : 6; //10; + + Box row; + + row = Box.createHorizontalBox(); + if (Base.isMacOS()) { + row.add(Box.createHorizontalStrut(17)); + } else { + row.add(createFixedLabel("")); + } + // Can't just set the bg color of the panel because it also tints the bevel + // (on OS X), which looks odd. So instead we override paintComponent(). + colorPanel = new JPanel() { + public void paintComponent(Graphics g) { + g.setColor(new Color(red, green, blue)); + Dimension size = getSize(); + g.fillRect(0, 0, size.width, size.height); + } + }; + colorPanel.setBorder(BorderFactory.createBevelBorder(BevelBorder.LOWERED)); + Dimension dim = new Dimension(70, 25); + colorPanel.setMinimumSize(dim); + colorPanel.setMaximumSize(dim); + colorPanel.setPreferredSize(dim); + row.add(colorPanel); + row.add(Box.createHorizontalGlue()); + box.add(row); + box.add(Box.createVerticalStrut(BETWEEN)); +// if (Base.isMacOS()) { // need a little extra +// box.add(Box.createVerticalStrut(BETWEEN)); +// } + + + row = Box.createHorizontalBox(); + row.add(createFixedLabel("H")); + row.add(hueField = new NumberField(4, false)); + row.add(new JLabel(" \u00B0")); // degree symbol + row.add(Box.createHorizontalGlue()); + box.add(row); + box.add(Box.createVerticalStrut(GAP)); + + row = Box.createHorizontalBox(); + row.add(createFixedLabel("S")); + row.add(saturationField = new NumberField(4, false)); + row.add(new JLabel(" %")); + row.add(Box.createHorizontalGlue()); + box.add(row); + box.add(Box.createVerticalStrut(GAP)); + + row = Box.createHorizontalBox(); + row.add(createFixedLabel("B")); + row.add(brightnessField = new NumberField(4, false)); + row.add(new JLabel(" %")); + row.add(Box.createHorizontalGlue()); + box.add(row); + box.add(Box.createVerticalStrut(BETWEEN)); + + // + + row = Box.createHorizontalBox(); + row.add(createFixedLabel("R")); + row.add(redField = new NumberField(4, false)); + row.add(Box.createHorizontalGlue()); + box.add(row); + box.add(Box.createVerticalStrut(GAP)); + + row = Box.createHorizontalBox(); + row.add(createFixedLabel("G")); + row.add(greenField = new NumberField(4, false)); + row.add(Box.createHorizontalGlue()); + box.add(row); + box.add(Box.createVerticalStrut(GAP)); + + row = Box.createHorizontalBox(); + row.add(createFixedLabel("B")); + row.add(blueField = new NumberField(4, false)); + row.add(Box.createHorizontalGlue()); + box.add(row); + box.add(Box.createVerticalStrut(BETWEEN)); + + // + + row = Box.createHorizontalBox(); + row.add(createFixedLabel("")); + // Windows needs extra space, OS X and Linux do not + // Mac OS X needs 6 because #CCCCCC is quite wide + final int hexCount = Base.isWindows() ? 7 : 6; + row.add(hexField = new NumberField(hexCount, true)); + row.add(Box.createHorizontalGlue()); + box.add(row); + box.add(Box.createVerticalStrut(GAP)); + + // + +// // Not great, because the insets make things weird anyway +// //Dimension dim = new Dimension(hexField.getPreferredSize()); +// Dimension dim = new Dimension(70, 20); +// colorPanel.setMinimumSize(dim); +// colorPanel.setMaximumSize(dim); +// colorPanel.setPreferredSize(dim); +//// colorPanel.setBorder(new EmptyBorder(hexField.getInsets())); + + // + + row = Box.createHorizontalBox(); + if (Base.isMacOS()) { + row.add(Box.createHorizontalStrut(11)); + } else { + row.add(createFixedLabel("")); + } + JButton button = new JButton(buttonName); + button.addActionListener(buttonListener); + //System.out.println("button: " + button.getInsets()); + row.add(button); + row.add(Box.createHorizontalGlue()); + box.add(row); + + // + + box.add(Box.createVerticalGlue()); + return box; + } + + + int labelH; + + /** + * return a label of a fixed width + */ + protected JLabel createFixedLabel(String title) { + JLabel label = new JLabel(title); + if (labelH == 0) { + labelH = label.getPreferredSize().height; + } + Dimension dim = new Dimension(15, labelH); + label.setPreferredSize(dim); + label.setMinimumSize(dim); + label.setMaximumSize(dim); + return label; + } + + + public class ColorRange extends PApplet { + + static final int WIDE = 256; + static final int HIGH = 256; + + int lastX, lastY; + + + public void setup() { + size(WIDE, HIGH); //, P3D); + noLoop(); + + colorMode(HSB, 360, 256, 256); + noFill(); + rectMode(CENTER); + + loadPixels(); + } + + public void draw() { +// if ((g == null) || (g.pixels == null)) return; + if ((width != WIDE) || (height < HIGH)) { + //System.out.println("bad size " + width + " " + height); + return; + } + + int index = 0; + for (int j = 0; j < 256; j++) { + for (int i = 0; i < 256; i++) { + pixels[index++] = color(hue, i, 255 - j); + } + } + + updatePixels(); + stroke((brightness > 50) ? 0 : 255); + rect(lastX, lastY, 9, 9); + } + + public void mousePressed() { + updateMouse(); + } + + public void mouseDragged() { + updateMouse(); + } + + public void updateMouse() { + if ((mouseX >= 0) && (mouseX < 256) && + (mouseY >= 0) && (mouseY < 256)) { + int nsaturation = (int) (100 * (mouseX / 255.0f)); + int nbrightness = 100 - ((int) (100 * (mouseY / 255.0f))); + saturationField.setText(String.valueOf(nsaturation)); + brightnessField.setText(String.valueOf(nbrightness)); + + lastX = mouseX; + lastY = mouseY; + } + } + + public Dimension getPreferredSize() { + return new Dimension(WIDE, HIGH); + } + + public Dimension getMinimumSize() { + return new Dimension(WIDE, HIGH); + } + + public Dimension getMaximumSize() { + return new Dimension(WIDE, HIGH); + } + + public void keyPressed() { + if (key == ESC) { + ColorChooser.this.hide(); + // don't quit out of processing + // http://dev.processing.org/bugs/show_bug.cgi?id=1006 + key = 0; + } + } + } + + + public class ColorSlider extends PApplet { + + static final int WIDE = 20; + static final int HIGH = 256; + + public void setup() { + size(WIDE, HIGH); //, P3D); + colorMode(HSB, 255, 100, 100); + noLoop(); + loadPixels(); + } + + public void draw() { +// if ((g == null) || (g.pixels == null)) return; + if ((width != WIDE) || (height < HIGH)) { + //System.out.println("bad size " + width + " " + height); + return; + } + + int index = 0; + int sel = 255 - (int) (255 * (hue / 359f)); + for (int j = 0; j < 256; j++) { + int c = color(255 - j, 100, 100); + if (j == sel) c = 0xFF000000; + for (int i = 0; i < WIDE; i++) { + g.pixels[index++] = c; + } + } + updatePixels(); + } + + public void mousePressed() { + updateMouse(); + } + + public void mouseDragged() { + updateMouse(); + } + + public void updateMouse() { + if ((mouseX >= 0) && (mouseX < 256) && + (mouseY >= 0) && (mouseY < 256)) { + int nhue = 359 - (int) (359 * (mouseY / 255.0f)); + hueField.setText(String.valueOf(nhue)); + } + } + + public Dimension getPreferredSize() { + return new Dimension(WIDE, HIGH); + } + + public Dimension getMinimumSize() { + return new Dimension(WIDE, HIGH); + } + + public Dimension getMaximumSize() { + return new Dimension(WIDE, HIGH); + } + + public void keyPressed() { + if (key == ESC) { + ColorChooser.this.hide(); + // don't quit out of processing + // http://dev.processing.org/bugs/show_bug.cgi?id=1006 + key = 0; + } + } + } + + + /** + * Extension of JTextField that only allows numbers + */ + class NumberField extends JTextField { + + public boolean allowHex; + + public NumberField(int cols, boolean allowHex) { + super(cols); + this.allowHex = allowHex; + } + + protected Document createDefaultModel() { + return new NumberDocument(this); + } + + public Dimension getPreferredSize() { + if (!allowHex) { + return new Dimension(45, super.getPreferredSize().height); + } + return super.getPreferredSize(); + } + + public Dimension getMinimumSize() { + return getPreferredSize(); + } + + public Dimension getMaximumSize() { + return getPreferredSize(); + } + } + + + /** + * Document model to go with JTextField that only allows numbers. + */ + class NumberDocument extends PlainDocument { + + NumberField parentField; + + public NumberDocument(NumberField parentField) { + this.parentField = parentField; + //System.out.println("setting parent to " + parentSelector); + } + + public void insertString(int offs, String str, AttributeSet a) + throws BadLocationException { + + if (str == null) return; + + char chars[] = str.toCharArray(); + int charCount = 0; + // remove any non-digit chars + for (int i = 0; i < chars.length; i++) { + boolean ok = Character.isDigit(chars[i]); + if (parentField.allowHex) { + if ((chars[i] >= 'A') && (chars[i] <= 'F')) ok = true; + if ((chars[i] >= 'a') && (chars[i] <= 'f')) ok = true; + if ((offs == 0) && (i == 0) && (chars[i] == '#')) ok = true; + } + if (ok) { + if (charCount != i) { // shift if necessary + chars[charCount] = chars[i]; + } + charCount++; + } + } + super.insertString(offs, new String(chars, 0, charCount), a); + // can't call any sort of methods on the enclosing class here + // seems to have something to do with how Document objects are set up + } + } + + +// static public void main(String[] args) { +// ColorSelector cs = new ColorSelector(); +// cs.init(null); +// EventQueue.invokeLater(cs); +// } +} diff --git a/app/src/processing/app/Editor.java b/app/src/processing/app/Editor.java index bd824d294..7f4b1a8be 100644 --- a/app/src/processing/app/Editor.java +++ b/app/src/processing/app/Editor.java @@ -472,6 +472,14 @@ public abstract class Editor extends JFrame implements RunnerListener { * with things in the Preferences window. */ protected void applyPreferences() { + // Update fonts and other items controllable from the prefs + textarea.getPainter().updateAppearance(); + textarea.repaint(); + + console.updateAppearance(); + + // All of this code was specific to using an external editor. + /* // // apply the setting for 'use external editor' // boolean external = Preferences.getBoolean("editor.external"); // textarea.setEditable(!external); @@ -494,7 +502,7 @@ public abstract class Editor extends JFrame implements RunnerListener { // } // apply changes to the font size for the editor - painter.setFont(Preferences.getFont("editor.font")); +// painter.setFont(Preferences.getFont("editor.font")); // in case tab expansion stuff has changed // removing this, just checking prefs directly instead @@ -505,6 +513,7 @@ public abstract class Editor extends JFrame implements RunnerListener { //sketchbook.rebuildMenus(); // For 0126, moved into Base, which will notify all editors. //base.rebuildMenusAsync(); + */ } @@ -933,25 +942,102 @@ public abstract class Editor extends JFrame implements RunnerListener { } +// /** +// * Attempt to init or run a Tool from the safety of a try/catch block that +// * will report errors to the user. +// * @param tool The Tool object to be inited or run +// * @param item null to call init(), or the existing JMenuItem for run() +// * @return +// */ +// protected boolean safeTool(Tool tool, JMenuItem item) { +// try { +// if (item == null) { +// tool.init(Editor.this); +// } else { +// tool.run(); +// } +// return true; +// +// } catch (NoSuchMethodError nsme) { +// System.out.println("tool is " + tool + " "); +// statusError("\"" + tool.getMenuTitle() + "\" " + +// "is not compatible with this version of Processing"); +// nsme.printStackTrace(); +// +// } catch (Exception ex) { +// statusError("An error occurred inside \"" + tool.getMenuTitle() + "\""); +// ex.printStackTrace(); +// } +// if (item != null) { +// item.setEnabled(false); // don't you try that again +// } +// return false; +// } + + + void addToolItem(final Tool tool, HashMap toolItems) { + String title = tool.getMenuTitle(); + final JMenuItem item = new JMenuItem(title); + item.addActionListener(new ActionListener() { + + public void actionPerformed(ActionEvent e) { + try { + tool.run(); + + } catch (NoSuchMethodError nsme) { + statusError("\"" + tool.getMenuTitle() + "\" is not" + + "compatible with this version of Processing"); + //nsme.printStackTrace(); + Base.log("Incompatible tool found during tool.run()", nsme); + item.setEnabled(false); + + } catch (Exception ex) { + statusError("An error occurred inside \"" + tool.getMenuTitle() + "\""); + ex.printStackTrace(); + item.setEnabled(false); + } + } + }); + //menu.add(item); + toolItems.put(title, item); + } + + protected void addTools(JMenu menu, ArrayList tools) { HashMap toolItems = new HashMap(); for (final ToolContribution tool : tools) { - String title = tool.getMenuTitle(); - JMenuItem item = new JMenuItem(title); - item.addActionListener(new ActionListener() { - boolean inited; + try { + tool.init(Editor.this); + // If init() fails, the item won't be added to the menu + addToolItem(tool, toolItems); + + // With the exceptions, we can't call statusError because the window + // isn't completely set up yet. Also not gonna pop up a warning because + // people may still be running different versions of Processing. + // TODO Once the dust settles on 2.x, change this to Base.showError() + // and open the Tools folder instead of showing System.err.println(). + + } catch (NoSuchMethodError nsme) { + System.err.println("\"" + tool.getMenuTitle() + "\" is not " + + "compatible with this version of Processing"); + System.err.println("The " + nsme.getMessage() + " method no longer exists."); + Base.log("Incompatible Tool found during tool.init()", nsme); - public void actionPerformed(ActionEvent e) { - if (!inited) { - tool.init(Editor.this); - inited = true; - } - EventQueue.invokeLater(tool); - } - }); - //menu.add(item); - toolItems.put(title, item); + } catch (NoClassDefFoundError ncdfe) { + System.err.println("\"" + tool.getMenuTitle() + "\" is not " + + "compatible with this version of Processing"); + System.err.println("The " + ncdfe.getMessage() + " class is no longer available."); + Base.log("Incompatible Tool found during tool.init()", ncdfe); + + } catch (Error err) { + System.err.println("An error occurred inside \"" + tool.getMenuTitle() + "\""); + err.printStackTrace(); + + } catch (Exception ex) { + System.err.println("An exception occurred inside \"" + tool.getMenuTitle() + "\""); + ex.printStackTrace(); + } } ArrayList toolList = new ArrayList(toolItems.keySet()); @@ -984,7 +1070,7 @@ public abstract class Editor extends JFrame implements RunnerListener { item.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { - SwingUtilities.invokeLater(tool); + EventQueue.invokeLater(tool); } }); menu.add(item); @@ -1009,9 +1095,6 @@ public abstract class Editor extends JFrame implements RunnerListener { addToolMenuItem(menu, "processing.app.tools.Archiver"); if (Base.isMacOS()) { - if (SerialFixer.isNeeded()) { - addToolMenuItem(menu, "processing.app.tools.SerialFixer"); - } addToolMenuItem(menu, "processing.app.tools.InstallCommander"); } @@ -1419,7 +1502,7 @@ public abstract class Editor extends JFrame implements RunnerListener { public int getScrollPosition() { - return textarea.getScrollPosition(); + return textarea.getVerticalScrollPosition(); } @@ -1688,7 +1771,7 @@ public abstract class Editor extends JFrame implements RunnerListener { } else { // replace with new bootiful text // selectionEnd hopefully at least in the neighborhood - int scrollPos = textarea.getScrollPosition(); + int scrollPos = textarea.getVerticalScrollPosition(); setText(formattedText); setSelection(selectionEnd, selectionEnd); @@ -1696,14 +1779,14 @@ public abstract class Editor extends JFrame implements RunnerListener { // Since we're not doing a good job of maintaining position anyway, // a more complicated workaround here is fairly pointless. // http://code.google.com/p/processing/issues/detail?id=1533 - if (scrollPos != textarea.getScrollPosition()) { + if (scrollPos != textarea.getVerticalScrollPosition()) { // boolean wouldBeVisible = // scrollPos >= textarea.getFirstLine() && // scrollPos < textarea.getLastLine(); // // // if it was visible, and now it's not, then allow the scroll // if (!(wasVisible && !wouldBeVisible)) { - textarea.setScrollPosition(scrollPos); + textarea.setVerticalScrollPosition(scrollPos); // } } getSketch().setModified(true); @@ -2010,6 +2093,9 @@ public abstract class Editor extends JFrame implements RunnerListener { // As of Processing 1.0.10, this always happens immediately. // http://dev.processing.org/bugs/show_bug.cgi?id=1456 + // With Java 7u40 on OS X, need to bring the window forward. + toFront(); + String prompt = "Save changes to " + sketch.getName() + "? "; if (!Base.isMacOS()) { @@ -2108,31 +2194,30 @@ public abstract class Editor extends JFrame implements RunnerListener { protected boolean handleOpenInternal(String path) { // check to make sure that this .pde file is // in a folder of the same name - File file = new File(path); - File parentFile = new File(file.getParent()); - String parentName = parentFile.getName(); - String pdeName = parentName + ".pde"; - File altFile = new File(file.getParent(), pdeName); + final File file = new File(path); + final File parentFile = new File(file.getParent()); + final String parentName = parentFile.getName(); + final String defaultName = parentName + "." + mode.getDefaultExtension(); + final File altFile = new File(file.getParent(), defaultName); - if (pdeName.equals(file.getName())) { + if (defaultName.equals(file.getName())) { // no beef with this guy - } else if (altFile.exists()) { - // user selected a .java from the same sketch, - // but open the .pde instead + // The user selected a source file from the same sketch, + // but open the file with the default extension instead. path = altFile.getAbsolutePath(); - //System.out.println("found alt file in same folder"); - - } else if (!path.endsWith(".pde")) { - Base.showWarning("Bad file selected", - "Processing can only open its own sketches\n" + - "and other files ending in .pde", null); + } else if (!mode.canEdit(file)) { + final String modeName = (mode.getTitle().equals("Java")) ? "Processing" + : mode.getTitle(); + Base + .showWarning("Bad file selected", modeName + + " can only open its own sketches\nand other files ending in " + + mode.getDefaultExtension(), null); return false; - } else { - String properParent = - file.getName().substring(0, file.getName().length() - 4); - + final String properParent = + file.getName().substring(0, file.getName().lastIndexOf('.')); + Object[] options = { "OK", "Cancel" }; String prompt = "The file \"" + file.getName() + "\" needs to be inside\n" + @@ -2328,9 +2413,9 @@ public abstract class Editor extends JFrame implements RunnerListener { } if (pageFormat != null) { //System.out.println("setting page format " + pageFormat); - printerJob.setPrintable(textarea.getPainter(), pageFormat); + printerJob.setPrintable(textarea.getPrintable(), pageFormat); } else { - printerJob.setPrintable(textarea.getPainter()); + printerJob.setPrintable(textarea.getPrintable()); } // set the name of the job to the code name printerJob.setJobName(sketch.getCurrentCode().getPrettyName()); @@ -2474,6 +2559,22 @@ public abstract class Editor extends JFrame implements RunnerListener { } } + + /** + * Returns the current notice message in the editor status bar. + */ + public String getStatusMessage(){ + return status.message; + } + + + /** + * Returns the current mode of the editor status bar: NOTICE, ERR or EDIT. + */ + public int getStatusMode(){ + return status.mode; + } + /** * Clear the status area. diff --git a/app/src/processing/app/EditorConsole.java b/app/src/processing/app/EditorConsole.java index dd822c7d0..6ad9eb709 100644 --- a/app/src/processing/app/EditorConsole.java +++ b/app/src/processing/app/EditorConsole.java @@ -164,6 +164,20 @@ public class EditorConsole extends JScrollPane { } + /** + * Update the font family and sizes based on the Preferences window. + */ + protected void updateAppearance() { + String fontFamily = Preferences.get("editor.font.family"); + int fontSize = Preferences.getInteger("console.font.size"); + StyleConstants.setFontFamily(stdStyle, fontFamily); + StyleConstants.setFontSize(stdStyle, fontSize); + StyleConstants.setFontFamily(errStyle, fontFamily); + StyleConstants.setFontSize(errStyle, fontSize); + clear(); // otherwise we'll have mixed fonts + } + + /** * Change coloring, fonts, etc in response to a mode change. */ diff --git a/app/src/processing/app/EditorLineStatus.java b/app/src/processing/app/EditorLineStatus.java index dc1a842fb..ff7a1bacd 100644 --- a/app/src/processing/app/EditorLineStatus.java +++ b/app/src/processing/app/EditorLineStatus.java @@ -23,6 +23,7 @@ package processing.app; import java.awt.*; + import javax.swing.*; @@ -87,6 +88,10 @@ public class EditorLineStatus extends JComponent { public void paintComponent(Graphics g) { + Graphics2D g2 = (Graphics2D) g; + g2.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, + RenderingHints.VALUE_TEXT_ANTIALIAS_ON); + g.setColor(background); Dimension size = getSize(); g.fillRect(0, 0, size.width, size.height); @@ -94,7 +99,8 @@ public class EditorLineStatus extends JComponent { g.setFont(font); g.setColor(foreground); int baseline = (high + g.getFontMetrics().getAscent()) / 2; - g.drawString(text, 6, baseline); + // With 7u40 (or Source Code Sans?) things seem to be edged up a bit + g.drawString(text, 6, baseline - 1); } diff --git a/app/src/processing/app/EditorState.java b/app/src/processing/app/EditorState.java index a55491af1..f8cc950c2 100644 --- a/app/src/processing/app/EditorState.java +++ b/app/src/processing/app/EditorState.java @@ -21,6 +21,7 @@ package processing.app; +import java.awt.Frame; import java.awt.GraphicsConfiguration; import java.awt.GraphicsDevice; import java.awt.GraphicsEnvironment; @@ -53,6 +54,7 @@ public class EditorState { // int displayW, displayH; // String deviceName; // not really useful b/c it's more about bounds anyway Rectangle deviceBounds; + boolean isMaximized; /** @@ -173,6 +175,7 @@ public class EditorState { synchronized (editors) { final int OVER = 50; Editor lastOpened = editors.get(editors.size() - 1); + isMaximized = (lastOpened.getExtendedState() == Frame.MAXIMIZED_BOTH); editorBounds = lastOpened.getBounds(); editorBounds.x += OVER; editorBounds.y += OVER; @@ -180,8 +183,14 @@ public class EditorState { if (!deviceBounds.contains(editorBounds)) { // Warp the next window to a randomish location on screen. - editorBounds.x = deviceBounds.x + (int) (Math.random() * (deviceBounds.width - defaultWidth)); - editorBounds.y = deviceBounds.y + (int) (Math.random() * (deviceBounds.height - defaultHeight)); + editorBounds.x = deviceBounds.x + + (int) (Math.random() * (deviceBounds.width - defaultWidth)); + editorBounds.y = deviceBounds.y + + (int) (Math.random() * (deviceBounds.height - defaultHeight)); + } + if (isMaximized) { + editorBounds.width = defaultWidth; + editorBounds.height = defaultHeight; } } } @@ -204,6 +213,9 @@ public class EditorState { if (dividerLocation != 0) { editor.setDividerLocation(dividerLocation); } + if (isMaximized) { + editor.setExtendedState(Frame.MAXIMIZED_BOTH); + } } diff --git a/app/src/processing/app/EditorStatus.java b/app/src/processing/app/EditorStatus.java index 361db92d9..c99d45f31 100644 --- a/app/src/processing/app/EditorStatus.java +++ b/app/src/processing/app/EditorStatus.java @@ -25,6 +25,7 @@ package processing.app; import java.awt.*; import java.awt.event.*; + import javax.swing.*; @@ -35,11 +36,9 @@ public class EditorStatus extends JPanel { Color[] bgcolor; Color[] fgcolor; - static final int NOTICE = 0; - static final int ERR = 1; - //static final int PROMPT = 2; - //static final int EDIT = 3; - static final int EDIT = 2; + static public final int NOTICE = 0; + static public final int ERR = 1; + static public final int EDIT = 2; static final int YES = 1; static final int NO = 2; @@ -196,6 +195,11 @@ public class EditorStatus extends JPanel { Graphics2D g2 = (Graphics2D) g; if (Toolkit.highResDisplay()) { g2.scale(2, 2); + if (Base.isUsableOracleJava()) { + // Oracle Java looks better with anti-aliasing turned on + g2.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, + RenderingHints.VALUE_TEXT_ANTIALIAS_ON); + } } else { g2.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON); diff --git a/app/src/processing/app/FindReplace.java b/app/src/processing/app/FindReplace.java index 736319916..de6e1b34a 100644 --- a/app/src/processing/app/FindReplace.java +++ b/app/src/processing/app/FindReplace.java @@ -454,9 +454,22 @@ public class FindReplace extends JFrame { editor.setSelection(0, 0); boolean foundAtLeastOne = false; - while (true) { + int startTab = -1, startIndex = -1, c = 50000; + // you couldn't seriously be replacing 50K times o_O + while (--c > 0) { if (find(false, false)) { - foundAtLeastOne = true; + if(editor.getSketch().getCurrentCodeIndex() == startTab + && editor.getSelectionStart() == startIndex){ + // we've reached where we started, so stop the replace + Toolkit.beep(); + editor.statusNotice("Reached beginning of search!"); + break; + } + if(!foundAtLeastOne){ + foundAtLeastOne = true; + startTab = editor.getSketch().getCurrentCodeIndex(); + startIndex = editor.getSelectionStart(); + } replace(); } else { break; diff --git a/app/src/processing/app/Mode.java b/app/src/processing/app/Mode.java index 237f9f650..ea19fde35 100644 --- a/app/src/processing/app/Mode.java +++ b/app/src/processing/app/Mode.java @@ -43,7 +43,7 @@ public abstract class Mode { protected File folder; - protected PdeKeywords tokenMarker = new PdeKeywords(); + protected TokenMarker tokenMarker; protected HashMap keywordToReference = new HashMap(); @@ -94,6 +94,7 @@ public abstract class Mode { public Mode(Base base, File folder) { this.base = base; this.folder = folder; + tokenMarker = createTokenMarker(); // Get paths for the libraries and examples in the mode folder examplesFolder = new File(folder, "examples"); @@ -125,27 +126,38 @@ public abstract class Mode { protected void loadKeywords(File keywordFile) throws IOException { + // overridden for Python, where # is an actual keyword + loadKeywords(keywordFile, "#"); + } + + + protected void loadKeywords(File keywordFile, + String commentPrefix) throws IOException { BufferedReader reader = PApplet.createReader(keywordFile); String line = null; while ((line = reader.readLine()) != null) { -// String[] pieces = PApplet.trim(PApplet.split(line, '\t')); - String[] pieces = PApplet.splitTokens(line); - if (pieces.length >= 2) { - String keyword = pieces[0]; - String coloring = pieces[1]; + if (!line.trim().startsWith(commentPrefix)) { + // Was difficult to make sure that mode authors were properly doing + // tab-separated values. By definition, there can't be additional + // spaces inside a keyword (or filename), so just splitting on tokens. + String[] pieces = PApplet.splitTokens(line); + if (pieces.length >= 2) { + String keyword = pieces[0]; + String coloring = pieces[1]; - if (coloring.length() > 0) { - tokenMarker.addColoring(keyword, coloring); - } - if (pieces.length == 3) { - String htmlFilename = pieces[2]; - if (htmlFilename.length() > 0) { - // if the file is for the version with parens, - // add a paren to the keyword - if (htmlFilename.endsWith("_")) { - keyword += "_"; + if (coloring.length() > 0) { + tokenMarker.addColoring(keyword, coloring); + } + if (pieces.length == 3) { + String htmlFilename = pieces[2]; + if (htmlFilename.length() > 0) { + // if the file is for the version with parens, + // add a paren to the keyword + if (htmlFilename.endsWith("_")) { + keyword += "_"; + } + keywordToReference.put(keyword, htmlFilename); } - keywordToReference.put(keyword, htmlFilename); } } } @@ -901,6 +913,10 @@ public abstract class Mode { public TokenMarker getTokenMarker() { return tokenMarker; } + + protected TokenMarker createTokenMarker() { + return new PdeKeywords(); + } // abstract public Formatter createFormatter(); @@ -959,9 +975,10 @@ public abstract class Mode { s = st.nextToken(); boolean bold = (s.indexOf("bold") != -1); - boolean italic = (s.indexOf("italic") != -1); +// boolean italic = (s.indexOf("italic") != -1); - return new SyntaxStyle(color, italic, bold); +// return new SyntaxStyle(color, italic, bold); + return new SyntaxStyle(color, bold); } @@ -998,6 +1015,18 @@ public abstract class Mode { } + /** + * @param f File to be checked against this mode's accepted extensions. + * @return Whether or not the given file name features an extension supported by this mode. + */ + public boolean canEdit(final File f) { + final int dot = f.getName().lastIndexOf('.'); + if (dot < 0) { + return false; + } + return validExtension(f.getName().substring(dot + 1)); + } + /** * Check this extension (no dots, please) against the list of valid * extensions. @@ -1017,6 +1046,18 @@ public abstract class Mode { abstract public String getDefaultExtension(); + /** + * Returns the appropriate file extension to use for auxilliary source files in a sketch. + * For example, in a Java-mode sketch, auxilliary files should be name "Foo.java"; in + * Python mode, they should be named "foo.py". + * + *

Modes that do not override this function will get the default behavior of returning the + * default extension. + */ + public String getModuleExtension() { + return getDefaultExtension(); + } + /** * Returns a String[] array of proper extensions. @@ -1058,4 +1099,9 @@ public abstract class Mode { // public void handleNewReplace() { // base.handleNewReplace(); // } + + @Override + public String toString() { + return getTitle(); + } } \ No newline at end of file diff --git a/app/src/processing/app/Preferences.java b/app/src/processing/app/Preferences.java index 7115d8908..525ae5f03 100644 --- a/app/src/processing/app/Preferences.java +++ b/app/src/processing/app/Preferences.java @@ -29,7 +29,6 @@ import java.util.*; import javax.swing.*; -import processing.app.syntax.*; import processing.core.*; @@ -61,6 +60,7 @@ import processing.core.*; */ public class Preferences { + static final Integer[] FONT_SIZES = { 10, 12, 14, 18, 24, 36, 48 }; // what to call the feller static final String PREFS_FILE = "preferences.txt"; //$NON-NLS-1$ @@ -113,19 +113,25 @@ public class Preferences { JCheckBox memoryOverrideBox; JTextField memoryField; JCheckBox checkUpdatesBox; - JTextField fontSizeField; + //JTextField fontSizeField; + JComboBox fontSizeField; + JComboBox consoleSizeField; JCheckBox inputMethodBox; JCheckBox autoAssociateBox; - JRadioButton bitsThirtyTwoButton; - JRadioButton bitsSixtyFourButton; + + //JRadioButton bitsThirtyTwoButton; + //JRadioButton bitsSixtyFourButton; + JComboBox displaySelectionBox; JComboBox languageSelectionBox; int displayCount; + + //Font[] monoFontList; + String[] monoFontFamilies; + JComboBox fontSelectionBox; - // the calling editor, so updates can be applied - -// Editor editor; + /** Base object so that updates can be applied to the list of editors. */ Base base; @@ -141,7 +147,9 @@ public class Preferences { // start by loading the defaults, in case something // important was deleted from the user prefs try { - load(Base.getLibStream("preferences.txt")); //$NON-NLS-1$ + // Name changed for 2.1b2 to avoid problems with users modifying or + // replacing the file after doing a search for "preferences.txt". + load(Base.getLibStream("defaults.txt")); //$NON-NLS-1$ } catch (Exception e) { Base.showError(null, "Could not read default settings.\n" + "You'll need to reinstall Processing.", e); @@ -239,6 +247,11 @@ public class Preferences { dialog = new JFrame(Language.text("preferences")); dialog.setResizable(false); +// GroupLayout layout = new GroupLayout(getContentPane()); +// dialog.getContentPane().setLayout(layout); +// layout.setAutoCreateGaps(true); +// layout.setAutoCreateContainerGaps(true); + Container pain = dialog.getContentPane(); pain.setLayout(null); @@ -321,22 +334,7 @@ public class Preferences { top += d.height + GUI_BETWEEN; - // Editor font size [ ] - - Container box = Box.createHorizontalBox(); - label = new JLabel(Language.text("preferences.editor_font_size")+": "); - box.add(label); - fontSizeField = new JTextField(4); - box.add(fontSizeField); - label = new JLabel(" ("+Language.text("preferences.requires_restart")+")"); - box.add(label); - pain.add(box); - d = box.getPreferredSize(); - box.setBounds(left, top, d.width, d.height); - Font editorFont = Preferences.getFont("editor.font"); - fontSizeField.setText(String.valueOf(editorFont.getSize())); - top += d.height + GUI_BETWEEN; - + // Editor and console font [ Source Code Pro ] // Nevermind on this for now.. Java doesn't seem to have a method for // enumerating only the fixed-width (monospaced) fonts. To do this @@ -345,20 +343,70 @@ public class Preferences { // we'd call that font fixed width. That's all a very expensive set of // operations, so it should also probably be cached between runs and // updated in the background. + + Container fontBox = Box.createHorizontalBox(); + JLabel fontLabel = new JLabel(Language.text("preferences.editor_and_console_font")+": "); + final String fontTip = "" + + "Select the font used in the Editor and the Console.
" + + "Only monospaced (fixed-width) fonts may be used,
" + + "though the list may be imperfect."; + fontLabel.setToolTipText(fontTip); + fontBox.add(fontLabel); + // get a wide name in there before getPreferredSize() is called + fontSelectionBox = new JComboBox(new Object[] { Toolkit.getMonoFontName() }); + fontSelectionBox.setToolTipText(fontTip); +// fontSelectionBox.addItem(Toolkit.getMonoFont(size, style)); + //updateDisplayList(); + fontSelectionBox.setEnabled(false); // don't enable until fonts are loaded + fontBox.add(fontSelectionBox); +// fontBox.add(Box.createHorizontalGlue()); + pain.add(fontBox); + d = fontBox.getPreferredSize(); + fontBox.setBounds(left, top, d.width + 150, d.height); +// fontBox.setBounds(left, top, dialog.getWidth() - left*2, d.height); + top += d.height + GUI_BETWEEN; -// // Editor font -// -// GraphicsEnvironment ge = -// GraphicsEnvironment.getLocalGraphicsEnvironment(); -// Font fonts[] = ge.getAllFonts(); -// ArrayList monoFonts = new ArrayList(); + + // Editor font size [ 12 ] Console font size [ 10 ] + Container box = Box.createHorizontalBox(); + label = new JLabel(Language.text("preferences.editor_font_size")+": "); + box.add(label); + //fontSizeField = new JTextField(4); + fontSizeField = new JComboBox(FONT_SIZES); +// fontSizeField = new JComboBox(FONT_SIZES); + fontSizeField.setEditable(true); + box.add(fontSizeField); +// label = new JLabel(" ("+Language.text("preferences.requires_restart")+")"); +// label = new JLabel(" (requires restart of Processing)"); +// box.add(label); + box.add(Box.createHorizontalStrut(GUI_BETWEEN)); + + label = new JLabel(Language.text("preferences.console_font_size")+": "); + + box.add(label); +// consoleSizeField = new JComboBox(FONT_SIZES); + consoleSizeField = new JComboBox(FONT_SIZES); + consoleSizeField.setEditable(true); + box.add(consoleSizeField); + + pain.add(box); + d = box.getPreferredSize(); + box.setBounds(left, top, d.width, d.height); +// Font editorFont = Preferences.getFont("editor.font"); + //fontSizeField.setText(String.valueOf(editorFont.getSize())); +// fontSizeField.setSelectedItem(editorFont.getSize()); + fontSizeField.setSelectedItem(Preferences.getFont("editor.font.size")); + top += d.height + GUI_BETWEEN; + + // [ ] Use smooth text in editor window - editorAntialiasBox = - new JCheckBox(Language.text("preferences.use_smooth_text")+ - " ("+Language.text("preferences.requires_restart")+")"); + editorAntialiasBox = new JCheckBox(Language.text("preferences.use_smooth_text")); +// new JCheckBox("Use smooth text in editor window " + +// "(requires restart of Processing)"); + pain.add(editorAntialiasBox); d = editorAntialiasBox.getPreferredSize(); // adding +10 because ubuntu + jre 1.5 truncating items @@ -407,17 +455,17 @@ public class Preferences { // top += d.height + GUI_BETWEEN; - // [ ] Delete previous folder on export + // [ ] Delete previous application folder on export deletePreviousBox = - new JCheckBox(Language.text("preferences.delete_previous_folder_on_export")); + new JCheckBox(Language.text("preferences.delete_previous_folder_on_export")); pain.add(deletePreviousBox); d = deletePreviousBox.getPreferredSize(); deletePreviousBox.setBounds(left, top, d.width + 10, d.height); right = Math.max(right, left + d.width); top += d.height + GUI_BETWEEN; - - + + // // [ ] Use external editor // // externalEditorBox = new JCheckBox("Use external editor"); @@ -480,6 +528,7 @@ public class Preferences { // Launch programs as [ ] 32-bit [ ] 64-bit (Mac OS X only) + /* if (Base.isMacOS()) { box = Box.createHorizontalBox(); label = new JLabel(Language.text("preferences.launch_programs_in")+" "); @@ -498,6 +547,7 @@ public class Preferences { box.setBounds(left, top, d.width, d.height); top += d.height + GUI_BETWEEN; } + */ // More preferences are in the ... @@ -644,7 +694,8 @@ public class Preferences { * then send a message to the editor saying that it's time to do the same. */ protected void applyFrame() { - setBoolean("editor.antialias", editorAntialiasBox.isSelected()); //$NON-NLS-1$ + setBoolean("editor.smooth", //$NON-NLS-1$ + editorAntialiasBox.isSelected()); setBoolean("export.delete_target_folder", //$NON-NLS-1$ deletePreviousBox.isSelected()); @@ -717,6 +768,7 @@ public class Preferences { } */ + /* // If a change has been made between 32- and 64-bit, the libraries need // to be reloaded so that their native paths are set correctly. if (Base.isMacOS()) { @@ -729,16 +781,51 @@ public class Preferences { } } } + */ + // Don't change anything if the user closes the window before fonts load + if (fontSelectionBox.isEnabled()) { + String fontFamily = (String) fontSelectionBox.getSelectedItem(); + set("editor.font.family", fontFamily); + } + + /* String newSizeText = fontSizeField.getText(); try { int newSize = Integer.parseInt(newSizeText.trim()); - String pieces[] = PApplet.split(get("editor.font"), ','); //$NON-NLS-1$ - pieces[2] = String.valueOf(newSize); - set("editor.font", PApplet.join(pieces, ',')); //$NON-NLS-1$ + //String pieces[] = PApplet.split(get("editor.font"), ','); //$NON-NLS-1$ + //pieces[2] = String.valueOf(newSize); + //set("editor.font", PApplet.join(pieces, ',')); //$NON-NLS-1$ + set("editor.font.size", String.valueOf(newSize)); } catch (Exception e) { - Base.log("ignoring invalid font size " + newSizeText); //$NON-NLS-1$ + Base.log("Ignoring invalid font size " + newSizeText); //$NON-NLS-1$ + } + */ + try { + Object selection = fontSizeField.getSelectedItem(); + if (selection instanceof String) { + // Replace with Integer version + selection = Integer.parseInt((String) selection); + } + set("editor.font.size", String.valueOf(selection)); + + } catch (NumberFormatException e) { + Base.log("Ignoring invalid font size " + fontSizeField); //$NON-NLS-1$ + fontSizeField.setSelectedItem(getInteger("editor.font.size")); + } + + try { + Object selection = consoleSizeField.getSelectedItem(); + if (selection instanceof String) { + // Replace with Integer version + selection = Integer.parseInt((String) selection); + } + set("console.font.size", String.valueOf(selection)); + + } catch (NumberFormatException e) { + Base.log("Ignoring invalid font size " + consoleSizeField); //$NON-NLS-1$ + consoleSizeField.setSelectedItem(getInteger("console.font.size")); } setBoolean("editor.input_method_support", inputMethodBox.isSelected()); //$NON-NLS-1$ @@ -755,7 +842,7 @@ public class Preferences { protected void showFrame() { - editorAntialiasBox.setSelected(getBoolean("editor.antialias")); //$NON-NLS-1$ + editorAntialiasBox.setSelected(getBoolean("editor.smooth")); //$NON-NLS-1$ inputMethodBox.setSelected(getBoolean("editor.input_method_support")); //$NON-NLS-1$ // set all settings entry boxes to their actual status @@ -788,12 +875,23 @@ public class Preferences { // System.out.println("setting num to " + displayNum); displaySelectionBox.setSelectedIndex(displayNum); } + + // This takes a while to load, so run it from a separate thread + new Thread(new Runnable() { + public void run() { + initFontList(); + } + }).start(); + + fontSizeField.setSelectedItem(getInteger("editor.font.size")); + consoleSizeField.setSelectedItem(getInteger("console.font.size")); memoryOverrideBox. setSelected(getBoolean("run.options.memory")); //$NON-NLS-1$ memoryField. setText(get("run.options.memory.maximum")); //$NON-NLS-1$ + /* if (Base.isMacOS()) { String bits = Preferences.get("run.options.bits"); //$NON-NLS-1$ if (bits.equals("32")) { //$NON-NLS-1$ @@ -807,6 +905,7 @@ public class Preferences { bitsThirtyTwoButton.setEnabled(false); } } + */ if (autoAssociateBox != null) { autoAssociateBox. @@ -817,6 +916,63 @@ public class Preferences { } + /** + * I have some ideas on how we could make Swing even more obtuse for the + * most basic usage scenarios. Is there someone on the team I can contact? + * Oracle, are you listening? + */ + class FontNamer extends JLabel implements ListCellRenderer { + public Component getListCellRendererComponent(JList list, + Font value, int index, + boolean isSelected, + boolean cellHasFocus) { + //if (Base.isMacOS()) { + setText(value.getFamily() + " / " + value.getName() + " (" + value.getPSName() + ")"); + return this; + } + } + + + void initFontList() { + /* + if (monoFontList == null) { + monoFontList = Toolkit.getMonoFontList().toArray(new Font[0]); + fontSelectionBox.setModel(new DefaultComboBoxModel(monoFontList)); + fontSelectionBox.setRenderer(new FontNamer()); + + // Preferred size just makes it extend to the container + //fontSelectionBox.setSize(fontSelectionBox.getPreferredSize()); + // Minimum size is better, but cuts things off (on OS X), so we add 20 + //Dimension minSize = fontSelectionBox.getMinimumSize(); + //Dimension minSize = fontSelectionBox.getPreferredSize(); + //fontSelectionBox.setSize(minSize.width + 20, minSize.height); + fontSelectionBox.setEnabled(true); + } + */ + if (monoFontFamilies == null) { + monoFontFamilies = Toolkit.getMonoFontFamilies(); + fontSelectionBox.setModel(new DefaultComboBoxModel(monoFontFamilies)); + String family = get("editor.font.family"); +// System.out.println("family is " + family); +// System.out.println("font sel items = " + fontSelectionBox.getItemCount()); +// for (int i = 0; i < fontSelectionBox.getItemCount(); i++) { +// String item = (String) fontSelectionBox.getItemAt(i); +// if (fontSelectionBox.getItemAt(i) == family) { +// System.out.println("found at index " + i); +// } else if (item.equals(family)) { +// System.out.println("equals at index " + i); +// } else { +// System.out.println("nothing doing: " + item); +// } +// } + // Set a reasonable default, in case selecting the family fails + fontSelectionBox.setSelectedItem("Monospaced"); + fontSelectionBox.setSelectedItem(family); + fontSelectionBox.setEnabled(true); + } + } + + void updateDisplayList() { GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment(); displayCount = ge.getScreenDevices().length; @@ -1059,6 +1215,7 @@ public class Preferences { } + /* static public SyntaxStyle getStyle(String what) { String str = get("editor." + what + ".style"); //, dflt); //$NON-NLS-1$ //$NON-NLS-2$ @@ -1073,9 +1230,11 @@ public class Preferences { s = st.nextToken(); boolean bold = (s.indexOf("bold") != -1); //$NON-NLS-1$ - boolean italic = (s.indexOf("italic") != -1); //$NON-NLS-1$ +// boolean italic = (s.indexOf("italic") != -1); //$NON-NLS-1$ //System.out.println(what + " = " + str + " " + bold + " " + italic); - return new SyntaxStyle(color, italic, bold); +// return new SyntaxStyle(color, italic, bold); + return new SyntaxStyle(color, bold); } + */ } diff --git a/app/src/processing/app/Recent.java b/app/src/processing/app/Recent.java index 4af3a566c..8dc1069b1 100644 --- a/app/src/processing/app/Recent.java +++ b/app/src/processing/app/Recent.java @@ -118,7 +118,14 @@ public class Recent { menu.removeAll(); String sketchbookPath = Base.getSketchbookFolder().getAbsolutePath(); // String homePath = System.getProperty("user.home"); - for (final Record rec : records) { + for (Record rec : records) { + updateMenuRecord(menu, rec, sketchbookPath); + } + } + + + private void updateMenuRecord(JMenu menu, final Record rec, String sketchbookPath) { + try { String recPath = new File(rec.getPath()).getParent(); String purtyPath = null; @@ -197,6 +204,11 @@ public class Recent { }); //menu.add(item); menu.insert(item, 0); + + } catch (Exception e) { + // Strange things can happen... report them for the geeky and move on: + // https://github.com/processing/processing/issues/2463 + e.printStackTrace(); } } diff --git a/app/src/processing/app/Sketch.java b/app/src/processing/app/Sketch.java index 194438a52..41cb57482 100644 --- a/app/src/processing/app/Sketch.java +++ b/app/src/processing/app/Sketch.java @@ -179,7 +179,7 @@ public class Sketch { // Don't allow people to use files with invalid names, since on load, // it would be otherwise possible to sneak in nasty filenames. [0116] - if (Sketch.isSanitaryName(base)) { + if (isSanitaryName(base)) { code[codeCount++] = new SketchCode(new File(folder, filename), extension); } @@ -324,7 +324,7 @@ public class Sketch { renamingCode = true; String prompt = (currentIndex == 0) ? "New name for sketch:" : "New name for file:"; - String oldName = (current.isExtension("pde")) ? + String oldName = (current.isExtension(mode.getDefaultExtension())) ? current.getPrettyName() : current.getFileName(); editor.status.edit(prompt, oldName); } @@ -333,17 +333,19 @@ public class Sketch { /** * This is called upon return from entering a new file name. * (that is, from either newCode or renameCode after the prompt) - * This code is almost identical for both the newCode and renameCode - * cases, so they're kept merged except for right in the middle - * where they diverge. */ protected void nameCode(String newName) { + newName = newName.trim(); + if (newName.length() == 0) { + return; + } + // make sure the user didn't hide the sketch folder ensureExistence(); // Add the extension here, this simplifies some of the logic below. if (newName.indexOf('.') == -1) { - newName += "." + mode.getDefaultExtension(); + newName += "." + (renamingCode ? mode.getDefaultExtension() : mode.getModuleExtension()); } // if renaming to the same thing as before, just ignore. @@ -359,21 +361,18 @@ public class Sketch { } } - newName = newName.trim(); - if (newName.equals("")) return; - - int dot = newName.indexOf('.'); - if (dot == 0) { + if (newName.startsWith(".")) { Base.showWarning("Problem with rename", - "The name cannot start with a period.", null); + "The name cannot start with a period."); return; } + int dot = newName.lastIndexOf('.'); String newExtension = newName.substring(dot+1).toLowerCase(); if (!mode.validExtension(newExtension)) { Base.showWarning("Problem with rename", "\"." + newExtension + "\"" + - "is not a valid extension.", null); + "is not a valid extension."); return; } @@ -384,7 +383,7 @@ public class Sketch { Base.showWarning("Problem with rename", "The first tab cannot be a ." + newExtension + " file.\n" + "(It may be time for your to graduate to a\n" + - "\"real\" programming environment, hotshot.)", null); + "\"real\" programming environment, hotshot.)"); return; } } @@ -425,14 +424,14 @@ public class Sketch { if (newFolder.exists()) { Base.showWarning("Cannot Rename", "Sorry, a sketch (or folder) named " + - "\"" + newName + "\" already exists.", null); + "\"" + newName + "\" already exists."); return; } // renaming the containing sketch folder boolean success = folder.renameTo(newFolder); if (!success) { - Base.showWarning("Error", "Could not rename the sketch folder.", null); + Base.showWarning("Error", "Could not rename the sketch folder."); return; } // let this guy know where he's living (at least for a split second) @@ -454,7 +453,7 @@ public class Sketch { if (!current.renameTo(newFile, newExtension)) { Base.showWarning("Error", "Could not rename \"" + current.getFileName() + - "\" to \"" + newFile.getName() + "\"", null); + "\" to \"" + newFile.getName() + "\""); return; } @@ -486,7 +485,7 @@ public class Sketch { if (!current.renameTo(newFile, newExtension)) { Base.showWarning("Error", "Could not rename \"" + current.getFileName() + - "\" to \"" + newFile.getName() + "\"", null); + "\" to \"" + newFile.getName() + "\""); return; } } @@ -870,7 +869,7 @@ public class Sketch { } // save the main tab with its new name - File newFile = new File(newFolder, newName + ".pde"); + File newFile = new File(newFolder, newName + "." + mode.getDefaultExtension()); code[0].saveAs(newFile); updateInternal(newName, newFolder); @@ -1434,11 +1433,13 @@ public class Sketch { /** - * Return true if the name is valid for a Processing sketch. + * Return true if the name is valid for a Processing sketch. Extensions of the form .foo are + * ignored. */ - static public boolean isSanitaryName(String name) { - if (name.toLowerCase().endsWith(".pde")) { - name = name.substring(0, name.length() - 4); + public static boolean isSanitaryName(String name) { + final int dot = name.lastIndexOf('.'); + if (dot >= 0) { + name = name.substring(0, dot); } return sanitizeName(name).equals(name); } diff --git a/app/src/processing/app/SketchCode.java b/app/src/processing/app/SketchCode.java index 8deaedbe7..4b1a45814 100644 --- a/app/src/processing/app/SketchCode.java +++ b/app/src/processing/app/SketchCode.java @@ -94,7 +94,7 @@ public class SketchCode { protected void makePrettyName() { prettyName = file.getName(); - int dot = prettyName.indexOf('.'); + int dot = prettyName.lastIndexOf('.'); prettyName = prettyName.substring(0, dot); } diff --git a/app/src/processing/app/Toolkit.java b/app/src/processing/app/Toolkit.java index a22ca11b9..34aa23834 100644 --- a/app/src/processing/app/Toolkit.java +++ b/app/src/processing/app/Toolkit.java @@ -30,18 +30,22 @@ import java.awt.Graphics2D; import java.awt.GraphicsDevice; import java.awt.GraphicsEnvironment; import java.awt.Image; +import java.awt.Window; import java.awt.datatransfer.Clipboard; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.KeyEvent; import java.awt.font.FontRenderContext; import java.awt.font.TextLayout; +import java.awt.geom.AffineTransform; import java.io.BufferedInputStream; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.lang.reflect.Field; import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; import javax.swing.ImageIcon; import javax.swing.JCheckBoxMenuItem; @@ -148,17 +152,22 @@ public class Toolkit { static ArrayList iconImages; + + // Deprecated version of the function, but can't get rid of it without + // breaking tools and modes (they'd only require a recompile, but they would + // no longer be backwards compatible. + static public void setIcon(Frame frame) { + setIcon((Window) frame); + } + + /** * Give this Frame the Processing icon set. Ignored on OS X, because they * thought different and made this function set the minified image of the * window, not the window icon for the dock or cmd-tab. */ - static public void setIcon(Frame frame) { + static public void setIcon(Window window) { if (!Base.isMacOS()) { -// // too low-res, prepping for nicer icons in 2.0 timeframe -// Image image = awtToolkit.createImage(PApplet.ICON_IMAGE); -// frame.setIconImage(image); - if (iconImages == null) { iconImages = new ArrayList(); final int[] sizes = { 16, 32, 48, 64, 128, 256, 512 }; @@ -166,7 +175,7 @@ public class Toolkit { iconImages.add(Toolkit.getLibImage("icons/pde-" + sz + ".png")); } } - frame.setIconImages(iconImages); + window.setIconImages(iconImages); } } @@ -320,17 +329,81 @@ public class Toolkit { // } + // Gets the plain (not bold, not italic) version of each + static private List getMonoFontList() { + GraphicsEnvironment ge = + GraphicsEnvironment.getLocalGraphicsEnvironment(); + Font[] fonts = ge.getAllFonts(); + ArrayList outgoing = new ArrayList(); + // Using AffineTransform.getScaleInstance(100, 100) doesn't change sizes + FontRenderContext frc = + new FontRenderContext(new AffineTransform(), + Preferences.getBoolean("editor.antialias"), + true); // use fractional metrics + for (Font font : fonts) { + if (font.getStyle() == Font.PLAIN && + font.canDisplay('i') && font.canDisplay('M') && + font.canDisplay(' ') && font.canDisplay('.')) { + + // The old method just returns 1 or 0, and using deriveFont(size) + // is overkill. It also causes deprecation warnings +// @SuppressWarnings("deprecation") +// FontMetrics fm = awtToolkit.getFontMetrics(font); + //FontMetrics fm = awtToolkit.getFontMetrics(font.deriveFont(24)); +// System.out.println(fm.charWidth('i') + " " + fm.charWidth('M')); +// if (fm.charWidth('i') == fm.charWidth('M') && +// fm.charWidth('M') == fm.charWidth(' ') && +// fm.charWidth(' ') == fm.charWidth('.')) { + double w = font.getStringBounds(" ", frc).getWidth(); + if (w == font.getStringBounds("i", frc).getWidth() && + w == font.getStringBounds("M", frc).getWidth() && + w == font.getStringBounds(".", frc).getWidth()) { + +// //PApplet.printArray(font.getAvailableAttributes()); +// Map attr = font.getAttributes(); +// System.out.println(font.getFamily() + " > " + font.getName()); +// System.out.println(font.getAttributes()); +// System.out.println(" " + attr.get(TextAttribute.WEIGHT)); +// System.out.println(" " + attr.get(TextAttribute.POSTURE)); + + outgoing.add(font); +// System.out.println(" good " + w); + } + } + } + return outgoing; + } + + + static public String[] getMonoFontFamilies() { + HashSet families = new HashSet(); + for (Font font : getMonoFontList()) { + families.add(font.getFamily()); + } + return families.toArray(new String[0]); + } + + static Font monoFont; static Font monoBoldFont; static Font sansFont; static Font sansBoldFont; + static public String getMonoFontName() { + if (monoFont == null) { + getMonoFont(12, Font.PLAIN); // load a dummy version + } + return monoFont.getName(); + } + + static public Font getMonoFont(int size, int style) { if (monoFont == null) { try { monoFont = createFont("SourceCodePro-Regular.ttf", size); - monoBoldFont = createFont("SourceCodePro-Semibold.ttf", size); + //monoBoldFont = createFont("SourceCodePro-Semibold.ttf", size); + monoBoldFont = createFont("SourceCodePro-Bold.ttf", size); } catch (Exception e) { Base.log("Could not load mono font", e); monoFont = new Font("Monospaced", Font.PLAIN, size); @@ -341,20 +414,15 @@ public class Toolkit { if (size == monoBoldFont.getSize()) { return monoBoldFont; } else { -// System.out.println("deriving new font"); return monoBoldFont.deriveFont((float) size); } } else { if (size == monoFont.getSize()) { return monoFont; } else { -// System.out.println("deriving new font"); return monoFont.deriveFont((float) size); } } -// return style == Font.BOLD ? -// monoBoldFont.deriveFont((float) size) : -// monoFont.deriveFont((float) size); } @@ -365,26 +433,20 @@ public class Toolkit { sansBoldFont = createFont("SourceSansPro-Semibold.ttf", size); } catch (Exception e) { Base.log("Could not load sans font", e); - sansFont = new Font("Monospaced", Font.PLAIN, size); - sansBoldFont = new Font("Monospaced", Font.BOLD, size); + sansFont = new Font("SansSerif", Font.PLAIN, size); + sansBoldFont = new Font("SansSerif", Font.BOLD, size); } } -// System.out.println("deriving new font"); -// return style == Font.BOLD ? -// sansBoldFont.deriveFont((float) size) : -// sansFont.deriveFont((float) size); if (style == Font.BOLD) { if (size == sansBoldFont.getSize()) { return sansBoldFont; } else { -// System.out.println("deriving new font"); return sansBoldFont.deriveFont((float) size); } } else { if (size == sansFont.getSize()) { return sansFont; } else { -// System.out.println("deriving new font"); return sansFont.deriveFont((float) size); } } diff --git a/app/src/processing/app/UpdateCheck.java b/app/src/processing/app/UpdateCheck.java index 86693e6a3..fe98fab2b 100644 --- a/app/src/processing/app/UpdateCheck.java +++ b/app/src/processing/app/UpdateCheck.java @@ -112,8 +112,9 @@ public class UpdateCheck { boolean offerToUpdateContributions = true; if (latest > Base.getRevision()) { - System.out.println("You are running Processing revision " + - Base.getRevision() + ", the latest is " + latest + "."); + System.out.println("You are running Processing revision 0" + + Base.getRevision() + ", the latest build is 0" + + latest + "."); // Assume the person is busy downloading the latest version offerToUpdateContributions = !promptToVisitDownloadPage(); } diff --git a/app/src/processing/app/contrib/AvailableContribution.java b/app/src/processing/app/contrib/AvailableContribution.java index b4cb57cd4..a815ab6da 100644 --- a/app/src/processing/app/contrib/AvailableContribution.java +++ b/app/src/processing/app/contrib/AvailableContribution.java @@ -48,7 +48,10 @@ class AvailableContribution extends Contribution { url = params.get("url"); sentence = params.get("sentence"); paragraph = params.get("paragraph"); - version = PApplet.parseInt(params.get("version"), 0); + String versionStr = params.get("version"); + if (versionStr != null) { + version = PApplet.parseInt(versionStr, 0); + } prettyVersion = params.get("prettyVersion"); } diff --git a/app/src/processing/app/contrib/Contribution.java b/app/src/processing/app/contrib/Contribution.java index 0bc88268b..8367b98d3 100644 --- a/app/src/processing/app/contrib/Contribution.java +++ b/app/src/processing/app/contrib/Contribution.java @@ -152,12 +152,22 @@ abstract public class Contribution { return false; } + + /** + * @return a single element list with "Unknown" as the category. + */ + static List defaultCategory() { + List outgoing = new ArrayList(); + outgoing.add("Unknown"); + return outgoing; + } + /** * @return the list of categories that this contribution is part of * (e.g. "Typography / Geometry"). "Unknown" if the category null. */ - static public List parseCategories(String categoryStr) { + static List parseCategories(String categoryStr) { List outgoing = new ArrayList(); if (categoryStr != null) { @@ -169,7 +179,7 @@ abstract public class Contribution { } } if (outgoing.size() == 0) { - outgoing.add("Unknown"); + return defaultCategory(); } return outgoing; } diff --git a/app/src/processing/app/contrib/ContributionListing.java b/app/src/processing/app/contrib/ContributionListing.java index 149cb3fdc..05531f63d 100644 --- a/app/src/processing/app/contrib/ContributionListing.java +++ b/app/src/processing/app/contrib/ContributionListing.java @@ -87,9 +87,10 @@ public class ContributionListing { */ protected void updateInstalledList(List installedContributions) { for (Contribution contribution : installedContributions) { - Contribution preexistingContribution = getContribution(contribution); - if (preexistingContribution != null) { - replaceContribution(preexistingContribution, contribution); + Contribution existingContribution = getContribution(contribution); + if (existingContribution != null) { + replaceContribution(existingContribution, contribution); + //} else if (contribution != null) { // 130925 why would this be necessary? } else { addContribution(contribution); } diff --git a/app/src/processing/app/contrib/ContributionManager.java b/app/src/processing/app/contrib/ContributionManager.java index 5bb246259..262b09573 100644 --- a/app/src/processing/app/contrib/ContributionManager.java +++ b/app/src/processing/app/contrib/ContributionManager.java @@ -22,9 +22,7 @@ package processing.app.contrib; import java.io.*; -import java.net.SocketTimeoutException; -import java.net.URL; -import java.net.URLConnection; +import java.net.*; import processing.app.Base; import processing.app.Editor; @@ -55,9 +53,13 @@ public class ContributionManager { boolean success = false; try { // System.out.println("downloading file " + source); - URLConnection conn = source.openConnection(); - conn.setConnectTimeout(2000); - conn.setReadTimeout(5000); +// URLConnection conn = source.openConnection(); + HttpURLConnection conn = (HttpURLConnection) source.openConnection(); + HttpURLConnection.setFollowRedirects(true); + conn.setConnectTimeout(15 * 1000); + conn.setReadTimeout(60 * 1000); + conn.setRequestMethod("GET"); + conn.connect(); // TODO this is often -1, may need to set progress to indeterminate int fileSize = conn.getContentLength(); diff --git a/app/src/processing/app/contrib/ContributionManagerDialog.java b/app/src/processing/app/contrib/ContributionManagerDialog.java index 1d05ad609..6583acff9 100644 --- a/app/src/processing/app/contrib/ContributionManagerDialog.java +++ b/app/src/processing/app/contrib/ContributionManagerDialog.java @@ -27,6 +27,7 @@ import java.awt.Container; import java.awt.Dimension; import java.awt.Font; import java.awt.event.*; +import java.net.SocketTimeoutException; import java.util.*; import javax.swing.*; @@ -85,13 +86,8 @@ public class ContributionManagerDialog { dialog.pack(); dialog.setLocationRelativeTo(null); -// Dimension screen = Toolkit.getScreenSize(); -// dialog.setLocation((screen.width - dialog.getWidth()) / 2, -// (screen.height - dialog.getHeight()) / 2); - contributionListPanel.grabFocus(); } - dialog.setVisible(true); if (contribListing.hasDownloadedLatestList()) { @@ -99,19 +95,21 @@ public class ContributionManagerDialog { } else { contribListing.downloadAvailableList(new ProgressMonitor() { -// public void startTask(String name, int maxValue) { -// } -// + public void finished() { super.finished(); - + updateContributionListing(); updateCategoryChooser(); - if (isError()) { - status.setErrorMessage("An error occured when downloading " + - "the list of available contributions."); -// } else { -// status.updateUI(); + if (error) { + if (exception instanceof SocketTimeoutException) { + status.setErrorMessage("Connection timed out while " + + "downloading the contribution list."); + } else { + status.setErrorMessage("Could not download the list" + + "of available contributions."); + } + exception.printStackTrace(); } } }); diff --git a/app/src/processing/app/contrib/ContributionPanel.java b/app/src/processing/app/contrib/ContributionPanel.java index 09ba24faf..ca6f2da4e 100644 --- a/app/src/processing/app/contrib/ContributionPanel.java +++ b/app/src/processing/app/contrib/ContributionPanel.java @@ -122,6 +122,7 @@ class ContributionPanel extends JPanel { updateButton.setEnabled(false); installRemoveButton.setEnabled(false); installProgressBar.setVisible(true); + installProgressBar.setIndeterminate(true); ((LocalContribution) contrib).removeContribution(listPanel.contribManager.editor, new JProgressMonitor(installProgressBar) { @@ -484,7 +485,7 @@ class ContributionPanel extends JPanel { protected void resetInstallProgressBarState() { installProgressBar.setString("Starting"); - installProgressBar.setIndeterminate(true); + installProgressBar.setIndeterminate(false); installProgressBar.setValue(0); installProgressBar.setVisible(false); } diff --git a/app/src/processing/app/contrib/LocalContribution.java b/app/src/processing/app/contrib/LocalContribution.java index cc494b613..03a6e0f31 100644 --- a/app/src/processing/app/contrib/LocalContribution.java +++ b/app/src/processing/app/contrib/LocalContribution.java @@ -79,6 +79,7 @@ public abstract class LocalContribution extends Contribution { Base.log("No properties file at " + propertiesFile.getAbsolutePath()); // We'll need this to be set at a minimum. name = folder.getName(); + categories = defaultCategory(); } } diff --git a/app/src/processing/app/contrib/ModeContribution.java b/app/src/processing/app/contrib/ModeContribution.java index 4a927c895..052628e7b 100644 --- a/app/src/processing/app/contrib/ModeContribution.java +++ b/app/src/processing/app/contrib/ModeContribution.java @@ -24,7 +24,6 @@ package processing.app.contrib; import java.io.File; import java.lang.reflect.Constructor; import java.util.*; -import java.util.HashMap; import processing.app.Base; import processing.app.Mode; @@ -43,19 +42,19 @@ public class ModeContribution extends LocalContribution { String searchName) { try { return new ModeContribution(base, folder, searchName); + } catch (IgnorableException ig) { Base.log(ig.getMessage()); - } catch (Error err) { - // Handles UnsupportedClassVersionError and others - err.printStackTrace(); - } catch (Exception e) { + + } catch (Throwable err) { + // Throwable to catch Exceptions or UnsupportedClassVersionError et al if (searchName == null) { - e.printStackTrace(); + err.printStackTrace(); } else { // For the built-in modes, don't print the exception, just log it // for debugging. This should be impossible for most users to reach, // but it helps us load experimental mode when it's available. - Base.log("ModeContribution.load() failed for " + searchName, e); + Base.log("ModeContribution.load() failed for " + searchName, err); } } return null; @@ -95,17 +94,32 @@ public class ModeContribution extends LocalContribution { existing.put(contrib.getFolder(), contrib); } File[] potential = ContributionType.MODE.listCandidates(modesFolder); - for (File folder : potential) { - if (!existing.containsKey(folder)) { - try { - contribModes.add(new ModeContribution(base, folder, null)); - } catch (IgnorableException ig) { - Base.log(ig.getMessage()); - } catch (Exception e) { - e.printStackTrace(); + // If modesFolder does not exist or is inaccessible (folks might like to + // mess with folders then report it as a bug) 'potential' will be null. + if (potential != null) { + for (File folder : potential) { + if (!existing.containsKey(folder)) { + try { + contribModes.add(new ModeContribution(base, folder, null)); + } catch (IgnorableException ig) { + Base.log(ig.getMessage()); + } catch (Throwable e) { + e.printStackTrace(); + } } } } + + // This allows you to build and test your Mode code from Eclipse. + // -Dusemode=com.foo.FrobMode:/path/to/FrobMode/resources + final String usemode = System.getProperty("usemode"); + if (usemode != null) { + final String[] modeinfo = usemode.split(":", 2); + final String modeClass = modeinfo[0]; + final String modeResourcePath = modeinfo[1]; + System.err.println("Attempting to load " + modeClass + " with resources at " + modeResourcePath); + contribModes.add(ModeContribution.load(base, new File(modeResourcePath), modeClass)); + } } diff --git a/app/src/processing/app/contrib/ToolContribution.java b/app/src/processing/app/contrib/ToolContribution.java index 978e95f49..bccbda1bb 100644 --- a/app/src/processing/app/contrib/ToolContribution.java +++ b/app/src/processing/app/contrib/ToolContribution.java @@ -74,27 +74,44 @@ public class ToolContribution extends LocalContribution implements Tool { static public ArrayList loadAll(File toolsFolder) { File[] list = ContributionType.TOOL.listCandidates(toolsFolder); ArrayList outgoing = new ArrayList(); - for (File folder : list) { - try { - ToolContribution tc = load(folder); - if (tc != null) { - outgoing.add(tc); + // If toolsFolder does not exist or is inaccessible (stranger things have + // happened, and are reported as bugs) list will come back null. + if (list != null) { + for (File folder : list) { + try { + ToolContribution tc = load(folder); + if (tc != null) { + outgoing.add(tc); + } + } catch (Exception e) { + e.printStackTrace(); } - } catch (Exception e) { - e.printStackTrace(); } } return outgoing; } +// Editor editor; // used to send error messages + public void init(Editor editor) { +// try { +// this.editor = editor; tool.init(editor); +// } catch (NoSuchMethodError nsme) { +// editor.statusError(tool.getMenuTitle() + " is not compatible with this version of Processing"); +// nsme.printStackTrace(); +// } } public void run() { +// try { tool.run(); +// } catch (NoSuchMethodError nsme) { +// editor.statusError(tool.getMenuTitle() + " is not compatible with this version of Processing"); +// nsme.printStackTrace(); +// } } diff --git a/app/src/processing/app/exec/ProcessHelper.java b/app/src/processing/app/exec/ProcessHelper.java index 66e9d331f..0bbd2aa97 100644 --- a/app/src/processing/app/exec/ProcessHelper.java +++ b/app/src/processing/app/exec/ProcessHelper.java @@ -128,37 +128,19 @@ public class ProcessHelper { } -// static public ProcessResult execute(String exe, String[] args, File dir) -// throws InterruptedException, IOException { -// final StringWriter outWriter = new StringWriter(); -// final StringWriter errWriter = new StringWriter(); -// final long startTime = System.currentTimeMillis(); -// -// final String prettyCommand = exe + " " + PApplet.join(args, " "); -// System.out.println("pretty cmd is " + prettyCommand); -// final Process process = dir == null ? -// Runtime.getRuntime().exec(exe, args) : -// Runtime.getRuntime().exec(exe, args, dir); -// ProcessRegistry.watch(process); -// try { -// String title = prettyCommand; -// new StreamPump(process.getInputStream(), "out: " + title).addTarget(outWriter).start(); -// new StreamPump(process.getErrorStream(), "err: " + title).addTarget(errWriter).start(); -// try { -// final int result = process.waitFor(); -// final long time = System.currentTimeMillis() - startTime; -// // System.err.println("ProcessHelper: <<<<< " -// // + Thread.currentThread().getId() + " " + cmd[0] + " (" + time -// // + "ms)"); -// return new ProcessResult(prettyCommand, result, outWriter.toString(), -// errWriter.toString(), time); -// } catch (final InterruptedException e) { -// System.err.println("Interrupted: " + prettyCommand); -// throw e; -// } -// } finally { -// process.destroy(); -// ProcessRegistry.unwatch(process); -// } -// } + static public boolean ffs(final String... cmd) { + try { + ProcessHelper helper = new ProcessHelper(cmd); + ProcessResult result = helper.execute(); + if (result.succeeded()) { + return true; + } + System.out.println(result.getStdout()); + System.err.println(result.getStderr()); + + } catch (Exception e) { + e.printStackTrace(); + } + return false; + } } \ No newline at end of file diff --git a/app/src/processing/app/languages/PDE.properties b/app/src/processing/app/languages/PDE.properties index d1982b8aa..9903f65a8 100644 --- a/app/src/processing/app/languages/PDE.properties +++ b/app/src/processing/app/languages/PDE.properties @@ -1,7 +1,7 @@ # --------------------------------------- -# Language: English (en) (Default) +# Language: English (en) (default) # --------------------------------------- @@ -118,7 +118,9 @@ preferences.button.width = 80 preferences.requires_restart = requires restart of Processing preferences.sketchbook_location = Sketchook location preferences.language = Language +preferences.editor_and_console_font = Editor and Console font preferences.editor_font_size = Editor font size +preferences.console_font_size = Console font size preferences.use_smooth_text = Use smooth text in editor window preferences.enable_complex_text_input = Enable complex text input preferences.enable_complex_text_input_example = i.e. Japanese diff --git a/app/src/processing/app/languages/PDE_de.properties b/app/src/processing/app/languages/PDE_de.properties index b7c357b94..f0630fef4 100644 --- a/app/src/processing/app/languages/PDE_de.properties +++ b/app/src/processing/app/languages/PDE_de.properties @@ -1,7 +1,7 @@ # --------------------------------------- -# Language: Deutsch (de) +# Language: German (Deutsch) (de) # --------------------------------------- @@ -113,7 +113,9 @@ preferences.button.width = 110 preferences.requires_restart = nach Neustart von Processing aktiv preferences.sketchbook_location = Sketchbook Pfad preferences.language = Sprache +preferences.editor_and_console_font = Editor und Console Schriftart preferences.editor_font_size = Editor Schriftgre +preferences.console_font_size = Console Schriftgre preferences.use_smooth_text = Editor Textglttung preferences.enable_complex_text_input = Komplexe Sprachen erlauben preferences.enable_complex_text_input_example = z.B. Japanisch diff --git a/app/src/processing/app/platform/ThinkDifferent.java b/app/src/processing/app/platform/ThinkDifferent.java index 79caaa338..aba1ed4ba 100644 --- a/app/src/processing/app/platform/ThinkDifferent.java +++ b/app/src/processing/app/platform/ThinkDifferent.java @@ -22,13 +22,10 @@ package processing.app.platform; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import javax.swing.JMenu; -import javax.swing.JMenuBar; -import javax.swing.JMenuItem; +import java.awt.Dimension; +import java.awt.event.*; + +import javax.swing.*; import processing.app.About; import processing.app.Base; @@ -77,38 +74,39 @@ public class ThinkDifferent implements ApplicationListener { application.setEnabledPreferencesMenu(true); // Set the menubar to be used when nothing else is open. http://j.mp/dkZmka - // http://developer.apple.com/mac/library/documentation/Java/Reference/ - // JavaSE6_AppleExtensionsRef/api/com/apple/eawt/Application.html - // Only available since Java for Mac OS X 10.6 Update 1, and - // Java for Mac OS X 10.5 Update 6, so need to load this dynamically + // Only available since Java for Mac OS X 10.6 Update 1, but removed + // dynamic loading code because that should be installed in 10.6.8, and + // we may be dropped 10.6 really soon anyway + + JMenuBar defaultMenuBar = new JMenuBar(); + JMenu fileMenu = buildFileMenu(base); + defaultMenuBar.add(fileMenu); + // This is kind of a gross way to do this, but the alternatives? Hrm. + Base.defaultFileMenu = fileMenu; + if (PApplet.javaVersion <= 1.6f) { // doesn't work on Oracle's Java -// if (System.getProperty("java.vendor").contains("Apple") || -// Base.isUsableOracleJava()) { try { - // com.apple.eawt.Application.setDefaultMenuBar(JMenuBar) - Class appClass = Application.class; - Method method = - appClass.getMethod("setDefaultMenuBar", new Class[] { JMenuBar.class }); - if (method != null) { - JMenuBar defaultMenuBar = new JMenuBar(); - JMenu fileMenu = buildFileMenu(base); - defaultMenuBar.add(fileMenu); - method.invoke(application, new Object[] { defaultMenuBar }); - // This is kind of a gross way to do this, but the alternatives? Hrm. - Base.defaultFileMenu = fileMenu; - } - } catch (InvocationTargetException ite) { - ite.getTargetException().printStackTrace(); + application.setDefaultMenuBar(defaultMenuBar); + } catch (Exception e) { e.printStackTrace(); // oh well nevermind } } else { - // http://java.net/jira/browse/MACOSX_PORT-775?page=com.atlassian.jira.plugin.system.issuetabpanels%3Aall-tabpanel - System.err.println("Skipping default menu bar due to Oracle Java 7 bug:"); - System.err.println("http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=8007267"); + // The douchebags at Oracle didn't feel that a working f*king menubar + // on OS X was important enough to make it into the 7u40 release. + //http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=8007267 + // It languished in the JDK 8 source and has been backported for 7u60: + //http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=8022667 + + JFrame offscreen = new JFrame(); + offscreen.setUndecorated(true); + offscreen.setJMenuBar(defaultMenuBar); + Dimension screen = Toolkit.getScreenSize(); + offscreen.setLocation(screen.width, screen.height); + offscreen.setVisible(true); } } - + public ThinkDifferent(Base base) { this.base = base; diff --git a/app/src/processing/app/syntax/Brackets.java b/app/src/processing/app/syntax/Brackets.java index c35e9c359..1fd46ec27 100644 --- a/app/src/processing/app/syntax/Brackets.java +++ b/app/src/processing/app/syntax/Brackets.java @@ -90,7 +90,7 @@ public class Brackets { readComment(text); } else if (d == '*') { readMLComment(text); - } + } else pos--; // Go back because there isn't a comment. } else if (c == '"' || c == '\'') { readString(text, c); } else if (c == '{' || c == '[' || c == '(' || c == '}' || c == ']' diff --git a/app/src/processing/app/syntax/HtmlSelection.java b/app/src/processing/app/syntax/HtmlSelection.java index d8e906604..d7a016aa5 100644 --- a/app/src/processing/app/syntax/HtmlSelection.java +++ b/app/src/processing/app/syntax/HtmlSelection.java @@ -9,12 +9,14 @@ import java.io.StringReader; import java.util.ArrayList; import java.util.List; + public class HtmlSelection implements Transferable { - private static List flavors = new ArrayList(); + private static List flavors; static { try { + flavors = new ArrayList(); flavors.add(DataFlavor.stringFlavor); flavors.add(new DataFlavor("text/html;class=java.lang.String")); flavors.add(new DataFlavor("text/html;class=java.io.Reader")); @@ -26,18 +28,22 @@ public class HtmlSelection implements Transferable { private String html; + public HtmlSelection(String html) { this.html = html; } + public DataFlavor[] getTransferDataFlavors() { return flavors.toArray(new DataFlavor[flavors.size()]); } + public boolean isDataFlavorSupported(DataFlavor flavor) { return flavors.contains(flavor); } + public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException { if (flavor.equals(DataFlavor.stringFlavor)) { diff --git a/app/src/processing/app/syntax/JEditTextArea.java b/app/src/processing/app/syntax/JEditTextArea.java index 89b77502f..de7eb16ce 100644 --- a/app/src/processing/app/syntax/JEditTextArea.java +++ b/app/src/processing/app/syntax/JEditTextArea.java @@ -24,7 +24,9 @@ import javax.swing.event.*; import javax.swing.text.*; import javax.swing.undo.*; import javax.swing.*; + import java.awt.im.InputMethodRequests; +import java.awt.print.Printable; import processing.app.syntax.im.InputMethodSupport; import processing.core.PApplet; @@ -129,7 +131,7 @@ public class JEditTextArea extends JComponent // Load the defaults setInputHandler(defaults.inputHandler); setDocument(defaults.document); - editable = defaults.editable; +// editable = defaults.editable; caretVisible = defaults.caretVisible; caretBlinks = defaults.caretBlinks; electricScroll = defaults.electricScroll; @@ -190,26 +192,65 @@ public class JEditTextArea extends JComponent /** * Get current position of the vertical scroll bar. [fry] + * @deprecated Use {@link #getVerticalScrollPosition()}. */ public int getScrollPosition() { - return vertical.getValue(); + return getVerticalScrollPosition(); } /** * Set position of the vertical scroll bar. [fry] + * @deprecated Use {@link #setVerticalScrollPosition(int)}. */ public void setScrollPosition(int what) { - vertical.setValue(what); + setVerticalScrollPosition(what); + } + + + /** + * Get current position of the vertical scroll bar. + */ + public int getVerticalScrollPosition() { + return vertical.getValue(); } + /** + * Set position of the vertical scroll bar. + */ + public void setVerticalScrollPosition(int what) { + vertical.setValue(what); + } + + + /** + * Get current position of the horizontal scroll bar. + */ + public int getHorizontalScrollPosition() { + return horizontal.getValue(); + } + + + /** + * Set position of the horizontal scroll bar. + */ + public void setHorizontalScrollPosition(int what) { + horizontal.setValue(what); + } + + /** * Returns the object responsible for painting this text area. */ public final TextAreaPainter getPainter() { return painter; } + + + public final Printable getPrintable() { + return painter.getPrintable(); + } /** @@ -587,7 +628,7 @@ public class JEditTextArea extends JComponent tokens = painter.currentLineTokens = tokenMarker.markTokens(lineSegment, line); } - Font defaultFont = painter.getFont(); +// Font defaultFont = painter.getFont(); SyntaxStyle[] styles = painter.getStyles(); for (;;) { @@ -599,7 +640,8 @@ public class JEditTextArea extends JComponent if (id == Token.NULL) { fm = painter.getFontMetrics(); } else { - fm = styles[id].getFontMetrics(defaultFont, this); + //fm = styles[id].getFontMetrics(defaultFont, this); + fm = painter.getFontMetrics(styles[id]); } int length = tokens.length; @@ -674,19 +716,21 @@ public class JEditTextArea extends JComponent } int offset = 0; - Font defaultFont = painter.getFont(); +// Font defaultFont = painter.getFont(); SyntaxStyle[] styles = painter.getStyles(); // System.out.println("painter is " + painter + ", doc is " + document); - for(;;) { + for (;;) { byte id = tokens.id; if(id == Token.END) return offset; - if(id == Token.NULL) + if (id == Token.NULL) { fm = painter.getFontMetrics(); - else - fm = styles[id].getFontMetrics(defaultFont, this); + } else { + //fm = styles[id].getFontMetrics(defaultFont, this); + fm = painter.getFontMetrics(styles[id]); + } int length = tokens.length; @@ -766,8 +810,8 @@ public class JEditTextArea extends JComponent * Set document with a twist, includes the old caret * and scroll positions, added for p5. [fry] */ - public void setDocument(SyntaxDocument document, - int start, int stop, int scroll) { + public void setDocument(SyntaxDocument document, + int start, int stop, int scroll) { if (this.document == document) return; if (this.document != null) @@ -778,7 +822,7 @@ public class JEditTextArea extends JComponent select(start, stop); updateScrollBars(); - setScrollPosition(scroll); + setVerticalScrollPosition(scroll); painter.repaint(); } @@ -787,65 +831,60 @@ public class JEditTextArea extends JComponent * Returns the document's token marker. Equivalent to calling * getDocument().getTokenMarker(). */ - public final TokenMarker getTokenMarker() - { + public final TokenMarker getTokenMarker() { return document.getTokenMarker(); } + /** * Sets the document's token marker. Equivalent to caling * getDocument().setTokenMarker(). * @param tokenMarker The token marker */ - public final void setTokenMarker(TokenMarker tokenMarker) - { + public final void setTokenMarker(TokenMarker tokenMarker) { document.setTokenMarker(tokenMarker); } + /** * Returns the length of the document. Equivalent to calling * getDocument().getLength(). */ - public final int getDocumentLength() - { + public final int getDocumentLength() { return document.getLength(); } + /** * Returns the number of lines in the document. */ - public final int getLineCount() - { + public final int getLineCount() { return document.getDefaultRootElement().getElementCount(); } + /** * Returns the line containing the specified offset. * @param offset The offset */ - public final int getLineOfOffset(int offset) - { + public final int getLineOfOffset(int offset) { return document.getDefaultRootElement().getElementIndex(offset); } + /** * Returns the start offset of the specified line. * @param line The line * @return The start offset of the specified line, or -1 if the line is * invalid */ - public int getLineStartOffset(int line) - { - Element lineElement = document.getDefaultRootElement() - .getElement(line); - if(lineElement == null) - return -1; - else - return lineElement.getStartOffset(); + public int getLineStartOffset(int line) { + Element lineElement = document.getDefaultRootElement().getElement(line); + return (lineElement == null) ? -1 : lineElement.getStartOffset(); } - public int getLineStartNonWhiteSpaceOffset(int line) - { + + public int getLineStartNonWhiteSpaceOffset(int line) { int offset = getLineStartOffset(line); int length = getLineLength(line); String str = getText(offset, length); @@ -858,37 +897,33 @@ public class JEditTextArea extends JComponent return offset + length; } + /** * Returns the end offset of the specified line. * @param line The line * @return The end offset of the specified line, or -1 if the line is * invalid. */ - public int getLineStopOffset(int line) - { - Element lineElement = document.getDefaultRootElement() - .getElement(line); - if(lineElement == null) - return -1; - else - return lineElement.getEndOffset(); + public int getLineStopOffset(int line) { + Element lineElement = document.getDefaultRootElement().getElement(line); + return (lineElement == null) ? -1 : lineElement.getEndOffset(); } - public int getLineStopNonWhiteSpaceOffset(int line) - { + + public int getLineStopNonWhiteSpaceOffset(int line) { int offset = getLineStopOffset(line); int length = getLineLength(line); String str = getText(offset - length - 1, length); - for(int i = 0; i < length; i++) { + for (int i = 0; i < length; i++) { if(!Character.isWhitespace(str.charAt(length - i - 1))) { return offset - i; } } - return offset - length; } + /** * Returns the start offset of the line after this line, or the end of * this line if there is no next line. @@ -896,42 +931,32 @@ public class JEditTextArea extends JComponent * @return The end offset of the specified line, or -1 if the line is * invalid. */ - public int getLineSelectionStopOffset(int line) - { - Element lineElement = document.getDefaultRootElement() - .getElement(line); - if(lineElement == null) - return -1; - else - return Math.min(lineElement.getEndOffset(),getDocumentLength()); + public int getLineSelectionStopOffset(int line) { + Element lineElement = document.getDefaultRootElement().getElement(line); + return (lineElement == null) ? -1 : + Math.min(lineElement.getEndOffset(), getDocumentLength()); } + /** * Returns the length of the specified line. * @param line The line */ - public int getLineLength(int line) - { - Element lineElement = document.getDefaultRootElement() - .getElement(line); - if(lineElement == null) - return -1; - else - return lineElement.getEndOffset() - - lineElement.getStartOffset() - 1; + public int getLineLength(int line) { + Element lineElement = document.getDefaultRootElement().getElement(line); + return (lineElement == null) ? -1 : + lineElement.getEndOffset() - lineElement.getStartOffset() - 1; } + /** * Returns the entire text of this text area. */ - public String getText() - { - try - { + public String getText() { + try { return document.getText(0,document.getLength()); - } - catch(BadLocationException bl) - { + + } catch(BadLocationException bl) { bl.printStackTrace(); return null; } @@ -941,8 +966,7 @@ public class JEditTextArea extends JComponent /** * Sets the entire text of this text area. */ - public void setText(String text) - { + public void setText(String text) { try { document.beginCompoundEdit(); document.remove(0,document.getLength()); @@ -963,18 +987,16 @@ public class JEditTextArea extends JComponent * @param len The length of the substring * @return The substring, or null if the offsets are invalid */ - public final String getText(int start, int len) - { - try - { + public final String getText(int start, int len) { + try { return document.getText(start,len); - } - catch(BadLocationException bl) - { + + } catch(BadLocationException bl) { bl.printStackTrace(); return null; } } + /** * Copies the specified substring of the document into a segment. @@ -983,49 +1005,47 @@ public class JEditTextArea extends JComponent * @param len The length of the substring * @param segment The segment */ - public final void getText(int start, int len, Segment segment) - { - try - { + public final void getText(int start, int len, Segment segment) { + try { document.getText(start,len,segment); - } - catch(BadLocationException bl) - { + + } catch(BadLocationException bl) { bl.printStackTrace(); segment.offset = segment.count = 0; } } + /** * Returns the text on the specified line. * @param lineIndex The line * @return The text, or null if the line is invalid */ - public final String getLineText(int lineIndex) - { + public final String getLineText(int lineIndex) { int start = getLineStartOffset(lineIndex); return getText(start,getLineStopOffset(lineIndex) - start - 1); } + /** * Copies the text on the specified line into a segment. If the line * is invalid, the segment will contain a null string. * @param lineIndex The line */ - public final void getLineText(int lineIndex, Segment segment) - { + public final void getLineText(int lineIndex, Segment segment) { int start = getLineStartOffset(lineIndex); getText(start,getLineStopOffset(lineIndex) - start - 1,segment); } + /** * Returns the selection start offset. */ - public final int getSelectionStart() - { + public final int getSelectionStart() { return selectionStart; } + /** * Returns the offset where the selection starts on the specified * line. @@ -1395,30 +1415,23 @@ public class JEditTextArea extends JComponent * Replaces the selection with the specified text. * @param selectedText The replacement text for the selection */ - public void setSelectedText(String selectedText) - { - if(!editable) - { - throw new InternalError("Text component" - + " read only"); + public void setSelectedText(String selectedText) { + if (!editable) { + throw new InternalError("Text component read only"); } - document.beginCompoundEdit(); - try - { - if(rectSelect) - { + try { + if (rectSelect) { Element map = document.getDefaultRootElement(); - int start = selectionStart - map.getElement(selectionStartLine) - .getStartOffset(); - int end = selectionEnd - map.getElement(selectionEndLine) - .getStartOffset(); + int start = selectionStart - + map.getElement(selectionStartLine).getStartOffset(); + int end = selectionEnd - + map.getElement(selectionEndLine).getStartOffset(); // Certain rectangles satisfy this condition... - if(end < start) - { + if (end < start) { int tmp = end; end = start; start = tmp; @@ -1427,123 +1440,102 @@ public class JEditTextArea extends JComponent int lastNewline = 0; int currNewline = 0; - for(int i = selectionStartLine; i <= selectionEndLine; i++) - { + for (int i = selectionStartLine; i <= selectionEndLine; i++) { Element lineElement = map.getElement(i); int lineStart = lineElement.getStartOffset(); int lineEnd = lineElement.getEndOffset() - 1; int rectStart = Math.min(lineEnd,lineStart + start); - document.remove(rectStart,Math.min(lineEnd - rectStart, - end - start)); + document.remove(rectStart,Math.min(lineEnd - rectStart, end - start)); - if(selectedText == null) - continue; - - currNewline = selectedText.indexOf('\n',lastNewline); - if(currNewline == -1) - currNewline = selectedText.length(); - - document.insertString(rectStart,selectedText - .substring(lastNewline,currNewline),null); - - lastNewline = Math.min(selectedText.length(), - currNewline + 1); + if (selectedText != null) { + currNewline = selectedText.indexOf('\n', lastNewline); + if (currNewline == -1) { + currNewline = selectedText.length(); + } + document.insertString(rectStart, selectedText.substring(lastNewline, currNewline), null); + lastNewline = Math.min(selectedText.length(), currNewline + 1); + } } - if(selectedText != null && - currNewline != selectedText.length()) - { - int offset = map.getElement(selectionEndLine) - .getEndOffset() - 1; - document.insertString(offset,"\n",null); - document.insertString(offset + 1,selectedText - .substring(currNewline + 1),null); + if (selectedText != null && + currNewline != selectedText.length()) { + int offset = map.getElement(selectionEndLine).getEndOffset() - 1; + document.insertString(offset, "\n", null); + document.insertString(offset + 1,selectedText.substring(currNewline + 1), null); + } + } else { + document.remove(selectionStart, selectionEnd - selectionStart); + if (selectedText != null) { + document.insertString(selectionStart, selectedText,null); } } - else - { - document.remove(selectionStart, - selectionEnd - selectionStart); - if(selectedText != null) - { - document.insertString(selectionStart, - selectedText,null); - } - } - } - catch(BadLocationException bl) - { + } catch(BadLocationException bl) { bl.printStackTrace(); - throw new InternalError("Cannot replace" - + " selection"); - } - // No matter what happends... stops us from leaving document - // in a bad state - finally - { + throw new InternalError("Cannot replace selection"); + + } finally { + // No matter what happens... stops us from leaving document in a bad state document.endCompoundEdit(); } - setCaretPosition(selectionEnd); } + /** * Returns true if this text area is editable, false otherwise. */ - public final boolean isEditable() - { + public final boolean isEditable() { return editable; } + /** * Sets if this component is editable. * @param editable True if this text area should be editable, * false otherwise */ - public final void setEditable(boolean editable) - { + public final void setEditable(boolean editable) { this.editable = editable; } + /** * Returns the right click popup menu. */ - public final JPopupMenu getRightClickPopup() - { + public final JPopupMenu getRightClickPopup() { return popup; } + /** * Sets the right click popup menu. * @param popup The popup */ - //public final void setRightClickPopup(EditPopupMenu popup) - public final void setRightClickPopup(JPopupMenu popup) - { + public final void setRightClickPopup(JPopupMenu popup) { this.popup = popup; } /** - * Returns the `magic' caret position. This can be used to preserve + * Returns the 'magic' caret position. This can be used to preserve * the column position when moving up and down lines. */ - public final int getMagicCaretPosition() - { + public final int getMagicCaretPosition() { return magicCaret; } + /** - * Sets the `magic' caret position. This can be used to preserve + * Sets the 'magic' caret position. This can be used to preserve * the column position when moving up and down lines. * @param magicCaret The magic caret position */ - public final void setMagicCaretPosition(int magicCaret) - { + public final void setMagicCaretPosition(int magicCaret) { this.magicCaret = magicCaret; } + /** * Similar to setSelectedText(), but overstrikes the * appropriate number of characters if overwrite mode is enabled. @@ -1983,7 +1975,7 @@ public class JEditTextArea extends JComponent protected boolean caretVisible; protected boolean blink; - protected boolean editable; + protected boolean editable = true; protected int firstLine; protected int visibleLines; @@ -2369,28 +2361,21 @@ public class JEditTextArea extends JComponent } - class FocusHandler implements FocusListener - { - public void focusGained(FocusEvent evt) - { -// System.out.println("JEditTextArea: focusGained"); + class FocusHandler implements FocusListener { + + public void focusGained(FocusEvent evt) { setCaretVisible(true); -// focusedComponent = JEditTextArea.this; } - public void focusLost(FocusEvent evt) - { -// System.out.println("JEditTextArea: focusLost"); + public void focusLost(FocusEvent evt) { setCaretVisible(false); -// focusedComponent = null; } } - class MouseHandler extends MouseAdapter - { - public void mousePressed(MouseEvent evt) - { + class MouseHandler extends MouseAdapter { + + public void mousePressed(MouseEvent event) { // try { // requestFocus(); // // Focus events not fired sometimes? @@ -2403,48 +2388,54 @@ public class JEditTextArea extends JComponent // the problem, though it's not clear why the wrong Document data was // being using regardless of the focusedComponent. // if (focusedComponent != JEditTextArea.this) return; - if (!hasFocus()) { + if (!hasFocus()) { // System.out.println("requesting focus in window"); - requestFocusInWindow(); - return; - } - - // isPopupTrigger wasn't working for danh on windows - boolean trigger = (evt.getModifiers() & InputEvent.BUTTON3_MASK) != 0; - // but it's required for macosx, since control-click does - // the same thing as a right-mouse click - if (!trigger && evt.isPopupTrigger()) trigger = true; - - if (trigger && (popup != null)) { - popup.show(painter,evt.getX(),evt.getY()); + requestFocusInWindow(); return; } - int line = yToLine(evt.getY()); - int offset = xToOffset(line,evt.getX()); + // isPopupTrigger() is handled differently across platforms, + // so it may fire during release, or during the press. + // http://docs.oracle.com/javase/7/docs/api/java/awt/event/MouseEvent.html#isPopupTrigger() + // However, we have to exit out of this method if it's a right-click + // anyway, because otherwise it'll de-select the current word. + // As a result, better to just check for BUTTON3 now, indicating that + // isPopupTrigger() is going to fire on the release anyway. + boolean windowsRightClick = + Base.isWindows() && (event.getButton() == MouseEvent.BUTTON3); + if ((event.isPopupTrigger() || windowsRightClick) && (popup != null)) { +// // Windows fires the popup trigger on release (see mouseReleased() below)( +// if (!Base.isWindows()) { +// if (event.isPopupTrigger() && (popup != null)) { + popup.show(painter, event.getX(), event.getY()); + return; +// } + } + + int line = yToLine(event.getY()); + int offset = xToOffset(line, event.getX()); int dot = getLineStartOffset(line) + offset; selectLine = false; selectWord = false; - switch(evt.getClickCount()) { + switch (event.getClickCount()) { case 1: - doSingleClick(evt,line,offset,dot); + doSingleClick(event,line,offset,dot); break; case 2: - // It uses the bracket matching stuff, so - // it can throw a BLE + // It uses the bracket matching stuff, so it can throw a BLE try { - doDoubleClick(evt,line,offset,dot); - } catch(BadLocationException bl) { + doDoubleClick(event, line, offset, dot); + } catch (BadLocationException bl) { bl.printStackTrace(); } break; case 3: - doTripleClick(evt,line,offset,dot); + doTripleClick(event,line,offset,dot); break; } // } catch (ArrayIndexOutOfBoundsException aioobe) { @@ -2455,8 +2446,19 @@ public class JEditTextArea extends JComponent } - private void doSingleClick(MouseEvent evt, int line, - int offset, int dot) { + /* + // Because isPopupTrigger() is handled differently across platforms, + // it may fire during release, or during the press. + // http://docs.oracle.com/javase/7/docs/api/java/awt/event/MouseEvent.html#isPopupTrigger() + public void mouseReleased(MouseEvent event) { + if (event.isPopupTrigger() && (popup != null)) { + popup.show(painter, event.getX(), event.getY()); + } + } + */ + + + private void doSingleClick(MouseEvent evt, int line, int offset, int dot) { if ((evt.getModifiers() & InputEvent.SHIFT_MASK) != 0) { rectSelect = (evt.getModifiers() & InputEvent.CTRL_MASK) != 0; select(getMarkPosition(),dot); @@ -2466,35 +2468,32 @@ public class JEditTextArea extends JComponent } - private void doDoubleClick(MouseEvent evt, int line, - int offset, int dot) throws BadLocationException - { + private void doDoubleClick(MouseEvent evt, int line, int offset, + int dot) throws BadLocationException { // Ignore empty lines - if (getLineLength(line) == 0) - return; - - try { - int bracket = bracketHelper.findMatchingBracket(document.getText(0, document.getLength()), - Math.max(0,dot - 1)); - if (bracket != -1) { - int mark = getMarkPosition(); - // Hack - if (bracket > mark) { - bracket++; - mark--; + if (getLineLength(line) != 0) { + try { + String text = document.getText(0, document.getLength()); + int bracket = bracketHelper.findMatchingBracket(text, Math.max(0, dot - 1)); + if (bracket != -1) { + int mark = getMarkPosition(); + // Hack + if (bracket > mark) { + bracket++; + mark--; + } + select(mark,bracket); + return; } - select(mark,bracket); - return; + } catch(BadLocationException bl) { + bl.printStackTrace(); } - } catch(BadLocationException bl) { - bl.printStackTrace(); - } - setNewSelectionWord( line, offset ); - select(newSelectionStart,newSelectionEnd); - selectWord = true; - selectionAncorStart = selectionStart; - selectionAncorEnd = selectionEnd; + setNewSelectionWord( line, offset ); + select(newSelectionStart,newSelectionEnd); + selectWord = true; + selectionAncorStart = selectionStart; + selectionAncorEnd = selectionEnd; /* String lineText = getLineText(line); @@ -2505,11 +2504,11 @@ public class JEditTextArea extends JComponent int lineStart = getLineStartOffset(line); select(lineStart + wordStart,lineStart + wordEnd); */ - } + } + } - private void doTripleClick(MouseEvent evt, int line, - int offset, int dot) - { + + private void doTripleClick(MouseEvent evt, int line, int offset, int dot) { selectLine = true; select(getLineStartOffset(line),getLineSelectionStopOffset(line)); selectionAncorStart = selectionStart; @@ -2517,61 +2516,43 @@ public class JEditTextArea extends JComponent } } - class CaretUndo extends AbstractUndoableEdit - { + + class CaretUndo extends AbstractUndoableEdit { private int start; private int end; - CaretUndo(int start, int end) - { + CaretUndo(int start, int end) { this.start = start; this.end = end; } - public boolean isSignificant() - { + public boolean isSignificant() { return false; } - public String getPresentationName() - { + public String getPresentationName() { return "caret move"; } - public void undo() throws CannotUndoException - { + public void undo() throws CannotUndoException { super.undo(); - select(start,end); } - public void redo() throws CannotRedoException - { + public void redo() throws CannotRedoException { super.redo(); - select(start,end); } - public boolean addEdit(UndoableEdit edit) - { - if(edit instanceof CaretUndo) - { + public boolean addEdit(UndoableEdit edit) { + if (edit instanceof CaretUndo) { CaretUndo cedit = (CaretUndo)edit; start = cedit.start; end = cedit.end; cedit.die(); - return true; } - else - return false; + return false; } } - -// static -// { -// caretTimer = new Timer(500, new CaretBlinker()); -// caretTimer.setInitialDelay(500); -// caretTimer.start(); -// } } diff --git a/app/src/processing/app/syntax/KeywordMap.java b/app/src/processing/app/syntax/KeywordMap.java index b8b66871d..cfa4caa68 100644 --- a/app/src/processing/app/syntax/KeywordMap.java +++ b/app/src/processing/app/syntax/KeywordMap.java @@ -76,7 +76,7 @@ public class KeywordMap { // continue; // } if (length == k.keyword.length) { - if (SyntaxUtilities.regionMatches(ignoreCase, text, offset, k.keyword)) { + if (regionMatches(ignoreCase, text, offset, k.keyword)) { return k.id; } } @@ -84,6 +84,36 @@ public class KeywordMap { } return Token.NULL; } + + + /** + * Checks if a subregion of a Segment is equal to a + * character array. + * @param ignoreCase True if case should be ignored, false otherwise + * @param text The segment + * @param offset The offset into the segment + * @param match The character array to match + */ + static public boolean regionMatches(boolean ignoreCase, Segment text, + int offset, char[] match) { + int length = offset + match.length; + char[] textArray = text.array; + if(length > text.offset + text.count) + return false; + for(int i = offset, j = 0; i < length; i++, j++) + { + char c1 = textArray[i]; + char c2 = match[j]; + if(ignoreCase) + { + c1 = Character.toUpperCase(c1); + c2 = Character.toUpperCase(c2); + } + if(c1 != c2) + return false; + } + return true; + } /** diff --git a/app/src/processing/app/syntax/PdeKeywords.java b/app/src/processing/app/syntax/PdeKeywords.java index 977d6f4b7..8d8b60b23 100644 --- a/app/src/processing/app/syntax/PdeKeywords.java +++ b/app/src/processing/app/syntax/PdeKeywords.java @@ -29,10 +29,6 @@ import javax.swing.text.Segment; import processing.app.Editor; -/** - * This class reads a keywords.txt file to get coloring put links to reference - * locations for the set of keywords. - */ public class PdeKeywords extends TokenMarker { private KeywordMap keywordColoring; diff --git a/app/src/processing/app/syntax/PdeTextAreaDefaults.java b/app/src/processing/app/syntax/PdeTextAreaDefaults.java index 16ba8a876..33427f864 100644 --- a/app/src/processing/app/syntax/PdeTextAreaDefaults.java +++ b/app/src/processing/app/syntax/PdeTextAreaDefaults.java @@ -180,7 +180,7 @@ public class PdeTextAreaDefaults extends TextAreaDefaults { inputHandler.addKeyBinding(mod + "+ENTER", InputHandler.REPEAT); document = new SyntaxDocument(); - editable = true; +// editable = true; // Set to 0 for revision 0215 because it causes strange jumps // http://code.google.com/p/processing/issues/detail?id=1055 @@ -194,8 +194,16 @@ public class PdeTextAreaDefaults extends TextAreaDefaults { // http://code.google.com/p/processing/issues/detail?id=1275 rows = 5; - font = Preferences.getFont("editor.font"); + /* + String fontFamily = Preferences.get("editor.font.family"); + int fontSize = Preferences.getInteger("editor.font.size"); + plainFont = new Font(fontFamily, Font.PLAIN, fontSize); + boldFont = new Font(fontFamily, Font.BOLD, fontSize); antialias = Preferences.getBoolean("editor.antialias"); + */ + + fgcolor = mode.getColor("editor.fgcolor"); + bgcolor = mode.getColor("editor.bgcolor"); styles = new SyntaxStyle[Token.ID_COUNT]; @@ -222,9 +230,6 @@ public class PdeTextAreaDefaults extends TextAreaDefaults { // area that's not in use by the text (replaced with tildes) styles[Token.INVALID] = mode.getStyle("invalid"); - fgcolor = mode.getColor("editor.fgcolor"); - bgcolor = mode.getColor("editor.bgcolor"); - caretColor = mode.getColor("editor.caret.color"); selectionColor = mode.getColor("editor.selection.color"); lineHighlight = mode.getBoolean("editor.linehighlight"); diff --git a/app/src/processing/app/syntax/SyntaxStyle.java b/app/src/processing/app/syntax/SyntaxStyle.java index 674bbdc78..9415a4bf0 100644 --- a/app/src/processing/app/syntax/SyntaxStyle.java +++ b/app/src/processing/app/syntax/SyntaxStyle.java @@ -9,191 +9,179 @@ package processing.app.syntax; -import java.awt.*; -import javax.swing.JComponent; - -import processing.app.Preferences; +import java.awt.Color; /** - * A simple text style class. It can specify the color, italic flag, - * and bold flag of a run of text. + * A simple text style class. + * It can specify the color and bold flag of a run of text. * @author Slava Pestov * @version $Id$ */ -public class SyntaxStyle -{ +public class SyntaxStyle { + private Color color; +// private boolean italic; + private boolean bold; +// private Font lastFont; +// private Font lastStyledFont; +// private FontMetrics fontMetrics; + + /** * Creates a new SyntaxStyle. * @param color The text color * @param italic True if the text should be italics * @param bold True if the text should be bold */ - public SyntaxStyle(Color color, boolean italic, boolean bold) - { +// public SyntaxStyle(Color color, boolean italic, boolean bold) { + public SyntaxStyle(Color color, boolean bold) { this.color = color; - this.italic = italic; +// this.italic = italic; this.bold = bold; } - /** - * Returns the color specified in this style. - */ - public Color getColor() - { + + /** Returns the color specified in this style. */ + public Color getColor() { return color; } - /** - * Returns true if no font styles are enabled. - */ - public boolean isPlain() - { - return !(bold || italic); - } + +// /** +// * Returns true if no font styles are enabled. +// */ +// public boolean isPlain() { +// return !(bold || italic); +// } - /** - * Returns true if italics is enabled for this style. - */ - public boolean isItalic() - { - return italic; - } +// /** +// * Returns true if italics is enabled for this style. +// */ +// public boolean isItalic() { +// return italic; +// } - /** - * Returns true if boldface is enabled for this style. - */ - public boolean isBold() - { + + /** Returns true if boldface is enabled for this style. */ + public boolean isBold() { return bold; } - /** - * Returns the specified font, but with the style's bold and - * italic flags applied. - */ - public Font getStyledFont(Font font) - { - if(font == null) - throw new NullPointerException("font param must not" - + " be null"); - if(font.equals(lastFont)) - return lastStyledFont; - lastFont = font; -// lastStyledFont = new Font(font.getFamily(), -// (bold ? Font.BOLD : 0) -// | (italic ? Font.ITALIC : 0), -// font.getSize()); - lastStyledFont = - findFont(font.getFamily(), bold ? Font.BOLD : Font.PLAIN, font.getSize()); - return lastStyledFont; - } - - /** - * Returns the font metrics for the styled font. - */ - public FontMetrics getFontMetrics(Font font, JComponent comp) { - if (font == null) { - throw new NullPointerException("font param must not be null"); - } - if (font.equals(lastFont) && fontMetrics != null) { - return fontMetrics; - } - lastFont = font; -// lastStyledFont = new Font(font.getFamily(), -// (bold ? Font.BOLD : 0) -// | (italic ? Font.ITALIC : 0), -// font.getSize()); - lastStyledFont = - findFont(font.getFamily(), bold ? Font.BOLD : Font.PLAIN, font.getSize()); - - //fontMetrics = Toolkit.getDefaultToolkit().getFontMetrics(lastStyledFont); - fontMetrics = comp.getFontMetrics(lastStyledFont); - return fontMetrics; - } - - /* - on Windows (and I presume Linux) we get something like this: - - mono family Source Code Pro - mono fontname Source Code Pro - mono name Source Code Pro - mono psname SourceCodePro-Regular - - mono family Source Code Pro Semibold - mono fontname Source Code Pro Semibold - mono name Source Code Pro Semibold - mono psname SourceCodePro-Semibold - - ...which means that 'family' is not a usable method. - */ - //private String monoFontFamily; - - private Font findFont(String familyName, int style, int size) { - // getFamily() is too unreliable across platforms - if (Preferences.get("editor.font").startsWith("processing.mono")) { - return processing.app.Toolkit.getMonoFont(size, style); - } else { - return new Font(familyName, style, size); - } - /* - if (monoFontFamily == null) { - // This should be more reliable across platforms than the - // family name, which only seems to work correctly on OS X. - // (Or perhaps only when it's installed locally.) - String psName = - processing.app.Toolkit.getMonoFont(size, style).getPSName(); - int dash = psName.indexOf('-'); - monoFontFamily = psName.substring(0, dash); - - // Just get the font family name for comparison - //monoFontFamily = - //processing.app.Toolkit.getMonoFont(size, style).getFamily(); - //processing.app.Toolkit.getMonoFont(size, style).getFamily(); - Font mono = processing.app.Toolkit.getMonoFont(size, style); - System.out.println("mono family " + mono.getFamily()); - System.out.println("mono fontname " + mono.getFontName()); - System.out.println("mono name " + mono.getName()); - System.out.println("mono psname " + mono.getPSName()); - } - if (familyName.equals(monoFontFamily)) { - System.out.println("getting style bold? " + (style == Font.BOLD)); - return processing.app.Toolkit.getMonoFont(size, style); - } else { - //System.out.println("name is " + name + " mono name is " + monoFontName + " " + style); - return new Font(familyName, style, size); - } - */ - } - - /** - * Sets the foreground color and font of the specified graphics - * context to that specified in this style. - * @param gfx The graphics context - * @param font The font to add the styles to - */ - public void setGraphicsFlags(Graphics gfx, Font font) - { - Font _font = getStyledFont(font); - gfx.setFont(_font); - gfx.setColor(color); - } + +// /** +// * Returns the specified font, but with the style's bold flags applied. +// */ +// public Font getStyledFont(Font font) { +// if (font.equals(lastFont)) { +// return lastStyledFont; +// } +// lastFont = font; +// lastStyledFont = +// findFont(font.getFamily(), bold ? Font.BOLD : Font.PLAIN, font.getSize()); +// return lastStyledFont; +// } +// +// +// /** +// * Returns the font metrics for the styled font. +// */ +// public FontMetrics getFontMetrics(Font font, JComponent comp) { +//// if (font == null) { +//// throw new NullPointerException("font param must not be null"); +//// } +// if (font.equals(lastFont) && fontMetrics != null) { +// return fontMetrics; +// } +// lastFont = font; +//// lastStyledFont = new Font(font.getFamily(), +//// (bold ? Font.BOLD : 0) +//// | (italic ? Font.ITALIC : 0), +//// font.getSize()); +// lastStyledFont = +// findFont(font.getFamily(), bold ? Font.BOLD : Font.PLAIN, font.getSize()); +// +// //fontMetrics = Toolkit.getDefaultToolkit().getFontMetrics(lastStyledFont); +// fontMetrics = comp.getFontMetrics(lastStyledFont); +// return fontMetrics; +// } +// +// +// /* +// on Windows (and I presume Linux) we get something like this: +// +// mono family Source Code Pro +// mono fontname Source Code Pro +// mono name Source Code Pro +// mono psname SourceCodePro-Regular +// +// mono family Source Code Pro Semibold +// mono fontname Source Code Pro Semibold +// mono name Source Code Pro Semibold +// mono psname SourceCodePro-Semibold +// +// ...which means that 'family' is not a usable method. +// */ +// //private String monoFontFamily; +// +// private Font findFont(String familyName, int style, int size) { +//// // getFamily() is too unreliable across platforms +//// if (Preferences.get("editor.font").startsWith("processing.mono")) { +//// return processing.app.Toolkit.getMonoFont(size, style); +//// } else { +// System.out.println("creating new font for " + familyName); +// return new Font(familyName, style, size); +//// } +// +// /* +// if (monoFontFamily == null) { +// // This should be more reliable across platforms than the +// // family name, which only seems to work correctly on OS X. +// // (Or perhaps only when it's installed locally.) +// String psName = +// processing.app.Toolkit.getMonoFont(size, style).getPSName(); +// int dash = psName.indexOf('-'); +// monoFontFamily = psName.substring(0, dash); +// +// // Just get the font family name for comparison +// //monoFontFamily = +// //processing.app.Toolkit.getMonoFont(size, style).getFamily(); +// //processing.app.Toolkit.getMonoFont(size, style).getFamily(); +// Font mono = processing.app.Toolkit.getMonoFont(size, style); +// System.out.println("mono family " + mono.getFamily()); +// System.out.println("mono fontname " + mono.getFontName()); +// System.out.println("mono name " + mono.getName()); +// System.out.println("mono psname " + mono.getPSName()); +// } +// if (familyName.equals(monoFontFamily)) { +// System.out.println("getting style bold? " + (style == Font.BOLD)); +// return processing.app.Toolkit.getMonoFont(size, style); +// } else { +// //System.out.println("name is " + name + " mono name is " + monoFontName + " " + style); +// return new Font(familyName, style, size); +// } +// */ +// } +// +// +// /** +// * Sets the foreground color and font of the specified graphics +// * context to that specified in this style. +// * @param gfx The graphics context +// * @param font The font to add the styles to +// */ +// public void setGraphicsFlags(Graphics gfx, Font font) { +// Font _font = getStyledFont(font); +// gfx.setFont(_font); +// gfx.setColor(color); +// } + /** * Returns a string representation of this object. */ - public String toString() - { + public String toString() { return getClass().getName() + "[color=" + color + - (italic ? ",italic" : "") + +// (italic ? ",italic" : "") + (bold ? ",bold" : "") + "]"; } - - // private members - private Color color; - private boolean italic; - private boolean bold; - private Font lastFont; - private Font lastStyledFont; - private FontMetrics fontMetrics; } diff --git a/app/src/processing/app/syntax/SyntaxUtilities.java b/app/src/processing/app/syntax/SyntaxUtilities.java deleted file mode 100644 index 1e40ac831..000000000 --- a/app/src/processing/app/syntax/SyntaxUtilities.java +++ /dev/null @@ -1,156 +0,0 @@ -/* - * SyntaxUtilities.java - Utility functions used by syntax colorizing - * Copyright (C) 1999 Slava Pestov - * - * You may use and modify this package for any purpose. Redistribution is - * permitted, in both source and binary form, provided that this notice - * remains intact in all source distributions of this package. - */ - -package processing.app.syntax; - -import javax.swing.text.*; -import java.awt.*; - - -/** - * Class with several utility functions used by jEdit's syntax colorizing - * subsystem. - * - * @author Slava Pestov - * @version $Id$ - */ -public class SyntaxUtilities { - - /** - * Checks if a subregion of a Segment is equal to a - * string. - * @param ignoreCase True if case should be ignored, false otherwise - * @param text The segment - * @param offset The offset into the segment - * @param match The string to match - */ - public static boolean regionMatches(boolean ignoreCase, Segment text, - int offset, String match) { - int length = offset + match.length(); - char[] textArray = text.array; - if(length > text.offset + text.count) - return false; - for(int i = offset, j = 0; i < length; i++, j++) - { - char c1 = textArray[i]; - char c2 = match.charAt(j); - if(ignoreCase) - { - c1 = Character.toUpperCase(c1); - c2 = Character.toUpperCase(c2); - } - if(c1 != c2) - return false; - } - return true; - } - - - /** - * Checks if a subregion of a Segment is equal to a - * character array. - * @param ignoreCase True if case should be ignored, false otherwise - * @param text The segment - * @param offset The offset into the segment - * @param match The character array to match - */ - public static boolean regionMatches(boolean ignoreCase, Segment text, - int offset, char[] match) { - int length = offset + match.length; - char[] textArray = text.array; - if(length > text.offset + text.count) - return false; - for(int i = offset, j = 0; i < length; i++, j++) - { - char c1 = textArray[i]; - char c2 = match[j]; - if(ignoreCase) - { - c1 = Character.toUpperCase(c1); - c2 = Character.toUpperCase(c2); - } - if(c1 != c2) - return false; - } - return true; - } - - -// /** -// * Returns the default style table. This can be passed to the -// * setStyles() method of SyntaxDocument -// * to use the default syntax styles. -// */ -// public static SyntaxStyle[] getDefaultSyntaxStyles() { -// SyntaxStyle[] styles = new SyntaxStyle[Token.ID_COUNT]; -// -// styles[Token.COMMENT1] = new SyntaxStyle(Color.black,true,false); -// styles[Token.COMMENT2] = new SyntaxStyle(new Color(0x990033),true,false); -// styles[Token.KEYWORD1] = new SyntaxStyle(Color.black,false,true); -// styles[Token.KEYWORD2] = new SyntaxStyle(Color.magenta,false,false); -// styles[Token.KEYWORD3] = new SyntaxStyle(new Color(0x009600),false,false); -// styles[Token.FUNCTION1] = new SyntaxStyle(Color.magenta,false,false); -// styles[Token.FUNCTION2] = new SyntaxStyle(new Color(0x009600),false,false); -// styles[Token.LITERAL1] = new SyntaxStyle(new Color(0x650099),false,false); -// styles[Token.LITERAL2] = new SyntaxStyle(new Color(0x650099),false,true); -// styles[Token.LABEL] = new SyntaxStyle(new Color(0x990033),false,true); -// styles[Token.OPERATOR] = new SyntaxStyle(Color.black,false,true); -// styles[Token.INVALID] = new SyntaxStyle(Color.red,false,true); -// -// return styles; -// } - - - /** - * Paints the specified line onto the graphics context. Note that this - * method munges the offset and count values of the segment. - * @param line The line segment - * @param tokens The token list for the line - * @param styles The syntax style list - * @param expander The tab expander used to determine tab stops. May - * be null - * @param gfx The graphics context - * @param x The x co-ordinate - * @param y The y co-ordinate - * @return The x co-ordinate, plus the width of the painted string - */ - public static int paintSyntaxLine(Segment line, Token tokens, - SyntaxStyle[] styles, - TabExpander expander, Graphics gfx, - int x, int y) { - Font defaultFont = gfx.getFont(); - Color defaultColor = gfx.getColor(); - - for (;;) { - byte id = tokens.id; - if(id == Token.END) - break; - - int length = tokens.length; - if (id == Token.NULL) { - if(!defaultColor.equals(gfx.getColor())) - gfx.setColor(defaultColor); - if(!defaultFont.equals(gfx.getFont())) - gfx.setFont(defaultFont); - } else { - styles[id].setGraphicsFlags(gfx,defaultFont); - } - line.count = length; - x = Utilities.drawTabbedText(line,x,y,gfx,expander,0); - line.offset += length; - - tokens = tokens.next; - } - - return x; - } - - // private members - private SyntaxUtilities() {} -} diff --git a/app/src/processing/app/syntax/TextAreaDefaults.java b/app/src/processing/app/syntax/TextAreaDefaults.java index 9401c4e32..1c9edb7cd 100644 --- a/app/src/processing/app/syntax/TextAreaDefaults.java +++ b/app/src/processing/app/syntax/TextAreaDefaults.java @@ -20,15 +20,17 @@ import java.awt.*; public class TextAreaDefaults { public InputHandler inputHandler; public SyntaxDocument document; - public boolean editable; +// public boolean editable; public boolean caretVisible; public boolean caretBlinks; public boolean blockCaret; public int electricScroll; + // default/preferred number of rows/cols public int cols; public int rows; + public SyntaxStyle[] styles; public Color caretColor; public Color selectionColor; @@ -40,9 +42,11 @@ public class TextAreaDefaults { public boolean eolMarkers; public boolean paintInvalid; - // moved from TextAreaPainter [fry] - public Font font; + /* + public Font plainFont; + public Font boldFont; + public boolean antialias; + */ public Color fgcolor; public Color bgcolor; - public boolean antialias; } diff --git a/app/src/processing/app/syntax/TextAreaPainter.java b/app/src/processing/app/syntax/TextAreaPainter.java index 4f23686ce..8c971605c 100644 --- a/app/src/processing/app/syntax/TextAreaPainter.java +++ b/app/src/processing/app/syntax/TextAreaPainter.java @@ -10,14 +10,16 @@ */ package processing.app.syntax; -import processing.app.syntax.im.CompositionTextPainter; +import java.awt.event.MouseEvent; +import java.awt.*; +import java.awt.print.*; import javax.swing.ToolTipManager; import javax.swing.text.*; import javax.swing.JComponent; -import java.awt.event.MouseEvent; -import java.awt.*; -import java.awt.print.*; + +import processing.app.Preferences; +import processing.app.syntax.im.CompositionTextPainter; /** @@ -25,25 +27,56 @@ import java.awt.print.*; * lines of text. * @author Slava Pestov */ -public class TextAreaPainter extends JComponent -implements TabExpander, Printable { +public class TextAreaPainter extends JComponent implements TabExpander { /** True if inside printing, will handle disabling the highlight */ boolean printing; - /** Current setting for editor.antialias preference */ - boolean antialias; /** A specific painter composed by the InputMethod.*/ protected CompositionTextPainter compositionTextPainter; + protected JEditTextArea textArea; + protected TextAreaDefaults defaults; + +// protected boolean blockCaret; +// protected SyntaxStyle[] styles; +// protected Color caretColor; +// protected Color selectionColor; +// protected Color lineHighlightColor; +// protected boolean lineHighlight; +// protected Color bracketHighlightColor; +// protected boolean bracketHighlight; +// protected Color eolMarkerColor; +// protected boolean eolMarkers; + +// protected int cols; +// protected int rows; + + // moved from TextAreaDefaults + private Font plainFont; + private Font boldFont; + private boolean antialias; +// private Color fgcolor; +// private Color bgcolor; + + protected int tabSize; + protected FontMetrics fm; + + protected Highlight highlights; + + int currentLineIndex; + Token currentLineTokens; + Segment currentLine; + /** * Creates a new repaint manager. This should be not be called directly. */ public TextAreaPainter(JEditTextArea textArea, TextAreaDefaults defaults) { this.textArea = textArea; + this.defaults = defaults; setAutoscrolls(true); - setDoubleBuffered(true); +// setDoubleBuffered(true); setOpaque(true); ToolTipManager.sharedInstance().registerComponent(this); @@ -53,28 +86,61 @@ implements TabExpander, Printable { setCursor(Cursor.getPredefinedCursor(Cursor.TEXT_CURSOR)); - // unfortunately probably can't just do setDefaults() since things aren't quite set up - setFont(defaults.font); - setForeground(defaults.fgcolor); - setBackground(defaults.bgcolor); +// // unfortunately probably can't just do setDefaults() since things aren't quite set up +// setFont(defaults.plainFont); +//// System.out.println("defaults font is " + defaults.font); +// setForeground(defaults.fgcolor); +// setBackground(defaults.bgcolor); + updateAppearance(); - blockCaret = defaults.blockCaret; - styles = defaults.styles; - caretColor = defaults.caretColor; - selectionColor = defaults.selectionColor; - lineHighlightColor = defaults.lineHighlightColor; - lineHighlight = defaults.lineHighlight; - bracketHighlightColor = defaults.bracketHighlightColor; - bracketHighlight = defaults.bracketHighlight; - eolMarkerColor = defaults.eolMarkerColor; - eolMarkers = defaults.eolMarkers; - antialias = defaults.antialias; +// blockCaret = defaults.blockCaret; +// styles = defaults.styles; +// caretColor = defaults.caretColor; +// selectionColor = defaults.selectionColor; +// lineHighlightColor = defaults.lineHighlightColor; +// lineHighlight = defaults.lineHighlight; +// bracketHighlightColor = defaults.bracketHighlightColor; +// bracketHighlight = defaults.bracketHighlight; +// eolMarkerColor = defaults.eolMarkerColor; +// eolMarkers = defaults.eolMarkers; +// antialias = defaults.antialias; - cols = defaults.cols; - rows = defaults.rows; +// cols = defaults.cols; +// rows = defaults.rows; } + public void updateAppearance() { +// // unfortunately probably can't just do setDefaults() since things aren't quite set up +// setFont(defaults.plainFont); +//// System.out.println("defaults font is " + defaults.font); + setForeground(defaults.fgcolor); + setBackground(defaults.bgcolor); + + String fontFamily = Preferences.get("editor.font.family"); + int fontSize = Preferences.getInteger("editor.font.size"); + plainFont = new Font(fontFamily, Font.PLAIN, fontSize); + if (!fontFamily.equals(plainFont.getFamily())) { + System.err.println(fontFamily + " not available, resetting to monospaced"); + fontFamily = "Monospaced"; + Preferences.set("editor.font.family", fontFamily); + plainFont = new Font(fontFamily, Font.PLAIN, fontSize); + } + boldFont = new Font(fontFamily, Font.BOLD, fontSize); + antialias = Preferences.getBoolean("editor.smooth"); +// System.out.println(plainFont.getFamily()); +// System.out.println(plainFont); + + // moved from setFont() override (never quite comfortable w/ that override) + fm = super.getFontMetrics(plainFont); + textArea.recalculateVisibleLines(); + +// fgcolor = mode.getColor("editor.fgcolor"); +// bgcolor = mode.getColor("editor.bgcolor"); + } + + + /* public void setDefaults(TextAreaDefaults defaults) { setFont(defaults.font); setForeground(defaults.fgcolor); @@ -96,12 +162,13 @@ implements TabExpander, Printable { cols = defaults.cols; rows = defaults.rows; } + */ /** - * Get CompositionTextPainter. if CompositionTextPainter is not created, create it. + * Get CompositionTextPainter, creating one if it doesn't exist. */ - public CompositionTextPainter getCompositionTextpainter(){ + public CompositionTextPainter getCompositionTextpainter() { if (compositionTextPainter == null){ compositionTextPainter = new CompositionTextPainter(textArea); } @@ -115,81 +182,82 @@ implements TabExpander, Printable { * @see processing.app.syntax.Token */ public final SyntaxStyle[] getStyles() { - return styles; + return defaults.styles; } - /** - * Sets the syntax styles used to paint colorized text. Entry n - * will be used to paint tokens with id = n. - * @param styles The syntax styles - * @see processing.app.syntax.Token - */ - public final void setStyles(SyntaxStyle[] styles) { - this.styles = styles; - repaint(); - } +// /** +// * Sets the syntax styles used to paint colorized text. Entry n +// * will be used to paint tokens with id = n. +// * @param styles The syntax styles +// * @see processing.app.syntax.Token +// */ +// public final void setStyles(SyntaxStyle[] styles) { +// this.styles = styles; +// repaint(); +// } - /** - * Returns the caret color. - */ - public final Color getCaretColor() { - return caretColor; - } +// /** +// * Returns the caret color. +// */ +// public final Color getCaretColor() { +// return caretColor; +// } - /** - * Sets the caret color. - * @param caretColor The caret color - */ - public final void setCaretColor(Color caretColor) { - this.caretColor = caretColor; - invalidateSelectedLines(); - } - - /** - * Returns the selection color. - */ - public final Color getSelectionColor() { - return selectionColor; - } +// /** +// * Sets the caret color. +// * @param caretColor The caret color +// */ +// public final void setCaretColor(Color caretColor) { +// this.caretColor = caretColor; +// invalidateSelectedLines(); +// } - /** - * Sets the selection color. - * @param selectionColor The selection color - */ - public final void setSelectionColor(Color selectionColor) { - this.selectionColor = selectionColor; - invalidateSelectedLines(); - } - - - /** - * Returns the line highlight color. - */ - public final Color getLineHighlightColor() { - return lineHighlightColor; - } +// /** +// * Returns the selection color. +// */ +// public final Color getSelectionColor() { +// return selectionColor; +// } - /** - * Sets the line highlight color. - * @param lineHighlightColor The line highlight color - */ - public final void setLineHighlightColor(Color lineHighlightColor) { - this.lineHighlightColor = lineHighlightColor; - invalidateSelectedLines(); - } +// /** +// * Sets the selection color. +// * @param selectionColor The selection color +// */ +// public final void setSelectionColor(Color selectionColor) { +// this.selectionColor = selectionColor; +// invalidateSelectedLines(); +// } - /** - * Returns true if line highlight is enabled, false otherwise. - */ - public final boolean isLineHighlightEnabled() { - return lineHighlight; - } +// /** +// * Returns the line highlight color. +// */ +// public final Color getLineHighlightColor() { +// return lineHighlightColor; +// } + + +// /** +// * Sets the line highlight color. +// * @param lineHighlightColor The line highlight color +// */ +// public final void setLineHighlightColor(Color lineHighlightColor) { +// this.lineHighlightColor = lineHighlightColor; +// invalidateSelectedLines(); +// } + + +// /** +// * Returns true if line highlight is enabled, false otherwise. +// */ +// public final boolean isLineHighlightEnabled() { +// return lineHighlight; +// } /** @@ -198,28 +266,29 @@ implements TabExpander, Printable { * should be enabled, false otherwise */ public final void setLineHighlightEnabled(boolean lineHighlight) { - this.lineHighlight = lineHighlight; +// this.lineHighlight = lineHighlight; + defaults.lineHighlight = lineHighlight; invalidateSelectedLines(); } - /** - * Returns the bracket highlight color. - */ - public final Color getBracketHighlightColor() { - return bracketHighlightColor; - } +// /** +// * Returns the bracket highlight color. +// */ +// public final Color getBracketHighlightColor() { +// return bracketHighlightColor; +// } - /** - * Sets the bracket highlight color. - * @param bracketHighlightColor The bracket highlight color - */ - public final void setBracketHighlightColor(Color bracketHighlightColor) - { - this.bracketHighlightColor = bracketHighlightColor; - invalidateLine(textArea.getBracketLine()); - } +// /** +// * Sets the bracket highlight color. +// * @param bracketHighlightColor The bracket highlight color +// */ +// public final void setBracketHighlightColor(Color bracketHighlightColor) { +// this.bracketHighlightColor = bracketHighlightColor; +// invalidateLine(textArea.getBracketLine()); +// } + /** * Returns true if bracket highlighting is enabled, false otherwise. @@ -227,91 +296,92 @@ implements TabExpander, Printable { * one before the caret (if any) is highlighted. */ public final boolean isBracketHighlightEnabled() { - return bracketHighlight; +// return bracketHighlight; + return defaults.bracketHighlight; } - /** - * Enables or disables bracket highlighting. - * When bracket highlighting is enabled, the bracket matching the - * one before the caret (if any) is highlighted. - * @param bracketHighlight True if bracket highlighting should be - * enabled, false otherwise - */ - public final void setBracketHighlightEnabled(boolean bracketHighlight) { - this.bracketHighlight = bracketHighlight; - invalidateLine(textArea.getBracketLine()); - } +// /** +// * Enables or disables bracket highlighting. +// * When bracket highlighting is enabled, the bracket matching the +// * one before the caret (if any) is highlighted. +// * @param bracketHighlight True if bracket highlighting should be +// * enabled, false otherwise +// */ +// public final void setBracketHighlightEnabled(boolean bracketHighlight) { +// this.bracketHighlight = bracketHighlight; +// invalidateLine(textArea.getBracketLine()); +// } /** * Returns true if the caret should be drawn as a block, false otherwise. */ public final boolean isBlockCaretEnabled() { - return blockCaret; + return defaults.blockCaret; } - /** - * Sets if the caret should be drawn as a block, false otherwise. - * @param blockCaret True if the caret should be drawn as a block, - * false otherwise. - */ - public final void setBlockCaretEnabled(boolean blockCaret) { - this.blockCaret = blockCaret; - invalidateSelectedLines(); - } +// /** +// * Sets if the caret should be drawn as a block, false otherwise. +// * @param blockCaret True if the caret should be drawn as a block, +// * false otherwise. +// */ +// public final void setBlockCaretEnabled(boolean blockCaret) { +// this.blockCaret = blockCaret; +// invalidateSelectedLines(); +// } - /** - * Returns the EOL marker color. - */ - public final Color getEOLMarkerColor() { - return eolMarkerColor; - } +// /** +// * Returns the EOL marker color. +// */ +// public final Color getEOLMarkerColor() { +// return eolMarkerColor; +// } - /** - * Sets the EOL marker color. - * @param eolMarkerColor The EOL marker color - */ - public final void setEOLMarkerColor(Color eolMarkerColor) { - this.eolMarkerColor = eolMarkerColor; - repaint(); - } +// /** +// * Sets the EOL marker color. +// * @param eolMarkerColor The EOL marker color +// */ +// public final void setEOLMarkerColor(Color eolMarkerColor) { +// this.eolMarkerColor = eolMarkerColor; +// repaint(); +// } - /** - * Returns true if EOL markers are drawn, false otherwise. - */ - public final boolean getEOLMarkersPainted() { - return eolMarkers; - } +// /** +// * Returns true if EOL markers are drawn, false otherwise. +// */ +// public final boolean getEOLMarkersPainted() { +// return eolMarkers; +// } - /** - * Sets if EOL markers are to be drawn. - * @param eolMarkers True if EOL markers should be drawn, false otherwise - */ - public final void setEOLMarkersPainted(boolean eolMarkers) { - this.eolMarkers = eolMarkers; - repaint(); - } +// /** +// * Sets if EOL markers are to be drawn. +// * @param eolMarkers True if EOL markers should be drawn, false otherwise +// */ +// public final void setEOLMarkersPainted(boolean eolMarkers) { +// this.eolMarkers = eolMarkers; +// repaint(); +// } - public final void setAntialias(boolean antialias) { - this.antialias = antialias; - } +// public final void setAntialias(boolean antialias) { +// this.antialias = antialias; +// } - /** - * Adds a custom highlight painter. - * @param highlight The highlight - */ - public void addCustomHighlight(Highlight highlight) { - highlight.init(textArea,highlights); - highlights = highlight; - } +// /** +// * Adds a custom highlight painter. +// * @param highlight The highlight +// */ +// public void addCustomHighlight(Highlight highlight) { +// highlight.init(textArea,highlights); +// highlights = highlight; +// } /** @@ -345,35 +415,41 @@ implements TabExpander, Printable { } - /** - * Returns the tool tip to display at the specified location. - * @param evt The mouse event - */ - public String getToolTipText(MouseEvent evt) { - return (highlights == null) ? null : highlights.getToolTipText(evt); - } +// /** +// * Returns the tool tip to display at the specified location. +// * @param evt The mouse event +// */ +// public String getToolTipText(MouseEvent evt) { +// return (highlights == null) ? null : highlights.getToolTipText(evt); +// } - /** - * Returns the font metrics used by this component. - */ + /** Returns the font metrics used by this component. */ public FontMetrics getFontMetrics() { return fm; } - - /** - * Sets the font for this component. This is overridden to update the - * cached font metrics and to recalculate which lines are visible. - * @param font The font - */ - public void setFont(Font font) { - super.setFont(font); - fm = super.getFontMetrics(font); - textArea.recalculateVisibleLines(); + + public FontMetrics getFontMetrics(SyntaxStyle style) { +// return getFontMetrics(style.isBold() ? +// defaults.boldFont : defaults.plainFont); + return getFontMetrics(style.isBold() ? boldFont : plainFont); } +// /** +// * Sets the font for this component. This is overridden to update the +// * cached font metrics and to recalculate which lines are visible. +// * @param font The font +// */ +// public void setFont(Font font) { +//// new Exception().printStackTrace(System.out); +// super.setFont(font); +// fm = super.getFontMetrics(font); +// textArea.recalculateVisibleLines(); +// } + + /** * Repaints the text. * @param gfx The graphics context @@ -385,12 +461,16 @@ implements TabExpander, Printable { RenderingHints.VALUE_TEXT_ANTIALIAS_ON : RenderingHints.VALUE_TEXT_ANTIALIAS_OFF); + // no effect, one way or the other +// g2.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, +// RenderingHints.VALUE_FRACTIONALMETRICS_ON); + tabSize = fm.charWidth(' ') * ((Integer)textArea.getDocument().getProperty(PlainDocument.tabSizeAttribute)).intValue(); Rectangle clipRect = gfx.getClipBounds(); gfx.setColor(getBackground()); - gfx.fillRect(clipRect.x,clipRect.y,clipRect.width,clipRect.height); + gfx.fillRect(clipRect.x, clipRect.y, clipRect.width, clipRect.height); // We don't use yToLine() here because that method doesn't // return lines past the end of the document @@ -407,44 +487,50 @@ implements TabExpander, Printable { int x = textArea.getHorizontalOffset(); for (int line = firstInvalid; line <= lastInvalid; line++) { - paintLine(gfx,tokenMarker,line,x); + paintLine(gfx, line, x, tokenMarker); } if (tokenMarker != null && tokenMarker.isNextLineRequested()) { int h = clipRect.y + clipRect.height; - repaint(0,h,getWidth(),getHeight() - h); + repaint(0, h, getWidth(), getHeight() - h); } } catch (Exception e) { - System.err.println("Error repainting line" - + " range {" + firstInvalid + "," - + lastInvalid + "}:"); + System.err.println("Error repainting line" + + " range {" + firstInvalid + "," + lastInvalid + "}:"); e.printStackTrace(); } } - public int print(Graphics g, PageFormat pageFormat, int pageIndex) { - int lineHeight = fm.getHeight(); - int linesPerPage = (int) (pageFormat.getImageableHeight() / lineHeight); - int lineCount = textArea.getLineCount(); - int lastPage = lineCount / linesPerPage; + public Printable getPrintable() { + return new Printable() { + + @Override + public int print(Graphics graphics, PageFormat pageFormat, + int pageIndex) throws PrinterException { + int lineHeight = fm.getHeight(); + int linesPerPage = (int) (pageFormat.getImageableHeight() / lineHeight); + int lineCount = textArea.getLineCount(); + int lastPage = lineCount / linesPerPage; - if (pageIndex > lastPage) { - return NO_SUCH_PAGE; + if (pageIndex > lastPage) { + return NO_SUCH_PAGE; - } else { - Graphics2D g2d = (Graphics2D)g; - TokenMarker tokenMarker = textArea.getDocument().getTokenMarker(); - int firstLine = pageIndex*linesPerPage; - g2d.translate(Math.max(54, pageFormat.getImageableX()), - pageFormat.getImageableY() - firstLine*lineHeight); - printing = true; - for (int line = firstLine; line < firstLine + linesPerPage; line++) { - paintLine(g2d, tokenMarker, line, 0); + } else { + Graphics2D g2 = (Graphics2D) graphics; + TokenMarker tokenMarker = textArea.getDocument().getTokenMarker(); + int firstLine = pageIndex*linesPerPage; + g2.translate(Math.max(54, pageFormat.getImageableX()), + pageFormat.getImageableY() - firstLine*lineHeight); + printing = true; + for (int line = firstLine; line < firstLine + linesPerPage; line++) { + paintLine(g2, line, 0, tokenMarker); + } + printing = false; + return PAGE_EXISTS; + } } - printing = false; - return PAGE_EXISTS; - } + }; } @@ -452,9 +538,9 @@ implements TabExpander, Printable { * Marks a line as needing a repaint. * @param line The line to invalidate */ - public final void invalidateLine(int line) { - repaint(0,textArea.lineToY(line) + fm.getMaxDescent() + fm.getLeading(), - getWidth(),fm.getHeight()); + final public void invalidateLine(int line) { + repaint(0, textArea.lineToY(line) + fm.getMaxDescent() + fm.getLeading(), + getWidth(), fm.getHeight()); } @@ -463,57 +549,47 @@ implements TabExpander, Printable { * @param firstLine The first line to invalidate * @param lastLine The last line to invalidate */ - public final void invalidateLineRange(int firstLine, int lastLine) { + final void invalidateLineRange(int firstLine, int lastLine) { repaint(0,textArea.lineToY(firstLine) + fm.getMaxDescent() + fm.getLeading(), getWidth(),(lastLine - firstLine + 1) * fm.getHeight()); } - /** - * Repaints the lines containing the selection. - */ - public final void invalidateSelectedLines() { + /** Repaints the lines containing the selection. */ + final void invalidateSelectedLines() { invalidateLineRange(textArea.getSelectionStartLine(), textArea.getSelectionStopLine()); } - /** - * Implementation of TabExpander interface. Returns next tab stop after - * a specified point. - * @param x The x co-ordinate - * @param tabOffset Ignored - * @return The next tab stop after x - */ + /** Returns next tab stop after a specified point. */ +// TabExpander tabExpander = new TabExpander() { + @Override public float nextTabStop(float x, int tabOffset) { int offset = textArea.getHorizontalOffset(); int ntabs = ((int)x - offset) / tabSize; return (ntabs + 1) * tabSize + offset; } +// }; + + + // do we go here? do will kill tabs? +// public float nextTabStop(float x, int tabOffset) { +// return x; +// } + - /** - * Returns the painter's preferred size. - */ public Dimension getPreferredSize() { - Dimension dim = new Dimension(); - dim.width = fm.charWidth('w') * cols; - dim.height = fm.getHeight() * rows; - return dim; + return new Dimension(fm.charWidth('w') * defaults.cols, + fm.getHeight() * defaults.rows); } - /** - * Returns the painter's minimum size. - */ public Dimension getMinimumSize() { return getPreferredSize(); } - // package-private members - int currentLineIndex; - Token currentLineTokens; - Segment currentLine; /** * Accessor used by tools that want to hook in and grab the formatting. @@ -521,6 +597,7 @@ implements TabExpander, Printable { public int getCurrentLineIndex() { return currentLineIndex; } + /** * Accessor used by tools that want to hook in and grab the formatting. @@ -528,6 +605,7 @@ implements TabExpander, Printable { public void setCurrentLineIndex(int what) { currentLineIndex = what; } + /** * Accessor used by tools that want to hook in and grab the formatting. @@ -536,12 +614,14 @@ implements TabExpander, Printable { return currentLineTokens; } + /** * Accessor used by tools that want to hook in and grab the formatting. */ public void setCurrentLineTokens(Token tokens) { currentLineTokens = tokens; } + /** * Accessor used by tools that want to hook in and grab the formatting. @@ -551,106 +631,177 @@ implements TabExpander, Printable { } - // protected members - protected JEditTextArea textArea; - - protected SyntaxStyle[] styles; - protected Color caretColor; - protected Color selectionColor; - protected Color lineHighlightColor; - protected Color bracketHighlightColor; - protected Color eolMarkerColor; - - protected boolean blockCaret; - protected boolean lineHighlight; - protected boolean bracketHighlight; - protected boolean eolMarkers; - protected int cols; - protected int rows; - - protected int tabSize; - protected FontMetrics fm; - - protected Highlight highlights; - - protected void paintLine(Graphics gfx, TokenMarker tokenMarker, + /** Old paintLine() method with kooky args order, kept around for X Mode. */ + @Deprecated + protected void paintLine(Graphics gfx, TokenMarker tokenMarker, int line, int x) { - Font defaultFont = getFont(); - Color defaultColor = getForeground(); +// Font defaultFont = getFont(); +// Color defaultColor = getForeground(); currentLineIndex = line; int y = textArea.lineToY(line); if (tokenMarker == null) { - paintPlainLine(gfx,line,defaultFont,defaultColor,x,y); + //paintPlainLine(gfx, line, defaultFont, defaultColor, x, y); + paintPlainLine(gfx, line, x, y); } else if (line >= 0 && line < textArea.getLineCount()) { - paintSyntaxLine(gfx,tokenMarker,line,defaultFont, - defaultColor,x,y); + //paintSyntaxLine(gfx, tokenMarker, line, defaultFont, defaultColor, x, y); + paintSyntaxLine(gfx, line, x, y, tokenMarker); } } - - protected void paintPlainLine(Graphics gfx, int line, Font defaultFont, - Color defaultColor, int x, int y) { - paintHighlight(gfx,line,y); - textArea.getLineText(line,currentLine); - - gfx.setFont(defaultFont); - gfx.setColor(defaultColor); - - y += fm.getHeight(); - x = Utilities.drawTabbedText(currentLine,x,y,gfx,this,0); - // Draw characters via input method. - if (compositionTextPainter != null && compositionTextPainter.hasComposedTextLayout()) { - compositionTextPainter.draw(gfx, lineHighlightColor); - } - if (eolMarkers) { - gfx.setColor(eolMarkerColor); - gfx.drawString(".",x,y); - } + + + protected void paintLine(Graphics gfx, int line, int x, + TokenMarker tokenMarker) { + paintLine(gfx, tokenMarker, line, x); } - protected void paintSyntaxLine(Graphics gfx, TokenMarker tokenMarker, - int line, Font defaultFont, - Color defaultColor, int x, int y) { - textArea.getLineText(currentLineIndex,currentLine); - currentLineTokens = tokenMarker.markTokens(currentLine, - currentLineIndex); - - paintHighlight(gfx,line,y); - - gfx.setFont(defaultFont); - gfx.setColor(defaultColor); - y += fm.getHeight(); - x = SyntaxUtilities.paintSyntaxLine(currentLine, - currentLineTokens, - styles, this, gfx, x, y); - /* - * Draw characters via input method. - */ - if (compositionTextPainter != null && compositionTextPainter.hasComposedTextLayout()) { - compositionTextPainter.draw(gfx, lineHighlightColor); - } - if (eolMarkers) { - gfx.setColor(eolMarkerColor); - gfx.drawString(".",x,y); - } - } - - - protected void paintHighlight(Graphics gfx, int line, int y) { + +// protected void paintPlainLine(Graphics gfx, int line, Font defaultFont, +// Color defaultColor, int x, int y) { + protected void paintPlainLine(Graphics gfx, int line, int x, int y) { if (!printing) { - if (line >= textArea.getSelectionStartLine() - && line <= textArea.getSelectionStopLine()) - paintLineHighlight(gfx,line,y); + paintHighlight(gfx,line,y); + } + textArea.getLineText(line, currentLine); - if (highlights != null) - highlights.paintHighlight(gfx,line,y); +// gfx.setFont(plainFont); +// gfx.setFont(defaultFont); +// gfx.setColor(defaultColor); - if (bracketHighlight && line == textArea.getBracketLine()) - paintBracketHighlight(gfx,line,y); + y += fm.getHeight(); + // doesn't respect fixed width like it should +// x = Utilities.drawTabbedText(currentLine, x, y, gfx, this, 0); +// int w = fm.charWidth(' '); + for (int i = 0; i < currentLine.count; i++) { + gfx.drawChars(currentLine.array, currentLine.offset+i, 1, x, y); + x = currentLine.array[currentLine.offset + i] == '\t' ? (int)nextTabStop(x, i) : + x + fm.charWidth(currentLine.array[currentLine.offset+i]); + } - if (line == textArea.getCaretLine()) - paintCaret(gfx,line,y); + // Draw characters via input method. + if (compositionTextPainter != null && + compositionTextPainter.hasComposedTextLayout()) { + compositionTextPainter.draw(gfx, defaults.lineHighlightColor); + } + if (defaults.eolMarkers) { + gfx.setColor(defaults.eolMarkerColor); + gfx.drawString(".", x, y); + } + } + + +// protected void paintSyntaxLine(Graphics gfx, TokenMarker tokenMarker, +// int line, Font defaultFont, +// Color defaultColor, int x, int y) { + protected void paintSyntaxLine(Graphics gfx, int line, int x, int y, + TokenMarker tokenMarker) { + textArea.getLineText(currentLineIndex, currentLine); + currentLineTokens = tokenMarker.markTokens(currentLine, currentLineIndex); + +// gfx.setFont(plainFont); + paintHighlight(gfx, line, y); + +// gfx.setFont(defaultFont); +// gfx.setColor(defaultColor); + y += fm.getHeight(); +// x = paintSyntaxLine(currentLine, +// currentLineTokens, +// defaults.styles, this, gfx, x, y); + x = paintSyntaxLine(gfx, currentLine, x, y, + currentLineTokens, + defaults.styles); + // Draw characters via input method. + if (compositionTextPainter != null && + compositionTextPainter.hasComposedTextLayout()) { + compositionTextPainter.draw(gfx, defaults.lineHighlightColor); + } + if (defaults.eolMarkers) { + gfx.setColor(defaults.eolMarkerColor); + gfx.drawString(".", x, y); + } + } + + + /** + * Paints the specified line onto the graphics context. Note that this + * method munges the offset and count values of the segment. + * @param line The line segment + * @param tokens The token list for the line + * @param styles The syntax style list + * @param expander The tab expander used to determine tab stops. May + * be null + * @param gfx The graphics context + * @param x The x co-ordinate + * @param y The y co-ordinate + * @return The x co-ordinate, plus the width of the painted string + */ +// public int paintSyntaxLine(Segment line, Token tokens, SyntaxStyle[] styles, +// TabExpander expander, Graphics gfx, +// int x, int y) { + protected int paintSyntaxLine(Graphics gfx, Segment line, int x, int y, + Token tokens, SyntaxStyle[] styles) { +// Font defaultFont = gfx.getFont(); +// Color defaultColor = gfx.getColor(); + +// for (byte id = tokens.id; id != Token.END; tokens = tokens.next) { + for (;;) { + byte id = tokens.id; + if (id == Token.END) + break; + + int length = tokens.length; + if (id == Token.NULL) { +// if(!defaultColor.equals(gfx.getColor())) +// gfx.setColor(defaultColor); +// if(!defaultFont.equals(gfx.getFont())) +// gfx.setFont(defaultFont); + gfx.setColor(defaults.fgcolor); + gfx.setFont(plainFont); + } else { + //styles[id].setGraphicsFlags(gfx,defaultFont); + SyntaxStyle ss = styles[id]; + gfx.setColor(ss.getColor()); + gfx.setFont(ss.isBold() ? boldFont : plainFont); + } + line.count = length; // huh? suspicious + // doesn't respect mono metrics, insists on spacing w/ fractional or something +// x = Utilities.drawTabbedText(line, x, y, gfx, this, 0); +// gfx.drawChars(line.array, line.offset, line.count, x, y); +// int w = fm.charWidth(' '); + for (int i = 0; i < line.count; i++) { + gfx.drawChars(line.array, line.offset+i, 1, x, y); + x = line.array[line.offset + i] == '\t' ? (int)nextTabStop(x, i) : + x + fm.charWidth(line.array[line.offset+i]); + } + //x += fm.charsWidth(line.array, line.offset, line.count); + //x += fm.charWidth(' ') * line.count; + line.offset += length; + + tokens = tokens.next; + } + + return x; + } + + + protected void paintHighlight(Graphics gfx, int line, int y) {//, boolean printing) { +// if (!printing) { + if (line >= textArea.getSelectionStartLine() && + line <= textArea.getSelectionStopLine()) { + paintLineHighlight(gfx, line, y); + } + + if (highlights != null) { + highlights.paintHighlight(gfx, line, y); + } + + if (defaults.bracketHighlight && line == textArea.getBracketLine()) { + paintBracketHighlight(gfx, line, y); + } + + if (line == textArea.getCaretLine()) { + paintCaret(gfx, line, y); } } @@ -663,12 +814,12 @@ implements TabExpander, Printable { int selectionEnd = textArea.getSelectionStop(); if (selectionStart == selectionEnd) { - if (lineHighlight) { - gfx.setColor(lineHighlightColor); - gfx.fillRect(0,y,getWidth(),height); + if (defaults.lineHighlight) { + gfx.setColor(defaults.lineHighlightColor); + gfx.fillRect(0, y, getWidth(), height); } } else { - gfx.setColor(selectionColor); + gfx.setColor(defaults.selectionColor); int selectionStartLine = textArea.getSelectionStartLine(); int selectionEndLine = textArea.getSelectionStopLine(); @@ -712,17 +863,15 @@ implements TabExpander, Printable { protected void paintBracketHighlight(Graphics gfx, int line, int y) { int position = textArea.getBracketPosition(); - if (position == -1) { - return; + if (position != -1) { + y += fm.getLeading() + fm.getMaxDescent(); + int x = textArea._offsetToX(line, position); + gfx.setColor(defaults.bracketHighlightColor); + // Hack!!! Since there is no fast way to get the character + // from the bracket matching routine, we use ( since all + // brackets probably have the same width anyway + gfx.drawRect(x,y,fm.charWidth('(') - 1, fm.getHeight() - 1); } - y += fm.getLeading() + fm.getMaxDescent(); - int x = textArea._offsetToX(line,position); - gfx.setColor(bracketHighlightColor); - // Hack!!! Since there is no fast way to get the character - // from the bracket matching routine, we use ( since all - // brackets probably have the same width anyway - gfx.drawRect(x,y,fm.charWidth('(') - 1, - fm.getHeight() - 1); } @@ -733,7 +882,7 @@ implements TabExpander, Printable { int offset = textArea.getCaretPosition() - textArea.getLineStartOffset(line); int caretX = textArea._offsetToX(line, offset); - int caretWidth = ((blockCaret || + int caretWidth = ((defaults.blockCaret || textArea.isOverwriteEnabled()) ? fm.charWidth('w') : 1); y += fm.getLeading() + fm.getMaxDescent(); @@ -741,10 +890,10 @@ implements TabExpander, Printable { //System.out.println("caretX, width = " + caretX + " " + caretWidth); - gfx.setColor(caretColor); + gfx.setColor(defaults.caretColor); if (textArea.isOverwriteEnabled()) { - gfx.fillRect(caretX,y + height - 1, caretWidth,1); + gfx.fillRect(caretX, y + height - 1, caretWidth,1); } else { // some machines don't like the drawRect for the single @@ -761,4 +910,4 @@ implements TabExpander, Printable { } } } -} \ No newline at end of file +} diff --git a/app/src/processing/app/syntax/TokenMarker.java b/app/src/processing/app/syntax/TokenMarker.java index 363bb61e3..0e414e901 100644 --- a/app/src/processing/app/syntax/TokenMarker.java +++ b/app/src/processing/app/syntax/TokenMarker.java @@ -25,6 +25,8 @@ import javax.swing.text.Segment; */ public abstract class TokenMarker { + abstract public void addColoring(String keyword, String coloring); + /** * A wrapper for the lower-level markTokensImpl method * that is called to split a line up into tokens. diff --git a/app/src/processing/app/syntax/im/CompositionTextPainter.java b/app/src/processing/app/syntax/im/CompositionTextPainter.java index 0084f491f..ae0930a3b 100644 --- a/app/src/processing/app/syntax/im/CompositionTextPainter.java +++ b/app/src/processing/app/syntax/im/CompositionTextPainter.java @@ -8,7 +8,6 @@ import java.awt.Point; import java.awt.font.TextLayout; import processing.app.syntax.JEditTextArea; -import processing.app.syntax.TextAreaPainter; /** * Paint texts from input method. Text via input method are transmitted by @@ -26,15 +25,17 @@ public class CompositionTextPainter { private int composedBeginCaretPosition = 0; private JEditTextArea textArea; + /** * Constructor for painter. - * @param textarea textarea used by PDE. + * @param textArea textarea used by PDE. */ public CompositionTextPainter(JEditTextArea textArea) { this.textArea = textArea; composedTextLayout = null; } + /** * Check the painter has TextLayout. * If a user input via InputMethod, this result will return true. @@ -44,6 +45,7 @@ public class CompositionTextPainter { return (composedTextLayout != null); } + /** * Set TextLayout to the painter. * TextLayout will be created and set by CompositionTextManager. @@ -55,6 +57,7 @@ public class CompositionTextPainter { this.composedTextLayout = composedTextLayout; this.composedBeginCaretPosition = composedStartCaretPosition; } + /** * Invalidate this TextLayout to set null. @@ -66,6 +69,7 @@ public class CompositionTextPainter { //this.composedBeginCaretPosition = textArea.getCaretPosition(); } + /** * Draw text via input method with composed text information. * This method can draw texts with some underlines to illustrate converting characters. @@ -92,6 +96,7 @@ public class CompositionTextPainter { refillComposedArea(fillBackGroundColor, composedLoc.x, composedLoc.y); composedTextLayout.draw((Graphics2D) gfx, composedLoc.x, composedLoc.y); } + /** * Fill color to erase characters drawn by original TextAreaPainter. @@ -109,16 +114,18 @@ public class CompositionTextPainter { int paintWidth = (int) composedTextLayout.getBounds().getWidth(); gfx.fillRect(x, newY, paintWidth, paintHeight); } + private Point getCaretLocation() { - Point loc = new Point(); - TextAreaPainter painter = textArea.getPainter(); - FontMetrics fm = painter.getFontMetrics(); + FontMetrics fm = textArea.getPainter().getFontMetrics(); int offsetY = fm.getHeight() - CompositionTextManager.COMPOSING_UNDERBAR_HEIGHT; int lineIndex = textArea.getCaretLine(); - loc.y = lineIndex * fm.getHeight() + offsetY; +// loc.y = lineIndex * fm.getHeight() + offsetY; int offsetX = composedBeginCaretPosition - textArea.getLineStartOffset(lineIndex); - loc.x = textArea.offsetToX(lineIndex, offsetX); - return loc; +// loc.x = textArea.offsetToX(lineIndex, offsetX); + return new Point(textArea.offsetToX(lineIndex, offsetX), + lineIndex * fm.getHeight() + offsetY); +// Point loc = new Point(); +// return loc; } } diff --git a/app/src/processing/app/tools/Archiver.java b/app/src/processing/app/tools/Archiver.java index e81ad47ee..a10643649 100644 --- a/app/src/processing/app/tools/Archiver.java +++ b/app/src/processing/app/tools/Archiver.java @@ -102,6 +102,11 @@ public class Archiver implements Tool { public void fileSelected(File newbie) { if (newbie != null) { try { + // Force a .zip extension + // https://github.com/processing/processing/issues/2526 + if (!newbie.getName().toLowerCase().endsWith(".zip")) { + newbie = new File(newbie.getAbsolutePath() + ".zip"); + } //System.out.println(newbie); FileOutputStream zipOutputFile = new FileOutputStream(newbie); ZipOutputStream zos = new ZipOutputStream(zipOutputFile); diff --git a/app/src/processing/app/tools/ColorSelector.java b/app/src/processing/app/tools/ColorSelector.java index f600128d6..59cce7760 100644 --- a/app/src/processing/app/tools/ColorSelector.java +++ b/app/src/processing/app/tools/ColorSelector.java @@ -3,7 +3,7 @@ /* Part of the Processing project - http://processing.org - Copyright (c) 2006-12 Ben Fry and Casey Reas + Copyright (c) 2006-14 Ben Fry and Casey Reas This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License version 2 @@ -22,19 +22,11 @@ package processing.app.tools; import processing.app.*; -import processing.core.*; -import java.awt.BorderLayout; import java.awt.Color; -import java.awt.Container; -import java.awt.Cursor; -import java.awt.Dimension; -import java.awt.Graphics; +import java.awt.datatransfer.Clipboard; +import java.awt.datatransfer.StringSelection; import java.awt.event.*; -import javax.swing.*; -import javax.swing.border.*; -import javax.swing.event.*; -import javax.swing.text.*; /** @@ -45,597 +37,39 @@ import javax.swing.text.*; * auto-insert of colorMode() or fill() or stroke() code cuz we couldn't * decide on a good way to do this.. your contributions welcome). */ -public class ColorSelector implements Tool, DocumentListener { - -// Editor editor; +public class ColorSelector implements Tool { /** * Only create one instance, otherwise we'll have dozens of animation * threads going if you open/close a lot of editor windows. */ - static JFrame frame; - - int hue, saturation, brightness; // range 360, 100, 100 - int red, green, blue; // range 256, 256, 256 - - ColorRange range; - ColorSlider slider; - - JTextField hueField, saturationField, brightnessField; - JTextField redField, greenField, blueField; - - JTextField hexField; - - JPanel colorPanel; - + static ColorChooser selector; + public String getMenuTitle() { return Language.text("menu.tools.color_selector"); } public void init(Editor editor) { -// this.editor = editor; - if (frame == null) { - createFrame(); + + // Language.text("color_selector") + + if (selector == null) { + selector = new ColorChooser(editor, false, Color.WHITE, + "Copy", new ActionListener() { + + @Override + public void actionPerformed(ActionEvent e) { + Clipboard clipboard = Toolkit.getSystemClipboard(); + clipboard.setContents(new StringSelection(selector.getHexColor()), null); + } + }); } } - void createFrame() { - frame = new JFrame(Language.text("color_selector")); - frame.getContentPane().setLayout(new BorderLayout()); - - Box box = Box.createHorizontalBox(); - box.setBorder(new EmptyBorder(12, 12, 12, 12)); - - range = new ColorRange(); - range.init(); - Box rangeBox = new Box(BoxLayout.Y_AXIS); - rangeBox.setAlignmentY(0); - rangeBox.setBorder(BorderFactory.createBevelBorder(BevelBorder.LOWERED)); - rangeBox.add(range); - box.add(rangeBox); - box.add(Box.createHorizontalStrut(10)); - - slider = new ColorSlider(); - slider.init(); - Box sliderBox = new Box(BoxLayout.Y_AXIS); - sliderBox.setAlignmentY(0); - sliderBox.setBorder(BorderFactory.createBevelBorder(BevelBorder.LOWERED)); - sliderBox.add(slider); - box.add(sliderBox); - box.add(Box.createHorizontalStrut(10)); - - box.add(createColorFields()); - box.add(Box.createHorizontalStrut(10)); - - frame.getContentPane().add(box, BorderLayout.CENTER); - frame.pack(); - frame.setResizable(false); - - // these don't help either.. they fix the component size but - // leave a gap where the component is located - //range.setSize(256, 256); - //slider.setSize(256, 20); - - Dimension size = frame.getSize(); - Dimension screen = Toolkit.getScreenSize(); - frame.setLocation((screen.width - size.width) / 2, - (screen.height - size.height) / 2); - - frame.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE); - frame.addWindowListener(new WindowAdapter() { - public void windowClosing(WindowEvent e) { - frame.setVisible(false); - } - }); - Toolkit.registerWindowCloseKeys(frame.getRootPane(), new ActionListener() { - public void actionPerformed(ActionEvent actionEvent) { - frame.setVisible(false); - } - }); - - Toolkit.setIcon(frame); - - hueField.getDocument().addDocumentListener(this); - saturationField.getDocument().addDocumentListener(this); - brightnessField.getDocument().addDocumentListener(this); - redField.getDocument().addDocumentListener(this); - greenField.getDocument().addDocumentListener(this); - blueField.getDocument().addDocumentListener(this); - hexField.getDocument().addDocumentListener(this); - - hexField.setText("#FFFFFF"); - } - - public void run() { - frame.setVisible(true); - // You've got to be f--ing kidding me.. why did the following line - // get deprecated for the pile of s-- that follows it? - //frame.setCursor(Cursor.CROSSHAIR_CURSOR); - frame.setCursor(Cursor.getPredefinedCursor(Cursor.CROSSHAIR_CURSOR)); + selector.show(); } - - - public void changedUpdate(DocumentEvent e) { - //System.out.println("changed"); - } - - public void removeUpdate(DocumentEvent e) { - //System.out.println("remove"); - } - - - boolean updating; - - public void insertUpdate(DocumentEvent e) { - if (updating) return; // don't update forever recursively - updating = true; - - Document doc = e.getDocument(); - if (doc == hueField.getDocument()) { - hue = bounded(hue, hueField, 359); - updateRGB(); - updateHex(); - - } else if (doc == saturationField.getDocument()) { - saturation = bounded(saturation, saturationField, 99); - updateRGB(); - updateHex(); - - } else if (doc == brightnessField.getDocument()) { - brightness = bounded(brightness, brightnessField, 99); - updateRGB(); - updateHex(); - - } else if (doc == redField.getDocument()) { - red = bounded(red, redField, 255); - updateHSB(); - updateHex(); - - } else if (doc == greenField.getDocument()) { - green = bounded(green, greenField, 255); - updateHSB(); - updateHex(); - - } else if (doc == blueField.getDocument()) { - blue = bounded(blue, blueField, 255); - updateHSB(); - updateHex(); - - } else if (doc == hexField.getDocument()) { - String str = hexField.getText(); - if (str.startsWith("#")) { - str = str.substring(1); - } - while (str.length() < 6) { - str += "0"; - } - if (str.length() > 6) { - str = str.substring(0, 6); - } - updateRGB2(Integer.parseInt(str, 16)); - updateHSB(); - } - range.redraw(); - slider.redraw(); - colorPanel.repaint(); - updating = false; - } - - - /** - * Set the RGB values based on the current HSB values. - */ - protected void updateRGB() { - int rgb = Color.HSBtoRGB(hue / 359f, - saturation / 99f, - brightness / 99f); - updateRGB2(rgb); - } - - - /** - * Set the RGB values based on a calculated ARGB int. - * Used by both updateRGB() to set the color from the HSB values, - * and by updateHex(), to unpack the hex colors and assign them. - */ - protected void updateRGB2(int rgb) { - red = (rgb >> 16) & 0xff; - green = (rgb >> 8) & 0xff; - blue = rgb & 0xff; - - redField.setText(String.valueOf(red)); - greenField.setText(String.valueOf(green)); - blueField.setText(String.valueOf(blue)); - } - - - /** - * Set the HSB values based on the current RGB values. - */ - protected void updateHSB() { - float hsb[] = new float[3]; - Color.RGBtoHSB(red, green, blue, hsb); - - hue = (int) (hsb[0] * 359.0f); - saturation = (int) (hsb[1] * 99.0f); - brightness = (int) (hsb[2] * 99.0f); - - hueField.setText(String.valueOf(hue)); - saturationField.setText(String.valueOf(saturation)); - brightnessField.setText(String.valueOf(brightness)); - } - - - protected void updateHex() { - hexField.setText("#" + - PApplet.hex(red, 2) + - PApplet.hex(green, 2) + - PApplet.hex(blue, 2)); - } - - - /** - * Get the bounded value for a specific range. If the value is outside - * the max, you can't edit right away, so just act as if it's already - * been bounded and return the bounded value, then fire an event to set - * it to the value that was just returned. - */ - protected int bounded(int current, final JTextField field, final int max) { - String text = field.getText(); - if (text.length() == 0) { - return 0; - } - try { - int value = Integer.parseInt(text); - if (value > max) { - SwingUtilities.invokeLater(new Runnable() { - public void run() { - field.setText(String.valueOf(max)); - } - }); - return max; - } - return value; - - } catch (NumberFormatException e) { - return current; // should not be reachable - } - } - - - protected Container createColorFields() { - Box box = Box.createVerticalBox(); - box.setAlignmentY(0); - - colorPanel = new JPanel() { - public void paintComponent(Graphics g) { - g.setColor(new Color(red, green, blue)); - Dimension size = getSize(); - g.fillRect(0, 0, size.width, size.height); - } - }; - colorPanel.setBorder(BorderFactory.createBevelBorder(BevelBorder.LOWERED)); - Dimension dim = new Dimension(60, 40); - colorPanel.setMinimumSize(dim); - //colorPanel.setMaximumSize(dim); - //colorPanel.setPreferredSize(dim); - box.add(colorPanel); - box.add(Box.createVerticalStrut(10)); - - Box row; - - row = Box.createHorizontalBox(); - row.add(createFixedLabel("H")); - row.add(hueField = new NumberField(4, false)); - row.add(new JLabel(" \u00B0")); // degree symbol - row.add(Box.createHorizontalGlue()); - box.add(row); - box.add(Box.createVerticalStrut(5)); - - row = Box.createHorizontalBox(); - row.add(createFixedLabel("S")); - row.add(saturationField = new NumberField(4, false)); - row.add(new JLabel(" %")); - row.add(Box.createHorizontalGlue()); - box.add(row); - box.add(Box.createVerticalStrut(5)); - - row = Box.createHorizontalBox(); - row.add(createFixedLabel("B")); - row.add(brightnessField = new NumberField(4, false)); - row.add(new JLabel(" %")); - row.add(Box.createHorizontalGlue()); - box.add(row); - box.add(Box.createVerticalStrut(10)); - - // - - row = Box.createHorizontalBox(); - row.add(createFixedLabel("R")); - row.add(redField = new NumberField(4, false)); - row.add(Box.createHorizontalGlue()); - box.add(row); - box.add(Box.createVerticalStrut(5)); - - row = Box.createHorizontalBox(); - row.add(createFixedLabel("G")); - row.add(greenField = new NumberField(4, false)); - row.add(Box.createHorizontalGlue()); - box.add(row); - box.add(Box.createVerticalStrut(5)); - - row = Box.createHorizontalBox(); - row.add(createFixedLabel("B")); - row.add(blueField = new NumberField(4, false)); - row.add(Box.createHorizontalGlue()); - box.add(row); - box.add(Box.createVerticalStrut(10)); - - // - - row = Box.createHorizontalBox(); - row.add(createFixedLabel("")); - row.add(hexField = new NumberField(5, true)); - row.add(Box.createHorizontalGlue()); - box.add(row); - box.add(Box.createVerticalStrut(10)); - - box.add(Box.createVerticalGlue()); - return box; - } - - - int labelH; - - /** - * return a label of a fixed width - */ - protected JLabel createFixedLabel(String title) { - JLabel label = new JLabel(title); - if (labelH == 0) { - labelH = label.getPreferredSize().height; - } - Dimension dim = new Dimension(20, labelH); - label.setPreferredSize(dim); - label.setMinimumSize(dim); - label.setMaximumSize(dim); - return label; - } - - - public class ColorRange extends PApplet { - - static final int WIDE = 256; - static final int HIGH = 256; - - int lastX, lastY; - - - public void setup() { - size(WIDE, HIGH); //, P3D); - noLoop(); - - colorMode(HSB, 360, 256, 256); - noFill(); - rectMode(CENTER); - - loadPixels(); - } - - public void draw() { -// if ((g == null) || (g.pixels == null)) return; - if ((width != WIDE) || (height < HIGH)) { - //System.out.println("bad size " + width + " " + height); - return; - } - - int index = 0; - for (int j = 0; j < 256; j++) { - for (int i = 0; i < 256; i++) { - pixels[index++] = color(hue, i, 255 - j); - } - } - - updatePixels(); - stroke((brightness > 50) ? 0 : 255); - rect(lastX, lastY, 9, 9); - } - - public void mousePressed() { - updateMouse(); - } - - public void mouseDragged() { - updateMouse(); - } - - public void updateMouse() { - if ((mouseX >= 0) && (mouseX < 256) && - (mouseY >= 0) && (mouseY < 256)) { - int nsaturation = (int) (100 * (mouseX / 255.0f)); - int nbrightness = 100 - ((int) (100 * (mouseY / 255.0f))); - saturationField.setText(String.valueOf(nsaturation)); - brightnessField.setText(String.valueOf(nbrightness)); - - lastX = mouseX; - lastY = mouseY; - } - } - - public Dimension getPreferredSize() { - return new Dimension(WIDE, HIGH); - } - - public Dimension getMinimumSize() { - return new Dimension(WIDE, HIGH); - } - - public Dimension getMaximumSize() { - return new Dimension(WIDE, HIGH); - } - - public void keyPressed() { - if (key == ESC) { - ColorSelector.frame.setVisible(false); - // don't quit out of processing - // http://dev.processing.org/bugs/show_bug.cgi?id=1006 - key = 0; - } - } - } - - - public class ColorSlider extends PApplet { - - static final int WIDE = 20; - static final int HIGH = 256; - - public void setup() { - size(WIDE, HIGH); //, P3D); - colorMode(HSB, 255, 100, 100); - noLoop(); - loadPixels(); - } - - public void draw() { -// if ((g == null) || (g.pixels == null)) return; - if ((width != WIDE) || (height < HIGH)) { - //System.out.println("bad size " + width + " " + height); - return; - } - - int index = 0; - int sel = 255 - (int) (255 * (hue / 359f)); - for (int j = 0; j < 256; j++) { - int c = color(255 - j, 100, 100); - if (j == sel) c = 0xFF000000; - for (int i = 0; i < WIDE; i++) { - g.pixels[index++] = c; - } - } - updatePixels(); - } - - public void mousePressed() { - updateMouse(); - } - - public void mouseDragged() { - updateMouse(); - } - - public void updateMouse() { - if ((mouseX >= 0) && (mouseX < 256) && - (mouseY >= 0) && (mouseY < 256)) { - int nhue = 359 - (int) (359 * (mouseY / 255.0f)); - hueField.setText(String.valueOf(nhue)); - } - } - - public Dimension getPreferredSize() { - return new Dimension(WIDE, HIGH); - } - - public Dimension getMinimumSize() { - return new Dimension(WIDE, HIGH); - } - - public Dimension getMaximumSize() { - return new Dimension(WIDE, HIGH); - } - - public void keyPressed() { - if (key == ESC) { - ColorSelector.frame.setVisible(false); - // don't quit out of processing - // http://dev.processing.org/bugs/show_bug.cgi?id=1006 - key = 0; - } - } - } - - - /** - * Extension of JTextField that only allows numbers - */ - class NumberField extends JTextField { - - public boolean allowHex; - - public NumberField(int cols, boolean allowHex) { - super(cols); - this.allowHex = allowHex; - } - - protected Document createDefaultModel() { - return new NumberDocument(this); - } - - public Dimension getPreferredSize() { - if (!allowHex) { - return new Dimension(45, super.getPreferredSize().height); - } - return super.getPreferredSize(); - } - - public Dimension getMinimumSize() { - return getPreferredSize(); - } - - public Dimension getMaximumSize() { - return getPreferredSize(); - } - } - - - /** - * Document model to go with JTextField that only allows numbers. - */ - class NumberDocument extends PlainDocument { - - NumberField parentField; - - public NumberDocument(NumberField parentField) { - this.parentField = parentField; - //System.out.println("setting parent to " + parentSelector); - } - - public void insertString(int offs, String str, AttributeSet a) - throws BadLocationException { - - if (str == null) return; - - char chars[] = str.toCharArray(); - int charCount = 0; - // remove any non-digit chars - for (int i = 0; i < chars.length; i++) { - boolean ok = Character.isDigit(chars[i]); - if (parentField.allowHex) { - if ((chars[i] >= 'A') && (chars[i] <= 'F')) ok = true; - if ((chars[i] >= 'a') && (chars[i] <= 'f')) ok = true; - if ((offs == 0) && (i == 0) && (chars[i] == '#')) ok = true; - } - if (ok) { - if (charCount != i) { // shift if necessary - chars[charCount] = chars[i]; - } - charCount++; - } - } - super.insertString(offs, new String(chars, 0, charCount), a); - // can't call any sort of methods on the enclosing class here - // seems to have something to do with how Document objects are set up - } - } - - -// static public void main(String[] args) { -// ColorSelector cs = new ColorSelector(); -// cs.init(null); -// EventQueue.invokeLater(cs); -// } } diff --git a/app/src/processing/app/tools/CreateFont.java b/app/src/processing/app/tools/CreateFont.java index d3de7bd2c..d119e76d3 100644 --- a/app/src/processing/app/tools/CreateFont.java +++ b/app/src/processing/app/tools/CreateFont.java @@ -112,9 +112,11 @@ public class CreateFont extends JFrame implements Tool { // also ignore dialog, dialoginput, monospaced, serif, sansserif // getFontList is deprecated in 1.4, so this has to be used + //long t = System.currentTimeMillis(); GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment(); - Font fonts[] = ge.getAllFonts(); + Font[] fonts = ge.getAllFonts(); + //System.out.println("font startup took " + (System.currentTimeMillis() - t) + " ms"); if (false) { ArrayList fontList = new ArrayList(); diff --git a/app/src/processing/app/tools/ExportExamples.java b/app/src/processing/app/tools/ExportExamples.java deleted file mode 100644 index 65889cbad..000000000 --- a/app/src/processing/app/tools/ExportExamples.java +++ /dev/null @@ -1,158 +0,0 @@ -/* -package processing.app.tools; - -import java.io.*; -//import java.util.HashMap; - -import processing.app.*; -import processing.mode.java.JavaBuild; - -public class ExportExamples implements Tool { - static final String DELETE_TARGET = "export.delete_target_folder"; - static final String SEPARATE_JAR = "export.applet.separate_jar_files"; - -// HashMap errors; - Editor orig; - - // copy the files to processing.org/content/examples with their hierarchy intact - // that won't be checked into svn, even though they were before - -// File webroot; -// File templates; -// File xml; - - Base base; - File[] folders; - File outputFolder; - String examplesPath; - - - public void init(Editor editor) { - orig = editor; - base = editor.getBase(); - Mode mode = editor.getMode(); - folders = mode.getExampleCategoryFolders(); - examplesPath = mode.getExamplesFolder().getAbsolutePath(); - - // Not perfect, but will work for Casey and I - File desktop = new File(System.getProperty("user.home"), "Desktop"); - outputFolder = new File(desktop, "examples"); -// webroot = new File("/Users/fry/coconut/processing.web"); -// templates = new File(webroot, "templates"); - } - - - public void run() { - new Thread(new Runnable() { public void run() { - if (outputFolder.exists()) { - Base.showWarning("Try Again", "Please remove the examples folder from the desktop,\n" + - "because that's where I wanna put things.", null); - return; - } -// errors = new HashMap(); - boolean delete = Preferences.getBoolean(DELETE_TARGET); - Preferences.setBoolean(DELETE_TARGET, false); - boolean separate = Preferences.getBoolean(SEPARATE_JAR); - Preferences.setBoolean(SEPARATE_JAR, true); - - for (File folder : folders) { - if (!folder.getName().equals("Books")) { - handleFolder(folder); - } - } - - Preferences.setBoolean(DELETE_TARGET, delete); - Preferences.setBoolean(SEPARATE_JAR, separate); - orig.statusNotice("Finished exporting examples."); - } }).start(); - -// if (errors.size() > 0) { -// orig.statusError((errors.size() == 1 ? "One sketch" : (errors.size() + " sketches")) + " had errors."); -// } else { -// } -// for (String path : errors.keySet()) { -// System.err.println("Error: " -// } - } - - - public void handleFolder(File folder) { - File pdeFile = new File(folder, folder.getName() + ".pde"); - if (pdeFile.exists()) { - String pdePath = pdeFile.getAbsolutePath(); - Editor editor = base.handleOpen(pdePath); - if (editor != null) { - try { -// System.out.println(pdePath); - if (handle(editor)) { - base.handleClose(editor, false); - try { - Thread.sleep(20); - } catch (InterruptedException e) { } - } - } catch (Exception e) { - e.printStackTrace(); - // errors.put(pdePath, e); - // System.err.println("Error handling " + pdePath); - // e.printStackTrace(); - } - } - } else { // recurse into the folder - //System.out.println(" into " + folder.getAbsolutePath()); - File[] sub = folder.listFiles(); - for (File f : sub) { - if (f.isDirectory()) { - handleFolder(f); - } - } - } - } - - - public boolean handle(Editor editor) throws SketchException, IOException { - Sketch sketch = editor.getSketch(); - File sketchFolder = sketch.getFolder(); - String sketchPath = sketchFolder.getAbsolutePath(); - String uniquePath = sketchPath.substring(examplesPath.length()); - File sketchTarget = new File(outputFolder, uniquePath); - - // copy the PDE files so that they can be pulled in by the generator script -// File[] files = sketchFolder.listFiles(); -// for (File file : files) { -// if (file.getName().endsWith(".pde")) { -// Base.copyFile(file, new File(sketchTarget, file.getName())); -// } -// } - // no need to do this because the source files will be in 'applet' anyway - - // build the applet into this folder - File appletFolder = new File(sketchTarget, "applet"); - JavaBuild build = new JavaBuild(sketch); - boolean result = build.exportApplet(appletFolder); - - // Just one copy of core.jar into the root - File coreTarget = new File(outputFolder, "core.jar"); - File sketchCore = new File(appletFolder, "core.jar"); - if (!coreTarget.exists()) { - Base.copyFile(sketchCore, coreTarget); - } - sketchCore.delete(); - - File loadingTarget = new File(outputFolder, "loading.gif"); - if (!loadingTarget.exists()) { - Base.copyFile(new File(appletFolder, "loading.gif"), loadingTarget); - } - - new File(appletFolder, "index.html").delete(); - new File(appletFolder, "loading.gif").delete(); - new File(appletFolder, sketch.getName() + ".java").delete(); - - return result; - } - - - public String getMenuTitle() { - return "Export Examples"; - } -} -*/ \ No newline at end of file diff --git a/app/src/processing/app/tools/FixEncoding.java b/app/src/processing/app/tools/FixEncoding.java deleted file mode 100644 index e58768ab3..000000000 --- a/app/src/processing/app/tools/FixEncoding.java +++ /dev/null @@ -1,109 +0,0 @@ -/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */ - -/* - Part of the Processing project - http://processing.org - - Copyright (c) 2008-11 Ben Fry and Casey Reas - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, version 2. - - This program 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 for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software Foundation, - Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -*/ - -package processing.app.tools; - -import java.io.BufferedReader; -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; -import java.io.InputStreamReader; - -import javax.swing.JOptionPane; - -import processing.app.*; - - -public class FixEncoding implements Tool { - Editor editor; - - - public String getMenuTitle() { - return "Fix Encoding & Reload"; - } - - - public void init(Editor editor) { - this.editor = editor; - } - - - public void run() { - Sketch sketch = editor.getSketch(); - //SketchCode code = sketch.current; - - if (sketch.isModified()) { - int result = - JOptionPane.showConfirmDialog(editor, - "Discard all changes and reload sketch?", - "Fix Encoding & Reload", - JOptionPane.YES_NO_OPTION, - JOptionPane.QUESTION_MESSAGE); - - if (result == JOptionPane.NO_OPTION) { - return; - } - } - try { - for (int i = 0; i < sketch.getCodeCount(); i++) { - SketchCode code = sketch.getCode(i); - code.setProgram(loadWithLocalEncoding(code.getFile())); - code.setModified(true); // yes, because we want them to save this - } - // Update the currently visible program with its code - editor.setText(sketch.getCurrentCode().getProgram()); - - } catch (IOException e) { - String msg = - "An error occurred while trying to fix the file encoding.\n" + - "Do not attempt to save this sketch as it may overwrite\n" + - "the old version. Use Open to re-open the sketch and try again.\n" + - e.getMessage(); - Base.showWarning("Fix Encoding & Reload", msg, e); - } - } - - - protected String loadWithLocalEncoding(File file) throws IOException { - // FileReader uses the default encoding, which is what we want. - String encoding = System.getProperty("file.encoding"); - if (Base.isMacOS()) { - // Remember that time that Apple decided to change the file encoding - // in later releases of Java? That was really awesome. - if (encoding.equals("UTF-8")) { - // Changing from UTF-8 to UTF-8 isn't going to help much. Argh. - encoding = "MacRoman"; - } - } - FileInputStream fis = new FileInputStream(file); - InputStreamReader isr = new InputStreamReader(fis, encoding); - BufferedReader reader = new BufferedReader(isr); - - StringBuffer buffer = new StringBuffer(); - String line = null; - while ((line = reader.readLine()) != null) { - buffer.append(line); - buffer.append('\n'); - } - reader.close(); - return buffer.toString(); - } -} \ No newline at end of file diff --git a/app/src/processing/app/tools/InstallCommander.java b/app/src/processing/app/tools/InstallCommander.java index d3eb69e4e..1967c4e30 100644 --- a/app/src/processing/app/tools/InstallCommander.java +++ b/app/src/processing/app/tools/InstallCommander.java @@ -81,30 +81,29 @@ public class InstallCommander implements Tool { writer.println("#!/bin/sh"); String[] jarList = new String[] { - "lib/pde.jar", - "lib/antlr.jar", - "lib/jna.jar", - "lib/ant.jar", - "lib/ant-launcher.jar", + "pde.jar", + "antlr.jar", + "jna.jar", + "ant.jar", + "ant-launcher.jar", // extra libraries for new JDI setup - "lib/org-netbeans-swing-outline.jar", - "lib/com.ibm.icu_4.4.2.v20110823.jar", - "lib/jdi.jar", - "lib/jdimodel.jar", - "lib/org.eclipse.osgi_3.8.1.v20120830-144521.jar", + "org-netbeans-swing-outline.jar", + "com.ibm.icu_4.4.2.v20110823.jar", + "jdi.jar", + "jdimodel.jar", + "org.eclipse.osgi_3.8.1.v20120830-144521.jar", "core/library/core.jar" }; String classPath = PApplet.join(jarList, ":"); - String javaRoot = System.getProperty("javaroot"); + //String javaRoot = System.getProperty("javaroot"); + String javaRoot = Base.getContentFile(".").getCanonicalPath(); writer.println("cd \"" + javaRoot + "\" && " + - "/usr/libexec/java_home " + - "--request " + - "--version 1.6 " + - "--exec java " + - "-cp " + classPath + + Base.getJavaPath() + + " -Djna.nosys=true" + + " -cp \"" + classPath + "\"" + " processing.mode.java.Commander \"$@\""); writer.flush(); writer.close(); diff --git a/app/src/processing/mode/java/AutoFormat.java b/app/src/processing/mode/java/AutoFormat.java index f7a86a0bf..91d8a7cec 100644 --- a/app/src/processing/mode/java/AutoFormat.java +++ b/app/src/processing/mode/java/AutoFormat.java @@ -38,56 +38,62 @@ import processing.core.PApplet; *

* After some further digging, this code in fact appears to be a modified * version of Jason Pell's GPLed "Java Beautifier" class found - * here. - * Which is itself based on code from Van Di-Han Ho from - * here. + * here, + * which is itself based on code from Van Di-Han Ho from + * here. * [Ben Fry, August 2009] */ public class AutoFormat implements Formatter { private char[] chars; private final StringBuilder buf = new StringBuilder(); - private final StringBuilder result = new StringBuilder(); private int indentValue; private boolean EOF; - private boolean a_flg, e_flg, if_flg, s_flag, q_flg; - private boolean s_if_flg[]; + private boolean a_flg, if_flg, s_flag, elseFlag; + + /** Number of ? entered without exiting at : of a?b:c structures. */ + private int conditionalLevel; + private int pos; -// private int lineNumber; - private int s_level[]; private int c_level; - private int sp_flg[][]; - private int s_ind[][]; - private int s_if_lev[]; - private int if_lev, level; - private int ind[]; - private int paren; - private int p_flg[]; + private int[][] sp_flg; + private int[][] s_ind; + private int if_lev; + + /** Number of curly brackets entered and not exited. */ + private int level; + + /** Number of parentheses entered and not exited. */ + private int parenLevel; + + private int[] ind; + private int[] p_flg; private char l_char; - private int ct; - private int s_tabs[][]; + private int[][] s_tabs; private boolean jdoc_flag; private char cc; private int tabs; private char c; + private char lastNonWhitespace = 0; private final Stack castFlags = new Stack(); - private void comment() { - final boolean save_s_flg = s_flag; + private void handleMultiLineComment() { + final boolean saved_s_flag = s_flag; - buf.append(c = next()); // extra char + char ch; + buf.append(ch = nextChar()); // extra char while (true) { - buf.append(c = next()); - while ((c != '/')) { - if (c == '\n') { -// lineNumber++; + buf.append(ch = nextChar()); + while (ch != '/') { + if (ch == '\n') { +// lineNumber++; writeIndentedComment(); s_flag = true; } - buf.append(c = next()); + buf.append(ch = nextChar()); } if (buf.length() >= 2 && buf.charAt(buf.length() - 2) == '*') { jdoc_flag = false; @@ -96,50 +102,65 @@ public class AutoFormat implements Formatter { } writeIndentedComment(); - s_flag = save_s_flg; + s_flag = saved_s_flag; jdoc_flag = false; return; } - - private char get_string() { - char ch; - while (true) { - buf.append(ch = next()); - if (ch == '\\') { - buf.append(next()); - continue; - } - if (ch == '\'' || ch == '"') { - buf.append(cc = next()); - while (!EOF && cc != ch) { - if (cc == '\\') { - buf.append(next()); - } - buf.append(cc = next()); - } - continue; - } - if (ch == '\n') { - writeIndentedLine(); - a_flg = true; - continue; - } - return ch; + + private void handleSingleLineComment() { + char ch = nextChar(); + while (ch != '\n') { + buf.append(ch); + ch = nextChar(); } +// lineNumber++; + writeIndentedLine(); + s_flag = true; } + +// /** +// * Transfers buf to result until a character is reached that +// * is neither \, \n, or in a string. \n is not written, but +// * writeIndentedLine is called on finding it. +// * @return The first character not listed above. +// */ +// private char get_string() { +// char ch1, ch2; +// while (true) { +// buf.append(ch1 = nextChar()); +// if (ch1 == '\\') { +// buf.append(nextChar()); +// } else if (ch1 == '\'' || ch1 == '\"') { +// buf.append(ch2 = nextChar()); +// while (!EOF && ch2 != ch1) { +// if (ch2 == '\\') { +// // Next char is escaped, ignore. +// buf.append(nextChar()); +// } +// buf.append(ch2 = nextChar()); +// } +// } else if (ch1 == '\n') { +// writeIndentedLine(); +// a_flg = true; +// } else { +// return ch1; +// } +// } +// } + private void writeIndentedLine() { if (buf.length() == 0) { if (s_flag) { - s_flag = a_flg = false; + s_flag = a_flg = elseFlag = false; } return; } if (s_flag) { - final boolean shouldIndent = (tabs > 0) && (buf.charAt(0) != '{') - && a_flg; + final boolean shouldIndent = + (tabs > 0) && (buf.charAt(0) != '{') && a_flg; if (shouldIndent) { tabs++; } @@ -150,9 +171,25 @@ public class AutoFormat implements Formatter { } a_flg = false; } + if (elseFlag) { + if (lastNonSpaceChar() == '}') { + trimRight(result); + result.append(' '); + } + elseFlag = false; + } result.append(buf); buf.setLength(0); } + + + private char lastNonSpaceChar() { + for (int i = result.length() - 1; i >= 0; i--) { + char c_i = result.charAt(i); + if (c_i != ' ' && c_i != '\n') return c_i; + } + return 0; + } private void writeIndentedComment() { @@ -170,7 +207,7 @@ public class AutoFormat implements Formatter { jdoc_flag = true; } if (buf.charAt(i) == '/' && buf.charAt(i + 1) == '*') { - if (saved_s_flag && prev() != ';') { + if (saved_s_flag && getLastNonWhitespace() != ';') { result.append(buf.substring(i)); } else { result.append(buf); @@ -187,18 +224,6 @@ public class AutoFormat implements Formatter { } - private void handleSingleLineComment() { - c = next(); - while (c != '\n') { - buf.append(c); - c = next(); - } -// lineNumber++; - writeIndentedLine(); - s_flag = true; - } - - private void printIndentation() { if (tabs < 0) { tabs = 0; @@ -208,7 +233,7 @@ public class AutoFormat implements Formatter { } final int spaces = tabs * indentValue; for (int k = 0; k < spaces; k++) { - result.append(" "); + result.append(' '); } } @@ -221,10 +246,7 @@ public class AutoFormat implements Formatter { } - private char lastNonWhitespace = 0; - - - private int prev() { + private int getLastNonWhitespace() { return lastNonWhitespace; } @@ -239,33 +261,32 @@ public class AutoFormat implements Formatter { if (pos == chars.length - 1) { EOF = true; } else { - pos--; // reset for next() + pos--; // reset for nextChar() } } - private char next() { + private char nextChar() { if (EOF) { - return 0; + return '\0'; } pos++; - final char c; + char retVal; if (pos < chars.length) { - c = chars[pos]; - if (!Character.isWhitespace(c)) - lastNonWhitespace = c; + retVal = chars[pos]; + if (!Character.isWhitespace(retVal)) + lastNonWhitespace = retVal; } else { - c = 0; + retVal = '\0'; } - if (pos == chars.length - 1) { + if (pos == chars.length-1) { EOF = true; } - return c; + return retVal; } - /* else processing */ - private void gotelse() { + private void gotElse() { tabs = s_tabs[c_level][if_lev]; p_flg[level] = sp_flg[c_level][if_lev]; ind[level] = s_ind[c_level][if_lev]; @@ -273,23 +294,22 @@ public class AutoFormat implements Formatter { } - /* read to new_line */ - private boolean getnl() { + private boolean readUntilNewLine() { final int savedTabs = tabs; char c = peek(); while (!EOF && (c == '\t' || c == ' ')) { - buf.append(next()); + buf.append(nextChar()); c = peek(); } if (c == '/') { - buf.append(next()); + buf.append(nextChar()); c = peek(); if (c == '*') { - buf.append(next()); - comment(); + buf.append(nextChar()); + handleMultiLineComment(); } else if (c == '/') { - buf.append(next()); + buf.append(nextChar()); handleSingleLineComment(); return true; } @@ -298,7 +318,7 @@ public class AutoFormat implements Formatter { c = peek(); if (c == '\n') { // eat it - next(); + nextChar(); // lineNumber++; tabs = savedTabs; return true; @@ -327,24 +347,25 @@ public class AutoFormat implements Formatter { public String format(final String source) { final String normalizedText = source.replaceAll("\r", ""); - final String cleanText = normalizedText - + (normalizedText.endsWith("\n") ? "" : "\n"); + final String cleanText = + normalizedText + (normalizedText.endsWith("\n") ? "" : "\n"); result.setLength(0); indentValue = Preferences.getInteger("editor.tabs.size"); -// lineNumber = 0; - q_flg = e_flg = a_flg = if_flg = false; +// lineNumber = 0; + boolean forFlag = a_flg = if_flg = false; s_flag = true; - c_level = if_lev = level = paren = 0; + int forParenthLevel = 0; + conditionalLevel = parenLevel = c_level = if_lev = level = 0; tabs = 0; jdoc_flag = false; - s_level = new int[10]; + int[] s_level = new int[10]; sp_flg = new int[20][10]; s_ind = new int[20][10]; - s_if_lev = new int[10]; - s_if_flg = new boolean[10]; + int[] s_if_lev = new int[10]; + boolean[] s_if_flg = new boolean[10]; ind = new int[10]; p_flg = new int[10]; s_tabs = new int[20][10]; @@ -352,10 +373,10 @@ public class AutoFormat implements Formatter { chars = cleanText.toCharArray(); // lineNumber = 1; - EOF = false; // set in next() when EOF + EOF = false; // set in nextChar() when EOF while (!EOF) { - c = next(); + c = nextChar(); switch (c) { default: buf.append(c); @@ -371,15 +392,12 @@ public class AutoFormat implements Formatter { case ' ': case '\t': - if (lookup("else")) { - gotelse(); + elseFlag = lookup("else"); + if (elseFlag) { + gotElse(); if ((!s_flag) || buf.length() > 0) { buf.append(c); } -// // issue https://github.com/processing/processing/issues/364 -// s_flag = false; -// trimRight(result); -// result.append(" "); writeIndentedLine(); s_flag = false; @@ -395,9 +413,9 @@ public class AutoFormat implements Formatter { if (EOF) { break; } - e_flg = lookup("else"); - if (e_flg) { - gotelse(); + elseFlag = lookup("else"); + if (elseFlag) { + gotElse(); } if (lookup_com("//")) { final char lastChar = buf.charAt(buf.length() - 1); @@ -409,18 +427,18 @@ public class AutoFormat implements Formatter { writeIndentedLine(); result.append("\n"); s_flag = true; - if (e_flg) { + if (elseFlag) { p_flg[level]++; tabs++; - } else if (prev() == l_char) { + } else if (getLastNonWhitespace() == l_char) { a_flg = true; } break; case '{': - if (lookup("else")) { - gotelse(); - } + elseFlag = lookup("else"); + if (elseFlag) gotElse(); + if (s_if_lev.length == c_level) { s_if_lev = PApplet.expand(s_if_lev); s_if_flg = PApplet.expand(s_if_flg); @@ -440,9 +458,9 @@ public class AutoFormat implements Formatter { buf.append(" "); buf.append(c); writeIndentedLine(); - getnl(); + readUntilNewLine(); writeIndentedLine(); - //fprintf(outfil,"\n"); + result.append("\n"); tabs++; s_flag = true; @@ -475,11 +493,12 @@ public class AutoFormat implements Formatter { printIndentation(); result.append(c); if (peek() == ';') { - result.append(next()); + result.append(nextChar()); } - getnl(); + + readUntilNewLine(); writeIndentedLine(); - result.append("\n"); + result.append('\n'); s_flag = true; if (c_level < s_level[level]) { if (level > 0) { @@ -497,21 +516,21 @@ public class AutoFormat implements Formatter { case '"': case '\'': buf.append(c); - cc = next(); + cc = nextChar(); while (!EOF && cc != c) { buf.append(cc); if (cc == '\\') { - buf.append(cc = next()); + buf.append(cc = nextChar()); } if (cc == '\n') { // lineNumber++; writeIndentedLine(); s_flag = true; } - cc = next(); + cc = nextChar(); } buf.append(cc); - if (getnl()) { + if (readUntilNewLine()) { l_char = cc; // push a newline into the stream chars[pos--] = '\n'; @@ -519,13 +538,21 @@ public class AutoFormat implements Formatter { break; case ';': + if (forFlag) { + // This is like a comma. + trimRight(buf); + buf.append("; "); + // Not non-whitespace: allow \n. + advanceToNonSpace(); + break; + } buf.append(c); writeIndentedLine(); if (p_flg[level] > 0 && ind[level] == 0) { tabs -= p_flg[level]; p_flg[level] = 0; } - getnl(); + readUntilNewLine(); writeIndentedLine(); result.append("\n"); s_flag = true; @@ -541,26 +568,39 @@ public class AutoFormat implements Formatter { case '\\': buf.append(c); - buf.append(next()); + buf.append(nextChar()); break; case '?': - q_flg = true; + conditionalLevel++; buf.append(c); break; case ':': - buf.append(c); + // Java 8 :: operator. if (peek() == ':') { writeIndentedLine(); - result.append(next()); + result.append(c).append(nextChar()); break; } - if (q_flg) { - q_flg = false; + // End a?b:c structures. + else if (conditionalLevel>0) { + conditionalLevel--; + buf.append(c); break; } + + else if (forFlag) { + trimRight(buf); + buf.append(" : "); + // Not to non-whitespace: allow \n. + advanceToNonSpace(); + break; + } + + buf.append(c); + if (!lookup("default") && !lookup("case")) { s_flag = false; writeIndentedLine(); @@ -570,26 +610,27 @@ public class AutoFormat implements Formatter { tabs++; } if (peek() == ';') { - result.append(next()); + result.append(nextChar()); } - getnl(); + readUntilNewLine(); writeIndentedLine(); - result.append("\n"); + result.append('\n'); s_flag = true; break; case '/': - final char la = peek(); - if (la == '/') { - buf.append(c).append(next()); + final char next = peek(); + if (next == '/') { + // call nextChar to move on. + buf.append(c).append(nextChar()); handleSingleLineComment(); result.append("\n"); - } else if (la == '*') { + } else if (next == '*') { if (buf.length() > 0) { writeIndentedLine(); } - buf.append(c).append(next()); - comment(); + buf.append(c).append(nextChar()); + handleMultiLineComment(); } else { buf.append(c); } @@ -599,15 +640,23 @@ public class AutoFormat implements Formatter { final boolean isCast = castFlags.isEmpty() ? false : castFlags.pop(); - paren--; - if (paren < 0) { - paren = 0; + parenLevel--; + + // If we're further back than the start of a for loop, we've + // left it. + if (forFlag && forParenthLevel > parenLevel) { + forFlag = false; } + + if (parenLevel < 0) { + parenLevel = 0; + } + buf.append(c); writeIndentedLine(); - if (getnl()) { + if (readUntilNewLine()) { chars[pos--] = '\n'; - if (paren != 0) { + if (parenLevel != 0) { a_flg = true; } else if (tabs > 0 && !isCast) { p_flg[level]++; @@ -630,42 +679,16 @@ public class AutoFormat implements Formatter { } buf.append(c); - paren++; + parenLevel++; + // isFor says "is it the start of a for?". If it is, we set forFlag and + // forParenthLevel. If it is not parenth_lvl was incremented above and + // that's it. if (isFor) { - // TODO(feinberg): handle new-style for loops - c = get_string(); - while (c != ';' && c != ':') { - c = get_string(); + if (!forFlag) { + forParenthLevel = parenLevel; + forFlag = true; } - ct = 0; - int for_done = 0; - while (for_done == 0) { - c = get_string(); - while (c != ')') { - if (c == '(') { - ct++; - } - c = get_string(); - } - if (ct != 0) { - ct--; - } else { - for_done = 1; - } - } // endwhile for_done - paren--; - if (paren < 0) { - paren = 0; - } - writeIndentedLine(); - if (getnl()) { - chars[pos--] = '\n'; - p_flg[level]++; - tabs++; - ind[level] = 0; - } - break; } else if (isIf) { writeIndentedLine(); s_tabs[c_level][if_lev] = tabs; diff --git a/app/src/processing/mode/java/Commander.java b/app/src/processing/mode/java/Commander.java index fdf55848a..43a327632 100644 --- a/app/src/processing/mode/java/Commander.java +++ b/app/src/processing/mode/java/Commander.java @@ -48,6 +48,7 @@ public class Commander implements RunnerListener { static final String forceArg = "--force"; static final String outputArg = "--output="; static final String exportApplicationArg = "--export"; + static final String noJavaArg = "--no-java"; static final String platformArg = "--platform="; static final String bitsArg = "--bits="; // static final String preferencesArg = "--preferences="; @@ -61,38 +62,19 @@ public class Commander implements RunnerListener { static final int EXPORT = 4; Sketch sketch; - + PrintStream systemOut; PrintStream systemErr; static public void main(String[] args) { - /* - if (args == null || args.length == 0) { -// System.out.println(System.getProperty("user.dir")); - args = new String[] { - "--export", -// "--build", -// "--run", -// "--present", - "--force", -// "--platform=windows", - "--platform=macosx", - "--bits=64", - "--sketch=/Users/fry/coconut/processing/java/examples/Basics/Lights/Directional", -// "--sketch=/Users/fry/coconut/sketchbook/sketchbook_libraries_test", - "--output=/Users/fry/Desktop/test-build" - }; - } - */ - // Do this early so that error messages go to the console Base.setCommandLine(); // init the platform so that prefs and other native code is ready to go Base.initPlatform(); // make sure a full JDK is installed Base.initRequirements(); - + // launch command line handler new Commander(args); } @@ -107,20 +89,25 @@ public class Commander implements RunnerListener { boolean force = false; // replace that no good output folder // String preferencesPath = null; int platform = PApplet.platform; // default to this platform - int platformBits = 0; +// int platformBits = 0; + int platformBits = Base.getNativeBits(); int task = HELP; + boolean embedJava = true; // Turns out the output goes as MacRoman or something else useless. // http://code.google.com/p/processing/issues/detail?id=1418 try { systemOut = new PrintStream(System.out, true, "UTF-8"); systemErr = new PrintStream(System.err, true, "UTF-8"); - + } catch (UnsupportedEncodingException e) { e.printStackTrace(); System.exit(1); } - + +// File preferencesFile = Base.getSettingsFile("preferences.txt"); +// System.out.println("Preferences file at " + preferencesFile.getAbsolutePath()); + for (String arg : args) { if (arg.length() == 0) { // ignore it, just the crappy shell script @@ -146,23 +133,28 @@ public class Commander implements RunnerListener { } else if (arg.equals(exportApplicationArg)) { task = EXPORT; + } else if (arg.equals(noJavaArg)) { + embedJava = false; + } else if (arg.startsWith(platformArg)) { - String platformStr = arg.substring(platformArg.length()); - platform = Base.getPlatformIndex(platformStr); - if (platform == -1) { - complainAndQuit(platformStr + " should instead be " + - "'windows', 'macosx', or 'linux'.", true); - } + complainAndQuit("The --platform option has been removed from Processing 2.1.", false); +// String platformStr = arg.substring(platformArg.length()); +// platform = Base.getPlatformIndex(platformStr); +// if (platform == -1) { +// complainAndQuit(platformStr + " should instead be " + +// "'windows', 'macosx', or 'linux'.", true); +// } } else if (arg.startsWith(bitsArg)) { - String bitsStr = arg.substring(bitsArg.length()); - if (bitsStr.equals("32")) { - platformBits = 32; - } else if (bitsStr.equals("64")) { - platformBits = 64; - } else { - complainAndQuit("Bits should be either 32 or 64, not " + bitsStr, true); - } + complainAndQuit("The --bits option has been removed from Processing 2.1.", false); +// String bitsStr = arg.substring(bitsArg.length()); +// if (bitsStr.equals("32")) { +// platformBits = 32; +// } else if (bitsStr.equals("64")) { +// platformBits = 64; +// } else { +// complainAndQuit("Bits should be either 32 or 64, not " + bitsStr, true); +// } } else if (arg.startsWith(sketchArg)) { sketchPath = arg.substring(sketchArg.length()); @@ -212,7 +204,7 @@ public class Commander implements RunnerListener { Base.removeDir(outputFolder); } else { complainAndQuit("The output folder already exists. " + - "Use --force to remove it.", false); + "Use --force to remove it.", false); } } @@ -273,11 +265,11 @@ public class Commander implements RunnerListener { // if (platformBits == 0) { // platformBits = Base.getNativeBits(); // } - if (platformBits == 0 && - Library.hasMultipleArch(platform, build.getImportedLibraries())) { - complainAndQuit("This sketch can be exported for 32- or 64-bit, please specify one.", true); - } - success = build.exportApplication(outputFolder, platform, platformBits); +// if (platformBits == 0 && +// Library.hasMultipleArch(platform, build.getImportedLibraries())) { +// complainAndQuit("This sketch can be exported for 32- or 64-bit, please specify one.", true); +// } + success = build.exportApplication(outputFolder, platform, platformBits, embedJava); } } } @@ -289,6 +281,7 @@ public class Commander implements RunnerListener { } catch (SketchException re) { statusError(re); + System.exit(1); } catch (IOException e) { e.printStackTrace(); @@ -316,15 +309,18 @@ public class Commander implements RunnerListener { if (codeIndex != -1) { // format the runner exception like emacs //blah.java:2:10:2:13: Syntax Error: This is a big error message + // Emacs doesn't like the double line thing coming from Java + // https://github.com/processing/processing/issues/2158 String filename = sketch.getCode(codeIndex).getFileName(); int line = re.getCodeLine() + 1; int column = re.getCodeColumn() + 1; //if (column == -1) column = 0; // TODO if column not specified, should just select the whole line. + // But what's the correct syntax for that? systemErr.println(filename + ":" + line + ":" + column + ":" + line + ":" + column + ":" + " " + re.getMessage()); - + } else { // no line number, pass the trace along to the user exception.printStackTrace(); } @@ -363,11 +359,12 @@ public class Commander implements RunnerListener { out.println("--present Preprocess, compile, and run a sketch full screen."); out.println(); out.println("--export Export an application."); - out.println("--platform Specify the platform (export to application only)."); - out.println(" Should be one of 'windows', 'macosx', or 'linux'."); - out.println("--bits Must be specified if libraries are used that are"); - out.println(" 32- or 64-bit specific such as the OpenGL library."); - out.println(" Otherwise specify 0 or leave it out."); + out.println("--no-java Do not embed Java. Use at your own risk!"); +// out.println("--platform Specify the platform (export to application only)."); +// out.println(" Should be one of 'windows', 'macosx', or 'linux'."); +// out.println("--bits Must be specified if libraries are used that are"); +// out.println(" 32- or 64-bit specific such as the OpenGL library."); +// out.println(" Otherwise specify 0 or leave it out."); out.println(); } @@ -391,4 +388,4 @@ public class Commander implements RunnerListener { public boolean isHalted() { return false; } -} \ No newline at end of file +} diff --git a/app/src/processing/mode/java/JavaBuild.java b/app/src/processing/mode/java/JavaBuild.java index 43112e227..46c18816d 100644 --- a/app/src/processing/mode/java/JavaBuild.java +++ b/app/src/processing/mode/java/JavaBuild.java @@ -26,8 +26,15 @@ import java.io.*; import java.util.*; import java.util.zip.*; +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.DefaultLogger; +import org.apache.tools.ant.Project; +import org.apache.tools.ant.ProjectHelper; + import processing.app.*; +import processing.app.exec.ProcessHelper; import processing.core.*; +import processing.data.XML; import processing.mode.java.preproc.*; // Would you believe there's a java.lang.Compiler class? I wouldn't. @@ -397,7 +404,7 @@ public class JavaBuild { throw new SketchException(ex.toString()); } - // grab the imports from the code just preproc'd + // grab the imports from the code just preprocessed importedLibraries = new ArrayList(); Library core = mode.getCoreLibrary(); @@ -408,10 +415,21 @@ public class JavaBuild { // System.out.println("extra imports: " + result.extraImports); for (String item : result.extraImports) { +// System.out.println("item = '" + item + "'"); // remove things up to the last dot int dot = item.lastIndexOf('.'); // http://dev.processing.org/bugs/show_bug.cgi?id=1145 String entry = (dot == -1) ? item : item.substring(0, dot); +// System.out.print(entry + " => "); + + if (item.startsWith("static ")) { + // import static - https://github.com/processing/processing/issues/8 + // Remove more stuff. + int dot2 = item.lastIndexOf('.'); + entry = entry.substring(7, (dot2 == -1) ? entry.length() : dot2); +// System.out.println(entry); + } + // System.out.println("library searching for " + entry); Library library = mode.getLibrary(entry); // System.out.println(" found " + library); @@ -427,7 +445,7 @@ public class JavaBuild { // If someone insists on unnecessarily repeating the code folder // import, don't show an error for it. if (codeFolderPackages != null) { - String itemPkg = item.substring(0, item.lastIndexOf('.')); + String itemPkg = entry; for (String pkg : codeFolderPackages) { if (pkg.equals(itemPkg)) { found = true; @@ -435,7 +453,7 @@ public class JavaBuild { } } } - if (ignorableImport(item)) { + if (ignorableImport(entry + '.')) { found = true; } if (!found) { @@ -531,6 +549,8 @@ public class JavaBuild { if (pkg.startsWith("processing.data.")) return true; if (pkg.startsWith("processing.event.")) return true; if (pkg.startsWith("processing.opengl.")) return true; + +// if (pkg.startsWith("com.jogamp.")) return true; // // ignore core, data, and opengl packages // String[] coreImports = preprocessor.getCoreImports(); @@ -1093,43 +1113,68 @@ public class JavaBuild { File folder = null; for (String platformName : PConstants.platformNames) { int platform = Base.getPlatformIndex(platformName); + + // Can only embed Java on the native platform + boolean embedJava = (platform == PApplet.platform) && + Preferences.getBoolean("export.application.embed_java"); + if (Preferences.getBoolean("export.application.platform." + platformName)) { if (Library.hasMultipleArch(platform, importedLibraries)) { // export the 32-bit version folder = new File(sketch.getFolder(), "application." + platformName + "32"); - if (!exportApplication(folder, platform, 32)) { + if (!exportApplication(folder, platform, 32, embedJava && Base.getNativeBits() == 32)) { return false; } // export the 64-bit version folder = new File(sketch.getFolder(), "application." + platformName + "64"); - if (!exportApplication(folder, platform, 64)) { + if (!exportApplication(folder, platform, 64, embedJava && Base.getNativeBits() == 64)) { return false; } } else { // just make a single one for this platform folder = new File(sketch.getFolder(), "application." + platformName); - if (!exportApplication(folder, platform, 0)) { + if (!exportApplication(folder, platform, 0, embedJava)) { return false; } } } } + + /* + File folder = null; + String platformName = Base.getPlatformName(); + boolean embedJava = Preferences.getBoolean("export.application.embed_java"); + if (Library.hasMultipleArch(PApplet.platform, importedLibraries)) { + if (Base.getNativeBits() == 32) { + // export the 32-bit version + folder = new File(sketch.getFolder(), "application." + platformName + "32"); + if (!exportApplication(folder, PApplet.platform, 32, embedJava)) { + return false; + } + } else if (Base.getNativeBits() == 64) { + // export the 64-bit version + folder = new File(sketch.getFolder(), "application." + platformName + "64"); + if (!exportApplication(folder, PApplet.platform, 64, embedJava)) { + return false; + } + } + } else { // just make a single one for this platform + folder = new File(sketch.getFolder(), "application." + platformName); + if (!exportApplication(folder, PApplet.platform, 0, embedJava)) { + return false; + } + } + */ return true; // all good } -// public boolean exportApplication(String destPath, -// String platformName, -// int exportBits) throws IOException, RunnerException { -// return exportApplication(destPath, Base.getPlatformIndex(platformName), exportBits); -// } - - /** * Export to application without GUI. Also called by the Commander. */ protected boolean exportApplication(File destFolder, int exportPlatform, - int exportBits) throws IOException, SketchException { + int exportBits, + boolean embedJava) throws IOException, SketchException { // TODO this should probably be a dialog box instead of a warning // on the terminal. And the message should be written better than this. // http://code.google.com/p/processing/issues/detail?id=884 @@ -1160,13 +1205,53 @@ public class JavaBuild { /// on macosx, need to copy .app skeleton since that's /// also where the jar files will be placed File dotAppFolder = null; + String jvmRuntime = ""; + String jdkPath = null; if (exportPlatform == PConstants.MACOSX) { dotAppFolder = new File(destFolder, sketch.getName() + ".app"); -// String APP_SKELETON = "skeleton.app"; - //File dotAppSkeleton = new File(folder, APP_SKELETON); - File dotAppSkeleton = mode.getContentFile("application/template.app"); - Base.copyDir(dotAppSkeleton, dotAppFolder); + File contentsOrig = new File(Base.getJavaHome(), "../../../../.."); + + if (embedJava) { + File jdkFolder = new File(Base.getJavaHome(), "../../.."); + String jdkFolderName = jdkFolder.getCanonicalFile().getName(); + jvmRuntime = "JVMRuntime\n " + jdkFolderName + ""; + jdkPath = new File(dotAppFolder, "Contents/PlugIns/" + jdkFolderName + ".jdk").getAbsolutePath(); + } + + File contentsFolder = new File(dotAppFolder, "Contents"); + contentsFolder.mkdirs(); + + // Info.plist will be written later + + // set the jar folder to a different location than windows/linux + //jarFolder = new File(dotAppFolder, "Contents/Resources/Java"); + jarFolder = new File(contentsFolder, "Java"); + + File macosFolder = new File(contentsFolder, "MacOS"); + macosFolder.mkdirs(); + Base.copyFile(new File(contentsOrig, "MacOS/Processing"), + new File(contentsFolder, "MacOS/" + sketch.getName())); + + File pkgInfo = new File(contentsFolder, "PkgInfo"); + PrintWriter writer = PApplet.createWriter(pkgInfo); + writer.println("APPL????"); + writer.flush(); + writer.close(); + + // Use faster(?) native copy here (also to do sym links) + if (embedJava) { + Base.copyDirNative(new File(contentsOrig, "PlugIns"), + new File(contentsFolder, "PlugIns")); + } + + File resourcesFolder = new File(contentsFolder, "Resources"); + Base.copyDir(new File(contentsOrig, "Resources/en.lproj"), + new File(resourcesFolder, "en.lproj")); + Base.copyFile(mode.getContentFile("application/sketch.icns"), + new File(resourcesFolder, "sketch.icns")); + + /* String stubName = "Contents/MacOS/JavaApplicationStub"; // need to set the stub to executable // will work on osx or *nix, but just dies on windows, oh well.. @@ -1189,17 +1274,25 @@ public class JavaBuild { String stubPath = stubFile.getAbsolutePath(); Runtime.getRuntime().exec(new String[] { "chmod", "+x", stubPath }); } - - // set the jar folder to a different location than windows/linux - jarFolder = new File(dotAppFolder, "Contents/Resources/Java"); + */ + } else if (exportPlatform == PConstants.LINUX) { + if (embedJava) { + Base.copyDirNative(Base.getJavaHome(), new File(destFolder, "java")); + } + + } else if (exportPlatform == PConstants.WINDOWS) { + if (embedJava) { + Base.copyDir(Base.getJavaHome(), new File(destFolder, "java")); + } } - /// make the jar folder (windows and linux) + /// make the jar folder (all platforms) if (!jarFolder.exists()) jarFolder.mkdirs(); + /* /// on windows, copy the exe file if (exportPlatform == PConstants.WINDOWS) { @@ -1208,7 +1301,8 @@ public class JavaBuild { File batFile = new File(destFolder, sketch.getName() + ".bat"); PrintWriter writer = PApplet.createWriter(batFile); writer.println("@echo off"); - writer.println("java -Djava.ext.dirs=lib -Djava.library.path=lib " + sketch.getName()); + String javaPath = embedJava ? ".\\java\\bin\\java.exe" : "java"; + writer.println(javaPath + " -Djna.nosys=true -Djava.ext.dirs=lib -Djava.library.path=lib " + sketch.getName()); writer.flush(); writer.close(); } else { @@ -1216,8 +1310,9 @@ public class JavaBuild { new File(destFolder, sketch.getName() + ".exe")); } } + */ - + /// start copying all jar files Vector jarListVector = new Vector(); @@ -1271,7 +1366,6 @@ public class JavaBuild { String includes = Base.contentsToClassPath(sketch.getCodeFolder()); // Use tokens to get rid of extra blanks, which causes huge exports String[] codeList = PApplet.splitTokens(includes, File.pathSeparator); -// String cp = ""; for (int i = 0; i < codeList.length; i++) { if (codeList[i].toLowerCase().endsWith(".jar") || codeList[i].toLowerCase().endsWith(".zip")) { @@ -1283,7 +1377,6 @@ public class JavaBuild { // cp += codeList[i] + File.pathSeparator; } } -// packClassPathIntoZipFile(cp, zos, zipFileContents); // this was double adding the code folder prior to 2.0a2 } zos.flush(); @@ -1292,15 +1385,6 @@ public class JavaBuild { jarListVector.add(sketch.getName() + ".jar"); -// /// add core.jar to the jar destination folder -// -// File bagelJar = Base.isMacOS() ? -// Base.getContentFile("core.jar") : -// Base.getContentFile("lib/core.jar"); -// Base.copyFile(bagelJar, new File(jarFolder, "core.jar")); -// jarListVector.add("core.jar"); - - /// add contents of 'library' folders to the export for (Library library : importedLibraries) { // add each item from the library folder / export list to the output @@ -1313,38 +1397,13 @@ public class JavaBuild { "a big fat lie and does not exist."); } else if (exportFile.isDirectory()) { - //System.err.println("Ignoring sub-folder \"" + exportList[i] + "\""); -// if (exportPlatform == PConstants.MACOSX) { -// // For OS X, copy subfolders to Contents/Resources/Java Base.copyDir(exportFile, new File(jarFolder, exportName)); -// } else { -// // For other platforms, just copy the folder to the same directory -// // as the application. -// Base.copyDir(exportFile, new File(destFolder, exportName)); -// } } else if (exportName.toLowerCase().endsWith(".zip") || exportName.toLowerCase().endsWith(".jar")) { Base.copyFile(exportFile, new File(jarFolder, exportName)); jarListVector.add(exportName); - // old style, prior to 2.0a2 -// } else if ((exportPlatform == PConstants.MACOSX) && -// (exportFile.getName().toLowerCase().endsWith(".jnilib"))) { -// // jnilib files can be placed in Contents/Resources/Java -// Base.copyFile(exportFile, new File(jarFolder, exportName)); -// -// } else { -// // copy the file to the main directory.. prolly a .dll or something -// Base.copyFile(exportFile, new File(destFolder, exportName)); -// } - - // first 2.0a2 attempt, until below... -// } else if (exportPlatform == PConstants.MACOSX) { -// Base.copyFile(exportFile, new File(jarFolder, exportName)); -// -// } else { -// Base.copyFile(exportFile, new File(destFolder, exportName)); } else { // Starting with 2.0a2 put extra export files (DLLs, plugins folder, // anything else for libraries) inside lib or Contents/Resources/Java @@ -1380,31 +1439,30 @@ public class JavaBuild { /// figure out run options for the VM - String runOptions = Preferences.get("run.options"); + List runOptions = new ArrayList(); if (Preferences.getBoolean("run.options.memory")) { - runOptions += " -Xms" + - Preferences.get("run.options.memory.initial") + "m"; - runOptions += " -Xmx" + - Preferences.get("run.options.memory.maximum") + "m"; + runOptions.add("-Xms" + Preferences.get("run.options.memory.initial") + "m"); + runOptions.add("-Xmx" + Preferences.get("run.options.memory.maximum") + "m"); } -// if (exportPlatform == PConstants.MACOSX) { -// // If no bits specified (libs are all universal, or no native libs) -// // then exportBits will be 0, and can be controlled via "Get Info". -// // Otherwise, need to specify the bits as a VM option. -// if (exportBits == 32) { -// runOptions += " -d32"; -// } else if (exportBits == 64) { -// runOptions += " -d64"; -// } -// } + // https://github.com/processing/processing/issues/2239 + runOptions.add("-Djna.nosys=true"); + /// macosx: write out Info.plist (template for classpath, etc) if (exportPlatform == PConstants.MACOSX) { - String PLIST_TEMPLATE = "template.plist"; + StringBuilder runOptionsXML = new StringBuilder(); + for (String opt : runOptions) { + runOptionsXML.append(" "); + runOptionsXML.append(opt); + runOptionsXML.append(""); + runOptionsXML.append('\n'); + } + + String PLIST_TEMPLATE = "Info.plist.tmpl"; File plistTemplate = new File(sketch.getFolder(), PLIST_TEMPLATE); if (!plistTemplate.exists()) { - plistTemplate = mode.getContentFile("application/template.plist"); + plistTemplate = mode.getContentFile("application/" + PLIST_TEMPLATE); } File plistFile = new File(dotAppFolder, "Contents/Info.plist"); PrintWriter pw = PApplet.createWriter(plistFile); @@ -1414,33 +1472,22 @@ public class JavaBuild { if (lines[i].indexOf("@@") != -1) { StringBuffer sb = new StringBuffer(lines[i]); int index = 0; - while ((index = sb.indexOf("@@vmoptions@@")) != -1) { - sb.replace(index, index + "@@vmoptions@@".length(), - runOptions); + while ((index = sb.indexOf("@@jvm_runtime@@")) != -1) { + sb.replace(index, index + "@@jvm_runtime@@".length(), + jvmRuntime); + } + while ((index = sb.indexOf("@@jvm_options_list@@")) != -1) { + sb.replace(index, index + "@@jvm_options_list@@".length(), + runOptionsXML.toString()); } while ((index = sb.indexOf("@@sketch@@")) != -1) { sb.replace(index, index + "@@sketch@@".length(), sketch.getName()); } - while ((index = sb.indexOf("@@classpath@@")) != -1) { - sb.replace(index, index + "@@classpath@@".length(), - exportClassPath.toString()); - } while ((index = sb.indexOf("@@lsuipresentationmode@@")) != -1) { sb.replace(index, index + "@@lsuipresentationmode@@".length(), Preferences.getBoolean("export.application.fullscreen") ? "4" : "0"); } - while ((index = sb.indexOf("@@lsarchitecturepriority@@")) != -1) { - // More about this mess: http://support.apple.com/kb/TS2827 - // First default to exportBits == 0 case - String arch = "x86_64\n i386"; - if (exportBits == 32) { - arch = "i386"; - } else if (exportBits == 64) { - arch = "x86_64"; - } - sb.replace(index, index + "@@lsarchitecturepriority@@".length(), arch); - } lines[i] = sb.toString(); } @@ -1450,17 +1497,93 @@ public class JavaBuild { pw.flush(); pw.close(); + // attempt to code sign if the Xcode tools appear to be installed + if (Base.isMacOS() && new File("/usr/bin/codesign_allocate").exists()) { + if (embedJava) { + ProcessHelper.ffs("codesign", "--force", "--sign", "-", jdkPath); + } + String appPath = dotAppFolder.getAbsolutePath(); + ProcessHelper.ffs("codesign", "--force", "--sign", "-", appPath); + } + } else if (exportPlatform == PConstants.WINDOWS) { - File argsFile = new File(destFolder + "/lib/args.txt"); - PrintWriter pw = PApplet.createWriter(argsFile); + File buildFile = new File(destFolder, "launch4j-build.xml"); + File configFile = new File(destFolder, "launch4j-config.xml"); - // Since this is only on Windows, make sure we use Windows CRLF - pw.print(runOptions + "\r\n"); - pw.print(sketch.getName() + "\r\n"); - pw.print(exportClassPath); + XML project = new XML("project"); + XML target = project.addChild("target"); + target.setString("name", "windows"); + + XML taskdef = target.addChild("taskdef"); + taskdef.setString("name", "launch4j"); + taskdef.setString("classname", "net.sf.launch4j.ant.Launch4jTask"); + String launchPath = mode.getContentFile("application/launch4j").getAbsolutePath(); + taskdef.setString("classpath", launchPath + "/launch4j.jar:" + launchPath + "/lib/xstream.jar"); + + XML launch4j = target.addChild("launch4j"); + // not all launch4j options are available when embedded inside the ant + // build file (i.e. the icon param doesn't work), so use a config file + // + launch4j.setString("configFile", configFile.getAbsolutePath()); + + XML config = new XML("launch4jConfig"); + config.addChild("headerType").setContent("gui"); + config.addChild("dontWrapJar").setContent("true"); + config.addChild("downloadUrl").setContent("http://java.com/download"); + + File exeFile = new File(destFolder, sketch.getName() + ".exe"); + config.addChild("outfile").setContent(exeFile.getAbsolutePath()); + + File iconFile = mode.getContentFile("application/sketch.ico"); + config.addChild("icon").setContent(iconFile.getAbsolutePath()); - pw.flush(); - pw.close(); + XML clazzPath = config.addChild("classPath"); + clazzPath.addChild("mainClass").setContent(sketch.getName()); + for (String jarName : jarList) { + clazzPath.addChild("cp").setContent("lib/" + jarName); + } + XML jre = config.addChild("jre"); + if (embedJava) { + jre.addChild("path").setContent("java"); + } + jre.addChild("minVersion").setContent("1.7.0_40"); + for (String opt : runOptions) { + jre.addChild("opt").setContent(opt); + } + + /* + XML config = launch4j.addChild("config"); + config.setString("headerType", "gui"); + File exeFile = new File(destFolder, sketch.getName() + ".exe"); + config.setString("outfile", exeFile.getAbsolutePath()); + config.setString("dontWrapJar", "true"); + config.setString("jarPath", "lib\\" + jarList[0]); + + File iconFile = mode.getContentFile("application/sketch.ico"); + config.addChild("icon").setContent(iconFile.getAbsolutePath()); + + XML clazzPath = config.addChild("classPath"); + clazzPath.setString("mainClass", sketch.getName()); + for (int i = 1; i < jarList.length; i++) { + String jarName = jarList[i]; + clazzPath.addChild("cp").setContent("lib\\" + jarName); + } + XML jre = config.addChild("jre"); + jre.setString("minVersion", "1.7.0_40"); + //PApplet.join(runOptions.toArray(new String[0]), " ") + for (String opt : runOptions) { + jre.addChild("opt").setContent(opt); + } + */ + + config.save(configFile); + project.save(buildFile); + if (!buildWindowsLauncher(buildFile, "windows")) { + // don't delete the build file, might be useful for debugging + return false; + } + configFile.delete(); + buildFile.delete(); } else { File shellScript = new File(destFolder, sketch.getName()); @@ -1473,7 +1596,13 @@ public class JavaBuild { pw.print("APPDIR=$(dirname \"$0\")\n"); // more posix compliant // another fix for bug #234, LD_LIBRARY_PATH ignored on some platforms //ps.print("LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$APPDIR\n"); - pw.print("java " + Preferences.get("run.options") + + if (embedJava) { + // https://github.com/processing/processing/issues/2349 + pw.print("$APPDIR/java/bin/"); + } + String runOptionsStr = + PApplet.join(runOptions.toArray(new String[0]), " "); + pw.print("java " + runOptionsStr + " -Djava.library.path=\"$APPDIR:$APPDIR/lib\"" + " -cp \"" + exportClassPath + "\"" + " " + sketch.getName() + " \"$@\"\n"); @@ -1512,23 +1641,66 @@ public class JavaBuild { } - /// remove the .class files from the export folder. -// for (File file : classFiles) { -// if (!file.delete()) { -// Base.showWarning("Could not delete", -// file.getName() + " could not \n" + -// "be deleted from the applet folder. \n" + -// "You'll need to remove it by hand.", null); -// } -// } - // these will now be removed automatically via the temp folder deleteOnExit() - - /// goodbye return true; } + /** + * Run the launch4j build.xml file through ant to create the exe. + * Most of this code was lifted from Android mode. + */ + protected boolean buildWindowsLauncher(File buildFile, String target) { + Project p = new Project(); + String path = buildFile.getAbsolutePath().replace('\\', '/'); + p.setUserProperty("ant.file", path); + + // deals with a problem where javac error messages weren't coming through + p.setUserProperty("build.compiler", "extJavac"); + + // too chatty + /* + // try to spew something useful to the console + final DefaultLogger consoleLogger = new DefaultLogger(); + consoleLogger.setErrorPrintStream(System.err); + consoleLogger.setOutputPrintStream(System.out); + // WARN, INFO, VERBOSE, DEBUG + consoleLogger.setMessageOutputLevel(Project.MSG_ERR); + p.addBuildListener(consoleLogger); + */ + + DefaultLogger errorLogger = new DefaultLogger(); + ByteArrayOutputStream errb = new ByteArrayOutputStream(); + PrintStream errp = new PrintStream(errb); + errorLogger.setErrorPrintStream(errp); + ByteArrayOutputStream outb = new ByteArrayOutputStream(); + PrintStream outp = new PrintStream(outb); + errorLogger.setOutputPrintStream(outp); + errorLogger.setMessageOutputLevel(Project.MSG_INFO); + p.addBuildListener(errorLogger); + + try { + p.fireBuildStarted(); + p.init(); + final ProjectHelper helper = ProjectHelper.getProjectHelper(); + p.addReference("ant.projectHelper", helper); + helper.parse(p, buildFile); + p.executeTarget(target); + return true; + + } catch (final BuildException e) { + // Send a "build finished" event to the build listeners for this project. + p.fireBuildFinished(e); + + String out = new String(outb.toByteArray()); + String err = new String(errb.toByteArray()); + System.out.println(out); + System.err.println(err); + } + return false; + } + + protected void addManifest(ZipOutputStream zos) throws IOException { ZipEntry entry = new ZipEntry("META-INF/MANIFEST.MF"); zos.putNextEntry(entry); diff --git a/app/src/processing/mode/java/JavaEditor.java b/app/src/processing/mode/java/JavaEditor.java index 3bb214a59..155ef546c 100644 --- a/app/src/processing/mode/java/JavaEditor.java +++ b/app/src/processing/mode/java/JavaEditor.java @@ -251,31 +251,33 @@ public class JavaEditor extends Editor { toolbar.deactivate(JavaToolbar.EXPORT); } - + +// JPanel presentColorPanel; +// JTextField presentColorPanel; + protected boolean exportApplicationPrompt() throws IOException, SketchException { JPanel panel = new JPanel(); panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS)); panel.add(Box.createVerticalStrut(6)); - //Box panel = Box.createVerticalBox(); - - //Box labelBox = Box.createHorizontalBox(); +// Box panel = Box.createVerticalBox(); +// Box labelBox = Box.createHorizontalBox(); // String msg = "Click Export to Application to create a standalone, " + // "double-clickable application for the selected plaforms."; - // String msg = "Export to Application creates a standalone, \n" + // "double-clickable application for the selected plaforms."; String line1 = Language.text("export.description.line1"); String line2 = Language.text("export.description.line2"); + //String line2 = "standalone application for the current plaform."; JLabel label1 = new JLabel(line1, SwingConstants.CENTER); JLabel label2 = new JLabel(line2, SwingConstants.CENTER); label1.setAlignmentX(Component.LEFT_ALIGNMENT); label2.setAlignmentX(Component.LEFT_ALIGNMENT); -// label1.setAlignmentX(); -// label2.setAlignmentX(0); panel.add(label1); panel.add(label2); - int wide = label2.getPreferredSize().width; + // The longer line is different between Windows and OS X. +// int wide = Math.max(label1.getPreferredSize().width, +// label2.getPreferredSize().width); panel.add(Box.createVerticalStrut(12)); final JCheckBox windowsButton = new JCheckBox("Windows"); @@ -288,7 +290,6 @@ public class JavaEditor extends Editor { }); final JCheckBox macosxButton = new JCheckBox("Mac OS X"); - //macosxButton.setMnemonic(KeyEvent.VK_M); macosxButton.setSelected(Preferences.getBoolean("export.application.platform.macosx")); macosxButton.addItemListener(new ItemListener() { public void itemStateChanged(ItemEvent e) { @@ -315,14 +316,15 @@ public class JavaEditor extends Editor { platformPanel.setBorder(new TitledBorder(Language.text("export.platforms"))); //Dimension goodIdea = new Dimension(wide, platformPanel.getPreferredSize().height); //platformPanel.setMaximumSize(goodIdea); - wide = Math.max(wide, platformPanel.getPreferredSize().width); +// wide = Math.max(wide, platformPanel.getPreferredSize().width); platformPanel.setAlignmentX(Component.LEFT_ALIGNMENT); panel.add(platformPanel); - -// Box indentPanel = Box.createHorizontalBox(); -// indentPanel.add(Box.createHorizontalStrut(new JCheckBox().getPreferredSize().width)); + int divWidth = platformPanel.getPreferredSize().width; + + //int indent = new JCheckBox().getPreferredSize().width; + int indent = 0; + final JCheckBox showStopButton = new JCheckBox(Language.text("export.options.show_stop_button")); - //showStopButton.setMnemonic(KeyEvent.VK_S); showStopButton.setSelected(Preferences.getBoolean("export.application.stop")); showStopButton.addItemListener(new ItemListener() { public void itemStateChanged(ItemEvent e) { @@ -330,9 +332,7 @@ public class JavaEditor extends Editor { } }); showStopButton.setEnabled(Preferences.getBoolean("export.application.fullscreen")); - showStopButton.setBorder(new EmptyBorder(3, 13, 6, 13)); -// indentPanel.add(showStopButton); -// indentPanel.setAlignmentX(Component.LEFT_ALIGNMENT); + showStopButton.setBorder(new EmptyBorder(3, 13 + indent, 6, 13)); final JCheckBox fullScreenButton = new JCheckBox(Language.text("export.options.fullscreen")); //fullscreenButton.setMnemonic(KeyEvent.VK_F); @@ -346,63 +346,200 @@ public class JavaEditor extends Editor { }); fullScreenButton.setBorder(new EmptyBorder(3, 13, 3, 13)); - JPanel optionPanel = new JPanel(); - optionPanel.setLayout(new BoxLayout(optionPanel, BoxLayout.Y_AXIS)); - optionPanel.add(fullScreenButton); - optionPanel.add(showStopButton); -// optionPanel.add(indentPanel); - optionPanel.setBorder(new TitledBorder(Language.text("export.options"))); - wide = Math.max(wide, platformPanel.getPreferredSize().width); - //goodIdea = new Dimension(wide, optionPanel.getPreferredSize().height); - optionPanel.setAlignmentX(Component.LEFT_ALIGNMENT); - //optionPanel.setMaximumSize(goodIdea); - panel.add(optionPanel); + JPanel presentPanel = new JPanel(); + presentPanel.setLayout(new BoxLayout(presentPanel, BoxLayout.Y_AXIS)); + Box fullScreenBox = Box.createHorizontalBox(); + fullScreenBox.add(fullScreenButton); + + /* + //run.present.stop.color +// presentColorPanel = new JTextField(); +// presentColorPanel.setFocusable(false); +// presentColorPanel.setEnabled(false); + presentColorPanel = new JPanel() { + public void paintComponent(Graphics g) { + g.setColor(Preferences.getColor("run.present.bgcolor")); + Dimension size = getSize(); + g.fillRect(0, 0, size.width, size.height); + } + }; + presentColorPanel.setBorder(BorderFactory.createBevelBorder(BevelBorder.LOWERED)); +// presentColorPanel.setBorder(BorderFactory.createBevelBorder(BevelBorder.RAISED)); + presentColorPanel.setMaximumSize(new Dimension(30, 20)); + fullScreenBox.add(presentColorPanel); + */ + fullScreenBox.add(new ColorPreference("run.present.bgcolor")); + //presentPanel.add(fullScreenButton); + fullScreenBox.add(Box.createHorizontalStrut(10)); + fullScreenBox.add(Box.createHorizontalGlue()); - Dimension good; - //label1, label2, platformPanel, optionPanel - good = new Dimension(wide, label1.getPreferredSize().height); - label1.setMaximumSize(good); - good = new Dimension(wide, label2.getPreferredSize().height); - label2.setMaximumSize(good); - good = new Dimension(wide, platformPanel.getPreferredSize().height); - platformPanel.setMaximumSize(good); - good = new Dimension(wide, optionPanel.getPreferredSize().height); - optionPanel.setMaximumSize(good); - -// JPanel actionPanel = new JPanel(); -// optionPanel.setLayout(new BoxLayout(optionPanel, BoxLayout.X_AXIS)); -// optionPanel.add(Box.createHorizontalGlue()); - -// final JDialog frame = new JDialog(editor, "Export to Application"); - -// JButton cancelButton = new JButton("Cancel"); -// cancelButton.addActionListener(new ActionListener() { -// public void actionPerformed(ActionEvent e) { -// frame.dispose(); -// return false; + presentPanel.add(fullScreenBox); + +// presentColorPanel.addMouseListener(new MouseAdapter() { +// public void mousePressed(MouseEvent e) { +// new ColorListener("run.present.bgcolor"); // } // }); - // Add the buttons in platform-specific order -// if (PApplet.platform == PConstants.MACOSX) { -// optionPanel.add(cancelButton); -// optionPanel.add(exportButton); -// } else { -// optionPanel.add(exportButton); -// optionPanel.add(cancelButton); -// } + Box showStopBox = Box.createHorizontalBox(); + showStopBox.add(showStopButton); + showStopBox.add(new ColorPreference("run.present.stop.color")); + showStopBox.add(Box.createHorizontalStrut(10)); + showStopBox.add(Box.createHorizontalGlue()); + presentPanel.add(showStopBox); + + //presentPanel.add(showStopButton); +// presentPanel.add(Box.createHorizontalStrut(10)); +// presentPanel.add(Box.createHorizontalGlue()); + presentPanel.setBorder(new TitledBorder("Full Screen")); +// wide = Math.max(wide, platformPanel.getPreferredSize().width); + presentPanel.setAlignmentX(Component.LEFT_ALIGNMENT); + panel.add(presentPanel); + +// Dimension good; +// good = new Dimension(wide, label1.getPreferredSize().height); +// label1.setMaximumSize(good); +// good = new Dimension(wide, label2.getPreferredSize().height); +// label2.setMaximumSize(good); +// good = new Dimension(wide, presentPanel.getPreferredSize().height); + + // + + JPanel embedPanel = new JPanel(); + embedPanel.setLayout(new BoxLayout(embedPanel, BoxLayout.Y_AXIS)); + + String platformName = null; + if (Base.isMacOS()) { + platformName = "Mac OS X"; + } else if (Base.isWindows()) { + platformName = "Windows (" + Base.getNativeBits() + "-bit)"; + } else if (Base.isLinux()) { + platformName = "Linux (" + Base.getNativeBits() + "-bit)"; + } + + boolean embed = Preferences.getBoolean("export.application.embed_java"); + final String embedWarning = + "

" + +// "" + + "Embedding Java will make the " + platformName + " application " + + "larger, but it will be far more likely to work. " + + "Users on other platforms will need to install Java 7."; + final String nopeWarning = + "
" + +// "" + + "Users on all platforms will have to install the latest " + + "version of Java 7 from http://java.com/download. " + + "
 "; + //"from java.com/download."; + final JLabel warningLabel = new JLabel(embed ? embedWarning : nopeWarning); + warningLabel.addMouseListener(new MouseAdapter() { + public void mousePressed(MouseEvent event) { + Base.openURL("http://java.com/download"); + } + }); + warningLabel.setBorder(new EmptyBorder(3, 13 + indent, 3, 13)); + + final JCheckBox embedJavaButton = + new JCheckBox("Embed Java for " + platformName); + embedJavaButton.setSelected(embed); + embedJavaButton.addItemListener(new ItemListener() { + public void itemStateChanged(ItemEvent e) { + boolean selected = embedJavaButton.isSelected(); + Preferences.setBoolean("export.application.embed_java", selected); + if (selected) { + warningLabel.setText(embedWarning); + } else { + warningLabel.setText(nopeWarning); + } + } + }); + embedJavaButton.setBorder(new EmptyBorder(3, 13, 3, 13)); + + embedPanel.add(embedJavaButton); + embedPanel.add(warningLabel); + embedPanel.setBorder(new TitledBorder("Embed Java")); + panel.add(embedPanel); + + // + + if (Base.isMacOS()) { + JPanel signPanel = new JPanel(); + signPanel.setLayout(new BoxLayout(signPanel, BoxLayout.Y_AXIS)); + signPanel.setBorder(new TitledBorder("Code Signing")); + + // gatekeeper: http://support.apple.com/kb/ht5290 + // for developers: https://developer.apple.com/developer-id/ + String thePain = + //"" + + "In recent versions of OS X, Apple has introduced the \u201CGatekeeper\u201D system, " + + "which makes it more difficult to run applications like those exported from Processing. "; + + if (new File("/usr/bin/codesign_allocate").exists()) { + thePain += + "This application will be \u201Cself-signed\u201D which means that Finder may report that the " + + "application is from an \u201Cunidentified developer\u201D. If the application will not " + + "run, try right-clicking the app and selecting Open from the pop-up menu. Or you can visit " + + "System Preferences \u2192 Security & Privacy and select Allow apps downloaded from: anywhere. "; + } else { + thePain += + "Gatekeeper requires applications to be \u201Csigned\u201D, or they will be reported as damaged. " + + "To prevent this message, install Xcode (and the Command Line Tools) from the App Store, or visit " + + "System Preferences \u2192 Security & Privacy and select Allow apps downloaded from: anywhere. "; + } + thePain += + "To avoid the messages entirely, manually code sign your app. " + + "For more information: https://developer.apple.com/developer-id/"; + + // xattr -d com.apple.quarantine thesketch.app + + //signPanel.add(new JLabel(thePain)); + //JEditorPane area = new JEditorPane("text/html", thePain); + //JTextPane area = new JEditorPane("text/html", thePain); + +// JTextArea area = new JTextArea(thePain); +// area.setBackground(null); +// area.setFont(new Font("Dialog", Font.PLAIN, 10)); +// area.setLineWrap(true); +// area.setWrapStyleWord(true); + // Are you f-king serious, Java API developers? + JLabel area = new JLabel("
" + thePain + "
"); + + area.setBorder(new EmptyBorder(3, 13, 3, 13)); +// area.setPreferredSize(new Dimension(embedPanel.getPreferredSize().width, 100)); +// area.setPreferredSize(new Dimension(300, 200)); + signPanel.add(area); +// signPanel.add(Box.createHorizontalGlue()); + signPanel.setAlignmentX(Component.LEFT_ALIGNMENT); + + area.addMouseListener(new MouseAdapter() { + public void mousePressed(MouseEvent event) { + Base.openURL("https://developer.apple.com/developer-id/"); + } + }); + + panel.add(signPanel); + } + //System.out.println(panel.getPreferredSize()); +// panel.setMinimumSize(new Dimension(316, 461)); +// panel.setPreferredSize(new Dimension(316, 461)); +// panel.setMaximumSize(new Dimension(316, 461)); + + // + String[] options = { Language.text("prompt.export"), Language.text("prompt.cancel") }; + final JOptionPane optionPane = new JOptionPane(panel, JOptionPane.PLAIN_MESSAGE, - //JOptionPane.QUESTION_MESSAGE, JOptionPane.YES_NO_OPTION, null, options, options[0]); + final JDialog dialog = new JDialog(this, Language.text("export"), true); dialog.setContentPane(optionPane); - +// System.out.println(optionPane.getLayout()); + optionPane.addPropertyChangeListener(new PropertyChangeListener() { public void propertyChange(PropertyChangeEvent e) { String prop = e.getPropertyName(); @@ -410,20 +547,24 @@ public class JavaEditor extends Editor { if (dialog.isVisible() && (e.getSource() == optionPane) && (prop.equals(JOptionPane.VALUE_PROPERTY))) { - //If you were going to check something - //before closing the window, you'd do - //it here. + // If you were going to check something before + // closing the window, you'd do it here. dialog.setVisible(false); } } }); dialog.pack(); +// System.out.println("after pack: " + panel.getPreferredSize()); +// dialog.setSize(optionPane.getPreferredSize()); dialog.setResizable(false); - + + // Center the window in the middle of the editor Rectangle bounds = getBounds(); dialog.setLocation(bounds.x + (bounds.width - dialog.getSize().width) / 2, bounds.y + (bounds.height - dialog.getSize().height) / 2); dialog.setVisible(true); + + //System.out.println(panel.getSize()); Object value = optionPane.getValue(); if (value.equals(options[0])) { @@ -435,7 +576,90 @@ public class JavaEditor extends Editor { return false; } + /* + Color bgcolor = Preferences.getColor("run.present.bgcolor"); + final ColorChooser c = new ColorChooser(JavaEditor.this, true, bgcolor, + "Select", new ActionListener() { + + @Override + public void actionPerformed(ActionEvent e) { + Preferences.setColor("run.present.bgcolor", c.getColor()); + } + }); + */ + /* + class ColorListener implements ActionListener { + ColorChooser chooser; + String prefName; + + public ColorListener(String prefName) { + this.prefName = prefName; + Color color = Preferences.getColor(prefName); + chooser = new ColorChooser(JavaEditor.this, true, color, "Select", this); + chooser.show(); + } + + @Override + public void actionPerformed(ActionEvent e) { + Color color = chooser.getColor(); + Preferences.setColor(prefName, color); +// presentColorPanel.setBackground(color); + presentColorPanel.repaint(); + chooser.hide(); + } + } + */ + + class ColorPreference extends JPanel implements ActionListener { + ColorChooser chooser; + String prefName; + + public ColorPreference(String pref) { + prefName = pref; + + setBorder(BorderFactory.createBevelBorder(BevelBorder.LOWERED)); + setPreferredSize(new Dimension(30, 20)); + setMaximumSize(new Dimension(30, 20)); + + addMouseListener(new MouseAdapter() { + public void mouseReleased(MouseEvent e) { + Color color = Preferences.getColor(prefName); + chooser = new ColorChooser(JavaEditor.this, true, color, "Select", ColorPreference.this); + chooser.show(); + } + }); + } + + public void paintComponent(Graphics g) { + g.setColor(Preferences.getColor(prefName)); + Dimension size = getSize(); + g.fillRect(0, 0, size.width, size.height); + } + + public void actionPerformed(ActionEvent e) { + Color color = chooser.getColor(); + Preferences.setColor(prefName, color); + //presentColorPanel.repaint(); + repaint(); + chooser.hide(); + } + } + + +// protected void selectColor(String prefName) { +// Color color = Preferences.getColor(prefName); +// final ColorChooser chooser = new ColorChooser(JavaEditor.this, true, color, +// "Select", new ActionListener() { +// +// @Override +// public void actionPerformed(ActionEvent e) { +// Preferences.setColor(prefName, c.getColor()); +// } +// }); +// } + + /** * Checks to see if the sketch has been modified, and if so, * asks the user to save the sketch or cancel the export. diff --git a/app/src/processing/mode/java/runner/Runner.java b/app/src/processing/mode/java/runner/Runner.java index c7a0599ff..314e37f0d 100644 --- a/app/src/processing/mode/java/runner/Runner.java +++ b/app/src/processing/mode/java/runner/Runner.java @@ -123,11 +123,7 @@ public class Runner implements MessageConsumer { } -// public void launch(String appletClassName, boolean presenting) { -// this.appletClassName = appletClassName; public boolean launchVirtualMachine(boolean presenting) { -// this.presenting = presenting; - String[] vmParams = getMachineParams(); String[] sketchParams = getSketchParams(presenting); int port = 8000 + (int) (Math.random() * 1000); @@ -139,10 +135,15 @@ public class Runner implements MessageConsumer { // Newer (Java 1.5+) version that uses JVMTI String jdwpArg = "-agentlib:jdwp=transport=dt_socket,address=" + portStr + ",server=y,suspend=y"; + // Everyone works the same under Java 7 (also on OS X) + String[] commandArgs = new String[] { Base.getJavaPath(), jdwpArg }; + + /* String[] commandArgs = null; if (!Base.isMacOS()) { commandArgs = new String[] { - "java", jdwpArg + Base.getJavaPath(), + jdwpArg }; } else { // Decided to just set this to 1.6 only, because otherwise it's gonna @@ -160,7 +161,7 @@ public class Runner implements MessageConsumer { // OS X at this point, because we require 10.6.8 and higher. That also // means we don't need to check for any other OS versions, the user is // a douchebag and modifies Info.plist to get around the restriction. - if (true) { + if (false) { if (System.getProperty("os.version").startsWith("10.6")) { commandArgs = new String[] { "/usr/libexec/java_home", @@ -183,41 +184,19 @@ public class Runner implements MessageConsumer { } else { // testing jdk-7u40 commandArgs = new String[] { - "/Library/Java/JavaVirtualMachines/jdk1.7.0_40.jdk/Contents/Home/bin/java", + //"/Library/Java/JavaVirtualMachines/jdk1.7.0_40.jdk/Contents/Home/bin/java", + Base.getJavaPath(), jdwpArg }; } } + */ commandArgs = PApplet.concat(commandArgs, vmParams); commandArgs = PApplet.concat(commandArgs, sketchParams); // PApplet.println(commandArgs); // commandArg.setValue(commandArgs); launchJava(commandArgs); -// try { -// Thread.sleep(2000); -// } catch (InterruptedException e) { -// e.printStackTrace(); -// } - -// boolean available = false; -// while (!available) { -// try { -// Socket socket = new Socket((String) null, port); -//// socket.close(); -// // this should mean we're all set? -// available = true; -// -// } catch (IOException e) { -// System.out.println("waiting"); -// //e.printStackTrace(); -// try { -// Thread.sleep(100); -// } catch (InterruptedException e1) { -// e1.printStackTrace(); -// } -// } -// } AttachingConnector connector = (AttachingConnector) findConnector("com.sun.jdi.SocketAttach"); @@ -467,7 +446,7 @@ public class Runner implements MessageConsumer { // PApplet.println("launchJava stderr:"); // PApplet.println(errorStrings); // PApplet.println("launchJava stdout:"); - PApplet.println(inputStrings); + PApplet.printArray(inputStrings); if (errorStrings != null && errorStrings.length > 1) { if (errorStrings[0].indexOf("Invalid maximum heap size") != -1) { @@ -699,7 +678,9 @@ public class Runner implements MessageConsumer { for (Event event : eventSet) { // System.out.println("EventThread.handleEvent -> " + event); - if (event instanceof ExceptionEvent) { + if (event instanceof VMStartEvent) { + vm.resume(); + } else if (event instanceof ExceptionEvent) { // for (ThreadReference thread : vm.allThreads()) { // System.out.println("thread : " + thread); //// thread.suspend(); @@ -731,8 +712,6 @@ public class Runner implements MessageConsumer { errThread.start(); outThread.start(); - vm.resume(); - // Shutdown begins when event thread terminates try { if (eventThread != null) eventThread.join(); // is this the problem? @@ -944,7 +923,7 @@ public class Runner implements MessageConsumer { ObjectReference ref = (ObjectReference)val; method = ((ClassType) ref.referenceType()).concreteMethodByName("getFileName", "()Ljava/lang/String;"); StringReference strref = (StringReference) ref.invokeMethod(thread, method, new ArrayList(), ObjectReference.INVOKE_SINGLE_THREADED); - String filename = strref.value(); + String filename = strref == null ? "Unknown Source" : strref.value(); method = ((ClassType) ref.referenceType()).concreteMethodByName("getLineNumber", "()I"); IntegerValue intval = (IntegerValue) ref.invokeMethod(thread, method, new ArrayList(), ObjectReference.INVOKE_SINGLE_THREADED); int lineNumber = intval.intValue() - 1; diff --git a/build/build-7u40.xml b/build/build-7u40.xml deleted file mode 100755 index 814f1c4c8..000000000 --- a/build/build-7u40.xml +++ /dev/null @@ -1,1054 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ======================================================= - Processing for Mac OS X can only be built on Mac OS X. - - Bye. - ======================================================= - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Code signing will only work if you have a $99/yr Apple developer ID. - - With a proper ID, if code signing fails, you may need to use: - export CODESIGN_ALLOCATE="/Applications/Xcode.app/Contents/Developer/usr/bin/codesign_allocate" - - - - - - - - - - - - - - - - - - - - - - - - - ======================================================= - Processing for Mac OS X was built. Grab it from - - macosx/processing-${version}-macosx.zip - ======================================================= - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ======================================================= - Processing for Mac OS X was built. Grab the image from - - macosx/processing-${version}.dmg - ======================================================= - - - - - - - - - - - - - - - ======================================================= - Processing for Linux can only be built on on unix systems. - - Bye. - ======================================================= - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ======================================================= - Processing for Linux was built. Grab the archive from - - ${linux.dist} - ======================================================= - - - - - - - - - - - - - - - ======================================================= - Processing for Windows can only be built on windows. - - Bye. - ======================================================= - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ======================================================= - Processing for Windows was built. Grab the archive from - - ${windows.dist} - ======================================================= - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/build/build.xml b/build/build.xml index 0c09692db..4d0f0b233 100755 --- a/build/build.xml +++ b/build/build.xml @@ -19,6 +19,122 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -34,28 +150,22 @@ - - - + + + - - - - - - + + + + value="macosx/work/Processing.app/Contents/Java"> @@ -68,6 +178,7 @@ + @@ -97,18 +208,6 @@ - - - - @@ -128,7 +227,6 @@ - @@ -140,7 +238,6 @@ - @@ -148,21 +245,8 @@ - - - - - - - - + + @@ -184,118 +268,15 @@ + - - - - - - - - - - - - - - - - - - - - - - - - - @@ -303,17 +284,7 @@ - - - - - + @@ -374,8 +345,6 @@ - - @@ -393,30 +362,123 @@ - - - - - - + + - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - + + + - + + + + + - - - + + - + + @@ -455,7 +517,18 @@ With a proper ID, if code signing fails, you may need to use: export CODESIGN_ALLOCATE="/Applications/Xcode.app/Contents/Developer/usr/bin/codesign_allocate" - + + + + + + + + + + + + @@ -470,10 +543,13 @@ + + @@ -490,57 +566,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ======================================================= - Processing for Mac OS X was built. Grab the image from - - macosx/processing-${version}.dmg - ======================================================= - - - @@ -573,12 +598,16 @@ - + + + + + - - @@ -595,9 +624,9 @@ - - - + + + @@ -606,13 +635,13 @@ + - - - + + + + + + + + + + + + + + + + + + + + + + + + + - + + + + + - - @@ -731,46 +791,24 @@ - - - + + + + + + + + + - - - - - - - - - - + + + @@ -778,20 +816,31 @@ - - - - + + + + + + + + + + + + + + + + + + + + + + + + @@ -851,6 +915,11 @@ + + + + @@ -871,9 +940,11 @@ - + + + diff --git a/build/google.pem b/build/google.pem deleted file mode 100644 index ed0bd764f..000000000 --- a/build/google.pem +++ /dev/null @@ -1,19 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIDIDCCAomgAwIBAgIENd70zzANBgkqhkiG9w0BAQUFADBOMQswCQYDVQQGEwJV -UzEQMA4GA1UEChMHRXF1aWZheDEtMCsGA1UECxMkRXF1aWZheCBTZWN1cmUgQ2Vy -dGlmaWNhdGUgQXV0aG9yaXR5MB4XDTk4MDgyMjE2NDE1MVoXDTE4MDgyMjE2NDE1 -MVowTjELMAkGA1UEBhMCVVMxEDAOBgNVBAoTB0VxdWlmYXgxLTArBgNVBAsTJEVx -dWlmYXggU2VjdXJlIENlcnRpZmljYXRlIEF1dGhvcml0eTCBnzANBgkqhkiG9w0B -AQEFAAOBjQAwgYkCgYEAwV2xWGcIYu6gmi0fCG2RFGiYCh7+2gRvE4RiIcPRfM6f -BeC4AfBONOziipUEZKzxa1NfBbPLZ4C/QgKO/t0BCezhABRP/PvwDN1Dulsr4R+A -cJkVV5MW8Q+XarfCaCMczE1ZMKxRHjuvK9buY0V7xdlfUNLjUA86iOe/FP3gx7kC -AwEAAaOCAQkwggEFMHAGA1UdHwRpMGcwZaBjoGGkXzBdMQswCQYDVQQGEwJVUzEQ -MA4GA1UEChMHRXF1aWZheDEtMCsGA1UECxMkRXF1aWZheCBTZWN1cmUgQ2VydGlm -aWNhdGUgQXV0aG9yaXR5MQ0wCwYDVQQDEwRDUkwxMBoGA1UdEAQTMBGBDzIwMTgw -ODIyMTY0MTUxWjALBgNVHQ8EBAMCAQYwHwYDVR0jBBgwFoAUSOZo+SvSspXXR9gj -IBBPM5iQn9QwHQYDVR0OBBYEFEjmaPkr0rKV10fYIyAQTzOYkJ/UMAwGA1UdEwQF -MAMBAf8wGgYJKoZIhvZ9B0EABA0wCxsFVjMuMGMDAgbAMA0GCSqGSIb3DQEBBQUA -A4GBAFjOKer89961zgK5F7WF0bnj4JXMJTENAKaSbn+2kmOeUJXRmm/kEd5jhW6Y -7qj/WsjTVbJmcVfewCHrPSqnI0kBBIZCe/zuf6IWUrVnZ9NA2zsmWLIodz2uFHdh -1voqZiegDfqnc1zqcPGUIWVEX/r87yloqaKHee9570+sB3c4 ------END CERTIFICATE----- diff --git a/build/googlecode_upload.py b/build/googlecode_upload.py deleted file mode 100755 index d2d5f974c..000000000 --- a/build/googlecode_upload.py +++ /dev/null @@ -1,248 +0,0 @@ -#!/usr/bin/env python -# -# Copyright 2006, 2007 Google Inc. All Rights Reserved. -# Author: danderson@google.com (David Anderson) -# -# Script for uploading files to a Google Code project. -# -# This is intended to be both a useful script for people who want to -# streamline project uploads and a reference implementation for -# uploading files to Google Code projects. -# -# To upload a file to Google Code, you need to provide a path to the -# file on your local machine, a small summary of what the file is, a -# project name, and a valid account that is a member or owner of that -# project. You can optionally provide a list of labels that apply to -# the file. The file will be uploaded under the same name that it has -# in your local filesystem (that is, the "basename" or last path -# component). Run the script with '--help' to get the exact syntax -# and available options. -# -# Note that the upload script requests that you enter your -# googlecode.com password. This is NOT your Gmail account password! -# This is the password you use on googlecode.com for committing to -# Subversion and uploading files. You can find your password by going -# to http://code.google.com/hosting/settings when logged in with your -# Gmail account. If you have already committed to your project's -# Subversion repository, the script will automatically retrieve your -# credentials from there (unless disabled, see the output of '--help' -# for details). -# -# If you are looking at this script as a reference for implementing -# your own Google Code file uploader, then you should take a look at -# the upload() function, which is the meat of the uploader. You -# basically need to build a multipart/form-data POST request with the -# right fields and send it to https://PROJECT.googlecode.com/files . -# Authenticate the request using HTTP Basic authentication, as is -# shown below. -# -# Licensed under the terms of the Apache Software License 2.0: -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Questions, comments, feature requests and patches are most welcome. -# Please direct all of these to the Google Code users group: -# http://groups.google.com/group/google-code-hosting - -"""Google Code file uploader script. -""" - -__author__ = 'danderson@google.com (David Anderson)' - -import httplib -import os.path -import optparse -import getpass -import base64 -import sys - - -def upload(file, project_name, user_name, password, summary, labels=None): - """Upload a file to a Google Code project's file server. - - Args: - file: The local path to the file. - project_name: The name of your project on Google Code. - user_name: Your Google account name. - password: The googlecode.com password for your account. - Note that this is NOT your global Google Account password! - summary: A small description for the file. - labels: an optional list of label strings with which to tag the file. - - Returns: a tuple: - http_status: 201 if the upload succeeded, something else if an - error occured. - http_reason: The human-readable string associated with http_status - file_url: If the upload succeeded, the URL of the file on Google - Code, None otherwise. - """ - # The login is the user part of user@gmail.com. If the login provided - # is in the full user@domain form, strip it down. - if user_name.endswith('@gmail.com'): - user_name = user_name[:user_name.index('@gmail.com')] - - form_fields = [('summary', summary)] - if labels is not None: - form_fields.extend([('label', l.strip()) for l in labels]) - - content_type, body = encode_upload_request(form_fields, file) - - upload_host = '%s.googlecode.com' % project_name - upload_uri = '/files' - auth_token = base64.b64encode('%s:%s'% (user_name, password)) - headers = { - 'Authorization': 'Basic %s' % auth_token, - 'User-Agent': 'Googlecode.com uploader v0.9.4', - 'Content-Type': content_type, - } - - server = httplib.HTTPSConnection(upload_host) - server.request('POST', upload_uri, body, headers) - resp = server.getresponse() - server.close() - - if resp.status == 201: - location = resp.getheader('Location', None) - else: - location = None - return resp.status, resp.reason, location - - -def encode_upload_request(fields, file_path): - """Encode the given fields and file into a multipart form body. - - fields is a sequence of (name, value) pairs. file is the path of - the file to upload. The file will be uploaded to Google Code with - the same file name. - - Returns: (content_type, body) ready for httplib.HTTP instance - """ - BOUNDARY = '----------Googlecode_boundary_reindeer_flotilla' - CRLF = '\r\n' - - body = [] - - # Add the metadata about the upload first - for key, value in fields: - body.extend( - ['--' + BOUNDARY, - 'Content-Disposition: form-data; name="%s"' % key, - '', - value, - ]) - - # Now add the file itself - file_name = os.path.basename(file_path) - f = open(file_path, 'rb') - file_content = f.read() - f.close() - - body.extend( - ['--' + BOUNDARY, - 'Content-Disposition: form-data; name="filename"; filename="%s"' - % file_name, - # The upload server determines the mime-type, no need to set it. - 'Content-Type: application/octet-stream', - '', - file_content, - ]) - - # Finalize the form body - body.extend(['--' + BOUNDARY + '--', '']) - - return 'multipart/form-data; boundary=%s' % BOUNDARY, CRLF.join(body) - - -def upload_find_auth(file_path, project_name, summary, labels=None, - user_name=None, password=None, tries=3): - """Find credentials and upload a file to a Google Code project's file server. - - file_path, project_name, summary, and labels are passed as-is to upload. - - Args: - file_path: The local path to the file. - project_name: The name of your project on Google Code. - summary: A small description for the file. - labels: an optional list of label strings with which to tag the file. - config_dir: Path to Subversion configuration directory, 'none', or None. - user_name: Your Google account name. - tries: How many attempts to make. - """ - - while tries > 0: - if user_name is None: - # Read username if not specified or loaded from svn config, or on - # subsequent tries. - sys.stdout.write('Please enter your googlecode.com username: ') - sys.stdout.flush() - user_name = sys.stdin.readline().rstrip() - if password is None: - # Read password if not loaded from svn config, or on subsequent tries. - print 'Please enter your googlecode.com password.' - print '** Note that this is NOT your Gmail account password! **' - print 'It is the password you use to access Subversion repositories,' - print 'and can be found here: http://code.google.com/hosting/settings' - password = getpass.getpass() - - status, reason, url = upload(file_path, project_name, user_name, password, - summary, labels) - # Returns 403 Forbidden instead of 401 Unauthorized for bad - # credentials as of 2007-07-17. - if status in [httplib.FORBIDDEN, httplib.UNAUTHORIZED]: - # Rest for another try. - user_name = password = None - tries = tries - 1 - else: - # We're done. - break - - return status, reason, url - - -def main(): - parser = optparse.OptionParser(usage='googlecode-upload.py -s SUMMARY ' - '-p PROJECT [options] FILE') - parser.add_option('-s', '--summary', dest='summary', - help='Short description of the file') - parser.add_option('-p', '--project', dest='project', - help='Google Code project name') - parser.add_option('-u', '--user', dest='user', - help='Your Google Code username') - parser.add_option('-w', '--password', dest='password', - help='Your Google Code password') - parser.add_option('-l', '--labels', dest='labels', - help='An optional list of comma-separated labels to attach ' - 'to the file') - - options, args = parser.parse_args() - - if not options.summary: - parser.error('File summary is missing.') - elif not options.project: - parser.error('Project name is missing.') - elif len(args) < 1: - parser.error('File to upload not provided.') - elif len(args) > 1: - parser.error('Only one file may be specified.') - - file_path = args[0] - - if options.labels: - labels = options.labels.split(',') - else: - labels = None - - status, reason, url = upload_find_auth(file_path, options.project, - options.summary, labels, - options.user, options.password) - if url: - print 'The file was uploaded successfully.' - print 'URL: %s' % url - return 0 - else: - print 'An error occurred. Your file was not uploaded.' - print 'Google Code upload server said: %s (%s)' % (reason, status) - return 1 - - -if __name__ == '__main__': - sys.exit(main()) diff --git a/build/javadoc/everything/package-list b/build/javadoc/everything/package-list index 324f2b779..cd4505a9e 100644 --- a/build/javadoc/everything/package-list +++ b/build/javadoc/everything/package-list @@ -3,12 +3,10 @@ japplemenubar processing.app processing.app.contrib processing.app.exec -processing.app.linux -processing.app.macosx +processing.app.platform processing.app.syntax processing.app.syntax.im processing.app.tools -processing.app.windows processing.core processing.data processing.event diff --git a/build/javadoc/stylesheet.css b/build/javadoc/stylesheet.css deleted file mode 100644 index 88654c77d..000000000 --- a/build/javadoc/stylesheet.css +++ /dev/null @@ -1,187 +0,0 @@ -/* Javadoc style sheet */ - -/* Define colors, fonts and other style attributes here to override the defaults */ - -/* Page background color */ -body { background-color: #FFFFFF } - -/* Headings */ -h1 { font-size: 120% } - - -/* Table colors */ -.TableHeadingColor { background: #CCCCCC } /* Dark mauve */ -.TableSubHeadingColor { background: #EEEEEE } /* Light mauve */ -.TableRowColor { background: #FFFFFF } /* White */ - -/* - * /* Font used in left-hand frame lists */ - * .FrameTitleFont { font-size: 90%; font-family: Verdana, Geneva, Arial, Helvetica, sans-serif } - * .FrameHeadingFont { font-size: 90%; font-family: Verdana, Geneva, Arial, Helvetica, sans-serif } - * .FrameItemFont { font-size: 90%; font-family: Verdana, Geneva, Arial, Helvetica, sans-serif } - */ - -/* Navigation bar fonts and colors */ -.NavBarCell1 { background-color:#EEEEEE;} /* Light mauve */ -.NavBarCell1Rev { background-color:#00008B;} /* Dark Blue */ - -.NavBarFont1 { font-family: Verdana, Geneva, Arial, Helvetica, sans-serif; color:#000000;} -.NavBarFont1Rev { font-family: Verdana, Geneva, Arial, Helvetica, sans-serif; color:#FFFFFF;} - -.NavBarCell2 { font-family: Verdana, Geneva, Arial, Helvetica, sans-serif; background-color:#FFFFFF;} -.NavBarCell3 { font-family: Verdana, Geneva, Arial, Helvetica, sans-serif; background-color:#FFFFFF;} - - - -/* - * Processing - Styling - * - * fjenett - mail@florianjenett.de - 2005.08.14 - * Updated by REAS 2011.09.02 - * - */ - - -/* first let's restyle what's there .. */ - -/* Table colors */ -.TableHeadingColor { - background: #CCCCCC; - color: #5A5A5A; - } -.TableSubHeadingColor { background: #EEEEEE } /* Gray */ -.TableRowColor { background: #FFFFFF } /* White */ - - -/* Font used in left-hand frame lists */ -.FrameTitleFont { - font-size: 1.0em; - font-family: Verdana, Geneva, Arial, Helvetica, sans-serif -} -.FrameHeadingFont { - font-size: 1.0em; - font-family: Verdana, Geneva, Arial, Helvetica, sans-serif; - color: #5A5A5A; -} -.FrameItemFont { - font-size: 1.0em; - font-family: Verdana, Geneva, Arial, Helvetica, sans-serif -} - - -/* Navigation bar fonts and colors */ -.NavBarCell1 { background-color:#CCCCCC;} /* Light */ -.NavBarCell1Rev { background-color:#5A5A5A;} /* Dark */ - -.NavBarFont1 { - font-family: Verdana, Geneva, Arial, Helvetica, sans-serif; - color:#5A5A5A; -} -.NavBarFont1Rev { - font-family: Verdana, Geneva, Arial, Helvetica, sans-serif; - color:#FFFFFF; -} - -.NavBarCell2 { font-family: Verdana, Geneva, Arial, Helvetica, sans-serif; background-color:#FFFFFF;} -.NavBarCell3 { font-family: Verdana, Geneva, Arial, Helvetica, sans-serif; background-color:#FFFFFF;} - - -/* try to style some more ... */ - -body, -html -{ - font-size: small; - font-family: Verdana, Geneva, Arial, Helvetica, sans-serif; - color: #333333; -} - -h1, h2, h3, h4, h5, h6 { - - color: #5A5A5A; - font-family: Verdana, Geneva, Arial, Helvetica, sans-serif; - font-weight: normal; - line-height: normal; -} - -h1, h2, h3, h4 { - word-spacing: 0.2em; - font-weight: bold; -} - -h4, h5, h6 { - font-weight: bold; -} -/* Font Sizes */ -h1 { font-size: 1.5em; } -h2 { font-size: 1.4em; } -h3 { font-size: 1.3em; } -h4 { font-size: 1.2em; } -h5 { font-size: 1.1em; } -h6 { font-size: 1em; } -p { font-size: 1em; } - -dl, dd, dt, -dt > b -{ - color: #666666; - font-size: 1.0em; -} - -code, -pre -{ - font-size: 1.2em; - color:#333333; -} - -pre -{ - font-size: 1.2em; -} - - -/* force the table-headers small .. */ -b -{ - font-size: small; -} - - -tr, -td -{ - border-top: 0px solid; - border-left: 0px solid; - border-color: #999999; -} - -table -{ - border: 0px; -} - -img -{ - border: 0px solid #000000; -} - -a { - text-decoration: underline; - font-weight: normal; - color: #3399CC; -} - -a:hover -a:active { - text-decoration: underline; - font-weight: normal; - color: #3399CC; -} - -a:visited, -a:link:visited { - text-decoration: underline; - font-weight: normal; - color: #3399CC; -} \ No newline at end of file diff --git a/build/linux/processing b/build/linux/processing index 14708c1ed..eedceaa5f 100755 --- a/build/linux/processing +++ b/build/linux/processing @@ -103,7 +103,7 @@ cmd_name='processing-java' if [ $current_name = $cmd_name ] then - java processing.mode.java.Commander "$@" + java -Djna.nosys=true processing.mode.java.Commander "$@" exit $? else # Start Processing in the same directory as this script @@ -114,5 +114,5 @@ else fi cd "$APPDIR" - java processing.app.Base "$SKETCH" & + java -Djna.nosys=true processing.app.Base "$SKETCH" & fi diff --git a/build/macosx/appbundler/.classpath b/build/macosx/appbundler/.classpath new file mode 100644 index 000000000..74aba6a10 --- /dev/null +++ b/build/macosx/appbundler/.classpath @@ -0,0 +1,7 @@ + + + + + + + diff --git a/experimental/.gitignore b/build/macosx/appbundler/.gitignore similarity index 80% rename from experimental/.gitignore rename to build/macosx/appbundler/.gitignore index ba077a403..fe99505dc 100644 --- a/experimental/.gitignore +++ b/build/macosx/appbundler/.gitignore @@ -1 +1,2 @@ bin + diff --git a/experimental/.project b/build/macosx/appbundler/.project similarity index 90% rename from experimental/.project rename to build/macosx/appbundler/.project index 1be6ebc0c..9e6f7c6d4 100644 --- a/experimental/.project +++ b/build/macosx/appbundler/.project @@ -1,6 +1,6 @@ - processing-experimental + processing-appbundler diff --git a/experimental/.settings/org.eclipse.jdt.core.prefs b/build/macosx/appbundler/.settings/org.eclipse.jdt.core.prefs similarity index 75% rename from experimental/.settings/org.eclipse.jdt.core.prefs rename to build/macosx/appbundler/.settings/org.eclipse.jdt.core.prefs index 8000cd6ca..7341ab168 100644 --- a/experimental/.settings/org.eclipse.jdt.core.prefs +++ b/build/macosx/appbundler/.settings/org.eclipse.jdt.core.prefs @@ -1,11 +1,11 @@ eclipse.preferences.version=1 org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled -org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6 +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.7 org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve -org.eclipse.jdt.core.compiler.compliance=1.6 +org.eclipse.jdt.core.compiler.compliance=1.7 org.eclipse.jdt.core.compiler.debug.lineNumber=generate org.eclipse.jdt.core.compiler.debug.localVariable=generate org.eclipse.jdt.core.compiler.debug.sourceFile=generate org.eclipse.jdt.core.compiler.problem.assertIdentifier=error org.eclipse.jdt.core.compiler.problem.enumIdentifier=error -org.eclipse.jdt.core.compiler.source=1.6 +org.eclipse.jdt.core.compiler.source=1.7 diff --git a/build/macosx/appbundler/README.md b/build/macosx/appbundler/README.md new file mode 100644 index 000000000..626dc3bab --- /dev/null +++ b/build/macosx/appbundler/README.md @@ -0,0 +1,91 @@ +appbundler +========== + +This is a fork of the [infinitekind fork](https://bitbucket.org/infinitekind/appbundler) of Oracle's appbundler. +(Many thanks for their additional work!) This fork covers several additional features (ability to remove JavaFX +binaries, a rewritten Info.plist writer, etc). See changes in the commits to this source. + +And here's the README from the [infinitekind fork](https://bitbucket.org/infinitekind/appbundler): + +A fork of the [Java Application Bundler](https://svn.java.net/svn/appbundler~svn) +with the following changes: + +- The native binary is created as universal (32/64) +- Fixes [icon not showing bug](http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=7159381) in `JavaAppLauncher` +- Adds `LC_CTYPE` environment variable to the `Info.plist` file in order to fix an [issue with `File.exists()` in OpenJDK 7](http://java.net/jira/browse/MACOSX_PORT-165) **(Contributed by Steve Hannah)** +- Allows to specify the name of the executable instead of using the default `"JavaAppLauncher"` **(contributed by Karl von Randow)** +- Adds `classpathref` support to the `bundleapp` task +- Adds support for `JVMArchs` and `LSArchitecturePriority` keys +- Allows to specify a custom value for `CFBundleVersion` +- Allows specifying registered file extensions using `CFBundleDocumentTypes` +- Passes to the Java application a set of environment variables with the paths of + the OSX special folders and whether the application is running in the + sandbox (see below). + +These are the environment variables passed to the JVM: + +- `LibraryDirectory` +- `DocumentsDirectory` +- `CachesDirectory` +- `ApplicationSupportDirectory` +- `SandboxEnabled` (the String `true` or `false`) + + +Example: + + + + + + + + + + + + + + + + + + + + + + + diff --git a/build/macosx/appbundler/TODO b/build/macosx/appbundler/TODO new file mode 100644 index 000000000..270d3e950 --- /dev/null +++ b/build/macosx/appbundler/TODO @@ -0,0 +1,3 @@ +- Architectures should be automatically inferred from the embedded JVM +- It makes more sense to take as input a JRE instead of taking a JDK and strip + it until it becomes a JRE... diff --git a/build/macosx/appbundler/build.xml b/build/macosx/appbundler/build.xml new file mode 100644 index 000000000..f2af99aea --- /dev/null +++ b/build/macosx/appbundler/build.xml @@ -0,0 +1,176 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/build/macosx/appbundler/native/main.m b/build/macosx/appbundler/native/main.m new file mode 100644 index 000000000..95a4485c4 --- /dev/null +++ b/build/macosx/appbundler/native/main.m @@ -0,0 +1,268 @@ +/* + * Copyright 2012, 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. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * 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. + */ + +#import +#include +#include + +#define JAVA_LAUNCH_ERROR "JavaLaunchError" + +#define JVM_RUNTIME_KEY "JVMRuntime" +#define WORKING_DIR "WorkingDirectory" +#define JVM_MAIN_CLASS_NAME_KEY "JVMMainClassName" +#define JVM_OPTIONS_KEY "JVMOptions" +#define JVM_ARGUMENTS_KEY "JVMArguments" + +#define JVM_RUN_PRIVILEGED "JVMRunPrivileged" + +#define UNSPECIFIED_ERROR "An unknown error occurred." + +#define APP_ROOT_PREFIX "$APP_ROOT" + +#define LIBJLI_DYLIB "/Library/Internet Plug-Ins/JavaAppletPlugin.plugin/Contents/Home/lib/jli/libjli.dylib" + +typedef int (JNICALL *JLI_Launch_t)(int argc, char ** argv, + int jargc, const char** jargv, + int appclassc, const char** appclassv, + const char* fullversion, + const char* dotversion, + const char* pname, + const char* lname, + jboolean javaargs, + jboolean cpwildcard, + jboolean javaw, + jint ergo); + +int launch(char *); + +int main(int argc, char *argv[]) { + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + + int result; + @try { + launch(argv[0]); + result = 0; + } @catch (NSException *exception) { + NSAlert *alert = [[NSAlert alloc] init]; + [alert setAlertStyle:NSCriticalAlertStyle]; + [alert setMessageText:[exception reason]]; + [alert runModal]; + + result = 1; + } + + [pool drain]; + + return result; +} + +int launch(char *commandName) { + // Get the main bundle + NSBundle *mainBundle = [NSBundle mainBundle]; + + // Get the main bundle's info dictionary + NSDictionary *infoDictionary = [mainBundle infoDictionary]; + + // Set the working directory based on config, defaulting to the user's home directory + NSString *workingDir = [infoDictionary objectForKey:@WORKING_DIR]; + if (workingDir != nil) { + workingDir = [workingDir stringByReplacingOccurrencesOfString:@APP_ROOT_PREFIX withString:[mainBundle bundlePath]]; + } else { + workingDir = NSHomeDirectory(); + } + + chdir([workingDir UTF8String]); + + // execute privileged + NSString *privileged = [infoDictionary objectForKey:@JVM_RUN_PRIVILEGED]; + if (privileged != nil && getuid() != 0) { + NSDictionary *error = [NSDictionary new]; + NSString *script = [NSString stringWithFormat:@"do shell script \"\\\"%@\\\" > /dev/null 2>&1 &\" with administrator privileges", [NSString stringWithCString:commandName encoding:NSASCIIStringEncoding]]; + NSAppleScript *appleScript = [[NSAppleScript new] initWithSource:script]; + if ([appleScript executeAndReturnError:&error]) { + // This means we successfully elevated the application and can stop in here. + return 0; // Should this return 'error' instead? [fry] + } + } + + // Locate the JLI_Launch() function + NSString *runtime = [infoDictionary objectForKey:@JVM_RUNTIME_KEY]; + + const char *libjliPath = NULL; + if (runtime != nil) { + NSString *runtimePath = [[[NSBundle mainBundle] builtInPlugInsPath] stringByAppendingPathComponent:runtime]; + libjliPath = [[runtimePath stringByAppendingPathComponent:@"Contents/Home/jre/lib/jli/libjli.dylib"] fileSystemRepresentation]; + } else { + libjliPath = LIBJLI_DYLIB; + } + + void *libJLI = dlopen(libjliPath, RTLD_LAZY); + + JLI_Launch_t jli_LaunchFxnPtr = NULL; + if (libJLI != NULL) { + jli_LaunchFxnPtr = dlsym(libJLI, "JLI_Launch"); + } + + if (jli_LaunchFxnPtr == NULL) { + [[NSException exceptionWithName:@JAVA_LAUNCH_ERROR + reason:NSLocalizedString(@"JRELoadError", @UNSPECIFIED_ERROR) + userInfo:nil] raise]; + } + + // Get the main class name + NSString *mainClassName = [infoDictionary objectForKey:@JVM_MAIN_CLASS_NAME_KEY]; + if (mainClassName == nil) { + [[NSException exceptionWithName:@JAVA_LAUNCH_ERROR + reason:NSLocalizedString(@"MainClassNameRequired", @UNSPECIFIED_ERROR) + userInfo:nil] raise]; + } + + // Set the class path + NSString *mainBundlePath = [mainBundle bundlePath]; + NSString *javaPath = + [mainBundlePath stringByAppendingString:@"/Contents/Java"]; + // Changed Contents/Java to the old Contents/Resources/Java [fry] + //[mainBundlePath stringByAppendingString:@"/Contents/Resources/Java"]; + // Removed the /Classes, because the P5 compiler (ECJ?) will throw an + // error if it doesn't exist. But it's harmless to leave the root dir, + // since it will always exist, and I guess if you wanted to put .class + // files in there, they'd work. If I knew more Cocoa, I'd just make this + // an empty string to start, to be appended a few lines later. [fry] + //NSMutableString *classPath = [NSMutableString stringWithFormat:@"-Djava.class.path=%@/Classes", javaPath]; + NSMutableString *classPath = [NSMutableString stringWithFormat:@"-Djava.class.path=%@", javaPath]; + + NSFileManager *defaultFileManager = [NSFileManager defaultManager]; + NSArray *javaDirectoryContents = + [defaultFileManager contentsOfDirectoryAtPath:javaPath error:nil]; + if (javaDirectoryContents == nil) { + [[NSException exceptionWithName:@JAVA_LAUNCH_ERROR + reason:NSLocalizedString(@"JavaDirectoryNotFound", @UNSPECIFIED_ERROR) + userInfo:nil] raise]; + } + + for (NSString *file in javaDirectoryContents) { + if ([file hasSuffix:@".jar"]) { + [classPath appendFormat:@":%@/%@", javaPath, file]; + } + } + + /* + // search the 'lib' subfolder as well [fry] + NSString *libPath = + [mainBundlePath stringByAppendingString:@"/Contents/Resources/Java/lib"]; + NSArray *libDirectoryContents = + [defaultFileManager contentsOfDirectoryAtPath:libPath error:nil]; + if (libDirectoryContents != nil) { + for (NSString *file in libDirectoryContents) { + if ([file hasSuffix:@".jar"]) { + [classPath appendFormat:@":%@/%@", libPath, file]; + } + } + } + */ + + // Set the library path + NSString *libraryPath = [NSString stringWithFormat:@"-Djava.library.path=:%@/Contents/Java:%@/Contents/MacOS", mainBundlePath, mainBundlePath]; + + // Get the VM options + NSArray *options = [infoDictionary objectForKey:@JVM_OPTIONS_KEY]; + if (options == nil) { + options = [NSArray array]; + } + + // Get the application arguments + NSArray *arguments = [infoDictionary objectForKey:@JVM_ARGUMENTS_KEY]; + if (arguments == nil) { + arguments = [NSArray array]; + } + + // Set OSX special folders + NSArray *paths = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, + NSUserDomainMask, YES); + NSString *basePath = [paths objectAtIndex:0]; + NSString *libraryDirectory = [NSString stringWithFormat:@"-DLibraryDirectory=%@", basePath]; + + paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, + NSUserDomainMask, YES); + basePath = [paths objectAtIndex:0]; + NSString *documentsDirectory = [NSString stringWithFormat:@"-DDocumentsDirectory=%@", basePath]; + + paths = NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, + NSUserDomainMask, YES); + basePath = [paths objectAtIndex:0]; + NSString *applicationSupportDirectory = [NSString stringWithFormat:@"-DApplicationSupportDirectory=%@", basePath]; + + paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, + NSUserDomainMask, YES); + basePath = [paths objectAtIndex:0]; + NSString *cachesDirectory = [NSString stringWithFormat:@"-DCachesDirectory=%@", basePath]; + + NSString *sandboxEnabled = @"true"; + if ([basePath rangeOfString:@"Containers"].location == NSNotFound) { + sandboxEnabled = @"false"; + } + NSString *sandboxEnabledVar = [NSString stringWithFormat:@"-DSandboxEnabled=%@", sandboxEnabled]; + + // Initialize the arguments to JLI_Launch() + // +5 due to the special directories and the sandbox enabled property + int argc = 1 + [options count] + 2 + [arguments count] + 1 + 5; + char *argv[argc]; + + int i = 0; + argv[i++] = commandName; + argv[i++] = strdup([classPath UTF8String]); + argv[i++] = strdup([libraryPath UTF8String]); + argv[i++] = strdup([libraryDirectory UTF8String]); + argv[i++] = strdup([documentsDirectory UTF8String]); + argv[i++] = strdup([applicationSupportDirectory UTF8String]); + argv[i++] = strdup([cachesDirectory UTF8String]); + argv[i++] = strdup([sandboxEnabledVar UTF8String]); + + for (NSString *option in options) { + option = [option stringByReplacingOccurrencesOfString:@APP_ROOT_PREFIX withString:[mainBundle bundlePath]]; + argv[i++] = strdup([option UTF8String]); + } + + argv[i++] = strdup([mainClassName UTF8String]); + + for (NSString *argument in arguments) { + argument = [argument stringByReplacingOccurrencesOfString:@APP_ROOT_PREFIX withString:[mainBundle bundlePath]]; + argv[i++] = strdup([argument UTF8String]); + } + + // Invoke JLI_Launch() + return jli_LaunchFxnPtr(argc, argv, + 0, NULL, + 0, NULL, + "", + "", + "java", + "java", + FALSE, + FALSE, + FALSE, + 0); +} diff --git a/build/macosx/appbundler/res/en.lproj/Localizable.strings b/build/macosx/appbundler/res/en.lproj/Localizable.strings new file mode 100644 index 000000000..0d306aaaf --- /dev/null +++ b/build/macosx/appbundler/res/en.lproj/Localizable.strings @@ -0,0 +1,3 @@ +"JRELoadError" = "Unable to load Java Runtime Environment."; +"MainClassNameRequired" = "Main class name is required."; +"JavaDirectoryNotFound" = "Unable to enumerate Java directory contents."; diff --git a/build/macosx/appbundler/src/com/oracle/appbundler/AppBundlerTask.java b/build/macosx/appbundler/src/com/oracle/appbundler/AppBundlerTask.java new file mode 100644 index 000000000..5d944f4d7 --- /dev/null +++ b/build/macosx/appbundler/src/com/oracle/appbundler/AppBundlerTask.java @@ -0,0 +1,754 @@ +/* + * Copyright 2012, 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. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * 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.appbundler; + +import java.io.BufferedOutputStream; +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileOutputStream; +import java.io.FileWriter; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.Writer; +import java.net.URL; +import java.nio.file.Files; +import java.nio.file.LinkOption; +import java.nio.file.Path; +import java.nio.file.StandardCopyOption; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.zip.ZipEntry; +import java.util.zip.ZipInputStream; + +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.DirectoryScanner; +import org.apache.tools.ant.Task; +import org.apache.tools.ant.types.FileSet; +import org.apache.tools.ant.types.Reference; +import org.apache.tools.ant.types.resources.FileResource; + +/** + * App bundler Ant task. + */ +public class AppBundlerTask extends Task { + // Output folder for generated bundle + private File outputDirectory = null; + + // General bundle properties + private String name = null; + private String displayName = null; + private String identifier = null; + private File iconFile = null; + private String executableName = EXECUTABLE_NAME; + + private String shortVersion = null; //"1.0"; + private String version = null; //"1.0"; + private String signature = "????"; + private String copyright = null; //""; + private String getInfo = null; + private String privileged = null; + private String workingDirectory = null; + + private String applicationCategory = null; + private boolean highResolutionCapable = true; + // Oracle Java 7 requires 10.7.3 or later, so require it here. + private String minimumSystem = "10.7.3"; + // By default, don't embed Java FX. + private boolean javafx = false; + + // JVM info properties + private String mainClassName = null; + private FileSet runtime = null; + private ArrayList classPath = new ArrayList<>(); + private ArrayList libraryPath = new ArrayList<>(); + private ArrayList options = new ArrayList<>(); + private ArrayList arguments = new ArrayList<>(); + private ArrayList architectures = new ArrayList<>(); + private ArrayList bundleDocuments = new ArrayList<>(); + + private Reference classPathRef; + + private static final String EXECUTABLE_NAME = "JavaAppLauncher"; + private static final String DEFAULT_ICON_NAME = "GenericApp.icns"; + private static final String OS_TYPE_CODE = "APPL"; + + private static final int BUFFER_SIZE = 2048; + + + public void setOutputDirectory(File outputDirectory) { + this.outputDirectory = outputDirectory; + } + + + public void setName(String name) { + this.name = name; + } + + + public void setDisplayName(String displayName) { + this.displayName = displayName; + } + + + public void setIdentifier(String identifier) { + this.identifier = identifier; + } + + + public void setIcon(File icon) { + this.iconFile = icon; + } + + + public void setExecutableName(String executable) { + this.executableName = executable; + } + + + public void setShortVersion(String shortVersion) { + this.shortVersion = shortVersion; + } + + + public void setVersion(String version) { + this.version = version; + } + + + public void setSignature(String signature) { + this.signature = signature; + } + + + public void setCopyright(String copyright) { + this.copyright = copyright; + } + + + public void setGetInfo(String getInfo) { + this.getInfo = getInfo; + } + + + public void setPrivileged(String privileged) { + this.privileged = privileged; + } + + + public void setWorkingDirectory(String workingDirectory) { + this.workingDirectory = workingDirectory; + } + + + public void setApplicationCategory(String applicationCategory) { + this.applicationCategory = applicationCategory; + } + + + public void setMinimumSystem(String minimumSystem) { + this.minimumSystem = minimumSystem; + } + + + public void setHighResolutionCapable(boolean highResolutionCapable) { + this.highResolutionCapable = highResolutionCapable; + } + + + public void setJavaFX(boolean javafx) { + this.javafx = javafx; + } + + + public void setMainClassName(String mainClassName) { + this.mainClassName = mainClassName; + } + + + public void addConfiguredRuntime(FileSet runtime) throws BuildException { + if (this.runtime != null) { + throw new BuildException("Runtime already specified."); + } + + this.runtime = runtime; + + runtime.appendIncludes(new String[] { + "jre/", + }); + + runtime.appendExcludes(new String[] { + "bin/", + + // original version, removed entire bin folder +// "jre/bin/", + + // remove everything except 'java' + "jre/bin/keytool", + "jre/bin/orbd", + "jre/bin/pack200", + "jre/bin/policytool", + "jre/bin/rmid", + "jre/bin/rmiregistry", + "jre/bin/servertool", + "jre/bin/tnameserv", + "jre/bin/unpack200", + + "jre/lib/deploy/", + "jre/lib/deploy.jar", + "jre/lib/javaws.jar", + "jre/lib/libdeploy.dylib", + "jre/lib/libnpjp2.dylib", + "jre/lib/plugin.jar", + "jre/lib/security/javaws.policy" + }); + + if (!javafx) { + // http://www.oracle.com/technetwork/java/javase/jdk-7-readme-429198.html + runtime.appendExcludes(new String[] { + "jre/THIRDPARTYLICENSEREADME-JAVAFX.txt", + + "jre/lib/javafx.properties", + "jre/lib/jfxrt.jar", + "jre/lib/security/javafx.policy", + + "jre/lib/fxplugins.dylib", + "jre/lib/libdecora-sse.dylib", + "jre/lib/libglass.dylib", + "jre/lib/libglib-2.0.0.dylib", + "jre/lib/libgstplugins-lite.dylib", + "jre/lib/libgstreamer-lite.dylib", + "jre/lib/libjavafx-font.dylib", + "jre/lib/libjavafx-iio.dylib", + "jre/lib/libjfxmedia.dylib", + "jre/lib/libjfxwebkit.dylib", + "jre/lib/libprism-es2.dylib" + }); + } + } + + + public void setClasspathRef(Reference ref) { + this.classPathRef = ref; + } + + + public void addConfiguredClassPath(FileSet classPath) { + this.classPath.add(classPath); + } + + + public void addConfiguredLibraryPath(FileSet libraryPath) { + this.libraryPath.add(libraryPath); + } + + + public void addConfiguredBundleDocument(BundleDocument document) { + this.bundleDocuments.add(document); + } + + + public void addConfiguredOption(Option option) throws BuildException { + String value = option.getValue(); + + if (value == null) { + throw new BuildException("Value is required."); + } + + options.add(value); + } + + + public void addConfiguredArgument(Argument argument) throws BuildException { + String value = argument.getValue(); + + if (value == null) { + throw new BuildException("Value is required."); + } + + arguments.add(value); + } + + + public void addConfiguredArch(Architecture architecture) throws BuildException { + String name = architecture.getName(); + + if (name == null) { + throw new BuildException("Name is required."); + } + + architectures.add(name); + } + + + @Override + public void execute() throws BuildException { + // Validate required properties + if (outputDirectory == null) { + throw new IllegalStateException("Output directory is required."); + } + + if (!outputDirectory.exists()) { + throw new IllegalStateException("Output directory does not exist."); + } + + if (!outputDirectory.isDirectory()) { + throw new IllegalStateException("Invalid output directory."); + } + + if (name == null) { + throw new IllegalStateException("Name is required."); + } + + if (displayName == null) { + throw new IllegalStateException("Display name is required."); + } + + if (identifier == null) { + throw new IllegalStateException("Identifier is required."); + } + + if (iconFile != null) { + if (!iconFile.exists()) { + throw new IllegalStateException("Icon does not exist."); + } + + if (iconFile.isDirectory()) { + throw new IllegalStateException("Invalid icon."); + } + } + + if (shortVersion == null) { + throw new IllegalStateException("Short version is required."); + } + + if (signature == null) { + throw new IllegalStateException("Signature is required."); + } + + if (signature.length() != 4) { + throw new IllegalStateException("Invalid signature."); + } + + if (copyright == null) { + throw new IllegalStateException("Copyright is required."); + } + + if (mainClassName == null) { + throw new IllegalStateException("Main class name is required."); + } + + // Create the app bundle + try { + System.out.println("Creating app bundle: " + name); + + // Create directory structure + File rootDirectory = new File(outputDirectory, name + ".app"); + delete(rootDirectory); + rootDirectory.mkdir(); + + File contentsDirectory = new File(rootDirectory, "Contents"); + contentsDirectory.mkdir(); + + File macOSDirectory = new File(contentsDirectory, "MacOS"); + macOSDirectory.mkdir(); + + File javaDirectory = new File(contentsDirectory, "Java"); + javaDirectory.mkdir(); + + File plugInsDirectory = new File(contentsDirectory, "PlugIns"); + plugInsDirectory.mkdir(); + + File resourcesDirectory = new File(contentsDirectory, "Resources"); + resourcesDirectory.mkdir(); + +// // Move back to Contents/Resources/Java instead of Contents/Java [fry] +// File javaDirectory = new File(resourcesDirectory, "Java"); +// javaDirectory.mkdir(); + + // Generate Info.plist + File infoPlistFile = new File(contentsDirectory, "Info.plist"); + infoPlistFile.createNewFile(); + writeInfoPlist(infoPlistFile); + + // Generate PkgInfo + File pkgInfoFile = new File(contentsDirectory, "PkgInfo"); + pkgInfoFile.createNewFile(); + writePkgInfo(pkgInfoFile); + + // Copy executable to MacOS folder + File executableFile = new File(macOSDirectory, executableName); + copy(getClass().getResource(EXECUTABLE_NAME), executableFile); + + executableFile.setExecutable(true, false); + + // Copy localized resources to Resources folder + copyResources(resourcesDirectory); + + // Copy runtime to PlugIns folder + copyRuntime(plugInsDirectory); + + // Copy class path entries to Java folder + copyClassPathEntries(javaDirectory); + + // Copy class path ref entries to Java folder + copyClassPathRefEntries(javaDirectory); + + // Copy library path entries to MacOS folder + copyLibraryPathEntries(macOSDirectory); + + // Copy icon to Resources folder + copyIcon(resourcesDirectory); + + // Copy the bundle/document icons as well + copyBundleIcons(resourcesDirectory); + + } catch (IOException exception) { + throw new BuildException(exception); + } + } + + + private void copyResources(File resourcesDirectory) throws IOException { + // Unzip res.zip into resources directory + InputStream inputStream = getClass().getResourceAsStream("res.zip"); + ZipInputStream zipInputStream = new ZipInputStream(inputStream); + + try { + ZipEntry zipEntry = zipInputStream.getNextEntry(); + while (zipEntry != null) { + File file = new File(resourcesDirectory, zipEntry.getName()); + + if (zipEntry.isDirectory()) { + file.mkdir(); + } else { + OutputStream outputStream = + new BufferedOutputStream(new FileOutputStream(file), BUFFER_SIZE); + try { + int b = zipInputStream.read(); + while (b != -1) { + outputStream.write(b); + b = zipInputStream.read(); + } + outputStream.flush(); + } finally { + outputStream.close(); + } + } + zipEntry = zipInputStream.getNextEntry(); + } + } finally { + zipInputStream.close(); + } + } + + + private void copyRuntime(File plugInsDirectory) throws IOException { + if (runtime != null) { + File runtimeHomeDirectory = runtime.getDir(); + File runtimeContentsDirectory = runtimeHomeDirectory.getParentFile(); + File runtimeDirectory = runtimeContentsDirectory.getParentFile(); + + // Create root plug-in directory + File pluginDirectory = new File(plugInsDirectory, runtimeDirectory.getName()); + pluginDirectory.mkdir(); + + // Create Contents directory + File pluginContentsDirectory = new File(pluginDirectory, runtimeContentsDirectory.getName()); + pluginContentsDirectory.mkdir(); + + // Copy MacOS directory + File runtimeMacOSDirectory = new File(runtimeContentsDirectory, "MacOS"); + copy(runtimeMacOSDirectory, new File(pluginContentsDirectory, runtimeMacOSDirectory.getName())); + + // Copy Info.plist file + File runtimeInfoPlistFile = new File(runtimeContentsDirectory, "Info.plist"); + copy(runtimeInfoPlistFile, new File(pluginContentsDirectory, runtimeInfoPlistFile.getName())); + + // Copy included contents of Home directory + File pluginHomeDirectory = new File(pluginContentsDirectory, runtimeHomeDirectory.getName()); + + DirectoryScanner directoryScanner = runtime.getDirectoryScanner(getProject()); + String[] includedFiles = directoryScanner.getIncludedFiles(); + + for (String includedFile : includedFiles) { + //for (int i = 0; i < includedFiles.length; i++) { + //String includedFile = includedFiles[i]; + File source = new File(runtimeHomeDirectory, includedFile); + File destination = new File(pluginHomeDirectory, includedFile); + copy(source, destination); + } + } + } + + + private void copyClassPathRefEntries(File javaDirectory) throws IOException { + if (classPathRef != null) { + org.apache.tools.ant.types.Path classpath = + (org.apache.tools.ant.types.Path) classPathRef.getReferencedObject(getProject()); + + Iterator iter = classpath.iterator(); + while (iter.hasNext()) { + FileResource resource = (FileResource) iter.next(); + File source = resource.getFile(); + File destination = new File(javaDirectory, source.getName()); + copy(source, destination); + } + } + } + + + private void copyClassPathEntries(File javaDirectory) throws IOException { + for (FileSet fileSet : classPath) { + File classPathDirectory = fileSet.getDir(); + DirectoryScanner directoryScanner = fileSet.getDirectoryScanner(getProject()); + String[] includedFiles = directoryScanner.getIncludedFiles(); + + for (String includedFile : includedFiles) { + File source = new File(classPathDirectory, includedFile); + File destination = new File(javaDirectory, new File(includedFile).getName()); + copy(source, destination); + } + } + } + + + private void copyLibraryPathEntries(File macOSDirectory) throws IOException { + for (FileSet fileSet : libraryPath) { + File libraryPathDirectory = fileSet.getDir(); + DirectoryScanner directoryScanner = fileSet.getDirectoryScanner(getProject()); + String[] includedFiles = directoryScanner.getIncludedFiles(); + + for (String includedFile : includedFiles) { + File source = new File(libraryPathDirectory, includedFile); + File destination = new File(macOSDirectory, new File(includedFile).getName()); + copy(source, destination); + } + } + } + + + private void copyIcon(File resourcesDirectory) throws IOException { + if (iconFile == null) { + copy(getClass().getResource(DEFAULT_ICON_NAME), + new File(resourcesDirectory, DEFAULT_ICON_NAME)); + } else { + copy(iconFile, new File(resourcesDirectory, iconFile.getName())); + } + } + + + private void copyBundleIcons(File resourcesDirectory) throws IOException { + for (BundleDocument bundleDocument : bundleDocuments) { + if (bundleDocument.hasIcon()) { + File iconFile = bundleDocument.getIconFile(); + copy(iconFile, new File(resourcesDirectory, iconFile.getName())); + } + } + } + + + private void writeInfoPlist(File file) throws IOException { + FileOutputStream output = new FileOutputStream(file); + PropertyLister plist = new PropertyLister(output); + + // Get started, write all necessary header info and open plist element + plist.writeStartDocument(); + + // Begin root dictionary + plist.writeStartDictElement(); + + // Write bundle properties + plist.writeProperty("CFBundleDevelopmentRegion", "English"); + plist.writeProperty("CFBundleExecutable", executableName); + plist.writeProperty("CFBundleIconFile", (iconFile == null) ? DEFAULT_ICON_NAME : iconFile.getName()); + plist.writeProperty("CFBundleIdentifier", identifier); + plist.writeProperty("CFBundleDisplayName", displayName); + plist.writeProperty("CFBundleInfoDictionaryVersion", "6.0"); + plist.writeProperty("CFBundleName", name); + plist.writeProperty("CFBundlePackageType", OS_TYPE_CODE); + plist.writeProperty("CFBundleShortVersionString", shortVersion); + plist.writeProperty("CFBundleVersion", version); + plist.writeProperty("CFBundleSignature", signature); + plist.writeProperty("NSHumanReadableCopyright", copyright); + + if (getInfo != null) { + plist.writeProperty("CFBundleGetInfoString", getInfo); + } + + if (applicationCategory != null) { + plist.writeProperty("LSApplicationCategoryType", applicationCategory); + } + + if (minimumSystem != null) { + plist.writeProperty("LSMinimumSystemVersion", minimumSystem); + } + + if (highResolutionCapable) { + plist.writeKey("NSHighResolutionCapable"); + plist.writeBoolean(true); + } + + if (runtime != null) { + plist.writeProperty("JVMRuntime", runtime.getDir().getParentFile().getParentFile().getName()); + } + + if (privileged != null) { + plist.writeProperty("JVMRunPrivileged", privileged); + } + + if (workingDirectory != null) { + plist.writeProperty("WorkingDirectory", workingDirectory); + } + + // Write main class name + plist.writeProperty("JVMMainClassName", mainClassName); + + // Write CFBundleDocument entries + plist.writeKey("CFBundleDocumentTypes"); + plist.writeStartArrayElement(); + for (BundleDocument bundleDocument: bundleDocuments) { + plist.writeStartDictElement(); + + plist.writeKey("CFBundleTypeExtensions"); + plist.writeStartArrayElement(); + for (String extension : bundleDocument.getExtensions()) { + plist.writeString(extension); + } + plist.writeEndElement(); + + if (bundleDocument.hasIcon()) { + plist.writeKey("CFBundleTypeIconFile"); + plist.writeString(bundleDocument.getIconName()); + } + + plist.writeKey("CFBundleTypeName"); + plist.writeString(bundleDocument.getName()); + + plist.writeKey("CFBundleTypeRole"); + plist.writeString(bundleDocument.getRole()); + + plist.writeKey("LSTypeIsPackage"); + plist.writeBoolean(bundleDocument.isPackage()); + + plist.writeEndElement(); + } + plist.writeEndElement(); + + // Write architectures + plist.writeKey("LSArchitecturePriority"); + plist.writeStartArrayElement(); + for (String architecture : architectures) { + plist.writeString(architecture); + } + plist.writeEndElement(); + + // Write Environment + plist.writeKey("LSEnvironment"); + plist.writeStartDictElement(); + plist.writeKey("LC_CTYPE"); + plist.writeString("UTF-8"); + plist.writeEndElement(); + + // Write options + plist.writeKey("JVMOptions"); + plist.writeStartArrayElement(); + for (String option : options) { + plist.writeString(option); + } + plist.writeEndElement(); + + // Write arguments + plist.writeKey("JVMArguments"); + plist.writeStartArrayElement(); + for (String argument : arguments) { + plist.writeString(argument); + } + plist.writeEndElement(); + + // End root dictionary + plist.writeEndElement(); + + // Close out the plist + plist.writeEndDocument(); + } + + + private void writePkgInfo(File file) throws IOException { + Writer out = new BufferedWriter(new FileWriter(file)); + + try { + out.write(OS_TYPE_CODE + signature); + out.flush(); + } finally { + out.close(); + } + } + + + private static void delete(File file) throws IOException { + Path filePath = file.toPath(); + + if (Files.exists(filePath, LinkOption.NOFOLLOW_LINKS)) { + if (Files.isDirectory(filePath, LinkOption.NOFOLLOW_LINKS)) { + File[] files = file.listFiles(); + + for (int i = 0; i < files.length; i++) { + delete(files[i]); + } + } + + Files.delete(filePath); + } + } + + + private static void copy(URL location, File file) throws IOException { + try (InputStream in = location.openStream()) { + Files.copy(in, file.toPath(), StandardCopyOption.REPLACE_EXISTING); + } + } + + + private static void copy(File source, File destination) throws IOException { + Path sourcePath = source.toPath(); + Path destinationPath = destination.toPath(); + + destination.getParentFile().mkdirs(); + + Files.copy(sourcePath, destinationPath, StandardCopyOption.REPLACE_EXISTING, LinkOption.NOFOLLOW_LINKS); + + if (Files.isDirectory(sourcePath, LinkOption.NOFOLLOW_LINKS)) { + String[] files = source.list(); + + for (int i = 0; i < files.length; i++) { + String file = files[i]; + copy(new File(source, file), new File(destination, file)); + } + } + } +} diff --git a/build/macosx/appbundler/src/com/oracle/appbundler/Architecture.java b/build/macosx/appbundler/src/com/oracle/appbundler/Architecture.java new file mode 100644 index 000000000..8c1d3d2b4 --- /dev/null +++ b/build/macosx/appbundler/src/com/oracle/appbundler/Architecture.java @@ -0,0 +1,45 @@ +/* + * Copyright 2012, The Infinite Kind 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. The Infinite Kind designates this + * particular file as subject to the "Classpath" exception as provided + * by The Infinite Kind in the LICENSE file that accompanied this code. + * + * 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. + * + */ + +package com.oracle.appbundler; + +/** + * Class representing an architecture that will be written in the Info.plist file + * to indicate which architectures the binary support. + */ +public class Architecture { + private String name = null; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + @Override + public String toString() { + return name; + } +} diff --git a/build/macosx/appbundler/src/com/oracle/appbundler/Argument.java b/build/macosx/appbundler/src/com/oracle/appbundler/Argument.java new file mode 100644 index 000000000..352c19c23 --- /dev/null +++ b/build/macosx/appbundler/src/com/oracle/appbundler/Argument.java @@ -0,0 +1,48 @@ +/* + * Copyright 2012, 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. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * 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.appbundler; + +/** + * Class representing an argument that will be passed to the Java application + * at startup. + */ +public class Argument { + private String value = null; + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } + + @Override + public String toString() { + return value; + } +} diff --git a/build/macosx/appbundler/src/com/oracle/appbundler/BundleDocument.java b/build/macosx/appbundler/src/com/oracle/appbundler/BundleDocument.java new file mode 100644 index 000000000..2f0a01fdf --- /dev/null +++ b/build/macosx/appbundler/src/com/oracle/appbundler/BundleDocument.java @@ -0,0 +1,121 @@ +/* + * Copyright 2012, The Infinite Kind 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. The Infinite Kind designates this + * particular file as subject to the "Classpath" exception as provided + * by The Infinite Kind in the LICENSE file that accompanied this code. + * + * 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. + * + */ + +package com.oracle.appbundler; + +import java.io.File; + +import org.apache.tools.ant.BuildException; + + +/** + * Represent a CFBundleDocument. + */ +public class BundleDocument { + private String name = "editor"; + private String role = ""; + private File icon = null; + private String[] extensions; + private boolean isPackage = false; + + private String capitalizeFirst(String string) { + char[] stringArray = string.toCharArray(); + stringArray[0] = Character.toUpperCase(stringArray[0]); + return new String(stringArray); + } + + public void setExtensions(String extensionsList) { + if(extensionsList == null) { + throw new BuildException("Extensions can't be null"); + } + + extensions = extensionsList.split(","); + for (String extension : extensions) { + extension.trim().toLowerCase(); + } + } + + public void setIcon(File icon) { + this.icon = icon; + } + + public void setName(String name) { + this.name = name; + } + + public void setRole(String role) { + this.role = capitalizeFirst(role); + } + + public void setIsPackage(String isPackageString) { + if(isPackageString.trim().equalsIgnoreCase("true")) { + this.isPackage = true; + } else { + this.isPackage = false; + } + } + +// public String getIcon() { +// return icon; +// } + + public String getIconName() { + return icon.getName(); + } + + + public File getIconFile() { + return icon; + } + + public String getName() { + return name; + } + + public String getRole() { + return role; + } + + public String[] getExtensions() { + return extensions; + } + + public boolean hasIcon() { + return icon != null; + } + + public boolean isPackage() { + return isPackage; + } + + @Override + public String toString() { + StringBuilder s = new StringBuilder(getName()); + s.append(" ").append(getRole()).append(" ").append(getIconName()). append(" "); + for(String extension : extensions) { + s.append(extension).append(" "); + } + + return s.toString(); + } +} diff --git a/build/macosx/appbundler/src/com/oracle/appbundler/GenericApp.icns b/build/macosx/appbundler/src/com/oracle/appbundler/GenericApp.icns new file mode 100644 index 000000000..20d16f39a Binary files /dev/null and b/build/macosx/appbundler/src/com/oracle/appbundler/GenericApp.icns differ diff --git a/build/macosx/appbundler/src/com/oracle/appbundler/Option.java b/build/macosx/appbundler/src/com/oracle/appbundler/Option.java new file mode 100644 index 000000000..d9eaf3f5e --- /dev/null +++ b/build/macosx/appbundler/src/com/oracle/appbundler/Option.java @@ -0,0 +1,47 @@ +/* + * Copyright 2012, 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. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * 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.appbundler; + +/** + * Class representing an option that will be passed to the JVM at startup. + */ +public class Option { + private String value = null; + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } + + @Override + public String toString() { + return value; + } +} diff --git a/build/macosx/appbundler/src/com/oracle/appbundler/PropertyLister.java b/build/macosx/appbundler/src/com/oracle/appbundler/PropertyLister.java new file mode 100644 index 000000000..2b4e7aa9e --- /dev/null +++ b/build/macosx/appbundler/src/com/oracle/appbundler/PropertyLister.java @@ -0,0 +1,133 @@ +package com.oracle.appbundler; + +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.io.UnsupportedEncodingException; +import java.util.Stack; + + +/** + * Class that handles Info.plist files. Started because of Java's brain dead + * XML implementation doesn't have simple indentation support, but evolved + * from there to hide some of the Info.plist innards for cleaner code in the + * AppBundlerTask object. + * @author fry at processing dot org + */ +class PropertyLister { + static private final String XML_HEADER = ""; + static private final String PLIST_DTD = ""; + static private final String PLIST_TAG = "plist"; + static private final String PLIST_VERSION_ATTRIBUTE = "version"; + static private final String DICT_TAG = "dict"; + static private final String KEY_TAG = "key"; + static private final String ARRAY_TAG = "array"; + static private final String STRING_TAG = "string"; + + PrintWriter writer; + String indentSpaces = " "; + Stack elements = new Stack<>(); + + + public PropertyLister(OutputStream output) throws UnsupportedEncodingException { + OutputStreamWriter osw = new OutputStreamWriter(output, "UTF-8"); + writer = new PrintWriter(osw); + } + + + void writeStartDocument() { + writer.println(XML_HEADER); + writer.println(PLIST_DTD); + + // Begin root 'plist' element + writeStartElement(PLIST_TAG, PLIST_VERSION_ATTRIBUTE, "1.0"); + } + + + void writeEndDocument() { + // End root 'plist' element + writeEndElement(); + + writer.flush(); + writer.close(); + } + + + void writeStartElement(String element, String... args) { + emitIndent(); + writer.print("<" + element); + + for (int i = 0; i < args.length; i += 2) { + String attr = args[i]; + String value = args[i+1]; + writer.print(" " + attr + "=\"" + value + "\""); + } + writer.println(">"); + elements.push(element); + } + + + void writeStartElement(String element) { + emitIndent(); + writer.println("<" + element + ">"); + elements.push(element); + } + + + void writeStartDictElement() { + writeStartElement(DICT_TAG); + } + + + void writeStartArrayElement() { + writeStartElement(ARRAY_TAG); + } + + + void writeEndElement() { + emitOutdent(); + writer.println(""); + } + + + void writeKey(String key) { + emitSingle(KEY_TAG, key); + } + + + void writeString(String value) { + emitSingle(STRING_TAG, value); + } + + + void writeBoolean(boolean value) { + emitIndent(); + writer.println("<" + (value ? "true" : "false") + "/>"); + } + + + void writeProperty(String property, String value) { + writeKey(property); + writeString(value); + } + + + private void emitSingle(String tag, String content) { + emitIndent(); + writer.println("<" + tag + ">" + content + ""); + } + + + private void emitIndent() { + for (int i = 0; i < elements.size(); i++) { + writer.print(indentSpaces); + } + } + + + private void emitOutdent() { + for (int i = 0; i < elements.size() - 1; i++) { + writer.print(indentSpaces); + } + } +} \ No newline at end of file diff --git a/build/macosx/pde.icns b/build/macosx/pde.icns new file mode 100644 index 000000000..9f697634c Binary files /dev/null and b/build/macosx/pde.icns differ diff --git a/build/macosx/template.app/Contents/Resources/processing.icns b/build/macosx/processing.icns similarity index 100% rename from build/macosx/template.app/Contents/Resources/processing.icns rename to build/macosx/processing.icns diff --git a/build/macosx/template.app/Contents/Info.plist b/build/macosx/template.app/Contents/Info.plist deleted file mode 100755 index 85f50779a..000000000 --- a/build/macosx/template.app/Contents/Info.plist +++ /dev/null @@ -1,120 +0,0 @@ - - - - - CFBundleName - Processing - - - CFBundleGetInfoString - VERSION, Copyright © Ben Fry and Casey Reas - CFBundleVersion - REVISION - CFBundleShortVersionString - VERSION - - - CFBundleAllowMixedLocalizations - true - CFBundleExecutable - JavaApplicationStub - CFBundleDevelopmentRegion - English - CFBundlePackageType - APPL - CFBundleSignature - Pde1 - CFBundleInfoDictionaryVersion - 6.0 - CFBundleIconFile - processing.icns - CFBundleIdentifier - org.processing.app - - CFBundleDocumentTypes - - - CFBundleTypeExtensions - - pde - java - - CFBundleTypeIconFile - pde.icns - CFBundleTypeName - Processing Source File - CFBundleTypeMIMETypes - - text/plain - - CFBundleTypeOSTypes - - TEXT - - CFBundleTypeRole - Editor - - - - - LSMinimumSystemVersion - 10.6.8 - - NSHighResolutionCapable - - - Java - - SplashFile - $JAVAROOT/lib/about.jpg - - VMOptions - - -Xms128M - -Xmx256M - - - MainClass - processing.app.Base - - - JVMVersion - 1.6* - - ClassPath - - $JAVAROOT/lib/pde.jar:$JAVAROOT/core/library/core.jar:$JAVAROOT/lib/ant.jar:$JAVAROOT/lib/ant-launcher.jar:$JAVAROOT/lib/antlr.jar:$JAVAROOT/lib/jna.jar:$JAVAROOT/lib/org-netbeans-swing-outline.jar:$JAVAROOT/lib/com.ibm.icu_4.4.2.v20110823.jar:$JAVAROOT/lib/jdi.jar:$JAVAROOT/lib/jdimodel.jar:$JAVAROOT/lib/org.eclipse.osgi_3.8.1.v20120830-144521.jar - - - Properties - - - javaroot - $JAVAROOT - - - apple.laf.useScreenMenuBar - true - - apple.awt.showGrowBox - true - com.apple.smallTabs - true - apple.awt.Antialiasing - false - apple.awt.TextAntialiasing - true - com.apple.hwaccel - true - apple.awt.use-file-dialog-packages - false - apple.awt.graphics.UseQuartz - true - - - - diff --git a/build/macosx/template.app/Contents/MacOS/JavaApplicationStub b/build/macosx/template.app/Contents/MacOS/JavaApplicationStub deleted file mode 100755 index 739948527..000000000 Binary files a/build/macosx/template.app/Contents/MacOS/JavaApplicationStub and /dev/null differ diff --git a/build/macosx/template.app/Contents/PkgInfo b/build/macosx/template.app/Contents/PkgInfo deleted file mode 100755 index 3f338b4a0..000000000 --- a/build/macosx/template.app/Contents/PkgInfo +++ /dev/null @@ -1 +0,0 @@ -APPLPde1 \ No newline at end of file diff --git a/build/macosx/template.app/Contents/Resources/pde.icns b/build/macosx/template.app/Contents/Resources/pde.icns deleted file mode 100644 index 214b19877..000000000 Binary files a/build/macosx/template.app/Contents/Resources/pde.icns and /dev/null differ diff --git a/build/shared/launch4j/bin/README.txt b/build/shared/launch4j/bin/README.txt deleted file mode 100644 index 5346d1e28..000000000 --- a/build/shared/launch4j/bin/README.txt +++ /dev/null @@ -1,68 +0,0 @@ -The MinGW binutils for Mac OS were built on OS X 10.6 by Ben Fry. - -The 64-bit Linux version was built on Ubuntu 12.04. - -The other binaries come from the original downloads on SourceForge. - - -Ben Fry - - -. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . - - -Mac OS X build instructions -12 November 2011 - -# Grab the launch4j download -wget http://downloads.sourceforge.net/launch4j/launch4j-3.0.2-macosx.tgz -tar xvfz launch4j-3.0.2-macosx.tgz -# Rename it so that we remember it's an Intel version -mv launch4j launch4j-3.0.2-macosx-intel - -# Find MinGW files here: http://sourceforge.net/projects/mingw/files/ -# Specifically this file: -wget http://downloads.sourceforge.net/mingw/binutils-2.19-src.tar.gz - -# To build the 32-bit version on Mac OS X -tar xvfz binutils-2.19-src.tar.gz -mv binutils-2.19 binutils-2.19-i386 -cd binutils-2.19-i386 -CXX='g++ -m32' CC='gcc -m32' CFLAGS=-m32 CXXFLAGS=-m32 LDFLAGS=-m32 ./configure --disable-werror --target=i686-pc-mingw32 --with-gnu-ld -make -# Lots of gibberish, followed by "make[1]: Nothing to be done for `all-target'." -# Files will be at ld/ld-new and binutils/windres -cd .. - -# Now build the 64-bit version -tar xvfz binutils-2.19-src.tar.gz -mv binutils-2.19 binutils-2.19-x64 -cd binutils-2.19-x64 -./configure --disable-werror --target=i686-pc-mingw32 -make -cd .. - -# Finally, merge the 32- and 64-bit versions together -lipo -create binutils-2.19-i386/ld/ld-new binutils-2.19-x64/ld/ld-new -output launch4j-3.0.2-macosx-intel/bin/ld -lipo -create binutils-2.19-i386/binutils/windres binutils-2.19-x64/binutils/windres -output launch4j-3.0.2-macosx-intel/bin/windres - - -. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . - - -Linux 64-bit build instructions -21 July 2012 - -# Find MinGW files here: http://sourceforge.net/projects/mingw/files/ - -wget http://downloads.sourceforge.net/mingw/binutils-2.19-src.tar.gz -tar xvfz binutils-2.19-src.tar.gz -cd binutils-2.19 -./configure --disable-werror --target=i686-pc-mingw32 -make -cd .. - -# Then copy them out -cp binutils-2.19/ld/ld-new ld-linux64 -cp binutils-2.19/binutils/windres windres-linux64 - diff --git a/build/shared/launch4j/bin/ld-linux32 b/build/shared/launch4j/bin/ld-linux32 deleted file mode 100755 index 1d3f4091d..000000000 Binary files a/build/shared/launch4j/bin/ld-linux32 and /dev/null differ diff --git a/build/shared/launch4j/bin/ld-linux64 b/build/shared/launch4j/bin/ld-linux64 deleted file mode 100755 index 3b6f8944c..000000000 Binary files a/build/shared/launch4j/bin/ld-linux64 and /dev/null differ diff --git a/build/shared/launch4j/bin/ld-macosx b/build/shared/launch4j/bin/ld-macosx deleted file mode 100755 index 14bc7196b..000000000 Binary files a/build/shared/launch4j/bin/ld-macosx and /dev/null differ diff --git a/build/shared/launch4j/bin/ld-windows32.exe b/build/shared/launch4j/bin/ld-windows32.exe deleted file mode 100755 index f388b9513..000000000 Binary files a/build/shared/launch4j/bin/ld-windows32.exe and /dev/null differ diff --git a/build/shared/launch4j/bin/windres-linux32 b/build/shared/launch4j/bin/windres-linux32 deleted file mode 100755 index a67a74be4..000000000 Binary files a/build/shared/launch4j/bin/windres-linux32 and /dev/null differ diff --git a/build/shared/launch4j/bin/windres-linux64 b/build/shared/launch4j/bin/windres-linux64 deleted file mode 100755 index f8f0be94d..000000000 Binary files a/build/shared/launch4j/bin/windres-linux64 and /dev/null differ diff --git a/build/shared/launch4j/bin/windres-macosx b/build/shared/launch4j/bin/windres-macosx deleted file mode 100755 index 6a9822cc7..000000000 Binary files a/build/shared/launch4j/bin/windres-macosx and /dev/null differ diff --git a/build/shared/launch4j/bin/windres-windows32.exe b/build/shared/launch4j/bin/windres-windows32.exe deleted file mode 100755 index 4ad2ae98a..000000000 Binary files a/build/shared/launch4j/bin/windres-windows32.exe and /dev/null differ diff --git a/build/shared/launch4j/head/head.o b/build/shared/launch4j/head/head.o deleted file mode 100644 index d595f9e78..000000000 Binary files a/build/shared/launch4j/head/head.o and /dev/null differ diff --git a/build/shared/lib/preferences.txt b/build/shared/lib/defaults.txt similarity index 94% rename from build/shared/lib/preferences.txt rename to build/shared/lib/defaults.txt index e6be447f2..5e0039b4d 100644 --- a/build/shared/lib/preferences.txt +++ b/build/shared/lib/defaults.txt @@ -26,6 +26,8 @@ # You'll have problems running Processing if you incorrectly # modify lines in this file. It will probably not start at all. +# AGAIN, DO NOT ALTER THIS FILE! I'M ONLY YELLING BECAUSE I LOVE YOU! + # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! @@ -106,14 +108,14 @@ editor.window.height.min.windows = 530 # Monaco is nicer on Mac OS X, so use that explicitly #editor.font.macosx = Monaco,plain,10 # trying for a built-in, consistent monospace for 2.0 -editor.font = processing.mono,plain,12 +#editor.font = processing.mono,plain,12 +editor.font.family = Source Code Pro +editor.font.size = 12 -# anti-aliased text, turned off by default -#editor.antialias=false -# linux fonts are hideous without anti-aliasing [0191] -#editor.antialias.linux=true -# turning anti-aliasing on by default for 2.0 -editor.antialias=true +# To reset everyone's default, replaced editor.antialias with editor.smooth +# for 2.1. Fonts are unusably gross on OS X (and Linux) w/o smoothing and +# the Oracle JVM, and many longtime users have anti-aliasing turned off. +editor.smooth = true # blink the caret by default editor.caret.blink = true @@ -165,7 +167,8 @@ toolbar.hide.image = false # font choice and size for the console #console.font = Monospaced,plain,11 #console.font.macosx = Monaco,plain,10 -console.font = processing.mono,plain,12 +#console.font = processing.mono,plain,12 +console.font.size = 12 # number of lines to show by default console.lines = 4 @@ -264,9 +267,9 @@ editor.token.invalid.style = #666666,bold # which platforms to export by default -export.application.platform.windows = true -export.application.platform.macosx = true -export.application.platform.linux = true +#export.application.platform.windows = true +#export.application.platform.macosx = true +#export.application.platform.linux = true # whether or not to export as full screen (present) mode export.application.fullscreen = false @@ -274,8 +277,8 @@ export.application.fullscreen = false # whether to show the stop button when exporting to application export.application.stop = true -# false will place all exported files into a single .jar -#export.applet.separate_jar_files = false +# embed Java by default for lower likelihood of problems +export.application.embed_java = true # set to false to no longer delete applet or application folders before export export.delete_target_folder = true diff --git a/build/shared/lib/fonts/SourceCodePro-Bold.ttf b/build/shared/lib/fonts/SourceCodePro-Bold.ttf new file mode 100644 index 000000000..a56f1fa5d Binary files /dev/null and b/build/shared/lib/fonts/SourceCodePro-Bold.ttf differ diff --git a/build/shared/lib/fonts/SourceCodePro-Semibold.ttf b/build/shared/lib/fonts/SourceCodePro-Semibold.ttf deleted file mode 100644 index b425f9cee..000000000 Binary files a/build/shared/lib/fonts/SourceCodePro-Semibold.ttf and /dev/null differ diff --git a/build/shared/lib/theme.txt b/build/shared/lib/theme.txt index ab31f72e8..70f11e26f 100755 --- a/build/shared/lib/theme.txt +++ b/build/shared/lib/theme.txt @@ -6,9 +6,7 @@ status.error.fgcolor = #ffffff status.error.bgcolor = #662000 status.edit.fgcolor = #000000 status.edit.bgcolor = #cc9900 -#status.font = SansSerif,plain,12 -#status.font.macosx = Helvetica,plain,12 -status.font = processing.sans,plain,13 +status.font = processing.sans,plain,14 # TABS # Settings for the tab area at the top. @@ -34,11 +32,11 @@ buttons.hide.color = #0E1B25 buttons.bgimage = true # TOOLBAR BUTTON TEXT -buttons.status.font = processing.sans,plain,13 +buttons.status.font = processing.sans,bold,13 buttons.status.color = #ffffff # MODE SELECTOR -mode.button.font = processing.sans,plain,12 +mode.button.font = processing.sans,bold,13 # outline color of the mode button mode.button.color = #ffffff @@ -46,7 +44,7 @@ mode.button.color = #ffffff # The editor line number status bar at the bottom of the screen linestatus.color = #ffffff linestatus.bgcolor = #29333d -linestatus.font = processing.sans,plain,12 +linestatus.font = processing.sans,plain,13 linestatus.height = 20 diff --git a/build/shared/revisions.txt b/build/shared/revisions.txt index fc4a21195..4b6516220 100644 --- a/build/shared/revisions.txt +++ b/build/shared/revisions.txt @@ -1,6 +1,714 @@ -PROCESSING 2.0.3 (REV 0221) - 23 August 2013 +PROCESSING 2.2.1 (REV 0227) - 19 May 2014 -Several OpenGL fixes. +A handful of bug fixes, the most prominent rolls back a change that broke +PDE X and other Modes and Tools. + ++ Bring back setIcon(Frame) for PDE X and others + https://github.com/processing/processing-experimental/issues/64 + ++ Add additional code for crashing when the Mode is changed or new editor + windows opened. + ++ Use mouseReleased() instead of mousePressed() in the color selector, + otherwise it registers the release as a click in the color window + https://github.com/processing/processing/issues/2514 + ++ Missing 'version' in contribution properties file causes NullPointerException + https://github.com/processing/processing/issues/2517 + ++ A handful of fixes to Auto Format + https://github.com/processing/processing/pull/2271 + ++ Command line tools not working on OS X due to AppleDouble file boogers. + https://github.com/processing/processing/issues/2520 + ++ Make "Archive Sketch" Tool force a .zip file extension + https://github.com/processing/processing/issues/2526 + ++ Event handling modifications in video and serial libraries w/ Python Mode + https://github.com/processing/processing/pull/2527 + https://github.com/processing/processing/pull/2528 + https://github.com/processing/processing/pull/2529 + ++ Permit mouse PRESS to set mouseX/mouseY + https://github.com/processing/processing/pull/2509 + ++ Fix for video: the loop() method was broken in the last release. + https://github.com/processing/processing/issues/2524 + ++ Updated reference files included in the download. + + +. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . + + +PROCESSING 2.2 (REV 0226) - 12 May 2014 + +Major changes to, and improvements upon, how "Export to Application" works. +Plus dozens of bug fixes for all manner of atrocities. + + +[ bug fixes and additions ] + ++ Sketches only starting once, or half-starting and hanging on Mac OS X. + A major problem on OS X, thanks to David Fokkema for tracking down a fix. + https://github.com/processing/processing/issues/2402 + https://github.com/processing/processing/pull/2455 + ++ Re-open current sketch in new mode editor if file extension is compatible. + This was a regression in 2.1.2 due to the Python Mode changes. + https://github.com/processing/processing/pull/2457 + https://github.com/processing/processing/issues/2456 + ++ Crash in the 'recent' menu on startup + https://github.com/processing/processing/issues/2463 + ++ Avoid conflict when some goofball has installed JNA DLLs in your path. + https://github.com/processing/processing/issues/2239 + ++ Add support for "import static" syntax from Java + https://github.com/processing/processing/issues/8 + https://github.com/processing/processing/pull/2273 + ++ Improve error handling during Tool loading. In previous releases, an + out of date QuickReference Tool was able to hang Processing. No longer. + https://github.com/processing/processing/issues/2229 + ++ Save the previous open dialog so that we return to the directory + https://github.com/processing/processing/pull/2366 + ++ "if-else" block formatting wasn't following Processing conventions + https://github.com/processing/processing/issues/364 + https://github.com/processing/processing/pull/2477 + ++ Tab characters not recognized or handled in the editor (since 2.1) + https://github.com/processing/processing/issues/2180 + https://github.com/processing/processing/issues/2183 + ++ Chinese text is overlapped in Processing 2.1 editor + https://github.com/processing/processing/issues/2173 + https://github.com/processing/processing/pull/2318 + https://github.com/processing/processing/pull/2323 + + +[ export to application ] + ++ The return of multi-platform export! Create applications for Windows + and Linux while using OS X. Create a Linux application from Windows. + Against my better judgement, we're supporting it again. It's extremely + difficult, but was disappointing to remove it earlier. + ++ When exporting with local Java embedded, always use that version + https://github.com/processing/processing/issues/2349 + ++ Change Windows export to use launch4j instead of our custom launcher. + This will fix many, many problems, but may introduce some new ones. + ++ Windows (64-bit) now creates a proper .exe instead of a .bat file + https://github.com/processing/processing/issues/923 + ++ Exported apps on Windows 64 were not quite working correctly + https://github.com/processing/processing/issues/2468 + ++ Improved icons on Windows for exported apps + ++ Add additional language and explanation to the Export dialog box + ++ Make it possible to edit the background colors for full screen as well as + the stop button color directly from the Export dialog box + https://github.com/processing/processing/issues/69 + ++ Exported apps reporting as "damaged" on OS X + https://github.com/processing/processing/issues/2095 + You'll have to install Xcode to remove the warnings, but it's possible + + +[ core ] + ++ Fix for splice() throwing a ClassCastException with other object types + https://github.com/processing/processing/issues/1445 + https://github.com/processing/processing/pull/2461 + ++ Add candDraw() method to the retina renderer to fix embedding problems + ++ Fix sketchPath() issue when used in other environments (i.e. Eclipse) + ++ Substitute MOVE cursor with HAND on OS X + https://github.com/processing/processing/issues/2358 + ++ Allow textWidth() with the default font + https://github.com/processing/processing/issues/2331 + https://github.com/processing/processing/pull/2338 + ++ Bug in relative moveto commands for SVG + https://github.com/processing/processing/issues/2377 + ++ Add a constructor to bind Server to a specific address + https://github.com/processing/processing/issues/2356 + ++ Fonts from loadFont() show up as blocks in P3D (regression) + https://github.com/processing/processing/issues/2465 + ++ loadPixels() problems in OpenGL + https://github.com/processing/processing/issues/2493 + + +. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . + + +PROCESSING 2.1.2 (REV 0225) - 15 April 2014 + +Lots of small bug fixes plus some additional changes to support +the new Python Mode, coming soon: https://github.com/jdf/processing.py + + +[ the pde ] + ++ The PDE was using 15% of CPU while just sitting idle. Thanks to + David Fokkema for the fix (and pull request). + https://github.com/processing/processing/issues/1561 + https://github.com/processing/processing/pull/2451 + ++ Fix exception caused by Runner when it can't find location + https://github.com/processing/processing/issues/2346 + https://github.com/processing/processing/pull/2359 + ++ Serial: Update to latest upstream (fixes potential port handle leak) + https://github.com/processing/processing/pull/2361 + ++ Add affordance for mode developers to run from Eclipse + https://github.com/processing/processing/pull/2422 + ++ Non-PDE extensions for modes cause a crash + https://github.com/processing/processing/issues/2419 + ++ Remove some hardcoding for .pde as extension + https://github.com/processing/processing/issues/2420 + ++ Update code signing for Processing.app for Mavericks changes + https://github.com/processing/processing/issues/2453 + + +[ the core ] + ++ sketchPath() was returning user.home in exported apps on OS X + https://github.com/processing/processing/issues/2181 + ++ Fix bug in StringDict(Reader) that wasn't setting the indices hashmap + ++ Call revalidate() via reflection so that build works under 1.6 (using + 1.6 very much not supported, but we need it for regression testing). + ++ Some text rendering improvements. Fairly limited in what we can fix here. + ++ PGraphics.colorCalcARGB(int, float) wasn't properly capping alpha values + https://github.com/processing/processing/issues/2439 + ++ Make sure that the window background color isn't the same as the default + sketch background color (otherwise the sketch area isn't clear). + https://github.com/processing/processing/issues/2297 + ++ Fix for occasional NullPointerException in paint() + https://github.com/processing/processing/issues/2354 + + +[ andres vs opengl, episode 225 ] + ++ copy() under OPENGL uses upside-down coordinates for cropping + https://github.com/processing/processing/issues/2345 + ++ Video on Windows causes exception + https://github.com/processing/processing/issues/2327 + ++ Shape Font Rendering was broken with the OpenGL Renderer + https://github.com/processing/processing/issues/2375 + ++ Depth buffer shouldn't be cleared when depth mask is disabled + https://github.com/processing/processing/issues/2296 + ++ Set pixels transparent by default in P2D/P3D + https://github.com/processing/processing/issues/2207 + ++ Unwind depth sorting because it was breaking DXF export + https://github.com/processing/processing/issues/2404 + ++ Sketch hangs if sketchRenderer() returns an OpenGL renderer + https://github.com/processing/processing/issues/2363 + ++ "buffer" uniform triggers shader compilation error + https://github.com/processing/processing/issues/2325 + buffer has been renamed to ppixels for shaders + ++ noLoop() clears screen on Windows 8 + https://github.com/processing/processing/issues/2416 + ++ Fix pixels[] array for video capture + https://github.com/processing/processing/issues/2424 + + +[ fixed in earlier releases ] + ++ draw() called again before finishing on OS X (retina issue) + https://github.com/processing/processing/issues/1709 + ++ get() not always setting alpha channel when used with point() + https://github.com/processing/processing/issues/1756 + ++ support for geometry and tessellation shaders (on desktop) + https://github.com/processing/processing/issues/2252 + + +. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . + + +PROCESSING 2.1.1 (REV 0224) - 21 January 2014 + +Several fixes for issues in 2.1 that weren't found in the 2.1 beta release. +Still a number of known issues, but this cleans up several of the biggies. + ++ Fixed infinite loop in Find/Replace + https://github.com/processing/processing/issues/2082 + ++ Updated to Minim 2.2 + https://github.com/processing/processing/pull/2250 + ++ Minor change to bracket handling + https://github.com/processing/processing/pull/2313 + ++ On the command line version, --no-java wasn't working properly + https://github.com/processing/processing/issues/2182 + ++ get() + video requires loadPixels in P2D/P3D + https://github.com/processing/processing/issues/2202 + + +[ windows ] + ++ Export to Application was broken on Windows + https://github.com/processing/processing/issues/2219 + ++ Right-click on selection was causing text to de-select on Windows + https://github.com/processing/processing/issues/2210 + + +[ mac os x ] + ++ On Mac OS X, the app was called procesing.app.Base + https://github.com/processing/processing/issues/2217 + ++ Better support for native libraries in exported applications on OS X + https://github.com/processing/processing/pull/2269 + + +[ serial library ] + ++ readStringUntil() missing from new serial library + https://github.com/processing/processing/issues/2174 + ++ Miscellaneous pdates to the serial library + https://github.com/processing/processing/pull/2265 + ++ Serial (apears to be) running slowly + https://github.com/processing/processing/issues/2249 + https://github.com/processing/processing/issues/2214 + ++ Read a single character at a time to emulate RXTX behavior + https://github.com/processing/processing/pull/2240 + ++ Add basic tests for throughput and latency + https://github.com/processing/processing/pull/2225 + ++ Add a debug() method to Serial + https://github.com/processing/processing/pull/2237 + ++ Switch the examples over to use printArray() + https://github.com/processing/processing/pull/2226 + ++ Handle the UnsatisfiedLinkError when loading the native library fails + https://github.com/processing/processing/pull/2266 + + +[ core fixes ] + ++ PImage resize() causes PImage not to be rendered in JAVA2D + https://github.com/processing/processing/issues/2179 + ++ Remove make.sh from core source folder... ancient + ++ Remove println() from the dataPath() method + ++ Add special case for 'null' to println() + ++ Added print() method to IntList + ++ Fix esoteric typo with alpha and color + https://github.com/processing/processing/issues/2230 + ++ pushStyle/popStyle should save/restore blendMode + https://github.com/processing/processing/issues/2232 + + +[ opengl updates ] + ++ PImage copy() function used with P2D flips the image + https://github.com/processing/processing/issues/2171 + ++ Filter shaders don't need to use the texture uniform + https://github.com/processing/processing/issues/2204 + ++ texture() bug with stroke() in P2D + https://github.com/processing/processing/issues/2205 + ++ Allow sharing of GL context amongst multiple windows + https://github.com/processing/processing/issues/1698 + ++ Texture sampling setting ignored when creating an offscreen PGraphics + https://github.com/processing/processing/issues/1900 + ++ Rounded rectangle broken with Processing 2.1 P3D renderer + https://github.com/processing/processing/issues/2193 + ++ Clear the global PGL on dispose() + https://github.com/processing/processing/pull/2279 + ++ Pie arcs have stroke between endpoints in P2D/P3D + https://github.com/processing/processing/issues/2233 + + +[ missing in the 2.1 release notes ] + ++ init() not called on tools until later + https://github.com/processing/processing/issues/1859 + ++ Finish changes so the PDE can use an unmodified JRE + https://github.com/processing/processing/issues/1840 + + +. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . + + +PROCESSING 2.1 (REV 0223) - 27 October 2013 + +There have been major changes since 2.0.3, most of them are outlined in +the release notes for 2.1 beta 1 (look down a few dozen lines). + +This release includes a few updates to fix problems introduced in the beta +release, as well as additional general bug fixes, especially for OpenGL. + ++ Added an option to not embed the Java runtime into an exported application. + This saves you the 100 MB footprint, but requires your users to install + Java 7u45 or later. Details on the same page that nobody read last time: + http://wiki.processing.org/w/Export_Info_and_Tips + ++ The new println() (see the beta 1 notes) makes some old code behave a + little differently. In the past, println() with an array would write + out the array, one element per line, with the index in the front. i.e.: + PFont.list()) would write something like this to the console: + + [0] "Serif" + [1] "SansSerif" + [2] "Monospaced" + [3] "Dialog" + [4] "DialogInput" + [5] "ACaslonPro-Bold" + [6] "ACaslonPro-BoldItalic" + ...and so on + + Now it's going to write out something like: + + Serif SansSerif Monospaced Dialog DialogInput ACaslonPro-Bold... + + To get the old behavior, use printArray(). It's the price of progress, + and shouldn't really "break" anyone's code since it's just writing to the + console. We think the new syntax outweighs the downside of the change. + + With arrays of primitive types (int[], float[], anything that's not an + object), we've added code so that println() works as before. But we + can't do the same for arrays of objects, such as String. + ++ The preference for font smoothing (anti-aliasing) in the editor has been + reset in this release. Fonts are unusably gross on OS X (and Linux) + without smoothing and Oracle's version of Java (now in use with 2.1), + and many longtime users have anti-aliasing turned off. You can still + turn off smoothing in the Preferences window, but the results may be poor. + https://github.com/processing/processing/issues/2164 + https://github.com/processing/processing/issues/2160 + + +[ bug fixes ] + ++ Fix dataPath() problem with OS X (was breaking Movie on export) + ++ Command line processing-java was broken in 2.1 beta 1 on OS X + https://github.com/processing/processing/issues/2159 + ++ Fix a situation where processing-java would return 0 instead of 1 on errors + https://github.com/processing/processing/issues/1798#issuecomment-26711847 + ++ Alpha values from the pixels array were coming back as 0 + https://github.com/processing/processing/issues/2030 + ++ Additional UI font tweaks due to decreased legibility with Oracle Java + https://github.com/processing/processing/issues/2135 + + +[ OpenGL updates ] + ++ Using sketchQuality() does not work properly with P3D, OPENGL, P2D + https://github.com/processing/processing/pull/2157 + ++ Fix crashes when the sketch window is resized + https://github.com/processing/processing/issues/1880 + https://github.com/processing/processing/pull/2156 + ++ scale() wasn't affecting stroke weight in P3D + https://github.com/processing/processing/issues/2162 + ++ Add set(boolean) to PShader + https://github.com/processing/processing/issues/1991 + https://github.com/processing/processing/pull/1993 + ++ Add PMatrix.preApply(PMatrix) + https://github.com/processing/processing/pull/2146 + https://github.com/processing/processing/issues/2145 + ++ Updated to another version of JOGL (jogl-2.1-b1115, gluegen-2.1-b735) + for OS X 10.9 support. + ++ Add warning when no uv texture coordinates are supplied + https://github.com/processing/processing/issues/2034 + ++ Flicker issues when resizing P2D/P3D/OPENGL + https://github.com/processing/processing/issues/15 + ++ Additional fix for occasional flash/flicker with drawing complex scenes + https://github.com/processing/processing/commit/cca2f08a24ef892c494f5a75aa0e4b01de7e5d8a + + +. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . + + +PROCESSING 2.1 beta 1 (REV 0222) - 20 October 2013 + +This release contains major changes. The big ones: + ++ Java 7 is now used across all platforms. On Mac OS X, + we're now embedding Java 7u45. More info here: + http://wiki.processing.org/w/Supported_Platforms#Java_Versions + ++ Major changes have been made to Export to Application. + Read here: http://wiki.processing.org/w/Export_Info_and_Tips + ++ print() and println() now make debugging fun! They can now take any + number of parameters, which means that code like: + println(x, y, mouseX, mouseY); + will print out + 3 5 142 220 + No more println(x + " " + y + " " + mouseX + " " + mouseY); + that was for suckers! + ++ A new serial library has been added! The results of Gottfried Haider's + Google Summer of Code project now replaces the old serial library. + +And everyone should wish Casey Reas happy birthday today! +This release is my birthday present for him. What did you get him? + +Here's a more detailed rundown of what else is in this release: + + +[ new features and additions ] + ++ For people using Eclipse, the new print() and println() methods add + some quirks because of how println() works for arrays in previous + versions of Processing. When printing an array, you should instead use + the new printArray() function, which works the same as the old println(). + This will help avoid the compiler warnings with Eclipse and others. + In the PDE, this should be transparent, because warnings are not shown. + https://github.com/processing/processing/issues/2056 + ++ Update the JavaDoc, remove java.* package prefix ugliness. Also link + out to the online version of the Oracle documentation. + ++ Major work on the source and the build scripts as we completed the + transition to Java 7, and away from Apple's deprecated Java 6. + http://wiki.processing.org/w/Supported_Platforms#Mac_OS_X + ++ Incorporated a version of Oracle's appbundler for OS X build. Made heavy + modifications to the original version to add features and simplify. + ++ Culled un-needed resources from the Java 7 runtime to save space. + Unfortunately Java 7 is also much larger than Java 6, so the size change + isn't as considerable as we would hope. + ++ Remove unused/outdated 'Mangler' Tool example + ++ Remove video library for other platforms in download. This saves + significant space because we're not doing cross-platform export anymore. + ++ Update apple.jar file with new version + https://developer.apple.com/legacy/library/samplecode/AppleJavaExtensions/Introduction/Intro.html + ++ Update build instructions on Github + https://github.com/processing/processing/wiki/Build-Instructions + https://github.com/processing/processing/issues/1629 + ++ Many changes to the Supported Platforms page + http://wiki.processing.org/w/Supported_Platforms + ++ Updates to the Export page to cover changes in this release + http://wiki.processing.org/w/Export_Info_and_Tips + ++ Removed the 32- and 64-bit selector from the Preferences on OS X. + Java 7 on OS X only supports 64-bit, so 32-bit is no longer available. + + +[ editor fixes ] + ++ Deal with null/missing folders for Tools and Modes + https://github.com/processing/processing/issues/2068 + ++ Non-compliant libraries cause crash on "Add Library" + https://github.com/processing/processing/issues/2026 + ++ Bad tools could bring down the environment + http://code.google.com/p/processing/issues/detail?id=798 + https://github.com/processing/processing/issues/836 + ++ Open new PDE maximized when current PDE is maximized + https://github.com/processing/processing/pull/2037 + ++ cmd-left was bringing up the text area popup, causing X Mode weirdness + https://github.com/processing/processing/issues/2103 + + +[ core bug fixes ] + ++ Screen stops updating sometimes with retina displays on OS X + https://github.com/processing/processing/issues/1699 + This was an Apple bug, and is fixed by our switch to Oracle's Java 7. + ++ Background color for present mode had no effect + https://github.com/processing/processing/issues/2071 + https://github.com/processing/processing/pull/2072 + ++ Add desktopPath() and desktopFile() methods for testing (not finished) + ++ Unicode NLF causing problems in XML files + https://github.com/processing/processing/issues/2100 + ++ Fix image transparency in PDF output + https://github.com/processing/processing/pull/2070 + ++ Java2D images crash after being resized + https://github.com/processing/processing/issues/2113 + ++ Constrain lerpColor() between 0 and 1. Unlike lerp(), where it might + make mathematical sense, going outside the boundary colors produces + really messy results. + ++ JSONObject/Array.format(-1) not working on embedded JSONObjects + https://github.com/processing/processing/issues/2119 + ++ Fix insertRow() bug with Table + https://github.com/processing/processing/issues/2137 + + +[ opengl updates ] + ++ Updated to JOGL 2.1.0 + https://github.com/processing/processing/issues/2136 + ++ Fixed inconsistency with P2D and resetMatrix() + https://github.com/processing/processing/issues/2087 + ++ Deal with text rendering problems + https://github.com/processing/processing/issues/2109 + ++ Fix textSize() problem with P2D + https://github.com/processing/processing/issues/2073 + ++ Repair incorrectly applied transformations in retained mode + https://github.com/processing/processing/issues/2097 + ++ push/popStyle() was causing color problems with P2D/P3D + https://github.com/processing/processing/issues/2102 + ++ Child SVG elements were misplaced when rendering with P2D/P3D + https://github.com/processing/processing/issues/2086 + ++ SUBTRACT and DIFFERENCE blend modes are swapped + https://github.com/processing/processing/issues/2075 + ++ Throw an error for textureMode(REPEAT) + https://github.com/processing/processing/issues/2052 + ++ Vertex codes were not being properly set in P2D/P3D + https://github.com/processing/processing/issues/2131 + ++ Some box normals were inverted + https://github.com/processing/processing/issues/2151 + + +[ new serial library ] + ++ Incorporate the new serial library. Woohoo! + https://github.com/processing/processing/pull/2093 + ++ 64-bit version of serial library + http://code.google.com/p/processing/issues/detail?id=1237 + https://github.com/processing/processing/issues/1275 + ++ Closed several serial bugs because they're no longer relevant: + http://code.google.com/p/processing/issues/detail?id=52 + http://code.google.com/p/processing/issues/detail?id=318 + https://github.com/processing/processing/issues/2114 + https://github.com/processing/processing/issues/2066 + https://github.com/processing/processing/issues/1460 + https://github.com/processing/processing/issues/1374 + + +[ font fixes and changes ] + ++ Add ability to change the editor (and console) font from a menu + in the Preferences window. + https://github.com/processing/processing/issues/2078 + ++ Add ability to change the console font size from the Preferences window. + http://code.google.com/p/processing/issues/detail?id=226 + https://github.com/processing/processing/issues/265 + ++ Allow editor and console font changes without restart. + ++ Anti-aliasing fix for the editor line status. + ++ Change to bold instead of semibold version of Source Code Pro. + The semibold wasn't mapping properly as the same family. + ++ Use the same font in the console as the editor. + ++ Fix Windows/Linux console font issues. + + +[ movie maker ] + ++ Fix default gamma issues inside MovieMaker by adding extra atom + ++ TGA files cause Movie Maker to not work properly + https://github.com/processing/processing/issues/1933 + ++ Fix file selection dialog with MovieMaker (instead of the nasty + Swing-based version that was in use) + ++ Add support for many other image file types to Movie Maker + + +. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . + + +PROCESSING 2.0.3 (REV 0221) - 5 September 2013 + +Lots of bug fixes, primarily a ton of work by Andres to improve +OpenGL rendering with P2D and P3D. [ andres vs the volcano ] @@ -47,11 +755,30 @@ Several OpenGL fixes. https://github.com/processing/processing/pull/2022 https://github.com/processing/processing/issues/2021 ++ Copy doesn't produce a true copy with P2D and P3D renderers + https://github.com/processing/processing/issues/1924 + ++ Additional improvements to memory handling with images + https://github.com/processing/processing/issues/1975 + ++ Additional memory handling changes for render buffers + https://github.com/processing/processing/issues/1776 + ++ PShape does not draw arc properly + https://github.com/processing/processing/issues/1990 + ++ PShape style is not restored after calling enableStyle in P2D/P3D + https://github.com/processing/processing/issues/2061 + [ other bug fixes ] + Fix options parsing on loadTable() to handle spaces. ++ PVector.angleBetween() returns 0 for 3D vectors whenever x and y are both 0 + https://github.com/processing/processing/issues/2045 + https://github.com/processing/processing/pull/2046 + . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . diff --git a/build/shared/tools/Mangler/make.sh b/build/shared/tools/Mangler/make.sh deleted file mode 100755 index e1c1ce37b..000000000 --- a/build/shared/tools/Mangler/make.sh +++ /dev/null @@ -1,11 +0,0 @@ -#!/bin/sh - -# The pde.jar file may be buried inside the .app file on Mac OS X. -PDE=`find ../.. -name pde.jar` - -javac -target 1.5 \ - -cp "../../lib/core.jar:$PDE" \ - -d bin \ - src/Mangler.java - -cd bin && zip -r ../tool/mangler.jar * && cd .. diff --git a/build/shared/tools/Mangler/src/Mangler.java b/build/shared/tools/Mangler/src/Mangler.java deleted file mode 100644 index cfd527954..000000000 --- a/build/shared/tools/Mangler/src/Mangler.java +++ /dev/null @@ -1,94 +0,0 @@ -/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */ - -/* - Part of the Processing project - http://processing.org - - Copyright (c) 2008 Ben Fry and Casey Reas - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program 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 for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software Foundation, - Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -*/ - -package com.transformers.supermangletron; - - -import java.text.SimpleDateFormat; -import java.util.Date; - -import javax.swing.JOptionPane; - -import processing.app.Editor; -import processing.app.tools.Tool; - - -/** - * Example Tools menu entry. - */ -public class Mangler implements Tool { - Editor editor; - - - public void init(Editor editor) { - this.editor = editor; - } - - - public String getMenuTitle() { - return "Mangle Selection"; - } - - - public void run() { - String sketchName = editor.getSketch().getName(); - - Object[] options = { "Yes, please", "No, thanks" }; - int result = JOptionPane.showOptionDialog(editor, - "Is " + sketchName + - " ready for destruction?", - "Super Mangle Tron", - JOptionPane.YES_NO_OPTION, - JOptionPane.QUESTION_MESSAGE, - null, - options, - options[1]); - if (result == JOptionPane.YES_OPTION) { - mangleSelection(); - } - } - - - protected void mangleSelection() { - if (editor.isSelectionActive()) { - String selection = editor.getSelectedText(); - char[] stuff = selection.toCharArray(); - // Randomly swap a bunch of characters in the text - for (int i = 0; i < stuff.length / 10; i++) { - int a = (int) (Math.random() * stuff.length); - int b = (int) (Math.random() * stuff.length); - if (stuff[a] == '\n' || stuff[b] == '\n') { - continue; // skip newline characters - } - stuff[a] = selection.charAt(b); - stuff[b] = selection.charAt(a); - } - editor.startCompoundEdit(); - editor.setSelectedText(new String(stuff)); - editor.stopCompoundEdit(); - editor.statusNotice("Now that feels better, doesn't it?"); - - } else { - editor.statusError("No selection, no dice."); - } - } -} diff --git a/build/shared/tools/MovieMaker/.classpath b/build/shared/tools/MovieMaker/.classpath index 74aba6a10..33f763597 100644 --- a/build/shared/tools/MovieMaker/.classpath +++ b/build/shared/tools/MovieMaker/.classpath @@ -3,5 +3,6 @@ + diff --git a/build/shared/tools/MovieMaker/.gitignore b/build/shared/tools/MovieMaker/.gitignore index 06fd927b5..7b057f39e 100644 --- a/build/shared/tools/MovieMaker/.gitignore +++ b/build/shared/tools/MovieMaker/.gitignore @@ -1,2 +1,4 @@ bin -tool + + + diff --git a/build/shared/tools/MovieMaker/build.xml b/build/shared/tools/MovieMaker/build.xml old mode 100644 new mode 100755 index a30d993fe..419914718 --- a/build/shared/tools/MovieMaker/build.xml +++ b/build/shared/tools/MovieMaker/build.xml @@ -1,30 +1,24 @@ - + - - - - - + - + classpath="../../../../app/bin" + nowarn="true" + compiler="org.eclipse.jdt.core.JDTCompilerAdapter"> + + diff --git a/build/shared/tools/MovieMaker/src/ch/randelshofer/gui/datatransfer/CompositeTransferable.java b/build/shared/tools/MovieMaker/src/ch/randelshofer/gui/datatransfer/CompositeTransferable.java index 22275f310..72d12198e 100644 --- a/build/shared/tools/MovieMaker/src/ch/randelshofer/gui/datatransfer/CompositeTransferable.java +++ b/build/shared/tools/MovieMaker/src/ch/randelshofer/gui/datatransfer/CompositeTransferable.java @@ -20,8 +20,8 @@ import java.io.*; * @author Werner Randelshofer */ public class CompositeTransferable implements java.awt.datatransfer.Transferable { - private HashMap transferables = new HashMap(); - private LinkedList flavors = new LinkedList(); + private HashMap transferables = new HashMap(); + private LinkedList flavors = new LinkedList(); /** Creates a new instance of CompositeTransferable */ public CompositeTransferable() { @@ -48,7 +48,7 @@ public class CompositeTransferable implements java.awt.datatransfer.Transferable * not supported. */ public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException, IOException { - Transferable t = (Transferable) transferables.get(flavor); + Transferable t = transferables.get(flavor); if (t == null) throw new UnsupportedFlavorException(flavor); return t.getTransferData(flavor); } @@ -60,7 +60,7 @@ public class CompositeTransferable implements java.awt.datatransfer.Transferable * @return an array of data flavors in which this data can be transferred */ public DataFlavor[] getTransferDataFlavors() { - return (DataFlavor[]) flavors.toArray(new DataFlavor[transferables.size()]); + return flavors.toArray(new DataFlavor[transferables.size()]); } /** diff --git a/build/shared/tools/MovieMaker/src/ch/randelshofer/media/quicktime/QuickTimeWriter.java b/build/shared/tools/MovieMaker/src/ch/randelshofer/media/quicktime/QuickTimeWriter.java index 15893e41d..001ea9ada 100644 --- a/build/shared/tools/MovieMaker/src/ch/randelshofer/media/quicktime/QuickTimeWriter.java +++ b/build/shared/tools/MovieMaker/src/ch/randelshofer/media/quicktime/QuickTimeWriter.java @@ -1268,7 +1268,12 @@ public class QuickTimeWriter { // A 32-bit integer containing the number of sample descriptions that follow. // A 32-bit integer indicating the number of bytes in the sample description. - d.writeInt(86); // sampleDescriptionTable[0].size +// d.writeInt(86); // sampleDescriptionTable[0].size + d.writeInt(86 + 12); + // This looks like a bug, it'll be incorrect if the color table + // is written down below. [fry 131008] + // 12 bytes for gamma (appears from the file) + // 18 bytes for colr d.writeType(mediaCompressionType); // sampleDescriptionTable[0].type @@ -1353,13 +1358,36 @@ public class QuickTimeWriter { // for the specified depth. Depths of 16, 24, and 32 have no // color table. - - if (videoColorTable != null) { writeColorTableAtom(leaf); } + // Add gamma atom so things aren't all washed out [fry 131008] + writeGammaAtom(leaf); } + /** + * Color table atoms define a list of preferred colors for displaying + * the movie on devices that support only 256 colors. The list may + * contain up to 256 colors. These optional atoms have a type value of + * 'ctab'. The color table atom contains a Macintosh color table data + * structure. + * + * @param stblAtom + * @throws IOException + */ + protected void writeGammaAtom(CompositeAtom stblAtom) throws IOException { + DataAtom leaf; + DataAtomOutputStream d; + leaf = new DataAtom("gama"); + stblAtom.add(leaf); + + d = leaf.getOutputStream(); + + // Write the gamma value as a 16:16 fixed number + //d.writeFixed16D16(1.8); + d.writeFixed16D16(2.2); + } + /** * Color table atoms define a list of preferred colors for displaying * the movie on devices that support only 256 colors. The list may diff --git a/build/shared/tools/MovieMaker/src/processing/app/tools/Chooser.java b/build/shared/tools/MovieMaker/src/processing/app/tools/Chooser.java new file mode 100644 index 000000000..2585f0563 --- /dev/null +++ b/build/shared/tools/MovieMaker/src/processing/app/tools/Chooser.java @@ -0,0 +1,243 @@ +package processing.app.tools; + +import java.awt.EventQueue; +import java.awt.FileDialog; +import java.awt.Frame; +import java.io.File; + +import javax.swing.JFileChooser; + + +/** File chooser additions, cannibalized from PApplet. */ +class Chooser { + static final boolean useNativeSelect = true; + + + static abstract class Callback { + //abstract void select(File file); + void handle(final File file) { + EventQueue.invokeLater(new Runnable() { +// new Thread(new Runnable() { + public void run() { + select(file); + } + }); +// }).start(); + } + + abstract void select(File file); + } + + +// Frame parent; +// +// public Chooser(Frame parent) { +// this.parent = parent; +// } + + /** + * Open a platform-specific file chooser dialog to select a file for input. + * After the selection is made, the selected File will be passed to the + * 'callback' function. If the dialog is closed or canceled, null will be + * sent to the function, so that the program is not waiting for additional + * input. The callback is necessary because of how threading works. + * + *
+   * void setup() {
+   *   selectInput("Select a file to process:", "fileSelected");
+   * }
+   *
+   * void fileSelected(File selection) {
+   *   if (selection == null) {
+   *     println("Window was closed or the user hit cancel.");
+   *   } else {
+   *     println("User selected " + fileSeleted.getAbsolutePath());
+   *   }
+   * }
+   * 
+ * + * For advanced users, the method must be 'public', which is true for all + * methods inside a sketch when run from the PDE, but must explicitly be + * set when using Eclipse or other development environments. + * + * @webref input:files + * @param prompt message to the user + * @param callback name of the method to be called when the selection is made + */ +// public void selectInput(String prompt, String callback) { +// selectInput(prompt, callback, null); +// } + + +// public void selectInput(String prompt, String callback, File file) { +// selectInput(prompt, callback, file, this); +// } + + +// public void selectInput(String prompt, String callback, +// File file, Object callbackObject) { +// selectInput(prompt, callback, file, callbackObject, selectFrame()); +// } + + + static public void selectInput(Frame parent, String prompt, File file, + Callback callback) { + selectImpl(parent, prompt, file, callback, FileDialog.LOAD); + } + + + /** + * See selectInput() for details. + * + * @webref output:files + * @param prompt message to the user + * @param callback name of the method to be called when the selection is made + */ +// public void selectOutput(String prompt, String callback) { +// selectOutput(prompt, callback, null); +// } +// +// public void selectOutput(String prompt, String callback, File file) { +// selectOutput(prompt, callback, file, this); +// } +// +// +// public void selectOutput(String prompt, String callback, +// File file, Object callbackObject) { +// selectOutput(prompt, callback, file, callbackObject, selectFrame()); +// } + + + static public void selectOutput(Frame parent, String prompt, File file, + Callback callback) { + selectImpl(parent, prompt, file, callback, FileDialog.SAVE); + } + + + static protected void selectImpl(final Frame parentFrame, + final String prompt, + final File defaultSelection, + final Callback callback, + final int mode) { +// EventQueue.invokeLater(new Runnable() { +// public void run() { + File selectedFile = null; + + if (useNativeSelect) { + FileDialog dialog = new FileDialog(parentFrame, prompt, mode); + if (defaultSelection != null) { + dialog.setDirectory(defaultSelection.getParent()); + dialog.setFile(defaultSelection.getName()); + } + dialog.setVisible(true); + String directory = dialog.getDirectory(); + String filename = dialog.getFile(); + if (filename != null) { + selectedFile = new File(directory, filename); + } + + } else { + JFileChooser chooser = new JFileChooser(); + chooser.setDialogTitle(prompt); + if (defaultSelection != null) { + chooser.setSelectedFile(defaultSelection); + } + + int result = -1; + if (mode == FileDialog.SAVE) { + result = chooser.showSaveDialog(parentFrame); + } else if (mode == FileDialog.LOAD) { + result = chooser.showOpenDialog(parentFrame); + } + if (result == JFileChooser.APPROVE_OPTION) { + selectedFile = chooser.getSelectedFile(); + } + } + //selectCallback(selectedFile, callbackMethod, callbackObject); + callback.handle(selectedFile); +// } +// }); + } + + + /** + * See selectInput() for details. + * + * @webref input:files + * @param prompt message to the user + * @param callback name of the method to be called when the selection is made + */ +// public void selectFolder(String prompt, String callback) { +// selectFolder(prompt, callback, null); +// } +// +// +// public void selectFolder(String prompt, String callback, File file) { +// selectFolder(prompt, callback, file, this); +// } +// +// +// public void selectFolder(String prompt, String callback, +// File file, Object callbackObject) { +// selectFolder(prompt, callback, file, callbackObject, selectFrame()); +// } + + + static public void selectFolder(final Frame parentFrame, + final String prompt, + final File defaultSelection, + final Callback callback) { +// EventQueue.invokeLater(new Runnable() { +// public void run() { + File selectedFile = null; + + if (System.getProperty("os.name").contains("Mac") && useNativeSelect) { + FileDialog fileDialog = + new FileDialog(parentFrame, prompt, FileDialog.LOAD); + System.setProperty("apple.awt.fileDialogForDirectories", "true"); + fileDialog.setVisible(true); + System.setProperty("apple.awt.fileDialogForDirectories", "false"); + String filename = fileDialog.getFile(); + if (filename != null) { + selectedFile = new File(fileDialog.getDirectory(), fileDialog.getFile()); + } + } else { + JFileChooser fileChooser = new JFileChooser(); + fileChooser.setDialogTitle(prompt); + fileChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); + if (defaultSelection != null) { + fileChooser.setSelectedFile(defaultSelection); + } + + int result = fileChooser.showOpenDialog(parentFrame); + if (result == JFileChooser.APPROVE_OPTION) { + selectedFile = fileChooser.getSelectedFile(); + } + } + //selectCallback(selectedFile, callbackMethod, callbackObject); + callback.handle(selectedFile); +// } +// }); + } + + +// static private void selectCallback(File selectedFile, +// String callbackMethod, +// Object callbackObject) { +// try { +// Class callbackClass = callbackObject.getClass(); +// Method selectMethod = +// callbackClass.getMethod(callbackMethod, new Class[] { File.class }); +// selectMethod.invoke(callbackObject, new Object[] { selectedFile }); +// +// } catch (IllegalAccessException iae) { +// System.err.println(callbackMethod + "() must be public"); +// +// } catch (InvocationTargetException ite) { +// ite.printStackTrace(); +// +// } catch (NoSuchMethodException nsme) { +// System.err.println(callbackMethod + "() could not be found"); +// } +// } +} \ No newline at end of file diff --git a/build/shared/tools/MovieMaker/src/processing/app/tools/MovieMaker.java b/build/shared/tools/MovieMaker/src/processing/app/tools/MovieMaker.java index c3e29e860..ec01f3762 100644 --- a/build/shared/tools/MovieMaker/src/processing/app/tools/MovieMaker.java +++ b/build/shared/tools/MovieMaker/src/processing/app/tools/MovieMaker.java @@ -1,7 +1,7 @@ package processing.app.tools; /* * Nearly all of this code is - * Copyright © 2010-2011 Werner Randelshofer, Immensee, Switzerland. + * Copyright (c) 2010-2011 Werner Randelshofer, Immensee, Switzerland. * All rights reserved. * (However, he should not be held responsible for the current mess of a hack * that it has become.) @@ -11,57 +11,58 @@ package processing.app.tools; * For details see accompanying license terms. */ -import ch.randelshofer.gui.datatransfer.FileTextFieldTransferHandler; -import ch.randelshofer.media.mp3.MP3AudioInputStream; -import ch.randelshofer.media.quicktime.QuickTimeWriter; - -import java.awt.Font; -import java.awt.Graphics2D; -import java.awt.RenderingHints; -import java.awt.Toolkit; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.awt.event.KeyEvent; -import java.awt.event.WindowAdapter; -import java.awt.event.WindowEvent; -import java.awt.image.BufferedImage; -import java.awt.image.DataBufferInt; -import java.io.File; -import java.io.FileFilter; -import java.io.IOException; -import java.util.Arrays; +import java.awt.*; +import java.awt.event.*; +import java.awt.image.*; +import java.io.*; +import java.util.*; import java.util.prefs.Preferences; + import javax.imageio.ImageIO; -import javax.sound.sampled.AudioFormat; -import javax.sound.sampled.AudioInputStream; -import javax.sound.sampled.AudioSystem; -import javax.sound.sampled.UnsupportedAudioFileException; +import javax.sound.sampled.*; import javax.swing.*; import javax.swing.border.EmptyBorder; import javax.swing.filechooser.FileSystemView; import processing.app.Editor; +import ch.randelshofer.gui.datatransfer.FileTextFieldTransferHandler; +import ch.randelshofer.media.mp3.MP3AudioInputStream; +import ch.randelshofer.media.quicktime.QuickTimeWriter; -// TODO [fry 2011-09-06] -// + The dialog box is super ugly. It's a hacked up version of the previous -// interface, but it'll take a bit of time to clean it up. -// http://code.google.com/p/processing/issues/detail?id=836 -// + the None compressor seems to have bugs, so just disabled it instead. -// + the 'pass through' option seems to be broken, it's been removed, and in -// its place is an option to use the same width and height as the originals. -// + when this new 'pass through' is set, there's some nastiness with how -// the 'final' width/height variables are passed to the movie maker. -// this is an easy fix but needs a couple minutes. - /** * Hacked from Werner Randelshofer's QuickTimeWriter demo. The original version * can be found here. + *

+ * A more up-to-date version of the project seems to be + * here. + * If someone would like to help us update the encoder, that'd be great. + *

+ * Broken out as a separate project because the license (CC) probably isn't + * compatible with the rest of Processing and we don't want any confusion. + *

+ * Added JAI ImageIO to support lots of other image file formats [131008]. + * Also copied the Processing TGA implementation. + *

+ * Added support for the gamma ('gama') atom [131008]. + *

+ * A few more notes on the implementation: + *

    + *
  • The dialog box is super ugly. It's a hacked up version of the previous + * interface, but I'm too scared to pull that GUI layout code apart. + *
  • The 'None' compressor seems to have bugs, so just disabled it instead. + *
  • The 'pass through' option seems to be broken, so it's been removed. + * In its place is an option to use the same width/height as the originals. + *
  • When this new 'pass through' is set, there's some nastiness with how + * the 'final' width/height variables are passed to the movie maker. + * This is an easy fix but needs a couple minutes. + *
+ * Ben Fry 2011-09-06, updated 2013-10-09 */ public class MovieMaker extends JFrame implements Tool { - private JFileChooser imageFolderChooser; - private JFileChooser soundFileChooser; - private JFileChooser movieFileChooser; +// private JFileChooser imageFolderChooser; +// private JFileChooser soundFileChooser; +// private JFileChooser movieFileChooser; private Preferences prefs; // private Editor editor; @@ -99,7 +100,7 @@ public class MovieMaker extends JFrame implements Tool { public void init(Editor editor) { // System.out.println("calling init for MovieMaker " + EventQueue.isDispatchThread()); // this.editor = editor; - initComponents(); + initComponents(editor == null); // String version = getClass().getPackage().getImplementationVersion(); // if (version != null) { @@ -186,7 +187,7 @@ public class MovieMaker extends JFrame implements Tool { } - private void initComponents() { + private void initComponents(final boolean standalone) { imageFolderHelpLabel = new JLabel(); imageFolderField = new JTextField(); chooseImageFolderButton = new JButton(); @@ -209,7 +210,7 @@ public class MovieMaker extends JFrame implements Tool { // fastStartRadio = new JRadioButton(); // fastStartCompressedRadio = new JRadioButton(); - FormListener formListener = new FormListener(); +// FormListener formListener = new FormListener(); setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE); addWindowListener(new WindowAdapter() { @@ -220,8 +221,11 @@ public class MovieMaker extends JFrame implements Tool { }); registerWindowCloseKeys(getRootPane(), new ActionListener() { public void actionPerformed(ActionEvent actionEvent) { - setVisible(false); -// System.exit(0); + if (standalone) { + System.exit(0); + } else { + setVisible(false); + } } }); setTitle("QuickTime Movie Maker"); @@ -231,28 +235,97 @@ public class MovieMaker extends JFrame implements Tool { "This tool creates a QuickTime movie from a sequence of images.
" + "
" + "To avoid artifacts caused by re-compressing images as video,
" + - "use uncompressed TIFF or (lossless) PNG images as the source.
" + + "use TIFF, TGA (from Processing), or PNG images as the source.
" + "
" + - "TIFF images will write more quickly, but require more disk space:
" + + "TIFF and TGA images will write more quickly, but require more disk:
" + "saveFrame(\"frames/####.tif\");
" + + "saveFrame(\"frames/####.tga\");
" + "
" + "PNG images are smaller, but your sketch will run more slowly:
" + "saveFrame(\"frames/####.png\");
" + "
" + "This code is based on QuickTime Movie Maker 1.5.1 2011-01-17.
" + - "Copyright © 2010-2011 Werner Randelshofer. All rights reserved.
" + - "This software is licensed under Creative Commons Atribution 3.0."); + "Copyright \u00A9 2010-2011 Werner Randelshofer. All rights reserved.
" + + "This software is licensed under Creative Commons Atribution 3.0."); imageFolderHelpLabel.setText("Drag a folder with image files into the field below:"); chooseImageFolderButton.setText("Choose..."); - chooseImageFolderButton.addActionListener(formListener); + //chooseImageFolderButton.addActionListener(formListener); + chooseImageFolderButton.addActionListener(new ActionListener() { + + @Override + public void actionPerformed(ActionEvent e) { + Chooser.selectFolder(MovieMaker.this, + "Select image folder...", + new File(imageFolderField.getText()), + new Chooser.Callback() { + void select(File file) { + if (file != null) { + imageFolderField.setText(file.getAbsolutePath()); + } + } + }); + } + }); + soundFileHelpLabel.setText("Drag a sound file into the field below (.au, .aiff, .wav, .mp3):"); chooseSoundFileButton.setText("Choose..."); - chooseSoundFileButton.addActionListener(formListener); + //chooseSoundFileButton.addActionListener(formListener); + chooseSoundFileButton.addActionListener(new ActionListener() { + + @Override + public void actionPerformed(ActionEvent e) { + Chooser.selectInput(MovieMaker.this, + "Select sound file...", + new File(soundFileField.getText()), + new Chooser.Callback() { + + void select(File file) { + if (file != null) { + soundFileField.setText(file.getAbsolutePath()); + } + } + }); + } + }); createMovieButton.setText("Create Movie..."); - createMovieButton.addActionListener(formListener); +// createMovieButton.addActionListener(formListener); + createMovieButton.addActionListener(new ActionListener() { + + @Override + public void actionPerformed(ActionEvent e) { + String lastPath = prefs.get("movie.outputFile", null); + File lastFile = lastPath == null ? null : new File(lastPath); + Chooser.selectOutput(MovieMaker.this, + "Save movie as...", + lastFile, + new Chooser.Callback() { + @Override + void select(File file) { + if (file != null) { + String path = file.getAbsolutePath(); + if (!path.toLowerCase().endsWith(".mov")) { + path += ".mov"; + } + prefs.put("movie.outputFile", path); + createMovie(new File(path)); +// final File target = new File(path); +// //new Thread(new Runnable() { +// EventQueue.invokeLater(new Runnable() { +// +// @Override +// public void run() { +// createMovie(target); +// } +// +// }); + } + } + }); + } + }); Font font = new Font("Dialog", Font.PLAIN, 11); @@ -389,58 +462,58 @@ public class MovieMaker extends JFrame implements Tool { // Code for dispatching events from components to event handlers. - private class FormListener implements java.awt.event.ActionListener { - FormListener() {} - public void actionPerformed(java.awt.event.ActionEvent evt) { - if (evt.getSource() == chooseImageFolderButton) { - MovieMaker.this.chooseImageFolder(evt); - } - else if (evt.getSource() == chooseSoundFileButton) { - MovieMaker.this.chooseSoundFile(evt); - } - else if (evt.getSource() == createMovieButton) { - MovieMaker.this.createMovie(evt); - } -// else if (evt.getSource() == fastStartCompressedRadio) { -// MovieMaker.this.streamingRadioPerformed(evt); +// private class FormListener implements java.awt.event.ActionListener { +// FormListener() {} +// public void actionPerformed(java.awt.event.ActionEvent evt) { +// if (evt.getSource() == chooseImageFolderButton) { +// MovieMaker.this.chooseImageFolder(evt); // } -// else if (evt.getSource() == fastStartRadio) { -// MovieMaker.this.streamingRadioPerformed(evt); +// else if (evt.getSource() == chooseSoundFileButton) { +// MovieMaker.this.chooseSoundFile(evt); // } -// else if (evt.getSource() == noPreparationRadio) { -// MovieMaker.this.streamingRadioPerformed(evt); +// else if (evt.getSource() == createMovieButton) { +// MovieMaker.this.createMovie(evt); // } - } - } +//// else if (evt.getSource() == fastStartCompressedRadio) { +//// MovieMaker.this.streamingRadioPerformed(evt); +//// } +//// else if (evt.getSource() == fastStartRadio) { +//// MovieMaker.this.streamingRadioPerformed(evt); +//// } +//// else if (evt.getSource() == noPreparationRadio) { +//// MovieMaker.this.streamingRadioPerformed(evt); +//// } +// } +// } - private void chooseImageFolder(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_chooseImageFolder - if (imageFolderChooser == null) { - imageFolderChooser = new JFileChooser(); - imageFolderChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); - if (imageFolderField.getText().length() > 0) { - imageFolderChooser.setSelectedFile(new File(imageFolderField.getText())); - } else if (soundFileField.getText().length() > 0) { - imageFolderChooser.setCurrentDirectory(new File(soundFileField.getText()).getParentFile()); - } - } - if (JFileChooser.APPROVE_OPTION == imageFolderChooser.showOpenDialog(this)) { - imageFolderField.setText(imageFolderChooser.getSelectedFile().getPath()); - } - } - - private void chooseSoundFile(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_chooseSoundFile - if (soundFileChooser == null) { - soundFileChooser = new JFileChooser(); - if (soundFileField.getText().length() > 0) { - soundFileChooser.setSelectedFile(new File(soundFileField.getText())); - } else if (imageFolderField.getText().length() > 0) { - soundFileChooser.setCurrentDirectory(new File(imageFolderField.getText())); - } - } - if (JFileChooser.APPROVE_OPTION == soundFileChooser.showOpenDialog(this)) { - soundFileField.setText(soundFileChooser.getSelectedFile().getPath()); - } - } +// private void chooseImageFolder(ActionEvent evt) { +// if (imageFolderChooser == null) { +// imageFolderChooser = new JFileChooser(); +// imageFolderChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); +// if (imageFolderField.getText().length() > 0) { +// imageFolderChooser.setSelectedFile(new File(imageFolderField.getText())); +// } else if (soundFileField.getText().length() > 0) { +// imageFolderChooser.setCurrentDirectory(new File(soundFileField.getText()).getParentFile()); +// } +// } +// if (JFileChooser.APPROVE_OPTION == imageFolderChooser.showOpenDialog(this)) { +// imageFolderField.setText(imageFolderChooser.getSelectedFile().getPath()); +// } +// } +// +// private void chooseSoundFile(ActionEvent evt) { +// if (soundFileChooser == null) { +// soundFileChooser = new JFileChooser(); +// if (soundFileField.getText().length() > 0) { +// soundFileChooser.setSelectedFile(new File(soundFileField.getText())); +// } else if (imageFolderField.getText().length() > 0) { +// soundFileChooser.setCurrentDirectory(new File(imageFolderField.getText())); +// } +// } +// if (JFileChooser.APPROVE_OPTION == soundFileChooser.showOpenDialog(this)) { +// soundFileField.setText(soundFileChooser.getSelectedFile().getPath()); +// } +// } // this is super naughty, and shouldn't be out here. it's a hack to get the @@ -448,7 +521,9 @@ public class MovieMaker extends JFrame implements Tool { // given a bit of time. you know, time? the infinite but non-renewable resource? int width, height; - private void createMovie(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_createMovie + private void createMovie(final File movieFile) { + createMovieButton.setEnabled(false); + // --------------------------------- // Check input // --------------------------------- @@ -507,6 +582,7 @@ public class MovieMaker extends JFrame implements Tool { // --------------------------------- // Choose an output file // --------------------------------- + /* if (movieFileChooser == null) { movieFileChooser = new JFileChooser(); if (prefs.get("movie.outputFile", null) != null) { @@ -528,6 +604,7 @@ public class MovieMaker extends JFrame implements Tool { : new File(movieFileChooser.getSelectedFile().getPath() + ".mov"); prefs.put("movie.outputFile", movieFile.getPath()); createMovieButton.setEnabled(false); + */ final boolean originalSize = originalSizeCheckBox.isSelected(); @@ -544,21 +621,32 @@ public class MovieMaker extends JFrame implements Tool { File[] imgFiles = null; if (imageFolder != null) { imgFiles = imageFolder.listFiles(new FileFilter() { - FileSystemView fsv = FileSystemView.getFileSystemView(); public boolean accept(File f) { return f.isFile() && !fsv.isHiddenFile(f) && !f.getName().equals("Thumbs.db"); } }); + if (imgFiles == null || imgFiles.length == 0) { + return new RuntimeException("No image files found."); + } Arrays.sort(imgFiles); } // Check on first image, if we can actually do pass through if (originalSize) { - ImageIcon temp = new ImageIcon(imgFiles[0].getAbsolutePath()); - width = temp.getIconWidth(); - height = temp.getIconHeight(); + // This was using ImageIcon, which can't handle some file types. + // For 2.1, switching to ImageIO (which is used for movie + // generation anyway) [fry 131008] + BufferedImage temp = readImage(imgFiles[0]); + if (temp == null) { + return new RuntimeException("Coult not read " + imgFiles[0].getAbsolutePath()); + } + width = temp.getWidth(); + height = temp.getHeight(); + if (width <= 0 || height <= 0) { + return new RuntimeException("Could not read " + imgFiles[0].getName() + ", it may be bad."); + } } // Delete movie file if it already exists. @@ -572,9 +660,9 @@ public class MovieMaker extends JFrame implements Tool { writeVideoOnlyVFR(movieFile, imgFiles, width, height, fps, videoFormat, /*passThrough,*/ streaming); } else { writeAudioOnly(movieFile, soundFile, streaming); - } return null; + } catch (Throwable t) { return t; } @@ -600,6 +688,104 @@ public class MovieMaker extends JFrame implements Tool { }//GEN-LAST:event_createMovie + + + private BufferedImage readImage(File file) throws IOException { + // Make sure that we're using a ClassLoader that's aware of the ImageIO jar + //Thread.currentThread().setContextClassLoader(getClass().getClassLoader()); + //BufferedImage image = ImageIO.read(file); + // rewritten to switch back to the default loader + Thread current = Thread.currentThread(); + ClassLoader origLoader = Thread.currentThread().getContextClassLoader(); + current.setContextClassLoader(getClass().getClassLoader()); + BufferedImage image = ImageIO.read(file); + current.setContextClassLoader(origLoader); + + /* + String[] loadImageFormats = ImageIO.getReaderFormatNames(); + if (loadImageFormats != null) { + for (String format : loadImageFormats) { + System.out.println(format); + } + } + */ + + if (image == null) { + String path = file.getAbsolutePath(); + // Might be an incompatible TGA or TIFF created by Processing + if (path.toLowerCase().endsWith(".tga")) { + return loadImageTGA(file); + + } else if (path.toLowerCase().endsWith(".tif")) { + throw new IOException("Try TGA or PNG images instead of TIFF."); + } + } + return image; + } + + + /* + static public void selectFolder(final Frame parentFrame, + final String prompt, +// final String callbackMethod, + final File defaultSelection, +// final Object callbackObject, + final SelectCallback callback) { +// EventQueue.invokeLater(new Runnable() { +// public void run() { + File selectedFile = null; + + if (System.getProperty("os.name").contains("Mac")) { + FileDialog fileDialog = + new FileDialog(parentFrame, prompt, FileDialog.LOAD); + System.setProperty("apple.awt.fileDialogForDirectories", "true"); + fileDialog.setVisible(true); + System.setProperty("apple.awt.fileDialogForDirectories", "false"); + String filename = fileDialog.getFile(); + if (filename != null) { + selectedFile = new File(fileDialog.getDirectory(), fileDialog.getFile()); + } + } else { + JFileChooser fileChooser = new JFileChooser(); + fileChooser.setDialogTitle(prompt); + fileChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); + if (defaultSelection != null) { + fileChooser.setSelectedFile(defaultSelection); + } + + int result = fileChooser.showOpenDialog(parentFrame); + if (result == JFileChooser.APPROVE_OPTION) { + selectedFile = fileChooser.getSelectedFile(); + } + } + //selectCallback(selectedFile, callbackMethod, callbackObject); + callback.select(selectedFile); +// } +// }); + } + */ + + +// static private void selectCallback(File selectedFile, +// String callbackMethod, +// Object callbackObject) { +// try { +// Class callbackClass = callbackObject.getClass(); +// Method selectMethod = +// callbackClass.getMethod(callbackMethod, new Class[] { File.class }); +// selectMethod.invoke(callbackObject, new Object[] { selectedFile }); +// +// } catch (IllegalAccessException iae) { +// System.err.println(callbackMethod + "() must be public"); +// +// } catch (InvocationTargetException ite) { +// ite.printStackTrace(); +// +// } catch (NoSuchMethodException nsme) { +// System.err.println(callbackMethod + "() could not be found"); +// } +// } + // private void streamingRadioPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_streamingRadioPerformed // prefs.put("movie.streaming", evt.getActionCommand()); @@ -608,7 +794,10 @@ public class MovieMaker extends JFrame implements Tool { /** variable frame rate. */ private void writeVideoOnlyVFR(File movieFile, File[] imgFiles, int width, int height, double fps, QuickTimeWriter.VideoFormat videoFormat, /*boolean passThrough,*/ String streaming) throws IOException { File tmpFile = streaming.equals("none") ? movieFile : new File(movieFile.getPath() + ".tmp"); - ProgressMonitor p = new ProgressMonitor(MovieMaker.this, "Creating " + movieFile.getName(), "Creating Output File...", 0, imgFiles.length); + ProgressMonitor p = new ProgressMonitor(MovieMaker.this, + "Creating " + movieFile.getName(), + "Creating output file...", + 0, imgFiles.length); Graphics2D g = null; BufferedImage img = null; BufferedImage prevImg = null; @@ -937,25 +1126,228 @@ public class MovieMaker extends JFrame implements Tool { } } -// /** -// * @param args the command line arguments -// */ -// public static void main(String args[]) { -// EventQueue.invokeLater(new Runnable() { -// public void run() { -// MovieMakerFrame m = new MovieMakerFrame(); -//// m.init(null); + + /** + * Targa image loader for RLE-compressed TGA files. + * Code taken from PApplet, any changes here should lead to updates there. + */ + static private BufferedImage loadImageTGA(File file) throws IOException { + InputStream is = new FileInputStream(file); + + try { + byte header[] = new byte[18]; + int offset = 0; + do { + int count = is.read(header, offset, header.length - offset); + if (count == -1) return null; + offset += count; + } while (offset < 18); + + /* + header[2] image type code + 2 (0x02) - Uncompressed, RGB images. + 3 (0x03) - Uncompressed, black and white images. + 10 (0x0A) - Run-length encoded RGB images. + 11 (0x0B) - Compressed, black and white images. (grayscale?) + + header[16] is the bit depth (8, 24, 32) + + header[17] image descriptor (packed bits) + 0x20 is 32 = origin upper-left + 0x28 is 32 + 8 = origin upper-left + 32 bits + + 7 6 5 4 3 2 1 0 + 128 64 32 16 8 4 2 1 + */ + + int format = 0; + final int RGB = 1; + final int ARGB = 2; + final int ALPHA = 4; + + if (((header[2] == 3) || (header[2] == 11)) && // B&W, plus RLE or not + (header[16] == 8) && // 8 bits + ((header[17] == 0x8) || (header[17] == 0x28))) { // origin, 32 bit + format = ALPHA; + + } else if (((header[2] == 2) || (header[2] == 10)) && // RGB, RLE or not + (header[16] == 24) && // 24 bits + ((header[17] == 0x20) || (header[17] == 0))) { // origin + format = RGB; + + } else if (((header[2] == 2) || (header[2] == 10)) && + (header[16] == 32) && + ((header[17] == 0x8) || (header[17] == 0x28))) { // origin, 32 + format = ARGB; + } + + if (format == 0) { + throw new IOException("Unknown .tga file format for " + file.getName()); + } + + int w = ((header[13] & 0xff) << 8) + (header[12] & 0xff); + int h = ((header[15] & 0xff) << 8) + (header[14] & 0xff); + //PImage outgoing = createImage(w, h, format); + int[] pixels = new int[w * h]; + + // where "reversed" means upper-left corner (normal for most of + // the modernized world, but "reversed" for the tga spec) + //boolean reversed = (header[17] & 0x20) != 0; + // https://github.com/processing/processing/issues/1682 + boolean reversed = (header[17] & 0x20) == 0; + + if ((header[2] == 2) || (header[2] == 3)) { // not RLE encoded + if (reversed) { + int index = (h-1) * w; + switch (format) { + case ALPHA: + for (int y = h-1; y >= 0; y--) { + for (int x = 0; x < w; x++) { + pixels[index + x] = is.read(); + } + index -= w; + } + break; + case RGB: + for (int y = h-1; y >= 0; y--) { + for (int x = 0; x < w; x++) { + pixels[index + x] = + is.read() | (is.read() << 8) | (is.read() << 16) | + 0xff000000; + } + index -= w; + } + break; + case ARGB: + for (int y = h-1; y >= 0; y--) { + for (int x = 0; x < w; x++) { + pixels[index + x] = + is.read() | (is.read() << 8) | (is.read() << 16) | + (is.read() << 24); + } + index -= w; + } + } + } else { // not reversed + int count = w * h; + switch (format) { + case ALPHA: + for (int i = 0; i < count; i++) { + pixels[i] = is.read(); + } + break; + case RGB: + for (int i = 0; i < count; i++) { + pixels[i] = + is.read() | (is.read() << 8) | (is.read() << 16) | + 0xff000000; + } + break; + case ARGB: + for (int i = 0; i < count; i++) { + pixels[i] = + is.read() | (is.read() << 8) | (is.read() << 16) | + (is.read() << 24); + } + break; + } + } + + } else { // header[2] is 10 or 11 + int index = 0; + + while (index < pixels.length) { + int num = is.read(); + boolean isRLE = (num & 0x80) != 0; + if (isRLE) { + num -= 127; // (num & 0x7F) + 1 + int pixel = 0; + switch (format) { + case ALPHA: + pixel = is.read(); + break; + case RGB: + pixel = 0xFF000000 | + is.read() | (is.read() << 8) | (is.read() << 16); + //(is.read() << 16) | (is.read() << 8) | is.read(); + break; + case ARGB: + pixel = is.read() | + (is.read() << 8) | (is.read() << 16) | (is.read() << 24); + break; + } + for (int i = 0; i < num; i++) { + pixels[index++] = pixel; + if (index == pixels.length) break; + } + } else { // write up to 127 bytes as uncompressed + num += 1; + switch (format) { + case ALPHA: + for (int i = 0; i < num; i++) { + pixels[index++] = is.read(); + } + break; + case RGB: + for (int i = 0; i < num; i++) { + pixels[index++] = 0xFF000000 | + is.read() | (is.read() << 8) | (is.read() << 16); + } + break; + case ARGB: + for (int i = 0; i < num; i++) { + pixels[index++] = is.read() | + (is.read() << 8) | (is.read() << 16) | (is.read() << 24); + } + break; + } + } + } + + if (!reversed) { + int[] temp = new int[w]; + for (int y = 0; y < h/2; y++) { + int z = (h-1) - y; + System.arraycopy(pixels, y*w, temp, 0, w); + System.arraycopy(pixels, z*w, pixels, y*w, w); + System.arraycopy(temp, 0, pixels, z*w, w); + } + } + } + //is.close(); + int type = (format == RGB) ? + BufferedImage.TYPE_INT_RGB : BufferedImage.TYPE_INT_ARGB; + BufferedImage image = new BufferedImage(w, h, type); + WritableRaster wr = image.getRaster(); + wr.setDataElements(0, 0, w, h, pixels); + return image; + + } finally { + is.close(); + } + } + + + /** + * @param args the command line arguments + */ + public static void main(String args[]) { + java.awt.EventQueue.invokeLater(new Runnable() { + public void run() { + MovieMaker m = new MovieMaker(); + m.init(null); // m.init(); -// m.setVisible(true); -//// m.pack(); -// } -// }); -// } + m.setVisible(true); +// m.pack(); + } + }); + } + private JLabel aboutLabel; private JButton chooseImageFolderButton; private JButton chooseSoundFileButton; - private JComboBox compressionBox; + private JComboBox compressionBox; private JLabel compressionLabel; // private JRadioButton fastStartCompressedRadio; // private JRadioButton fastStartRadio; diff --git a/build/shared/tools/MovieMaker/tool/.gitignore b/build/shared/tools/MovieMaker/tool/.gitignore new file mode 100644 index 000000000..fb9e88d2d --- /dev/null +++ b/build/shared/tools/MovieMaker/tool/.gitignore @@ -0,0 +1,2 @@ +MovieMaker.jar + diff --git a/build/shared/tools/howto.txt b/build/shared/tools/howto.txt deleted file mode 100644 index 77df31c91..000000000 --- a/build/shared/tools/howto.txt +++ /dev/null @@ -1,143 +0,0 @@ -TOOLS IN PROCESSING - -With initial help from code contributed by fjen, Processing release 0147 and -later have a dynamically loading tools menu, which can be used to expand the -environment in fun and fantastic ways. - -A Tool is a chunk of code that runs from the Tools menu. Tools are a means -of building onto the Processing Development Environment without needing to -rebuild the beast from source. - -The interface (at least for now) is extremely simple: - - -package processing.app.tools.Tool; - -public interface Tool extends Runnable { - - public void init(Editor editor); - - public void run(); - - public String getMenuTitle(); -} - - -The init() method is called when an Editor window first opens. This means -you won't have access to a sketch object, or a GUI, and should only do minimal -setup. (However it'd be a good idea to stash the "Editor" object for later.) - -The run() method will be called by the main application when the tool is -selected from the menu. This is called using invokeLater(), so that the tool -can safely use Swing and any other GUI yackety yack. If you're using a Frame, -you'll need to detect whether the Frame is already open (and bring it to the -front) or whether to create a new window. - -Faceless tools also use the run() method. You should avail yourselves of the -statusNotice() and statusError() methods in Editor, to let the user know what's -happened. (As per p. 107 of the Processing Development Environment Tools -Reference User Interface Guide.) - -The getMenuTitle() method just returns the title for what should appear in the -Tools menu. Not doing shortcuts for now, because resolving them between tools -(and the rest of the interface) is fugly. We would also need additional -modifiers for shift and alt. It just gets messy quick. Ordering in the Tools -menu is alphabetical. - - -////////////////////////////////////////////////////////////// - - -Where to put Tools - -Core tools live inside the "tools" subfolder of the Processing distribution, -however users should install "contributed" tools in their sketchbook folder, -inside a subfolder named "tools". - -If a tool works only with a particular release of Processing, then it may make -sense for the user to put things into the Processing tools folder, however we'd -like to keep users out of there as much as possible. In fact, it may not be -visible in future releases of Processing (for instance, on Mac OS X, the tools -folder is hidden inside the .app bundle). - -Tools should be laid out similar to libraries, though the structure is a little -more flexible. The tool folder should be the name of the main class (without -its package name but using the same capitalization), and have a subfolder named -"tool" that contains the .jar and .zip files it uses. I'll use the included -"Mangler" tool as an example. - -(This Tool is not built by default, due to a lack of non-dubious arguments -regarding the usefulness of including (by default) a Tool that mangles code.) - -The folder should be called Mangler (note the capitalization), and contain: - -sketchbook/Mangler -> tool folder -sketchbook/Mangler/tool -> location for code -sketchbook/Mangler/tool/mangle.jar -> jar with one or more classes - -The naming of jar and zip files in the tool/* directory doesn't matter. - -When Processing loads, the jar and zip files will be searched for -Mangler.class. Even though this tool is found in package poos.shoe, -it will be sussed out. Package names are required. - -Loose .class files are not supported, use only jar and zip files. - - -////////////////////////////////////////////////////////////// - - -What You Can and Cannot Do - -The only API methods that are officially scrubbed and sanctioned by the -Commissioner on Fair API and Proper Manners for use by the Tools classes -are found in: - -processing.app.Base -processing.app.Editor -processing.app.Preferences -processing.app.Sketch -processing.app.SketchCode - -In fact, most of the API you should be talking to is inside Editor. -Full API documentation can be found on dev.processing.org: -http://dev.processing.org/reference/everything/ -(Keep in mind that this is not always perfectly up to date, but we'll try.) - -Of course, you're welcome to go spelunking through the rest of the API -(that's where all the fun stuff is anyway), but don't be upset when something -changes and breaks your tool and makes your users sad. - -Currently, native code is not supported with tools. This might be possible, -but it's another potentially messy thing to dynamically add native library -paths to running code. (See "Future Releases" below.) - - -////////////////////////////////////////////////////////////// - - -Future Releases - -In future releases, we are considering the following features: - -1. How shortcut keys are handled. - http://dev.processing.org/bugs/show_bug.cgi?id=140 - -2. Whether to allow tools to dock into the Preferences panel. - http://dev.processing.org/bugs/show_bug.cgi?id=883 - -3. A means to run native code from the Tools menu. - http://dev.processing.org/bugs/show_bug.cgi?id=884 - -4. Methods for reorganizing the Tools menu, or placing contributed - Tools inside other menus (such as Edit or Sketch). - http://dev.processing.org/bugs/show_bug.cgi?id=885 - -This is the first round of documentation for the Tools menu, we reserve the -right to update, clarify, and change our mind in future releases. - - -////////////////////////////////////////////////////////////// - - -Ben Fry, last updated 19 August 2008 diff --git a/build/windows/.gitignore b/build/windows/.gitignore old mode 100644 new mode 100755 index 5d5afb57b..63b84630b --- a/build/windows/.gitignore +++ b/build/windows/.gitignore @@ -1 +1 @@ -jre.zip +jre.tgz diff --git a/build/windows/launcher/about.bmp b/build/windows/about.bmp similarity index 100% rename from build/windows/launcher/about.bmp rename to build/windows/about.bmp diff --git a/build/windows/launcher/application.ico b/build/windows/application.ico similarity index 100% rename from build/windows/launcher/application.ico rename to build/windows/application.ico diff --git a/build/windows/launcher/config-cmd.xml b/build/windows/config-cmd.xml similarity index 67% rename from build/windows/launcher/config-cmd.xml rename to build/windows/config-cmd.xml index e06dffdd1..44c7b4aa7 100755 --- a/build/windows/launcher/config-cmd.xml +++ b/build/windows/config-cmd.xml @@ -2,17 +2,17 @@ true console lib - processing-java.exe + work/processing-java.exe . normal - http://java.sun.com/javase/downloads/ + http://java.com/download false false - + processing.mode.java.Commander lib/pde.jar @@ -20,7 +20,7 @@ lib/jna.jar lib/antlr.jar lib/ant.jar - lib/ant-launcher.jar + lib/ant-launcher.jar lib/org-netbeans-swing-outline.jar lib/com.ibm.icu_4.4.2.v20110823.jar lib/jdi.jar @@ -30,18 +30,16 @@ java - + + -Djna.nosys=true + + 1.7.0_40 An error occurred while starting the application. This application was configured to use a bundled Java Runtime Environment but the runtime is missing or corrupted. - This application requires at least Java Development Kit (not JRE) - The registry refers to a nonexistent Java Development Kit installation or the runtime is corrupted. + This application requires Java to be installed + The registry refers to a nonexistent Java installation or the runtime is corrupted. An application instance is already running. diff --git a/build/windows/launcher/config.xml b/build/windows/config.xml similarity index 74% rename from build/windows/launcher/config.xml rename to build/windows/config.xml index dc7d954f2..4a3e91c5c 100755 --- a/build/windows/launcher/config.xml +++ b/build/windows/config.xml @@ -2,12 +2,12 @@ true gui lib - processing.exe + work/processing.exe . normal - http://java.sun.com/javase/downloads/ + http://java.com/download false false @@ -30,12 +30,10 @@ java - + + -Djna.nosys=true + + 1.7.0_40 about.bmp @@ -46,8 +44,8 @@ An error occurred while starting the application. This application was configured to use a bundled Java Runtime Environment but the runtime is missing or corrupted. - This application requires at least Java Development Kit (not JRE) - The registry refers to a nonexistent Java Development Kit installation or the runtime is corrupted. + This application requires Java to be installed + The registry refers to a nonexistent Java installation or the runtime is corrupted. An application instance is already running. diff --git a/build/windows/export/Makefile b/build/windows/export/Makefile deleted file mode 100755 index 6c1dce21c..000000000 --- a/build/windows/export/Makefile +++ /dev/null @@ -1,22 +0,0 @@ -CXXFLAGS = -mwindows -mno-cygwin -O2 -Wall -#POBJS = launcher.o launcher-rc.o -# don't want to include the p5 icons for the exported feller -AOBJS = launcher.o - -#processing.exe: $(POBJS) -# $(LINK.cc) $(CXXFLAGS) -o $@ $(POBJS) -# cp processing.exe ../work/ - -application.exe: $(AOBJS) - $(LINK.cc) $(CXXFLAGS) -DEXPORT -o $@ $(AOBJS) - cp application.exe ../work/modes/java/application/template.exe - cp application.exe ../../../java/application/template.exe - -$(POBJS): Makefile - -#launcher-rc.o: launcher.rc -# windres -i $< -o $@ - -clean: - $(RM) $(OBJS) application.exe -# $(RM) $(OBJS) processing.exe application.exe diff --git a/build/windows/export/application.exe b/build/windows/export/application.exe deleted file mode 100755 index 03d8d5753..000000000 Binary files a/build/windows/export/application.exe and /dev/null differ diff --git a/build/windows/export/launcher.cpp b/build/windows/export/launcher.cpp deleted file mode 100644 index eb09b8334..000000000 --- a/build/windows/export/launcher.cpp +++ /dev/null @@ -1,403 +0,0 @@ -// -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- - -// if this code looks shitty, that's because it is. people are likely -// to have the durndest things in their CLASSPATH environment variable. -// mostly because installers often mangle them without the user knowing. -// so who knows where and when the quotes will show up. the code below is -// based on a couple years of trial and error with processing releases. - -// For revision 0102, a lot of changes were made to deal with stripping -// the quotes from the PATH, CLASSPATH, and QTJAVA environment variables. -// Any elements of the PATH and CLASSPATH that don't exist (whether files -// or directories) are also stripped out before being set. -// (Bug 112) - -// For revision 0201 (love that symmetry), the 'lib' folder was added to -// the java.library.path, so that we can hide the pile of DLLs included -// with some libraries (I'm looking at you, video), inside the lib folder. -// QTJAVA mess was also excised, now that we're switching to gstreamer. - -// The size of all of the strings was made sort of ambiguously large, since -// 1) nothing is hurt by allocating an extra few bytes temporarily and -// 2) if the user has a long path, and it gets copied five times over for the -// CLASSPATH, the program runs the risk of crashing. Bad bad. - -// TODO this code leaks memory all over the place because nothing has been -// done to properly handle creation/deletion of new strings. - -// TODO switch to unicode versions of all methods in order to better support -// running on non-English (non-Roman especially) versions of Windows. - -#define ARGS_FILE_PATH "\\lib\\args.txt" - -#include -#include -#include - - -void removeLineEndings(char *what); -char *scrubPath(char *incoming); -char *mallocChars(int count); -void removeQuotes(char *quoted); -void removeTrailingSlash(char *slashed); - -//#define DEBUG - -int STDCALL -WinMain (HINSTANCE hInst, HINSTANCE hPrev, LPSTR lpCmd, int nShow) -{ - // command line that was passed to this application - char *incoming_cmd_line = (char *)malloc((strlen(lpCmd) + 1) * sizeof(char)); - strcpy(incoming_cmd_line, lpCmd); - - // get the full path to the application that was launched, - // drop the app name but keep the path - char *exe_directory = (char *)malloc(MAX_PATH * sizeof(char)); - //*exe_directory = 0; - GetModuleFileName(NULL, exe_directory, MAX_PATH); - // remove the application name - *(strrchr(exe_directory, '\\')) = '\0'; - - - // open the file that contains the main class name and java args - - char *args_file_path = (char*) - malloc(strlen(exe_directory) * sizeof(char) + - strlen(ARGS_FILE_PATH) * sizeof(char) + 1); - strcpy(args_file_path, exe_directory); - strcat(args_file_path, ARGS_FILE_PATH); - - char java_args[512]; - char java_main_class[512]; - char jar_list[512]; - char *app_classpath = (char *)malloc(10 * strlen(exe_directory) + 4096); - - FILE *argsfile = fopen(args_file_path, "r"); - if (argsfile == NULL) { - sprintf(app_classpath, - "This program is missing the \"lib\" folder, " - "which should be located at\n%s", - exe_directory); - MessageBox(NULL, app_classpath, "Folder Missing", MB_OK); - return 0; - - } else { - fgets(java_args, 511, argsfile); - removeLineEndings(java_args); - fgets(java_main_class, 511, argsfile); - removeLineEndings(java_main_class); - fgets(jar_list, 511, argsfile); - removeLineEndings(jar_list); - -#ifdef DEBUG - MessageBox(NULL, java_args, "args", MB_OK); - MessageBox(NULL, java_main_class, "class", MB_OK); - MessageBox(NULL, jar_list, "jarlist", MB_OK); -#endif - - app_classpath[0] = 0; - char *jar = (char*) strtok(jar_list, ","); - while (jar != NULL) { - char entry[1024]; - sprintf(entry, "%s\\lib\\%s;", exe_directory, jar); - strcat(app_classpath, entry); - jar = (char*) strtok(NULL, ","); - } - fclose(argsfile); - } - - // - - char *cp = (char *)malloc(10 * strlen(exe_directory) + 4096); - - // test to see if running with a java runtime nearby or not - char *testpath = (char *)malloc(MAX_PATH * sizeof(char)); - *testpath = 0; - strcpy(testpath, exe_directory); - strcat(testpath, "\\java\\bin\\java.exe"); - FILE *fp = fopen(testpath, "rb"); - int local_jre_installed = (fp != NULL); - if (fp != NULL) fclose(fp); - - //const char *envClasspath = getenv("CLASSPATH"); - //char *env_classpath = (char *)malloc(16384 * sizeof(char)); - - // ignoring CLASSPATH for now, because it's not needed - // and causes more trouble than it's worth [0060] - //env_classpath[0] = 0; - - // don't put quotes around contents of cp, even though %s might have - // spaces in it. don't put quotes in it, because it's setting the - // environment variable for CLASSPATH, not being included on the - // command line. so setting the env var it's ok to have spaces, - // and the quotes prevent javax.comm.properties from being found. - - strcpy(cp, app_classpath); - if (local_jre_installed) { - char *local_jre = mallocChars(64 + strlen(exe_directory) * 2); - sprintf(local_jre, "%s\\java\\lib\\rt.jar;%s\\java\\lib\\tools.jar;", exe_directory, exe_directory); - strcat(cp, local_jre); - } - - char *clean_cp = scrubPath(cp); - //if (!SetEnvironmentVariable("CLASSPATH", cp)) { - if (!SetEnvironmentVariable("CLASSPATH", clean_cp)) { - MessageBox(NULL, "Could not set CLASSPATH environment variable", - "Processing Error", MB_OK); - return 1; - } - -#ifdef DEBUG - MessageBox(NULL, "done with classpath cleaning", "2", MB_OK); -#endif - - int env_path_length = strlen(getenv("PATH")); - char *env_path = mallocChars(env_path_length); - strcpy(env_path, getenv("PATH")); - char *clean_path; - - // need to add the local jre to the path for 'java mode' in the env - if (local_jre_installed) { - char *path_to_clean = - mallocChars(env_path_length + strlen(exe_directory) + 30); - sprintf(path_to_clean, "%s\\java\\bin;%s", exe_directory, env_path); - clean_path = scrubPath(path_to_clean); - } else { - clean_path = scrubPath(getenv("PATH")); - } - - //MessageBox(NULL, clean_path, "after scrubbing PATH", MB_OK); - //MessageBox(NULL, "3", "checking", MB_OK); - - if (!SetEnvironmentVariable("PATH", clean_path)) { - MessageBox(NULL, "Could not set PATH environment variable", - "Processing Error", MB_OK); - return 0; - } - - // what gets put together to pass to jre - char *outgoing_cmd_line = (char *)malloc(16384 * sizeof(char)); - - // prepend the args for -mx and -ms - strcpy(outgoing_cmd_line, java_args); - strcat(outgoing_cmd_line, " "); - - // for 2.0a2, add the 'lib' folder to the java.library.path - strcat(outgoing_cmd_line, "\"-Djava.library.path="); - strcat(outgoing_cmd_line, exe_directory); - strcat(outgoing_cmd_line, "\\lib\" "); - - // add the name of the class to execute and a space before the next arg - strcat(outgoing_cmd_line, java_main_class); - strcat(outgoing_cmd_line, " "); - - // append additional incoming stuff (document names), if any - strcat(outgoing_cmd_line, incoming_cmd_line); - - //MessageBox(NULL, outgoing_cmd_line, "cmd_line", MB_OK); - - char *executable = - (char *)malloc((strlen(exe_directory) + 256) * sizeof(char)); - // exe_directory is the name path to the current application - - if (local_jre_installed) { - strcpy(executable, exe_directory); - // copy in the path for javaw, relative to launcher.exe - strcat(executable, "\\java\\bin\\javaw.exe"); - } else { -#ifdef DEBUG - strcpy(executable, "java.exe"); -#else - strcpy(executable, "javaw.exe"); -#endif - } - - SHELLEXECUTEINFO ShExecInfo; - -#ifdef DEBUG - MessageBox(NULL, outgoing_cmd_line, executable, MB_OK); -#endif - - // set up the execution info - ShExecInfo.cbSize = sizeof(SHELLEXECUTEINFO); - ShExecInfo.fMask = 0; - ShExecInfo.hwnd = 0; - ShExecInfo.lpVerb = "open"; - ShExecInfo.lpFile = executable; - ShExecInfo.lpParameters = outgoing_cmd_line; - ShExecInfo.lpDirectory = exe_directory; - ShExecInfo.nShow = SW_SHOWNORMAL; - ShExecInfo.hInstApp = NULL; - - if (!ShellExecuteEx(&ShExecInfo)) { - MessageBox(NULL, "Error calling ShellExecuteEx()", - "Processing Error", MB_OK); - return 0; - } - - if (reinterpret_cast(ShExecInfo.hInstApp) <= 32) { - // some type of error occurred - switch (reinterpret_cast(ShExecInfo.hInstApp)) { - case ERROR_FILE_NOT_FOUND: - case ERROR_PATH_NOT_FOUND: - MessageBox(NULL, "A required file could not be found. \n" - "You may need to install a Java runtime\n" - "or re-install Processing.", - "Processing Error", MB_OK); - break; - case 0: - case SE_ERR_OOM: - MessageBox(NULL, "Not enough memory or resources to run at" - " this time.", "Processing Error", MB_OK); - - break; - default: - MessageBox(NULL, "There is a problem with your installation.\n" - "If the problem persists, re-install the program.", - "Processing Error", MB_OK); - break; - } - } - - return 0; - - /* - PROCESS_INFORMATION pi; - memset(&pi, 0, sizeof(pi)); - STARTUPINFO si; - memset(&si, 0, sizeof(si)); - si.cb = sizeof(si); - int wait = 0; - - DWORD dwExitCode = (DWORD) -1; - char cmdline[32768]; - //executable; - //outgoing_cmd_line; - //exe_directory; - strcpy(cmdline, "\""); - strcat(cmdline, executable); - strcat(cmdline, "\" "); - strcat(cmdline, outgoing_cmd_line); - - if (CreateProcess(NULL, cmdline, NULL, NULL, - //TRUE, priority, NULL, NULL, - TRUE, 0, NULL, exe_directory, - &si, &pi)) { - if (wait) { - WaitForSingleObject(pi.hProcess, INFINITE); - GetExitCodeProcess(pi.hProcess, &dwExitCode); - //debug("Exit code:\t%d\n", dwExitCode); - //closeHandles(); - char big_trouble[128]; - sprintf(big_trouble, "Sorry, could not launch. (Error %d)", - (int) dwExitCode); - MessageBox(NULL, big_trouble, "Apologies", MB_OK); - } else { - dwExitCode = 0; - } - } - return dwExitCode; - */ -} - - -void removeLineEndings(char *what) { - int index = strlen(what) - 1; - while (index >= 0) { - if ((what[index] == 10) || (what[index] == 13)) { - what[index] = 0; - --index; - } else { - return; - } - } -} - - -// take a PATH environment variable, split on semicolons, -// remove extraneous quotes, perhaps even make 8.3 syntax if necessary -char *scrubPath(char *incoming) { - char *cleaned = mallocChars(strlen(incoming) * 2); - - int found_so_far = 0; - char *p = (char*) strtok(incoming, ";"); - while (p != NULL) { - char entry[1024]; - /* - if (*p == '\"') { - // if this segment of the path contains quotes, remove them - int fixed_length = strlen(p) - 2; - strncpy(entry, &p[1], fixed_length); - entry[fixed_length] = 0; - //MessageBox(NULL, entry, "clipped", MB_OK); - - // if it doesn't actually end with a quote, then the person - // is screwed anyway.. they can deal with that themselves - } else { - strcpy(entry, p); - } - */ - strcpy(entry, p); - removeQuotes(entry); - // a trailing slash will cause FindFirstFile to fail.. grr [0109] - removeTrailingSlash(entry); - //MessageBox(NULL, entry, "entry", MB_OK); - - // if this path doesn't exist, don't add it - WIN32_FIND_DATA find_file_data; - HANDLE hfind = FindFirstFile(entry, &find_file_data); - if (hfind != INVALID_HANDLE_VALUE) { - if (found_so_far) strcat(cleaned, ";"); - strcat(cleaned, entry); - //MessageBox(NULL, cleaned, "cleaned so far", MB_OK); - FindClose(hfind); - found_so_far = 1; - //} else { - //MessageBox(NULL, entry, "removing", MB_OK); - } - // grab the next entry - p = (char*) strtok(NULL, ";"); - } - //MessageBox(NULL, cleaned, "scrubPath", MB_OK); - return cleaned; -} - - -// eventually make this handle unicode -char *mallocChars(int count) { - // add one for the terminator - char *outgoing = (char*) malloc(count * sizeof(char) + 1); - outgoing[0] = 0; // for safety - return outgoing; -} - - -void removeQuotes(char *quoted) { - int len = strlen(quoted); - // remove quote at the front - if (quoted[0] == '\"') { - for (int i = 0; i < len - 1; i++) { - quoted[i] = quoted[i+1]; - } - len--; - quoted[len] = 0; - } - // remove quote at the end - if (len > 1) { - if (quoted[len - 1] == '\"') { - len--; - quoted[len] = 0; - } - } -} - - -void removeTrailingSlash(char *slashed) { - int len = strlen(slashed); - if (len > 1) { - if (slashed[len - 1] == '\\') { - len--; - slashed[len] = 0; - } - } -} diff --git a/build/windows/launcher/processing.bat b/build/windows/processing.bat similarity index 100% rename from build/windows/launcher/processing.bat rename to build/windows/processing.bat diff --git a/core/.settings/org.eclipse.jdt.core.prefs b/core/.settings/org.eclipse.jdt.core.prefs index 0c08b2e5b..8e54f4641 100644 --- a/core/.settings/org.eclipse.jdt.core.prefs +++ b/core/.settings/org.eclipse.jdt.core.prefs @@ -1,4 +1,5 @@ eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.annotation.inheritNullAnnotations=disabled org.eclipse.jdt.core.compiler.annotation.missingNonNullByDefaultAnnotation=ignore org.eclipse.jdt.core.compiler.annotation.nonnull=org.eclipse.jdt.annotation.NonNull org.eclipse.jdt.core.compiler.annotation.nonnullbydefault=org.eclipse.jdt.annotation.NonNullByDefault @@ -47,6 +48,7 @@ org.eclipse.jdt.core.compiler.problem.missingSynchronizedOnInheritedMethod=ignor org.eclipse.jdt.core.compiler.problem.noEffectAssignment=warning org.eclipse.jdt.core.compiler.problem.noImplicitStringConversion=warning org.eclipse.jdt.core.compiler.problem.nonExternalizedStringLiteral=ignore +org.eclipse.jdt.core.compiler.problem.nonnullParameterAnnotationDropped=warning org.eclipse.jdt.core.compiler.problem.nullAnnotationInferenceConflict=error org.eclipse.jdt.core.compiler.problem.nullReference=warning org.eclipse.jdt.core.compiler.problem.nullSpecViolation=error @@ -67,6 +69,7 @@ org.eclipse.jdt.core.compiler.problem.specialParameterHidingField=disabled org.eclipse.jdt.core.compiler.problem.staticAccessReceiver=warning org.eclipse.jdt.core.compiler.problem.suppressOptionalErrors=disabled org.eclipse.jdt.core.compiler.problem.suppressWarnings=enabled +org.eclipse.jdt.core.compiler.problem.syntacticNullAnalysisForFields=disabled org.eclipse.jdt.core.compiler.problem.syntheticAccessEmulation=ignore org.eclipse.jdt.core.compiler.problem.typeParameterHiding=warning org.eclipse.jdt.core.compiler.problem.unavoidableGenericTypeProblems=enabled @@ -75,7 +78,7 @@ org.eclipse.jdt.core.compiler.problem.unclosedCloseable=warning org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=ignore org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=warning org.eclipse.jdt.core.compiler.problem.unnecessaryElse=ignore -org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=ignore +org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=warning org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownException=ignore org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionExemptExceptionAndThrowable=enabled @@ -90,6 +93,7 @@ org.eclipse.jdt.core.compiler.problem.unusedParameterIncludeDocCommentReference= org.eclipse.jdt.core.compiler.problem.unusedParameterWhenImplementingAbstract=disabled org.eclipse.jdt.core.compiler.problem.unusedParameterWhenOverridingConcrete=disabled org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=warning +org.eclipse.jdt.core.compiler.problem.unusedTypeParameter=ignore org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=warning org.eclipse.jdt.core.compiler.source=1.6 diff --git a/core/build.xml b/core/build.xml old mode 100644 new mode 100755 index 00d1cbbd6..8bb8b3a2c --- a/core/build.xml +++ b/core/build.xml @@ -12,14 +12,30 @@ classpath="methods/methods.jar" /> + + + + + + + - + classpath="library/jogl-all.jar; library/gluegen-rt.jar" + nowarn="true"> + + + smoothMode(), smoothQuality(), quality() +o NEAREST, BILINEAR, BICUBIC, or 0, 2, 4? (need 8x too, so maybe numbers) +X setAntiAlias() should instead just use parent.smooth +X final decision on pg.setQuality(sketchQuality()) +X should probably be setQuality(parent.sketchQuality()) + +andres +X Fonts from loadFont() show up as blocks in P3D (regression) +X https://github.com/processing/processing/issues/2465 +X loadPixels problem in OpenGL +X https://github.com/processing/processing/issues/2493 + + +0225 core (2.1.2) +X bug with StringDict(Reader) that wasn't setting the indices hashmap +X check this with other versions of this class +X call revalidate() via reflection +X text looks lousy compared to the Apple JVM +X mess with rendering hints? (notes in PGraphicsJava2D) +X improvements made, but still not amazing.. just at level of Windows/Linux +X PGraphics.colorCalcARGB(int, float) doesn't cap alpha +X https://github.com/processing/processing/issues/2439 +X run window border color changed in 2.1 +X https://github.com/processing/processing/issues/2297 +X simple NPE issue that needs workaround +X https://github.com/processing/processing/issues/2354 + +fixed in 2.1 +X draw() called again before finishing on OS X (retina issue) +X https://github.com/processing/processing/issues/1709 +X get() not always setting alpha channel when used with point() +X https://github.com/processing/processing/issues/1756 +A support for geometry and tessellation shaders (on desktop) +A https://github.com/processing/processing/issues/2252 + +andres +X copy() under OPENGL uses upside-down coordinates for cropping +X https://github.com/processing/processing/issues/2345 +X video on windows causes exception +X https://github.com/processing/processing/issues/2327 +X Shape Font Rendering was broken with the OpenGL Renderer +X https://github.com/processing/processing/issues/2375 +A depth buffer shouldn't be cleared when depth mask is disabled +A https://github.com/processing/processing/issues/2296 +A set pixels transparent by default in P2D/P3D +A https://github.com/processing/processing/issues/2207 +A unwind depth sorting because it was breaking DXF export +A https://github.com/processing/processing/issues/2404 +A Sketch hangs if sketchRenderer() returns an OpenGL renderer +A https://github.com/processing/processing/issues/2363 +A "buffer" uniform triggers shader compilation error +A https://github.com/processing/processing/issues/2325 +A buffer has been renamed to ppixels for shaders +A noLoop clears screen on Windows 8 +A https://github.com/processing/processing/issues/2416 +A fix pixels[] array for video capture +A https://github.com/processing/processing/issues/2424 + + +0224 core (2.1.1) +X PImage resize() causes PImage not to be rendered in JAVA2D +X https://github.com/processing/processing/issues/2179 +X remove make.sh from core.. ancient +X remove println() from dataPath() +X add special case for 'null' to println() +X added print() method to IntList +X fix esoteric typo with alpha and color +X https://github.com/processing/processing/issues/2230 +A pushStyle/popStyle should save/restore blendMode +A https://github.com/processing/processing/issues/2232 +A get() + video requires loadPixels in P2D/P3D +A https://github.com/processing/processing/issues/2202 + +opengl +A PImage copy() function used with P2D flips the image +A https://github.com/processing/processing/issues/2171 +A filter shaders don't need to use the texture uniform +A https://github.com/processing/processing/issues/2204 +A texture() bug with stroke() in P2D +A https://github.com/processing/processing/issues/2205 +A allow sharing of GL context amongst multiple windows +A https://github.com/processing/processing/issues/1698 +A texture sampling setting is ignored when creating an offscreen PGraphics +A https://github.com/processing/processing/issues/1900 +A rounded rect broken with Processing 2.1 P3D renderer +A https://github.com/processing/processing/issues/2193 +X Clear the global PGL on dispose() (from JDF) +X https://github.com/processing/processing/pull/2279 +A pie arcs have stroke between endpoints in P2D/P3D +A https://github.com/processing/processing/issues/2233 + + +0223 core (2.1) +X fix dataPath() problem with OS X (was breaking Movie) +X alpha values from the pixels array coming back as 0 +X only tested on background(), not image() +X https://github.com/processing/processing/issues/2030 + +opengl +X Using sketchQuality() does not work properly with P3D, OPENGL, P2D +X https://github.com/processing/processing/pull/2157 +X Fix crashes when the sketch window is resized +X https://github.com/processing/processing/issues/1880 +X https://github.com/processing/processing/pull/2156 +X scale() wasn't affecting stroke weight in P3D +X https://github.com/processing/processing/issues/2162 +X add set(boolean) to PShader +X https://github.com/processing/processing/issues/1991 +X https://github.com/processing/processing/pull/1993 +X add PMatrix.preApply(PMatrix) +X https://github.com/processing/processing/pull/2146 +X https://github.com/processing/processing/issues/2145 +X updated to another version of JOGL +X updated to jogl-2.1-b1115, gluegen-2.1-b735 for OSX 10.9 support +X Add warning when no uv texture coordinates are supplied +X https://github.com/processing/processing/issues/2034 +X threading/flicker issues when resizing P2D/P3D/OPENGL +X https://github.com/processing/processing/issues/15 +X additional flicker avoidance +X https://github.com/processing/processing/commit/cca2f08a24ef892c494f5a75aa0e4b01de7e5d8a + + +0222 core (2.1b1) +X background color for present mode has no effect +X https://github.com/processing/processing/issues/2071 +X https://github.com/processing/processing/pull/2072 +X add desktopPath() and desktopFile() methods for testing +X screen stops updating sometimes with retina +X https://github.com/processing/processing/issues/1699 +X Unicode NLF causing problems in XML files +X https://github.com/processing/processing/issues/2100 +X not handled by BufferedReader (or XML parser) +X http://stackoverflow.com/questions/10556875/list-of-unicode-characters-that-should-be-filtered-in-output +X http://stackoverflow.com/questions/3072152/what-is-unicode-character-2028-ls-line-separator-used-for +X fix image transparency in PDF output +X https://github.com/processing/processing/pull/2070 +X Java2D images crash after being resized +X https://github.com/processing/processing/issues/2113 +X constrain lerpColor() between 0 and 1 +X JSONObject/Array.format(-1) not working on embedded JSONObjects +X https://github.com/processing/processing/issues/2119 +X allow println() and print() to take varargs +o https://github.com/processing/processing/issues/2056 +X causes conflict with printing arrays +X added printArray() function instead +o custom DPI settings with PDF +X https://github.com/processing/processing/pull/2069 +X pdf not rendering unicode with beginRecord() +X seems to have fixed itself / can't reproduce +X http://code.google.com/p/processing/issues/detail?id=90 +X https://github.com/processing/processing/issues/129 +X insertRow() bug +X https://github.com/processing/processing/issues/2137 + +opengl +X fix inconsistency with P2D and resetMatrix() +X https://github.com/processing/processing/issues/2087 +X text rendering problems +X https://github.com/processing/processing/issues/2109 +X textSize() not working properly in P2D +X https://github.com/processing/processing/issues/2073 +X incorrectly applied transformations in retained mode +X https://github.com/processing/processing/issues/2097 +X push/popStyle() causes color problems with P2D/P3D +X https://github.com/processing/processing/issues/2102 +X child SVG elements misplaced when rendering with P2D/P3D +X https://github.com/processing/processing/issues/2086 +X SUBTRACT and DIFFERENCE blend modes are swapped +X https://github.com/processing/processing/issues/2075 +X throw an error for textureMode(REPEAT) [fry] +X https://github.com/processing/processing/issues/2052 +X Updated to JOGL 2.1.0 +X https://github.com/processing/processing/issues/2136 +X vertex codes not being properly set in P2D/P3D +X https://github.com/processing/processing/issues/2131 +X some box normals are inverted +X https://github.com/processing/processing/issues/2151 + + +0221 core (2.0.3) +X fix options parsing on loadTable() to handle spaces +X add static versions of loadJSONObject and loadJSONArray that take File inputs +X PVector.angleBetween() returns 0 for 3D vectors whenever x and y are both 0 +X https://github.com/processing/processing/issues/2045 +X https://github.com/processing/processing/pull/2046 + +andres +X blendMode() change causes OpenGL renderer to be very slow +X https://github.com/processing/processing/issues/2021 +X serious OpenGL performance issues on OS X (fixed earlier?) +X https://github.com/processing/processing/issues/1714 +X fixed with a recent JOGL update +X P2D low quality text rendering +X https://github.com/processing/processing/issues/1972 +X Rendering artifacts on the diagonal line (topleft to bottomright) in P2D +X https://github.com/processing/processing/issues/1964 +X Fix issues with slow text rendering and OpenGL +X https://github.com/processing/processing/issues/2025 +X loadShape doesn't load OBJ files in subdirectories properly +X https://github.com/processing/processing/issues/2003 +X more OpenGL issues fixed by JOGL or newer drivers +X https://github.com/processing/processing/issues/1986 +X Vertical offset when sketch height is indivisible by 2 +X https://github.com/processing/processing/issues/1985 +X ellipse() causes RuntimeException: java.lang.OutOfMemoryError +X https://github.com/processing/processing/issues/1941 +X beginShape()..endShape() lines look wrong at joins/caps with P2D +X https://github.com/processing/processing/issues/1927 +X Corrupted text with large font and OpenGL +X https://github.com/processing/processing/issues/1869 +X loadFont hangs on Processing 2.0 with any OpenGL renderer +X https://github.com/processing/processing/issues/1854 +X copy doesn't produce a true copy with P2D and P3D renderers +X https://github.com/processing/processing/issues/1924 +X Additional improvements to memory handling with images +X https://github.com/processing/processing/issues/1975 +X PShape does not draw arc properly +X https://github.com/processing/processing/issues/1990 + +X Additional memory handling changes for render buffers +X https://github.com/processing/processing/issues/1776 +X PShape style is not restored after calling enableStyle in P2D/P3D +X https://github.com/processing/processing/issues/2061 + +video +X problem with bit shifting +X https://github.com/processing/processing/pull/2023 +X https://github.com/processing/processing/pull/2022 +X https://github.com/processing/processing/issues/2021 + + 0220 core (2.0.2) X basic getShape(ch) implementation for font glyph shapes X change QUAD_BEZIER_VERTEX to QUADRATIC_VERTEX to match the API call diff --git a/core/make.sh b/core/make.sh deleted file mode 100755 index 78d91b33a..000000000 --- a/core/make.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/bin/sh - -#javadoc -public -d doc *.java -#javadoc -private -d doc *.java -chmod +x preproc.pl -./preproc.pl -jikes -d . +D *.java diff --git a/core/methods/build.xml b/core/methods/build.xml old mode 100644 new mode 100755 index c37b243eb..ffe0d42bb --- a/core/methods/build.xml +++ b/core/methods/build.xml @@ -7,7 +7,11 @@ + includeantruntime="true" + nowarn="true" + compiler="org.eclipse.jdt.core.JDTCompilerAdapter"> + +
diff --git a/core/src/processing/core/PApplet.java b/core/src/processing/core/PApplet.java index 2f4610242..110814425 100755 --- a/core/src/processing/core/PApplet.java +++ b/core/src/processing/core/PApplet.java @@ -56,6 +56,7 @@ import java.util.zip.*; import javax.imageio.ImageIO; import javax.swing.JFrame; import javax.swing.JFileChooser; +import javax.swing.filechooser.FileSystemView; /** @@ -266,6 +267,9 @@ public class PApplet extends Applet boolean useStrategy = false; Canvas canvas; + Method revalidateMethod; + + // /** // * Usually just 0, but with multiple displays, the X and Y coordinates of // * the screen will depend on the current screen's position relative to @@ -323,7 +327,7 @@ public class PApplet extends Applet public String[] args; /** Path to sketch folder */ - public String sketchPath; //folder; + public String sketchPath; static final boolean DEBUG = false; // static final boolean DEBUG = true; @@ -410,6 +414,8 @@ public class PApplet extends Applet * * ( end auto-generated ) * @webref environment + * @see PApplet#height + * @see PApplet#size(int, int) */ public int width; @@ -424,7 +430,8 @@ public class PApplet extends Applet * * ( end auto-generated ) * @webref environment - * + * @see PApplet#width + * @see PApplet#size(int, int) */ public int height; @@ -437,11 +444,16 @@ public class PApplet extends Applet * ( end auto-generated ) * @webref input:mouse * @see PApplet#mouseY + * @see PApplet#pmouseX + * @see PApplet#pmouseY * @see PApplet#mousePressed * @see PApplet#mousePressed() * @see PApplet#mouseReleased() + * @see PApplet#mouseClicked() * @see PApplet#mouseMoved() * @see PApplet#mouseDragged() + * @see PApplet#mouseButton + * @see PApplet#mouseWheel(MouseEvent) * * */ @@ -456,11 +468,16 @@ public class PApplet extends Applet * ( end auto-generated ) * @webref input:mouse * @see PApplet#mouseX + * @see PApplet#pmouseX + * @see PApplet#pmouseY * @see PApplet#mousePressed * @see PApplet#mousePressed() * @see PApplet#mouseReleased() + * @see PApplet#mouseClicked() * @see PApplet#mouseMoved() * @see PApplet#mouseDragged() + * @see PApplet#mouseButton + * @see PApplet#mouseWheel(MouseEvent) * */ public int mouseY; @@ -489,9 +506,17 @@ public class PApplet extends Applet * * ( end auto-generated ) * @webref input:mouse - * @see PApplet#pmouseY * @see PApplet#mouseX * @see PApplet#mouseY + * @see PApplet#pmouseY + * @see PApplet#mousePressed + * @see PApplet#mousePressed() + * @see PApplet#mouseReleased() + * @see PApplet#mouseClicked() + * @see PApplet#mouseMoved() + * @see PApplet#mouseDragged() + * @see PApplet#mouseButton + * @see PApplet#mouseWheel(MouseEvent) */ public int pmouseX; @@ -505,9 +530,17 @@ public class PApplet extends Applet * * ( end auto-generated ) * @webref input:mouse - * @see PApplet#pmouseX * @see PApplet#mouseX * @see PApplet#mouseY + * @see PApplet#pmouseX + * @see PApplet#mousePressed + * @see PApplet#mousePressed() + * @see PApplet#mouseReleased() + * @see PApplet#mouseClicked() + * @see PApplet#mouseMoved() + * @see PApplet#mouseDragged() + * @see PApplet#mouseButton + * @see PApplet#mouseWheel(MouseEvent) */ public int pmouseY; @@ -557,10 +590,15 @@ public class PApplet extends Applet * @webref input:mouse * @see PApplet#mouseX * @see PApplet#mouseY + * @see PApplet#pmouseX + * @see PApplet#pmouseY + * @see PApplet#mousePressed * @see PApplet#mousePressed() * @see PApplet#mouseReleased() + * @see PApplet#mouseClicked() * @see PApplet#mouseMoved() * @see PApplet#mouseDragged() + * @see PApplet#mouseWheel(MouseEvent) */ public int mouseButton; @@ -575,9 +613,15 @@ public class PApplet extends Applet * @webref input:mouse * @see PApplet#mouseX * @see PApplet#mouseY + * @see PApplet#pmouseX + * @see PApplet#pmouseY + * @see PApplet#mousePressed() * @see PApplet#mouseReleased() + * @see PApplet#mouseClicked() * @see PApplet#mouseMoved() * @see PApplet#mouseDragged() + * @see PApplet#mouseButton + * @see PApplet#mouseWheel(MouseEvent) */ public boolean mousePressed; @@ -769,6 +813,11 @@ public class PApplet extends Applet Object pauseObject = new Object(); Thread thread; + // Background default needs to be different from the default value in + // PGraphics.backgroundColor, otherwise size(100, 100) bg spills over. + // https://github.com/processing/processing/issues/2297 + static final Color WINDOW_BGCOLOR = new Color(0xDD, 0xDD, 0xDD); + // messages to send if attached as an external vm /** @@ -777,6 +826,8 @@ public class PApplet extends Applet */ static public final String ARGS_EDITOR_LOCATION = "--editor-location"; + static public final String ARGS_EXTERNAL = "--external"; + /** * Location for where to position the applet window on screen. *

@@ -784,8 +835,6 @@ public class PApplet extends Applet * location, or could be used by other classes to launch at a * specific position on-screen. */ - static public final String ARGS_EXTERNAL = "--external"; - static public final String ARGS_LOCATION = "--location"; static public final String ARGS_DISPLAY = "--display"; @@ -865,6 +914,12 @@ public class PApplet extends Applet useActive = false; } + if (javaVersion >= 1.7f) { + try { + revalidateMethod = getClass().getMethod("revalidate", new Class[] {}); + } catch (Exception e) { } + } + // send tab keys through to the PApplet setFocusTraversalKeysEnabled(false); @@ -895,11 +950,18 @@ public class PApplet extends Applet online = false; } - try { - if (sketchPath == null) { - sketchPath = System.getProperty("user.dir"); - } - } catch (Exception e) { } // may be a security problem + // Removed in 2.1.2, brought back for 2.1.3. Usually sketchPath is set + // inside runSketch(), but if this sketch takes care of calls to init() + // and setup() itself (i.e. it's in a larger Java application), it'll + // still need to be set here so that fonts, etc can be retrieved. + if (sketchPath == null) { + sketchPath = calcSketchPath(); + } + + // Figure out the available display width and height. + // No major problem if this fails, we have to try again anyway in + // handleDraw() on the first (== 0) frame. + checkDisplaySize(); Dimension size = getSize(); if ((size.width != 0) && (size.height != 0)) { @@ -954,6 +1016,21 @@ public class PApplet extends Applet } + private void checkDisplaySize() { + if (getGraphicsConfiguration() != null) { + GraphicsDevice displayDevice = getGraphicsConfiguration().getDevice(); + + if (displayDevice != null) { + Rectangle screenRect = + displayDevice.getDefaultConfiguration().getBounds(); + + displayWidth = screenRect.width; + displayHeight = screenRect.height; + } + } + } + + private boolean checkRetina() { if (platform == MACOSX) { // This should probably be reset each time there's a display change. @@ -1558,6 +1635,7 @@ public class PApplet extends Applet * @see PApplet#noLoop() * @see PApplet#redraw() * @see PApplet#frameRate(float) + * @see PGraphics#background(float, float, float, float) */ public void draw() { // if no draw method, then shut things down @@ -1650,6 +1728,8 @@ public class PApplet extends Applet * @webref environment * @param w width of the display window in units of pixels * @param h height of the display window in units of pixels + * @see PApplet#width + * @see PApplet#height */ public void size(int w, int h) { size(w, h, JAVA2D, null); @@ -2263,17 +2343,18 @@ public class PApplet extends Applet long now = System.nanoTime(); if (frameCount == 0) { - GraphicsConfiguration gc = getGraphicsConfiguration(); - if (gc == null) return; - GraphicsDevice displayDevice = - getGraphicsConfiguration().getDevice(); - if (displayDevice == null) return; - Rectangle screenRect = - displayDevice.getDefaultConfiguration().getBounds(); -// screenX = screenRect.x; -// screenY = screenRect.y; - displayWidth = screenRect.width; - displayHeight = screenRect.height; +// GraphicsConfiguration gc = getGraphicsConfiguration(); +// if (gc == null) return; +// GraphicsDevice displayDevice = +// getGraphicsConfiguration().getDevice(); +// if (displayDevice == null) return; +// Rectangle screenRect = +// displayDevice.getDefaultConfiguration().getBounds(); +//// screenX = screenRect.x; +//// screenY = screenRect.y; +// displayWidth = screenRect.width; +// displayHeight = screenRect.height; + checkDisplaySize(); try { //println("Calling setup()"); @@ -2333,7 +2414,9 @@ public class PApplet extends Applet render(); } else { Graphics screen = getGraphics(); - screen.drawImage(g.image, 0, 0, width, height, null); + if (screen != null) { + screen.drawImage(g.image, 0, 0, width, height, null); + } } } else { repaint(); @@ -2690,10 +2773,14 @@ public class PApplet extends Applet // also prevents mouseExited() on the mac from hosing the mouse // position, because x/y are bizarre values on the exit event. // see also the id check below.. both of these go together. - // Not necessary to set mouseX/Y on PRESS or RELEASE events because the - // actual position will have been set by a MOVE or DRAG event. - if (event.getAction() == MouseEvent.DRAG || - event.getAction() == MouseEvent.MOVE) { + // Not necessary to set mouseX/Y on RELEASE events because the + // actual position will have been set by a PRESS or DRAG event. + // However, PRESS events might come without a preceeding move, + // if the sketch window gains focus on that PRESS. + final int action = event.getAction(); + if (action == MouseEvent.DRAG || + action == MouseEvent.MOVE || + action == MouseEvent.PRESS) { pmouseX = emouseX; pmouseY = emouseY; mouseX = event.getX(); @@ -2727,7 +2814,7 @@ public class PApplet extends Applet // Do this up here in case a registered method relies on the // boolean for mousePressed. - switch (event.getAction()) { + switch (action) { case MouseEvent.PRESS: mousePressed = true; break; @@ -2738,7 +2825,7 @@ public class PApplet extends Applet handleMethods("mouseEvent", new Object[] { event }); - switch (event.getAction()) { + switch (action) { case MouseEvent.PRESS: // mousePressed = true; mousePressed(event); @@ -2767,8 +2854,8 @@ public class PApplet extends Applet break; } - if ((event.getAction() == MouseEvent.DRAG) || - (event.getAction() == MouseEvent.MOVE)) { + if ((action == MouseEvent.DRAG) || + (action == MouseEvent.MOVE)) { emouseX = mouseX; emouseY = mouseY; } @@ -2981,11 +3068,15 @@ public class PApplet extends Applet * @webref input:mouse * @see PApplet#mouseX * @see PApplet#mouseY + * @see PApplet#pmouseX + * @see PApplet#pmouseY * @see PApplet#mousePressed - * @see PApplet#mouseButton * @see PApplet#mouseReleased() + * @see PApplet#mouseClicked() * @see PApplet#mouseMoved() * @see PApplet#mouseDragged() + * @see PApplet#mouseButton + * @see PApplet#mouseWheel(MouseEvent) */ public void mousePressed() { } @@ -3005,11 +3096,15 @@ public class PApplet extends Applet * @webref input:mouse * @see PApplet#mouseX * @see PApplet#mouseY + * @see PApplet#pmouseX + * @see PApplet#pmouseY * @see PApplet#mousePressed - * @see PApplet#mouseButton * @see PApplet#mousePressed() + * @see PApplet#mouseClicked() * @see PApplet#mouseMoved() * @see PApplet#mouseDragged() + * @see PApplet#mouseButton + * @see PApplet#mouseWheel(MouseEvent) */ public void mouseReleased() { } @@ -3033,11 +3128,15 @@ public class PApplet extends Applet * @webref input:mouse * @see PApplet#mouseX * @see PApplet#mouseY - * @see PApplet#mouseButton + * @see PApplet#pmouseX + * @see PApplet#pmouseY + * @see PApplet#mousePressed * @see PApplet#mousePressed() * @see PApplet#mouseReleased() * @see PApplet#mouseMoved() * @see PApplet#mouseDragged() + * @see PApplet#mouseButton + * @see PApplet#mouseWheel(MouseEvent) */ public void mouseClicked() { } @@ -3057,10 +3156,15 @@ public class PApplet extends Applet * @webref input:mouse * @see PApplet#mouseX * @see PApplet#mouseY + * @see PApplet#pmouseX + * @see PApplet#pmouseY * @see PApplet#mousePressed * @see PApplet#mousePressed() * @see PApplet#mouseReleased() + * @see PApplet#mouseClicked() * @see PApplet#mouseMoved() + * @see PApplet#mouseButton + * @see PApplet#mouseWheel(MouseEvent) */ public void mouseDragged() { } @@ -3080,10 +3184,15 @@ public class PApplet extends Applet * @webref input:mouse * @see PApplet#mouseX * @see PApplet#mouseY + * @see PApplet#pmouseX + * @see PApplet#pmouseY * @see PApplet#mousePressed * @see PApplet#mousePressed() * @see PApplet#mouseReleased() + * @see PApplet#mouseClicked() * @see PApplet#mouseDragged() + * @see PApplet#mouseButton + * @see PApplet#mouseWheel(MouseEvent) */ public void mouseMoved() { } @@ -3120,6 +3229,17 @@ public class PApplet extends Applet * * @webref input:mouse * @param event the MouseEvent + * @see PApplet#mouseX + * @see PApplet#mouseY + * @see PApplet#pmouseX + * @see PApplet#pmouseY + * @see PApplet#mousePressed + * @see PApplet#mousePressed() + * @see PApplet#mouseReleased() + * @see PApplet#mouseClicked() + * @see PApplet#mouseMoved() + * @see PApplet#mouseDragged() + * @see PApplet#mouseButton */ public void mouseWheel(MouseEvent event) { mouseWheel(); @@ -3665,6 +3785,8 @@ public class PApplet extends Applet * ( end auto-generated ) * @webref environment * @param fps number of desired frames per second + * @see PApplet#frameRate + * @see PApplet#frameCount * @see PApplet#setup() * @see PApplet#draw() * @see PApplet#loop() @@ -3989,8 +4111,12 @@ public class PApplet extends Applet } } - - void exitActual() { + /** + * Some subclasses (I'm looking at you, processing.py) might wish to do something + * other than actually terminate the JVM. This gives them a chance to do whatever + * they have in mind when cleaning up. + */ + protected void exitActual() { try { System.exit(0); } catch (SecurityException e) { @@ -4149,6 +4275,7 @@ public class PApplet extends Applet * @webref output:image * @see PApplet#save(String) * @see PApplet#createGraphics(int, int, String, String) + * @see PApplet#frameCount * @param filename any sequence of letters or numbers that ends with either ".tif", ".tga", ".jpg", or ".png" */ public void saveFrame(String filename) { @@ -4201,6 +4328,11 @@ public class PApplet extends Applet * @param kind either ARROW, CROSS, HAND, MOVE, TEXT, or WAIT */ public void cursor(int kind) { + // Swap the HAND cursor because MOVE doesn't seem to be available on OS X + // https://github.com/processing/processing/issues/2358 + if (platform == MACOSX && kind == MOVE) { + kind = HAND; + } setCursor(Cursor.getPredefinedCursor(kind)); cursorVisible = true; this.cursorType = kind; @@ -4335,8 +4467,9 @@ public class PApplet extends Applet * ( end auto-generated ) * @webref output:text_area * @usage IDE - * @param what boolean, byte, char, color, int, float, String, Object - * @see PApplet#println(byte) + * @param what data to print to console + * @see PApplet#println() + * @see PApplet#printArray(Object) * @see PApplet#join(String[], char) */ static public void print(byte what) { @@ -4379,6 +4512,26 @@ public class PApplet extends Applet System.out.flush(); } + /** + * @param variables list of data, separated by commas + */ + static public void print(Object... variables) { + StringBuilder sb = new StringBuilder(); + for (Object o : variables) { + if (sb.length() != 0) { + sb.append(" "); + } + if (o == null) { + sb.append("null"); + } else { + sb.append(o.toString()); + } + } + System.out.print(sb.toString()); + } + + + /* static public void print(Object what) { if (what == null) { // special case since this does fuggly things on > 1.1 @@ -4387,9 +4540,10 @@ public class PApplet extends Applet System.out.println(what.toString()); } } + */ - // -/** + + /** * ( begin auto-generated from println.xml ) * * Writes to the text area of the Processing environment's console. This is @@ -4408,14 +4562,15 @@ public class PApplet extends Applet * @webref output:text_area * @usage IDE * @see PApplet#print(byte) + * @see PApplet#printArray(Object) */ static public void println() { System.out.println(); } - // + /** - * @param what boolean, byte, char, color, int, float, String, Object + * @param what data to print to console */ static public void println(byte what) { System.out.println(what); @@ -4457,7 +4612,60 @@ public class PApplet extends Applet System.out.flush(); } + /** + * @param variables list of data, separated by commas + */ + static public void println(Object... variables) { +// System.out.println("got " + variables.length + " variables"); + print(variables); + println(); + } + + + /* + // Breaking this out since the compiler doesn't know the difference between + // Object... and just Object (with an array passed in). This should take care + // of the confusion for at least the most common case (a String array). + // On second thought, we're going the printArray() route, since the other + // object types are also used frequently. + static public void println(String[] array) { + for (int i = 0; i < array.length; i++) { + System.out.println("[" + i + "] \"" + array[i] + "\""); + } + System.out.flush(); + } + */ + + + /** + * For arrays, use printArray() instead. This function causes a warning + * because the new print(Object...) and println(Object...) functions can't + * be reliably bound by the compiler. + */ static public void println(Object what) { + if (what == null) { + System.out.println("null"); + } else if (what.getClass().isArray()) { + printArray(what); + } else { + System.out.println(what.toString()); + System.out.flush(); + } + } + + /** + * ( begin auto-generated from printArray.xml ) + * + * To come... + * + * ( end auto-generated ) + * @webref output:text_area + * @param what one-dimensional array + * @usage IDE + * @see PApplet#print(byte) + * @see PApplet#println() + */ + static public void printArray(Object what) { if (what == null) { // special case since this does fuggly things on > 1.1 System.out.println("null"); @@ -5141,6 +5349,8 @@ public class PApplet extends Applet * @param amt float between 0.0 and 1.0 * @see PGraphics#curvePoint(float, float, float, float, float) * @see PGraphics#bezierPoint(float, float, float, float, float) + * @see PVector#lerp(PVector, float) + * @see PGraphics#lerpColor(int, int, float) */ static public final float lerp(float start, float stop, float amt) { return start + (stop-start) * amt; @@ -5863,6 +6073,13 @@ public class PApplet extends Applet * Rewritten for 0115 to read/write RLE-encoded targa images. * For 0125, non-RLE encoded images are now supported, along with * images whose y-order is reversed (which is standard for TGA files). + *

+ * A version of this function is in MovieMaker.java. Any fixes here + * should be applied over in MovieMaker as well. + *

+ * Known issue with RLE encoding and odd behavior in some apps: + * https://github.com/processing/processing/issues/2096 + * Please help! */ protected PImage loadImageTGA(String filename) throws IOException { InputStream is = createInput(filename); @@ -5880,7 +6097,7 @@ public class PApplet extends Applet header[2] image type code 2 (0x02) - Uncompressed, RGB images. 3 (0x03) - Uncompressed, black and white images. - 10 (0x0A) - Runlength encoded RGB images. + 10 (0x0A) - Run-length encoded RGB images. 11 (0x0B) - Compressed, black and white images. (grayscale?) header[16] is the bit depth (8, 24, 32) @@ -6103,6 +6320,7 @@ public class PApplet extends Applet public XML loadXML(String filename, String options) { try { return new XML(createReader(filename), options); +// return new XML(createInput(filename), options); } catch (Exception e) { e.printStackTrace(); return null; @@ -6294,7 +6512,7 @@ public class PApplet extends Applet /** - * @webref input:files + * @webref output:files * @param table the Table object to save to a file * @param filename the filename to which the Table should be saved * @see Table @@ -7729,13 +7947,39 @@ public class PApplet extends Applet } + static File desktopFolder; + + /** Not a supported function. For testing use only. */ + static public File desktopFile(String what) { + if (desktopFolder == null) { + // Should work on Linux and OS X (on OS X, even with the localized version). + desktopFolder = new File(System.getProperty("user.home"), "Desktop"); + if (!desktopFolder.exists()) { + if (platform == WINDOWS) { + FileSystemView filesys = FileSystemView.getFileSystemView(); + desktopFolder = filesys.getHomeDirectory(); + } else { + throw new UnsupportedOperationException("Could not find a suitable desktop foldder"); + } + } + } + return new File(desktopFolder, what); + } + + + /** Not a supported function. For testing use only. */ + static public String desktopPath(String what) { + return desktopFile(what).getAbsolutePath(); + } + + /** * Return a full path to an item in the data folder. *

* This is only available with applications, not applets or Android. * On Windows and Linux, this is simply the data folder, which is located * in the same directory as the EXE file and lib folders. On Mac OS X, this - * is a path to the data folder buried inside Contents/Resources/Java. + * is a path to the data folder buried inside Contents/Java. * For the latter point, that also means that the data folder should not be * considered writable. Use sketchPath() for now, or inputPath() and * outputPath() once they're available in the 2.0 release. @@ -7762,7 +8006,7 @@ public class PApplet extends Applet String jarPath = getClass().getProtectionDomain().getCodeSource().getLocation().getPath(); - if (jarPath.contains("Contents/Resources/Java/")) { + if (jarPath.contains("Contents/Java/")) { // The path will be URL encoded (%20 for spaces) coming from above // http://code.google.com/p/processing/issues/detail?id=1073 File containingFolder = new File(urlDecode(jarPath)).getParentFile(); @@ -7776,7 +8020,7 @@ public class PApplet extends Applet /** * On Windows and Linux, this is simply the data folder. On Mac OS X, this is - * the path to the data folder buried inside Contents/Resources/Java + * the path to the data folder buried inside Contents/Java */ // public File inputFile(String where) { // } @@ -8374,19 +8618,20 @@ public class PApplet extends Applet } static final public Object splice(Object list, Object value, int index) { - Object[] outgoing = null; + Class type = list.getClass().getComponentType(); + Object outgoing = null; int length = Array.getLength(list); // check whether item being spliced in is an array if (value.getClass().getName().charAt(0) == '[') { int vlength = Array.getLength(value); - outgoing = new Object[length + vlength]; + outgoing = Array.newInstance(type, length + vlength); System.arraycopy(list, 0, outgoing, 0, index); System.arraycopy(value, 0, outgoing, index, vlength); System.arraycopy(list, index, outgoing, index + vlength, length - index); } else { - outgoing = new Object[length + 1]; + outgoing = Array.newInstance(type, length + 1); System.arraycopy(list, 0, outgoing, 0, index); Array.set(outgoing, index, value); System.arraycopy(list, index, outgoing, index + 1, length - index); @@ -10005,7 +10250,7 @@ public class PApplet extends Applet int alpha = (int) falpha; if (gray > 255) gray = 255; else if (gray < 0) gray = 0; if (alpha > 255) alpha = 255; else if (alpha < 0) alpha = 0; - return 0xff000000 | (gray << 16) | (gray << 8) | gray; + return (alpha << 24) | (gray << 16) | (gray << 8) | gray; } return g.color(fgray, falpha); } @@ -10142,6 +10387,21 @@ public class PApplet extends Applet if (!newBounds.equals(oldBounds)) { // the ComponentListener in PApplet will handle calling size() setBounds(newBounds); + + // In 0225, calling this via reflection so that we can still + // compile in Java 1.6. This is a trap since we really need + // to move to 1.7 and cannot support 1.6, but things like text + // are still a little wonky on 1.7, especially on OS X. + // This gives us a way to at least test against older VMs. + //revalidate(); // let the layout manager do its work + if (revalidateMethod != null) { + try { + revalidateMethod.invoke(PApplet.this); + } catch (Exception ex) { + ex.printStackTrace(); + revalidateMethod = null; + } + } } } } @@ -10342,14 +10602,7 @@ public class PApplet extends Applet boolean hideStop = false; String param = null, value = null; - - // try to get the user folder. if running under java web start, - // this may cause a security exception if the code is not signed. - // http://processing.org/discourse/yabb_beta/YaBB.cgi?board=Integrate;action=display;num=1159386274 - String folder = null; - try { - folder = System.getProperty("user.dir"); - } catch (Exception e) { } + String folder = calcSketchPath(); int argIndex = 0; while (argIndex < args.length) { @@ -10445,7 +10698,7 @@ public class PApplet extends Applet Frame frame = new JFrame(displayDevice.getDefaultConfiguration()); // Default Processing gray, which will be replaced below if another // color is specified on the command line (i.e. in the prefs). - frame.setBackground(new Color(0xCC, 0xCC, 0xCC)); + ((JFrame) frame).getContentPane().setBackground(WINDOW_BGCOLOR); // Cannot call setResizable(false) until later due to OS X (issue #467) final PApplet applet; @@ -10557,7 +10810,7 @@ public class PApplet extends Applet //frame.setExtendedState(Frame.MAXIMIZED_BOTH); frame.setUndecorated(true); if (backgroundColor != null) { - frame.setBackground(backgroundColor); + ((JFrame) frame).getContentPane().setBackground(backgroundColor); } // if (exclusive) { // displayDevice.setFullScreenWindow(frame); @@ -10714,7 +10967,7 @@ public class PApplet extends Applet // // this means no bg color unless specified // backgroundColor = SystemColor.control; // } - frame.setBackground(backgroundColor); + ((JFrame) frame).getContentPane().setBackground(backgroundColor); } // int usableWindowH = windowH - insets.top - insets.bottom; @@ -10782,7 +11035,7 @@ public class PApplet extends Applet final String[] argsWithSketchName = new String[args.length + 1]; System.arraycopy(args, 0, argsWithSketchName, 0, args.length); final String className = this.getClass().getSimpleName(); - final String cleanedClass = + final String cleanedClass = className.replaceAll("__[^_]+__\\$", "").replaceAll("\\$\\d+", ""); argsWithSketchName[args.length] = cleanedClass; runSketch(argsWithSketchName, this); @@ -10794,6 +11047,34 @@ public class PApplet extends Applet } + static protected String calcSketchPath() { + // try to get the user folder. if running under java web start, + // this may cause a security exception if the code is not signed. + // http://processing.org/discourse/yabb_beta/YaBB.cgi?board=Integrate;action=display;num=1159386274 + String folder = null; + try { + folder = System.getProperty("user.dir"); + + // Workaround for bug in Java for OS X from Oracle (7u51) + // https://github.com/processing/processing/issues/2181 + if (platform == MACOSX) { + String jarPath = + PApplet.class.getProtectionDomain().getCodeSource().getLocation().getPath(); + // The jarPath from above will be URL encoded (%20 for spaces) + jarPath = urlDecode(jarPath); + if (jarPath.contains("Contents/Java/")) { + String appPath = jarPath.substring(0, jarPath.indexOf(".app") + 4); + File containingFolder = new File(appPath).getParentFile(); + folder = containingFolder.getAbsolutePath(); + } + } + } catch (Exception e) { + e.printStackTrace(); + } + return folder; + } + + ////////////////////////////////////////////////////////////// @@ -14083,7 +14364,9 @@ public class PApplet extends Applet * ( end auto-generated ) * * @webref color:setting - * @see PGraphics#stroke(float, float, float, float) + * @see PGraphics#stroke(int, float) + * @see PGraphics#fill(float, float, float, float) + * @see PGraphics#noFill() */ public void noStroke() { if (recorder != null) recorder.noStroke(); @@ -14115,7 +14398,11 @@ public class PApplet extends Applet * * @param rgb color value in hexadecimal notation * @see PGraphics#noStroke() + * @see PGraphics#strokeWeight(float) + * @see PGraphics#strokeJoin(int) + * @see PGraphics#strokeCap(int) * @see PGraphics#fill(int, float) + * @see PGraphics#noFill() * @see PGraphics#tint(int, float) * @see PGraphics#background(float, float, float, float) * @see PGraphics#colorMode(int, float, float, float, float) @@ -14279,6 +14566,8 @@ public class PApplet extends Applet * @webref color:setting * @usage web_application * @see PGraphics#fill(float, float, float, float) + * @see PGraphics#stroke(int, float) + * @see PGraphics#noStroke() */ public void noFill() { if (recorder != null) recorder.noFill(); @@ -14316,6 +14605,7 @@ public class PApplet extends Applet * @param rgb color variable or hex value * @see PGraphics#noFill() * @see PGraphics#stroke(int, float) + * @see PGraphics#noStroke() * @see PGraphics#tint(int, float) * @see PGraphics#background(float, float, float, float) * @see PGraphics#colorMode(int, float, float, float, float) @@ -15155,6 +15445,7 @@ public class PApplet extends Applet * @param amt between 0.0 and 1.0 * @see PImage#blendColor(int, int, int) * @see PGraphics#color(float, float, float, float) + * @see PApplet#lerp(float, float, float) */ public int lerpColor(int c1, int c2, float amt) { return g.lerpColor(c1, c2, amt); diff --git a/core/src/processing/core/PGraphics.java b/core/src/processing/core/PGraphics.java index 037ae6fa8..517527102 100644 --- a/core/src/processing/core/PGraphics.java +++ b/core/src/processing/core/PGraphics.java @@ -1096,7 +1096,7 @@ public class PGraphics extends PImage implements PConstants { if (which == ENABLE_NATIVE_FONTS || which == DISABLE_NATIVE_FONTS) { showWarning("hint(ENABLE_NATIVE_FONTS) no longer supported. " + - "Use createFont() instead."); + "Use createFont() instead."); } if (which > 0) { hints[which] = true; @@ -1224,6 +1224,9 @@ public class PGraphics extends PImage implements PConstants { * @see PGraphics#textureWrap(int) */ public void textureMode(int mode) { + if (mode != IMAGE && mode != NORMAL) { + throw new RuntimeException("textureMode() only supports IMAGE and NORMAL"); + } this.textureMode = mode; } @@ -5814,6 +5817,8 @@ public class PGraphics extends PImage implements PConstants { ellipseMode(s.ellipseMode); shapeMode(s.shapeMode); + blendMode(s.blendMode); + if (s.tint) { tint(s.tintColor); } else { @@ -5892,6 +5897,8 @@ public class PGraphics extends PImage implements PConstants { s.ellipseMode = ellipseMode; s.shapeMode = shapeMode; + s.blendMode = blendMode; + s.colorMode = colorMode; s.colorModeX = colorModeX; s.colorModeY = colorModeY; @@ -6028,7 +6035,9 @@ public class PGraphics extends PImage implements PConstants { * ( end auto-generated ) * * @webref color:setting - * @see PGraphics#stroke(float, float, float, float) + * @see PGraphics#stroke(int, float) + * @see PGraphics#fill(float, float, float, float) + * @see PGraphics#noFill() */ public void noStroke() { stroke = false; @@ -6059,7 +6068,11 @@ public class PGraphics extends PImage implements PConstants { * * @param rgb color value in hexadecimal notation * @see PGraphics#noStroke() + * @see PGraphics#strokeWeight(float) + * @see PGraphics#strokeJoin(int) + * @see PGraphics#strokeCap(int) * @see PGraphics#fill(int, float) + * @see PGraphics#noFill() * @see PGraphics#tint(int, float) * @see PGraphics#background(float, float, float, float) * @see PGraphics#colorMode(int, float, float, float, float) @@ -6263,6 +6276,8 @@ public class PGraphics extends PImage implements PConstants { * @webref color:setting * @usage web_application * @see PGraphics#fill(float, float, float, float) + * @see PGraphics#stroke(int, float) + * @see PGraphics#noStroke() */ public void noFill() { fill = false; @@ -6299,6 +6314,7 @@ public class PGraphics extends PImage implements PConstants { * @param rgb color variable or hex value * @see PGraphics#noFill() * @see PGraphics#stroke(int, float) + * @see PGraphics#noStroke() * @see PGraphics#tint(int, float) * @see PGraphics#background(float, float, float, float) * @see PGraphics#colorMode(int, float, float, float, float) @@ -7271,14 +7287,15 @@ public class PGraphics extends PImage implements PConstants { * Strangely the old version of this code ignored the alpha * value. not sure if that was a bug or what. *

- * Note, no need for a bounds check since it's a 32 bit number. + * Note, no need for a bounds check for 'argb' since it's a 32 bit number. + * Bounds now checked on alpha, however (rev 0225). */ protected void colorCalcARGB(int argb, float alpha) { if (alpha == colorModeA) { calcAi = (argb >> 24) & 0xff; calcColor = argb; } else { - calcAi = (int) (((argb >> 24) & 0xff) * (alpha / colorModeA)); + calcAi = (int) (((argb >> 24) & 0xff) * PApplet.constrain((alpha / colorModeA), 0, 1)); calcColor = (calcAi << 24) | (argb & 0xFFFFFF); } calcRi = (argb >> 16) & 0xff; @@ -7621,6 +7638,7 @@ public class PGraphics extends PImage implements PConstants { * @param amt between 0.0 and 1.0 * @see PImage#blendColor(int, int, int) * @see PGraphics#color(float, float, float, float) + * @see PApplet#lerp(float, float, float) */ public int lerpColor(int c1, int c2, float amt) { return lerpColor(c1, c2, amt, colorMode); @@ -7635,6 +7653,9 @@ public class PGraphics extends PImage implements PConstants { * individual color components of a color supplied as an int value. */ static public int lerpColor(int c1, int c2, float amt, int mode) { + if (amt < 0) amt = 0; + if (amt > 1) amt = 1; + if (mode == RGB) { float a1 = ((c1 >> 24) & 0xff); float r1 = (c1 >> 16) & 0xff; diff --git a/core/src/processing/core/PGraphicsJava2D.java b/core/src/processing/core/PGraphicsJava2D.java index f35c83998..4b43c7e90 100644 --- a/core/src/processing/core/PGraphicsJava2D.java +++ b/core/src/processing/core/PGraphicsJava2D.java @@ -3,7 +3,8 @@ /* Part of the Processing project - http://processing.org - Copyright (c) 2005-11 Ben Fry and Casey Reas + Copyright (c) 2013-14 The Processing Foundation + Copyright (c) 2005-13 Ben Fry and Casey Reas This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public @@ -24,10 +25,13 @@ package processing.core; import java.awt.*; +import java.awt.font.TextAttribute; import java.awt.geom.*; import java.awt.image.*; import java.io.InputStream; import java.util.Arrays; +import java.util.Hashtable; +import java.util.Map; import java.util.zip.GZIPInputStream; import processing.data.XML; @@ -35,22 +39,15 @@ import processing.data.XML; /** * Subclass for PGraphics that implements the graphics API using Java2D. - * - *

Pixel operations too slow? As of release 0085 (the first beta), - * the default renderer uses Java2D. It's more accurate than the renderer - * used in alpha releases of Processing (it handles stroke caps and joins, - * and has better polygon tessellation), but it's super slow for handling - * pixels. At least until we get a chance to get the old 2D renderer - * (now called P2D) working in a similar fashion, you can use - * size(w, h, P3D) instead of size(w, h) which will - * be faster for general pixel flipping madness.

- * - *

To get access to the Java 2D "Graphics2D" object for the default + *

+ * To get access to the Java 2D "Graphics2D" object for the default * renderer, use: *

Graphics2D g2 = ((PGraphicsJava2D)g).g2;
* This will let you do Java 2D stuff directly, but is not supported in * any way shape or form. Which just means "have fun, but don't complain - * if it breaks."

+ * if it breaks." + *

+ * Advanced debugging notes for Java2D. */ public class PGraphicsJava2D extends PGraphics { BufferStrategy strategy; @@ -376,7 +373,7 @@ public class PGraphicsJava2D extends PGraphics { PApplet.debug("PGraphicsJava2D.redraw() top of outer do { } block"); do { PApplet.debug("PGraphicsJava2D.redraw() top of inner do { } block"); - System.out.println("strategy is " + strategy); + PApplet.debug("strategy is " + strategy); Graphics bsg = strategy.getDrawGraphics(); if (vimage != null) { bsg.drawImage(vimage, 0, 0, null); @@ -425,6 +422,14 @@ public class PGraphicsJava2D extends PGraphics { @Override protected void defaultSettings() { if (!useCanvas) { + // Papered over another threading issue... + // See if this comes back now that the other issue is fixed. +// while (g2 == null) { +// try { +// System.out.println("sleeping until g2 is available"); +// Thread.sleep(5); +// } catch (InterruptedException e) { } +// } defaultComposite = g2.getComposite(); } super.defaultSettings(); @@ -1187,10 +1192,26 @@ public class PGraphicsJava2D extends PGraphics { g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, quality == 4 ? RenderingHints.VALUE_INTERPOLATION_BICUBIC : RenderingHints.VALUE_INTERPOLATION_BILINEAR); + + // http://docs.oracle.com/javase/tutorial/2d/text/renderinghints.html + // Oracle Java text anti-aliasing on OS X looks like s*t compared to the + // text rendering with Apple's old Java 6. Below, several attempts to fix: + g2.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, + RenderingHints.VALUE_TEXT_ANTIALIAS_ON); + // Turns out this is the one that actually makes things work. + // Kerning is still screwed up, however. + g2.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, + RenderingHints.VALUE_FRACTIONALMETRICS_ON); +// g2.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, +// RenderingHints.VALUE_TEXT_ANTIALIAS_GASP); +// g2.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, +// RenderingHints.VALUE_TEXT_ANTIALIAS_LCD_HRGB); + // g2.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, // RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY); } @@ -1214,6 +1235,8 @@ public class PGraphicsJava2D extends PGraphics { RenderingHints.VALUE_ANTIALIAS_OFF); g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR); + g2.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, + RenderingHints.VALUE_TEXT_ANTIALIAS_OFF); } @@ -1247,15 +1270,25 @@ public class PGraphicsJava2D extends PGraphics { // Image not ready yet, or an error if (who.width <= 0 || who.height <= 0) return; - if (getCache(who) == null) { + ImageCache cash = (ImageCache) getCache(who); + + // Nuke the cache if the image was resized + if (cash != null) { + if (who.width != cash.image.getWidth() || + who.height != cash.image.getHeight()) { + cash = null; + } + } + + if (cash == null) { //System.out.println("making new image cache"); - this.setCache(who, new ImageCache(who)); + cash = new ImageCache(); //who); + setCache(who, cash); who.updatePixels(); // mark the whole thing for update who.modified = true; } - ImageCache cash = (ImageCache) getCache(who); - // if image previously was tinted, or the color changed + // If image previously was tinted, or the color changed // or the image was tinted, and tint is now disabled if ((tint && !cash.tinted) || (tint && (cash.tintedColor != tintColor)) || @@ -1273,8 +1306,8 @@ public class PGraphicsJava2D extends PGraphics { (int) x1, (int) y1, (int) x2, (int) y2, u1, v1, u2, v2, null); - // every few years I think "nah, Java2D couldn't possibly be that - // f*king slow, why are we doing this by hand? then comes the affirmation. + // Every few years I think "nah, Java2D couldn't possibly be that f*king + // slow, why are we doing this by hand?" then comes the affirmation: // Composite oldComp = null; // if (false && tint) { // oldComp = g2.getComposite(); @@ -1300,17 +1333,18 @@ public class PGraphicsJava2D extends PGraphics { class ImageCache { boolean tinted; int tintedColor; - int tintedPixels[]; // one row of tinted pixels + int[] tintedTemp; // one row of tinted pixels BufferedImage image; +// BufferedImage compat; - public ImageCache(PImage source) { -// this.source = source; - // even if RGB, set the image type to ARGB, because the - // image may have an alpha value for its tint(). -// int type = BufferedImage.TYPE_INT_ARGB; - //System.out.println("making new buffered image"); -// image = new BufferedImage(source.width, source.height, type); - } +// public ImageCache(PImage source) { +//// this.source = source; +// // even if RGB, set the image type to ARGB, because the +// // image may have an alpha value for its tint(). +//// int type = BufferedImage.TYPE_INT_ARGB; +// //System.out.println("making new buffered image"); +//// image = new BufferedImage(source.width, source.height, type); +// } /** * Update the pixels of the cache image. Already determined that the tint @@ -1318,29 +1352,41 @@ public class PGraphicsJava2D extends PGraphics { * with the update without further checks. */ public void update(PImage source, boolean tint, int tintColor) { - int bufferType = BufferedImage.TYPE_INT_ARGB; + //int bufferType = BufferedImage.TYPE_INT_ARGB; + int targetType = ARGB; boolean opaque = (tintColor & 0xFF000000) == 0xFF000000; if (source.format == RGB) { if (!tint || (tint && opaque)) { - bufferType = BufferedImage.TYPE_INT_RGB; + //bufferType = BufferedImage.TYPE_INT_RGB; + targetType = RGB; } } - boolean wrongType = (image != null) && (image.getType() != bufferType); - if ((image == null) || wrongType) { - image = new BufferedImage(source.width, source.height, bufferType); +// boolean wrongType = (image != null) && (image.getType() != bufferType); +// if ((image == null) || wrongType) { +// image = new BufferedImage(source.width, source.height, bufferType); +// } + // Must always use an ARGB image, otherwise will write zeros + // in the alpha channel when drawn to the screen. + // https://github.com/processing/processing/issues/2030 + if (image == null) { + image = new BufferedImage(source.width, source.height, + BufferedImage.TYPE_INT_ARGB); } WritableRaster wr = image.getRaster(); if (tint) { - if (tintedPixels == null || tintedPixels.length != source.width) { - tintedPixels = new int[source.width]; + if (tintedTemp == null || tintedTemp.length != source.width) { + tintedTemp = new int[source.width]; } int a2 = (tintColor >> 24) & 0xff; +// System.out.println("tint color is " + a2); +// System.out.println("source.pixels[0] alpha is " + (source.pixels[0] >>> 24)); int r2 = (tintColor >> 16) & 0xff; int g2 = (tintColor >> 8) & 0xff; int b2 = (tintColor) & 0xff; - if (bufferType == BufferedImage.TYPE_INT_RGB) { + //if (bufferType == BufferedImage.TYPE_INT_RGB) { + if (targetType == RGB) { // The target image is opaque, meaning that the source image has no // alpha (is not ARGB), and the tint has no alpha. int index = 0; @@ -1351,12 +1397,15 @@ public class PGraphicsJava2D extends PGraphics { int g1 = (argb1 >> 8) & 0xff; int b1 = (argb1) & 0xff; - tintedPixels[x] = //0xFF000000 | + // Prior to 2.1, the alpha channel was commented out here, + // but can't remember why (just thought unnecessary b/c of RGB?) + // https://github.com/processing/processing/issues/2030 + tintedTemp[x] = 0xFF000000 | (((r2 * r1) & 0xff00) << 8) | ((g2 * g1) & 0xff00) | (((b2 * b1) & 0xff00) >> 8); } - wr.setDataElements(0, y, source.width, 1, tintedPixels); + wr.setDataElements(0, y, source.width, 1, tintedTemp); } // could this be any slower? // float[] scales = { tintR, tintG, tintB }; @@ -1364,16 +1413,17 @@ public class PGraphicsJava2D extends PGraphics { // RescaleOp op = new RescaleOp(scales, offsets, null); // op.filter(image, image); - } else if (bufferType == BufferedImage.TYPE_INT_ARGB) { + //} else if (bufferType == BufferedImage.TYPE_INT_ARGB) { + } else if (targetType == ARGB) { if (source.format == RGB && (tintColor & 0xffffff) == 0xffffff) { int hi = tintColor & 0xff000000; int index = 0; for (int y = 0; y < source.height; y++) { for (int x = 0; x < source.width; x++) { - tintedPixels[x] = hi | (source.pixels[index++] & 0xFFFFFF); + tintedTemp[x] = hi | (source.pixels[index++] & 0xFFFFFF); } - wr.setDataElements(0, y, source.width, 1, tintedPixels); + wr.setDataElements(0, y, source.width, 1, tintedTemp); } } else { int index = 0; @@ -1385,7 +1435,7 @@ public class PGraphicsJava2D extends PGraphics { int r1 = (argb1 >> 16) & 0xff; int g1 = (argb1 >> 8) & 0xff; int b1 = (argb1) & 0xff; - tintedPixels[x] = alpha | + tintedTemp[x] = alpha | (((r2 * r1) & 0xff00) << 8) | ((g2 * g1) & 0xff00) | (((b2 * b1) & 0xff00) >> 8); @@ -1397,7 +1447,7 @@ public class PGraphicsJava2D extends PGraphics { int r1 = (argb1 >> 16) & 0xff; int g1 = (argb1 >> 8) & 0xff; int b1 = (argb1) & 0xff; - tintedPixels[x] = + tintedTemp[x] = (((a2 * a1) & 0xff00) << 16) | (((r2 * r1) & 0xff00) << 8) | ((g2 * g1) & 0xff00) | @@ -1407,11 +1457,11 @@ public class PGraphicsJava2D extends PGraphics { int lower = tintColor & 0xFFFFFF; for (int x = 0; x < source.width; x++) { int a1 = source.pixels[index++]; - tintedPixels[x] = + tintedTemp[x] = (((a2 * a1) & 0xff00) << 16) | lower; } } - wr.setDataElements(0, y, source.width, 1, tintedPixels); + wr.setDataElements(0, y, source.width, 1, tintedTemp); } } // Not sure why ARGB images take the scales in this order... @@ -1420,11 +1470,30 @@ public class PGraphicsJava2D extends PGraphics { // RescaleOp op = new RescaleOp(scales, offsets, null); // op.filter(image, image); } - } else { + } else { // !tint + if (targetType == RGB && (source.pixels[0] >> 24 == 0)) { + // If it's an RGB image and the high bits aren't set, need to set + // the high bits to opaque because we're drawing ARGB images. + source.filter(OPAQUE); + // Opting to just manipulate the image here, since it shouldn't + // affect anything else (and alpha(get(x, y)) should return 0xff). + // Wel also make no guarantees about the values of the pixels array + // in a PImage and how the high bits will be set. + } + // If no tint, just shove the pixels on in there verbatim wr.setDataElements(0, 0, source.width, source.height, source.pixels); } this.tinted = tint; this.tintedColor = tintColor; + +// GraphicsConfiguration gc = parent.getGraphicsConfiguration(); +// compat = gc.createCompatibleImage(image.getWidth(), +// image.getHeight(), +// Transparency.TRANSLUCENT); +// +// Graphics2D g = compat.createGraphics(); +// g.drawImage(image, 0, 0, null); +// g.dispose(); } } @@ -1513,7 +1582,7 @@ public class PGraphicsJava2D extends PGraphics { @Override public float textDescent() { if (textFont == null) { - defaultFontOrDeath("textAscent"); + defaultFontOrDeath("textDescent"); } Font font = (Font) textFont.getNative(); //if (font != null && (textFont.isStream() || hints[ENABLE_NATIVE_FONTS])) { @@ -1552,7 +1621,7 @@ public class PGraphicsJava2D extends PGraphics { @Override public void textSize(float size) { if (textFont == null) { - defaultFontOrDeath("textAscent", size); + defaultFontOrDeath("textSize", size); } // if a native version available, derive this font @@ -1564,9 +1633,24 @@ public class PGraphicsJava2D extends PGraphics { Font font = (Font) textFont.getNative(); //if (font != null && (textFont.isStream() || hints[ENABLE_NATIVE_FONTS])) { if (font != null) { - Font dfont = font.deriveFont(size); - g2.setFont(dfont); - textFont.setNative(dfont); + Map map = + new Hashtable(); + map.put(TextAttribute.SIZE, size); + map.put(TextAttribute.KERNING, + TextAttribute.KERNING_ON); +// map.put(TextAttribute.TRACKING, +// TextAttribute.TRACKING_TIGHT); + font = font.deriveFont(map); + g2.setFont(font); + textFont.setNative(font); + +// Font dfont = font.deriveFont(size); +//// Map attrs = dfont.getAttributes(); +//// for (TextAttribute ta : attrs.keySet()) { +//// System.out.println(ta + " -> " + attrs.get(ta)); +//// } +// g2.setFont(dfont); +// textFont.setNative(dfont); } // take care of setting the textSize and textLeading vars @@ -1584,6 +1668,10 @@ public class PGraphicsJava2D extends PGraphics { @Override protected float textWidthImpl(char buffer[], int start, int stop) { + if (textFont == null) { + defaultFontOrDeath("textWidth"); + } + Font font = (Font) textFont.getNative(); //if (font != null && (textFont.isStream() || hints[ENABLE_NATIVE_FONTS])) { if (font != null) { @@ -1673,31 +1761,28 @@ public class PGraphicsJava2D extends PGraphics { RenderingHints.VALUE_ANTIALIAS_ON : RenderingHints.VALUE_ANTIALIAS_OFF); - //System.out.println("setting frac metrics"); - //g2.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, - // RenderingHints.VALUE_FRACTIONALMETRICS_ON); - g2.setColor(fillColorObject); + int length = stop - start; + if (length != 0) { g2.drawChars(buffer, start, length, (int) (x + 0.5f), (int) (y + 0.5f)); + // better to use round here? also, drawChars now just calls drawString +// g2.drawString(new String(buffer, start, stop - start), Math.round(x), Math.round(y)); + // better to use drawString() with floats? (nope, draws the same) //g2.drawString(new String(buffer, start, length), x, y); - // this didn't seem to help the scaling issue - // and creates garbage because of the new temporary object - //java.awt.font.GlyphVector gv = textFontNative.createGlyphVector(g2.getFontRenderContext(), new String(buffer, start, stop)); - //g2.drawGlyphVector(gv, x, y); - - // System.out.println("text() " + new String(buffer, start, stop)); + // this didn't seem to help the scaling issue, and creates garbage + // because of a fairly heavyweight new temporary object +// java.awt.font.GlyphVector gv = +// font.createGlyphVector(g2.getFontRenderContext(), new String(buffer, start, stop - start)); +// g2.drawGlyphVector(gv, x, y); + } // return to previous smoothing state if it was changed //g2.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, textAntialias); g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, antialias); -// textX = x + textWidthImpl(buffer, start, stop); -// textY = y; -// textZ = 0; // this will get set by the caller if non-zero - } else { // otherwise just do the default super.textLineImpl(buffer, start, stop, x, y); } @@ -2295,7 +2380,7 @@ public class PGraphicsJava2D extends PGraphics { if (primarySurface) { // 'offscreen' will probably be removed in the next release if (useOffscreen) { - raster = ((BufferedImage) offscreen).getRaster(); + raster = offscreen.getRaster(); } else if (image instanceof VolatileImage) { // when possible, we'll try VolatileImage raster = ((VolatileImage) image).getSnapshot().getRaster(); @@ -2326,7 +2411,15 @@ public class PGraphicsJava2D extends PGraphics { pixels = new int[width * height]; } - getRaster().getDataElements(0, 0, width, height, pixels); + WritableRaster raster = getRaster(); + raster.getDataElements(0, 0, width, height, pixels); + if (raster.getNumBands() == 3) { + // Java won't set the high bits when RGB, returns 0 for alpha + // https://github.com/processing/processing/issues/2030 + for (int i = 0; i < pixels.length; i++) { + pixels[i] = 0xff000000 | pixels[i]; + } + } //((BufferedImage) image).getRGB(0, 0, width, height, pixels, 0, width); // WritableRaster raster = ((BufferedImage) (useOffscreen && primarySurface ? offscreen : image)).getRaster(); // WritableRaster raster = image.getRaster(); @@ -2395,8 +2488,12 @@ public class PGraphicsJava2D extends PGraphics { if ((x < 0) || (y < 0) || (x >= width) || (y >= height)) return 0; //return ((BufferedImage) image).getRGB(x, y); // WritableRaster raster = ((BufferedImage) (useOffscreen && primarySurface ? offscreen : image)).getRaster(); -// WritableRaster raster = image.getRaster(); - getRaster().getDataElements(x, y, getset); + WritableRaster raster = getRaster(); + raster.getDataElements(x, y, getset); + if (raster.getNumBands() == 3) { + // https://github.com/processing/processing/issues/2030 + return getset[0] | 0xff000000; + } return getset[0]; } @@ -2422,6 +2519,10 @@ public class PGraphicsJava2D extends PGraphics { if (sourceWidth == target.width && sourceHeight == target.height) { raster.getDataElements(sourceX, sourceY, sourceWidth, sourceHeight, target.pixels); + // https://github.com/processing/processing/issues/2030 + if (raster.getNumBands() == 3) { + target.filter(OPAQUE); + } } else { // TODO optimize, incredibly inefficient to reallocate this much memory @@ -2432,7 +2533,15 @@ public class PGraphicsJava2D extends PGraphics { int sourceOffset = 0; int targetOffset = targetY*target.width + targetX; for (int y = 0; y < sourceHeight; y++) { - System.arraycopy(temp, sourceOffset, target.pixels, targetOffset, sourceWidth); + if (raster.getNumBands() == 3) { + for (int i = 0; i < sourceWidth; i++) { + // Need to set the high bits for this feller + // https://github.com/processing/processing/issues/2030 + target.pixels[targetOffset + i] = 0xFF000000 | temp[sourceOffset + i]; + } + } else { + System.arraycopy(temp, sourceOffset, target.pixels, targetOffset, sourceWidth); + } sourceOffset += sourceWidth; targetOffset += target.width; } diff --git a/core/src/processing/core/PGraphicsRetina2D.java b/core/src/processing/core/PGraphicsRetina2D.java index 44c058942..786e3ba6f 100644 --- a/core/src/processing/core/PGraphicsRetina2D.java +++ b/core/src/processing/core/PGraphicsRetina2D.java @@ -87,11 +87,19 @@ public class PGraphicsRetina2D extends PGraphicsJava2D { // FRAME + + @Override + public boolean canDraw() { + return parent.getGraphicsConfiguration() != null; + } + + @Override public void beginDraw() { // g2 = (Graphics2D) parent.getGraphics(); GraphicsConfiguration gc = parent.getGraphicsConfiguration(); + // if (false) { // if (image == null || ((VolatileImage) image).validate(gc) == VolatileImage.IMAGE_INCOMPATIBLE) { // image = gc.createCompatibleVolatileImage(width*2, height*2); diff --git a/core/src/processing/core/PImage.java b/core/src/processing/core/PImage.java index e3994c91e..430d67228 100644 --- a/core/src/processing/core/PImage.java +++ b/core/src/processing/core/PImage.java @@ -477,7 +477,7 @@ public class PImage implements PConstants, Cloneable { if (pixels == null || pixels.length != width*height) { pixels = new int[width*height]; } - isLoaded(); + setLoaded(); } diff --git a/core/src/processing/core/PMatrix.java b/core/src/processing/core/PMatrix.java index 53f7fa54b..2670eb5ce 100644 --- a/core/src/processing/core/PMatrix.java +++ b/core/src/processing/core/PMatrix.java @@ -97,6 +97,8 @@ public interface PMatrix { /** * Apply another matrix to the left of this one. */ + public void preApply(PMatrix left); + public void preApply(PMatrix2D left); public void preApply(PMatrix3D left); diff --git a/core/src/processing/core/PMatrix2D.java b/core/src/processing/core/PMatrix2D.java index d5616b58d..1ee6b0a88 100644 --- a/core/src/processing/core/PMatrix2D.java +++ b/core/src/processing/core/PMatrix2D.java @@ -247,6 +247,15 @@ public class PMatrix2D implements PMatrix { /** * Apply another matrix to the left of this one. */ + public void preApply(PMatrix source) { + if (source instanceof PMatrix2D) { + preApply((PMatrix2D) source); + } else if (source instanceof PMatrix3D) { + preApply((PMatrix3D) source); + } + } + + public void preApply(PMatrix2D left) { preApply(left.m00, left.m01, left.m02, left.m10, left.m11, left.m12); diff --git a/core/src/processing/core/PMatrix3D.java b/core/src/processing/core/PMatrix3D.java index deeb23b45..f75cb1227 100644 --- a/core/src/processing/core/PMatrix3D.java +++ b/core/src/processing/core/PMatrix3D.java @@ -369,6 +369,15 @@ public final class PMatrix3D implements PMatrix /*, PConstants*/ { /** * Apply another matrix to the left of this one. */ + public void preApply(PMatrix source) { + if (source instanceof PMatrix2D) { + preApply((PMatrix2D) source); + } else if (source instanceof PMatrix3D) { + preApply((PMatrix3D) source); + } + } + + public void preApply(PMatrix3D left) { preApply(left.m00, left.m01, left.m02, left.m03, left.m10, left.m11, left.m12, left.m13, diff --git a/core/src/processing/core/PShape.java b/core/src/processing/core/PShape.java index 40ec381f5..8420f72f6 100644 --- a/core/src/processing/core/PShape.java +++ b/core/src/processing/core/PShape.java @@ -167,6 +167,8 @@ public class PShape implements PConstants { protected float shininess; protected int sphereDetailU, sphereDetailV; + protected int rectMode; + protected int ellipseMode; /** Temporary toggle for whether styles should be honored. */ protected boolean style = true; diff --git a/core/src/processing/core/PShapeSVG.java b/core/src/processing/core/PShapeSVG.java index 1be0b30d6..7bac4fb62 100644 --- a/core/src/processing/core/PShapeSVG.java +++ b/core/src/processing/core/PShapeSVG.java @@ -586,6 +586,8 @@ public class PShapeSVG extends PShape { case 'm': // m - move to (relative) cx = cx + PApplet.parseFloat(pathTokens[i + 1]); cy = cy + PApplet.parseFloat(pathTokens[i + 2]); + movetoX = cx; + movetoY = cy; parsePathMoveto(cx, cy); implicitCommand = 'l'; i += 3; diff --git a/core/src/processing/core/PStyle.java b/core/src/processing/core/PStyle.java index ccea03a42..7b27695cf 100644 --- a/core/src/processing/core/PStyle.java +++ b/core/src/processing/core/PStyle.java @@ -30,6 +30,8 @@ public class PStyle implements PConstants { public int ellipseMode; public int shapeMode; + public int blendMode; + public int colorMode; public float colorModeX; public float colorModeY; diff --git a/core/src/processing/core/PVector.java b/core/src/processing/core/PVector.java index ca25eb1db..49e54a0d0 100644 --- a/core/src/processing/core/PVector.java +++ b/core/src/processing/core/PVector.java @@ -161,7 +161,7 @@ public class PVector implements Serializable { * @param x the x component of the vector * @param y the y component of the vector * @param z the z component of the vector - * @brief Set the x, y, and z component of the vector + * @brief Set the components of the vector */ public void set(float x, float y, float z) { this.x = x; @@ -170,11 +170,8 @@ public class PVector implements Serializable { } /** - * - * @webref pvector:method * @param x the x component of the vector * @param y the y component of the vector - * @brief Set the x, y components of the vector */ public void set(float x, float y) { this.x = x; @@ -322,7 +319,7 @@ public class PVector implements Serializable { * @webref pvector:method * @usage web_application * @brief Make a new 2D unit vector from an angle - * @param angle the angle + * @param angle the angle in radians * @return the new unit PVector */ static public PVector fromAngle(float angle) { @@ -880,6 +877,7 @@ public class PVector implements Serializable { * @brief Linear interpolate the vector to another vector * @param v the vector to lerp to * @param amt The amount of interpolation; some value between 0.0 (old vector) and 1.0 (new vector). 0.1 is very near the new vector. 0.5 is halfway in between. + * @see PApplet#lerp(float, float, float) */ public void lerp(PVector v, float amt) { x = PApplet.lerp(x,v.x,amt); diff --git a/core/src/processing/data/FloatDict.java b/core/src/processing/data/FloatDict.java index 91b2b7538..e9b902396 100644 --- a/core/src/processing/data/FloatDict.java +++ b/core/src/processing/data/FloatDict.java @@ -53,18 +53,16 @@ public class FloatDict { * @nowebref */ public FloatDict(BufferedReader reader) { -// public FloatHash(PApplet parent, String filename) { String[] lines = PApplet.loadStrings(reader); keys = new String[lines.length]; values = new float[lines.length]; -// boolean csv = (lines[0].indexOf('\t') == -1); for (int i = 0; i < lines.length; i++) { -// String[] pieces = csv ? Table.splitLineCSV(lines[i]) : PApplet.split(lines[i], '\t'); String[] pieces = PApplet.split(lines[i], '\t'); if (pieces.length == 2) { keys[count] = pieces[0]; values[count] = PApplet.parseFloat(pieces[1]); + indices.put(pieces[0], count); count++; } } @@ -518,7 +516,7 @@ public class FloatDict { } - protected void swap(int a, int b) { + public void swap(int a, int b) { String tkey = keys[a]; float tvalue = values[a]; keys[a] = keys[b]; diff --git a/core/src/processing/data/IntDict.java b/core/src/processing/data/IntDict.java index 4cc3d927f..b581eaecf 100644 --- a/core/src/processing/data/IntDict.java +++ b/core/src/processing/data/IntDict.java @@ -76,18 +76,16 @@ public class IntDict { * @nowebref */ public IntDict(BufferedReader reader) { -// public IntHash(PApplet parent, String filename) { String[] lines = PApplet.loadStrings(reader); keys = new String[lines.length]; values = new int[lines.length]; -// boolean csv = (lines[0].indexOf('\t') == -1); for (int i = 0; i < lines.length; i++) { -// String[] pieces = csv ? Table.splitLineCSV(lines[i]) : PApplet.split(lines[i], '\t'); String[] pieces = PApplet.split(lines[i], '\t'); if (pieces.length == 2) { keys[count] = pieces[0]; values[count] = PApplet.parseInt(pieces[1]); + indices.put(pieces[0], count); count++; } } @@ -526,7 +524,7 @@ public class IntDict { } - protected void swap(int a, int b) { + public void swap(int a, int b) { String tkey = keys[a]; int tvalue = values[a]; keys[a] = keys[b]; diff --git a/core/src/processing/data/IntList.java b/core/src/processing/data/IntList.java index 1b8860fcc..78775be3f 100644 --- a/core/src/processing/data/IntList.java +++ b/core/src/processing/data/IntList.java @@ -771,6 +771,13 @@ public class IntList implements Iterable { } + public void print() { + for (int i = 0; i < size(); i++) { + System.out.format("[%d] %d%n", i, data[i]); + } + } + + @Override public String toString() { StringBuilder sb = new StringBuilder(); diff --git a/core/src/processing/data/JSONArray.java b/core/src/processing/data/JSONArray.java index c25ac93f3..0e665c805 100644 --- a/core/src/processing/data/JSONArray.java +++ b/core/src/processing/data/JSONArray.java @@ -1182,9 +1182,10 @@ public class JSONArray { if (length == 1) { JSONObject.writeValue(writer, this.myArrayList.get(0), - thisFactor, indent); + indentFactor, indent); +// thisFactor, indent); } else if (length != 0) { - final int newindent = indent + thisFactor; + final int newIndent = indent + thisFactor; for (int i = 0; i < length; i += 1) { if (commanate) { @@ -1193,9 +1194,11 @@ public class JSONArray { if (indentFactor != -1) { writer.write('\n'); } - JSONObject.indent(writer, newindent); + JSONObject.indent(writer, newIndent); +// JSONObject.writeValue(writer, this.myArrayList.get(i), +// thisFactor, newIndent); JSONObject.writeValue(writer, this.myArrayList.get(i), - thisFactor, newindent); + indentFactor, newIndent); commanate = true; } if (indentFactor != -1) { diff --git a/core/src/processing/data/JSONObject.java b/core/src/processing/data/JSONObject.java index 0b1c1127c..69add3837 100644 --- a/core/src/processing/data/JSONObject.java +++ b/core/src/processing/data/JSONObject.java @@ -1644,10 +1644,10 @@ public class JSONObject { return value.toString(); } if (value instanceof Map) { - return new JSONObject((Map)value).toString(); + return new JSONObject(value).toString(); } if (value instanceof Collection) { - return new JSONArray((Collection)value).toString(); + return new JSONArray(value).toString(); } if (value.getClass().isArray()) { return new JSONArray(value).toString(); @@ -1683,13 +1683,13 @@ public class JSONObject { } if (object instanceof Collection) { - return new JSONArray((Collection)object); + return new JSONArray(object); } if (object.getClass().isArray()) { return new JSONArray(object); } if (object instanceof Map) { - return new JSONObject((Map)object); + return new JSONObject(object); } Package objectPackage = object.getClass().getPackage(); String objectPackageName = objectPackage != null @@ -1732,9 +1732,9 @@ public class JSONObject { } else if (value instanceof JSONArray) { ((JSONArray) value).write(writer, indentFactor, indent); } else if (value instanceof Map) { - new JSONObject((Map) value).write(writer, indentFactor, indent); + new JSONObject(value).write(writer, indentFactor, indent); } else if (value instanceof Collection) { - new JSONArray((Collection) value).write(writer, indentFactor, + new JSONArray(value).write(writer, indentFactor, indent); } else if (value.getClass().isArray()) { new JSONArray(value).write(writer, indentFactor, indent); @@ -1790,9 +1790,10 @@ public class JSONObject { if (actualFactor > 0) { writer.write(' '); } - writeValue(writer, this.map.get(key), actualFactor, indent); + //writeValue(writer, this.map.get(key), actualFactor, indent); + writeValue(writer, this.map.get(key), indentFactor, indent); } else if (length != 0) { - final int newindent = indent + actualFactor; + final int newIndent = indent + actualFactor; while (keys.hasNext()) { Object key = keys.next(); if (commanate) { @@ -1801,14 +1802,14 @@ public class JSONObject { if (indentFactor != -1) { writer.write('\n'); } - indent(writer, newindent); + indent(writer, newIndent); writer.write(quote(key.toString())); writer.write(':'); if (actualFactor > 0) { writer.write(' '); } - writeValue(writer, this.map.get(key), actualFactor, - newindent); + //writeValue(writer, this.map.get(key), actualFactor, newIndent); + writeValue(writer, this.map.get(key), indentFactor, newIndent); commanate = true; } if (indentFactor != -1) { diff --git a/core/src/processing/data/StringDict.java b/core/src/processing/data/StringDict.java index 5fb533616..38b2175eb 100644 --- a/core/src/processing/data/StringDict.java +++ b/core/src/processing/data/StringDict.java @@ -63,6 +63,7 @@ public class StringDict { if (pieces.length == 2) { keys[count] = pieces[0]; values[count] = pieces[1]; + indices.put(keys[count], count); count++; } } @@ -316,7 +317,7 @@ public class StringDict { } - protected void swap(int a, int b) { + public void swap(int a, int b) { String tkey = keys[a]; String tvalue = values[a]; keys[a] = keys[b]; diff --git a/core/src/processing/data/Table.java b/core/src/processing/data/Table.java index 4f8650a21..d98a9db51 100644 --- a/core/src/processing/data/Table.java +++ b/core/src/processing/data/Table.java @@ -1165,7 +1165,12 @@ public class Table { output.writeDouble(row.getDouble(col)); break; case CATEGORY: - output.writeInt(columnCategories[col].index(row.getString(col))); + String peace = row.getString(col); + if (peace.equals(missingString)) { + output.writeInt(missingCategory); + } else { + output.writeInt(columnCategories[col].index(peace)); + } break; } } @@ -1859,35 +1864,35 @@ public class Table { case INT: { int[] intTemp = new int[rowCount+1]; System.arraycopy(columns[col], 0, intTemp, 0, insert); - System.arraycopy(columns[col], insert, intTemp, insert+1, (rowCount - insert) + 1); + System.arraycopy(columns[col], insert, intTemp, insert+1, rowCount - insert); columns[col] = intTemp; break; } case LONG: { long[] longTemp = new long[rowCount+1]; System.arraycopy(columns[col], 0, longTemp, 0, insert); - System.arraycopy(columns[col], insert, longTemp, insert+1, (rowCount - insert) + 1); + System.arraycopy(columns[col], insert, longTemp, insert+1, rowCount - insert); columns[col] = longTemp; break; } case FLOAT: { float[] floatTemp = new float[rowCount+1]; System.arraycopy(columns[col], 0, floatTemp, 0, insert); - System.arraycopy(columns[col], insert, floatTemp, insert+1, (rowCount - insert) + 1); + System.arraycopy(columns[col], insert, floatTemp, insert+1, rowCount - insert); columns[col] = floatTemp; break; } case DOUBLE: { double[] doubleTemp = new double[rowCount+1]; System.arraycopy(columns[col], 0, doubleTemp, 0, insert); - System.arraycopy(columns[col], insert, doubleTemp, insert+1, (rowCount - insert) + 1); + System.arraycopy(columns[col], insert, doubleTemp, insert+1, rowCount - insert); columns[col] = doubleTemp; break; } case STRING: { String[] stringTemp = new String[rowCount+1]; System.arraycopy(columns[col], 0, stringTemp, 0, insert); - System.arraycopy(columns[col], insert, stringTemp, insert+1, (rowCount - insert) + 1); + System.arraycopy(columns[col], insert, stringTemp, insert+1, rowCount - insert); columns[col] = stringTemp; break; } @@ -2087,7 +2092,12 @@ public class Table { if (piece == null) { indexData[row] = missingCategory; } else { - indexData[row] = columnCategories[col].index(String.valueOf(piece)); + String peace = String.valueOf(piece); + if (peace.equals(missingString)) { // missingString might be null + indexData[row] = missingCategory; + } else { + indexData[row] = columnCategories[col].index(peace); + } } break; default: @@ -2933,6 +2943,9 @@ public class Table { } + /** + * Treat entries with this string as "missing". Also used for categorial. + */ public void setMissingString(String value) { missingString = value; } @@ -3562,6 +3575,7 @@ public class Table { read(input); } + /** gets the index, and creates one if it doesn't already exist. */ int index(String key) { Integer value = dataToIndex.get(key); if (value != null) { @@ -4131,7 +4145,12 @@ public class Table { } break; case CATEGORY: - output.writeInt(columnCategories[col].index(pieces[col])); + String peace = pieces[col]; + if (peace.equals(missingString)) { + output.writeInt(missingCategory); + } else { + output.writeInt(columnCategories[col].index(peace)); + } break; } } diff --git a/core/src/processing/data/XML.java b/core/src/processing/data/XML.java index 28023b768..27ce09342 100644 --- a/core/src/processing/data/XML.java +++ b/core/src/processing/data/XML.java @@ -82,7 +82,9 @@ public class XML implements Serializable { /** - * Advanced users only; see loadXML() in PApplet. + * Advanced users only; use loadXML() in PApplet. This is not a supported + * function and is subject to change. It is available simply for users that + * would like to handle the exceptions in a particular way. * * @nowebref */ @@ -92,7 +94,7 @@ public class XML implements Serializable { /** - * Advanced users only; see loadXML() in PApplet. + * Advanced users only; use loadXML() in PApplet. * * @nowebref */ @@ -109,19 +111,31 @@ public class XML implements Serializable { /** - * Shouldn't be part of main p5 reference, this is for advanced users. - * Note that while it doesn't accept anything but UTF-8, this is preserved - * so that we have some chance of implementing that in the future. + * Unlike the loadXML() method in PApplet, this version works with files + * that are not in UTF-8 format. * * @nowebref */ public XML(InputStream input, String options) throws IOException, ParserConfigurationException, SAXException { - this(PApplet.createReader(input), options); + //this(PApplet.createReader(input), options); // won't handle non-UTF8 + DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + + try { + // Prevent 503 errors from www.w3.org + factory.setAttribute("http://apache.org/xml/features/nonvalidating/load-external-dtd", false); + } catch (IllegalArgumentException e) { + // ignore this; Android doesn't like it + } + + factory.setExpandEntityReferences(false); + DocumentBuilder builder = factory.newDocumentBuilder(); + Document document = builder.parse(new InputSource(input)); + node = document.getDocumentElement(); } /** - * Advanced users only; see loadXML() in PApplet. + * Advanced users only; use loadXML() in PApplet. * * @nowebref */ @@ -131,11 +145,17 @@ public class XML implements Serializable { /** - * Advanced users only; see loadXML() in PApplet. + * Advanced users only; use loadXML() in PApplet. + * + * Added extra code to handle \u2028 (Unicode NLF), which is sometimes + * inserted by web browsers (Safari?) and not distinguishable from a "real" + * LF (or CRLF) in some text editors (i.e. TextEdit on OS X). Only doing + * this for XML (and not all Reader objects) because LFs are essential. + * https://github.com/processing/processing/issues/2100 * * @nowebref */ - public XML(Reader reader, String options) throws IOException, ParserConfigurationException, SAXException { + public XML(final Reader reader, String options) throws IOException, ParserConfigurationException, SAXException { DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); // Prevent 503 errors from www.w3.org @@ -164,17 +184,24 @@ public class XML implements Serializable { // builder = new SAXBuilder(); // builder.setValidation(validating); -// print(dataPath("1broke.html"), System.out); + Document document = builder.parse(new InputSource(new Reader() { + @Override + public int read(char[] cbuf, int off, int len) throws IOException { + int count = reader.read(cbuf, off, len); + for (int i = 0; i < count; i++) { + if (cbuf[off+i] == '\u2028') { + cbuf[off+i] = '\n'; + } + } + return count; + } -// Document document = builder.parse(dataPath("1_alt.html")); - Document document = builder.parse(new InputSource(reader)); + @Override + public void close() throws IOException { + reader.close(); + } + })); node = document.getDocumentElement(); -// name = node.getNodeName(); - -// NodeList nodeList = document.getDocumentElement().getChildNodes(); -// for (int i = 0; i < nodeList.getLength(); i++) { -// } -// print(createWriter("data/1_alt_reparse.html"), document.getDocumentElement(), 0); } @@ -233,6 +260,11 @@ public class XML implements Serializable { // } + public boolean save(File file) { + return save(file, null); + } + + public boolean save(File file, String options) { PrintWriter writer = PApplet.createWriter(file); boolean result = write(writer); diff --git a/core/src/processing/event/MouseEvent.java b/core/src/processing/event/MouseEvent.java index ce48658fb..bde328ff3 100644 --- a/core/src/processing/event/MouseEvent.java +++ b/core/src/processing/event/MouseEvent.java @@ -117,4 +117,33 @@ public class MouseEvent extends Event { // public void setClickCount(int clickCount) { // this.clickCount = clickCount; // } + + private String actionString() { + switch (action) { + default: + return "UNKNOWN"; + case CLICK: + return "CLICK"; + case DRAG: + return "DRAG"; + case ENTER: + return "ENTER"; + case EXIT: + return "EXIT"; + case MOVE: + return "MOVE"; + case PRESS: + return "PRESS"; + case RELEASE: + return "RELEASE"; + case WHEEL: + return "WHEEL"; + } + } + + @Override + public String toString() { + return String.format("", + actionString(), x, y, count, button); + } } diff --git a/core/src/processing/opengl/ColorVert.glsl b/core/src/processing/opengl/ColorVert.glsl index 5a03b41a0..3736ffd2e 100644 --- a/core/src/processing/opengl/ColorVert.glsl +++ b/core/src/processing/opengl/ColorVert.glsl @@ -20,15 +20,15 @@ #define PROCESSING_COLOR_SHADER -uniform mat4 transform; +uniform mat4 transformMatrix; -attribute vec4 vertex; +attribute vec4 position; attribute vec4 color; varying vec4 vertColor; void main() { - gl_Position = transform * vertex; + gl_Position = transformMatrix * position; vertColor = color; } \ No newline at end of file diff --git a/core/src/processing/opengl/FontTexture.java b/core/src/processing/opengl/FontTexture.java index 2a9fbadeb..8c1c66f5d 100644 --- a/core/src/processing/opengl/FontTexture.java +++ b/core/src/processing/opengl/FontTexture.java @@ -63,8 +63,9 @@ class FontTexture implements PConstants { protected TextureInfo[] glyphTexinfos; protected HashMap texinfoMap; + public FontTexture(PGraphicsOpenGL pg, PFont font, boolean is3D) { - pgl = PGraphicsOpenGL.pgl; + pgl = pg.pgl; this.is3D = is3D; initTexture(pg, font); @@ -130,15 +131,15 @@ class FontTexture implements PConstants { if (is3D) { // Bilinear sampling ensures that the texture doesn't look pixelated // either when it is magnified or minified... - tex = new Texture(w, h, new Texture.Parameters(ARGB, Texture.BILINEAR, - false)); + tex = new Texture(pg, w, h, + new Texture.Parameters(ARGB, Texture.BILINEAR, false)); } else { // ...however, the effect of bilinear sampling is to add some blurriness // to the text in its original size. In 2D, we assume that text will be // shown at its original size, so linear sampling is chosen instead (which // only affects minimized text). - tex = new Texture(w, h, new Texture.Parameters(ARGB, Texture.LINEAR, - false)); + tex = new Texture(pg, w, h, + new Texture.Parameters(ARGB, Texture.LINEAR, false)); } if (textures == null) { diff --git a/core/src/processing/opengl/FrameBuffer.java b/core/src/processing/opengl/FrameBuffer.java index 8821861c9..31d50f559 100644 --- a/core/src/processing/opengl/FrameBuffer.java +++ b/core/src/processing/opengl/FrameBuffer.java @@ -40,6 +40,7 @@ import java.nio.IntBuffer; */ public class FrameBuffer implements PConstants { + protected PGraphicsOpenGL pg; protected PGL pgl; protected int context; // The context that created this framebuffer. @@ -67,16 +68,17 @@ public class FrameBuffer implements PConstants { protected IntBuffer pixelBuffer; - FrameBuffer() { - pgl = PGraphicsOpenGL.pgl; + FrameBuffer(PGraphicsOpenGL pg) { + this.pg = pg; + pgl = pg.pgl; context = pgl.createEmptyContext(); } - FrameBuffer(int w, int h, int samples, int colorBuffers, - int depthBits, int stencilBits, boolean packedDepthStencil, - boolean screen) { - this(); + FrameBuffer(PGraphicsOpenGL pg, int w, int h, int samples, int colorBuffers, + int depthBits, int stencilBits, boolean packedDepthStencil, + boolean screen) { + this(pg); glFbo = 0; glDepth = 0; @@ -136,13 +138,13 @@ public class FrameBuffer implements PConstants { } - FrameBuffer(int w, int h) { - this(w, h, 1, 1, 0, 0, false, false); + FrameBuffer(PGraphicsOpenGL pg, int w, int h) { + this(pg, w, h, 1, 1, 0, 0, false, false); } - FrameBuffer(int w, int h, boolean screen) { - this(w, h, 1, 1, 0, 0, false, screen); + FrameBuffer(PGraphicsOpenGL pg, int w, int h, boolean screen) { + this(pg, w, h, 1, 1, 0, 0, false, screen); } @@ -172,36 +174,47 @@ public class FrameBuffer implements PConstants { } public void clear() { - PGraphicsOpenGL.pushFramebuffer(); - PGraphicsOpenGL.setFramebuffer(this); + pg.pushFramebuffer(); + pg.setFramebuffer(this); pgl.clearDepth(1); pgl.clearStencil(0); pgl.clearColor(0, 0, 0, 0); pgl.clear(PGL.DEPTH_BUFFER_BIT | PGL.STENCIL_BUFFER_BIT | PGL.COLOR_BUFFER_BIT); - PGraphicsOpenGL.popFramebuffer(); + pg.popFramebuffer(); } - public void copy(FrameBuffer dest, FrameBuffer current) { - pgl.bindFramebuffer(PGL.READ_FRAMEBUFFER, this.glFbo); - pgl.bindFramebuffer(PGL.DRAW_FRAMEBUFFER, dest.glFbo); + public void copyColor(FrameBuffer dest) { + copy(dest, PGL.COLOR_BUFFER_BIT); + } + + public void copyDepth(FrameBuffer dest) { + copy(dest, PGL.DEPTH_BUFFER_BIT); + } + + public void copyStencil(FrameBuffer dest) { + copy(dest, PGL.STENCIL_BUFFER_BIT); + } + + public void copy(FrameBuffer dest, int mask) { + pgl.bindFramebufferImpl(PGL.READ_FRAMEBUFFER, this.glFbo); + pgl.bindFramebufferImpl(PGL.DRAW_FRAMEBUFFER, dest.glFbo); pgl.blitFramebuffer(0, 0, this.width, this.height, - 0, 0, dest.width, dest.height, - PGL.COLOR_BUFFER_BIT, PGL.NEAREST); - pgl.bindFramebuffer(PGL.READ_FRAMEBUFFER, current.glFbo); - pgl.bindFramebuffer(PGL.DRAW_FRAMEBUFFER, current.glFbo); + 0, 0, dest.width, dest.height, mask, PGL.NEAREST); + pgl.bindFramebufferImpl(PGL.READ_FRAMEBUFFER, pg.getCurrentFB().glFbo); + pgl.bindFramebufferImpl(PGL.DRAW_FRAMEBUFFER, pg.getCurrentFB().glFbo); } public void bind() { - pgl.bindFramebuffer(PGL.FRAMEBUFFER, glFbo); + pgl.bindFramebufferImpl(PGL.FRAMEBUFFER, glFbo); } public void disableDepthTest() { noDepth = true; } - public void finish(PGraphicsOpenGL pg) { + public void finish() { if (noDepth) { // No need to clear depth buffer because depth testing was disabled. if (pg.getHint(ENABLE_DEPTH_TEST)) { @@ -271,8 +284,8 @@ public class FrameBuffer implements PConstants { colorBufferTex[i] = textures[i]; } - PGraphicsOpenGL.pushFramebuffer(); - PGraphicsOpenGL.setFramebuffer(this); + pg.pushFramebuffer(); + pg.setFramebuffer(this); // Making sure nothing is attached. for (int i = 0; i < numColorBuffers; i++) { @@ -288,7 +301,7 @@ public class FrameBuffer implements PConstants { pgl.validateFramebuffer(); - PGraphicsOpenGL.popFramebuffer(); + pg.popFramebuffer(); } @@ -300,8 +313,8 @@ public class FrameBuffer implements PConstants { colorBufferTex[i1] = tmp; } - PGraphicsOpenGL.pushFramebuffer(); - PGraphicsOpenGL.setFramebuffer(this); + pg.pushFramebuffer(); + pg.setFramebuffer(this); for (int i = 0; i < numColorBuffers; i++) { pgl.framebufferTexture2D(PGL.FRAMEBUFFER, PGL.COLOR_ATTACHMENT0 + i, colorBufferTex[i].glTarget, @@ -309,7 +322,7 @@ public class FrameBuffer implements PConstants { } pgl.validateFramebuffer(); - PGraphicsOpenGL.popFramebuffer(); + pg.popFramebuffer(); } @@ -345,7 +358,7 @@ public class FrameBuffer implements PConstants { glFbo = 0; } else { //create the FBO object... - glFbo = PGraphicsOpenGL.createFrameBufferObject(context); + glFbo = PGraphicsOpenGL.createFrameBufferObject(context, pgl); // ... and then create the rest of the stuff. if (multisample) { @@ -420,17 +433,17 @@ public class FrameBuffer implements PConstants { protected void createColorBufferMultisample() { if (screenFb) return; - PGraphicsOpenGL.pushFramebuffer(); - PGraphicsOpenGL.setFramebuffer(this); + pg.pushFramebuffer(); + pg.setFramebuffer(this); - glMultisample = PGraphicsOpenGL.createRenderBufferObject(context); + glMultisample = PGraphicsOpenGL.createRenderBufferObject(context, pgl); pgl.bindRenderbuffer(PGL.RENDERBUFFER, glMultisample); pgl.renderbufferStorageMultisample(PGL.RENDERBUFFER, nsamples, PGL.RGBA8, width, height); pgl.framebufferRenderbuffer(PGL.FRAMEBUFFER, PGL.COLOR_ATTACHMENT0, PGL.RENDERBUFFER, glMultisample); - PGraphicsOpenGL.popFramebuffer(); + pg.popFramebuffer(); } @@ -441,10 +454,10 @@ public class FrameBuffer implements PConstants { throw new RuntimeException("PFramebuffer: size undefined."); } - PGraphicsOpenGL.pushFramebuffer(); - PGraphicsOpenGL.setFramebuffer(this); + pg.pushFramebuffer(); + pg.setFramebuffer(this); - glDepthStencil = PGraphicsOpenGL.createRenderBufferObject(context); + glDepthStencil = PGraphicsOpenGL.createRenderBufferObject(context, pgl); pgl.bindRenderbuffer(PGL.RENDERBUFFER, glDepthStencil); if (multisample) { @@ -460,7 +473,7 @@ public class FrameBuffer implements PConstants { pgl.framebufferRenderbuffer(PGL.FRAMEBUFFER, PGL.STENCIL_ATTACHMENT, PGL.RENDERBUFFER, glDepthStencil); - PGraphicsOpenGL.popFramebuffer(); + pg.popFramebuffer(); } @@ -471,10 +484,10 @@ public class FrameBuffer implements PConstants { throw new RuntimeException("PFramebuffer: size undefined."); } - PGraphicsOpenGL.pushFramebuffer(); - PGraphicsOpenGL.setFramebuffer(this); + pg.pushFramebuffer(); + pg.setFramebuffer(this); - glDepth = PGraphicsOpenGL.createRenderBufferObject(context); + glDepth = PGraphicsOpenGL.createRenderBufferObject(context, pgl); pgl.bindRenderbuffer(PGL.RENDERBUFFER, glDepth); int glConst = PGL.DEPTH_COMPONENT16; @@ -496,7 +509,7 @@ public class FrameBuffer implements PConstants { pgl.framebufferRenderbuffer(PGL.FRAMEBUFFER, PGL.DEPTH_ATTACHMENT, PGL.RENDERBUFFER, glDepth); - PGraphicsOpenGL.popFramebuffer(); + pg.popFramebuffer(); } @@ -507,10 +520,10 @@ public class FrameBuffer implements PConstants { throw new RuntimeException("PFramebuffer: size undefined."); } - PGraphicsOpenGL.pushFramebuffer(); - PGraphicsOpenGL.setFramebuffer(this); + pg.pushFramebuffer(); + pg.setFramebuffer(this); - glStencil = PGraphicsOpenGL.createRenderBufferObject(context); + glStencil = PGraphicsOpenGL.createRenderBufferObject(context, pgl); pgl.bindRenderbuffer(PGL.RENDERBUFFER, glStencil); int glConst = PGL.STENCIL_INDEX1; @@ -531,7 +544,7 @@ public class FrameBuffer implements PConstants { pgl.framebufferRenderbuffer(PGL.FRAMEBUFFER, PGL.STENCIL_ATTACHMENT, PGL.RENDERBUFFER, glStencil); - PGraphicsOpenGL.popFramebuffer(); + pg.popFramebuffer(); } diff --git a/core/src/processing/opengl/LightVert.glsl b/core/src/processing/opengl/LightVert.glsl index 6e5829fe2..bfbb6a8d3 100644 --- a/core/src/processing/opengl/LightVert.glsl +++ b/core/src/processing/opengl/LightVert.glsl @@ -20,8 +20,8 @@ #define PROCESSING_LIGHT_SHADER -uniform mat4 modelview; -uniform mat4 transform; +uniform mat4 modelviewMatrix; +uniform mat4 transformMatrix; uniform mat3 normalMatrix; uniform int lightCount; @@ -33,7 +33,7 @@ uniform vec3 lightSpecular[8]; uniform vec3 lightFalloff[8]; uniform vec2 lightSpot[8]; -attribute vec4 vertex; +attribute vec4 position; attribute vec4 color; attribute vec3 normal; @@ -75,10 +75,10 @@ float blinnPhongFactor(vec3 lightDir, vec3 vertPos, vec3 vecNormal, float shine) void main() { // Vertex in clip coordinates - gl_Position = transform * vertex; + gl_Position = transformMatrix * position; // Vertex in eye coordinates - vec3 ecVertex = vec3(modelview * vertex); + vec3 ecVertex = vec3(modelviewMatrix * position); // Normal vector in eye coordinates vec3 ecNormal = normalize(normalMatrix * normal); diff --git a/core/src/processing/opengl/LineVert.glsl b/core/src/processing/opengl/LineVert.glsl index 52f334b28..d16f4d519 100644 --- a/core/src/processing/opengl/LineVert.glsl +++ b/core/src/processing/opengl/LineVert.glsl @@ -20,14 +20,14 @@ #define PROCESSING_LINE_SHADER -uniform mat4 modelview; -uniform mat4 projection; +uniform mat4 modelviewMatrix; +uniform mat4 projectionMatrix; uniform vec4 viewport; uniform int perspective; uniform vec3 scale; -attribute vec4 vertex; +attribute vec4 position; attribute vec4 color; attribute vec4 direction; @@ -45,20 +45,20 @@ vec4 windowToClipVector(vec2 window, vec4 viewport, float clip_w) { } void main() { - vec4 posp = modelview * vertex; + vec4 posp = modelviewMatrix * position; // Moving vertices slightly toward the camera // to avoid depth-fighting with the fill triangles. // Discussed here: // http://www.opengl.org/discussion_boards/ubbthreads.php?ubb=showflat&Number=252848 posp.xyz = posp.xyz * scale; - vec4 clipp = projection * posp; + vec4 clipp = projectionMatrix * posp; float thickness = direction.w; if (thickness != 0.0) { - vec4 posq = posp + modelview * vec4(direction.xyz, 0); + vec4 posq = posp + modelviewMatrix * vec4(direction.xyz, 0); posq.xyz = posq.xyz * scale; - vec4 clipq = projection * posq; + vec4 clipq = projectionMatrix * posq; vec3 window_p = clipToWindow(clipp, viewport); vec3 window_q = clipToWindow(clipq, viewport); diff --git a/core/src/processing/opengl/PGL.java b/core/src/processing/opengl/PGL.java index 942ad20f0..806ef8fc0 100644 --- a/core/src/processing/opengl/PGL.java +++ b/core/src/processing/opengl/PGL.java @@ -3,7 +3,7 @@ /* Part of the Processing project - http://processing.org - Copyright (c) 2011-12 Ben Fry and Casey Reas + Copyright (c) 2011-13 Ben Fry and Casey Reas This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public @@ -23,115 +23,83 @@ package processing.opengl; -import java.awt.BorderLayout; -import java.awt.Canvas; -import java.awt.Color; -import java.awt.Font; -import java.awt.Graphics2D; -import java.awt.Shape; -import java.awt.font.FontRenderContext; -import java.awt.font.GlyphVector; -import java.awt.geom.PathIterator; -import java.nio.Buffer; +import processing.core.PApplet; +import processing.core.PGraphics; +import java.io.IOException; +import java.net.URL; +import java.nio.Buffer; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.FloatBuffer; import java.nio.IntBuffer; import java.nio.ShortBuffer; import java.util.Arrays; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; - -import javax.media.opengl.GL; -import javax.media.opengl.GL2; -import javax.media.opengl.GL2ES2; -import javax.media.opengl.GLAutoDrawable; -import javax.media.opengl.GLCapabilities; -import javax.media.opengl.GLCapabilitiesImmutable; -import javax.media.opengl.GLContext; -import javax.media.opengl.GLDrawable; -import javax.media.opengl.GLEventListener; -import javax.media.opengl.GLException; -import javax.media.opengl.GLFBODrawable; -import javax.media.opengl.GLProfile; -import javax.media.opengl.awt.GLCanvas; -import javax.media.opengl.glu.GLU; -import javax.media.opengl.glu.GLUtessellator; -import javax.media.opengl.glu.GLUtessellatorCallbackAdapter; - -import processing.core.PApplet; -import processing.core.PConstants; -import processing.event.KeyEvent; -import processing.event.MouseEvent; - -import com.jogamp.newt.awt.NewtCanvasAWT; -import com.jogamp.newt.event.InputEvent; -import com.jogamp.newt.opengl.GLWindow; -import com.jogamp.opengl.FBObject; /** - * Processing-OpenGL abstraction layer. + * Processing-OpenGL abstraction layer. Needs to be implemented by subclasses + * using specific OpenGL-Java bindings. + * + * It includes a full GLES 2.0 interface. * - * Warnings are suppressed for static access because presumably on Android, - * the GL2 vs GL distinctions are necessary, whereas on desktop they are not. */ -@SuppressWarnings("static-access") -public class PGL { - /////////////////////////////////////////////////////////// +public abstract class PGL { + // ........................................................ - // Public members to access the underlying GL objects and context + // Basic fields - /** Basic GL functionality, common to all profiles */ - public static GL gl; + /** The PGraphics object using this interface */ + protected PGraphicsOpenGL pg; - /** GLU interface **/ - public static GLU glu; + /** OpenGL thread */ + protected Thread glThread; - /** The rendering context (holds rendering state info) */ - public static GLContext context; + /** ID of the GL context associated to the surface **/ + protected int glContext; - /** The canvas where OpenGL rendering takes place */ - public static Canvas canvas; + /** true if this is the GL interface for a primary surface PGraphics */ + public boolean primaryPGL; - /** Selected GL profile */ - public static GLProfile profile; - - /////////////////////////////////////////////////////////// + // ........................................................ // Parameters + protected static boolean USE_FBOLAYER_BY_DEFAULT = false; + protected static int REQUESTED_DEPTH_BITS = 24; + protected static int REQUESTED_STENCIL_BITS = 8; + protected static int REQUESTED_ALPHA_BITS = 8; + /** Switches between the use of regular and direct buffers. */ - protected static final boolean USE_DIRECT_BUFFERS = true; - protected static final int MIN_DIRECT_BUFFER_SIZE = 1; + protected static boolean USE_DIRECT_BUFFERS = true; + protected static int MIN_DIRECT_BUFFER_SIZE = 1; /** This flag enables/disables a hack to make sure that anything drawn * in setup will be maintained even a renderer restart (e.g.: smooth change). * See the code and comments involving this constant in * PGraphicsOpenGL.endDraw(). */ - protected static final boolean SAVE_SURFACE_TO_PIXELS_HACK = true; + protected static boolean SAVE_SURFACE_TO_PIXELS_HACK = true; /** Enables/disables mipmap use. */ - protected static final boolean MIPMAPS_ENABLED = true; + protected static boolean MIPMAPS_ENABLED = true; /** Initial sizes for arrays of input and tessellated data. */ - protected static final int DEFAULT_IN_VERTICES = 64; - protected static final int DEFAULT_IN_EDGES = 128; - protected static final int DEFAULT_IN_TEXTURES = 64; - protected static final int DEFAULT_TESS_VERTICES = 64; - protected static final int DEFAULT_TESS_INDICES = 128; + protected static int DEFAULT_IN_VERTICES = 64; + protected static int DEFAULT_IN_EDGES = 128; + protected static int DEFAULT_IN_TEXTURES = 64; + protected static int DEFAULT_TESS_VERTICES = 64; + protected static int DEFAULT_TESS_INDICES = 128; /** Maximum lights by default is 8, the minimum defined by OpenGL. */ - protected static final int MAX_LIGHTS = 8; + protected static int MAX_LIGHTS = 8; /** Maximum index value of a tessellated vertex. GLES restricts the vertex * indices to be of type unsigned short. Since Java only supports signed * shorts as primitive type we have 2^15 = 32768 as the maximum number of * vertices that can be referred to within a single VBO. */ - protected static final int MAX_VERTEX_INDEX = 32767; - protected static final int MAX_VERTEX_INDEX1 = MAX_VERTEX_INDEX + 1; + protected static int MAX_VERTEX_INDEX = 32767; + protected static int MAX_VERTEX_INDEX1 = MAX_VERTEX_INDEX + 1; /** Count of tessellated fill, line or point vertices that will * trigger a flush in the immediate mode. It doesn't necessarily @@ -139,98 +107,191 @@ public class PGL { * be effectively much large since the renderer uses offsets to * refer to vertices beyond the MAX_VERTEX_INDEX limit. */ - protected static final int FLUSH_VERTEX_COUNT = MAX_VERTEX_INDEX1; + protected static int FLUSH_VERTEX_COUNT = MAX_VERTEX_INDEX1; /** Minimum/maximum dimensions of a texture used to hold font data. */ - protected static final int MIN_FONT_TEX_SIZE = 256; - protected static final int MAX_FONT_TEX_SIZE = 1024; + protected static int MIN_FONT_TEX_SIZE = 256; + protected static int MAX_FONT_TEX_SIZE = 1024; /** Minimum stroke weight needed to apply the full path stroking * algorithm that properly generates caps and joins. */ - protected static final float MIN_CAPS_JOINS_WEIGHT = 2f; + protected static float MIN_CAPS_JOINS_WEIGHT = 2f; /** Maximum length of linear paths to be stroked with the * full algorithm that generates accurate caps and joins. */ - protected static final int MAX_CAPS_JOINS_LENGTH = 5000; + protected static int MAX_CAPS_JOINS_LENGTH = 5000; /** Minimum array size to use arrayCopy method(). */ - protected static final int MIN_ARRAYCOPY_SIZE = 2; + protected static int MIN_ARRAYCOPY_SIZE = 2; /** Factor used to displace the stroke vertices towards the camera in * order to make sure the lines are always on top of the fill geometry */ - protected static final float STROKE_DISPLACEMENT = 0.999f; + protected static float STROKE_DISPLACEMENT = 0.999f; - /** Time that the Processing's animation thread will wait for JOGL's rendering - * thread to be done with a single frame. - */ - protected static final int DRAW_TIMEOUT_MILLIS = 500; + // ........................................................ - /** JOGL's windowing toolkit */ - // The two windowing toolkits available to use in JOGL: - protected static final int AWT = 0; // http://jogamp.org/wiki/index.php/Using_JOGL_in_AWT_SWT_and_Swing - protected static final int NEWT = 1; // http://jogamp.org/jogl/doc/NEWT-Overview.html + // FBO layer - /** OS-specific configuration */ - protected static int WINDOW_TOOLKIT; - protected static int EVENTS_TOOLKIT; - protected static boolean USE_FBOLAYER_BY_DEFAULT; - protected static boolean USE_JOGL_FBOLAYER; - protected static int REQUESTED_DEPTH_BITS = 24; - protected static int REQUESTED_STENCIL_BITS = 8; - protected static int REQUESTED_ALPHA_BITS = 8; - static { - if (PApplet.platform == PConstants.WINDOWS) { - // Using AWT on Windows because NEWT displays a black background while - // initializing, and the cursor functions don't work. GLWindow has some - // functions for basic cursor handling (hide/show): - // GLWindow.setPointerVisible(false); - // but apparently nothing to set the cursor icon: - // https://jogamp.org/bugzilla/show_bug.cgi?id=409 - WINDOW_TOOLKIT = AWT; - EVENTS_TOOLKIT = AWT; - USE_FBOLAYER_BY_DEFAULT = false; - USE_JOGL_FBOLAYER = false; - REQUESTED_DEPTH_BITS = 24; - REQUESTED_STENCIL_BITS = 8; - REQUESTED_ALPHA_BITS = 8; - } else if (PApplet.platform == PConstants.MACOSX) { - // Note: with the JOGL jars included in the 2.0 release (jogl-2.0-b993, - // gluegen-1.0-b671), the JOGL FBO layer seems incompatible with NEWT. - WINDOW_TOOLKIT = AWT; - EVENTS_TOOLKIT = AWT; - USE_FBOLAYER_BY_DEFAULT = true; - USE_JOGL_FBOLAYER = true; - REQUESTED_DEPTH_BITS = 24; - REQUESTED_STENCIL_BITS = 8; - REQUESTED_ALPHA_BITS = 8; - } else if (PApplet.platform == PConstants.LINUX) { - WINDOW_TOOLKIT = AWT; - EVENTS_TOOLKIT = AWT; - USE_FBOLAYER_BY_DEFAULT = false; - USE_JOGL_FBOLAYER = false; - REQUESTED_DEPTH_BITS = 24; - REQUESTED_STENCIL_BITS = 8; - REQUESTED_ALPHA_BITS = 8; - } else if (PApplet.platform == PConstants.OTHER) { - WINDOW_TOOLKIT = NEWT; // NEWT works on the Raspberry pi? - EVENTS_TOOLKIT = NEWT; - USE_FBOLAYER_BY_DEFAULT = false; - USE_JOGL_FBOLAYER = false; - REQUESTED_DEPTH_BITS = 24; - REQUESTED_STENCIL_BITS = 8; - REQUESTED_ALPHA_BITS = 8; - } - } + protected boolean fboLayerRequested = false; + protected boolean fboLayerCreated = false; + protected boolean fboLayerInUse = false; + protected boolean firstFrame = true; + protected int reqNumSamples; + protected int numSamples; + protected IntBuffer glColorFbo; + protected IntBuffer glMultiFbo; + protected IntBuffer glColorBuf; + protected IntBuffer glColorTex; + protected IntBuffer glDepthStencil; + protected IntBuffer glDepth; + protected IntBuffer glStencil; + protected int fboWidth, fboHeight; + protected int backTex, frontTex; + + /** Flags used to handle the creation of a separate front texture */ + protected boolean usingFrontTex = false; + protected boolean needSepFrontTex = false; + + // ........................................................ + + // Texture rendering + + protected boolean loadedTex2DShader = false; + protected int tex2DShaderProgram; + protected int tex2DVertShader; + protected int tex2DFragShader; + protected int tex2DShaderContext; + protected int tex2DVertLoc; + protected int tex2DTCoordLoc; + protected int tex2DSamplerLoc; + protected int tex2DGeoVBO; + + protected boolean loadedTexRectShader = false; + protected int texRectShaderProgram; + protected int texRectVertShader; + protected int texRectFragShader; + protected int texRectShaderContext; + protected int texRectVertLoc; + protected int texRectTCoordLoc; + protected int texRectSamplerLoc; + protected int texRectGeoVBO; + + protected float[] texCoords = { + // X, Y, U, V + -1.0f, -1.0f, 0.0f, 0.0f, + +1.0f, -1.0f, 1.0f, 0.0f, + -1.0f, +1.0f, 0.0f, 1.0f, + +1.0f, +1.0f, 1.0f, 1.0f + }; + protected FloatBuffer texData; + + protected static final String SHADER_PREPROCESSOR_DIRECTIVE = + "#ifdef GL_ES\n" + + "precision mediump float;\n" + + "precision mediump int;\n" + + "#endif\n"; + + protected static String[] texVertShaderSource = { + "attribute vec2 position;", + "attribute vec2 texCoord;", + "varying vec2 vertTexCoord;", + "void main() {", + " gl_Position = vec4(position, 0, 1);", + " vertTexCoord = texCoord;", + "}" + }; + + protected static String[] tex2DFragShaderSource = { + SHADER_PREPROCESSOR_DIRECTIVE, + "uniform sampler2D texMap;", + "varying vec2 vertTexCoord;", + "void main() {", + " gl_FragColor = texture2D(texMap, vertTexCoord.st);", + "}" + }; + + protected static String[] texRectFragShaderSource = { + SHADER_PREPROCESSOR_DIRECTIVE, + "uniform sampler2DRect texMap;", + "varying vec2 vertTexCoord;", + "void main() {", + " gl_FragColor = texture2DRect(texMap, vertTexCoord.st);", + "}" + }; + + /** Which texturing targets are enabled */ + protected boolean[] texturingTargets = { false, false }; + + /** Used to keep track of which textures are bound to each target */ + protected int maxTexUnits; + protected int activeTexUnit = 0; + protected int[][] boundTextures; + + // ........................................................ + + // Framerate handling + + protected float targetFps = 60; + protected float currentFps = 60; + protected boolean setFps = false; + + // ........................................................ + + // Utility buffers + + protected ByteBuffer byteBuffer; + protected IntBuffer intBuffer; + protected IntBuffer viewBuffer; + + protected IntBuffer colorBuffer; + protected FloatBuffer depthBuffer; + protected ByteBuffer stencilBuffer; + + // ........................................................ + + // Error messages + + protected static final String WIKI = + " Read http://wiki.processing.org/w/OpenGL_Issues for help."; + + protected static final String FRAMEBUFFER_ERROR = + "Framebuffer error (%1$s), rendering will probably not work as expected" + WIKI; + + protected static final String MISSING_FBO_ERROR = + "Framebuffer objects are not supported by this hardware (or driver)" + WIKI; + + protected static final String MISSING_GLSL_ERROR = + "GLSL shaders are not supported by this hardware (or driver)" + WIKI; + + protected static final String MISSING_GLFUNC_ERROR = + "GL function %1$s is not available on this hardware (or driver)" + WIKI; + + protected static final String UNSUPPORTED_GLPROF_ERROR = + "Unsupported OpenGL profile."; + + protected static final String TEXUNIT_ERROR = + "Number of texture units not supported by this hardware (or driver)" + WIKI; + + protected static final String NONPRIMARY_ERROR = + "The renderer is trying to call a PGL function that can only be called on a primary PGL. " + + "This is most likely due to a bug in the renderer's code, please report it with an " + + "issue on Processing's github page https://github.com/processing/processing/issues?state=open " + + "if using any of the built-in OpenGL renderers. If you are using a contributed " + + "library, contact the library's developers."; + + // ........................................................ + + // Constants /** Size of different types in bytes */ - protected static final int SIZEOF_SHORT = Short.SIZE / 8; - protected static final int SIZEOF_INT = Integer.SIZE / 8; - protected static final int SIZEOF_FLOAT = Float.SIZE / 8; - protected static final int SIZEOF_BYTE = Byte.SIZE / 8; - protected static final int SIZEOF_INDEX = SIZEOF_SHORT; - protected static final int INDEX_TYPE = GL.GL_UNSIGNED_SHORT; + protected static int SIZEOF_SHORT = Short.SIZE / 8; + protected static int SIZEOF_INT = Integer.SIZE / 8; + protected static int SIZEOF_FLOAT = Float.SIZE / 8; + protected static int SIZEOF_BYTE = Byte.SIZE / 8; + protected static int SIZEOF_INDEX = SIZEOF_SHORT; + protected static int INDEX_TYPE = 0x1403; // GL_UNSIGNED_SHORT /** Machine Epsilon for float precision. */ protected static float FLOAT_EPS = Float.MIN_VALUE; @@ -253,206 +314,17 @@ public class PGL { protected static boolean BIG_ENDIAN = ByteOrder.nativeOrder() == ByteOrder.BIG_ENDIAN; - protected static final String SHADER_PREPROCESSOR_DIRECTIVE = - "#ifdef GL_ES\n" + - "precision mediump float;\n" + - "precision mediump int;\n" + - "#endif\n"; - /** OpenGL thread */ - protected static Thread glThread; - - /** The PGraphics object using this interface */ - protected PGraphicsOpenGL pg; - - /** The capabilities of the OpenGL rendering surface */ - protected static GLCapabilitiesImmutable capabilities; - - /** The rendering surface */ - protected static GLDrawable drawable; - - /** GLES2 functionality (shaders, etc) */ - protected static GL2ES2 gl2; - - /** GL2 desktop functionality (blit framebuffer, map buffer range, - * multisampled renerbuffers) */ - protected static GL2 gl2x; - - /** The AWT-OpenGL canvas */ - protected static GLCanvas canvasAWT; - - /** The NEWT-OpenGL canvas */ - protected static NewtCanvasAWT canvasNEWT; - - /** The NEWT window */ - protected static GLWindow window; - - /** The listener that fires the frame rendering in Processing */ - protected static PGLListener listener; - - /** This countdown latch is used to maintain the synchronization between - * Processing's drawing thread and JOGL's rendering thread */ - protected CountDownLatch drawLatch; - - /** Desired target framerate */ - protected float targetFps = 60; - protected float currentFps = 60; - protected boolean setFps = false; - protected int fcount, lastm; - protected int fint = 3; - - /** Which texturing targets are enabled */ - protected static boolean[] texturingTargets = { false, false }; - - /** Used to keep track of which textures are bound to each target */ - protected static int maxTexUnits; - protected static int activeTexUnit = 0; - protected static int[][] boundTextures; - - /////////////////////////////////////////////////////////// - - // FBO layer - - protected static boolean fboLayerRequested = false; - protected static boolean fboLayerCreated = false; - protected static boolean fboLayerInUse = false; - protected static boolean firstFrame = true; - protected static int reqNumSamples; - protected static int numSamples; - protected static IntBuffer glColorFbo; - protected static IntBuffer glMultiFbo; - protected static IntBuffer glColorBuf; - protected static IntBuffer glColorTex; - protected static IntBuffer glDepthStencil; - protected static IntBuffer glDepth; - protected static IntBuffer glStencil; - protected static int fboWidth, fboHeight; - protected static int backTex, frontTex; - - /** Back (== draw, current frame) buffer */ - protected static FBObject backFBO; - /** Sink buffer, used in the multisampled case */ - protected static FBObject sinkFBO; - /** Front (== read, previous frame) buffer */ - protected static FBObject frontFBO; - protected static FBObject.TextureAttachment backTexAttach; - protected static FBObject.TextureAttachment frontTexAttach; - - /** Flags used to handle the creation of a separte front texture */ - protected boolean usingFrontTex = false; - protected boolean needSepFrontTex = false; - - /** Flag used to do request final display() call to make sure that the - * buffers are properly swapped. - */ - protected boolean prevCanDraw = false; - - /////////////////////////////////////////////////////////// - - // Texture rendering - - protected static boolean loadedTex2DShader = false; - protected static int tex2DShaderProgram; - protected static int tex2DVertShader; - protected static int tex2DFragShader; - protected static GLContext tex2DShaderContext; - protected static int tex2DVertLoc; - protected static int tex2DTCoordLoc; - - protected static boolean loadedTexRectShader = false; - protected static int texRectShaderProgram; - protected static int texRectVertShader; - protected static int texRectFragShader; - protected static GLContext texRectShaderContext; - protected static int texRectVertLoc; - protected static int texRectTCoordLoc; - - protected static float[] texCoords = { - // X, Y, U, V - -1.0f, -1.0f, 0.0f, 0.0f, - +1.0f, -1.0f, 1.0f, 0.0f, - -1.0f, +1.0f, 0.0f, 1.0f, - +1.0f, +1.0f, 1.0f, 1.0f - }; - protected static FloatBuffer texData; - - protected static String texVertShaderSource = - "attribute vec2 inVertex;" + - "attribute vec2 inTexcoord;" + - "varying vec2 vertTexcoord;" + - "void main() {" + - " gl_Position = vec4(inVertex, 0, 1);" + - " vertTexcoord = inTexcoord;" + - "}"; - - protected static String tex2DFragShaderSource = - SHADER_PREPROCESSOR_DIRECTIVE + - "uniform sampler2D textureSampler;" + - "varying vec2 vertTexcoord;" + - "void main() {" + - " gl_FragColor = texture2D(textureSampler, vertTexcoord.st);" + - "}"; - - protected static String texRectFragShaderSource = - SHADER_PREPROCESSOR_DIRECTIVE + - "uniform sampler2DRect textureSampler;" + - "varying vec2 vertTexcoord;" + - "void main() {" + - " gl_FragColor = texture2DRect(textureSampler, vertTexcoord.st);" + - "}"; - - /////////////////////////////////////////////////////////// - - // Utilities - - protected ByteBuffer byteBuffer; - protected IntBuffer intBuffer; - protected IntBuffer viewBuffer; - - protected IntBuffer colorBuffer; - protected FloatBuffer depthBuffer; - protected ByteBuffer stencilBuffer; - - protected float[] projMatrix; - protected float[] mvMatrix; - - /////////////////////////////////////////////////////////// - - // Error messages - - protected static final String WIKI = - " Read http://wiki.processing.org/w/OpenGL_Issues for help."; - - protected static final String FRAMEBUFFER_ERROR = - "Framebuffer error (%1$s), rendering will probably not work as expected" + WIKI; - - protected static final String MISSING_FBO_ERROR = - "Framebuffer objects are not supported by this hardware (or driver)" + WIKI; - - protected static final String MISSING_GLSL_ERROR = - "GLSL shaders are not supported by this hardware (or driver)" + WIKI; - - protected static final String MISSING_GLFUNC_ERROR = - "GL function %1$s is not available on this hardware (or driver)" + WIKI; - - protected static final String TEXUNIT_ERROR = - "Number of texture units not supported by this hardware (or driver)" + WIKI; - - - /////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////// // Initialization, finalization - public PGL() { - } + public PGL() { } public PGL(PGraphicsOpenGL pg) { this.pg = pg; - if (glu == null) { - glu = new GLU(); - } if (glColorTex == null) { glColorTex = allocateIntBuffer(2); glColorFbo = allocateIntBuffer(1); @@ -473,150 +345,28 @@ public class PGL { } - protected void setFps(float fps) { - if (!setFps || targetFps != fps) { - if (60 < fps) { - // Disables v-sync - gl.setSwapInterval(0); - } else if (30 < fps) { - gl.setSwapInterval(1); - } else { - gl.setSwapInterval(2); - } - targetFps = currentFps = fps; - setFps = true; - } + public void setPrimary(boolean primary) { + primaryPGL = primary; } - protected void initSurface(int antialias) { - if (profile == null) { - profile = GLProfile.getDefault(); - } - GLCapabilities reqCaps = new GLCapabilities(profile); - reqCaps.setDepthBits(REQUESTED_DEPTH_BITS); - reqCaps.setStencilBits(REQUESTED_STENCIL_BITS); - reqCaps.setAlphaBits(REQUESTED_ALPHA_BITS); - initSurface(antialias, reqCaps, null); - } + /** + * Return the native canvas the OpenGL context associated to this PGL object + * is rendering to (if any). + */ + public abstract Object getCanvas(); - protected void initSurface(int antialias, GLCapabilities reqCaps, - GLContext sharedCtx) { - if (profile == null) { - profile = GLProfile.getDefault(); - } else { - // Restarting... - if (canvasAWT != null) { - canvasAWT.removeGLEventListener(listener); - pg.parent.removeListeners(canvasAWT); - pg.parent.remove(canvasAWT); - } else if (canvasNEWT != null) { - window.removeGLEventListener(listener); - pg.parent.remove(canvasNEWT); - } - sinkFBO = backFBO = frontFBO = null; - } + protected abstract void setFps(float fps); - // Setting up the desired capabilities; - GLCapabilities caps = reqCaps == null ? new GLCapabilities(profile) : - reqCaps; - caps.setBackgroundOpaque(true); - caps.setOnscreen(true); - if (USE_FBOLAYER_BY_DEFAULT) { - if (USE_JOGL_FBOLAYER) { - caps.setPBuffer(false); - caps.setFBO(true); - if (1 < antialias) { - caps.setSampleBuffers(true); - caps.setNumSamples(antialias); - } else { - caps.setSampleBuffers(false); - } - fboLayerRequested = false; - } else { - caps.setPBuffer(false); - caps.setFBO(false); - caps.setSampleBuffers(false); - fboLayerRequested = 1 < antialias; - } - } else { - if (1 < antialias) { - caps.setSampleBuffers(true); - caps.setNumSamples(antialias); - } else { - caps.setSampleBuffers(false); - } - fboLayerRequested = false; - } - caps.setDepthBits(REQUESTED_DEPTH_BITS); - caps.setStencilBits(REQUESTED_STENCIL_BITS); - caps.setAlphaBits(REQUESTED_ALPHA_BITS); - reqNumSamples = qualityToSamples(antialias); - if (WINDOW_TOOLKIT == AWT) { - if (sharedCtx == null) { - canvasAWT = new GLCanvas(caps); - } else { - canvasAWT = new GLCanvas(caps, sharedCtx); - } - canvasAWT.setBounds(0, 0, pg.width, pg.height); - canvasAWT.setBackground(new Color(pg.backgroundColor, true)); - canvasAWT.setFocusable(true); + protected abstract void initSurface(int antialias); - pg.parent.setLayout(new BorderLayout()); - pg.parent.add(canvasAWT, BorderLayout.CENTER); - canvasAWT.requestFocusInWindow(); - pg.parent.removeListeners(pg.parent); - pg.parent.addListeners(canvasAWT); + protected abstract void reinitSurface(); - canvas = canvasAWT; - canvasNEWT = null; - listener = new PGLListener(); - canvasAWT.addGLEventListener(listener); - } else if (WINDOW_TOOLKIT == NEWT) { - window = GLWindow.create(caps); - if (sharedCtx != null) { - window.setSharedContext(sharedCtx); - } - canvasNEWT = new NewtCanvasAWT(window); - canvasNEWT.setBounds(0, 0, pg.width, pg.height); - canvasNEWT.setBackground(new Color(pg.backgroundColor, true)); - canvasNEWT.setFocusable(true); - - pg.parent.setLayout(new BorderLayout()); - pg.parent.add(canvasNEWT, BorderLayout.CENTER); - canvasNEWT.requestFocusInWindow(); - - if (EVENTS_TOOLKIT == NEWT) { - NEWTMouseListener mouseListener = new NEWTMouseListener(); - window.addMouseListener(mouseListener); - NEWTKeyListener keyListener = new NEWTKeyListener(); - window.addKeyListener(keyListener); - NEWTWindowListener winListener = new NEWTWindowListener(); - window.addWindowListener(winListener); - } else if (EVENTS_TOOLKIT == AWT) { - pg.parent.removeListeners(canvasNEWT); - pg.parent.addListeners(canvasNEWT); - } - - canvas = canvasNEWT; - canvasAWT = null; - - listener = new PGLListener(); - window.addGLEventListener(listener); - } - - canvas.setFocusTraversalKeysEnabled(false); - - fboLayerCreated = false; - fboLayerInUse = false; - firstFrame = true; - - setFps = false; - } + protected abstract void registerListeners(); protected void deleteSurface() { @@ -630,75 +380,36 @@ public class PGL { deleteRenderbuffers(1, glStencil); } - if (canvasAWT != null) { - canvasAWT.removeGLEventListener(listener); - pg.parent.removeListeners(canvasAWT); - } else if (canvasNEWT != null) { - window.removeGLEventListener(listener); - } - fboLayerCreated = false; fboLayerInUse = false; firstFrame = false; - - GLProfile.shutdown(); } - protected int getReadFramebuffer() { - if (fboLayerInUse) { - return glColorFbo.get(0); - } else if (capabilities.isFBO()) { - return context.getDefaultReadFramebuffer(); - } else { - return 0; - } + protected int getReadFramebuffer() { + return fboLayerInUse ? glColorFbo.get(0) : 0; } - protected int getDrawFramebuffer() { - if (fboLayerInUse) { - if (1 < numSamples) { - return glMultiFbo.get(0); - } else { - return glColorFbo.get(0); - } - } else if (capabilities.isFBO()) { - return context.getDefaultDrawFramebuffer(); - } else { - return 0; - } + protected int getDrawFramebuffer() { + if (fboLayerInUse) return 1 < numSamples ? glMultiFbo.get(0) : + glColorFbo.get(0); + else return 0; } - protected int getDefaultDrawBuffer() { - if (fboLayerInUse) { - return COLOR_ATTACHMENT0; - } else if (capabilities.isFBO()) { - return GL.GL_COLOR_ATTACHMENT0; - } else if (capabilities.getDoubleBuffered()) { - return GL.GL_BACK; - } else { - return GL.GL_FRONT; - } + protected int getDefaultDrawBuffer() { + return fboLayerInUse ? COLOR_ATTACHMENT0 : FRONT; } - protected int getDefaultReadBuffer() { - if (fboLayerInUse) { - return COLOR_ATTACHMENT0; - } else if (capabilities.isFBO()) { - return GL.GL_COLOR_ATTACHMENT0; - } else if (capabilities.getDoubleBuffered()) { - return GL.GL_BACK; - } else { - return GL.GL_FRONT; - } + protected int getDefaultReadBuffer() { + return fboLayerInUse ? COLOR_ATTACHMENT0 : FRONT; } - protected boolean isFBOBacked() { - return fboLayerInUse || capabilities.isFBO(); + protected boolean isFBOBacked() {; + return fboLayerInUse; } @@ -712,25 +423,17 @@ public class PGL { } - protected int getDepthBits() { - if (USE_JOGL_FBOLAYER) { - return capabilities.getDepthBits(); - } else { - intBuffer.rewind(); - getIntegerv(DEPTH_BITS, intBuffer); - return intBuffer.get(0); - } + protected int getDepthBits() { + intBuffer.rewind(); + getIntegerv(DEPTH_BITS, intBuffer); + return intBuffer.get(0); } - protected int getStencilBits() { - if (USE_JOGL_FBOLAYER) { - return capabilities.getStencilBits(); - } else { - intBuffer.rewind(); - getIntegerv(STENCIL_BITS, intBuffer); - return intBuffer.get(0); - } + protected int getStencilBits() { + intBuffer.rewind(); + getIntegerv(STENCIL_BITS, intBuffer); + return intBuffer.get(0); } @@ -749,64 +452,33 @@ public class PGL { protected Texture wrapBackTexture(Texture texture) { - if (texture == null || changedBackTex) { - if (USE_JOGL_FBOLAYER) { - texture = new Texture(); - texture.init(pg.width, pg.height, - backTexAttach.getName(), TEXTURE_2D, RGBA, - backTexAttach.getWidth(), backTexAttach.getHeight(), - backTexAttach.minFilter, backTexAttach.magFilter, - backTexAttach.wrapS, backTexAttach.wrapT); - texture.invertedY(true); - texture.colorBuffer(true); - pg.setCache(pg, texture); - } else { - texture = new Texture(); - texture.init(pg.width, pg.height, - glColorTex.get(backTex), TEXTURE_2D, RGBA, - fboWidth, fboHeight, NEAREST, NEAREST, - CLAMP_TO_EDGE, CLAMP_TO_EDGE); - texture.invertedY(true); - texture.colorBuffer(true); - pg.setCache(pg, texture); - } + if (texture == null) { + texture = new Texture(pg); + texture.init(pg.width, pg.height, + glColorTex.get(backTex), TEXTURE_2D, RGBA, + fboWidth, fboHeight, NEAREST, NEAREST, + CLAMP_TO_EDGE, CLAMP_TO_EDGE); + texture.invertedY(true); + texture.colorBuffer(true); + pg.setCache(pg, texture); } else { - if (USE_JOGL_FBOLAYER) { - texture.glName = backTexAttach.getName(); - } else { - texture.glName = glColorTex.get(backTex); - } + texture.glName = glColorTex.get(backTex); } return texture; } - protected Texture wrapFrontTexture(Texture texture) { - if (texture == null || changedFrontTex) { - if (USE_JOGL_FBOLAYER) { - texture = new Texture(); - texture.init(pg.width, pg.height, - backTexAttach.getName(), TEXTURE_2D, RGBA, - frontTexAttach.getWidth(), frontTexAttach.getHeight(), - frontTexAttach.minFilter, frontTexAttach.magFilter, - frontTexAttach.wrapS, frontTexAttach.wrapT); - texture.invertedY(true); - texture.colorBuffer(true); - } else { - texture = new Texture(); - texture.init(pg.width, pg.height, - glColorTex.get(frontTex), TEXTURE_2D, RGBA, - fboWidth, fboHeight, NEAREST, NEAREST, - CLAMP_TO_EDGE, CLAMP_TO_EDGE); - texture.invertedY(true); - texture.colorBuffer(true); - } + protected Texture wrapFrontTexture(Texture texture) { + if (texture == null) { + texture = new Texture(pg); + texture.init(pg.width, pg.height, + glColorTex.get(frontTex), TEXTURE_2D, RGBA, + fboWidth, fboHeight, NEAREST, NEAREST, + CLAMP_TO_EDGE, CLAMP_TO_EDGE); + texture.invertedY(true); + texture.colorBuffer(true); } else { - if (USE_JOGL_FBOLAYER) { - texture.glName = frontTexAttach.getName(); - } else { - texture.glName = glColorTex.get(frontTex); - } + texture.glName = glColorTex.get(frontTex); } return texture; } @@ -814,44 +486,23 @@ public class PGL { protected void bindFrontTexture() { usingFrontTex = true; - if (USE_JOGL_FBOLAYER) { - if (!texturingIsEnabled(TEXTURE_2D)) { - enableTexturing(TEXTURE_2D); - } - bindTexture(TEXTURE_2D, frontTexAttach.getName()); - } else { - if (!texturingIsEnabled(TEXTURE_2D)) { - enableTexturing(TEXTURE_2D); - } - bindTexture(TEXTURE_2D, glColorTex.get(frontTex)); + if (!texturingIsEnabled(TEXTURE_2D)) { + enableTexturing(TEXTURE_2D); } + bindTexture(TEXTURE_2D, glColorTex.get(frontTex)); } protected void unbindFrontTexture() { - if (USE_JOGL_FBOLAYER) { - if (textureIsBound(TEXTURE_2D, frontTexAttach.getName())) { - // We don't want to unbind another texture - // that might be bound instead of this one. - if (!texturingIsEnabled(TEXTURE_2D)) { - enableTexturing(TEXTURE_2D); - bindTexture(TEXTURE_2D, 0); - disableTexturing(TEXTURE_2D); - } else { - bindTexture(TEXTURE_2D, 0); - } - } - } else { - if (textureIsBound(TEXTURE_2D, glColorTex.get(frontTex))) { - // We don't want to unbind another texture - // that might be bound instead of this one. - if (!texturingIsEnabled(TEXTURE_2D)) { - enableTexturing(TEXTURE_2D); - bindTexture(TEXTURE_2D, 0); - disableTexturing(TEXTURE_2D); - } else { - bindTexture(TEXTURE_2D, 0); - } + if (textureIsBound(TEXTURE_2D, glColorTex.get(frontTex))) { + // We don't want to unbind another texture + // that might be bound instead of this one. + if (!texturingIsEnabled(TEXTURE_2D)) { + enableTexturing(TEXTURE_2D); + bindTexture(TEXTURE_2D, 0); + disableTexturing(TEXTURE_2D); + } else { + bindTexture(TEXTURE_2D, 0); } } } @@ -859,35 +510,130 @@ public class PGL { protected void syncBackTexture() { if (usingFrontTex) needSepFrontTex = true; - if (USE_JOGL_FBOLAYER) { - if (1 < numSamples) { - backFBO.syncSamplingSink(gl); - backFBO.bind(gl); - } - } else { - if (1 < numSamples) { - bindFramebuffer(READ_FRAMEBUFFER, glMultiFbo.get(0)); - bindFramebuffer(DRAW_FRAMEBUFFER, glColorFbo.get(0)); - blitFramebuffer(0, 0, fboWidth, fboHeight, - 0, 0, fboWidth, fboHeight, - COLOR_BUFFER_BIT, NEAREST); - } + if (1 < numSamples) { + bindFramebufferImpl(READ_FRAMEBUFFER, glMultiFbo.get(0)); + bindFramebufferImpl(DRAW_FRAMEBUFFER, glColorFbo.get(0)); + blitFramebuffer(0, 0, fboWidth, fboHeight, + 0, 0, fboWidth, fboHeight, + COLOR_BUFFER_BIT, NEAREST); } } - protected int qualityToSamples(int quality) { - if (quality <= 1) { - return 1; + /////////////////////////////////////////////////////////// + + // Frame rendering + + + protected void beginDraw(boolean clear0) { + if (needFBOLayer(clear0)) { + if (!fboLayerCreated) createFBOLayer(); + + bindFramebufferImpl(FRAMEBUFFER, glColorFbo.get(0)); + framebufferTexture2D(FRAMEBUFFER, COLOR_ATTACHMENT0, + TEXTURE_2D, glColorTex.get(backTex), 0); + + if (1 < numSamples) { + bindFramebufferImpl(FRAMEBUFFER, glMultiFbo.get(0)); + } + + if (firstFrame) { + // No need to draw back color buffer because we are in the first frame. + int argb = pg.backgroundColor; + float a = ((argb >> 24) & 0xff) / 255.0f; + float r = ((argb >> 16) & 0xff) / 255.0f; + float g = ((argb >> 8) & 0xff) / 255.0f; + float b = ((argb) & 0xff) / 255.0f; + clearColor(r, g, b, a); + clear(COLOR_BUFFER_BIT); + } else if (!clear0) { + // Render previous back texture (now is the front) as background, + // because no background() is being used ("incremental drawing") + drawTexture(TEXTURE_2D, glColorTex.get(frontTex), + fboWidth, fboHeight, pg.width, pg.height, + 0, 0, pg.width, pg.height, + 0, 0, pg.width, pg.height); + } + + fboLayerInUse = true; } else { - // Number of samples is always an even number: - int n = 2 * (quality / 2); - return n; + fboLayerInUse = false; + } + + if (firstFrame) { + firstFrame = false; + } + + if (!USE_FBOLAYER_BY_DEFAULT) { + // The result of this assignment is the following: if the user requested + // at some point the use of the FBO layer, but subsequently didn't + // request it again, then the rendering won't render to the FBO layer if + // not needed by the condif, since it is slower than simple onscreen + // rendering. + fboLayerRequested = false; } } - protected void createFBOLayer() { + protected void endDraw(boolean clear0) { + if (fboLayerInUse) { + syncBackTexture(); + + // Draw the contents of the back texture to the screen framebuffer. + bindFramebufferImpl(FRAMEBUFFER, 0); + + clearDepth(1); + clearColor(0, 0, 0, 0); + clear(COLOR_BUFFER_BIT | DEPTH_BUFFER_BIT); + + // Render current back texture to screen, without blending. + disable(BLEND); + drawTexture(TEXTURE_2D, glColorTex.get(backTex), + fboWidth, fboHeight, pg.width, pg.height, + 0, 0, pg.width, pg.height, 0, 0, pg.width, pg.height); + + // Swapping front and back textures. + int temp = frontTex; + frontTex = backTex; + backTex = temp; + } + } + + + protected abstract void getGL(PGL pgl); + + + protected abstract boolean canDraw(); + + + protected abstract void requestFocus(); + + + protected abstract void requestDraw(); + + + protected abstract void swapBuffers(); + + + protected boolean threadIsCurrent() { + return Thread.currentThread() == glThread; + } + + + protected void beginGL() { } + + + protected void endGL() { } + + + private boolean needFBOLayer(boolean clear0) { + // TODO: need to revise this, on windows we might not want to use FBO layer + // even with anti-aliasing enabled... + return !clear0 || fboLayerRequested || 1 < numSamples; + } + + + private void createFBOLayer() { String ext = getString(EXTENSIONS); if (-1 < ext.indexOf("texture_non_power_of_two")) { fboWidth = pg.width; @@ -906,8 +652,8 @@ public class PGL { boolean multisample = 1 < numSamples; boolean packed = ext.indexOf("packed_depth_stencil") != -1; - int depthBits = getDepthBits(); - int stencilBits = getStencilBits(); + int depthBits = PApplet.min(REQUESTED_DEPTH_BITS, getDepthBits()); + int stencilBits = PApplet.min(REQUESTED_STENCIL_BITS, getStencilBits()); genTextures(2, glColorTex); for (int i = 0; i < 2; i++) { @@ -926,14 +672,14 @@ public class PGL { frontTex = 1; genFramebuffers(1, glColorFbo); - bindFramebuffer(FRAMEBUFFER, glColorFbo.get(0)); + bindFramebufferImpl(FRAMEBUFFER, glColorFbo.get(0)); framebufferTexture2D(FRAMEBUFFER, COLOR_ATTACHMENT0, TEXTURE_2D, glColorTex.get(backTex), 0); if (multisample) { // Creating multisampled FBO genFramebuffers(1, glMultiFbo); - bindFramebuffer(FRAMEBUFFER, glMultiFbo.get(0)); + bindFramebufferImpl(FRAMEBUFFER, glMultiFbo.get(0)); // color render buffer... genRenderbuffers(1, glColorBuf); @@ -1022,225 +768,12 @@ public class PGL { clearColor(r, g, b, a); clear(DEPTH_BUFFER_BIT | STENCIL_BUFFER_BIT | COLOR_BUFFER_BIT); - bindFramebuffer(FRAMEBUFFER, 0); + bindFramebufferImpl(FRAMEBUFFER, 0); fboLayerCreated = true; } - /////////////////////////////////////////////////////////// - - // Frame rendering - - - protected void beginDraw(boolean clear0) { - if (!setFps) setFps(targetFps); - - if (USE_JOGL_FBOLAYER) return; - - if (needFBOLayer(clear0)) { - if (!fboLayerCreated) createFBOLayer(); - - bindFramebuffer(FRAMEBUFFER, glColorFbo.get(0)); - framebufferTexture2D(FRAMEBUFFER, COLOR_ATTACHMENT0, - TEXTURE_2D, glColorTex.get(backTex), 0); - - if (1 < numSamples) { - bindFramebuffer(FRAMEBUFFER, glMultiFbo.get(0)); - } - - if (firstFrame) { - // No need to draw back color buffer because we are in the first frame. - int argb = pg.backgroundColor; - float a = ((argb >> 24) & 0xff) / 255.0f; - float r = ((argb >> 16) & 0xff) / 255.0f; - float g = ((argb >> 8) & 0xff) / 255.0f; - float b = ((argb) & 0xff) / 255.0f; - clearColor(r, g, b, a); - clear(COLOR_BUFFER_BIT); - } else if (!clear0) { - // Render previous back texture (now is the front) as background, - // because no background() is being used ("incremental drawing") - drawTexture(TEXTURE_2D, glColorTex.get(frontTex), - fboWidth, fboHeight, pg.width, pg.height, - 0, 0, pg.width, pg.height, - 0, 0, pg.width, pg.height); - } - - fboLayerInUse = true; - } else { - fboLayerInUse = false; - } - - if (firstFrame) { - firstFrame = false; - } - - if (!USE_FBOLAYER_BY_DEFAULT) { - // The result of this assignment is the following: if the user requested - // at some point the use of the FBO layer, but subsequently didn't - // request it again, then the rendering won't render to the FBO layer if - // not needed by the condif, since it is slower than simple onscreen - // rendering. - fboLayerRequested = false; - } - } - - - protected void endDraw(boolean clear0) { - if (isFBOBacked()) { - if (USE_JOGL_FBOLAYER) { - if (!clear0 && isFBOBacked() && !isMultisampled()) { - // Draw the back texture into the front texture, which will be used as - // back texture in the next frame. Otherwise flickering will occur if - // the sketch uses "incremental drawing" (background() not called). - frontFBO.bind(gl); - gl.glDisable(GL.GL_BLEND); - drawTexture(TEXTURE_2D, backTexAttach.getName(), - backTexAttach.getWidth(), backTexAttach.getHeight(), - pg.width, pg.height, - 0, 0, pg.width, pg.height, 0, 0, pg.width, pg.height); - backFBO.bind(gl); - } - } else if (fboLayerInUse) { - syncBackTexture(); - - // Draw the contents of the back texture to the screen framebuffer. - bindFramebuffer(FRAMEBUFFER, 0); - - clearDepth(1); - clearColor(0, 0, 0, 0); - clear(COLOR_BUFFER_BIT | DEPTH_BUFFER_BIT); - - // Render current back texture to screen, without blending. - disable(BLEND); - drawTexture(TEXTURE_2D, glColorTex.get(backTex), - fboWidth, fboHeight, pg.width, pg.height, - 0, 0, pg.width, pg.height, - 0, 0, pg.width, pg.height); - - // Swapping front and back textures. - int temp = frontTex; - frontTex = backTex; - backTex = temp; - } - } - } - - - protected void requestFocus() { - if (canvas != null) { - canvas.requestFocus(); - } - } - - - protected boolean canDraw() { - return pg.initialized && pg.parent.isDisplayable(); - } - - - protected void requestDraw() { - boolean canDraw = pg.parent.canDraw(); - if (pg.initialized && (canDraw || prevCanDraw)) { - try { - drawLatch = new CountDownLatch(1); - if (WINDOW_TOOLKIT == AWT) { - canvasAWT.display(); - } else if (WINDOW_TOOLKIT == NEWT) { - window.display(); - } - try { - drawLatch.await(DRAW_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS); - } catch (InterruptedException e) { - e.printStackTrace(); - } - - if (canDraw) prevCanDraw = true; - else prevCanDraw = false; - } catch (GLException e) { - // Unwrap GLException so that only the causing exception is shown. - Throwable tr = e.getCause(); - if (tr instanceof RuntimeException) { - throw (RuntimeException)tr; - } else { - throw new RuntimeException(tr); - } - } - } - } - - - protected void swapBuffers() { - if (WINDOW_TOOLKIT == AWT) { - canvasAWT.swapBuffers(); - } else if (WINDOW_TOOLKIT == NEWT) { - window.swapBuffers(); - } - } - - - protected boolean threadIsCurrent() { - return Thread.currentThread() == glThread; - } - - - protected boolean needFBOLayer(boolean clear0) { - return !clear0 || fboLayerRequested || 1 < numSamples; - } - - - protected void beginGL() { - if (projMatrix == null) { - projMatrix = new float[16]; - } - gl2x.glMatrixMode(GL2.GL_PROJECTION); - projMatrix[ 0] = pg.projection.m00; - projMatrix[ 1] = pg.projection.m10; - projMatrix[ 2] = pg.projection.m20; - projMatrix[ 3] = pg.projection.m30; - projMatrix[ 4] = pg.projection.m01; - projMatrix[ 5] = pg.projection.m11; - projMatrix[ 6] = pg.projection.m21; - projMatrix[ 7] = pg.projection.m31; - projMatrix[ 8] = pg.projection.m02; - projMatrix[ 9] = pg.projection.m12; - projMatrix[10] = pg.projection.m22; - projMatrix[11] = pg.projection.m32; - projMatrix[12] = pg.projection.m03; - projMatrix[13] = pg.projection.m13; - projMatrix[14] = pg.projection.m23; - projMatrix[15] = pg.projection.m33; - gl2x.glLoadMatrixf(projMatrix, 0); - - if (mvMatrix == null) { - mvMatrix = new float[16]; - } - gl2x.glMatrixMode(GL2.GL_MODELVIEW); - mvMatrix[ 0] = pg.modelview.m00; - mvMatrix[ 1] = pg.modelview.m10; - mvMatrix[ 2] = pg.modelview.m20; - mvMatrix[ 3] = pg.modelview.m30; - mvMatrix[ 4] = pg.modelview.m01; - mvMatrix[ 5] = pg.modelview.m11; - mvMatrix[ 6] = pg.modelview.m21; - mvMatrix[ 7] = pg.modelview.m31; - mvMatrix[ 8] = pg.modelview.m02; - mvMatrix[ 9] = pg.modelview.m12; - mvMatrix[10] = pg.modelview.m22; - mvMatrix[11] = pg.modelview.m32; - mvMatrix[12] = pg.modelview.m03; - mvMatrix[13] = pg.modelview.m13; - mvMatrix[14] = pg.modelview.m23; - mvMatrix[15] = pg.modelview.m33; - gl2x.glLoadMatrixf(mvMatrix, 0); - } - - - protected void endGL() { - } - - /////////////////////////////////////////////////////////// // Context interface @@ -1252,144 +785,7 @@ public class PGL { protected int getCurrentContext() { - return context.hashCode(); - } - - - /////////////////////////////////////////////////////////// - - // Tessellator interface - - - protected Tessellator createTessellator(TessellatorCallback callback) { - return new Tessellator(callback); - } - - - protected class Tessellator { - protected GLUtessellator tess; - protected TessellatorCallback callback; - protected GLUCallback gluCallback; - - public Tessellator(TessellatorCallback callback) { - this.callback = callback; - tess = GLU.gluNewTess(); - gluCallback = new GLUCallback(); - - GLU.gluTessCallback(tess, GLU.GLU_TESS_BEGIN, gluCallback); - GLU.gluTessCallback(tess, GLU.GLU_TESS_END, gluCallback); - GLU.gluTessCallback(tess, GLU.GLU_TESS_VERTEX, gluCallback); - GLU.gluTessCallback(tess, GLU.GLU_TESS_COMBINE, gluCallback); - GLU.gluTessCallback(tess, GLU.GLU_TESS_ERROR, gluCallback); - } - - public void beginPolygon() { - GLU.gluTessBeginPolygon(tess, null); - } - - public void endPolygon() { - GLU.gluTessEndPolygon(tess); - } - - public void setWindingRule(int rule) { - GLU.gluTessProperty(tess, GLU.GLU_TESS_WINDING_RULE, rule); - } - - public void beginContour() { - GLU.gluTessBeginContour(tess); - } - - public void endContour() { - GLU.gluTessEndContour(tess); - } - - public void addVertex(double[] v) { - GLU.gluTessVertex(tess, v, 0, v); - } - - protected class GLUCallback extends GLUtessellatorCallbackAdapter { - @Override - public void begin(int type) { - callback.begin(type); - } - - @Override - public void end() { - callback.end(); - } - - @Override - public void vertex(Object data) { - callback.vertex(data); - } - - @Override - public void combine(double[] coords, Object[] data, - float[] weight, Object[] outData) { - callback.combine(coords, data, weight, outData); - } - - @Override - public void error(int errnum) { - callback.error(errnum); - } - } - } - - protected String tessError(int err) { - return glu.gluErrorString(err); - } - - protected interface TessellatorCallback { - public void begin(int type); - public void end(); - public void vertex(Object data); - public void combine(double[] coords, Object[] data, - float[] weight, Object[] outData); - public void error(int errnum); - } - - - /////////////////////////////////////////////////////////// - - // FontOutline interface - - - protected final static boolean SHAPE_TEXT_SUPPORTED = true; - - protected final static int SEG_MOVETO = PathIterator.SEG_MOVETO; - protected final static int SEG_LINETO = PathIterator.SEG_LINETO; - protected final static int SEG_QUADTO = PathIterator.SEG_QUADTO; - protected final static int SEG_CUBICTO = PathIterator.SEG_CUBICTO; - protected final static int SEG_CLOSE = PathIterator.SEG_CLOSE; - - protected FontOutline createFontOutline(char ch, Object font) { - return new FontOutline(ch, font); - } - - protected class FontOutline { - PathIterator iter; - - public FontOutline(char ch, Object font) { - char textArray[] = new char[] { ch }; - Graphics2D graphics = (Graphics2D) pg.parent.getGraphics(); - FontRenderContext frc = graphics.getFontRenderContext(); - GlyphVector gv = ((Font)font).createGlyphVector(frc, textArray); - Shape shp = gv.getOutline(); - iter = shp.getPathIterator(null); - } - - public boolean isDone() { - return iter.isDone(); - } - - public int currentSegment(float coords[]) { - return iter.currentSegment(coords); - } - - public void next() { - iter.next(); - } + return glContext; } @@ -1399,7 +795,7 @@ public class PGL { protected boolean contextIsCurrent(int other) { - return other == -1 || other == context.hashCode(); + return other == -1 || other == glContext; } @@ -1456,7 +852,7 @@ public class PGL { int initColor) { int[] glcolor = new int[16 * 16]; Arrays.fill(glcolor, javaToNativeARGB(initColor)); - IntBuffer texels = PGL.allocateDirectIntBuffer(16 * 16); + IntBuffer texels = allocateDirectIntBuffer(16 * 16); texels.put(glcolor); texels.rewind(); for (int y = 0; y < height; y += 16) { @@ -1515,29 +911,45 @@ public class PGL { } - protected void drawTexture2D(int id, int texW, int texH, int scrW, int scrH, - int texX0, int texY0, int texX1, int texY1, - int scrX0, int scrY0, int scrX1, int scrY1) { - if (!loadedTex2DShader || - tex2DShaderContext.hashCode() != context.hashCode()) { - tex2DVertShader = createShader(VERTEX_SHADER, texVertShaderSource); - tex2DFragShader = createShader(FRAGMENT_SHADER, tex2DFragShaderSource); - if (0 < tex2DVertShader && 0 < tex2DFragShader) { - tex2DShaderProgram = createProgram(tex2DVertShader, tex2DFragShader); + protected PGL initTex2DShader() { + PGL ppgl = primaryPGL ? this : pg.getPrimaryPGL(); + + if (!ppgl.loadedTex2DShader || ppgl.tex2DShaderContext != ppgl.glContext) { + String vertSource = PApplet.join(texVertShaderSource, "\n"); + String fragSource = PApplet.join(tex2DFragShaderSource, "\n"); + ppgl.tex2DVertShader = createShader(VERTEX_SHADER, vertSource); + ppgl.tex2DFragShader = createShader(FRAGMENT_SHADER, fragSource); + if (0 < ppgl.tex2DVertShader && 0 < ppgl.tex2DFragShader) { + ppgl.tex2DShaderProgram = createProgram(ppgl.tex2DVertShader, ppgl.tex2DFragShader); } - if (0 < tex2DShaderProgram) { - tex2DVertLoc = getAttribLocation(tex2DShaderProgram, "inVertex"); - tex2DTCoordLoc = getAttribLocation(tex2DShaderProgram, "inTexcoord"); + if (0 < ppgl.tex2DShaderProgram) { + ppgl.tex2DVertLoc = getAttribLocation(ppgl.tex2DShaderProgram, "position"); + ppgl.tex2DTCoordLoc = getAttribLocation(ppgl.tex2DShaderProgram, "texCoord"); + ppgl.tex2DSamplerLoc = getUniformLocation(ppgl.tex2DShaderProgram, "texMap"); } - loadedTex2DShader = true; - tex2DShaderContext = context; + ppgl.loadedTex2DShader = true; + ppgl.tex2DShaderContext = ppgl.glContext; + + genBuffers(1, intBuffer); + ppgl.tex2DGeoVBO = intBuffer.get(0); + bindBuffer(ARRAY_BUFFER, ppgl.tex2DGeoVBO); + bufferData(ARRAY_BUFFER, 16 * SIZEOF_FLOAT, null, STATIC_DRAW); } if (texData == null) { texData = allocateDirectFloatBuffer(texCoords.length); } - if (0 < tex2DShaderProgram) { + return ppgl; + } + + + protected void drawTexture2D(int id, int texW, int texH, int scrW, int scrH, + int texX0, int texY0, int texX1, int texY1, + int scrX0, int scrY0, int scrX1, int scrY1) { + PGL ppgl = initTex2DShader(); + + if (0 < ppgl.tex2DShaderProgram) { // The texture overwrites anything drawn earlier. boolean depthTest = getDepthTest(); disable(DEPTH_TEST); @@ -1554,10 +966,10 @@ public class PGL { getIntegerv(VIEWPORT, viewBuffer); viewport(0, 0, scrW, scrH); - useProgram(tex2DShaderProgram); + useProgram(ppgl.tex2DShaderProgram); - enableVertexAttribArray(tex2DVertLoc); - enableVertexAttribArray(tex2DTCoordLoc); + enableVertexAttribArray(ppgl.tex2DVertLoc); + enableVertexAttribArray(ppgl.tex2DTCoordLoc); // Vertex coordinates of the textured quad are specified // in normalized screen space (-1, 1): @@ -1592,25 +1004,26 @@ public class PGL { enabledTex = true; } bindTexture(TEXTURE_2D, id); - - bindBuffer(ARRAY_BUFFER, 0); // Making sure that no VBO is bound at this point. + uniform1i(ppgl.tex2DSamplerLoc, 0); texData.position(0); - vertexAttribPointer(tex2DVertLoc, 2, FLOAT, false, 4 * SIZEOF_FLOAT, - texData); - texData.position(2); - vertexAttribPointer(tex2DTCoordLoc, 2, FLOAT, false, 4 * SIZEOF_FLOAT, - texData); + bindBuffer(ARRAY_BUFFER, ppgl.tex2DGeoVBO); + bufferData(ARRAY_BUFFER, 16 * SIZEOF_FLOAT, texData, STATIC_DRAW); + + vertexAttribPointer(ppgl.tex2DVertLoc, 2, FLOAT, false, 4 * SIZEOF_FLOAT, 0); + vertexAttribPointer(ppgl.tex2DTCoordLoc, 2, FLOAT, false, 4 * SIZEOF_FLOAT, 2 * SIZEOF_FLOAT); drawArrays(TRIANGLE_STRIP, 0, 4); + bindBuffer(ARRAY_BUFFER, 0); // Making sure that no VBO is bound at this point. + bindTexture(TEXTURE_2D, 0); if (enabledTex) { disableTexturing(TEXTURE_2D); } - disableVertexAttribArray(tex2DVertLoc); - disableVertexAttribArray(tex2DTCoordLoc); + disableVertexAttribArray(ppgl.tex2DVertLoc); + disableVertexAttribArray(ppgl.tex2DTCoordLoc); useProgram(0); @@ -1622,35 +1035,51 @@ public class PGL { depthMask(depthMask); viewport(viewBuffer.get(0), viewBuffer.get(1), - viewBuffer.get(2),viewBuffer.get(3)); + viewBuffer.get(2), viewBuffer.get(3)); } } + protected PGL initTexRectShader() { + PGL ppgl = primaryPGL ? this : pg.getPrimaryPGL(); + + if (!ppgl.loadedTexRectShader || ppgl.texRectShaderContext != ppgl.glContext) { + String vertSource = PApplet.join(texVertShaderSource, "\n"); + String fragSource = PApplet.join(texRectFragShaderSource, "\n"); + ppgl.texRectVertShader = createShader(VERTEX_SHADER, vertSource); + ppgl.texRectFragShader = createShader(FRAGMENT_SHADER, fragSource); + if (0 < ppgl.texRectVertShader && 0 < ppgl.texRectFragShader) { + ppgl.texRectShaderProgram = createProgram(ppgl.texRectVertShader, + ppgl.texRectFragShader); + } + if (0 < ppgl.texRectShaderProgram) { + ppgl.texRectVertLoc = getAttribLocation(ppgl.texRectShaderProgram, "position"); + ppgl.texRectTCoordLoc = getAttribLocation(ppgl.texRectShaderProgram, "texCoord"); + ppgl.texRectSamplerLoc = getUniformLocation(ppgl.texRectShaderProgram, "texMap"); + } + ppgl.loadedTexRectShader = true; + ppgl.texRectShaderContext = ppgl.glContext; + + genBuffers(1, intBuffer); + ppgl.texRectGeoVBO = intBuffer.get(0); + bindBuffer(ARRAY_BUFFER, ppgl.texRectGeoVBO); + bufferData(ARRAY_BUFFER, 16 * SIZEOF_FLOAT, null, STATIC_DRAW); + } + + return ppgl; + } + + protected void drawTextureRect(int id, int texW, int texH, int scrW, int scrH, int texX0, int texY0, int texX1, int texY1, int scrX0, int scrY0, int scrX1, int scrY1) { - if (!loadedTexRectShader || - texRectShaderContext.hashCode() != context.hashCode()) { - texRectVertShader = createShader(VERTEX_SHADER, texVertShaderSource); - texRectFragShader = createShader(FRAGMENT_SHADER, texRectFragShaderSource); - if (0 < texRectVertShader && 0 < texRectFragShader) { - texRectShaderProgram = createProgram(texRectVertShader, - texRectFragShader); - } - if (0 < texRectShaderProgram) { - texRectVertLoc = getAttribLocation(texRectShaderProgram, "inVertex"); - texRectTCoordLoc = getAttribLocation(texRectShaderProgram, "inTexcoord"); - } - loadedTexRectShader = true; - texRectShaderContext = context; - } + PGL ppgl = initTexRectShader(); if (texData == null) { texData = allocateDirectFloatBuffer(texCoords.length); } - if (0 < texRectShaderProgram) { + if (0 < ppgl.texRectShaderProgram) { // The texture overwrites anything drawn earlier. boolean depthTest = getDepthTest(); disable(DEPTH_TEST); @@ -1667,10 +1096,10 @@ public class PGL { getIntegerv(VIEWPORT, viewBuffer); viewport(0, 0, scrW, scrH); - useProgram(texRectShaderProgram); + useProgram(ppgl.texRectShaderProgram); - enableVertexAttribArray(texRectVertLoc); - enableVertexAttribArray(texRectTCoordLoc); + enableVertexAttribArray(ppgl.texRectVertLoc); + enableVertexAttribArray(ppgl.texRectTCoordLoc); // Vertex coordinates of the textured quad are specified // in normalized screen space (-1, 1): @@ -1705,25 +1134,26 @@ public class PGL { enabledTex = true; } bindTexture(TEXTURE_RECTANGLE, id); - - bindBuffer(ARRAY_BUFFER, 0); // Making sure that no VBO is bound at this point. + uniform1i(ppgl.texRectSamplerLoc, 0); texData.position(0); - vertexAttribPointer(texRectVertLoc, 2, FLOAT, false, 4 * SIZEOF_FLOAT, - texData); - texData.position(2); - vertexAttribPointer(texRectTCoordLoc, 2, FLOAT, false, 4 * SIZEOF_FLOAT, - texData); + bindBuffer(ARRAY_BUFFER, ppgl.texRectGeoVBO); + bufferData(ARRAY_BUFFER, 16 * SIZEOF_FLOAT, texData, STATIC_DRAW); + + vertexAttribPointer(ppgl.texRectVertLoc, 2, FLOAT, false, 4 * SIZEOF_FLOAT, 0); + vertexAttribPointer(ppgl.texRectTCoordLoc, 2, FLOAT, false, 4 * SIZEOF_FLOAT, 2 * SIZEOF_FLOAT); drawArrays(TRIANGLE_STRIP, 0, 4); + bindBuffer(ARRAY_BUFFER, 0); // Making sure that no VBO is bound at this point. + bindTexture(TEXTURE_RECTANGLE, 0); if (enabledTex) { disableTexturing(TEXTURE_RECTANGLE); } - disableVertexAttribArray(texRectVertLoc); - disableVertexAttribArray(texRectTCoordLoc); + disableVertexAttribArray(ppgl.texRectVertLoc); + disableVertexAttribArray(ppgl.texRectTCoordLoc); useProgram(0); @@ -1735,7 +1165,7 @@ public class PGL { depthMask(depthMask); viewport(viewBuffer.get(0), viewBuffer.get(1), - viewBuffer.get(2),viewBuffer.get(3)); + viewBuffer.get(2), viewBuffer.get(3)); } } @@ -1787,7 +1217,7 @@ public class PGL { * endian) to Java ARGB. */ protected static int nativeToJavaARGB(int color) { - if (PGL.BIG_ENDIAN) { // RGBA to ARGB + if (BIG_ENDIAN) { // RGBA to ARGB return (color >>> 8) | ((color << 24) & 0xFF000000); // equivalent to // ((color >> 8) & 0x00FFFFFF) | ((color << 24) & 0xFF000000) @@ -2019,6 +1449,106 @@ public class PGL { } + protected static int qualityToSamples(int quality) { + if (quality <= 1) { + return 1; + } else { + // Number of samples is always an even number: + int n = 2 * (quality / 2); + return n; + } + } + + + protected String[] loadVertexShader(String filename) { + return pg.parent.loadStrings(filename); + } + + + protected String[] loadFragmentShader(String filename) { + return pg.parent.loadStrings(filename); + } + + + protected String[] loadFragmentShader(URL url) { + try { + return PApplet.loadStrings(url.openStream()); + } catch (IOException e) { + PGraphics.showException("Cannot load fragment shader " + url.getFile()); + } + return null; + } + + + protected String[] loadVertexShader(URL url) { + try { + return PApplet.loadStrings(url.openStream()); + } catch (IOException e) { + PGraphics.showException("Cannot load vertex shader " + url.getFile()); + } + return null; + } + + + protected String[] loadVertexShader(String filename, int version) { + return loadVertexShader(filename); + } + + + protected String[] loadFragmentShader(String filename, int version) { + return loadFragmentShader(filename); + } + + + protected String[] loadFragmentShader(URL url, int version) { + return loadFragmentShader(url); + } + + + protected String[] loadVertexShader(URL url, int version) { + return loadVertexShader(url); + } + + + protected static String[] convertFragmentSource(String[] fragSrc0, + int version0, int version1) { + if (version0 == 120 && version1 == 150) { + String[] fragSrc = new String[fragSrc0.length + 2]; + fragSrc[0] = "#version 150"; + fragSrc[1] = "out vec4 fragColor;"; + for (int i = 0; i < fragSrc0.length; i++) { + String line = fragSrc0[i]; + line = line.replace("varying", "in"); + line = line.replace("attribute", "in"); + line = line.replace("gl_FragColor", "fragColor"); + line = line.replace("texture", "texMap"); + line = line.replace("texMap2D(", "texture("); + line = line.replace("texMap2DRect(", "texture("); + fragSrc[i + 2] = line; + } + return fragSrc; + } + return fragSrc0; + } + + + + protected static String[] convertVertexSource(String[] vertSrc0, + int version0, int version1) { + if (version0 == 120 && version1 == 150) { + String[] vertSrc = new String[vertSrc0.length + 1]; + vertSrc[0] = "#version 150"; + for (int i = 0; i < vertSrc0.length; i++) { + String line = vertSrc0[i]; + line = line.replace("attribute", "in"); + line = line.replace("varying", "out"); + vertSrc[i + 1] = line; + } + return vertSrc; + } + return vertSrc0; + } + protected int createShader(int shaderType, String source) { int shader = createShader(shaderType); if (shader != 0) { @@ -2121,13 +1651,21 @@ public class PGL { protected boolean hasFBOs() { - return context.hasBasicFBOSupport(); + // FBOs might still be available through extensions. + int major = getGLVersion()[0]; + if (major < 2) { + String ext = getString(EXTENSIONS); + return ext.indexOf("_framebuffer_object") != -1 && + ext.indexOf("_vertex_shader") != -1 && + ext.indexOf("_shader_objects") != -1 && + ext.indexOf("_shading_language") != -1; + } else { + return true; + } } protected boolean hasShaders() { - if (context.hasGLSL()) return true; - // GLSL might still be available through extensions. For instance, // GLContext.hasGLSL() gives false for older intel integrated chipsets on // OSX, where OpenGL is 1.4 but shaders are available. @@ -2138,9 +1676,64 @@ public class PGL { ext.indexOf("_vertex_shader") != -1 && ext.indexOf("_shader_objects") != -1 && ext.indexOf("_shading_language") != -1; + } else { + return true; } + } - return false; + + protected boolean hasNpotTexSupport() { + int major = getGLVersion()[0]; + if (major < 3) { + String ext = getString(EXTENSIONS); + return -1 < ext.indexOf("_texture_non_power_of_two"); + } else { + return true; + } + } + + + protected boolean hasAutoMipmapGenSupport() { + int major = getGLVersion()[0]; + if (major < 3) { + String ext = getString(EXTENSIONS); + return -1 < ext.indexOf("_generate_mipmap"); + } else { + return true; + } + } + + + protected boolean hasFboMultisampleSupport() { + int major = getGLVersion()[0]; + if (major < 3) { + String ext = getString(EXTENSIONS); + return -1 < ext.indexOf("_framebuffer_multisample"); + } else { + return true; + } + } + + + protected boolean hasPackedDepthStencilSupport() { + int major = getGLVersion()[0]; + if (major < 3) { + String ext = getString(EXTENSIONS); + return -1 < ext.indexOf("_packed_depth_stencil"); + } else { + return true; + } + } + + + protected boolean hasAnisoSamplingSupport() { + int major = getGLVersion()[0]; + if (major < 3) { + String ext = getString(EXTENSIONS); + return -1 < ext.indexOf("_texture_filter_anisotropic"); + } else { + return true; + } } @@ -2175,7 +1768,7 @@ public class PGL { protected static ByteBuffer allocateByteBuffer(byte[] arr) { if (USE_DIRECT_BUFFERS) { - return PGL.allocateDirectByteBuffer(arr.length); + return allocateDirectByteBuffer(arr.length); } else { return ByteBuffer.wrap(arr); } @@ -2186,7 +1779,7 @@ public class PGL { boolean wrap) { if (USE_DIRECT_BUFFERS) { if (buf == null || buf.capacity() < arr.length) { - buf = PGL.allocateDirectByteBuffer(arr.length); + buf = allocateDirectByteBuffer(arr.length); } buf.position(0); buf.put(arr); @@ -2264,7 +1857,7 @@ public class PGL { protected static ShortBuffer allocateShortBuffer(short[] arr) { if (USE_DIRECT_BUFFERS) { - return PGL.allocateDirectShortBuffer(arr.length); + return allocateDirectShortBuffer(arr.length); } else { return ShortBuffer.wrap(arr); } @@ -2275,7 +1868,7 @@ public class PGL { boolean wrap) { if (USE_DIRECT_BUFFERS) { if (buf == null || buf.capacity() < arr.length) { - buf = PGL.allocateDirectShortBuffer(arr.length); + buf = allocateDirectShortBuffer(arr.length); } buf.position(0); buf.put(arr); @@ -2353,7 +1946,7 @@ public class PGL { protected static IntBuffer allocateIntBuffer(int[] arr) { if (USE_DIRECT_BUFFERS) { - return PGL.allocateDirectIntBuffer(arr.length); + return allocateDirectIntBuffer(arr.length); } else { return IntBuffer.wrap(arr); } @@ -2364,7 +1957,7 @@ public class PGL { boolean wrap) { if (USE_DIRECT_BUFFERS) { if (buf == null || buf.capacity() < arr.length) { - buf = PGL.allocateDirectIntBuffer(arr.length); + buf = allocateDirectIntBuffer(arr.length); } buf.position(0); buf.put(arr); @@ -2441,7 +2034,7 @@ public class PGL { protected static FloatBuffer allocateFloatBuffer(float[] arr) { if (USE_DIRECT_BUFFERS) { - return PGL.allocateDirectFloatBuffer(arr.length); + return allocateDirectFloatBuffer(arr.length); } else { return FloatBuffer.wrap(arr); } @@ -2452,7 +2045,7 @@ public class PGL { boolean wrap) { if (USE_DIRECT_BUFFERS) { if (buf == null || buf.capacity() < arr.length) { - buf = PGL.allocateDirectFloatBuffer(arr.length); + buf = allocateDirectFloatBuffer(arr.length); } buf.position(0); buf.put(arr); @@ -2512,271 +2105,81 @@ public class PGL { } + // TODO: the next three functions shouldn't be here... + + protected int getFontAscent(Object font) { + return 0; + } + + + protected int getFontDescent(Object font) { + return 0; + } + + + protected int getTextWidth(Object font, char buffer[], int start, int stop) { + return 0; + } + + + protected Object getDerivedFont(Object font, float size) { + return null; + } + + /////////////////////////////////////////////////////////// - // Event listeners - protected boolean changedFrontTex = false; - protected boolean changedBackTex = false; + // Tessellator interface - protected class PGLListener implements GLEventListener { - public PGLListener() {} - @Override - public void display(GLAutoDrawable glDrawable) { - drawable = glDrawable; - context = glDrawable.getContext(); + protected abstract Tessellator createTessellator(TessellatorCallback callback); - glThread = Thread.currentThread(); - gl = context.getGL(); - gl2 = gl.getGL2ES2(); - try { - gl2x = gl.getGL2(); - } catch (javax.media.opengl.GLException e) { - gl2x = null; - } - - if (USE_JOGL_FBOLAYER && capabilities.isFBO()) { - // The onscreen drawing surface is backed by an FBO layer. - GLFBODrawable fboDrawable = null; - - if (WINDOW_TOOLKIT == AWT) { - GLCanvas glCanvas = (GLCanvas)glDrawable; - fboDrawable = (GLFBODrawable)glCanvas.getDelegatedDrawable(); - } else { - GLWindow glWindow = (GLWindow)glDrawable; - fboDrawable = (GLFBODrawable)glWindow.getDelegatedDrawable(); - } - - if (fboDrawable != null) { - backFBO = fboDrawable.getFBObject(GL.GL_BACK); - if (1 < numSamples) { - if (needSepFrontTex) { - // When using multisampled FBO, the back buffer is the MSAA - // surface so it cannot be read from. The sink buffer contains - // the readable 2D texture. - // In this case, we create an auxiliary "front" buffer that it is - // swapped with the sink buffer at the beginning of each frame. - // In this way, we always have a readable copy of the previous - // frame in the front texture, while the back is synchronized - // with the contents of the MSAA back buffer when requested. - if (frontFBO == null) { - // init - frontFBO = new FBObject(); - frontFBO.reset(gl, pg.width, pg.height); - frontFBO.attachTexture2D(gl, 0, true); - sinkFBO = backFBO.getSamplingSinkFBO(); - changedFrontTex = changedBackTex = true; - } else { - // swap - FBObject temp = sinkFBO; - sinkFBO = frontFBO; - frontFBO = temp; - backFBO.setSamplingSink(sinkFBO); - changedFrontTex = changedBackTex = false; - } - backTexAttach = (FBObject.TextureAttachment) sinkFBO. - getColorbuffer(0); - frontTexAttach = (FBObject.TextureAttachment)frontFBO. - getColorbuffer(0); - } else { - // Default setting (to save resources): the front and back - // textures are the same. - sinkFBO = backFBO.getSamplingSinkFBO(); - backTexAttach = (FBObject.TextureAttachment) sinkFBO. - getColorbuffer(0); - frontTexAttach = backTexAttach; - } - - } else { - // w/out multisampling, rendering is done on the back buffer. - frontFBO = fboDrawable.getFBObject(GL.GL_FRONT); - - backTexAttach = fboDrawable.getTextureBuffer(GL.GL_BACK); - frontTexAttach = fboDrawable.getTextureBuffer(GL.GL_FRONT); - } - } - } - - pg.parent.handleDraw(); - drawLatch.countDown(); - } - - @Override - public void dispose(GLAutoDrawable adrawable) { - } - - @Override - public void init(GLAutoDrawable adrawable) { - drawable = adrawable; - context = adrawable.getContext(); - capabilities = adrawable.getChosenGLCapabilities(); - gl = context.getGL(); - - if (!hasFBOs()) { - throw new RuntimeException(MISSING_FBO_ERROR); - } - if (!hasShaders()) { - throw new RuntimeException(MISSING_GLSL_ERROR); - } - if (USE_JOGL_FBOLAYER && capabilities.isFBO()) { - int maxs = maxSamples(); - numSamples = PApplet.min(capabilities.getNumSamples(), maxs); - } - } - - @Override - public void reshape(GLAutoDrawable adrawable, int x, int y, int w, int h) { - drawable = adrawable; - context = adrawable.getContext(); - } + protected interface Tessellator { + public void beginPolygon(); + public void endPolygon(); + public void setWindingRule(int rule); + public void beginContour(); + public void endContour(); + public void addVertex(double[] v); } - protected void nativeMouseEvent(com.jogamp.newt.event.MouseEvent nativeEvent, - int peAction) { - int modifiers = nativeEvent.getModifiers(); - int peModifiers = modifiers & - (InputEvent.SHIFT_MASK | - InputEvent.CTRL_MASK | - InputEvent.META_MASK | - InputEvent.ALT_MASK); - int peButton = 0; - if ((modifiers & InputEvent.BUTTON1_MASK) != 0) { - peButton = PConstants.LEFT; - } else if ((modifiers & InputEvent.BUTTON2_MASK) != 0) { - peButton = PConstants.CENTER; - } else if ((modifiers & InputEvent.BUTTON3_MASK) != 0) { - peButton = PConstants.RIGHT; - } - - if (PApplet.platform == PConstants.MACOSX) { - //if (nativeEvent.isPopupTrigger()) { - if ((modifiers & InputEvent.CTRL_MASK) != 0) { - peButton = PConstants.RIGHT; - } - } - - int peCount = 0; - if (peAction == MouseEvent.WHEEL) { - peCount = nativeEvent.isShiftDown() ? (int)nativeEvent.getRotation()[0] : - (int)nativeEvent.getRotation()[1]; - } else { - peCount = nativeEvent.getClickCount(); - } - - MouseEvent me = new MouseEvent(nativeEvent, nativeEvent.getWhen(), - peAction, peModifiers, - nativeEvent.getX(), nativeEvent.getY(), - peButton, - peCount); - - pg.parent.postEvent(me); + protected interface TessellatorCallback { + public void begin(int type); + public void end(); + public void vertex(Object data); + public void combine(double[] coords, Object[] data, + float[] weight, Object[] outData); + public void error(int errnum); } - protected void nativeKeyEvent(com.jogamp.newt.event.KeyEvent nativeEvent, - int peAction) { - int peModifiers = nativeEvent.getModifiers() & - (InputEvent.SHIFT_MASK | - InputEvent.CTRL_MASK | - InputEvent.META_MASK | - InputEvent.ALT_MASK); - char keyChar; - if ((int)nativeEvent.getKeyChar() == 0) { - keyChar = PConstants.CODED; - } else { - keyChar = nativeEvent.getKeyChar(); - } - - KeyEvent ke = new KeyEvent(nativeEvent, nativeEvent.getWhen(), - peAction, peModifiers, - keyChar, - nativeEvent.getKeyCode()); - - pg.parent.postEvent(ke); + protected String tessError(int err) { + return ""; } - class NEWTWindowListener implements com.jogamp.newt.event.WindowListener { - @Override - public void windowGainedFocus(com.jogamp.newt.event.WindowEvent arg0) { - pg.parent.focusGained(null); - } - @Override - public void windowLostFocus(com.jogamp.newt.event.WindowEvent arg0) { - pg.parent.focusLost(null); - } + /////////////////////////////////////////////////////////// - @Override - public void windowDestroyNotify(com.jogamp.newt.event.WindowEvent arg0) { - } + // FontOutline interface - @Override - public void windowDestroyed(com.jogamp.newt.event.WindowEvent arg0) { - } - @Override - public void windowMoved(com.jogamp.newt.event.WindowEvent arg0) { - } + protected static boolean SHAPE_TEXT_SUPPORTED; + protected static int SEG_MOVETO; + protected static int SEG_LINETO; + protected static int SEG_QUADTO; + protected static int SEG_CUBICTO; + protected static int SEG_CLOSE; - @Override - public void windowRepaint(com.jogamp.newt.event.WindowUpdateEvent arg0) { - } - @Override - public void windowResized(com.jogamp.newt.event.WindowEvent arg0) { } - } + protected abstract FontOutline createFontOutline(char ch, Object font); - // NEWT mouse listener - class NEWTMouseListener extends com.jogamp.newt.event.MouseAdapter { - @Override - public void mousePressed(com.jogamp.newt.event.MouseEvent e) { - nativeMouseEvent(e, MouseEvent.PRESS); - } - @Override - public void mouseReleased(com.jogamp.newt.event.MouseEvent e) { - nativeMouseEvent(e, MouseEvent.RELEASE); - } - @Override - public void mouseClicked(com.jogamp.newt.event.MouseEvent e) { - nativeMouseEvent(e, MouseEvent.CLICK); - } - @Override - public void mouseDragged(com.jogamp.newt.event.MouseEvent e) { - nativeMouseEvent(e, MouseEvent.DRAG); - } - @Override - public void mouseMoved(com.jogamp.newt.event.MouseEvent e) { - nativeMouseEvent(e, MouseEvent.MOVE); - } - @Override - public void mouseWheelMoved(com.jogamp.newt.event.MouseEvent e) { - nativeMouseEvent(e, MouseEvent.WHEEL); - } - @Override - public void mouseEntered(com.jogamp.newt.event.MouseEvent e) { - nativeMouseEvent(e, MouseEvent.ENTER); - } - @Override - public void mouseExited(com.jogamp.newt.event.MouseEvent e) { - nativeMouseEvent(e, MouseEvent.EXIT); - } - } - // NEWT key listener - class NEWTKeyListener extends com.jogamp.newt.event.KeyAdapter { - @Override - public void keyPressed(com.jogamp.newt.event.KeyEvent e) { - nativeKeyEvent(e, KeyEvent.PRESS); - } - @Override - public void keyReleased(com.jogamp.newt.event.KeyEvent e) { - nativeKeyEvent(e, KeyEvent.RELEASE); - } - public void keyTyped(com.jogamp.newt.event.KeyEvent e) { - nativeKeyEvent(e, KeyEvent.TYPE); - } + protected interface FontOutline { + public boolean isDone(); + public int currentSegment(float coords[]); + public void next(); } @@ -2792,601 +2195,449 @@ public class PGL { // The entire GLES 2.0 specification is available below: // http://www.khronos.org/opengles/2_X/ // + // Implementations of the PGL functions for specific OpenGL bindings (JOGL, + // LWJGL) should simply call the corresponding GL function in the bindings. + // readPixels(), activeTexture() and bindTexture() are special cases, please + // read their comments. + // Also, keep in mind the note about the PGL constants below. + // ////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////// // Constants + // Very important note: set the GL constants in your PGL subclass by using an + // static initialization block as follows: + // static { + // FALSE = SUPER_DUPER_JAVA_OPENGL_BINDINGS.GL_FALSE; + // TRUE = SUPER_DUPER_JAVA_OPENGL_BINDINGS.GL_TRUE; + // ... + // } + // and not by re-declaring the constants, because doing so will lead to + // errors when the constants are accessed through PGL because they are not + // overridden but hidden by the new declarations, and hence they keep their + // initial values (all zeroes) when accessed through the superclass. - public static final int FALSE = GL.GL_FALSE; - public static final int TRUE = GL.GL_TRUE; + public static int FALSE; + public static int TRUE; - public static final int INT = GL2.GL_INT; - public static final int BYTE = GL.GL_BYTE; - public static final int SHORT = GL.GL_SHORT; - public static final int FLOAT = GL.GL_FLOAT; - public static final int BOOL = GL2.GL_BOOL; - public static final int UNSIGNED_INT = GL.GL_UNSIGNED_INT; - public static final int UNSIGNED_BYTE = GL.GL_UNSIGNED_BYTE; - public static final int UNSIGNED_SHORT = GL.GL_UNSIGNED_SHORT; + public static int INT; + public static int BYTE; + public static int SHORT; + public static int FLOAT; + public static int BOOL; + public static int UNSIGNED_INT; + public static int UNSIGNED_BYTE; + public static int UNSIGNED_SHORT; - public static final int RGB = GL.GL_RGB; - public static final int RGBA = GL.GL_RGBA; - public static final int ALPHA = GL.GL_ALPHA; - public static final int LUMINANCE = GL.GL_LUMINANCE; - public static final int LUMINANCE_ALPHA = GL.GL_LUMINANCE_ALPHA; + public static int RGB; + public static int RGBA; + public static int ALPHA; + public static int LUMINANCE; + public static int LUMINANCE_ALPHA; - public static final int UNSIGNED_SHORT_5_6_5 = GL.GL_UNSIGNED_SHORT_5_6_5; - public static final int UNSIGNED_SHORT_4_4_4_4 = GL.GL_UNSIGNED_SHORT_4_4_4_4; - public static final int UNSIGNED_SHORT_5_5_5_1 = GL.GL_UNSIGNED_SHORT_5_5_5_1; + public static int UNSIGNED_SHORT_5_6_5; + public static int UNSIGNED_SHORT_4_4_4_4; + public static int UNSIGNED_SHORT_5_5_5_1; - public static final int RGBA4 = GL2.GL_RGBA4; - public static final int RGB5_A1 = GL2.GL_RGB5_A1; - public static final int RGB565 = GL2.GL_RGB565; + public static int RGBA4; + public static int RGB5_A1; + public static int RGB565; + public static int RGB8; + public static int RGBA8; + public static int ALPHA8; - public static final int READ_ONLY = GL2.GL_READ_ONLY; - public static final int WRITE_ONLY = GL2.GL_WRITE_ONLY; - public static final int READ_WRITE = GL2.GL_READ_WRITE; + public static int READ_ONLY; + public static int WRITE_ONLY; + public static int READ_WRITE; - public static final int TESS_WINDING_NONZERO = GLU.GLU_TESS_WINDING_NONZERO; - public static final int TESS_WINDING_ODD = GLU.GLU_TESS_WINDING_ODD; + public static int TESS_WINDING_NONZERO; + public static int TESS_WINDING_ODD; - public static final int GENERATE_MIPMAP_HINT = GL.GL_GENERATE_MIPMAP_HINT; - public static final int FASTEST = GL.GL_FASTEST; - public static final int NICEST = GL.GL_NICEST; - public static final int DONT_CARE = GL.GL_DONT_CARE; + public static int GENERATE_MIPMAP_HINT; + public static int FASTEST; + public static int NICEST; + public static int DONT_CARE; - public static final int VENDOR = GL.GL_VENDOR; - public static final int RENDERER = GL.GL_RENDERER; - public static final int VERSION = GL.GL_VERSION; - public static final int EXTENSIONS = GL.GL_EXTENSIONS; - public static final int SHADING_LANGUAGE_VERSION = GL2ES2.GL_SHADING_LANGUAGE_VERSION; + public static int VENDOR; + public static int RENDERER; + public static int VERSION; + public static int EXTENSIONS; + public static int SHADING_LANGUAGE_VERSION; - public static final int MAX_SAMPLES = GL2.GL_MAX_SAMPLES; - public static final int SAMPLES = GL.GL_SAMPLES; + public static int MAX_SAMPLES; + public static int SAMPLES; - public static final int ALIASED_LINE_WIDTH_RANGE = GL.GL_ALIASED_LINE_WIDTH_RANGE; - public static final int ALIASED_POINT_SIZE_RANGE = GL.GL_ALIASED_POINT_SIZE_RANGE; + public static int ALIASED_LINE_WIDTH_RANGE; + public static int ALIASED_POINT_SIZE_RANGE; - public static final int DEPTH_BITS = GL.GL_DEPTH_BITS; - public static final int STENCIL_BITS = GL.GL_STENCIL_BITS; + public static int DEPTH_BITS; + public static int STENCIL_BITS; - public static final int CCW = GL.GL_CCW; - public static final int CW = GL.GL_CW; + public static int CCW; + public static int CW; - public static final int VIEWPORT = GL.GL_VIEWPORT; + public static int VIEWPORT; - public static final int ARRAY_BUFFER = GL.GL_ARRAY_BUFFER; - public static final int ELEMENT_ARRAY_BUFFER = GL.GL_ELEMENT_ARRAY_BUFFER; + public static int ARRAY_BUFFER; + public static int ELEMENT_ARRAY_BUFFER; - public static final int MAX_VERTEX_ATTRIBS = GL2.GL_MAX_VERTEX_ATTRIBS; + public static int MAX_VERTEX_ATTRIBS; - public static final int STATIC_DRAW = GL.GL_STATIC_DRAW; - public static final int DYNAMIC_DRAW = GL.GL_DYNAMIC_DRAW; - public static final int STREAM_DRAW = GL2.GL_STREAM_DRAW; + public static int STATIC_DRAW; + public static int DYNAMIC_DRAW; + public static int STREAM_DRAW; - public static final int BUFFER_SIZE = GL.GL_BUFFER_SIZE; - public static final int BUFFER_USAGE = GL.GL_BUFFER_USAGE; + public static int BUFFER_SIZE; + public static int BUFFER_USAGE; - public static final int POINTS = GL.GL_POINTS; - public static final int LINE_STRIP = GL.GL_LINE_STRIP; - public static final int LINE_LOOP = GL.GL_LINE_LOOP; - public static final int LINES = GL.GL_LINES; - public static final int TRIANGLE_FAN = GL.GL_TRIANGLE_FAN; - public static final int TRIANGLE_STRIP = GL.GL_TRIANGLE_STRIP; - public static final int TRIANGLES = GL.GL_TRIANGLES; + public static int POINTS; + public static int LINE_STRIP; + public static int LINE_LOOP; + public static int LINES; + public static int TRIANGLE_FAN; + public static int TRIANGLE_STRIP; + public static int TRIANGLES; - public static final int CULL_FACE = GL.GL_CULL_FACE; - public static final int FRONT = GL.GL_FRONT; - public static final int BACK = GL.GL_BACK; - public static final int FRONT_AND_BACK = GL.GL_FRONT_AND_BACK; + public static int CULL_FACE; + public static int FRONT; + public static int BACK; + public static int FRONT_AND_BACK; - public static final int POLYGON_OFFSET_FILL = GL.GL_POLYGON_OFFSET_FILL; + public static int POLYGON_OFFSET_FILL; - public static final int UNPACK_ALIGNMENT = GL.GL_UNPACK_ALIGNMENT; - public static final int PACK_ALIGNMENT = GL.GL_PACK_ALIGNMENT; + public static int UNPACK_ALIGNMENT; + public static int PACK_ALIGNMENT; - public static final int TEXTURE_2D = GL.GL_TEXTURE_2D; - public static final int TEXTURE_RECTANGLE = GL2.GL_TEXTURE_RECTANGLE; + public static int TEXTURE_2D; + public static int TEXTURE_RECTANGLE; - public static final int TEXTURE_BINDING_2D = GL.GL_TEXTURE_BINDING_2D; - public static final int TEXTURE_BINDING_RECTANGLE = GL2.GL_TEXTURE_BINDING_RECTANGLE; + public static int TEXTURE_BINDING_2D; + public static int TEXTURE_BINDING_RECTANGLE; - public static final int MAX_TEXTURE_SIZE = GL.GL_MAX_TEXTURE_SIZE; - public static final int TEXTURE_MAX_ANISOTROPY = GL.GL_TEXTURE_MAX_ANISOTROPY_EXT; - public static final int MAX_TEXTURE_MAX_ANISOTROPY = GL.GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT; + public static int MAX_TEXTURE_SIZE; + public static int TEXTURE_MAX_ANISOTROPY; + public static int MAX_TEXTURE_MAX_ANISOTROPY; - public static final int MAX_VERTEX_TEXTURE_IMAGE_UNITS = GL2ES2.GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS; - public static final int MAX_TEXTURE_IMAGE_UNITS = GL2ES2.GL_MAX_TEXTURE_IMAGE_UNITS; - public static final int MAX_COMBINED_TEXTURE_IMAGE_UNITS = GL2ES2.GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS; + public static int MAX_VERTEX_TEXTURE_IMAGE_UNITS; + public static int MAX_TEXTURE_IMAGE_UNITS; + public static int MAX_COMBINED_TEXTURE_IMAGE_UNITS; - public static final int NUM_COMPRESSED_TEXTURE_FORMATS = GL2ES2.GL_NUM_COMPRESSED_TEXTURE_FORMATS; - public static final int COMPRESSED_TEXTURE_FORMATS = GL2ES2.GL_COMPRESSED_TEXTURE_FORMATS; + public static int NUM_COMPRESSED_TEXTURE_FORMATS; + public static int COMPRESSED_TEXTURE_FORMATS; - public static final int NEAREST = GL.GL_NEAREST; - public static final int LINEAR = GL.GL_LINEAR; - public static final int LINEAR_MIPMAP_NEAREST = GL.GL_LINEAR_MIPMAP_NEAREST; - public static final int LINEAR_MIPMAP_LINEAR = GL.GL_LINEAR_MIPMAP_LINEAR; + public static int NEAREST; + public static int LINEAR; + public static int LINEAR_MIPMAP_NEAREST; + public static int LINEAR_MIPMAP_LINEAR; - public static final int CLAMP_TO_EDGE = GL.GL_CLAMP_TO_EDGE; - public static final int REPEAT = GL.GL_REPEAT; + public static int CLAMP_TO_EDGE; + public static int REPEAT; - public static final int TEXTURE0 = GL.GL_TEXTURE0; - public static final int TEXTURE1 = GL.GL_TEXTURE1; - public static final int TEXTURE2 = GL.GL_TEXTURE2; - public static final int TEXTURE3 = GL.GL_TEXTURE3; - public static final int TEXTURE_MIN_FILTER = GL.GL_TEXTURE_MIN_FILTER; - public static final int TEXTURE_MAG_FILTER = GL.GL_TEXTURE_MAG_FILTER; - public static final int TEXTURE_WRAP_S = GL.GL_TEXTURE_WRAP_S; - public static final int TEXTURE_WRAP_T = GL.GL_TEXTURE_WRAP_T; + public static int TEXTURE0; + public static int TEXTURE1; + public static int TEXTURE2; + public static int TEXTURE3; + public static int TEXTURE_MIN_FILTER; + public static int TEXTURE_MAG_FILTER; + public static int TEXTURE_WRAP_S; + public static int TEXTURE_WRAP_T; + public static int TEXTURE_WRAP_R; - public static final int TEXTURE_CUBE_MAP = GL.GL_TEXTURE_CUBE_MAP; - public static final int TEXTURE_CUBE_MAP_POSITIVE_X = GL.GL_TEXTURE_CUBE_MAP_POSITIVE_X; - public static final int TEXTURE_CUBE_MAP_POSITIVE_Y = GL.GL_TEXTURE_CUBE_MAP_POSITIVE_Y; - public static final int TEXTURE_CUBE_MAP_POSITIVE_Z = GL.GL_TEXTURE_CUBE_MAP_POSITIVE_Z; - public static final int TEXTURE_CUBE_MAP_NEGATIVE_X = GL.GL_TEXTURE_CUBE_MAP_NEGATIVE_X; - public static final int TEXTURE_CUBE_MAP_NEGATIVE_Y = GL.GL_TEXTURE_CUBE_MAP_NEGATIVE_Y; - public static final int TEXTURE_CUBE_MAP_NEGATIVE_Z = GL.GL_TEXTURE_CUBE_MAP_NEGATIVE_Z; + public static int TEXTURE_CUBE_MAP; + public static int TEXTURE_CUBE_MAP_POSITIVE_X; + public static int TEXTURE_CUBE_MAP_POSITIVE_Y; + public static int TEXTURE_CUBE_MAP_POSITIVE_Z; + public static int TEXTURE_CUBE_MAP_NEGATIVE_X; + public static int TEXTURE_CUBE_MAP_NEGATIVE_Y; + public static int TEXTURE_CUBE_MAP_NEGATIVE_Z; - public static final int VERTEX_SHADER = GL2.GL_VERTEX_SHADER; - public static final int FRAGMENT_SHADER = GL2.GL_FRAGMENT_SHADER; - public static final int INFO_LOG_LENGTH = GL2.GL_INFO_LOG_LENGTH; - public static final int SHADER_SOURCE_LENGTH = GL2.GL_SHADER_SOURCE_LENGTH; - public static final int COMPILE_STATUS = GL2.GL_COMPILE_STATUS; - public static final int LINK_STATUS = GL2.GL_LINK_STATUS; - public static final int VALIDATE_STATUS = GL2.GL_VALIDATE_STATUS; - public static final int SHADER_TYPE = GL2.GL_SHADER_TYPE; - public static final int DELETE_STATUS = GL2.GL_DELETE_STATUS; + public static int VERTEX_SHADER; + public static int FRAGMENT_SHADER; + public static int INFO_LOG_LENGTH; + public static int SHADER_SOURCE_LENGTH; + public static int COMPILE_STATUS; + public static int LINK_STATUS; + public static int VALIDATE_STATUS; + public static int SHADER_TYPE; + public static int DELETE_STATUS; - public static final int FLOAT_VEC2 = GL2.GL_FLOAT_VEC2; - public static final int FLOAT_VEC3 = GL2.GL_FLOAT_VEC3; - public static final int FLOAT_VEC4 = GL2.GL_FLOAT_VEC4; - public static final int FLOAT_MAT2 = GL2.GL_FLOAT_MAT2; - public static final int FLOAT_MAT3 = GL2.GL_FLOAT_MAT3; - public static final int FLOAT_MAT4 = GL2.GL_FLOAT_MAT4; - public static final int INT_VEC2 = GL2.GL_INT_VEC2; - public static final int INT_VEC3 = GL2.GL_INT_VEC3; - public static final int INT_VEC4 = GL2.GL_INT_VEC4; - public static final int BOOL_VEC2 = GL2.GL_BOOL_VEC2; - public static final int BOOL_VEC3 = GL2.GL_BOOL_VEC3; - public static final int BOOL_VEC4 = GL2.GL_BOOL_VEC4; - public static final int SAMPLER_2D = GL2.GL_SAMPLER_2D; - public static final int SAMPLER_CUBE = GL2.GL_SAMPLER_CUBE; + public static int FLOAT_VEC2; + public static int FLOAT_VEC3; + public static int FLOAT_VEC4; + public static int FLOAT_MAT2; + public static int FLOAT_MAT3; + public static int FLOAT_MAT4; + public static int INT_VEC2; + public static int INT_VEC3; + public static int INT_VEC4; + public static int BOOL_VEC2; + public static int BOOL_VEC3; + public static int BOOL_VEC4; + public static int SAMPLER_2D; + public static int SAMPLER_CUBE; - public static final int LOW_FLOAT = GL2.GL_LOW_FLOAT; - public static final int MEDIUM_FLOAT = GL2.GL_MEDIUM_FLOAT; - public static final int HIGH_FLOAT = GL2.GL_HIGH_FLOAT; - public static final int LOW_INT = GL2.GL_LOW_INT; - public static final int MEDIUM_INT = GL2.GL_MEDIUM_INT; - public static final int HIGH_INT = GL2.GL_HIGH_INT; + public static int LOW_FLOAT; + public static int MEDIUM_FLOAT; + public static int HIGH_FLOAT; + public static int LOW_INT; + public static int MEDIUM_INT; + public static int HIGH_INT; - public static final int CURRENT_VERTEX_ATTRIB = GL2.GL_CURRENT_VERTEX_ATTRIB; + public static int CURRENT_VERTEX_ATTRIB; - public static final int VERTEX_ATTRIB_ARRAY_BUFFER_BINDING = GL2.GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING; - public static final int VERTEX_ATTRIB_ARRAY_ENABLED = GL2.GL_VERTEX_ATTRIB_ARRAY_ENABLED; - public static final int VERTEX_ATTRIB_ARRAY_SIZE = GL2.GL_VERTEX_ATTRIB_ARRAY_SIZE; - public static final int VERTEX_ATTRIB_ARRAY_STRIDE = GL2.GL_VERTEX_ATTRIB_ARRAY_STRIDE; - public static final int VERTEX_ATTRIB_ARRAY_TYPE = GL2.GL_VERTEX_ATTRIB_ARRAY_TYPE; - public static final int VERTEX_ATTRIB_ARRAY_NORMALIZED = GL2.GL_VERTEX_ATTRIB_ARRAY_NORMALIZED; + public static int VERTEX_ATTRIB_ARRAY_BUFFER_BINDING; + public static int VERTEX_ATTRIB_ARRAY_ENABLED; + public static int VERTEX_ATTRIB_ARRAY_SIZE; + public static int VERTEX_ATTRIB_ARRAY_STRIDE; + public static int VERTEX_ATTRIB_ARRAY_TYPE; + public static int VERTEX_ATTRIB_ARRAY_NORMALIZED; + public static int VERTEX_ATTRIB_ARRAY_POINTER; - public static final int BLEND = GL.GL_BLEND; - public static final int ONE = GL.GL_ONE; - public static final int ZERO = GL.GL_ZERO; - public static final int SRC_ALPHA = GL.GL_SRC_ALPHA; - public static final int DST_ALPHA = GL.GL_DST_ALPHA; - public static final int ONE_MINUS_SRC_ALPHA = GL.GL_ONE_MINUS_SRC_ALPHA; - public static final int ONE_MINUS_DST_COLOR = GL.GL_ONE_MINUS_DST_COLOR; - public static final int ONE_MINUS_SRC_COLOR = GL.GL_ONE_MINUS_SRC_COLOR; - public static final int DST_COLOR = GL.GL_DST_COLOR; - public static final int SRC_COLOR = GL.GL_SRC_COLOR; + public static int BLEND; + public static int ONE; + public static int ZERO; + public static int SRC_ALPHA; + public static int DST_ALPHA; + public static int ONE_MINUS_SRC_ALPHA; + public static int ONE_MINUS_DST_COLOR; + public static int ONE_MINUS_SRC_COLOR; + public static int DST_COLOR; + public static int SRC_COLOR; - public static final int SAMPLE_ALPHA_TO_COVERAGE = GL.GL_SAMPLE_ALPHA_TO_COVERAGE; - public static final int SAMPLE_COVERAGE = GL.GL_SAMPLE_COVERAGE; + public static int SAMPLE_ALPHA_TO_COVERAGE; + public static int SAMPLE_COVERAGE; - public static final int KEEP = GL.GL_KEEP; - public static final int REPLACE = GL.GL_REPLACE; - public static final int INCR = GL.GL_INCR; - public static final int DECR = GL.GL_DECR; - public static final int INVERT = GL.GL_INVERT; - public static final int INCR_WRAP = GL.GL_INCR_WRAP; - public static final int DECR_WRAP = GL.GL_DECR_WRAP; - public static final int NEVER = GL.GL_NEVER; - public static final int ALWAYS = GL.GL_ALWAYS; + public static int KEEP; + public static int REPLACE; + public static int INCR; + public static int DECR; + public static int INVERT; + public static int INCR_WRAP; + public static int DECR_WRAP; + public static int NEVER; + public static int ALWAYS; - public static final int EQUAL = GL.GL_EQUAL; - public static final int LESS = GL.GL_LESS; - public static final int LEQUAL = GL.GL_LEQUAL; - public static final int GREATER = GL.GL_GREATER; - public static final int GEQUAL = GL.GL_GEQUAL; - public static final int NOTEQUAL = GL.GL_NOTEQUAL; + public static int EQUAL; + public static int LESS; + public static int LEQUAL; + public static int GREATER; + public static int GEQUAL; + public static int NOTEQUAL; - public static final int FUNC_ADD = GL.GL_FUNC_ADD; - public static final int FUNC_MIN = GL2.GL_MIN; - public static final int FUNC_MAX = GL2.GL_MAX; - public static final int FUNC_REVERSE_SUBTRACT = GL.GL_FUNC_REVERSE_SUBTRACT; - public static final int FUNC_SUBTRACT = GL.GL_FUNC_SUBTRACT; + public static int FUNC_ADD; + public static int FUNC_MIN; + public static int FUNC_MAX; + public static int FUNC_REVERSE_SUBTRACT; + public static int FUNC_SUBTRACT; - public static final int DITHER = GL.GL_DITHER; + public static int DITHER; - public static final int CONSTANT_COLOR = GL2.GL_CONSTANT_COLOR; - public static final int CONSTANT_ALPHA = GL2.GL_CONSTANT_ALPHA; - public static final int ONE_MINUS_CONSTANT_COLOR = GL2.GL_ONE_MINUS_CONSTANT_COLOR; - public static final int ONE_MINUS_CONSTANT_ALPHA = GL2.GL_ONE_MINUS_CONSTANT_ALPHA; - public static final int SRC_ALPHA_SATURATE = GL.GL_SRC_ALPHA_SATURATE; + public static int CONSTANT_COLOR; + public static int CONSTANT_ALPHA; + public static int ONE_MINUS_CONSTANT_COLOR; + public static int ONE_MINUS_CONSTANT_ALPHA; + public static int SRC_ALPHA_SATURATE; - public static final int SCISSOR_TEST = GL.GL_SCISSOR_TEST; - public static final int DEPTH_TEST = GL.GL_DEPTH_TEST; - public static final int DEPTH_WRITEMASK = GL.GL_DEPTH_WRITEMASK; - public static final int ALPHA_TEST = GL2.GL_ALPHA_TEST; + public static int SCISSOR_TEST; + public static int STENCIL_TEST; + public static int DEPTH_TEST; + public static int DEPTH_WRITEMASK; + public static int ALPHA_TEST; - public static final int COLOR_BUFFER_BIT = GL.GL_COLOR_BUFFER_BIT; - public static final int DEPTH_BUFFER_BIT = GL.GL_DEPTH_BUFFER_BIT; - public static final int STENCIL_BUFFER_BIT = GL.GL_STENCIL_BUFFER_BIT; + public static int COLOR_BUFFER_BIT; + public static int DEPTH_BUFFER_BIT; + public static int STENCIL_BUFFER_BIT; - public static final int FRAMEBUFFER = GL.GL_FRAMEBUFFER; - public static final int COLOR_ATTACHMENT0 = GL.GL_COLOR_ATTACHMENT0; - public static final int COLOR_ATTACHMENT1 = GL2.GL_COLOR_ATTACHMENT1; - public static final int COLOR_ATTACHMENT2 = GL2.GL_COLOR_ATTACHMENT2; - public static final int COLOR_ATTACHMENT3 = GL2.GL_COLOR_ATTACHMENT3; - public static final int RENDERBUFFER = GL.GL_RENDERBUFFER; - public static final int DEPTH_ATTACHMENT = GL.GL_DEPTH_ATTACHMENT; - public static final int STENCIL_ATTACHMENT = GL.GL_STENCIL_ATTACHMENT; - public static final int READ_FRAMEBUFFER = GL2.GL_READ_FRAMEBUFFER; - public static final int DRAW_FRAMEBUFFER = GL2.GL_DRAW_FRAMEBUFFER; + public static int FRAMEBUFFER; + public static int COLOR_ATTACHMENT0; + public static int COLOR_ATTACHMENT1; + public static int COLOR_ATTACHMENT2; + public static int COLOR_ATTACHMENT3; + public static int RENDERBUFFER; + public static int DEPTH_ATTACHMENT; + public static int STENCIL_ATTACHMENT; + public static int READ_FRAMEBUFFER; + public static int DRAW_FRAMEBUFFER; - public static final int RGBA8 = GL.GL_RGBA8; - public static final int DEPTH24_STENCIL8 = GL.GL_DEPTH24_STENCIL8; + public static int DEPTH24_STENCIL8; - public static final int DEPTH_COMPONENT = GL2.GL_DEPTH_COMPONENT; - public static final int DEPTH_COMPONENT16 = GL.GL_DEPTH_COMPONENT16; - public static final int DEPTH_COMPONENT24 = GL.GL_DEPTH_COMPONENT24; - public static final int DEPTH_COMPONENT32 = GL.GL_DEPTH_COMPONENT32; + public static int DEPTH_COMPONENT; + public static int DEPTH_COMPONENT16; + public static int DEPTH_COMPONENT24; + public static int DEPTH_COMPONENT32; - public static final int STENCIL_INDEX = GL2.GL_STENCIL_INDEX; - public static final int STENCIL_INDEX1 = GL.GL_STENCIL_INDEX1; - public static final int STENCIL_INDEX4 = GL.GL_STENCIL_INDEX4; - public static final int STENCIL_INDEX8 = GL.GL_STENCIL_INDEX8; + public static int STENCIL_INDEX; + public static int STENCIL_INDEX1; + public static int STENCIL_INDEX4; + public static int STENCIL_INDEX8; - public static final int DEPTH_STENCIL = GL.GL_DEPTH_STENCIL; + public static int DEPTH_STENCIL; - public static final int FRAMEBUFFER_COMPLETE = GL.GL_FRAMEBUFFER_COMPLETE; - public static final int FRAMEBUFFER_INCOMPLETE_ATTACHMENT = GL.GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT; - public static final int FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT = GL.GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT; - public static final int FRAMEBUFFER_INCOMPLETE_DIMENSIONS = GL.GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS; - public static final int FRAMEBUFFER_INCOMPLETE_FORMATS = GL.GL_FRAMEBUFFER_INCOMPLETE_FORMATS; - public static final int FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER = GL2.GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER; - public static final int FRAMEBUFFER_INCOMPLETE_READ_BUFFER = GL2.GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER; - public static final int FRAMEBUFFER_UNSUPPORTED = GL.GL_FRAMEBUFFER_UNSUPPORTED; + public static int FRAMEBUFFER_COMPLETE; + public static int FRAMEBUFFER_INCOMPLETE_ATTACHMENT; + public static int FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT; + public static int FRAMEBUFFER_INCOMPLETE_DIMENSIONS; + public static int FRAMEBUFFER_INCOMPLETE_FORMATS; + public static int FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER; + public static int FRAMEBUFFER_INCOMPLETE_READ_BUFFER; + public static int FRAMEBUFFER_UNSUPPORTED; - public static final int FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE = GL2.GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE; - public static final int FRAMEBUFFER_ATTACHMENT_OBJECT_NAME = GL2.GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME; - public static final int FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL = GL2.GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL; - public static final int FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE = GL2.GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE; + public static int FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE; + public static int FRAMEBUFFER_ATTACHMENT_OBJECT_NAME; + public static int FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL; + public static int FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE; - public static final int RENDERBUFFER_WIDTH = GL2.GL_RENDERBUFFER_WIDTH; - public static final int RENDERBUFFER_HEIGHT = GL2.GL_RENDERBUFFER_HEIGHT; - public static final int RENDERBUFFER_RED_SIZE = GL2.GL_RENDERBUFFER_RED_SIZE; - public static final int RENDERBUFFER_GREEN_SIZE = GL2.GL_RENDERBUFFER_GREEN_SIZE; - public static final int RENDERBUFFER_BLUE_SIZE = GL2.GL_RENDERBUFFER_BLUE_SIZE; - public static final int RENDERBUFFER_ALPHA_SIZE = GL2.GL_RENDERBUFFER_ALPHA_SIZE; - public static final int RENDERBUFFER_DEPTH_SIZE = GL2.GL_RENDERBUFFER_DEPTH_SIZE; - public static final int RENDERBUFFER_STENCIL_SIZE = GL2.GL_RENDERBUFFER_STENCIL_SIZE; - public static final int RENDERBUFFER_INTERNAL_FORMAT = GL2.GL_RENDERBUFFER_INTERNAL_FORMAT; + public static int RENDERBUFFER_WIDTH; + public static int RENDERBUFFER_HEIGHT; + public static int RENDERBUFFER_RED_SIZE; + public static int RENDERBUFFER_GREEN_SIZE; + public static int RENDERBUFFER_BLUE_SIZE; + public static int RENDERBUFFER_ALPHA_SIZE; + public static int RENDERBUFFER_DEPTH_SIZE; + public static int RENDERBUFFER_STENCIL_SIZE; + public static int RENDERBUFFER_INTERNAL_FORMAT; - public static final int MULTISAMPLE = GL.GL_MULTISAMPLE; - public static final int POINT_SMOOTH = GL2.GL_POINT_SMOOTH; - public static final int LINE_SMOOTH = GL.GL_LINE_SMOOTH; - public static final int POLYGON_SMOOTH = GL2.GL_POLYGON_SMOOTH; + public static int MULTISAMPLE; + public static int POINT_SMOOTH; + public static int LINE_SMOOTH; + public static int POLYGON_SMOOTH; /////////////////////////////////////////////////////////// // Special Functions - public void flush() { - gl.glFlush(); - } - - public void finish() { - gl.glFinish(); - } - - public void hint(int target, int hint) { - gl.glHint(target, hint); - } + public abstract void flush(); + public abstract void finish(); + public abstract void hint(int target, int hint); /////////////////////////////////////////////////////////// // State and State Requests - public void enable(int value) { - if (-1 < value) { - gl.glEnable(value); - } - } - - public void disable(int value) { - if (-1 < value) { - gl.glDisable(value); - } - } - - public void getBooleanv(int value, IntBuffer data) { - if (-1 < value) { - if (byteBuffer.capacity() < data.capacity()) { - byteBuffer = allocateDirectByteBuffer(data.capacity()); - } - gl.glGetBooleanv(value, byteBuffer); - for (int i = 0; i < data.capacity(); i++) { - data.put(i, byteBuffer.get(i)); - } - } else { - fillIntBuffer(data, 0, data.capacity() - 1, 0); - } - } - - public void getIntegerv(int value, IntBuffer data) { - if (-1 < value) { - gl.glGetIntegerv(value, data); - } else { - fillIntBuffer(data, 0, data.capacity() - 1, 0); - } - } - - public void getFloatv(int value, FloatBuffer data) { - if (-1 < value) { - gl.glGetFloatv(value, data); - } else { - fillFloatBuffer(data, 0, data.capacity() - 1, 0); - } - } - - public boolean isEnabled(int value) { - return gl.glIsEnabled(value); - } - - public String getString(int name) { - return gl.glGetString(name); - } + public abstract void enable(int value); + public abstract void disable(int value); + public abstract void getBooleanv(int value, IntBuffer data); + public abstract void getIntegerv(int value, IntBuffer data); + public abstract void getFloatv(int value, FloatBuffer data); + public abstract boolean isEnabled(int value); + public abstract String getString(int name); /////////////////////////////////////////////////////////// // Error Handling - public int getError() { - return gl.glGetError(); - } - - public String errorString(int err) { - return glu.gluErrorString(err); - } + public abstract int getError(); + public abstract String errorString(int err); ////////////////////////////////////////////////////////////////////////////// // Buffer Objects - public void genBuffers(int n, IntBuffer buffers) { - gl.glGenBuffers(n, buffers); - } - - public void deleteBuffers(int n, IntBuffer buffers) { - gl.glDeleteBuffers(n, buffers); - } - - public void bindBuffer(int target, int buffer) { - gl.glBindBuffer(target, buffer); - } - - public void bufferData(int target, int size, Buffer data, int usage) { - gl.glBufferData(target, size, data, usage); - } - - public void bufferSubData(int target, int offset, int size, Buffer data) { - gl.glBufferSubData(target, offset, size, data); - } - - public void isBuffer(int buffer) { - gl.glIsBuffer(buffer); - } - - public void getBufferParameteriv(int target, int value, IntBuffer data) { - gl.glGetBufferParameteriv(target, value, data); - } - - public ByteBuffer mapBuffer(int target, int access) { - return gl2.glMapBuffer(target, access); - } - - public ByteBuffer mapBufferRange(int target, int offset, int length, int access) { - if (gl2x != null) { - return gl2x.glMapBufferRange(target, offset, length, access); - } else { - return null; - } - } - - public void unmapBuffer(int target) { - gl2.glUnmapBuffer(target); - } + public abstract void genBuffers(int n, IntBuffer buffers); + public abstract void deleteBuffers(int n, IntBuffer buffers); + public abstract void bindBuffer(int target, int buffer); + public abstract void bufferData(int target, int size, Buffer data, int usage); + public abstract void bufferSubData(int target, int offset, int size, Buffer data); + public abstract void isBuffer(int buffer); + public abstract void getBufferParameteriv(int target, int value, IntBuffer data); + public abstract ByteBuffer mapBuffer(int target, int access); + public abstract ByteBuffer mapBufferRange(int target, int offset, int length, int access); + public abstract void unmapBuffer(int target); ////////////////////////////////////////////////////////////////////////////// // Viewport and Clipping - public void depthRangef(float n, float f) { - gl.glDepthRangef(n, f); - } - - public void viewport(int x, int y, int w, int h) { - gl.glViewport(x, y, w, h); - } + public abstract void depthRangef(float n, float f); + public abstract void viewport(int x, int y, int w, int h); ////////////////////////////////////////////////////////////////////////////// // Reading Pixels + // This is a special case: because the renderer might be using an FBO even on + // the main surface, some extra handling might be needed before and after + // reading the pixels. To make this transparent to the user, the actual call + // to glReadPixels() should be done in readPixelsImpl(). - public void readPixels(int x, int y, int width, int height, int format, int type, Buffer buffer) { - boolean needBeginOp = format != STENCIL_INDEX && - format != DEPTH_COMPONENT && format != DEPTH_STENCIL; - if (needBeginOp) { - PGraphicsOpenGL.pgCurrent.beginPixelsOp(PGraphicsOpenGL.OP_READ); - } + public void readPixels(int x, int y, int width, int height, int format, int type, Buffer buffer){ + boolean pgCall = format != STENCIL_INDEX && + format != DEPTH_COMPONENT && format != DEPTH_STENCIL; + if (pgCall) pg.beginReadPixels(); readPixelsImpl(x, y, width, height, format, type, buffer); - if (needBeginOp) { - PGraphicsOpenGL.pgCurrent.endPixelsOp(); - } + if (pgCall) pg.endReadPixels(); } - protected void readPixelsImpl(int x, int y, int width, int height, int format, int type, Buffer buffer) { - gl.glReadPixels(x, y, width, height, format, type, buffer); - } + protected abstract void readPixelsImpl(int x, int y, int width, int height, int format, int type, Buffer buffer); ////////////////////////////////////////////////////////////////////////////// // Vertices - public void vertexAttrib1f(int index, float value) { - gl2.glVertexAttrib1f(index, value); - } - - public void vertexAttrib2f(int index, float value0, float value1) { - gl2.glVertexAttrib2f(index, value0, value1); - } - - public void vertexAttrib3f(int index, float value0, float value1, float value2) { - gl2.glVertexAttrib3f(index, value0, value1, value2); - } - - public void vertexAttrib4f(int index, float value0, float value1, float value2, float value3) { - gl2.glVertexAttrib4f(index, value0, value1, value2, value3); - } - - public void vertexAttrib1fv(int index, FloatBuffer values) { - gl2.glVertexAttrib1fv(index, values); - } - - public void vertexAttrib2fv(int index, FloatBuffer values) { - gl2.glVertexAttrib2fv(index, values); - } - - public void vertexAttrib3fv(int index, FloatBuffer values) { - gl2.glVertexAttrib3fv(index, values); - } - - public void vertexAttri4fv(int index, FloatBuffer values) { - gl2.glVertexAttrib4fv(index, values); - } - - public void vertexAttribPointer(int index, int size, int type, boolean normalized, int stride, int offset) { - gl2.glVertexAttribPointer(index, size, type, normalized, stride, offset); - } - - public void vertexAttribPointer(int index, int size, int type, boolean normalized, int stride, Buffer data) { - gl2.glVertexAttribPointer(index, size, type, normalized, stride, data); - } - - public void enableVertexAttribArray(int index) { - gl2.glEnableVertexAttribArray(index); - } - - public void disableVertexAttribArray(int index) { - gl2.glDisableVertexAttribArray(index); - } - - public void drawArrays(int mode, int first, int count) { - gl.glDrawArrays(mode, first, count); - } - - public void drawElements(int mode, int count, int type, int offset) { - gl.glDrawElements(mode, count, type, offset); - } - - public void drawElements(int mode, int count, int type, Buffer indices) { - gl.glDrawElements(mode, count, type, indices); - } + public abstract void vertexAttrib1f(int index, float value); + public abstract void vertexAttrib2f(int index, float value0, float value1); + public abstract void vertexAttrib3f(int index, float value0, float value1, float value2); + public abstract void vertexAttrib4f(int index, float value0, float value1, float value2, float value3); + public abstract void vertexAttrib1fv(int index, FloatBuffer values); + public abstract void vertexAttrib2fv(int index, FloatBuffer values); + public abstract void vertexAttrib3fv(int index, FloatBuffer values); + public abstract void vertexAttri4fv(int index, FloatBuffer values); + public abstract void vertexAttribPointer(int index, int size, int type, boolean normalized, int stride, int offset); + public abstract void vertexAttribPointer(int index, int size, int type, boolean normalized, int stride, Buffer data); + public abstract void enableVertexAttribArray(int index); + public abstract void disableVertexAttribArray(int index); + public abstract void drawArrays(int mode, int first, int count); + public abstract void drawElements(int mode, int count, int type, int offset); + public abstract void drawElements(int mode, int count, int type, Buffer indices); ////////////////////////////////////////////////////////////////////////////// // Rasterization - public void lineWidth(float width) { - gl.glLineWidth(width); - } - - public void frontFace(int dir) { - gl.glFrontFace(dir); - } - - public void cullFace(int mode) { - gl.glCullFace(mode); - } - - public void polygonOffset(float factor, float units) { - gl.glPolygonOffset(factor, units); - } + public abstract void lineWidth(float width); + public abstract void frontFace(int dir); + public abstract void cullFace(int mode); + public abstract void polygonOffset(float factor, float units); ////////////////////////////////////////////////////////////////////////////// // Pixel Rectangles - public void pixelStorei(int pname, int param) { - gl.glPixelStorei(pname, param); - } + public abstract void pixelStorei(int pname, int param); /////////////////////////////////////////////////////////// // Texturing + public abstract void texImage2D(int target, int level, int internalFormat, int width, int height, int border, int format, int type, Buffer data); + public abstract void copyTexImage2D(int target, int level, int internalFormat, int x, int y, int width, int height, int border); + public abstract void texSubImage2D(int target, int level, int xOffset, int yOffset, int width, int height, int format, int type, Buffer data); + public abstract void copyTexSubImage2D(int target, int level, int xOffset, int yOffset, int x, int y, int width, int height); + public abstract void compressedTexImage2D(int target, int level, int internalFormat, int width, int height, int border, int imageSize, Buffer data); + public abstract void compressedTexSubImage2D(int target, int level, int xOffset, int yOffset, int width, int height, int format, int imageSize, Buffer data); + public abstract void texParameteri(int target, int pname, int param); + public abstract void texParameterf(int target, int pname, float param); + public abstract void texParameteriv(int target, int pname, IntBuffer params); + public abstract void texParameterfv(int target, int pname, FloatBuffer params); + public abstract void generateMipmap(int target); + public abstract void genTextures(int n, IntBuffer textures); + public abstract void deleteTextures(int n, IntBuffer textures); + public abstract void getTexParameteriv(int target, int pname, IntBuffer params); + public abstract void getTexParameterfv(int target, int pname, FloatBuffer params); + public abstract boolean isTexture(int texture); + + // activeTexture() and bindTexture() have some extra logic to keep track of + // the bound textures, so the actual GL call should go in activeTextureImpl() + // and bindTextureImpl(). public void activeTexture(int texture) { - gl.glActiveTexture(texture); activeTexUnit = texture - TEXTURE0; + activeTextureImpl(texture); } - public void texImage2D(int target, int level, int internalFormat, int width, int height, int border, int format, int type, Buffer data) { - gl.glTexImage2D(target, level, internalFormat, width, height, border, format, type, data); - } - - public void copyTexImage2D(int target, int level, int internalFormat, int x, int y, int width, int height, int border) { - gl.glCopyTexImage2D(target, level, internalFormat, x, y, width, height, border); - } - - public void texSubImage2D(int target, int level, int xOffset, int yOffset, int width, int height, int format, int type, Buffer data) { - gl.glTexSubImage2D(target, level, xOffset, yOffset, width, height, format, type, data); - } - - public void copyTexSubImage2D(int target, int level, int xOffset, int yOffset, int x, int y, int width, int height) { - gl.glCopyTexSubImage2D(target, level, x, y, xOffset, xOffset, width, height); - } - - public void compressedTexImage2D(int target, int level, int internalFormat, int width, int height, int border, int imageSize, Buffer data) { - gl.glCompressedTexImage2D(target, level, internalFormat, width, height, border, imageSize, data); - } - - public void compressedTexSubImage2D(int target, int level, int xOffset, int yOffset, int width, int height, int format, int imageSize, Buffer data) { - gl.glCompressedTexSubImage2D(target, level, xOffset, yOffset, width, height, format, imageSize, data); - } - - public void texParameteri(int target, int pname, int param) { - gl.glTexParameteri(target, pname, param); - } - - public void texParameterf(int target, int pname, float param) { - gl.glTexParameterf(target, pname, param); - } - - public void texParameteriv(int target, int pname, IntBuffer params) { - gl.glTexParameteriv(target, pname, params); - } - - public void texParameterfv(int target, int pname, FloatBuffer params) { - gl.glTexParameterfv(target, pname, params); - } - - public void generateMipmap(int target) { - gl.glGenerateMipmap(target); - } + protected abstract void activeTextureImpl(int texture); public void bindTexture(int target, int texture) { - gl.glBindTexture(target, texture); + bindTextureImpl(target, texture); if (boundTextures == null) { maxTexUnits = getMaxTexUnits(); @@ -3403,439 +2654,121 @@ public class PGL { boundTextures[activeTexUnit][1] = texture; } } - - public void genTextures(int n, IntBuffer textures) { - gl.glGenTextures(n, textures); - } - - public void deleteTextures(int n, IntBuffer textures) { - gl.glDeleteTextures(n, textures); - } - - public void getTexParameteriv(int target, int pname, IntBuffer params) { - gl.glGetTexParameteriv(target, pname, params); - } - - public void getTexParameterfv(int target, int pname, FloatBuffer params) { - gl.glGetTexParameterfv(target, pname, params); - } - - public boolean isTexture(int texture) { - return gl.glIsTexture(texture); - } + protected abstract void bindTextureImpl(int target, int texture); /////////////////////////////////////////////////////////// // Shaders and Programs - public int createShader(int type) { - return gl2.glCreateShader(type); - } - - public void shaderSource(int shader, String source) { - gl2.glShaderSource(shader, 1, new String[] { source }, (int[]) null, 0); - } - - public void compileShader(int shader) { - gl2.glCompileShader(shader); - } - - public void releaseShaderCompiler() { - gl2.glReleaseShaderCompiler(); - } - - public void deleteShader(int shader) { - gl2.glDeleteShader(shader); - } - - public void shaderBinary(int count, IntBuffer shaders, int binaryFormat, Buffer binary, int length) { - gl2.glShaderBinary(count, shaders, binaryFormat, binary, length); - } - - public int createProgram() { - return gl2.glCreateProgram(); - } - - public void attachShader(int program, int shader) { - gl2.glAttachShader(program, shader); - } - - public void detachShader(int program, int shader) { - gl2.glDetachShader(program, shader); - } - - public void linkProgram(int program) { - gl2.glLinkProgram(program); - } - - public void useProgram(int program) { - gl2.glUseProgram(program); - } - - public void deleteProgram(int program) { - gl2.glDeleteProgram(program); - } - - public void getActiveAttrib(int program, int index, int[] size, int[] type, String[] name) { - int[] tmp = {0, 0, 0}; - byte[] namebuf = new byte[1024]; - gl2.glGetActiveAttrib(program, index, 1024, tmp, 0, tmp, 1, tmp, 2, namebuf, 0); - if (size != null && size.length != 0) size[0] = tmp[1]; - if (type != null && type.length != 0) type[0] = tmp[2]; - if (name != null && name.length != 0) name[0] = new String(namebuf, 0, tmp[0]); - } - - public int getAttribLocation(int program, String name) { - return gl2.glGetAttribLocation(program, name); - } - - public void bindAttribLocation(int program, int index, String name) { - gl2.glBindAttribLocation(program, index, name); - } - - public int getUniformLocation(int program, String name) { - return gl2.glGetUniformLocation(program, name); - } - - public void getActiveUniform(int program, int index, int[] size,int[] type, String[] name) { - int[] tmp= {0, 0, 0}; - byte[] namebuf = new byte[1024]; - gl2.glGetActiveUniform(program, index, 1024, tmp, 0, tmp, 1, tmp, 2, namebuf, 0); - if (size != null && size.length != 0) size[0] = tmp[1]; - if (type != null && type.length != 0) type[0] = tmp[2]; - if (name != null && name.length != 0) name[0] = new String(namebuf, 0, tmp[0]); - } - - public void uniform1i(int location, int value) { - gl2.glUniform1i(location, value); - } - - public void uniform2i(int location, int value0, int value1) { - gl2.glUniform2i(location, value0, value1); - } - - public void uniform3i(int location, int value0, int value1, int value2) { - gl2.glUniform3i(location, value0, value1, value2); - } - - public void uniform4i(int location, int value0, int value1, int value2, int value3) { - gl2.glUniform4i(location, value0, value1, value2, value3); - } - - public void uniform1f(int location, float value) { - gl2.glUniform1f(location, value); - } - - public void uniform2f(int location, float value0, float value1) { - gl2.glUniform2f(location, value0, value1); - } - - public void uniform3f(int location, float value0, float value1, float value2) { - gl2.glUniform3f(location, value0, value1, value2); - } - - public void uniform4f(int location, float value0, float value1, float value2, float value3) { - gl2.glUniform4f(location, value0, value1, value2, value3); - } - - public void uniform1iv(int location, int count, IntBuffer v) { - gl2.glUniform1iv(location, count, v); - } - - public void uniform2iv(int location, int count, IntBuffer v) { - gl2.glUniform2iv(location, count, v); - } - - public void uniform3iv(int location, int count, IntBuffer v) { - gl2.glUniform3iv(location, count, v); - } - - public void uniform4iv(int location, int count, IntBuffer v) { - gl2.glUniform4iv(location, count, v); - } - - public void uniform1fv(int location, int count, FloatBuffer v) { - gl2.glUniform1fv(location, count, v); - } - - public void uniform2fv(int location, int count, FloatBuffer v) { - gl2.glUniform2fv(location, count, v); - } - - public void uniform3fv(int location, int count, FloatBuffer v) { - gl2.glUniform3fv(location, count, v); - } - - public void uniform4fv(int location, int count, FloatBuffer v) { - gl2.glUniform4fv(location, count, v); - } - - public void uniformMatrix2fv(int location, int count, boolean transpose, FloatBuffer mat) { - gl2.glUniformMatrix2fv(location, count, transpose, mat); - } - - public void uniformMatrix3fv(int location, int count, boolean transpose, FloatBuffer mat) { - gl2.glUniformMatrix3fv(location, count, transpose, mat); - } - - public void uniformMatrix4fv(int location, int count, boolean transpose, FloatBuffer mat) { - gl2.glUniformMatrix4fv(location, count, transpose, mat); - } - - public void validateProgram(int program) { - gl2.glValidateProgram(program); - } - - public boolean isShader(int shader) { - return gl2.glIsShader(shader); - } - - public void getShaderiv(int shader, int pname, IntBuffer params) { - gl2.glGetShaderiv(shader, pname, params); - } - - public void getAttachedShaders(int program, int maxCount, IntBuffer count, IntBuffer shaders) { - gl2.glGetAttachedShaders(program, maxCount, count, shaders); - } - - public String getShaderInfoLog(int shader) { - int[] val = { 0 }; - gl2.glGetShaderiv(shader, GL2.GL_INFO_LOG_LENGTH, val, 0); - int length = val[0]; - - byte[] log = new byte[length]; - gl2.glGetShaderInfoLog(shader, length, val, 0, log, 0); - return new String(log); - } - - public String getShaderSource(int shader) { - int[] len = {0}; - byte[] buf = new byte[1024]; - gl2.glGetShaderSource(shader, 1024, len, 0, buf, 0); - return new String(buf, 0, len[0]); - } - - public void getShaderPrecisionFormat(int shaderType, int precisionType, IntBuffer range, IntBuffer precision) { - gl2.glGetShaderPrecisionFormat(shaderType, precisionType, range, precision); - } - - public void getVertexAttribfv(int index, int pname, FloatBuffer params) { - gl2.glGetVertexAttribfv(index, pname, params); - } - - public void getVertexAttribiv(int index, int pname, IntBuffer params) { - gl2.glGetVertexAttribiv(index, pname, params); - } - - public void getVertexAttribPointerv() { - throw new RuntimeException(String.format(MISSING_GLFUNC_ERROR, "glGetVertexAttribPointerv()")); - } - - public void getUniformfv(int program, int location, FloatBuffer params) { - gl2.glGetUniformfv(program, location, params); - } - - public void getUniformiv(int program, int location, IntBuffer params) { - gl2.glGetUniformiv(program, location, params); - } - - public boolean isProgram(int program) { - return gl2.glIsProgram(program); - } - - public void getProgramiv(int program, int pname, IntBuffer params) { - gl2.glGetProgramiv(program, pname, params); - } - - public String getProgramInfoLog(int program) { - int[] val = { 0 }; - gl2.glGetShaderiv(program, GL2.GL_INFO_LOG_LENGTH, val, 0); - int length = val[0]; - - if (0 < length) { - byte[] log = new byte[length]; - gl2.glGetProgramInfoLog(program, length, val, 0, log, 0); - return new String(log); - } else { - return "Unknow error"; - } - } + public abstract int createShader(int type); + public abstract void shaderSource(int shader, String source); + public abstract void compileShader(int shader); + public abstract void releaseShaderCompiler(); + public abstract void deleteShader(int shader); + public abstract void shaderBinary(int count, IntBuffer shaders, int binaryFormat, Buffer binary, int length); + public abstract int createProgram(); + public abstract void attachShader(int program, int shader); + public abstract void detachShader(int program, int shader); + public abstract void linkProgram(int program); + public abstract void useProgram(int program); + public abstract void deleteProgram(int program); + public abstract String getActiveAttrib(int program, int index, IntBuffer size, IntBuffer type); + public abstract int getAttribLocation(int program, String name); + public abstract void bindAttribLocation(int program, int index, String name); + public abstract int getUniformLocation(int program, String name); + public abstract String getActiveUniform(int program, int index, IntBuffer size, IntBuffer type); + public abstract void uniform1i(int location, int value); + public abstract void uniform2i(int location, int value0, int value1); + public abstract void uniform3i(int location, int value0, int value1, int value2); + public abstract void uniform4i(int location, int value0, int value1, int value2, int value3); + public abstract void uniform1f(int location, float value); + public abstract void uniform2f(int location, float value0, float value1); + public abstract void uniform3f(int location, float value0, float value1, float value2); + public abstract void uniform4f(int location, float value0, float value1, float value2, float value3); + public abstract void uniform1iv(int location, int count, IntBuffer v); + public abstract void uniform2iv(int location, int count, IntBuffer v); + public abstract void uniform3iv(int location, int count, IntBuffer v); + public abstract void uniform4iv(int location, int count, IntBuffer v); + public abstract void uniform1fv(int location, int count, FloatBuffer v); + public abstract void uniform2fv(int location, int count, FloatBuffer v); + public abstract void uniform3fv(int location, int count, FloatBuffer v); + public abstract void uniform4fv(int location, int count, FloatBuffer v); + public abstract void uniformMatrix2fv(int location, int count, boolean transpose, FloatBuffer mat); + public abstract void uniformMatrix3fv(int location, int count, boolean transpose, FloatBuffer mat); + public abstract void uniformMatrix4fv(int location, int count, boolean transpose, FloatBuffer mat); + public abstract void validateProgram(int program); + public abstract boolean isShader(int shader); + public abstract void getShaderiv(int shader, int pname, IntBuffer params); + public abstract void getAttachedShaders(int program, int maxCount, IntBuffer count, IntBuffer shaders); + public abstract String getShaderInfoLog(int shader); + public abstract String getShaderSource(int shader); + public abstract void getShaderPrecisionFormat(int shaderType, int precisionType, IntBuffer range, IntBuffer precision); + public abstract void getVertexAttribfv(int index, int pname, FloatBuffer params); + public abstract void getVertexAttribiv(int index, int pname, IntBuffer params); + public abstract void getVertexAttribPointerv(int index, int pname, ByteBuffer data); + public abstract void getUniformfv(int program, int location, FloatBuffer params); + public abstract void getUniformiv(int program, int location, IntBuffer params); + public abstract boolean isProgram(int program); + public abstract void getProgramiv(int program, int pname, IntBuffer params); + public abstract String getProgramInfoLog(int program); /////////////////////////////////////////////////////////// // Per-Fragment Operations - public void scissor(int x, int y, int w, int h) { - gl.glScissor(x, y, w, h); - } - - public void sampleCoverage(float value, boolean invert) { - gl2.glSampleCoverage(value, invert); - } - - public void stencilFunc(int func, int ref, int mask) { - gl2.glStencilFunc(func, ref, mask); - } - - public void stencilFuncSeparate(int face, int func, int ref, int mask) { - gl2.glStencilFuncSeparate(face, func, ref, mask); - } - - public void stencilOp(int sfail, int dpfail, int dppass) { - gl2.glStencilOp(sfail, dpfail, dppass); - } - - public void stencilOpSeparate(int face, int sfail, int dpfail, int dppass) { - gl2.glStencilOpSeparate(face, sfail, dpfail, dppass); - } - - public void depthFunc(int func) { - gl.glDepthFunc(func); - } - - public void blendEquation(int mode) { - gl.glBlendEquation(mode); - } - - public void blendEquationSeparate(int modeRGB, int modeAlpha) { - gl.glBlendEquationSeparate(modeRGB, modeAlpha); - } - - public void blendFunc(int src, int dst) { - gl.glBlendFunc(src, dst); - } - - public void blendFuncSeparate(int srcRGB, int dstRGB, int srcAlpha, int dstAlpha) { - gl.glBlendFuncSeparate(srcRGB, dstRGB, srcAlpha, dstAlpha); - } - - public void blendColor(float red, float green, float blue, float alpha) { - gl2.glBlendColor(red, green, blue, alpha); - } - - public void alphaFunc(int func, float ref) { - if (gl2x != null) { - gl2x.glAlphaFunc(func, ref); - } - } + public abstract void scissor(int x, int y, int w, int h); + public abstract void sampleCoverage(float value, boolean invert); + public abstract void stencilFunc(int func, int ref, int mask); + public abstract void stencilFuncSeparate(int face, int func, int ref, int mask); + public abstract void stencilOp(int sfail, int dpfail, int dppass); + public abstract void stencilOpSeparate(int face, int sfail, int dpfail, int dppass); + public abstract void depthFunc(int func); + public abstract void blendEquation(int mode); + public abstract void blendEquationSeparate(int modeRGB, int modeAlpha); + public abstract void blendFunc(int src, int dst); + public abstract void blendFuncSeparate(int srcRGB, int dstRGB, int srcAlpha, int dstAlpha); + public abstract void blendColor(float red, float green, float blue, float alpha); + public abstract void alphaFunc(int func, float ref); /////////////////////////////////////////////////////////// // Whole Framebuffer Operations - public void colorMask(boolean r, boolean g, boolean b, boolean a) { - gl.glColorMask(r, g, b, a); - } - - public void depthMask(boolean mask) { - gl.glDepthMask(mask); - } - - public void stencilMask(int mask) { - gl.glStencilMask(mask); - } - - public void stencilMaskSeparate(int face, int mask) { - gl2.glStencilMaskSeparate(face, mask); - } - - public void clear(int buf) { - gl.glClear(buf); - } - - public void clearColor(float r, float g, float b, float a) { - gl.glClearColor(r, g, b, a); - } - - public void clearDepth(float d) { - gl.glClearDepthf(d); - } - - public void clearStencil(int s) { - gl.glClearStencil(s); - } + public abstract void colorMask(boolean r, boolean g, boolean b, boolean a); + public abstract void depthMask(boolean mask); + public abstract void stencilMask(int mask); + public abstract void stencilMaskSeparate(int face, int mask); + public abstract void clear(int buf); + public abstract void clearColor(float r, float g, float b, float a); + public abstract void clearDepth(float d); + public abstract void clearStencil(int s); /////////////////////////////////////////////////////////// // Framebuffers Objects public void bindFramebuffer(int target, int framebuffer) { - gl.glBindFramebuffer(target, framebuffer); + pg.beginBindFramebuffer(target, framebuffer); + bindFramebufferImpl(target, framebuffer); + pg.endBindFramebuffer(target, framebuffer); } + protected abstract void bindFramebufferImpl(int target, int framebuffer); - public void deleteFramebuffers(int n, IntBuffer framebuffers) { - gl.glDeleteFramebuffers(n, framebuffers); - } - - public void genFramebuffers(int n, IntBuffer framebuffers) { - gl.glGenFramebuffers(n, framebuffers); - } - - public void bindRenderbuffer(int target, int renderbuffer) { - gl.glBindRenderbuffer(target, renderbuffer); - } - - public void deleteRenderbuffers(int n, IntBuffer renderbuffers) { - gl.glDeleteRenderbuffers(n, renderbuffers); - } - - public void genRenderbuffers(int n, IntBuffer renderbuffers) { - gl.glGenRenderbuffers(n, renderbuffers); - } - - public void renderbufferStorage(int target, int internalFormat, int width, int height) { - gl.glRenderbufferStorage(target, internalFormat, width, height); - } - - public void framebufferRenderbuffer(int target, int attachment, int rendbuferfTarget, int renderbuffer) { - gl.glFramebufferRenderbuffer(target, attachment, rendbuferfTarget, renderbuffer); - } - - public void framebufferTexture2D(int target, int attachment, int texTarget, int texture, int level) { - gl.glFramebufferTexture2D(target, attachment, texTarget, texture, level); - } - - public int checkFramebufferStatus(int target) { - return gl.glCheckFramebufferStatus(target); - } - - public boolean isFramebuffer(int framebuffer) { - return gl2.glIsFramebuffer(framebuffer); - } - - public void getFramebufferAttachmentParameteriv(int target, int attachment, int pname, IntBuffer params) { - gl2.glGetFramebufferAttachmentParameteriv(target, attachment, pname, params); - } - - public boolean isRenderbuffer(int renderbuffer) { - return gl2.glIsRenderbuffer(renderbuffer); - } - - public void getRenderbufferParameteriv(int target, int pname, IntBuffer params) { - gl2.glGetRenderbufferParameteriv(target, pname, params); - } - - public void blitFramebuffer(int srcX0, int srcY0, int srcX1, int srcY1, int dstX0, int dstY0, int dstX1, int dstY1, int mask, int filter) { - if (gl2x != null) { - gl2x.glBlitFramebuffer(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter); - } - } - - public void renderbufferStorageMultisample(int target, int samples, int format, int width, int height) { - if (gl2x != null) { - gl2x.glRenderbufferStorageMultisample(target, samples, format, width, height); - } - } - - public void readBuffer(int buf) { - if (gl2x != null) { - gl2x.glReadBuffer(buf); - } - } - - public void drawBuffer(int buf) { - if (gl2x != null) { - gl2x.glDrawBuffer(buf); - } - } + public abstract void deleteFramebuffers(int n, IntBuffer framebuffers); + public abstract void genFramebuffers(int n, IntBuffer framebuffers); + public abstract void bindRenderbuffer(int target, int renderbuffer); + public abstract void deleteRenderbuffers(int n, IntBuffer renderbuffers); + public abstract void genRenderbuffers(int n, IntBuffer renderbuffers); + public abstract void renderbufferStorage(int target, int internalFormat, int width, int height); + public abstract void framebufferRenderbuffer(int target, int attachment, int rendbuferfTarget, int renderbuffer); + public abstract void framebufferTexture2D(int target, int attachment, int texTarget, int texture, int level); + public abstract int checkFramebufferStatus(int target); + public abstract boolean isFramebuffer(int framebuffer); + public abstract void getFramebufferAttachmentParameteriv(int target, int attachment, int pname, IntBuffer params); + public abstract boolean isRenderbuffer(int renderbuffer); + public abstract void getRenderbufferParameteriv(int target, int pname, IntBuffer params); + public abstract void blitFramebuffer(int srcX0, int srcY0, int srcX1, int srcY1, int dstX0, int dstY0, int dstX1, int dstY1, int mask, int filter); + public abstract void renderbufferStorageMultisample(int target, int samples, int format, int width, int height); + public abstract void readBuffer(int buf); + public abstract void drawBuffer(int buf); } diff --git a/core/src/processing/opengl/PGraphics2D.java b/core/src/processing/opengl/PGraphics2D.java index dd4089dca..b3fc4e890 100644 --- a/core/src/processing/opengl/PGraphics2D.java +++ b/core/src/processing/opengl/PGraphics2D.java @@ -119,7 +119,7 @@ public class PGraphics2D extends PGraphicsOpenGL { @Override protected void defaultPerspective() { - super.ortho(0, width, 0, height, -1, +1); + super.ortho(0, width, -height, 0, -1, +1); } @@ -156,7 +156,8 @@ public class PGraphics2D extends PGraphicsOpenGL { @Override protected void defaultCamera() { - super.camera(width/2f, height/2f); + cameraEyeX = cameraEyeY = cameraEyeZ = 0; + resetMatrix(); } @@ -261,7 +262,7 @@ public class PGraphics2D extends PGraphicsOpenGL { } if (svg != null) { - PShapeOpenGL p2d = PShapeOpenGL.createShape2D(pg.parent, svg); + PShapeOpenGL p2d = PShapeOpenGL.createShape2D((PGraphicsOpenGL)pg, svg); return p2d; } else { return null; @@ -276,7 +277,7 @@ public class PGraphics2D extends PGraphicsOpenGL { @Override public PShape createShape(PShape source) { - return PShapeOpenGL.createShape2D(parent, source); + return PShapeOpenGL.createShape2D(this, source); } @@ -288,60 +289,31 @@ public class PGraphics2D extends PGraphicsOpenGL { @Override public PShape createShape(int type) { - return createShapeImpl(parent, type); + return createShapeImpl(this, type); } @Override public PShape createShape(int kind, float... p) { - return createShapeImpl(parent, kind, p); + return createShapeImpl(this, kind, p); } - static protected PShapeOpenGL createShapeImpl(PApplet parent, int type) { + static protected PShapeOpenGL createShapeImpl(PGraphicsOpenGL pg, int type) { PShapeOpenGL shape = null; if (type == PConstants.GROUP) { - shape = new PShapeOpenGL(parent, PConstants.GROUP); + shape = new PShapeOpenGL(pg, PConstants.GROUP); } else if (type == PShape.PATH) { - shape = new PShapeOpenGL(parent, PShape.PATH); + shape = new PShapeOpenGL(pg, PShape.PATH); } else if (type == PShape.GEOMETRY) { - shape = new PShapeOpenGL(parent, PShape.GEOMETRY); + shape = new PShapeOpenGL(pg, PShape.GEOMETRY); } - - /* - if (type == POINTS) { - shape = new PShapeOpenGL(parent, PShape.GEOMETRY); - shape.setKind(POINTS); - } else if (type == LINES) { - shape = new PShapeOpenGL(parent, PShape.GEOMETRY); - shape.setKind(LINES); - } else if (type == TRIANGLE || type == TRIANGLES) { - shape = new PShapeOpenGL(parent, PShape.GEOMETRY); - shape.setKind(TRIANGLES); - } else if (type == TRIANGLE_FAN) { - shape = new PShapeOpenGL(parent, PShape.GEOMETRY); - shape.setKind(TRIANGLE_FAN); - } else if (type == TRIANGLE_STRIP) { - shape = new PShapeOpenGL(parent, PShape.GEOMETRY); - shape.setKind(TRIANGLE_STRIP); - } else if (type == QUAD || type == QUADS) { - shape = new PShapeOpenGL(parent, PShape.GEOMETRY); - shape.setKind(QUADS); - } else if (type == QUAD_STRIP) { - shape = new PShapeOpenGL(parent, PShape.GEOMETRY); - shape.setKind(QUAD_STRIP); - } else if (type == POLYGON) { - shape = new PShapeOpenGL(parent, PShape.GEOMETRY); - shape.setKind(POLYGON); - } - */ - shape.is3D(false); return shape; } - static protected PShapeOpenGL createShapeImpl(PApplet parent, + static protected PShapeOpenGL createShapeImpl(PGraphicsOpenGL pg, int kind, float... p) { PShapeOpenGL shape = null; int len = p.length; @@ -351,49 +323,49 @@ public class PGraphics2D extends PGraphicsOpenGL { showWarning("Wrong number of parameters"); return null; } - shape = new PShapeOpenGL(parent, PShape.PRIMITIVE); + shape = new PShapeOpenGL(pg, PShape.PRIMITIVE); shape.setKind(POINT); } else if (kind == LINE) { if (len != 4) { showWarning("Wrong number of parameters"); return null; } - shape = new PShapeOpenGL(parent, PShape.PRIMITIVE); + shape = new PShapeOpenGL(pg, PShape.PRIMITIVE); shape.setKind(LINE); } else if (kind == TRIANGLE) { if (len != 6) { showWarning("Wrong number of parameters"); return null; } - shape = new PShapeOpenGL(parent, PShape.PRIMITIVE); + shape = new PShapeOpenGL(pg, PShape.PRIMITIVE); shape.setKind(TRIANGLE); } else if (kind == QUAD) { if (len != 8) { showWarning("Wrong number of parameters"); return null; } - shape = new PShapeOpenGL(parent, PShape.PRIMITIVE); + shape = new PShapeOpenGL(pg, PShape.PRIMITIVE); shape.setKind(QUAD); } else if (kind == RECT) { - if (len != 4 && len != 5 && len != 8) { + if (len != 4 && len != 5 && len != 8 && len != 9) { showWarning("Wrong number of parameters"); return null; } - shape = new PShapeOpenGL(parent, PShape.PRIMITIVE); + shape = new PShapeOpenGL(pg, PShape.PRIMITIVE); shape.setKind(RECT); } else if (kind == ELLIPSE) { - if (len != 4) { + if (len != 4 && len != 5) { showWarning("Wrong number of parameters"); return null; } - shape = new PShapeOpenGL(parent, PShape.PRIMITIVE); + shape = new PShapeOpenGL(pg, PShape.PRIMITIVE); shape.setKind(ELLIPSE); } else if (kind == ARC) { if (len != 6 && len != 7) { showWarning("Wrong number of parameters"); return null; } - shape = new PShapeOpenGL(parent, PShape.PRIMITIVE); + shape = new PShapeOpenGL(pg, PShape.PRIMITIVE); shape.setKind(ARC); } else if (kind == BOX) { showWarning("Primitive not supported in 2D"); diff --git a/core/src/processing/opengl/PGraphics3D.java b/core/src/processing/opengl/PGraphics3D.java index a60641dac..77edcc9f0 100644 --- a/core/src/processing/opengl/PGraphics3D.java +++ b/core/src/processing/opengl/PGraphics3D.java @@ -87,7 +87,20 @@ public class PGraphics3D extends PGraphicsOpenGL { pushProjection(); ortho(0, width, 0, height, -1, +1); pushMatrix(); - camera(width/2, height/2); + + // Set camera for 2D rendering, it simply centers at (width/2, height/2) + float centerX = width/2; + float centerY = height/2; + modelview.reset(); + modelview.translate(-centerX, -centerY); + + modelviewInv.set(modelview); + modelviewInv.invert(); + + camera.set(modelview); + cameraInv.set(modelviewInv); + + updateProjmodelview(); } @@ -131,7 +144,7 @@ public class PGraphics3D extends PGraphicsOpenGL { if (obj != null) { int prevTextureMode = pg.textureMode; pg.textureMode = NORMAL; - PShapeOpenGL p3d = PShapeOpenGL.createShape3D(pg.parent, obj); + PShapeOpenGL p3d = PShapeOpenGL.createShape3D((PGraphicsOpenGL)pg, obj); pg.textureMode = prevTextureMode; return p3d; } else { @@ -147,7 +160,7 @@ public class PGraphics3D extends PGraphicsOpenGL { @Override public PShape createShape(PShape source) { - return PShapeOpenGL.createShape3D(parent, source); + return PShapeOpenGL.createShape3D(this, source); } @@ -159,63 +172,31 @@ public class PGraphics3D extends PGraphicsOpenGL { @Override public PShape createShape(int type) { - return createShapeImpl(parent, type); + return createShapeImpl(this, type); } @Override public PShape createShape(int kind, float... p) { - return createShapeImpl(parent, kind, p); + return createShapeImpl(this, kind, p); } - static protected PShapeOpenGL createShapeImpl(PApplet parent, int type) { + static protected PShapeOpenGL createShapeImpl(PGraphicsOpenGL pg, int type) { PShapeOpenGL shape = null; if (type == PConstants.GROUP) { - shape = new PShapeOpenGL(parent, PConstants.GROUP); + shape = new PShapeOpenGL(pg, PConstants.GROUP); } else if (type == PShape.PATH) { - shape = new PShapeOpenGL(parent, PShape.PATH); + shape = new PShapeOpenGL(pg, PShape.PATH); } else if (type == PShape.GEOMETRY) { - shape = new PShapeOpenGL(parent, PShape.GEOMETRY); + shape = new PShapeOpenGL(pg, PShape.GEOMETRY); } - - /* - (type == POINTS) { - shape = new PShapeOpenGL(parent, PShape.GEOMETRY); - - shape.setKind(POINTS); - } else if (type == LINES) { - shape = new PShapeOpenGL(parent, PShape.GEOMETRY); - - shape.setKind(LINES); - } else if (type == TRIANGLE || type == TRIANGLES) { - shape = new PShapeOpenGL(parent, PShape.GEOMETRY); - - shape.setKind(TRIANGLES); - } else if (type == TRIANGLE_FAN) { - shape = new PShapeOpenGL(parent, PShape.GEOMETRY); - shape.setKind(TRIANGLE_FAN); - } else if (type == TRIANGLE_STRIP) { - shape = new PShapeOpenGL(parent, PShape.GEOMETRY); - shape.setKind(TRIANGLE_STRIP); - } else if (type == QUAD || type == QUADS) { - shape = new PShapeOpenGL(parent, PShape.GEOMETRY); - shape.setKind(QUADS); - } else if (type == QUAD_STRIP) { - shape = new PShapeOpenGL(parent, PShape.GEOMETRY); - shape.setKind(QUAD_STRIP); - } else if (type == POLYGON) { - shape = new PShapeOpenGL(parent, PShape.GEOMETRY); - shape.setKind(POLYGON); - } - */ - shape.is3D(true); return shape; } - static protected PShapeOpenGL createShapeImpl(PApplet parent, + static protected PShapeOpenGL createShapeImpl(PGraphicsOpenGL pg, int kind, float... p) { PShapeOpenGL shape = null; int len = p.length; @@ -225,63 +206,63 @@ public class PGraphics3D extends PGraphicsOpenGL { showWarning("Wrong number of parameters"); return null; } - shape = new PShapeOpenGL(parent, PShape.PRIMITIVE); + shape = new PShapeOpenGL(pg, PShape.PRIMITIVE); shape.setKind(POINT); } else if (kind == LINE) { if (len != 4 && len != 6) { showWarning("Wrong number of parameters"); return null; } - shape = new PShapeOpenGL(parent, PShape.PRIMITIVE); + shape = new PShapeOpenGL(pg, PShape.PRIMITIVE); shape.setKind(LINE); } else if (kind == TRIANGLE) { if (len != 6) { showWarning("Wrong number of parameters"); return null; } - shape = new PShapeOpenGL(parent, PShape.PRIMITIVE); + shape = new PShapeOpenGL(pg, PShape.PRIMITIVE); shape.setKind(TRIANGLE); } else if (kind == QUAD) { if (len != 8) { showWarning("Wrong number of parameters"); return null; } - shape = new PShapeOpenGL(parent, PShape.PRIMITIVE); + shape = new PShapeOpenGL(pg, PShape.PRIMITIVE); shape.setKind(QUAD); } else if (kind == RECT) { - if (len != 4 && len != 5 && len != 8) { + if (len != 4 && len != 5 && len != 8 && len != 9) { showWarning("Wrong number of parameters"); return null; } - shape = new PShapeOpenGL(parent, PShape.PRIMITIVE); + shape = new PShapeOpenGL(pg, PShape.PRIMITIVE); shape.setKind(RECT); } else if (kind == ELLIPSE) { - if (len != 4) { + if (len != 4 && len != 5) { showWarning("Wrong number of parameters"); return null; } - shape = new PShapeOpenGL(parent, PShape.PRIMITIVE); + shape = new PShapeOpenGL(pg, PShape.PRIMITIVE); shape.setKind(ELLIPSE); } else if (kind == ARC) { if (len != 6 && len != 7) { showWarning("Wrong number of parameters"); return null; } - shape = new PShapeOpenGL(parent, PShape.PRIMITIVE); + shape = new PShapeOpenGL(pg, PShape.PRIMITIVE); shape.setKind(ARC); } else if (kind == BOX) { if (len != 1 && len != 3) { showWarning("Wrong number of parameters"); return null; } - shape = new PShapeOpenGL(parent, PShape.PRIMITIVE); + shape = new PShapeOpenGL(pg, PShape.PRIMITIVE); shape.setKind(BOX); } else if (kind == SPHERE) { if (len < 1 || 3 < len) { showWarning("Wrong number of parameters"); return null; } - shape = new PShapeOpenGL(parent, PShape.PRIMITIVE); + shape = new PShapeOpenGL(pg, PShape.PRIMITIVE); shape.setKind(SPHERE); } else { showWarning("Unrecognized primitive type"); diff --git a/core/src/processing/opengl/PGraphicsOpenGL.java b/core/src/processing/opengl/PGraphicsOpenGL.java index a0db323c3..12c6fe413 100644 --- a/core/src/processing/opengl/PGraphicsOpenGL.java +++ b/core/src/processing/opengl/PGraphicsOpenGL.java @@ -24,8 +24,6 @@ package processing.opengl; import processing.core.*; -import java.awt.Font; -import java.awt.FontMetrics; import java.net.URL; import java.nio.*; import java.util.*; @@ -36,72 +34,13 @@ import java.util.*; */ public class PGraphicsOpenGL extends PGraphics { /** Interface between Processing and OpenGL */ - public static PGL pgl; - - /** The main PApplet renderer. */ - protected static PGraphicsOpenGL pgPrimary = null; + public PGL pgl; /** The renderer currently in use. */ - protected static PGraphicsOpenGL pgCurrent = null; + protected PGraphicsOpenGL currentPG; /** Font cache for texture objects. */ - protected WeakHashMap fontMap = - new WeakHashMap(); - - // ........................................................ - - static final String OPENGL_THREAD_ERROR = - "Cannot run the OpenGL renderer outside the main thread, change your code" + - "\nso the drawing calls are all inside the main thread, " + - "\nor use the default renderer instead."; - static final String BLEND_DRIVER_ERROR = - "blendMode(%1$s) is not supported by this hardware (or driver)"; - static final String BLEND_RENDERER_ERROR = - "blendMode(%1$s) is not supported by this renderer"; - static final String ALREADY_DRAWING_ERROR = - "Already called beginDraw()"; - static final String NO_BEGIN_DRAW_ERROR = - "Cannot call endDraw() before beginDraw()"; - static final String NESTED_DRAW_ERROR = - "Already called drawing on another PGraphicsOpenGL object"; - static final String ALREADY_BEGAN_CONTOUR_ERROR = - "Already called beginContour()"; - static final String NO_BEGIN_CONTOUR_ERROR = - "Need to call beginContour() first"; - static final String UNSUPPORTED_SMOOTH_LEVEL_ERROR = - "Smooth level %1$s is not available. Using %2$s instead"; - static final String UNSUPPORTED_SMOOTH_ERROR = - "Smooth is not supported by this hardware (or driver)"; - static final String TOO_MANY_SMOOTH_CALLS_ERROR = - "The smooth/noSmooth functions are being called too often.\n" + - "This results in screen flickering, so they will be disabled\n" + - "for the rest of the sketch's execution"; - static final String UNSUPPORTED_SHAPE_FORMAT_ERROR = - "Unsupported shape format"; - static final String INVALID_FILTER_SHADER_ERROR = - "Your shader needs to be of TEXTURE type to be used as a filter"; - static final String INVALID_PROCESSING_SHADER_ERROR = - "The GLSL code doesn't seem to contain a valid shader to use in Processing"; - static final String WRONG_SHADER_TYPE_ERROR = - "shader() called with a wrong shader"; - static final String UNKNOWN_SHADER_KIND_ERROR = - "Unknown shader kind"; - static final String NO_TEXLIGHT_SHADER_ERROR = - "Your shader needs to be of TEXLIGHT type " + - "to render this geometry properly, using default shader instead."; - static final String NO_LIGHT_SHADER_ERROR = - "Your shader needs to be of LIGHT type " + - "to render this geometry properly, using default shader instead."; - static final String NO_TEXTURE_SHADER_ERROR = - "Your shader needs to be of TEXTURE type " + - "to render this geometry properly, using default shader instead."; - static final String NO_COLOR_SHADER_ERROR = - "Your shader needs to be of COLOR type " + - "to render this geometry properly, using default shader instead."; - static final String TOO_LONG_STROKE_PATH_ERROR = - "Stroke path is too long, some bevel triangles won't be added"; - static final String TESSELLATION_ERROR = - "Tessellation Error: %1$s"; + protected WeakHashMap fontMap; // ........................................................ @@ -155,8 +94,8 @@ public class PGraphicsOpenGL extends PGraphics { protected boolean pointBuffersCreated = false; protected int pointBuffersContext; - protected static final int INIT_VERTEX_BUFFER_SIZE = 256; - protected static final int INIT_INDEX_BUFFER_SIZE = 512; + static protected final int INIT_VERTEX_BUFFER_SIZE = 256; + static protected final int INIT_INDEX_BUFFER_SIZE = 512; // ........................................................ @@ -175,8 +114,6 @@ public class PGraphicsOpenGL extends PGraphics { /** Some hardware limits */ static public int maxTextureSize; static public int maxSamples; - static public float maxPointSize; - static public float maxLineWidth; static public float maxAnisoAmount; static public int depthBits; static public int stencilBits; @@ -190,7 +127,7 @@ public class PGraphicsOpenGL extends PGraphics { // ........................................................ - // GL objects: + // GL resources: static protected HashMap glTextureObjects = new HashMap(); @@ -231,32 +168,20 @@ public class PGraphicsOpenGL extends PGraphics { PGraphicsOpenGL.class.getResource("PointVert.glsl"); static protected URL defPointShaderFragURL = PGraphicsOpenGL.class.getResource("PointFrag.glsl"); - - static protected ColorShader defColorShader; - static protected TextureShader defTextureShader; - static protected LightShader defLightShader; - static protected TexlightShader defTexlightShader; - static protected LineShader defLineShader; - static protected PointShader defPointShader; - static protected URL maskShaderFragURL = PGraphicsOpenGL.class.getResource("MaskFrag.glsl"); - static protected TextureShader maskShader; - protected ColorShader colorShader; - protected TextureShader textureShader; - protected LightShader lightShader; - protected TexlightShader texlightShader; - protected LineShader lineShader; - protected PointShader pointShader; + protected PShader defColorShader; + protected PShader defTextureShader; + protected PShader defLightShader; + protected PShader defTexlightShader; + protected PShader defLineShader; + protected PShader defPointShader; + protected PShader maskShader; - // When shader warnings are enabled, the renderer is strict in regards to the - // use of the polygon shaders. For instance, if a light shader is set to - // render lit geometry, but the geometry is mixed with some pieces of unlit or - // textured geometry, then it will warn that the set shader cannot be used for - // that other type of geometry, even though Processing will use the correct, - // built-in shaders to handle it. - protected boolean shaderWarningsEnabled = true; + protected PShader polyShader; + protected PShader lineShader; + protected PShader pointShader; // ........................................................ @@ -264,8 +189,8 @@ public class PGraphicsOpenGL extends PGraphics { protected InGeometry inGeo; protected TessGeometry tessGeo; - static protected Tessellator tessellator; protected TexCache texCache; + static protected Tessellator tessellator; // ........................................................ @@ -413,11 +338,11 @@ public class PGraphicsOpenGL extends PGraphics { static protected final int FB_STACK_DEPTH = 16; - static protected int fbStackDepth; - static protected FrameBuffer[] fbStack = new FrameBuffer[FB_STACK_DEPTH]; - static protected FrameBuffer drawFramebuffer; - static protected FrameBuffer readFramebuffer; - static protected FrameBuffer currentFramebuffer; + protected int fbStackDepth; + protected FrameBuffer[] fbStack; + protected FrameBuffer drawFramebuffer; + protected FrameBuffer readFramebuffer; + protected FrameBuffer currentFramebuffer; // ....................................................... @@ -493,7 +418,6 @@ public class PGraphicsOpenGL extends PGraphics { protected boolean openContour = false; protected boolean breakShape = false; protected boolean defaultEdges = false; - protected PImage textureImage0; static protected final int EDGE_MIDDLE = 0; static protected final int EDGE_START = 1; @@ -518,22 +442,80 @@ public class PGraphicsOpenGL extends PGraphics { final static protected float POINT_ACCURACY_FACTOR = 10.0f; /** Used in quad point tessellation. */ - final protected float[][] QUAD_POINT_SIGNS = + final static protected float[][] QUAD_POINT_SIGNS = { {-1, +1}, {-1, -1}, {+1, -1}, {+1, +1} }; /** To get data from OpenGL. */ static protected IntBuffer intBuffer; static protected FloatBuffer floatBuffer; + // ........................................................ + + // Error strings: + + static final String OPENGL_THREAD_ERROR = + "Cannot run the OpenGL renderer outside the main thread, change your code" + + "\nso the drawing calls are all inside the main thread, " + + "\nor use the default renderer instead."; + static final String BLEND_DRIVER_ERROR = + "blendMode(%1$s) is not supported by this hardware (or driver)"; + static final String BLEND_RENDERER_ERROR = + "blendMode(%1$s) is not supported by this renderer"; + static final String ALREADY_BEGAN_CONTOUR_ERROR = + "Already called beginContour()"; + static final String NO_BEGIN_CONTOUR_ERROR = + "Need to call beginContour() first"; + static final String UNSUPPORTED_SMOOTH_LEVEL_ERROR = + "Smooth level %1$s is not available. Using %2$s instead"; + static final String UNSUPPORTED_SMOOTH_ERROR = + "Smooth is not supported by this hardware (or driver)"; + static final String TOO_MANY_SMOOTH_CALLS_ERROR = + "The smooth/noSmooth functions are being called too often.\n" + + "This results in screen flickering, so they will be disabled\n" + + "for the rest of the sketch's execution"; + static final String UNSUPPORTED_SHAPE_FORMAT_ERROR = + "Unsupported shape format"; + static final String MISSING_UV_TEXCOORDS_ERROR = + "No uv texture coordinates supplied with vertex() call"; + static final String INVALID_FILTER_SHADER_ERROR = + "Your shader cannot be used as a filter because is of type POINT or LINES"; + static final String INCONSISTENT_SHADER_TYPES = + "The vertex and fragment shaders have different types"; + static final String WRONG_SHADER_TYPE_ERROR = + "shader() called with a wrong shader"; + static final String SHADER_NEED_LIGHT_ATTRIBS = + "The provided shader needs light attributes (ambient, diffuse, etc.), but " + + "the current scene is unlit, so the default shader will be used instead"; + static final String MISSING_FRAGMENT_SHADER = + "The fragment shader is missing, cannot create shader object"; + static final String MISSING_VERTEX_SHADER = + "The vertex shader is missing, cannot create shader object"; + static final String UNKNOWN_SHADER_KIND_ERROR = + "Unknown shader kind"; + static final String NO_TEXLIGHT_SHADER_ERROR = + "Your shader needs to be of TEXLIGHT type " + + "to render this geometry properly, using default shader instead."; + static final String NO_LIGHT_SHADER_ERROR = + "Your shader needs to be of LIGHT type " + + "to render this geometry properly, using default shader instead."; + static final String NO_TEXTURE_SHADER_ERROR = + "Your shader needs to be of TEXTURE type " + + "to render this geometry properly, using default shader instead."; + static final String NO_COLOR_SHADER_ERROR = + "Your shader needs to be of COLOR type " + + "to render this geometry properly, using default shader instead."; + static final String TOO_LONG_STROKE_PATH_ERROR = + "Stroke path is too long, some bevel triangles won't be added"; + static final String TESSELLATION_ERROR = + "Tessellation Error: %1$s"; + ////////////////////////////////////////////////////////////// // INIT/ALLOCATE/FINISH public PGraphicsOpenGL() { - if (pgl == null) { - pgl = new PGL(this); - } + pgl = createPGL(this); if (tessellator == null) { tessellator = new Tessellator(); @@ -546,9 +528,9 @@ public class PGraphicsOpenGL extends PGraphics { viewport = PGL.allocateIntBuffer(4); - inGeo = newInGeometry(IMMEDIATE); - tessGeo = newTessGeometry(IMMEDIATE); - texCache = newTexCache(); + inGeo = newInGeometry(this, IMMEDIATE); + tessGeo = newTessGeometry(this, IMMEDIATE); + texCache = newTexCache(this); initialized = false; } @@ -557,7 +539,12 @@ public class PGraphicsOpenGL extends PGraphics { @Override public void setPrimary(boolean primary) { super.setPrimary(primary); + pgl.setPrimary(primary); format = ARGB; + if (primary) { + fbStack = new FrameBuffer[FB_STACK_DEPTH]; + fontMap = new WeakHashMap(); + } } @@ -579,7 +566,6 @@ public class PGraphicsOpenGL extends PGraphics { height = iheight; allocate(); - reapplySettings(); // init perspective projection based on new dimensions cameraFOV = 60 * DEG_TO_RAD; // at least for now @@ -590,10 +576,6 @@ public class PGraphicsOpenGL extends PGraphics { cameraFar = cameraZ * 10.0f; cameraAspect = (float) width / (float) height; - // Forces a restart of OpenGL so the canvas has the right size. - restartPGL(); - - // set this flag so that beginDraw() will do an update to the camera. sized = true; } @@ -637,17 +619,19 @@ public class PGraphicsOpenGL extends PGraphics { public void dispose() { // PGraphics super.dispose(); - // Swap buffers the end to make sure that no - // garbage is shown on the screen, this particularly - // affects non-interactive sketches on windows that - // render only 1 frame, so no enough rendering - // iterations have been conducted so far to properly - // initialize all the buffers. - pgl.swapBuffers(); + if (primarySurface) { + // Swap buffers the end to make sure that no + // garbage is shown on the screen, this particularly + // affects non-interactive sketches on windows that + // render only 1 frame, so no enough rendering + // iterations have been conducted so far to properly + // initialize all the buffers. + pgl.swapBuffers(); + } - deletePolyBuffers(); - deleteLineBuffers(); - deletePointBuffers(); + finalizePolyBuffers(); + finalizeLineBuffers(); + finalizePointBuffers(); deleteSurfaceTextures(); if (primarySurface) { @@ -661,18 +645,19 @@ public class PGraphicsOpenGL extends PGraphics { } } - deleteFinalizedGLResources(); + deleteFinalizedGLResources(pgl); - if (primarySurface) pgl.deleteSurface(); + if (primarySurface) { + pgl.deleteSurface(); + } } -// @Override @Override protected void finalize() throws Throwable { try { - deletePolyBuffers(); - deleteLineBuffers(); - deletePointBuffers(); + finalizePolyBuffers(); + finalizeLineBuffers(); + finalizePointBuffers(); deleteSurfaceTextures(); if (!primarySurface) { @@ -695,21 +680,44 @@ public class PGraphicsOpenGL extends PGraphics { } + ////////////////////////////////////////////////////////////// + + // IMAGE METADATA FOR THIS RENDERER + + + @Override + public void setCache(PImage image, Object storage) { + getPrimaryPG().cacheMap.put(image, storage); + } + + + @Override + public Object getCache(PImage image) { + return getPrimaryPG().cacheMap.get(image); + } + + + @Override + public void removeCache(PImage image) { + getPrimaryPG().cacheMap.remove(image); + } + + ////////////////////////////////////////////////////////////// protected void setFontTexture(PFont font, FontTexture fontTexture) { - fontMap.put(font, fontTexture); + getPrimaryPG().fontMap.put(font, fontTexture); } protected FontTexture getFontTexture(PFont font) { - return fontMap.get(font); + return getPrimaryPG().fontMap.get(font); } protected void removeFontTexture(PFont font) { - fontMap.remove(font); + getPrimaryPG().fontMap.remove(font); } @@ -745,8 +753,8 @@ public class PGraphicsOpenGL extends PGraphics { // Texture Objects ----------------------------------------------------------- - protected static int createTextureObject(int context) { - deleteFinalizedTextureObjects(); + protected static int createTextureObject(int context, PGL pgl) { + deleteFinalizedTextureObjects(pgl); pgl.genTextures(1, intBuffer); int id = intBuffer.get(0); @@ -759,7 +767,7 @@ public class PGraphicsOpenGL extends PGraphics { return id; } - protected static void deleteTextureObject(int id, int context) { + protected static void deleteTextureObject(int id, int context, PGL pgl) { GLResource res = new GLResource(id, context); if (glTextureObjects.containsKey(res)) { intBuffer.put(0, id); @@ -768,7 +776,7 @@ public class PGraphicsOpenGL extends PGraphics { } } - protected static void deleteAllTextureObjects() { + protected static void deleteAllTextureObjects(PGL pgl) { for (GLResource res : glTextureObjects.keySet()) { intBuffer.put(0, res.id); if (pgl.threadIsCurrent()) pgl.deleteTextures(1, intBuffer); @@ -784,7 +792,7 @@ public class PGraphicsOpenGL extends PGraphics { } } - protected static void deleteFinalizedTextureObjects() { + protected static void deleteFinalizedTextureObjects(PGL pgl) { Set finalized = new HashSet(); for (GLResource res : glTextureObjects.keySet()) { @@ -809,8 +817,8 @@ public class PGraphicsOpenGL extends PGraphics { // Vertex Buffer Objects ----------------------------------------------------- - protected static int createVertexBufferObject(int context) { - deleteFinalizedVertexBufferObjects(); + protected static int createVertexBufferObject(int context, PGL pgl) { + deleteFinalizedVertexBufferObjects(pgl); pgl.genBuffers(1, intBuffer); int id = intBuffer.get(0); @@ -823,7 +831,7 @@ public class PGraphicsOpenGL extends PGraphics { return id; } - protected static void deleteVertexBufferObject(int id, int context) { + protected static void deleteVertexBufferObject(int id, int context, PGL pgl) { GLResource res = new GLResource(id, context); if (glVertexBuffers.containsKey(res)) { intBuffer.put(0, id); @@ -832,7 +840,7 @@ public class PGraphicsOpenGL extends PGraphics { } } - protected static void deleteAllVertexBufferObjects() { + protected static void deleteAllVertexBufferObjects(PGL pgl) { for (GLResource res : glVertexBuffers.keySet()) { intBuffer.put(0, res.id); if (pgl.threadIsCurrent()) pgl.deleteBuffers(1, intBuffer); @@ -848,7 +856,7 @@ public class PGraphicsOpenGL extends PGraphics { } } - protected static void deleteFinalizedVertexBufferObjects() { + protected static void deleteFinalizedVertexBufferObjects(PGL pgl) { Set finalized = new HashSet(); for (GLResource res : glVertexBuffers.keySet()) { @@ -873,8 +881,8 @@ public class PGraphicsOpenGL extends PGraphics { // FrameBuffer Objects ------------------------------------------------------- - protected static int createFrameBufferObject(int context) { - deleteFinalizedFrameBufferObjects(); + protected static int createFrameBufferObject(int context, PGL pgl) { + deleteFinalizedFrameBufferObjects(pgl); pgl.genFramebuffers(1, intBuffer); int id = intBuffer.get(0); @@ -887,7 +895,7 @@ public class PGraphicsOpenGL extends PGraphics { return id; } - protected static void deleteFrameBufferObject(int id, int context) { + protected static void deleteFrameBufferObject(int id, int context, PGL pgl) { GLResource res = new GLResource(id, context); if (glFrameBuffers.containsKey(res)) { intBuffer.put(0, id); @@ -896,7 +904,7 @@ public class PGraphicsOpenGL extends PGraphics { } } - protected static void deleteAllFrameBufferObjects() { + protected static void deleteAllFrameBufferObjects(PGL pgl) { for (GLResource res : glFrameBuffers.keySet()) { intBuffer.put(0, res.id); if (pgl.threadIsCurrent()) pgl.deleteFramebuffers(1, intBuffer); @@ -912,7 +920,7 @@ public class PGraphicsOpenGL extends PGraphics { } } - protected static void deleteFinalizedFrameBufferObjects() { + protected static void deleteFinalizedFrameBufferObjects(PGL pgl) { Set finalized = new HashSet(); for (GLResource res : glFrameBuffers.keySet()) { @@ -939,8 +947,8 @@ public class PGraphicsOpenGL extends PGraphics { // RenderBuffer Objects ------------------------------------------------------ - protected static int createRenderBufferObject(int context) { - deleteFinalizedRenderBufferObjects(); + protected static int createRenderBufferObject(int context, PGL pgl) { + deleteFinalizedRenderBufferObjects(pgl); pgl.genRenderbuffers(1, intBuffer); int id = intBuffer.get(0); @@ -953,7 +961,7 @@ public class PGraphicsOpenGL extends PGraphics { return id; } - protected static void deleteRenderBufferObject(int id, int context) { + protected static void deleteRenderBufferObject(int id, int context, PGL pgl) { GLResource res = new GLResource(id, context); if (glRenderBuffers.containsKey(res)) { intBuffer.put(0, id); @@ -962,7 +970,7 @@ public class PGraphicsOpenGL extends PGraphics { } } - protected static void deleteAllRenderBufferObjects() { + protected static void deleteAllRenderBufferObjects(PGL pgl) { for (GLResource res : glRenderBuffers.keySet()) { intBuffer.put(0, res.id); if (pgl.threadIsCurrent()) pgl.deleteRenderbuffers(1, intBuffer); @@ -978,7 +986,7 @@ public class PGraphicsOpenGL extends PGraphics { } } - protected static void deleteFinalizedRenderBufferObjects() { + protected static void deleteFinalizedRenderBufferObjects(PGL pgl) { Set finalized = new HashSet(); for (GLResource res : glRenderBuffers.keySet()) { @@ -1003,8 +1011,8 @@ public class PGraphicsOpenGL extends PGraphics { // GLSL Program Objects ------------------------------------------------------ - protected static int createGLSLProgramObject(int context) { - deleteFinalizedGLSLProgramObjects(); + protected static int createGLSLProgramObject(int context, PGL pgl) { + deleteFinalizedGLSLProgramObjects(pgl); int id = pgl.createProgram(); @@ -1016,7 +1024,7 @@ public class PGraphicsOpenGL extends PGraphics { return id; } - protected static void deleteGLSLProgramObject(int id, int context) { + protected static void deleteGLSLProgramObject(int id, int context, PGL pgl) { GLResource res = new GLResource(id, context); if (glslPrograms.containsKey(res)) { if (pgl.threadIsCurrent()) pgl.deleteProgram(res.id); @@ -1024,7 +1032,7 @@ public class PGraphicsOpenGL extends PGraphics { } } - protected static void deleteAllGLSLProgramObjects() { + protected static void deleteAllGLSLProgramObjects(PGL pgl) { for (GLResource res : glslPrograms.keySet()) { if (pgl.threadIsCurrent()) pgl.deleteProgram(res.id); } @@ -1039,7 +1047,7 @@ public class PGraphicsOpenGL extends PGraphics { } } - protected static void deleteFinalizedGLSLProgramObjects() { + protected static void deleteFinalizedGLSLProgramObjects(PGL pgl) { Set finalized = new HashSet(); for (GLResource res : glslPrograms.keySet()) { @@ -1063,8 +1071,8 @@ public class PGraphicsOpenGL extends PGraphics { // GLSL Vertex Shader Objects ------------------------------------------------ - protected static int createGLSLVertShaderObject(int context) { - deleteFinalizedGLSLVertShaderObjects(); + protected static int createGLSLVertShaderObject(int context, PGL pgl) { + deleteFinalizedGLSLVertShaderObjects(pgl); int id = pgl.createShader(PGL.VERTEX_SHADER); @@ -1076,7 +1084,7 @@ public class PGraphicsOpenGL extends PGraphics { return id; } - protected static void deleteGLSLVertShaderObject(int id, int context) { + protected static void deleteGLSLVertShaderObject(int id, int context, PGL pgl) { GLResource res = new GLResource(id, context); if (glslVertexShaders.containsKey(res)) { if (pgl.threadIsCurrent()) pgl.deleteShader(res.id); @@ -1084,7 +1092,7 @@ public class PGraphicsOpenGL extends PGraphics { } } - protected static void deleteAllGLSLVertShaderObjects() { + protected static void deleteAllGLSLVertShaderObjects(PGL pgl) { for (GLResource res : glslVertexShaders.keySet()) { if (pgl.threadIsCurrent()) pgl.deleteShader(res.id); } @@ -1100,7 +1108,7 @@ public class PGraphicsOpenGL extends PGraphics { } } - protected static void deleteFinalizedGLSLVertShaderObjects() { + protected static void deleteFinalizedGLSLVertShaderObjects(PGL pgl) { Set finalized = new HashSet(); for (GLResource res : glslVertexShaders.keySet()) { @@ -1124,8 +1132,8 @@ public class PGraphicsOpenGL extends PGraphics { // GLSL Fragment Shader Objects ---------------------------------------------- - protected static int createGLSLFragShaderObject(int context) { - deleteFinalizedGLSLFragShaderObjects(); + protected static int createGLSLFragShaderObject(int context, PGL pgl) { + deleteFinalizedGLSLFragShaderObjects(pgl); int id = pgl.createShader(PGL.FRAGMENT_SHADER); @@ -1137,7 +1145,7 @@ public class PGraphicsOpenGL extends PGraphics { return id; } - protected static void deleteGLSLFragShaderObject(int id, int context) { + protected static void deleteGLSLFragShaderObject(int id, int context, PGL pgl) { GLResource res = new GLResource(id, context); if (glslFragmentShaders.containsKey(res)) { if (pgl.threadIsCurrent()) pgl.deleteShader(res.id); @@ -1145,7 +1153,7 @@ public class PGraphicsOpenGL extends PGraphics { } } - protected static void deleteAllGLSLFragShaderObjects() { + protected static void deleteAllGLSLFragShaderObjects(PGL pgl) { for (GLResource res : glslFragmentShaders.keySet()) { if (pgl.threadIsCurrent()) pgl.deleteShader(res.id); } @@ -1161,7 +1169,7 @@ public class PGraphicsOpenGL extends PGraphics { } } - protected static void deleteFinalizedGLSLFragShaderObjects() { + protected static void deleteFinalizedGLSLFragShaderObjects(PGL pgl) { Set finalized = new HashSet(); for (GLResource res : glslFragmentShaders.keySet()) { @@ -1185,14 +1193,14 @@ public class PGraphicsOpenGL extends PGraphics { // All OpenGL resources ------------------------------------------------------ - protected static void deleteFinalizedGLResources() { - deleteFinalizedTextureObjects(); - deleteFinalizedVertexBufferObjects(); - deleteFinalizedFrameBufferObjects(); - deleteFinalizedRenderBufferObjects(); - deleteFinalizedGLSLProgramObjects(); - deleteFinalizedGLSLVertShaderObjects(); - deleteFinalizedGLSLFragShaderObjects(); + protected static void deleteFinalizedGLResources(PGL pgl) { + deleteFinalizedTextureObjects(pgl); + deleteFinalizedVertexBufferObjects(pgl); + deleteFinalizedFrameBufferObjects(pgl); + deleteFinalizedRenderBufferObjects(pgl); + deleteFinalizedGLSLProgramObjects(pgl); + deleteFinalizedGLSLVertShaderObjects(pgl); + deleteFinalizedGLSLFragShaderObjects(pgl); } @@ -1201,37 +1209,45 @@ public class PGraphicsOpenGL extends PGraphics { // FRAMEBUFFERS - protected static void pushFramebuffer() { - if (fbStackDepth == FB_STACK_DEPTH) { + protected void pushFramebuffer() { + PGraphicsOpenGL ppg = getPrimaryPG(); + if (ppg.fbStackDepth == FB_STACK_DEPTH) { throw new RuntimeException("Too many pushFramebuffer calls"); } - fbStack[fbStackDepth] = currentFramebuffer; - fbStackDepth++; + ppg.fbStack[ppg.fbStackDepth] = ppg.currentFramebuffer; + ppg.fbStackDepth++; } - protected static void setFramebuffer(FrameBuffer fbo) { - if (currentFramebuffer != fbo) { - currentFramebuffer = fbo; - currentFramebuffer.bind(); + protected void setFramebuffer(FrameBuffer fbo) { + PGraphicsOpenGL ppg = getPrimaryPG(); + if (ppg.currentFramebuffer != fbo) { + ppg.currentFramebuffer = fbo; + if (ppg.currentFramebuffer != null) ppg.currentFramebuffer.bind(); } } - protected static void popFramebuffer() { - if (fbStackDepth == 0) { + protected void popFramebuffer() { + PGraphicsOpenGL ppg = getPrimaryPG(); + if (ppg.fbStackDepth == 0) { throw new RuntimeException("popFramebuffer call is unbalanced."); } - fbStackDepth--; - FrameBuffer fbo = fbStack[fbStackDepth]; - if (currentFramebuffer != fbo) { - currentFramebuffer.finish(pgPrimary); - currentFramebuffer = fbo; - currentFramebuffer.bind(); + ppg.fbStackDepth--; + FrameBuffer fbo = ppg.fbStack[ppg.fbStackDepth]; + if (ppg.currentFramebuffer != fbo) { + ppg.currentFramebuffer.finish(); + ppg.currentFramebuffer = fbo; + if (ppg.currentFramebuffer != null) ppg.currentFramebuffer.bind(); } } + protected FrameBuffer getCurrentFB() { + return getPrimaryPG().currentFramebuffer; + } + + ////////////////////////////////////////////////////////////// // FRAME RENDERING @@ -1245,41 +1261,41 @@ public class PGraphicsOpenGL extends PGraphics { int sizei = INIT_VERTEX_BUFFER_SIZE * PGL.SIZEOF_INT; int sizex = INIT_INDEX_BUFFER_SIZE * PGL.SIZEOF_INDEX; - glPolyVertex = createVertexBufferObject(polyBuffersContext); + glPolyVertex = createVertexBufferObject(polyBuffersContext, pgl); pgl.bindBuffer(PGL.ARRAY_BUFFER, glPolyVertex); pgl.bufferData(PGL.ARRAY_BUFFER, 3 * sizef, null, PGL.STATIC_DRAW); - glPolyColor = createVertexBufferObject(polyBuffersContext); + glPolyColor = createVertexBufferObject(polyBuffersContext, pgl); pgl.bindBuffer(PGL.ARRAY_BUFFER, glPolyColor); pgl.bufferData(PGL.ARRAY_BUFFER, sizei, null, PGL.STATIC_DRAW); - glPolyNormal = createVertexBufferObject(polyBuffersContext); + glPolyNormal = createVertexBufferObject(polyBuffersContext, pgl); pgl.bindBuffer(PGL.ARRAY_BUFFER, glPolyNormal); pgl.bufferData(PGL.ARRAY_BUFFER, 3 * sizef, null, PGL.STATIC_DRAW); - glPolyTexcoord = createVertexBufferObject(polyBuffersContext); + glPolyTexcoord = createVertexBufferObject(polyBuffersContext, pgl); pgl.bindBuffer(PGL.ARRAY_BUFFER, glPolyTexcoord); pgl.bufferData(PGL.ARRAY_BUFFER, 2 * sizef, null, PGL.STATIC_DRAW); - glPolyAmbient = createVertexBufferObject(polyBuffersContext); + glPolyAmbient = createVertexBufferObject(polyBuffersContext, pgl); pgl.bindBuffer(PGL.ARRAY_BUFFER, glPolyAmbient); pgl.bufferData(PGL.ARRAY_BUFFER, sizei, null, PGL.STATIC_DRAW); - glPolySpecular = createVertexBufferObject(polyBuffersContext); + glPolySpecular = createVertexBufferObject(polyBuffersContext, pgl); pgl.bindBuffer(PGL.ARRAY_BUFFER, glPolySpecular); pgl.bufferData(PGL.ARRAY_BUFFER, sizei, null, PGL.STATIC_DRAW); - glPolyEmissive = createVertexBufferObject(polyBuffersContext); + glPolyEmissive = createVertexBufferObject(polyBuffersContext, pgl); pgl.bindBuffer(PGL.ARRAY_BUFFER, glPolyEmissive); pgl.bufferData(PGL.ARRAY_BUFFER, sizei, null, PGL.STATIC_DRAW); - glPolyShininess = createVertexBufferObject(polyBuffersContext); + glPolyShininess = createVertexBufferObject(polyBuffersContext, pgl); pgl.bindBuffer(PGL.ARRAY_BUFFER, glPolyShininess); pgl.bufferData(PGL.ARRAY_BUFFER, sizef, null, PGL.STATIC_DRAW); pgl.bindBuffer(PGL.ARRAY_BUFFER, 0); - glPolyIndex = createVertexBufferObject(polyBuffersContext); + glPolyIndex = createVertexBufferObject(polyBuffersContext, pgl); pgl.bindBuffer(PGL.ELEMENT_ARRAY_BUFFER, glPolyIndex); pgl.bufferData(PGL.ELEMENT_ARRAY_BUFFER, sizex, null, PGL.STATIC_DRAW); @@ -1290,7 +1306,8 @@ public class PGraphicsOpenGL extends PGraphics { } - protected void updatePolyBuffers(boolean lit, boolean tex) { + protected void updatePolyBuffers(boolean lit, boolean tex, + boolean needNormals, boolean needTexCoords) { createPolyBuffers(); int size = tessGeo.polyVertexCount; @@ -1308,11 +1325,6 @@ public class PGraphicsOpenGL extends PGraphics { tessGeo.polyColorsBuffer, PGL.STATIC_DRAW); if (lit) { - tessGeo.updatePolyNormalsBuffer(); - pgl.bindBuffer(PGL.ARRAY_BUFFER, glPolyNormal); - pgl.bufferData(PGL.ARRAY_BUFFER, 3 * sizef, - tessGeo.polyNormalsBuffer, PGL.STATIC_DRAW); - tessGeo.updatePolyAmbientBuffer(); pgl.bindBuffer(PGL.ARRAY_BUFFER, glPolyAmbient); pgl.bufferData(PGL.ARRAY_BUFFER, sizei, @@ -1333,8 +1345,14 @@ public class PGraphicsOpenGL extends PGraphics { pgl.bufferData(PGL.ARRAY_BUFFER, sizef, tessGeo.polyShininessBuffer, PGL.STATIC_DRAW); } + if (lit || needNormals) { + tessGeo.updatePolyNormalsBuffer(); + pgl.bindBuffer(PGL.ARRAY_BUFFER, glPolyNormal); + pgl.bufferData(PGL.ARRAY_BUFFER, 3 * sizef, + tessGeo.polyNormalsBuffer, PGL.STATIC_DRAW); + } - if (tex) { + if (tex || needTexCoords) { tessGeo.updatePolyTexCoordsBuffer(); pgl.bindBuffer(PGL.ARRAY_BUFFER, glPolyTexcoord); pgl.bufferData(PGL.ARRAY_BUFFER, 2 * sizef, @@ -1360,37 +1378,53 @@ public class PGraphicsOpenGL extends PGraphics { } - protected void deletePolyBuffers() { - if (polyBuffersCreated) { - deleteVertexBufferObject(glPolyVertex, polyBuffersContext); + protected void finalizePolyBuffers() { + if (glPolyVertex != 0) { + PGraphicsOpenGL.finalizeVertexBufferObject(glPolyVertex, polyBuffersContext); glPolyVertex = 0; - - deleteVertexBufferObject(glPolyColor, polyBuffersContext); - glPolyColor = 0; - - deleteVertexBufferObject(glPolyNormal, polyBuffersContext); - glPolyNormal = 0; - - deleteVertexBufferObject(glPolyTexcoord, polyBuffersContext); - glPolyTexcoord = 0; - - deleteVertexBufferObject(glPolyAmbient, polyBuffersContext); - glPolyAmbient = 0; - - deleteVertexBufferObject(glPolySpecular, polyBuffersContext); - glPolySpecular = 0; - - deleteVertexBufferObject(glPolyEmissive, polyBuffersContext); - glPolyEmissive = 0; - - deleteVertexBufferObject(glPolyShininess, polyBuffersContext); - glPolyShininess = 0; - - deleteVertexBufferObject(glPolyIndex, polyBuffersContext); - glPolyIndex = 0; - - polyBuffersCreated = false; } + + if (glPolyColor != 0) { + PGraphicsOpenGL.finalizeVertexBufferObject(glPolyColor, polyBuffersContext); + glPolyColor = 0; + } + + if (glPolyNormal != 0) { + PGraphicsOpenGL.finalizeVertexBufferObject(glPolyNormal, polyBuffersContext); + glPolyNormal = 0; + } + + if (glPolyTexcoord != 0) { + PGraphicsOpenGL.finalizeVertexBufferObject(glPolyTexcoord, polyBuffersContext); + glPolyTexcoord = 0; + } + + if (glPolyAmbient != 0) { + PGraphicsOpenGL.finalizeVertexBufferObject(glPolyAmbient, polyBuffersContext); + glPolyAmbient = 0; + } + + if (glPolySpecular != 0) { + PGraphicsOpenGL.finalizeVertexBufferObject(glPolySpecular, polyBuffersContext); + glPolySpecular = 0; + } + + if (glPolyEmissive != 0) { + PGraphicsOpenGL.finalizeVertexBufferObject(glPolyEmissive, polyBuffersContext); + glPolyEmissive = 0; + } + + if (glPolyShininess != 0) { + PGraphicsOpenGL.finalizeVertexBufferObject(glPolyShininess, polyBuffersContext); + glPolyShininess = 0; + } + + if (glPolyIndex != 0) { + PGraphicsOpenGL.finalizeVertexBufferObject(glPolyIndex, polyBuffersContext); + glPolyIndex = 0; + } + + polyBuffersCreated = false; } @@ -1402,22 +1436,22 @@ public class PGraphicsOpenGL extends PGraphics { int sizei = INIT_VERTEX_BUFFER_SIZE * PGL.SIZEOF_INT; int sizex = INIT_INDEX_BUFFER_SIZE * PGL.SIZEOF_INDEX; - glLineVertex = createVertexBufferObject(lineBuffersContext); + glLineVertex = createVertexBufferObject(lineBuffersContext, pgl); pgl.bindBuffer(PGL.ARRAY_BUFFER, glLineVertex); pgl.bufferData(PGL.ARRAY_BUFFER, 3 * sizef, null, PGL.STATIC_DRAW); - glLineColor = createVertexBufferObject(lineBuffersContext); + glLineColor = createVertexBufferObject(lineBuffersContext, pgl); pgl.bindBuffer(PGL.ARRAY_BUFFER, glLineColor); pgl.bufferData(PGL.ARRAY_BUFFER, sizei, null, PGL.STATIC_DRAW); - glLineAttrib = createVertexBufferObject(lineBuffersContext); + glLineAttrib = createVertexBufferObject(lineBuffersContext, pgl); pgl.bindBuffer(PGL.ARRAY_BUFFER, glLineAttrib); pgl.bufferData(PGL.ARRAY_BUFFER, 4 * sizef, null, PGL.STATIC_DRAW); pgl.bindBuffer(PGL.ARRAY_BUFFER, 0); - glLineIndex = createVertexBufferObject(lineBuffersContext); + glLineIndex = createVertexBufferObject(lineBuffersContext, pgl); pgl.bindBuffer(PGL.ELEMENT_ARRAY_BUFFER, glLineIndex); pgl.bufferData(PGL.ELEMENT_ARRAY_BUFFER, sizex, null, PGL.STATIC_DRAW); @@ -1469,22 +1503,28 @@ public class PGraphicsOpenGL extends PGraphics { } - protected void deleteLineBuffers() { - if (lineBuffersCreated) { - deleteVertexBufferObject(glLineVertex, lineBuffersContext); + protected void finalizeLineBuffers() { + if (glLineVertex != 0) { + PGraphicsOpenGL.finalizeVertexBufferObject(glLineVertex, lineBuffersContext); glLineVertex = 0; - - deleteVertexBufferObject(glLineColor, lineBuffersContext); - glLineColor = 0; - - deleteVertexBufferObject(glLineAttrib, lineBuffersContext); - glLineAttrib = 0; - - deleteVertexBufferObject(glLineIndex, lineBuffersContext); - glLineIndex = 0; - - lineBuffersCreated = false; } + + if (glLineColor != 0) { + PGraphicsOpenGL.finalizeVertexBufferObject(glLineColor, lineBuffersContext); + glLineColor = 0; + } + + if (glLineAttrib != 0) { + PGraphicsOpenGL.finalizeVertexBufferObject(glLineAttrib, lineBuffersContext); + glLineAttrib = 0; + } + + if (glLineIndex != 0) { + PGraphicsOpenGL.finalizeVertexBufferObject(glLineIndex, lineBuffersContext); + glLineIndex = 0; + } + + lineBuffersCreated = false; } @@ -1496,21 +1536,21 @@ public class PGraphicsOpenGL extends PGraphics { int sizei = INIT_VERTEX_BUFFER_SIZE * PGL.SIZEOF_INT; int sizex = INIT_INDEX_BUFFER_SIZE * PGL.SIZEOF_INDEX; - glPointVertex = createVertexBufferObject(pointBuffersContext); + glPointVertex = createVertexBufferObject(pointBuffersContext, pgl); pgl.bindBuffer(PGL.ARRAY_BUFFER, glPointVertex); pgl.bufferData(PGL.ARRAY_BUFFER, 3 * sizef, null, PGL.STATIC_DRAW); - glPointColor = createVertexBufferObject(pointBuffersContext); + glPointColor = createVertexBufferObject(pointBuffersContext, pgl); pgl.bindBuffer(PGL.ARRAY_BUFFER, glPointColor); pgl.bufferData(PGL.ARRAY_BUFFER, sizei, null, PGL.STATIC_DRAW); - glPointAttrib = createVertexBufferObject(pointBuffersContext); + glPointAttrib = createVertexBufferObject(pointBuffersContext, pgl); pgl.bindBuffer(PGL.ARRAY_BUFFER, glPointAttrib); pgl.bufferData(PGL.ARRAY_BUFFER, 2 * sizef, null, PGL.STATIC_DRAW); pgl.bindBuffer(PGL.ARRAY_BUFFER, 0); - glPointIndex = createVertexBufferObject(pointBuffersContext); + glPointIndex = createVertexBufferObject(pointBuffersContext, pgl); pgl.bindBuffer(PGL.ELEMENT_ARRAY_BUFFER, glPointIndex); pgl.bufferData(PGL.ELEMENT_ARRAY_BUFFER, sizex, null, PGL.STATIC_DRAW); @@ -1562,28 +1602,34 @@ public class PGraphicsOpenGL extends PGraphics { } - protected void deletePointBuffers() { - if (pointBuffersCreated) { - deleteVertexBufferObject(glPointVertex, pointBuffersContext); + protected void finalizePointBuffers() { + if (glPointVertex != 0) { + PGraphicsOpenGL.finalizeVertexBufferObject(glPointVertex, pointBuffersContext); glPointVertex = 0; - - deleteVertexBufferObject(glPointColor, pointBuffersContext); - glPointColor = 0; - - deleteVertexBufferObject(glPointAttrib, pointBuffersContext); - glPointAttrib = 0; - - deleteVertexBufferObject(glPointIndex, pointBuffersContext); - glPointIndex = 0; - - pointBuffersCreated = false; } + + if (glPointColor != 0) { + PGraphicsOpenGL.finalizeVertexBufferObject(glPointColor, pointBuffersContext); + glPointColor = 0; + } + + if (glPointAttrib != 0) { + PGraphicsOpenGL.finalizeVertexBufferObject(glPointAttrib, pointBuffersContext); + glPointAttrib = 0; + } + + if (glPointIndex != 0) { + PGraphicsOpenGL.finalizeVertexBufferObject(glPointIndex, pointBuffersContext); + glPointIndex = 0; + } + + pointBuffersCreated = false; } @Override public void requestFocus() { // ignore - //pgl.requestFocus(); + pgl.requestFocus(); } @@ -1601,7 +1647,8 @@ public class PGraphicsOpenGL extends PGraphics { public void requestDraw() { if (primarySurface) { if (initialized) { - pgl.requestDraw(); + if (sized) pgl.reinitSurface(); + if (parent.canDraw()) pgl.requestDraw(); } else { initPrimary(); } @@ -1611,6 +1658,13 @@ public class PGraphicsOpenGL extends PGraphics { @Override public void beginDraw() { + if (primarySurface) { + setCurrentPG(this); + } else { + pgl.getGL(getPrimaryPGL()); + getPrimaryPG().setCurrentPG(this); + } + report("top beginDraw()"); if (!checkGLThread()) { @@ -1618,23 +1672,14 @@ public class PGraphicsOpenGL extends PGraphics { } if (drawing) { - PGraphics.showWarning(ALREADY_DRAWING_ERROR); return; } - if (pgCurrent != null && !pgCurrent.primarySurface && - !this.primarySurface) { - // It seems that the user is trying to start another beginDraw()/endDraw() - // block for an offscreen surface, still drawing on another one. - PGraphics.showWarning(NESTED_DRAW_ERROR); - return; - } - - if (!primarySurface && pgPrimary.texCache.containsTexture(this)) { + if (!primarySurface && getPrimaryPG().texCache.containsTexture(this)) { // This offscreen surface is being used as a texture earlier in draw, - // so we should update the rendering up to this point since it will + // so we should update the rendering up to this point since it will be // modified. - pgPrimary.flush(); + getPrimaryPG().flush(); } if (!glParamsRead) { @@ -1649,8 +1694,6 @@ public class PGraphicsOpenGL extends PGraphics { } setDrawDefaults(); // TODO: look at using checkSettings() instead... - - pgCurrent = this; drawing = true; report("bot beginDraw()"); @@ -1662,7 +1705,6 @@ public class PGraphicsOpenGL extends PGraphics { report("top endDraw()"); if (!drawing) { - PGraphics.showWarning(NO_BEGIN_DRAW_ERROR); return; } @@ -1670,7 +1712,7 @@ public class PGraphicsOpenGL extends PGraphics { flush(); if (PGL.SAVE_SURFACE_TO_PIXELS_HACK && - (!pgPrimary.initialized || parent.frameCount == 0)) { + (!getPrimaryPG().initialized || parent.frameCount == 0)) { // Smooth was disabled/enabled at some point during drawing. We save // the current contents of the back buffer (because the buffers haven't // been swapped yet) to the pixels array. The frameCount == 0 condition @@ -1686,12 +1728,10 @@ public class PGraphicsOpenGL extends PGraphics { endOffscreenDraw(); } - if (pgCurrent == pgPrimary) { - // Done with the main surface - pgCurrent = null; + if (primarySurface) { + setCurrentPG(null); } else { - // Done with an offscreen surface, going back to onscreen drawing. - pgCurrent = pgPrimary; + getPrimaryPG().setCurrentPG(getPrimaryPG()); } drawing = false; @@ -1699,6 +1739,37 @@ public class PGraphicsOpenGL extends PGraphics { } + // Factory method + protected PGL createPGL(PGraphicsOpenGL pg) { + return new PJOGL(pg); + } + + + protected PGraphicsOpenGL getPrimaryPG() { + if (primarySurface) { + return this; + } else { + return (PGraphicsOpenGL)parent.g; + } + } + + protected void setCurrentPG(PGraphicsOpenGL pg) { + currentPG = pg; + } + + protected PGraphicsOpenGL getCurrentPG() { + return currentPG; + } + + protected PGL getPrimaryPGL() { + if (primarySurface) { + return pgl; + } else { + return ((PGraphicsOpenGL)parent.g).pgl; + } + } + + @Override public PGL beginPGL() { flush(); @@ -1739,8 +1810,6 @@ public class PGraphicsOpenGL extends PGraphics { pgl.disable(PGL.MULTISAMPLE); } else { pgl.enable(PGL.MULTISAMPLE); - pgl.disable(PGL.POINT_SMOOTH); - pgl.disable(PGL.LINE_SMOOTH); pgl.disable(PGL.POLYGON_SMOOTH); } @@ -1764,10 +1833,33 @@ public class PGraphicsOpenGL extends PGraphics { pgl.depthMask(true); } - currentFramebuffer.bind(); - pgl.drawBuffer(currentFramebuffer.getDefaultDrawBuffer()); + FrameBuffer fb = getCurrentFB(); + if (fb != null) { + fb.bind(); + pgl.drawBuffer(fb.getDefaultDrawBuffer()); + } } + protected void beginBindFramebuffer(int target, int framebuffer) { + // Actually, nothing to do here. + } + + protected void endBindFramebuffer(int target, int framebuffer) { + FrameBuffer fb = getCurrentFB(); + if (framebuffer == 0 && fb != null && fb.glFbo != 0) { + // The user is setting the framebuffer to 0 (screen buffer), but the + // renderer is drawing into an offscreen buffer. + fb.bind(); + } + } + + protected void beginReadPixels() { + beginPixelsOp(OP_READ); + } + + protected void endReadPixels() { + endPixelsOp(); + } protected void beginPixelsOp(int op) { FrameBuffer pixfb = null; @@ -1791,7 +1883,7 @@ public class PGraphicsOpenGL extends PGraphics { if (op == OP_READ) { if (offscreenMultisample) { // Making sure the offscreen FBO is up-to-date - multisampleFramebuffer.copy(offscreenFramebuffer, currentFramebuffer); + multisampleFramebuffer.copyColor(offscreenFramebuffer); } // We always read the screen pixels from the color FBO. pixfb = offscreenFramebuffer; @@ -1804,7 +1896,7 @@ public class PGraphicsOpenGL extends PGraphics { } // Set the framebuffer where the pixel operation shall be carried out. - if (pixfb != currentFramebuffer) { + if (pixfb != getCurrentFB()) { pushFramebuffer(); setFramebuffer(pixfb); pixOpChangedFB = true; @@ -1812,9 +1904,9 @@ public class PGraphicsOpenGL extends PGraphics { // We read from/write to the draw buffer. if (op == OP_READ) { - pgl.readBuffer(currentFramebuffer.getDefaultDrawBuffer()); + pgl.readBuffer(getCurrentFB().getDefaultDrawBuffer()); } else if (op == OP_WRITE) { - pgl.drawBuffer(currentFramebuffer.getDefaultDrawBuffer()); + pgl.drawBuffer(getCurrentFB().getDefaultDrawBuffer()); } pixelsOp = op; @@ -1829,8 +1921,8 @@ public class PGraphicsOpenGL extends PGraphics { } // Restoring default read/draw buffer configuration. - pgl.readBuffer(currentFramebuffer.getDefaultReadBuffer()); - pgl.drawBuffer(currentFramebuffer.getDefaultDrawBuffer()); + pgl.readBuffer(getCurrentFB().getDefaultReadBuffer()); + pgl.drawBuffer(getCurrentFB().getDefaultDrawBuffer()); pixelsOp = OP_NONE; } @@ -2039,13 +2131,12 @@ public class PGraphicsOpenGL extends PGraphics { @Override public void beginShape(int kind) { shape = kind; - curveVertexCount = 0; inGeo.clear(); - breakShape = true; + curveVertexCount = 0; + breakShape = false; defaultEdges = true; - textureImage0 = textureImage; // The superclass method is called to avoid an early flush. super.noTexture(); @@ -2114,6 +2205,7 @@ public class PGraphicsOpenGL extends PGraphics { @Override public void vertex(float x, float y) { vertexImpl(x, y, 0, 0, 0); + if (textureImage != null) PGraphics.showWarning(MISSING_UV_TEXCOORDS_ERROR); } @@ -2126,6 +2218,7 @@ public class PGraphicsOpenGL extends PGraphics { @Override public void vertex(float x, float y, float z) { vertexImpl(x, y, z, 0, 0); + if (textureImage != null) PGraphics.showWarning(MISSING_UV_TEXCOORDS_ERROR); } @@ -2168,17 +2261,16 @@ public class PGraphicsOpenGL extends PGraphics { u, v, scolor, sweight, ambientColor, specularColor, emissiveColor, shininess, - vertexCode()); + VERTEX, vertexBreak()); } - protected int vertexCode() { - int code = VERTEX; + protected boolean vertexBreak() { if (breakShape) { - code = BREAK; breakShape = false; + return true; } - return code; + return false; } @@ -2221,12 +2313,13 @@ public class PGraphicsOpenGL extends PGraphics { tessellator.setInGeometry(inGeo); tessellator.setTessGeometry(tessGeo); tessellator.setFill(fill || textureImage != null); + tessellator.setTexCache(texCache, textureImage); tessellator.setStroke(stroke); tessellator.setStrokeColor(strokeColor); tessellator.setStrokeWeight(strokeWeight); tessellator.setStrokeCap(strokeCap); tessellator.setStrokeJoin(strokeJoin); - tessellator.setTexCache(texCache, textureImage0, textureImage); + tessellator.setRenderer(this); tessellator.setTransform(modelview); tessellator.set3D(is3D()); @@ -2251,8 +2344,6 @@ public class PGraphicsOpenGL extends PGraphics { if (normalMode == NORMAL_MODE_AUTO) inGeo.calcTriangleStripNormals(); tessellator.tessellateTriangleStrip(); } else if (shape == QUAD || shape == QUADS) { - - if (stroke && defaultEdges) inGeo.addQuadsEdges(); if (normalMode == NORMAL_MODE_AUTO) inGeo.calcQuadsNormals(); tessellator.tessellateQuads(); @@ -2261,7 +2352,6 @@ public class PGraphicsOpenGL extends PGraphics { if (normalMode == NORMAL_MODE_AUTO) inGeo.calcQuadStripNormals(); tessellator.tessellateQuadStrip(); } else if (shape == POLYGON) { - if (stroke && defaultEdges) inGeo.addPolygonEdges(mode == CLOSE); tessellator.tessellatePolygon(false, mode == CLOSE, normalMode == NORMAL_MODE_AUTO); } @@ -2277,7 +2367,7 @@ public class PGraphicsOpenGL extends PGraphics { tessellator.setStrokeWeight(strokeWeight); tessellator.setStrokeCap(strokeCap); tessellator.setStrokeJoin(strokeJoin); - tessellator.setTexCache(texCache, textureImage0, textureImage); + tessellator.setTexCache(texCache, textureImage); tessellator.setTransform(modelview); tessellator.set3D(is3D()); @@ -2364,14 +2454,18 @@ public class PGraphicsOpenGL extends PGraphics { protected void flushPolys() { - updatePolyBuffers(lights, texCache.hasTextures); + boolean customShader = polyShader != null; + boolean needNormals = customShader ? polyShader.accessNormals() : false; + boolean needTexCoords = customShader ? polyShader.accessTexCoords() : false; + + updatePolyBuffers(lights, texCache.hasTextures, needNormals, needTexCoords); for (int i = 0; i < texCache.size; i++) { Texture tex = texCache.getTexture(i); // If the renderer is 2D, then lights should always be false, // so no need to worry about that. - BaseShader shader = getPolyShader(lights, tex != null); + PShader shader = getPolyShader(lights, tex != null); shader.bind(); int first = texCache.firstCache[i]; @@ -2402,18 +2496,18 @@ public class PGraphicsOpenGL extends PGraphics { voffset * PGL.SIZEOF_FLOAT); } - if (tex != null) { + if (lights || needNormals) { shader.setNormalAttribute(glPolyNormal, 3, PGL.FLOAT, 0, 3 * voffset * PGL.SIZEOF_FLOAT); + } + + if (tex != null || needTexCoords) { shader.setTexcoordAttribute(glPolyTexcoord, 2, PGL.FLOAT, 0, 2 * voffset * PGL.SIZEOF_FLOAT); shader.setTexture(tex); } - pgl.bindBuffer(PGL.ELEMENT_ARRAY_BUFFER, glPolyIndex); - pgl.drawElements(PGL.TRIANGLES, icount, PGL.INDEX_TYPE, - ioffset * PGL.SIZEOF_INDEX); - pgl.bindBuffer(PGL.ELEMENT_ARRAY_BUFFER, 0); + shader.draw(glPolyIndex, icount, ioffset); } shader.unbind(); @@ -2422,15 +2516,218 @@ public class PGraphicsOpenGL extends PGraphics { } + class Triangle { + int i0, i1, i2; + PImage tex; + float dist; + Triangle(int i0, int i1, int i2, PImage tex, float dist) { + this.i0 = i0; + this.i1 = i1; + this.i2 = i2; + this.tex = tex; + this.dist = dist; + } + } + // Adapted from Ben Van Citters code: + // http://openprocessing.org/sketch/100912 + Triangle[] sortedPolyTriangles = null; + int sortedTriangleCount = 0; + void sortTriangles() { + if (sortedPolyTriangles == null) { + sortedPolyTriangles = new Triangle[512]; + } + + float[] vertices = tessGeo.polyVertices; + short[] indices = tessGeo.polyIndices; + float[] src0 = {0, 0, 0, 0}; + float[] src1 = {0, 0, 0, 0}; + float[] src2 = {0, 0, 0, 0}; + float[] pt0 = {0, 0, 0, 0}; + float[] pt1 = {0, 0, 0, 0}; + float[] pt2 = {0, 0, 0, 0}; + + sortedTriangleCount = 0; + for (int i = 0; i < texCache.size; i++) { + PImage textureImage = texCache.getTextureImage(i); + int first = texCache.firstCache[i]; + int last = texCache.lastCache[i]; + IndexCache cache = tessGeo.polyIndexCache; + for (int n = first; n <= last; n++) { + int ioffset = n == first ? texCache.firstIndex[i] : + cache.indexOffset[n]; + int icount = n == last ? texCache.lastIndex[i] - ioffset + 1 : + cache.indexOffset[n] + cache.indexCount[n] - + ioffset; + int voffset = cache.vertexOffset[n]; + for (int tr = ioffset / 3; tr < (ioffset + icount) / 3; tr++) { + if (sortedPolyTriangles.length == sortedTriangleCount) { + // expand array + int newSize = sortedTriangleCount << 1; + Triangle[] temp = new Triangle[newSize]; + PApplet.arrayCopy(sortedPolyTriangles, 0, temp, 0, newSize); + sortedPolyTriangles = temp; + } + + int i0 = voffset + indices[3 * tr + 0]; + int i1 = voffset + indices[3 * tr + 1]; + int i2 = voffset + indices[3 * tr + 2]; + PApplet.arrayCopy(vertices, 4 * i0, src0, 0, 4); + PApplet.arrayCopy(vertices, 4 * i1, src1, 0, 4); + PApplet.arrayCopy(vertices, 4 * i2, src2, 0, 4); + modelview.mult(src0, pt0); + modelview.mult(src1, pt1); + modelview.mult(src2, pt2); + // add all three verts together and divide... could use another determination + // of the 'depth' of the triangle such as min or max vert dist... + float[] pos = new float[]{(pt0[X] + pt1[X] + pt2[X]) /3, + (pt0[Y] + pt1[Y] + pt2[Y]) /3, + (pt0[Z] + pt1[Z] + pt2[Z]) /3}; + + // pt0, pt1 and pt2 are in eye coordinates since they have been + // multiplied by the modelview matrix. + float d = PApplet.dist(0f, 0f, 0f, pos[0], pos[1], pos[2]); + + Triangle tri = new Triangle(i0, i1, i2, textureImage, d); + sortedPolyTriangles[sortedTriangleCount] = tri; + sortedTriangleCount++; + } + } + } + quickSortTris(0, sortedTriangleCount - 1); + } + + // an 'in-place' implementation of quick I whipped together late at night + // based off of the algorithm found on wikipedia: http://en.wikipedia.org/wiki/Quicksort + private void quickSortTris(int leftI, int rightI) { + if (leftI < rightI) { + int pivotIndex = (leftI + rightI)/2; + int newPivotIndex = partition(leftI,rightI,pivotIndex); + quickSortTris(leftI, newPivotIndex-1); + quickSortTris(newPivotIndex+1, rightI); + } + } + + //part of quicksort + private int partition(int leftIndex, int rightIndex, int pivotIndex) { + float pivotVal = sortedPolyTriangles[pivotIndex].dist; + swapTris(pivotIndex,rightIndex); + int storeIndex = leftIndex; + for(int i = leftIndex; i < rightIndex; i++) + { + if(sortedPolyTriangles[i].dist > pivotVal) + { + swapTris(i,storeIndex); + storeIndex++; + } + } + swapTris(rightIndex,storeIndex); + return storeIndex; + } + + //part of quicksort + private void swapTris(int a, int b) { + Triangle tmp = sortedPolyTriangles[a]; + sortedPolyTriangles[a] = sortedPolyTriangles[b]; + sortedPolyTriangles[b] = tmp; + } + + + void rawPolys() { raw.colorMode(RGB); raw.noStroke(); raw.beginShape(TRIANGLES); + + //sortTriangles(); + float[] vertices = tessGeo.polyVertices; int[] color = tessGeo.polyColors; float[] uv = tessGeo.polyTexCoords; - short[] indices = tessGeo.polyIndices; + short[] indices = tessGeo.polyIndices; // unused [fry] + +/* + sortTriangles(); + for (int i = 0; i < sortedTriangleCount; i++) { + Triangle tri = sortedPolyTriangles[i]; + int i0 = tri.i0; + int i1 = tri.i1; + int i2 = tri.i2; + PImage tex = tri.tex; + + float[] pt0 = {0, 0, 0, 0}; + float[] pt1 = {0, 0, 0, 0}; + float[] pt2 = {0, 0, 0, 0}; + int argb0 = PGL.nativeToJavaARGB(color[i0]); + int argb1 = PGL.nativeToJavaARGB(color[i1]); + int argb2 = PGL.nativeToJavaARGB(color[i2]); + + if (flushMode == FLUSH_CONTINUOUSLY) { + float[] src0 = {0, 0, 0, 0}; + float[] src1 = {0, 0, 0, 0}; + float[] src2 = {0, 0, 0, 0}; + PApplet.arrayCopy(vertices, 4 * i0, src0, 0, 4); + PApplet.arrayCopy(vertices, 4 * i1, src1, 0, 4); + PApplet.arrayCopy(vertices, 4 * i2, src2, 0, 4); + modelview.mult(src0, pt0); + modelview.mult(src1, pt1); + modelview.mult(src2, pt2); + } else { + PApplet.arrayCopy(vertices, 4 * i0, pt0, 0, 4); + PApplet.arrayCopy(vertices, 4 * i1, pt1, 0, 4); + PApplet.arrayCopy(vertices, 4 * i2, pt2, 0, 4); + } + + if (tex != null) { + raw.texture(tex); + if (raw.is3D()) { + raw.fill(argb0); + raw.vertex(pt0[X], pt0[Y], pt0[Z], uv[2 * i0 + 0], uv[2 * i0 + 1]); + raw.fill(argb1); + raw.vertex(pt1[X], pt1[Y], pt1[Z], uv[2 * i1 + 0], uv[2 * i1 + 1]); + raw.fill(argb2); + raw.vertex(pt2[X], pt2[Y], pt2[Z], uv[2 * i2 + 0], uv[2 * i2 + 1]); + } else if (raw.is2D()) { + float sx0 = screenXImpl(pt0[0], pt0[1], pt0[2], pt0[3]); + float sy0 = screenYImpl(pt0[0], pt0[1], pt0[2], pt0[3]); + float sx1 = screenXImpl(pt1[0], pt1[1], pt1[2], pt1[3]); + float sy1 = screenYImpl(pt1[0], pt1[1], pt1[2], pt1[3]); + float sx2 = screenXImpl(pt2[0], pt2[1], pt2[2], pt2[3]); + float sy2 = screenYImpl(pt2[0], pt2[1], pt2[2], pt2[3]); + raw.fill(argb0); + raw.vertex(sx0, sy0, uv[2 * i0 + 0], uv[2 * i0 + 1]); + raw.fill(argb1); + raw.vertex(sx1, sy1, uv[2 * i1 + 0], uv[2 * i1 + 1]); + raw.fill(argb1); + raw.vertex(sx2, sy2, uv[2 * i2 + 0], uv[2 * i2 + 1]); + } + } else { + if (raw.is3D()) { + raw.fill(argb0); + raw.vertex(pt0[X], pt0[Y], pt0[Z]); + raw.fill(argb1); + raw.vertex(pt1[X], pt1[Y], pt1[Z]); + raw.fill(argb2); + raw.vertex(pt2[X], pt2[Y], pt2[Z]); + } else if (raw.is2D()) { + float sx0 = screenXImpl(pt0[0], pt0[1], pt0[2], pt0[3]); + float sy0 = screenYImpl(pt0[0], pt0[1], pt0[2], pt0[3]); + float sx1 = screenXImpl(pt1[0], pt1[1], pt1[2], pt1[3]); + float sy1 = screenYImpl(pt1[0], pt1[1], pt1[2], pt1[3]); + float sx2 = screenXImpl(pt2[0], pt2[1], pt2[2], pt2[3]); + float sy2 = screenYImpl(pt2[0], pt2[1], pt2[2], pt2[3]); + raw.fill(argb0); + raw.vertex(sx0, sy0); + raw.fill(argb1); + raw.vertex(sx1, sy1); + raw.fill(argb2); + raw.vertex(sx2, sy2); + } + } + + } +*/ + for (int i = 0; i < texCache.size; i++) { PImage textureImage = texCache.getTextureImage(i); @@ -2524,6 +2821,7 @@ public class PGraphicsOpenGL extends PGraphics { } } + raw.endShape(); } @@ -2531,7 +2829,7 @@ public class PGraphicsOpenGL extends PGraphics { protected void flushLines() { updateLineBuffers(); - LineShader shader = getLineShader(); + PShader shader = getLineShader(); shader.bind(); IndexCache cache = tessGeo.lineIndexCache; @@ -2547,10 +2845,7 @@ public class PGraphicsOpenGL extends PGraphics { shader.setLineAttribute(glLineAttrib, 4, PGL.FLOAT, 0, 4 * voffset * PGL.SIZEOF_FLOAT); - pgl.bindBuffer(PGL.ELEMENT_ARRAY_BUFFER, glLineIndex); - pgl.drawElements(PGL.TRIANGLES, icount, PGL.INDEX_TYPE, - ioffset * PGL.SIZEOF_INDEX); - pgl.bindBuffer(PGL.ELEMENT_ARRAY_BUFFER, 0); + shader.draw(glLineIndex, icount, ioffset); } shader.unbind(); @@ -2635,7 +2930,7 @@ public class PGraphicsOpenGL extends PGraphics { protected void flushPoints() { updatePointBuffers(); - PointShader shader = getPointShader(); + PShader shader = getPointShader(); shader.bind(); IndexCache cache = tessGeo.pointIndexCache; @@ -2651,10 +2946,7 @@ public class PGraphicsOpenGL extends PGraphics { shader.setPointAttribute(glPointAttrib, 2, PGL.FLOAT, 0, 2 * voffset * PGL.SIZEOF_FLOAT); - pgl.bindBuffer(PGL.ELEMENT_ARRAY_BUFFER, glPointIndex); - pgl.drawElements(PGL.TRIANGLES, icount, PGL.INDEX_TYPE, - ioffset * PGL.SIZEOF_INDEX); - pgl.bindBuffer(PGL.ELEMENT_ARRAY_BUFFER, 0); + shader.draw(glPointIndex, icount, ioffset); } shader.unbind(); @@ -2758,9 +3050,7 @@ public class PGraphicsOpenGL extends PGraphics { inGeo.setNormal(normalX, normalY, normalZ); inGeo.addBezierVertex(x2, y2, z2, x3, y3, z3, - x4, y4, z4, - fill, stroke, bezierDetail, vertexCode(), shape); - + x4, y4, z4, vertexBreak()); } @@ -2786,8 +3076,7 @@ public class PGraphicsOpenGL extends PGraphics { ambientColor, specularColor, emissiveColor, shininess); inGeo.setNormal(normalX, normalY, normalZ); inGeo.addQuadraticVertex(cx, cy, cz, - x3, y3, z3, - fill, stroke, bezierDetail, vertexCode(), shape); + x3, y3, z3, vertexBreak()); } @@ -2812,8 +3101,7 @@ public class PGraphicsOpenGL extends PGraphics { inGeo.setMaterial(fillColor, strokeColor, strokeWeight, ambientColor, specularColor, emissiveColor, shininess); inGeo.setNormal(normalX, normalY, normalZ); - inGeo.addCurveVertex(x, y, z, - fill, stroke, curveDetail, vertexCode(), shape); + inGeo.addCurveVertex(x, y, z, vertexBreak()); } @@ -2904,70 +3192,43 @@ public class PGraphicsOpenGL extends PGraphics { x2, y2, 0, x3, y3, 0, x4, y4, 0, - fill, stroke); - endShape(); - } - - ////////////////////////////////////////////////////////////// - - // RECT - - // public void rectMode(int mode) - - @Override - public void rect(float a, float b, float c, float d) { - beginShape(QUADS); - defaultEdges = false; - normalMode = NORMAL_MODE_SHAPE; - inGeo.setMaterial(fillColor, strokeColor, strokeWeight, - ambientColor, specularColor, emissiveColor, shininess); - inGeo.setNormal(normalX, normalY, normalZ); - inGeo.addRect(a, b, c, d, - fill, stroke, rectMode); + stroke); endShape(); } @Override - public void rect(float a, float b, float c, float d, - float tl, float tr, float br, float bl) { + protected void rectImpl(float x1, float y1, float x2, float y2, + float tl, float tr, float br, float bl) { beginShape(POLYGON); defaultEdges = false; normalMode = NORMAL_MODE_SHAPE; inGeo.setMaterial(fillColor, strokeColor, strokeWeight, ambientColor, specularColor, emissiveColor, shininess); inGeo.setNormal(normalX, normalY, normalZ); - inGeo.addRect(a, b, c, d, - tl, tr, br, bl, - fill, stroke, bezierDetail, rectMode); + inGeo.addRect(x1, y1, x2, y2, tl, tr, br, bl, stroke); endShape(CLOSE); } - // protected void rectImpl(float x1, float y1, float x2, float y2) ////////////////////////////////////////////////////////////// // ELLIPSE - // public void ellipseMode(int mode) - @Override - public void ellipse(float a, float b, float c, float d) { + public void ellipseImpl(float a, float b, float c, float d) { beginShape(TRIANGLE_FAN); defaultEdges = false; normalMode = NORMAL_MODE_SHAPE; inGeo.setMaterial(fillColor, strokeColor, strokeWeight, ambientColor, specularColor, emissiveColor, shininess); inGeo.setNormal(normalX, normalY, normalZ); - inGeo.addEllipse(a, b, c, d, fill, stroke, ellipseMode); + inGeo.addEllipse(a, b, c, d, fill, stroke); endShape(); } - // public void ellipse(float a, float b, float c, float d) - - @Override protected void arcImpl(float x, float y, float w, float h, float start, float stop, int mode) { @@ -3009,6 +3270,10 @@ public class PGraphicsOpenGL extends PGraphics { @Override public void sphere(float r) { + if ((sphereDetailU < 3) || (sphereDetailV < 2)) { + sphereDetail(30); + } + beginShape(TRIANGLES); defaultEdges = false; normalMode = NORMAL_MODE_VERTEX; @@ -3086,7 +3351,11 @@ public class PGraphicsOpenGL extends PGraphics { @Override public void smooth() { - smooth(2); + if (quality < 2) { + smooth(2); + } else { + smooth(quality); + } } @@ -3114,6 +3383,7 @@ public class PGraphicsOpenGL extends PGraphics { lastSmoothCall = parent.frameCount; quality = level; + if (quality == 1) { quality = 0; } @@ -3164,8 +3434,8 @@ public class PGraphicsOpenGL extends PGraphics { pushMatrix(); if (shapeMode == CENTER) { - translate(x - shape.getWidth() / 2, y - shape.getHeight() / 2, z - - shape.getDepth() / 2); + translate(x - shape.getWidth() / 2, y - shape.getHeight() / 2, + z - shape.getDepth() / 2); } else if ((shapeMode == CORNER) || (shapeMode == CORNERS)) { translate(x, y, z); @@ -3277,43 +3547,45 @@ public class PGraphicsOpenGL extends PGraphics { @Override public float textAscent() { - if (textFont == null) { - defaultFontOrDeath("textAscent"); - } - Font font = (Font) textFont.getNative(); - if (font != null) { - FontMetrics metrics = parent.getFontMetrics(font); - return metrics.getAscent(); - } - return super.textAscent(); + if (textFont == null) defaultFontOrDeath("textAscent"); + Object font = textFont.getNative(); + float ascent = 0; + if (font != null) ascent = pgl.getFontAscent(font); + if (ascent == 0) ascent = super.textAscent(); + return ascent; } @Override public float textDescent() { - if (textFont == null) { - defaultFontOrDeath("textAscent"); - } - Font font = (Font) textFont.getNative(); - if (font != null) { - FontMetrics metrics = parent.getFontMetrics(font); - return metrics.getDescent(); - } - return super.textDescent(); + if (textFont == null) defaultFontOrDeath("textAscent"); + Object font = textFont.getNative(); + float descent = 0; + if (font != null) descent = pgl.getFontDescent(font); + if (descent == 0) descent = super.textDescent(); + return descent; } @Override protected float textWidthImpl(char buffer[], int start, int stop) { - Font font = (Font) textFont.getNative(); - if (font != null) { - // maybe should use one of the newer/fancier functions for this? - int length = stop - start; - FontMetrics metrics = parent.getFontMetrics(font); - return metrics.charsWidth(buffer, start, length); - } + Object font = textFont.getNative(); + float twidth = 0; + if (font != null) twidth = pgl.getTextWidth(font, buffer, start, stop); + if (twidth == 0) twidth = super.textWidthImpl(buffer, start, stop); + return twidth; + } - return super.textWidthImpl(buffer, start, stop); + + @Override + public void textSize(float size) { + if (textFont == null) defaultFontOrDeath("textSize", size); + Object font = textFont.getNative(); + if (font != null) { + Object dfont = pgl.getDerivedFont(font, size); + textFont.setNative(dfont); + } + super.textSize(size); } @@ -3324,11 +3596,11 @@ public class PGraphicsOpenGL extends PGraphics { protected void textLineImpl(char buffer[], int start, int stop, float x, float y) { if (textMode == MODEL) { - textTex = pgPrimary.getFontTexture(textFont); + textTex = getFontTexture(textFont); if (textTex == null || textTex.contextIsOutdated()) { - textTex = new FontTexture(pgPrimary, textFont, is3D()); - pgPrimary.setFontTexture(textFont, textTex); + textTex = new FontTexture(this, textFont, is3D()); + setFontTexture(textFont, textTex); } textTex.begin(); @@ -3387,7 +3659,7 @@ public class PGraphicsOpenGL extends PGraphics { if (tinfo == null) { // Adding new glyph to the font texture. - tinfo = textTex.addToTexture(pgPrimary, glyph); + tinfo = textTex.addToTexture(this, glyph); } float high = glyph.height / (float) textFont.getSize(); @@ -3466,20 +3738,19 @@ public class PGraphicsOpenGL extends PGraphics { float lastX = 0; float lastY = 0; + boolean open = false; beginShape(); while (!outline.isDone()) { int type = outline.currentSegment(textPoints); - switch (type) { - case PGL.SEG_MOVETO: // 1 point (2 vars) in textPoints - case PGL.SEG_LINETO: // 1 point - if (type == PGL.SEG_MOVETO) { - beginContour(); - } + if (!open) { + beginContour(); + open = true; + } + if (type == PGL.SEG_MOVETO || type == PGL.SEG_LINETO) { // 1 point vertex(x + textPoints[0], y + textPoints[1]); lastX = textPoints[0]; lastY = textPoints[1]; - break; - case PGL.SEG_QUADTO: // 2 points + } else if (type == PGL.SEG_QUADTO) { // 2 points for (int i = 1; i < bezierDetail; i++) { float t = (float)i / (float)bezierDetail; vertex(x + bezierPoint(lastX, @@ -3493,8 +3764,7 @@ public class PGraphicsOpenGL extends PGraphics { } lastX = textPoints[2]; lastY = textPoints[3]; - break; - case PGL.SEG_CUBICTO: // 3 points + } else if (type == PGL.SEG_CUBICTO) { // 3 points for (int i = 1; i < bezierDetail; i++) { float t = (float)i / (float)bezierDetail; vertex(x + bezierPoint(lastX, textPoints[0], @@ -3504,10 +3774,9 @@ public class PGraphicsOpenGL extends PGraphics { } lastX = textPoints[4]; lastY = textPoints[5]; - break; - case PGL.SEG_CLOSE: + } else if (type == PGL.SEG_CLOSE) { endContour(); - break; + open = false; } outline.next(); } @@ -4217,21 +4486,6 @@ public class PGraphicsOpenGL extends PGraphics { } - // Sets a camera for 2D rendering, which only involves centering - public void camera(float centerX, float centerY) { - modelview.reset(); - modelview.translate(-centerX, -centerY); - - modelviewInv.set(modelview); - modelviewInv.invert(); - - camera.set(modelview); - cameraInv.set(modelviewInv); - - updateProjmodelview(); - } - - /** * Print the current camera matrix. */ @@ -4257,7 +4511,7 @@ public class PGraphicsOpenGL extends PGraphics { */ @Override public void ortho() { - ortho(0, width, 0, height, cameraNear, cameraFar); + ortho(0, width, 0, height, 0, cameraEyeZ * 10); } @@ -4268,7 +4522,7 @@ public class PGraphicsOpenGL extends PGraphics { @Override public void ortho(float left, float right, float bottom, float top) { - ortho(left, right, bottom, top, cameraNear, cameraFar); + ortho(left, right, bottom, top, 0, cameraEyeZ * 10); } @@ -4280,21 +4534,27 @@ public class PGraphicsOpenGL extends PGraphics { public void ortho(float left, float right, float bottom, float top, float near, float far) { - left -= width/2f; - right -= width/2f; - bottom -= height/2f; - top -= height/2f; + float w = right - left; + float h = top - bottom; + float d = far - near; + + // Applying the camera translation (only on x and y, as near and far + // are given as distances from the viewer) + left -= cameraEyeX; + right -= cameraEyeX; + bottom -= cameraEyeY; + top -= cameraEyeY; // Flushing geometry with a different perspective configuration. flush(); - float x = +2.0f / (right - left); - float y = +2.0f / (top - bottom); - float z = -2.0f / (far - near); + float x = +2.0f / w; + float y = +2.0f / h; + float z = -2.0f / d; - float tx = -(right + left) / (right - left); - float ty = -(top + bottom) / (top - bottom); - float tz = -(far + near) / (far - near); + float tx = -(right + left) / w; + float ty = -(top + bottom) / h; + float tz = -(far + near) / d; // The minus sign is needed to invert the Y axis. projection.set(x, 0, 0, tx, @@ -4562,8 +4822,20 @@ public class PGraphicsOpenGL extends PGraphics { return nonZero(ow) ? oz / ow : oz; } + ////////////////////////////////////////////////////////////// + // STYLES + @Override + public void popStyle() { + // popStyle() sets ambient to true (because it calls ambient() in style()) + // and so setting the setAmbient flag to true, even if the user didn't call + // ambient, so need to revert to false. + boolean savedSetAmbient = setAmbient; + super.popStyle(); + if (!savedSetAmbient) setAmbient = false; + } + // public void pushStyle() // public void popStyle() // public void style(PStyle) @@ -5047,6 +5319,11 @@ public class PGraphicsOpenGL extends PGraphics { if (0 < parent.frameCount) { clearColorBuffer = true; } + // Setting the background as opaque. If this an offscreen surface, the + // alpha channel will be set to 1 in endOffscreenDraw(), even if + // blending operations during draw create translucent areas in the + // color buffer. + backgroundA = 1; } @@ -5054,13 +5331,9 @@ public class PGraphicsOpenGL extends PGraphics { protected void backgroundImpl() { flush(); - pgl.depthMask(true); - pgl.clearDepth(1); - pgl.clear(PGL.DEPTH_BUFFER_BIT); - if (hints[DISABLE_DEPTH_MASK]) { - pgl.depthMask(false); - } else { - pgl.depthMask(true); + if (!hints[DISABLE_DEPTH_MASK]) { + pgl.clearDepth(1); + pgl.clear(PGL.DEPTH_BUFFER_BIT); } pgl.clearColor(backgroundR, backgroundG, backgroundB, backgroundA); @@ -5115,7 +5388,7 @@ public class PGraphicsOpenGL extends PGraphics { * Don't use this inside glBegin/glEnd otherwise it'll * throw an GL_INVALID_OPERATION error. */ - public void report(String where) { + protected void report(String where) { if (!hints[DISABLE_OPENGL_ERRORS]) { int err = pgl.getError(); if (err != 0) { @@ -5141,16 +5414,6 @@ public class PGraphicsOpenGL extends PGraphics { } - - ////////////////////////////////////////////////////////////// - - // PIMAGE METHODS - - // getImage - // setCache, getCache, removeCache - // isModified, setModified - - ////////////////////////////////////////////////////////////// // LOAD/UPDATE PIXELS @@ -5241,9 +5504,9 @@ public class PGraphicsOpenGL extends PGraphics { try { if (0 < x || 0 < y || w < width || h < height) { - // The pixels to copy to the texture need to be consecutive, and they - // are not in the pixels array, so putting each row one after another - // in nativePixels. + // The pixels to be copied to the texture need to be consecutive, and + // they are not in the pixels array, so putting each row one after + // another in nativePixels. int offset0 = y * width + x; int offset1 = 0; @@ -5276,12 +5539,12 @@ public class PGraphicsOpenGL extends PGraphics { // non-multisampled FBO, texture is actually the color buffer used by the // color FBO, so with the copy operation we should be done updating the // (off)screen buffer. - // First, copy the pixels to the texture. We don't need to invert the // pixel copy because the texture will be drawn inverted. - + int tw = PApplet.min(texture.glWidth - x, w); + int th = PApplet.min(texture.glHeight - y, h); pgl.copyToTexture(texture.glTarget, texture.glFormat, texture.glName, - x, y, w, h, nativePixelBuffer); + x, y, tw, th, nativePixelBuffer); beginPixelsOp(OP_WRITE); drawTexture(x, y, w, h); endPixelsOp(); @@ -5336,6 +5599,17 @@ public class PGraphicsOpenGL extends PGraphics { setgetPixels = true; super.setImpl(sourceImage, sourceX, sourceY, sourceWidth, sourceHeight, targetX, targetY); + // do we need this? + // see https://github.com/processing/processing/issues/2125 +// if (sourceImage.format == RGB) { +// int targetOffset = targetY * width + targetX; +// for (int y = sourceY; y < sourceY + sourceHeight; y++) { +// for (int x = targetOffset; x < targetOffset + sourceWidth; x++) { +// pixels[x] |= 0xff000000; +// } +// targetOffset += width; +// } +// } } @@ -5344,8 +5618,8 @@ public class PGraphicsOpenGL extends PGraphics { // LOAD/UPDATE TEXTURE - // Copies the contents of the color buffer into the pixels - // array, and then the pixels array into the screen texture. + // Loads the current contents of the renderer's drawing surface into the + // its texture. public void loadTexture() { boolean needEndDraw = false; if (!drawing) { @@ -5382,12 +5656,10 @@ public class PGraphicsOpenGL extends PGraphics { texture.setNative(nativePixelBuffer, 0, 0, width, height); } - } else { - // We need to copy the contents of the multisampled buffer to the - // color buffer, so the later is up-to-date with the last drawing. - if (offscreenMultisample) { - multisampleFramebuffer.copy(offscreenFramebuffer, currentFramebuffer); - } + } else if (offscreenMultisample) { + // We need to copy the contents of the multisampled buffer to the color + // buffer, so the later is up-to-date with the last drawing. + multisampleFramebuffer.copyColor(offscreenFramebuffer); } if (needEndDraw) { @@ -5418,41 +5690,21 @@ public class PGraphicsOpenGL extends PGraphics { } - /* - public void drawTexture(int target, int id, int width, int height, - int X0, int Y0, int X1, int Y1) { - beginPGL(); - pgl.drawTexture(target, id, width, height, X0, Y0, X1, Y1); - endPGL(); - } - - - public void drawTexture(int target, int id, int texW, int texH, - int texX0, int texY0, int texX1, int texY1, - int scrX0, int scrY0, int scrX1, int scrY1) { - beginPGL(); - pgl.drawTexture(target, id, texW, texH, width, height, - texX0, texY0, texX1, texY1, - scrX0, scrY0, scrX1, scrY1); - endPGL(); - } -*/ - protected void loadTextureImpl(int sampling, boolean mipmap) { if (width == 0 || height == 0) return; if (texture == null || texture.contextIsOutdated()) { Texture.Parameters params = new Texture.Parameters(ARGB, sampling, mipmap); - texture = new Texture(width, height, params); + texture = new Texture(this, width, height, params); texture.invertedY(true); texture.colorBuffer(true); - pgPrimary.setCache(this, texture); + setCache(this, texture); } } protected void createPTexture() { - ptexture = new Texture(width, height, texture.getParameters()); + ptexture = new Texture(this, width, height, texture.getParameters()); ptexture.invertedY(true); ptexture.colorBuffer(true); } @@ -5524,12 +5776,13 @@ public class PGraphicsOpenGL extends PGraphics { "the same size as the applet."); } - if (maskShader == null) { - maskShader = new TextureShader(parent, defTextureShaderVertURL, - maskShaderFragURL); + PGraphicsOpenGL ppg = getPrimaryPG(); + if (ppg.maskShader == null) { + ppg.maskShader = new PShader(parent, defTextureShaderVertURL, + maskShaderFragURL); } - maskShader.set("mask", alpha); - filter(maskShader); + ppg.maskShader.set("mask", alpha); + filter(ppg.maskShader); } @@ -5567,7 +5820,7 @@ public class PGraphicsOpenGL extends PGraphics { @Override public void filter(PShader shader) { - if (!(shader instanceof TextureShader)) { + if (!shader.isPolyShader()) { PGraphics.showWarning(INVALID_FILTER_SHADER_ERROR); return; } @@ -5581,7 +5834,7 @@ public class PGraphicsOpenGL extends PGraphics { loadTexture(); if (filterTexture == null || filterTexture.contextIsOutdated()) { - filterTexture = new Texture(texture.width, texture.height, + filterTexture = new Texture(this, texture.width, texture.height, texture.getParameters()); filterTexture.invertedY(true); filterImage = wrapTexture(filterTexture); @@ -5609,8 +5862,8 @@ public class PGraphicsOpenGL extends PGraphics { stroke = false; int prevBlendMode = blendMode; blendMode(REPLACE); - TextureShader prevTexShader = textureShader; - textureShader = (TextureShader) shader; + PShader prevShader = polyShader; + polyShader = shader; beginShape(QUADS); texture(filterImage); @@ -5622,7 +5875,7 @@ public class PGraphicsOpenGL extends PGraphics { end2D(); // Restoring previous configuration. - textureShader = prevTexShader; + polyShader = prevShader; stroke = prevStroke; lights = prevLights; textureMode = prevTextureMode; @@ -5643,59 +5896,88 @@ public class PGraphicsOpenGL extends PGraphics { ////////////////////////////////////////////////////////////// - /** - * Extremely slow and not optimized, should use GL methods instead. Currently - * calls a beginPixels() on the whole canvas, then does the copy, then it - * calls endPixels(). - */ - // public void copy(int sx1, int sy1, int sx2, int sy2, - // int dx1, int dy1, int dx2, int dy2) + // COPY - // public void copy(PImage src, - // int sx1, int sy1, int sx2, int sy2, - // int dx1, int dy1, int dx2, int dy2) + + @Override + public void copy(int sx, int sy, int sw, int sh, + int dx, int dy, int dw, int dh) { + if (primarySurface) pgl.requestFBOLayer(); + loadTexture(); + if (filterTexture == null || filterTexture.contextIsOutdated()) { + filterTexture = new Texture(this, texture.width, texture.height, + texture.getParameters()); + filterTexture.invertedY(true); + filterImage = wrapTexture(filterTexture); + } + filterTexture.put(texture, sx, height - (sy + sh), sw, height - sy); + copy(filterImage, sx, sy, sw, sh, dx, dy, dw, dh); + } + + + @Override + public void copy(PImage src, + int sx, int sy, int sw, int sh, + int dx, int dy, int dw, int dh) { + boolean needEndDraw = false; + if (!drawing) { + beginDraw(); + needEndDraw = true; + } + + flush(); // make sure that the screen contents are up to date. + + Texture tex = getTexture(src); + boolean invX = tex.invertedX(); + boolean invY = tex.invertedY(); + int scrX0, scrX1; + int scrY0, scrY1; + if (invX) { + scrX0 = dx + dw; + scrX1 = dx; + } else { + scrX0 = dx; + scrX1 = dx + dw; + } + + int texX0 = sx; + int texX1 = sx + sw; + int texY0, texY1; + if (invY) { + scrY0 = height - (dy + dh); + scrY1 = height - dy; + texY0 = tex.height - (sy + sh); + texY1 = tex.height - sy; + } else { + // Because drawTexture uses bottom-to-top orientation of Y axis. + scrY0 = height - dy; + scrY1 = height - (dy + dh); + texY0 = sy; + texY1 = sy + sh; + } + + pgl.drawTexture(tex.glTarget, tex.glName, + tex.glWidth, tex.glHeight, width, height, + texX0, texY0, texX1, texY1, + scrX0, scrY0, scrX1, scrY1); + + + if (needEndDraw) { + endDraw(); + } + } ////////////////////////////////////////////////////////////// // BLEND - // static public int blendColor(int c1, int c2, int mode) - - // public void blend(PImage src, - // int sx, int sy, int dx, int dy, int mode) { - // set(dx, dy, PImage.blendColor(src.get(sx, sy), get(dx, dy), mode)); - // } - - - /** - * Extremely slow and not optimized, should use GL methods instead. Currently - * calls a beginPixels() on the whole canvas, then does the copy, then it - * calls endPixels(). Please help fix: Bug 941, Bug 942. - */ - // public void blend(int sx1, int sy1, int sx2, int sy2, - // int dx1, int dy1, int dx2, int dy2, int mode) { - // loadPixels(); - // super.blend(sx1, sy1, sx2, sy2, dx1, dy1, dx2, dy2, mode); - // updatePixels(); - // } - - // public void blend(PImage src, - // int sx1, int sy1, int sx2, int sy2, - // int dx1, int dy1, int dx2, int dy2, int mode) { - // loadPixels(); - // super.blend(src, sx1, sy1, sx2, sy2, dx1, dy1, dx2, dy2, mode); - // updatePixels(); - // } - /** * Allows to set custom blend modes for the entire scene, using openGL. * Reference article about blending modes: * http://www.pegtop.net/delphi/articles/blendmodes/ - * HARD_LIGHT, SOFT_LIGHT, OVERLAY, DODGE, BURN modes cannot be + * DIFFERENCE, HARD_LIGHT, SOFT_LIGHT, OVERLAY, DODGE, BURN modes cannot be * implemented in fixed-function pipeline because they require * conditional blending and non-linear blending equations. */ @@ -5728,9 +6010,11 @@ public class PGraphicsOpenGL extends PGraphics { } else if (blendMode == SUBTRACT) { if (blendEqSupported) { - pgl.blendEquation(PGL.FUNC_ADD); + pgl.blendEquation(PGL.FUNC_REVERSE_SUBTRACT); + pgl.blendFunc(PGL.ONE, PGL.SRC_ALPHA); + } else { + PGraphics.showWarning(BLEND_DRIVER_ERROR, "SUBTRACT"); } - pgl.blendFunc(PGL.ONE_MINUS_DST_COLOR, PGL.ZERO); } else if (blendMode == LIGHTEST) { if (blendEqSupported) { @@ -5748,14 +6032,6 @@ public class PGraphicsOpenGL extends PGraphics { PGraphics.showWarning(BLEND_DRIVER_ERROR, "DARKEST"); } - } else if (blendMode == DIFFERENCE) { - if (blendEqSupported) { - pgl.blendEquation(PGL.FUNC_REVERSE_SUBTRACT); - pgl.blendFunc(PGL.ONE, PGL.ONE); - } else { - PGraphics.showWarning(BLEND_DRIVER_ERROR, "DIFFERENCE"); - } - } else if (blendMode == EXCLUSION) { if (blendEqSupported) { pgl.blendEquation(PGL.FUNC_ADD); @@ -5774,6 +6050,9 @@ public class PGraphicsOpenGL extends PGraphics { } pgl.blendFunc(PGL.ONE_MINUS_DST_COLOR, PGL.ONE); + } else if (blendMode == DIFFERENCE) { + PGraphics.showWarning(BLEND_RENDERER_ERROR, "DIFFERENCE"); + } else if (blendMode == OVERLAY) { PGraphics.showWarning(BLEND_RENDERER_ERROR, "OVERLAY"); @@ -5812,7 +6091,15 @@ public class PGraphicsOpenGL extends PGraphics { * off the screen (or offscreen drawing surface). */ public Texture getTexture() { - loadTexture(); + return getTexture(true); + } + + + /** + * Not an approved function either, don't use it. + */ + public Texture getTexture(boolean load) { + if (load) loadTexture(); return texture; } @@ -5845,12 +6132,30 @@ public class PGraphicsOpenGL extends PGraphics { } + /** + * Not an approved function, test its use in libraries to grab the FB objects + * for offscreen PGraphics. + */ + public FrameBuffer getFrameBuffer() { + return getFrameBuffer(false); + } + + + public FrameBuffer getFrameBuffer(boolean multi) { + if (multi) { + return multisampleFramebuffer; + } else { + return offscreenFramebuffer; + } + } + + protected Object initCache(PImage img) { if (!checkGLThread()) { return null; } - Texture tex = (Texture)pgPrimary.getCache(img); + Texture tex = (Texture)getCache(img); if (tex == null || tex.contextIsOutdated()) { tex = addTexture(img); if (tex != null) { @@ -5903,8 +6208,8 @@ public class PGraphicsOpenGL extends PGraphics { if (img.parent == null) { img.parent = parent; } - Texture tex = new Texture(img.width, img.height, params); - pgPrimary.setCache(img, tex); + Texture tex = new Texture(this, img.width, img.height, params); + setCache(img, tex); return tex; } @@ -5938,18 +6243,22 @@ public class PGraphicsOpenGL extends PGraphics { img.width = tex.width; img.height = tex.height; img.format = ARGB; - pgPrimary.setCache(img, tex); + setCache(img, tex); return img; } protected void updateTexture(PImage img, Texture tex) { if (tex != null) { - int x = img.getModifiedX1(); - int y = img.getModifiedY1(); - int w = img.getModifiedX2() - x; - int h = img.getModifiedY2() - y; - tex.set(img.pixels, x, y, w, h, img.format); + if (img.isModified()) { + int x = img.getModifiedX1(); + int y = img.getModifiedY1(); + int w = img.getModifiedX2() - x; + int h = img.getModifiedY2() - y; + tex.set(img.pixels, x, y, w, h, img.format); + } else if (img.isLoaded()) { + tex.set(img.pixels, 0, 0, img.width, img.height, img.format); + } } img.setModified(false); img.setLoaded(false); @@ -6000,10 +6309,9 @@ public class PGraphicsOpenGL extends PGraphics { protected void initPrimary() { pgl.initSurface(quality); if (texture != null) { - pgPrimary.removeCache(this); + removeCache(this); texture = ptexture = null; } - pgPrimary = this; initialized = true; } @@ -6012,11 +6320,11 @@ public class PGraphicsOpenGL extends PGraphics { pgl.beginDraw(clearColorBuffer); if (drawFramebuffer == null) { - drawFramebuffer = new FrameBuffer(width, height, true); + drawFramebuffer = new FrameBuffer(this, width, height, true); } drawFramebuffer.setFBO(pgl.getDrawFramebuffer()); if (readFramebuffer == null) { - readFramebuffer = new FrameBuffer(width, height, true); + readFramebuffer = new FrameBuffer(this, width, height, true); } readFramebuffer.setFBO(pgl.getReadFramebuffer()); if (currentFramebuffer == null) { @@ -6037,7 +6345,7 @@ public class PGraphicsOpenGL extends PGraphics { protected void initOffscreen() { // Getting the context and capabilities from the main renderer. - loadTextureImpl(Texture.BILINEAR, false); + loadTextureImpl(textureSampling, false); // In case of reinitialization (for example, when the smooth level // is changed), we make sure that all the OpenGL resources associated @@ -6053,7 +6361,7 @@ public class PGraphicsOpenGL extends PGraphics { packedDepthStencilSupported; if (PGraphicsOpenGL.fboMultisampleSupported && 1 < quality) { multisampleFramebuffer = - new FrameBuffer(texture.glWidth, texture.glHeight, quality, 0, + new FrameBuffer(this, texture.glWidth, texture.glHeight, quality, 0, depthBits, stencilBits, packed, false); multisampleFramebuffer.clear(); @@ -6063,13 +6371,13 @@ public class PGraphicsOpenGL extends PGraphics { // to doesn't need depth and stencil buffers since they are part of the // multisampled framebuffer. offscreenFramebuffer = - new FrameBuffer(texture.glWidth, texture.glHeight, 1, 1, 0, 0, + new FrameBuffer(this, texture.glWidth, texture.glHeight, 1, 1, 0, 0, false, false); } else { quality = 0; offscreenFramebuffer = - new FrameBuffer(texture.glWidth, texture.glHeight, 1, 1, + new FrameBuffer(this, texture.glWidth, texture.glHeight, 1, 1, depthBits, stencilBits, packed, false); offscreenMultisample = false; } @@ -6121,14 +6429,22 @@ public class PGraphicsOpenGL extends PGraphics { protected void endOffscreenDraw() { + if (backgroundA == 1) { + // Set alpha channel to opaque in order to match behavior of JAVA2D: + pgl.colorMask(false, false, false, true); + pgl.clearColor(0, 0, 0, backgroundA); + pgl.clear(PGL.COLOR_BUFFER_BIT); + pgl.colorMask(true, true, true, true); + } + if (offscreenMultisample) { - multisampleFramebuffer.copy(offscreenFramebuffer, currentFramebuffer); + multisampleFramebuffer.copyColor(offscreenFramebuffer); } popFramebuffer(); texture.updateTexels(); // Mark all texels in screen texture as modified. - pgPrimary.restoreGL(); + getPrimaryPG().restoreGL(); } @@ -6178,14 +6494,19 @@ public class PGraphicsOpenGL extends PGraphics { } else { pgl.enable(PGL.MULTISAMPLE); } - pgl.disable(PGL.POINT_SMOOTH); - pgl.disable(PGL.LINE_SMOOTH); pgl.disable(PGL.POLYGON_SMOOTH); if (sized) { +// reapplySettings(); + // To avoid having garbage in the screen after a resize, // in the case background is not called in draw(). - background(backgroundColor); + if (primarySurface) { + background(backgroundColor); + } else { + // offscreen surfaces are transparent by default. + background(0x00 << 24 | (backgroundColor & 0xFFFFFF)); + } // Sets the default projection and camera (initializes modelview). // If the user has setup up their own projection, they'll need @@ -6233,9 +6554,7 @@ public class PGraphicsOpenGL extends PGraphics { if (restoreSurface) { restoreSurfaceFromPixels(); - //if (1 < parent.frameCount) { restoreSurface = false; - //} } if (hints[DISABLE_DEPTH_MASK]) { @@ -6261,16 +6580,11 @@ public class PGraphicsOpenGL extends PGraphics { OPENGL_EXTENSIONS = pgl.getString(PGL.EXTENSIONS); GLSL_VERSION = pgl.getString(PGL.SHADING_LANGUAGE_VERSION); - npotTexSupported = - -1 < OPENGL_EXTENSIONS.indexOf("_texture_non_power_of_two"); - autoMipmapGenSupported = - -1 < OPENGL_EXTENSIONS.indexOf("_generate_mipmap"); - fboMultisampleSupported = - -1 < OPENGL_EXTENSIONS.indexOf("_framebuffer_multisample"); - packedDepthStencilSupported = - -1 < OPENGL_EXTENSIONS.indexOf("_packed_depth_stencil"); - anisoSamplingSupported = - -1 < OPENGL_EXTENSIONS.indexOf("_texture_filter_anisotropic"); + npotTexSupported = pgl.hasNpotTexSupport(); + autoMipmapGenSupported = pgl.hasAutoMipmapGenSupport(); + fboMultisampleSupported = pgl.hasFboMultisampleSupport(); + packedDepthStencilSupported = pgl.hasPackedDepthStencilSupport(); + anisoSamplingSupported = pgl.hasAnisoSamplingSupport(); try { pgl.blendEquation(PGL.FUNC_ADD); @@ -6288,12 +6602,6 @@ public class PGraphicsOpenGL extends PGraphics { pgl.getIntegerv(PGL.MAX_SAMPLES, intBuffer); maxSamples = intBuffer.get(0); - pgl.getIntegerv(PGL.ALIASED_LINE_WIDTH_RANGE, intBuffer); - maxLineWidth = intBuffer.get(0); - - pgl.getIntegerv(PGL.ALIASED_POINT_SIZE_RANGE, intBuffer); - maxPointSize = intBuffer.get(0); - if (anisoSamplingSupported) { pgl.getFloatv(PGL.MAX_TEXTURE_MAX_ANISOTROPY, floatBuffer); maxAnisoAmount = floatBuffer.get(0); @@ -6310,133 +6618,77 @@ public class PGraphicsOpenGL extends PGraphics { @Override public PShader loadShader(String fragFilename) { - int shaderType = getShaderType(fragFilename); - if (shaderType == -1) { - PGraphics.showWarning(INVALID_PROCESSING_SHADER_ERROR); + if (fragFilename == null || fragFilename.equals("")) { + PGraphics.showWarning(MISSING_FRAGMENT_SHADER); return null; } - PShader shader = null; - if (shaderType == PShader.POINT) { - shader = new PointShader(parent); - shader.setVertexShader(defPointShaderVertURL); - } else if (shaderType == PShader.LINE) { - shader = new LineShader(parent); - shader.setVertexShader(defLineShaderVertURL); - } else if (shaderType == PShader.TEXLIGHT) { - shader = new TexlightShader(parent); - shader.setVertexShader(defTexlightShaderVertURL); - } else if (shaderType == PShader.LIGHT) { - shader = new LightShader(parent); - shader.setVertexShader(defLightShaderVertURL); - } else if (shaderType == PShader.TEXTURE) { - shader = new TextureShader(parent); - shader.setVertexShader(defTextureShaderVertURL); - } else if (shaderType == PShader.COLOR) { - shader = new ColorShader(parent); - shader.setVertexShader(defColorShaderVertURL); - } + + int type = PShader.getShaderType(parent.loadStrings(fragFilename), + PShader.POLY); + PShader shader = new PShader(parent); + shader.setType(type); shader.setFragmentShader(fragFilename); + if (type == PShader.POINT) { + String[] vertSource = pgl.loadVertexShader(defPointShaderVertURL, 120); + shader.setVertexShader(vertSource); + } else if (type == PShader.LINE) { + String[] vertSource = pgl.loadVertexShader(defLineShaderVertURL, 120); + shader.setVertexShader(vertSource); + } else if (type == PShader.TEXLIGHT) { + String[] vertSource = pgl.loadVertexShader(defTexlightShaderVertURL, 120); + shader.setVertexShader(vertSource); + } else if (type == PShader.LIGHT) { + String[] vertSource = pgl.loadVertexShader(defLightShaderVertURL, 120); + shader.setVertexShader(vertSource); + } else if (type == PShader.TEXTURE) { + String[] vertSource = pgl.loadVertexShader(defTextureShaderVertURL, 120); + shader.setVertexShader(vertSource); + } else if (type == PShader.COLOR) { + String[] vertSource = pgl.loadVertexShader(defColorShaderVertURL, 120); + shader.setVertexShader(vertSource); + } else { + String[] vertSource = pgl.loadVertexShader(defTextureShaderVertURL, 120); + shader.setVertexShader(vertSource); + } return shader; } @Override public PShader loadShader(String fragFilename, String vertFilename) { - int shaderType = getShaderType(vertFilename); - if (shaderType == -1) { - shaderType = getShaderType(fragFilename); - } - if (shaderType == -1) { - PGraphics.showWarning(INVALID_PROCESSING_SHADER_ERROR); - return null; - } - - PShader shader = null; if (fragFilename == null || fragFilename.equals("")) { - if (shaderType == PShader.POINT) { - shader = new PointShader(parent); - shader.setFragmentShader(defPointShaderFragURL); - } else if (shaderType == PShader.LINE) { - shader = new LineShader(parent); - shader.setFragmentShader(defLineShaderFragURL); - } else if (shaderType == PShader.TEXLIGHT) { - shader = new TexlightShader(parent); - shader.setFragmentShader(defTextureShaderFragURL); - } else if (shaderType == PShader.LIGHT) { - shader = new LightShader(parent); - shader.setFragmentShader(defColorShaderFragURL); - } else if (shaderType == PShader.TEXTURE) { - shader = new TextureShader(parent); - shader.setFragmentShader(defTextureShaderFragURL); - } else if (shaderType == PShader.COLOR) { - shader = new ColorShader(parent); - shader.setFragmentShader(defColorShaderFragURL); - } - if (shader != null) { - shader.setVertexShader(vertFilename); - } + PGraphics.showWarning(MISSING_FRAGMENT_SHADER); + return null; + } else if (fragFilename == null || fragFilename.equals("")) { + PGraphics.showWarning(MISSING_VERTEX_SHADER); + return null; } else { - if (shaderType == PShader.POINT) { - shader = new PointShader(parent, vertFilename, fragFilename); - } else if (shaderType == PShader.LINE) { - shader = new LineShader(parent, vertFilename, fragFilename); - } else if (shaderType == PShader.TEXLIGHT) { - shader = new TexlightShader(parent, vertFilename, fragFilename); - } else if (shaderType == PShader.LIGHT) { - shader = new LightShader(parent, vertFilename, fragFilename); - } else if (shaderType == PShader.TEXTURE) { - shader = new TextureShader(parent, vertFilename, fragFilename); - } else if (shaderType == PShader.COLOR) { - shader = new ColorShader(parent, vertFilename, fragFilename); - } + return new PShader(parent, vertFilename, fragFilename); } - return shader; } @Override public void shader(PShader shader) { - shader(shader, POLYGON); + flush(); // Flushing geometry drawn with a different shader. + + if (shader.isPolyShader()) polyShader = shader; + else if (shader.isLineShader()) lineShader = shader; + else if (shader.isPointShader()) pointShader = shader; + else PGraphics.showWarning(UNKNOWN_SHADER_KIND_ERROR); } @Override + // TODO: deprecate this method, the kind arguments is not used anymore public void shader(PShader shader, int kind) { - flush(); // Flushing geometry drawn with a different shader. - - if (kind == TRIANGLES || kind == QUADS || kind == POLYGON) { - if (shader instanceof TextureShader) { - textureShader = (TextureShader) shader; - } else if (shader instanceof ColorShader) { - colorShader = (ColorShader) shader; - } else if (shader instanceof TexlightShader) { - texlightShader = (TexlightShader) shader; - } else if (shader instanceof LightShader) { - lightShader = (LightShader) shader; - } else { - PGraphics.showWarning(WRONG_SHADER_TYPE_ERROR); - } - } else if (kind == LINES) { - if (shader instanceof LineShader) { - lineShader = (LineShader)shader; - } else { - PGraphics.showWarning(WRONG_SHADER_TYPE_ERROR); - } - } else if (kind == POINTS) { - if (shader instanceof PointShader) { - pointShader = (PointShader)shader; - } else { - PGraphics.showWarning(WRONG_SHADER_TYPE_ERROR); - } - } else { - PGraphics.showWarning(UNKNOWN_SHADER_KIND_ERROR); - } + shader(shader); } @Override public void resetShader() { - resetShader(POLYGON); + resetShader(TRIANGLES); } @@ -6445,10 +6697,7 @@ public class PGraphicsOpenGL extends PGraphics { flush(); // Flushing geometry drawn with a different shader. if (kind == TRIANGLES || kind == QUADS || kind == POLYGON) { - textureShader = null; - colorShader = null; - texlightShader = null; - lightShader = null; + polyShader = null; } else if (kind == LINES) { lineShader = null; } else if (kind == POINTS) { @@ -6459,35 +6708,6 @@ public class PGraphicsOpenGL extends PGraphics { } - public void shaderWarnings(boolean enable) { - shaderWarningsEnabled = enable; - } - - - protected int getShaderType(String filename) { - String[] source = parent.loadStrings(filename); - int type = -1; - for (int i = 0; i < source.length; i++) { - String line = source[i].trim(); - - if (line.indexOf("#define PROCESSING_POINT_SHADER") == 0) { - type = PShader.POINT; - } else if (line.indexOf("#define PROCESSING_LINE_SHADER") == 0) { - type = PShader.LINE; - } else if (line.indexOf("#define PROCESSING_COLOR_SHADER") == 0) { - type = PShader.COLOR; - } else if (line.indexOf("#define PROCESSING_LIGHT_SHADER") == 0) { - type = PShader.LIGHT; - } else if (line.indexOf("#define PROCESSING_TEXTURE_SHADER") == 0) { - type = PShader.TEXTURE; - } else if (line.indexOf("#define PROCESSING_TEXLIGHT_SHADER") == 0) { - type = PShader.TEXLIGHT; - } - } - return type; - } - - protected void deleteDefaultShaders() { // The default shaders contains references to the PGraphics object that // creates them, so when restarting the renderer, those references should @@ -6502,116 +6722,88 @@ public class PGraphicsOpenGL extends PGraphics { } - protected BaseShader getPolyShader(boolean lit, boolean tex) { - BaseShader shader; + protected PShader getPolyShader(boolean lit, boolean tex) { + PShader shader; + PGraphicsOpenGL ppg = getPrimaryPG(); + boolean useDefault = polyShader == null; + if (polyShader != null) { + polyShader.setRenderer(this); + polyShader.loadAttributes(); + polyShader.loadUniforms(); + } if (lit) { if (tex) { - if (texlightShader == null) { - if (defTexlightShader == null) { - defTexlightShader = new TexlightShader(parent, - defTexlightShaderVertURL, - defTextureShaderFragURL); + if (useDefault || !polyShader.checkPolyType(PShader.TEXLIGHT)) { + if (ppg.defTexlightShader == null) { + String[] vertSource = pgl.loadVertexShader(defTexlightShaderVertURL, 120); + String[] fragSource = pgl.loadFragmentShader(defTextureShaderFragURL, 120); + ppg.defTexlightShader = new PShader(parent, vertSource, fragSource); } - shader = defTexlightShader; - texlightShaderCheck(); + shader = ppg.defTexlightShader; } else { - shader = texlightShader; + shader = polyShader; } } else { - if (lightShader == null) { - if (defLightShader == null) { - defLightShader = new LightShader(parent, - defLightShaderVertURL, - defColorShaderFragURL); + if (useDefault || !polyShader.checkPolyType(PShader.LIGHT)) { + if (ppg.defLightShader == null) { + String[] vertSource = pgl.loadVertexShader(defLightShaderVertURL, 120); + String[] fragSource = pgl.loadFragmentShader(defColorShaderFragURL, 120); + ppg.defLightShader = new PShader(parent, vertSource, fragSource); } - shader = defLightShader; - lightShaderCheck(); + shader = ppg.defLightShader; } else { - shader = lightShader; + shader = polyShader; } } } else { + if (polyShader != null && polyShader.accessLightAttribs()) { + PGraphics.showWarning(SHADER_NEED_LIGHT_ATTRIBS); + useDefault = true; + } + if (tex) { - if (textureShader == null) { - if (defTextureShader == null) { - defTextureShader = new TextureShader(parent, - defTextureShaderVertURL, - defTextureShaderFragURL); + if (useDefault || !polyShader.checkPolyType(PShader.TEXTURE)) { + if (ppg.defTextureShader == null) { + String[] vertSource = pgl.loadVertexShader(defTextureShaderVertURL, 120); + String[] fragSource = pgl.loadFragmentShader(defTextureShaderFragURL, 120); + ppg.defTextureShader = new PShader(parent, vertSource, fragSource); } - shader = defTextureShader; - textureShaderCheck(); + shader = ppg.defTextureShader; } else { - shader = textureShader; + shader = polyShader; } } else { - if (colorShader == null) { - if (defColorShader == null) { - defColorShader = new ColorShader(parent, - defColorShaderVertURL, - defColorShaderFragURL); + if (useDefault || !polyShader.checkPolyType(PShader.COLOR)) { + if (ppg.defColorShader == null) { + String[] vertSource = pgl.loadVertexShader(defColorShaderVertURL, 120); + String[] fragSource = pgl.loadFragmentShader(defColorShaderFragURL, 120); + ppg.defColorShader = new PShader(parent, vertSource, fragSource); } - shader = defColorShader; - colorShaderCheck(); + shader = ppg.defColorShader; } else { - shader = colorShader; + shader = polyShader; } } } - shader.setRenderer(this); - shader.loadAttributes(); - shader.loadUniforms(); + if (shader != polyShader) { + shader.setRenderer(this); + shader.loadAttributes(); + shader.loadUniforms(); + } return shader; } - protected void texlightShaderCheck() { - if (shaderWarningsEnabled && - (lightShader != null || - textureShader != null || - colorShader != null)) { - PGraphics.showWarning(NO_TEXLIGHT_SHADER_ERROR); - } - } - - - protected void lightShaderCheck() { - if (shaderWarningsEnabled && - (texlightShader != null || - textureShader != null || - colorShader != null)) { - PGraphics.showWarning(NO_LIGHT_SHADER_ERROR); - } - } - - - protected void textureShaderCheck() { - if (shaderWarningsEnabled && - (texlightShader != null || - lightShader != null || - colorShader != null)) { - PGraphics.showWarning(NO_TEXTURE_SHADER_ERROR); - } - } - - - protected void colorShaderCheck() { - if (shaderWarningsEnabled && - (texlightShader != null || - lightShader != null || - textureShader != null)) { - PGraphics.showWarning(NO_COLOR_SHADER_ERROR); - } - } - - - protected LineShader getLineShader() { - LineShader shader; + protected PShader getLineShader() { + PShader shader; + PGraphicsOpenGL ppg = getPrimaryPG(); if (lineShader == null) { - if (defLineShader == null) { - defLineShader = new LineShader(parent, defLineShaderVertURL, - defLineShaderFragURL); + if (ppg.defLineShader == null) { + String[] vertSource = pgl.loadVertexShader(defLineShaderVertURL, 120); + String[] fragSource = pgl.loadFragmentShader(defLineShaderFragURL, 120); + ppg.defLineShader = new PShader(parent, vertSource, fragSource); } - shader = defLineShader; + shader = ppg.defLineShader; } else { shader = lineShader; } @@ -6622,14 +6814,16 @@ public class PGraphicsOpenGL extends PGraphics { } - protected PointShader getPointShader() { - PointShader shader; + protected PShader getPointShader() { + PShader shader; + PGraphicsOpenGL ppg = getPrimaryPG(); if (pointShader == null) { - if (defPointShader == null) { - defPointShader = new PointShader(parent, defPointShaderVertURL, - defPointShaderFragURL); + if (ppg.defPointShader == null) { + String[] vertSource = pgl.loadVertexShader(defPointShaderVertURL, 120); + String[] fragSource = pgl.loadFragmentShader(defPointShaderFragURL, 120); + ppg.defPointShader = new PShader(parent, vertSource, fragSource); } - shader = defPointShader; + shader = ppg.defPointShader; } else { shader = pointShader; } @@ -6640,765 +6834,6 @@ public class PGraphicsOpenGL extends PGraphics { } - protected class BaseShader extends PShader { - protected int transformLoc; - protected int modelviewLoc; - protected int projectionLoc; - protected int bufferLoc; - protected int bufferUnit; - protected int viewportLoc; - - public BaseShader(PApplet parent) { - super(parent); - } - - public BaseShader(PApplet parent, String vertFilename, String fragFilename) { - super(parent, vertFilename, fragFilename); - } - - public BaseShader(PApplet parent, URL vertURL, URL fragURL) { - super(parent, vertURL, fragURL); - } - - @Override - public void loadUniforms() { - transformLoc = getUniformLoc("transform"); - modelviewLoc = getUniformLoc("modelview"); - projectionLoc = getUniformLoc("projection"); - viewportLoc = getUniformLoc("viewport"); - bufferLoc = getUniformLoc("buffer"); - } - - @Override - public void unbind() { - if (-1 < bufferLoc) { - pgl.requestFBOLayer(); - pgl.activeTexture(PGL.TEXTURE0 + bufferUnit); - pgCurrent.unbindFrontTexture(); - pgl.activeTexture(PGL.TEXTURE0); - } - - pgl.bindBuffer(PGL.ARRAY_BUFFER, 0); - - super.unbind(); - } - - protected void setCommonUniforms() { - if (-1 < transformLoc) { - pgCurrent.updateGLProjmodelview(); - setUniformMatrix(transformLoc, pgCurrent.glProjmodelview); - } - - if (-1 < modelviewLoc) { - pgCurrent.updateGLModelview(); - setUniformMatrix(modelviewLoc, pgCurrent.glModelview); - } - - if (-1 < projectionLoc) { - pgCurrent.updateGLProjection(); - setUniformMatrix(projectionLoc, pgCurrent.glProjection); - } - - if (-1 < viewportLoc) { - float x = pgCurrent.viewport.get(0); - float y = pgCurrent.viewport.get(1); - float w = pgCurrent.viewport.get(2); - float h = pgCurrent.viewport.get(3); - setUniformValue(viewportLoc, x, y, w, h); - } - - if (-1 < bufferLoc) { - bufferUnit = getLastTexUnit() + 1; - setUniformValue(bufferLoc, bufferUnit); - pgl.activeTexture(PGL.TEXTURE0 + bufferUnit); - pgCurrent.bindFrontTexture(); - } else { - bufferUnit = -1; - } - } - - public void setVertexAttribute(int vboId, int size, int type, - int stride, int offset) { } - public void setColorAttribute(int vboId, int size, int type, - int stride, int offset) { } - public void setNormalAttribute(int vboId, int size, int type, - int stride, int offset) { } - public void setAmbientAttribute(int vboId, int size, int type, - int stride, int offset) { } - public void setSpecularAttribute(int vboId, int size, int type, - int stride, int offset) { } - public void setEmissiveAttribute(int vboId, int size, int type, - int stride, int offset) { } - public void setShininessAttribute(int vboId, int size, int type, - int stride, int offset) { } - public void setTexcoordAttribute(int vboId, int size, int type, - int stride, int offset) { } - public void setTexture(Texture tex) { } - } - - - protected class ColorShader extends BaseShader { - protected int vertexLoc; - protected int colorLoc; - - public ColorShader(PApplet parent) { - super(parent); - } - - public ColorShader(PApplet parent, String vertFilename, - String fragFilename) { - super(parent, vertFilename, fragFilename); - } - - public ColorShader(PApplet parent, URL vertURL, URL fragURL) { - super(parent, vertURL, fragURL); - } - - @Override - public void loadAttributes() { - vertexLoc = getAttributeLoc("vertex"); - colorLoc = getAttributeLoc("color"); - } - - @Override - public void loadUniforms() { - super.loadUniforms(); - } - - @Override - public void setVertexAttribute(int vboId, int size, int type, - int stride, int offset) { - setAttributeVBO(vertexLoc, vboId, size, type, false, stride, offset); - } - - @Override - public void setColorAttribute(int vboId, int size, int type, - int stride, int offset) { - setAttributeVBO(colorLoc, vboId, size, type, true, stride, offset); - } - - @Override - public void bind() { - super.bind(); - if (pgCurrent == null) { - setRenderer(PGraphicsOpenGL.pgCurrent); - loadAttributes(); - loadUniforms(); - } - - if (-1 < vertexLoc) pgl.enableVertexAttribArray(vertexLoc); - if (-1 < colorLoc) pgl.enableVertexAttribArray(colorLoc); - - setCommonUniforms(); - } - - @Override - public void unbind() { - if (-1 < vertexLoc) pgl.disableVertexAttribArray(vertexLoc); - if (-1 < colorLoc) pgl.disableVertexAttribArray(colorLoc); - - super.unbind(); - } - } - - - protected class LightShader extends BaseShader { - protected int normalMatrixLoc; - - protected int lightCountLoc; - protected int lightPositionLoc; - protected int lightNormalLoc; - protected int lightAmbientLoc; - protected int lightDiffuseLoc; - protected int lightSpecularLoc; - protected int lightFalloffLoc; - protected int lightSpotLoc; - - protected int vertexLoc; - protected int colorLoc; - protected int normalLoc; - - protected int ambientLoc; - protected int specularLoc; - protected int emissiveLoc; - protected int shininessLoc; - - public LightShader(PApplet parent) { - super(parent); - } - - public LightShader(PApplet parent, String vertFilename, - String fragFilename) { - super(parent, vertFilename, fragFilename); - } - - public LightShader(PApplet parent, URL vertURL, URL fragURL) { - super(parent, vertURL, fragURL); - } - - @Override - public void loadAttributes() { - vertexLoc = getAttributeLoc("vertex"); - colorLoc = getAttributeLoc("color"); - normalLoc = getAttributeLoc("normal"); - - ambientLoc = getAttributeLoc("ambient"); - specularLoc = getAttributeLoc("specular"); - emissiveLoc = getAttributeLoc("emissive"); - shininessLoc = getAttributeLoc("shininess"); - } - - @Override - public void loadUniforms() { - super.loadUniforms(); - - normalMatrixLoc = getUniformLoc("normalMatrix"); - - lightCountLoc = getUniformLoc("lightCount"); - lightPositionLoc = getUniformLoc("lightPosition"); - lightNormalLoc = getUniformLoc("lightNormal"); - lightAmbientLoc = getUniformLoc("lightAmbient"); - lightDiffuseLoc = getUniformLoc("lightDiffuse"); - lightSpecularLoc = getUniformLoc("lightSpecular"); - lightFalloffLoc = getUniformLoc("lightFalloff"); - lightSpotLoc = getUniformLoc("lightSpot"); - } - - @Override - public void setVertexAttribute(int vboId, int size, int type, - int stride, int offset) { - setAttributeVBO(vertexLoc, vboId, size, type, false, stride, offset); - } - - @Override - public void setColorAttribute(int vboId, int size, int type, - int stride, int offset) { - setAttributeVBO(colorLoc, vboId, size, type, true, stride, offset); - } - - @Override - public void setNormalAttribute(int vboId, int size, int type, - int stride, int offset) { - setAttributeVBO(normalLoc, vboId, size, type, false, stride, offset); - } - - @Override - public void setAmbientAttribute(int vboId, int size, int type, - int stride, int offset) { - setAttributeVBO(ambientLoc, vboId, size, type, true, stride, offset); - } - - @Override - public void setSpecularAttribute(int vboId, int size, int type, - int stride, int offset) { - setAttributeVBO(specularLoc, vboId, size, type, true, stride, offset); - } - - @Override - public void setEmissiveAttribute(int vboId, int size, int type, - int stride, int offset) { - setAttributeVBO(emissiveLoc, vboId, size, type, true, stride, offset); - } - - @Override - public void setShininessAttribute(int vboId, int size, int type, - int stride, int offset) { - setAttributeVBO(shininessLoc, vboId, size, type, false, stride, offset); - } - - @Override - public void bind() { - super.bind(); - if (pgCurrent == null) { - setRenderer(PGraphicsOpenGL.pgCurrent); - loadAttributes(); - loadUniforms(); - } - - if (-1 < vertexLoc) pgl.enableVertexAttribArray(vertexLoc); - if (-1 < colorLoc) pgl.enableVertexAttribArray(colorLoc); - if (-1 < normalLoc) pgl.enableVertexAttribArray(normalLoc); - - if (-1 < ambientLoc) pgl.enableVertexAttribArray(ambientLoc); - if (-1 < specularLoc) pgl.enableVertexAttribArray(specularLoc); - if (-1 < emissiveLoc) pgl.enableVertexAttribArray(emissiveLoc); - if (-1 < shininessLoc) pgl.enableVertexAttribArray(shininessLoc); - - if (-1 < normalMatrixLoc) { - pgCurrent.updateGLNormal(); - setUniformMatrix(normalMatrixLoc, pgCurrent.glNormal); - } - - int count = pgCurrent.lightCount; - setUniformValue(lightCountLoc, count); - setUniformVector(lightPositionLoc, pgCurrent.lightPosition, 4, count); - setUniformVector(lightNormalLoc, pgCurrent.lightNormal, 3, count); - setUniformVector(lightAmbientLoc, pgCurrent.lightAmbient, 3, count); - setUniformVector(lightDiffuseLoc, pgCurrent.lightDiffuse, 3, count); - setUniformVector(lightSpecularLoc, pgCurrent.lightSpecular, 3, count); - setUniformVector(lightFalloffLoc, pgCurrent.lightFalloffCoefficients, - 3, count); - setUniformVector(lightSpotLoc, pgCurrent.lightSpotParameters, 2, count); - - setCommonUniforms(); - } - - @Override - public void unbind() { - if (-1 < vertexLoc) pgl.disableVertexAttribArray(vertexLoc); - if (-1 < colorLoc) pgl.disableVertexAttribArray(colorLoc); - if (-1 < normalLoc) pgl.disableVertexAttribArray(normalLoc); - - if (-1 < ambientLoc) pgl.disableVertexAttribArray(ambientLoc); - if (-1 < specularLoc) pgl.disableVertexAttribArray(specularLoc); - if (-1 < emissiveLoc) pgl.disableVertexAttribArray(emissiveLoc); - if (-1 < shininessLoc) pgl.disableVertexAttribArray(shininessLoc); - - super.unbind(); - } - } - - - protected class TextureShader extends ColorShader { - protected Texture texture; - protected int texUnit; - protected int texCoordLoc; - - protected int textureLoc; - protected int texMatrixLoc; - protected int texOffsetLoc; - - protected int normalMatrixLoc; - protected int normalLoc; - - protected float[] tcmat; - - public TextureShader(PApplet parent) { - super(parent); - } - - public TextureShader(PApplet parent, String vertFilename, - String fragFilename) { - super(parent, vertFilename, fragFilename); - } - - public TextureShader(PApplet parent, URL vertURL, URL fragURL) { - super(parent, vertURL, fragURL); - } - - @Override - public void loadUniforms() { - super.loadUniforms(); - - textureLoc = getUniformLoc("texture"); - texMatrixLoc = getUniformLoc("texMatrix"); - texOffsetLoc = getUniformLoc("texOffset"); - - normalMatrixLoc = getUniformLoc("normalMatrix"); - } - - @Override - public void loadAttributes() { - super.loadAttributes(); - - texCoordLoc = getAttributeLoc("texCoord"); - - normalLoc = getAttributeLoc("normal"); - } - - @Override - public void setNormalAttribute(int vboId, int size, int type, - int stride, int offset) { - setAttributeVBO(normalLoc, vboId, size, type, false, stride, offset); - } - - @Override - public void setTexcoordAttribute(int vboId, int size, int type, - int stride, int offset) { - setAttributeVBO(texCoordLoc, vboId, size, type, false, stride, offset); - } - - @Override - public int getLastTexUnit() { - return -1 < bufferUnit ? bufferUnit : super.getLastTexUnit(); - } - - @Override - public void setTexture(Texture tex) { - float scaleu = 1; - float scalev = 1; - float dispu = 0; - float dispv = 0; - - if (tex.invertedX()) { - scaleu = -1; - dispu = 1; - } - - if (tex.invertedY()) { - scalev = -1; - dispv = 1; - } - - scaleu *= tex.maxTexcoordU(); - dispu *= tex.maxTexcoordU(); - scalev *= tex.maxTexcoordV(); - dispv *= tex.maxTexcoordV(); - - if (-1 < texMatrixLoc) { - if (tcmat == null) { - tcmat = new float[16]; - } - tcmat[0] = scaleu; tcmat[4] = 0; tcmat[ 8] = 0; tcmat[12] = dispu; - tcmat[1] = 0; tcmat[5] = scalev; tcmat[ 9] = 0; tcmat[13] = dispv; - tcmat[2] = 0; tcmat[6] = 0; tcmat[10] = 0; tcmat[14] = 0; - tcmat[3] = 0; tcmat[7] = 0; tcmat[11] = 0; tcmat[15] = 0; - setUniformMatrix(texMatrixLoc, tcmat); - } - - setUniformValue(texOffsetLoc, 1.0f / tex.width, 1.0f / tex.height); - - if (-1 < textureLoc) { - texUnit = getLastTexUnit() + 1; - setUniformValue(textureLoc, texUnit); - pgl.activeTexture(PGL.TEXTURE0 + texUnit); - tex.bind(); - texture = tex; - } - } - - @Override - public void bind() { - super.bind(); - - if (-1 < texCoordLoc) pgl.enableVertexAttribArray(texCoordLoc); - - if (-1 < normalLoc) pgl.enableVertexAttribArray(normalLoc); - if (-1 < normalMatrixLoc) { - pgCurrent.updateGLNormal(); - setUniformMatrix(normalMatrixLoc, pgCurrent.glNormal); - } - } - - @Override - public void unbind() { - if (-1 < texCoordLoc) pgl.disableVertexAttribArray(texCoordLoc); - if (-1 < normalLoc) pgl.disableVertexAttribArray(normalLoc); - - if (-1 < textureLoc && texture != null) { - pgl.activeTexture(PGL.TEXTURE0 + texUnit); - texture.unbind(); - pgl.activeTexture(PGL.TEXTURE0); - texture = null; - } - - super.unbind(); - } - } - - - protected class TexlightShader extends LightShader { - protected Texture texture; - protected int texUnit; - protected int texCoordLoc; - - protected int textureLoc; - protected int texMatrixLoc; - protected int texOffsetLoc; - - protected float[] tcmat; - - public TexlightShader(PApplet parent) { - super(parent); - } - - public TexlightShader(PApplet parent, String vertFilename, - String fragFilename) { - super(parent, vertFilename, fragFilename); - } - - public TexlightShader(PApplet parent, URL vertURL, URL fragURL) { - super(parent, vertURL, fragURL); - } - - @Override - public void loadUniforms() { - super.loadUniforms(); - - textureLoc = getUniformLoc("texture"); - texMatrixLoc = getUniformLoc("texMatrix"); - texOffsetLoc = getUniformLoc("texOffset"); - } - - @Override - public void loadAttributes() { - super.loadAttributes(); - - texCoordLoc = getAttributeLoc("texCoord"); - } - - @Override - public void setTexcoordAttribute(int vboId, int size, int type, - int stride, int offset) { - setAttributeVBO(texCoordLoc, vboId, size, type, false, stride, offset); - } - - @Override - public int getLastTexUnit() { - return -1 < bufferUnit ? bufferUnit : super.getLastTexUnit(); - } - - @Override - public void setTexture(Texture tex) { - float scaleu = 1; - float scalev = 1; - float dispu = 0; - float dispv = 0; - - if (tex.invertedX()) { - scaleu = -1; - dispu = 1; - } - - if (tex.invertedY()) { - scalev = -1; - dispv = 1; - } - - scaleu *= tex.maxTexcoordU; - dispu *= tex.maxTexcoordU; - scalev *= tex.maxTexcoordV; - dispv *= tex.maxTexcoordV; - - if (-1 < texMatrixLoc) { - if (tcmat == null) { - tcmat = new float[16]; - } - tcmat[0] = scaleu; tcmat[4] = 0; tcmat[ 8] = 0; tcmat[12] = dispu; - tcmat[1] = 0; tcmat[5] = scalev; tcmat[ 9] = 0; tcmat[13] = dispv; - tcmat[2] = 0; tcmat[6] = 0; tcmat[10] = 0; tcmat[14] = 0; - tcmat[3] = 0; tcmat[7] = 0; tcmat[11] = 0; tcmat[15] = 0; - setUniformMatrix(texMatrixLoc, tcmat); - } - - setUniformValue(texOffsetLoc, 1.0f / tex.width, 1.0f / tex.height); - - if (-1 < textureLoc) { - texUnit = getLastTexUnit() + 1; - setUniformValue(textureLoc, texUnit); - pgl.activeTexture(PGL.TEXTURE0 + texUnit); - tex.bind(); - texture = tex; - } - } - - @Override - public void bind() { - super.bind(); - - if (-1 < texCoordLoc) pgl.enableVertexAttribArray(texCoordLoc); - } - - @Override - public void unbind() { - if (-1 < texCoordLoc) pgl.disableVertexAttribArray(texCoordLoc); - - if (-1 < textureLoc && texture != null) { - pgl.activeTexture(PGL.TEXTURE0 + texUnit); - texture.unbind(); - pgl.activeTexture(PGL.TEXTURE0); - texture = null; - } - - super.unbind(); - } - } - - - protected class LineShader extends BaseShader { - protected int perspectiveLoc; - protected int scaleLoc; - - protected int vertexLoc; - protected int colorLoc; - protected int directionLoc; - - public LineShader(PApplet parent) { - super(parent); - } - - public LineShader(PApplet parent, String vertFilename, - String fragFilename) { - super(parent, vertFilename, fragFilename); - } - - public LineShader(PApplet parent, URL vertURL, URL fragURL) { - super(parent, vertURL, fragURL); - } - - @Override - public void loadAttributes() { - vertexLoc = getAttributeLoc("vertex"); - colorLoc = getAttributeLoc("color"); - directionLoc = getAttributeLoc("direction"); - } - - @Override - public void loadUniforms() { - super.loadUniforms(); - - viewportLoc = getUniformLoc("viewport"); - perspectiveLoc = getUniformLoc("perspective"); - scaleLoc = getUniformLoc("scale"); - } - - @Override - public void setVertexAttribute(int vboId, int size, int type, - int stride, int offset) { - setAttributeVBO(vertexLoc, vboId, size, type, false, stride, offset); - } - - @Override - public void setColorAttribute(int vboId, int size, int type, - int stride, int offset) { - setAttributeVBO(colorLoc, vboId, size, type, true, stride, offset); - } - - public void setLineAttribute(int vboId, int size, int type, - int stride, int offset) { - setAttributeVBO(directionLoc, vboId, size, type, false, stride, offset); - } - - @Override - public void bind() { - super.bind(); - if (pgCurrent == null) { - setRenderer(PGraphicsOpenGL.pgCurrent); - loadAttributes(); - loadUniforms(); - } - - if (-1 < vertexLoc) pgl.enableVertexAttribArray(vertexLoc); - if (-1 < colorLoc) pgl.enableVertexAttribArray(colorLoc); - if (-1 < directionLoc) pgl.enableVertexAttribArray(directionLoc); - - if (pgCurrent.getHint(ENABLE_STROKE_PERSPECTIVE) && - pgCurrent.nonOrthoProjection()) { - setUniformValue(perspectiveLoc, 1); - } else { - setUniformValue(perspectiveLoc, 0); - } - - if (pgCurrent.getHint(DISABLE_OPTIMIZED_STROKE)) { - setUniformValue(scaleLoc, 1.0f, 1.0f, 1.0f); - } else { - float f = PGL.STROKE_DISPLACEMENT; - if (orthoProjection()) { - setUniformValue(scaleLoc, 1, 1, f); - } else { - setUniformValue(scaleLoc, f, f, f); - } - } - - setCommonUniforms(); - } - - @Override - public void unbind() { - if (-1 < vertexLoc) pgl.disableVertexAttribArray(vertexLoc); - if (-1 < colorLoc) pgl.disableVertexAttribArray(colorLoc); - if (-1 < directionLoc) pgl.disableVertexAttribArray(directionLoc); - - super.unbind(); - } - } - - - protected class PointShader extends BaseShader { - protected int perspectiveLoc; - - protected int vertexLoc; - protected int colorLoc; - protected int offsetLoc; - - public PointShader(PApplet parent) { - super(parent); - } - - public PointShader(PApplet parent, String vertFilename, - String fragFilename) { - super(parent, vertFilename, fragFilename); - } - - public PointShader(PApplet parent, URL vertURL, URL fragURL) { - super(parent, vertURL, fragURL); - } - - @Override - public void loadAttributes() { - vertexLoc = getAttributeLoc("vertex"); - colorLoc = getAttributeLoc("color"); - offsetLoc = getAttributeLoc("offset"); - } - - @Override - public void loadUniforms() { - super.loadUniforms(); - - perspectiveLoc = getUniformLoc("perspective"); - } - - @Override - public void setVertexAttribute(int vboId, int size, int type, - int stride, int offset) { - setAttributeVBO(vertexLoc, vboId, size, type, false, stride, offset); - } - - @Override - public void setColorAttribute(int vboId, int size, int type, - int stride, int offset) { - setAttributeVBO(colorLoc, vboId, size, type, true, stride, offset); - } - - public void setPointAttribute(int vboId, int size, int type, - int stride, int offset) { - setAttributeVBO(offsetLoc, vboId, size, type, false, stride, offset); - } - - @Override - public void bind() { - super.bind(); - if (pgCurrent == null) { - setRenderer(PGraphicsOpenGL.pgCurrent); - loadAttributes(); - loadUniforms(); - } - - if (-1 < vertexLoc) pgl.enableVertexAttribArray(vertexLoc); - if (-1 < colorLoc) pgl.enableVertexAttribArray(colorLoc); - if (-1 < offsetLoc) pgl.enableVertexAttribArray(offsetLoc); - - if (pgCurrent.getHint(ENABLE_STROKE_PERSPECTIVE) && - pgCurrent.nonOrthoProjection()) { - setUniformValue(perspectiveLoc, 1); - } else { - setUniformValue(perspectiveLoc, 0); - } - - super.setCommonUniforms(); - } - - @Override - public void unbind() { - if (-1 < vertexLoc) pgl.disableVertexAttribArray(vertexLoc); - if (-1 < colorLoc) pgl.disableVertexAttribArray(colorLoc); - if (-1 < offsetLoc) pgl.disableVertexAttribArray(offsetLoc); - - super.unbind(); - } - } - - ////////////////////////////////////////////////////////////// // Utils @@ -7416,24 +6851,25 @@ public class PGraphicsOpenGL extends PGraphics { // Input (raw) and Tessellated geometry, tessellator. - protected InGeometry newInGeometry(int mode) { - return new InGeometry(mode); + static protected InGeometry newInGeometry(PGraphicsOpenGL pg, int mode) { + return new InGeometry(pg, mode); } - protected TessGeometry newTessGeometry(int mode) { - return new TessGeometry(mode); + static protected TessGeometry newTessGeometry(PGraphicsOpenGL pg, int mode) { + return new TessGeometry(pg, mode); } - protected TexCache newTexCache() { - return new TexCache(); + static protected TexCache newTexCache(PGraphicsOpenGL pg) { + return new TexCache(pg); } // Holds an array of textures and the range of vertex // indices each texture applies to. - protected class TexCache { + static protected class TexCache { + PGraphicsOpenGL pg; int size; PImage[] textures; int[] firstIndex; @@ -7442,7 +6878,8 @@ public class PGraphicsOpenGL extends PGraphics { int[] lastCache; boolean hasTextures; - TexCache() { + TexCache(PGraphicsOpenGL pg) { + this.pg = pg; allocate(); } @@ -7478,7 +6915,7 @@ public class PGraphicsOpenGL extends PGraphics { Texture tex = null; if (img != null) { - tex = pgPrimary.getTexture(img); + tex = pg.getTexture(img); } return tex; @@ -7551,7 +6988,7 @@ public class PGraphicsOpenGL extends PGraphics { // Stores the offsets and counts of indices and vertices // to render a piece of geometry that doesn't fit in a single // glDrawElements() call. - protected class IndexCache { + static protected class IndexCache { int size; int[] indexCount; int[] indexOffset; @@ -7656,23 +7093,14 @@ public class PGraphicsOpenGL extends PGraphics { // Holds the input vertices: xyz coordinates, fill/tint color, // normal, texture coordinates and stroke color and weight. - protected class InGeometry { + static protected class InGeometry { + PGraphicsOpenGL pg; int renderMode; + int vertexCount; + int codeCount; int edgeCount; - // Range of vertices that will be processed by the - // tessellator. They can be used in combination with the - // edges array to have the tessellator using only a specific - // range of vertices to generate fill geometry, while the - // line geometry will be read from the edge vertices, which - // could be completely different. - int firstVertex; - int lastVertex; - - int firstEdge; - int lastEdge; - float[] vertices; int[] colors; float[] normals; @@ -7680,8 +7108,10 @@ public class PGraphicsOpenGL extends PGraphics { int[] strokeColors; float[] strokeWeights; - // lines - boolean[] breaks; + // vertex codes + int[] codes; + + // Stroke edges int[][] edges; // Material properties @@ -7700,7 +7130,8 @@ public class PGraphicsOpenGL extends PGraphics { float shininessFactor; float normalX, normalY, normalZ; - InGeometry(int mode) { + InGeometry(PGraphicsOpenGL pg, int mode) { + this.pg = pg; renderMode = mode; allocate(); } @@ -7710,12 +7141,13 @@ public class PGraphicsOpenGL extends PGraphics { // Allocate/dispose void clear() { - vertexCount = firstVertex = lastVertex = 0; - edgeCount = firstEdge = lastEdge = 0; + vertexCount = 0; + codeCount = 0; + edgeCount = 0; } void clearEdges() { - edgeCount = firstEdge = lastEdge = 0; + edgeCount = 0; } void allocate() { @@ -7729,7 +7161,6 @@ public class PGraphicsOpenGL extends PGraphics { specular = new int[PGL.DEFAULT_IN_VERTICES]; emissive = new int[PGL.DEFAULT_IN_VERTICES]; shininess = new float[PGL.DEFAULT_IN_VERTICES]; - breaks = new boolean[PGL.DEFAULT_IN_VERTICES]; edges = new int[PGL.DEFAULT_IN_EDGES][3]; clear(); @@ -7749,7 +7180,14 @@ public class PGraphicsOpenGL extends PGraphics { expandSpecular(newSize); expandEmissive(newSize); expandShininess(newSize); - expandBreaks(newSize); + } + } + + void codeCheck() { + if (codeCount == codes.length) { + int newLen = codeCount << 1; + + expandCodes(newLen); } } @@ -7791,17 +7229,17 @@ public class PGraphicsOpenGL extends PGraphics { int getNumEdgeClosures() { int count = 0; - for (int i = firstEdge; i <= lastEdge; i++) { + for (int i = 0; i < edgeCount; i++) { if (edges[i][2] == EDGE_CLOSE) count++; } return count; } int getNumEdgeVertices(boolean bevel) { - int segVert = lastEdge - firstEdge + 1; + int segVert = edgeCount; int bevVert = 0; if (bevel) { - for (int i = firstEdge; i <= lastEdge; i++) { + for (int i = 0; i < edgeCount; i++) { int[] edge = edges[i]; if (edge[2] == EDGE_MIDDLE || edge[2] == EDGE_START) bevVert++; if (edge[2] == EDGE_CLOSE) segVert--; @@ -7813,10 +7251,10 @@ public class PGraphicsOpenGL extends PGraphics { } int getNumEdgeIndices(boolean bevel) { - int segInd = lastEdge - firstEdge + 1; + int segInd = edgeCount; int bevInd = 0; if (bevel) { - for (int i = firstEdge; i <= lastEdge; i++) { + for (int i = 0; i < edgeCount; i++) { int[] edge = edges[i]; if (edge[2] == EDGE_MIDDLE || edge[2] == EDGE_START) bevInd++; if (edge[2] == EDGE_CLOSE) segInd--; @@ -7922,10 +7360,10 @@ public class PGraphicsOpenGL extends PGraphics { shininess = temp; } - void expandBreaks(int n) { - boolean temp[] = new boolean[n]; - PApplet.arrayCopy(breaks, 0, temp, 0, vertexCount); - breaks = temp; + void expandCodes(int n) { + int temp[] = new int[n]; + PApplet.arrayCopy(codes, 0, temp, 0, codeCount); + codes = temp; } void expandEdges(int n) { @@ -7950,7 +7388,10 @@ public class PGraphicsOpenGL extends PGraphics { trimSpecular(); trimEmissive(); trimShininess(); - trimBreaks(); + } + + if (0 < codeCount && codeCount < codes.length) { + trimCodes(); } if (0 < edgeCount && edgeCount < edges.length) { @@ -8018,10 +7459,10 @@ public class PGraphicsOpenGL extends PGraphics { shininess = temp; } - void trimBreaks() { - boolean temp[] = new boolean[vertexCount]; - PApplet.arrayCopy(breaks, 0, temp, 0, vertexCount); - breaks = temp; + void trimCodes() { + int temp[] = new int[codeCount]; + PApplet.arrayCopy(codes, 0, temp, 0, codeCount); + codes = temp; } void trimEdges() { @@ -8034,8 +7475,12 @@ public class PGraphicsOpenGL extends PGraphics { // // Vertices + int addVertex(float x, float y, boolean brk) { + return addVertex(x, y, VERTEX, brk); + } + int addVertex(float x, float y, - int code) { + int code, boolean brk) { return addVertex(x, y, 0, fillColor, normalX, normalY, normalZ, @@ -8043,12 +7488,18 @@ public class PGraphicsOpenGL extends PGraphics { strokeColor, strokeWeight, ambientColor, specularColor, emissiveColor, shininessFactor, - code); + code, brk); } int addVertex(float x, float y, float u, float v, - int code) { + boolean brk) { + return addVertex(x, y, u, v, VERTEX, brk); + } + + int addVertex(float x, float y, + float u, float v, + int code, boolean brk) { return addVertex(x, y, 0, fillColor, normalX, normalY, normalZ, @@ -8056,11 +7507,15 @@ public class PGraphicsOpenGL extends PGraphics { strokeColor, strokeWeight, ambientColor, specularColor, emissiveColor, shininessFactor, - code); + code, brk); + } + + int addVertex(float x, float y, float z, boolean brk) { + return addVertex(x, y, z, VERTEX, brk); } int addVertex(float x, float y, float z, - int code) { + int code, boolean brk) { return addVertex(x, y, z, fillColor, normalX, normalY, normalZ, @@ -8068,12 +7523,18 @@ public class PGraphicsOpenGL extends PGraphics { strokeColor, strokeWeight, ambientColor, specularColor, emissiveColor, shininessFactor, - code); + code, brk); } int addVertex(float x, float y, float z, float u, float v, - int code) { + boolean brk) { + return addVertex(x, y, z, u, v, VERTEX, brk); + } + + int addVertex(float x, float y, float z, + float u, float v, + int code, boolean brk) { return addVertex(x, y, z, fillColor, normalX, normalY, normalZ, @@ -8081,7 +7542,7 @@ public class PGraphicsOpenGL extends PGraphics { strokeColor, strokeWeight, ambientColor, specularColor, emissiveColor, shininessFactor, - code); + code, brk); } int addVertex(float x, float y, float z, @@ -8090,12 +7551,10 @@ public class PGraphicsOpenGL extends PGraphics { float u, float v, int scolor, float sweight, int am, int sp, int em, float shine, - int code) { + int code, boolean brk) { vertexCheck(); int index; - curveVertexCount = 0; - index = 3 * vertexCount; vertices[index++] = x; vertices[index++] = y; @@ -8120,147 +7579,50 @@ public class PGraphicsOpenGL extends PGraphics { emissive[vertexCount] = PGL.javaToNativeARGB(em); shininess[vertexCount] = shine; - breaks[vertexCount] = code == BREAK; + if (brk || (code == VERTEX && codes != null) || + code == BEZIER_VERTEX || + code == QUADRATIC_VERTEX || + code == CURVE_VERTEX) { + if (codes == null) { + codes = new int[PApplet.max(PGL.DEFAULT_IN_VERTICES, vertexCount)]; + Arrays.fill(codes, 0, vertexCount, VERTEX); + codeCount = vertexCount; + } + + if (brk) { + codeCheck(); + codes[codeCount] = BREAK; + codeCount++; + } + + if (code != -1) { + codeCheck(); + codes[codeCount] = code; + codeCount++; + } + } - lastVertex = vertexCount; vertexCount++; - return lastVertex; + return vertexCount - 1; } - void addBezierVertex(float x2, float y2, float z2, - float x3, float y3, float z3, - float x4, float y4, float z4, - boolean fill, boolean stroke, int detail, int code) { - addBezierVertex(x2, y2, z2, - x3, y3, z3, - x4, y4, z4, - fill, stroke, detail, code, POLYGON); - } - - void addBezierVertex(float x2, float y2, float z2, - float x3, float y3, float z3, - float x4, float y4, float z4, - boolean fill, boolean stroke, int detail, int code, - int shape) { - bezierInitCheck(); - bezierVertexCheck(shape, vertexCount); - - PMatrix3D draw = bezierDrawMatrix; - - float x1 = getLastVertexX(); - float y1 = getLastVertexY(); - float z1 = getLastVertexZ(); - - float xplot1 = draw.m10*x1 + draw.m11*x2 + draw.m12*x3 + draw.m13*x4; - float xplot2 = draw.m20*x1 + draw.m21*x2 + draw.m22*x3 + draw.m23*x4; - float xplot3 = draw.m30*x1 + draw.m31*x2 + draw.m32*x3 + draw.m33*x4; - - float yplot1 = draw.m10*y1 + draw.m11*y2 + draw.m12*y3 + draw.m13*y4; - float yplot2 = draw.m20*y1 + draw.m21*y2 + draw.m22*y3 + draw.m23*y4; - float yplot3 = draw.m30*y1 + draw.m31*y2 + draw.m32*y3 + draw.m33*y4; - - float zplot1 = draw.m10*z1 + draw.m11*z2 + draw.m12*z3 + draw.m13*z4; - float zplot2 = draw.m20*z1 + draw.m21*z2 + draw.m22*z3 + draw.m23*z4; - float zplot3 = draw.m30*z1 + draw.m31*z2 + draw.m32*z3 + draw.m33*z4; - - for (int j = 0; j < detail; j++) { - x1 += xplot1; xplot1 += xplot2; xplot2 += xplot3; - y1 += yplot1; yplot1 += yplot2; yplot2 += yplot3; - z1 += zplot1; zplot1 += zplot2; zplot2 += zplot3; - addVertex(x1, y1, z1, j == 0 && code == BREAK ? BREAK : VERTEX); - } + public void addBezierVertex(float x2, float y2, float z2, + float x3, float y3, float z3, + float x4, float y4, float z4, boolean brk) { + addVertex(x2, y2, z2, BEZIER_VERTEX, brk); + addVertex(x3, y3, z3, -1, false); + addVertex(x4, y4, z4, -1, false); } public void addQuadraticVertex(float cx, float cy, float cz, - float x3, float y3, float z3, - boolean fill, boolean stroke, int detail, - int code) { - addQuadraticVertex(cx, cy, cz, - x3, y3, z3, - fill, stroke, detail, code, POLYGON); + float x3, float y3, float z3, boolean brk) { + addVertex(cx, cy, cz, QUADRATIC_VERTEX, brk); + addVertex(x3, y3, z3, -1, false); } - public void addQuadraticVertex(float cx, float cy, float cz, - float x3, float y3, float z3, - boolean fill, boolean stroke, int detail, - int code, int shape) { - float x1 = getLastVertexX(); - float y1 = getLastVertexY(); - float z1 = getLastVertexZ(); - addBezierVertex( - x1 + ((cx-x1)*2/3.0f), y1 + ((cy-y1)*2/3.0f), z1 + ((cz-z1)*2/3.0f), - x3 + ((cx-x3)*2/3.0f), y3 + ((cy-y3)*2/3.0f), z3 + ((cz-z3)*2/3.0f), - x3, y3, z3, - fill, stroke, detail, code, shape); - } - - void addCurveVertex(float x, float y, float z, - boolean fill, boolean stroke, int detail, int code) { - addCurveVertex(x, y, z, - fill, stroke, detail, code, POLYGON); - } - - void addCurveVertex(float x, float y, float z, - boolean fill, boolean stroke, int detail, int code, - int shape) { - curveVertexCheck(shape); - - float[] vertex = curveVertices[curveVertexCount]; - vertex[X] = x; - vertex[Y] = y; - vertex[Z] = z; - curveVertexCount++; - - // draw a segment if there are enough points - if (curveVertexCount > 3) { - float[] v1 = curveVertices[curveVertexCount-4]; - float[] v2 = curveVertices[curveVertexCount-3]; - float[] v3 = curveVertices[curveVertexCount-2]; - float[] v4 = curveVertices[curveVertexCount-1]; - addCurveVertexSegment(v1[X], v1[Y], v1[Z], - v2[X], v2[Y], v2[Z], - v3[X], v3[Y], v3[Z], - v4[X], v4[Y], v4[Z], - detail, code); - } - } - - void addCurveVertexSegment(float x1, float y1, float z1, - float x2, float y2, float z2, - float x3, float y3, float z3, - float x4, float y4, float z4, - int detail, int code) { - float x0 = x2; - float y0 = y2; - float z0 = z2; - - PMatrix3D draw = curveDrawMatrix; - - float xplot1 = draw.m10*x1 + draw.m11*x2 + draw.m12*x3 + draw.m13*x4; - float xplot2 = draw.m20*x1 + draw.m21*x2 + draw.m22*x3 + draw.m23*x4; - float xplot3 = draw.m30*x1 + draw.m31*x2 + draw.m32*x3 + draw.m33*x4; - - float yplot1 = draw.m10*y1 + draw.m11*y2 + draw.m12*y3 + draw.m13*y4; - float yplot2 = draw.m20*y1 + draw.m21*y2 + draw.m22*y3 + draw.m23*y4; - float yplot3 = draw.m30*y1 + draw.m31*y2 + draw.m32*y3 + draw.m33*y4; - - float zplot1 = draw.m10*z1 + draw.m11*z2 + draw.m12*z3 + draw.m13*z4; - float zplot2 = draw.m20*z1 + draw.m21*z2 + draw.m22*z3 + draw.m23*z4; - float zplot3 = draw.m30*z1 + draw.m31*z2 + draw.m32*z3 + draw.m33*z4; - - // addVertex() will reset curveVertexCount, so save it - int savedCount = curveVertexCount; - - addVertex(x0, y0, z0, code == BREAK ? BREAK : VERTEX); - for (int j = 0; j < detail; j++) { - x0 += xplot1; xplot1 += xplot2; xplot2 += xplot3; - y0 += yplot1; yplot1 += yplot2; yplot2 += yplot3; - z0 += zplot1; zplot1 += zplot2; zplot2 += zplot3; - addVertex(x0, y0, z0, VERTEX); - } - - curveVertexCount = savedCount; + public void addCurveVertex(float x, float y, float z, boolean brk) { + addVertex(x, y, z, CURVE_VERTEX, brk); } // Returns the vertex data in the PGraphics double array format. @@ -8291,29 +7653,32 @@ public class PGraphicsOpenGL extends PGraphics { vert[SA] = ((strokeColors[i] >> 24) & 0xFF) / 255.0f; vert[SW] = strokeWeights[i]; - - /* - // Android doesn't have these: - vert[AR] = ((ambient[i] >> 16) & 0xFF) / 255.0f; - vert[AG] = ((ambient[i] >> 8) & 0xFF) / 255.0f; - vert[AB] = ((ambient[i] >> 0) & 0xFF) / 255.0f; - - vert[SPR] = ((specular[i] >> 16) & 0xFF) / 255.0f; - vert[SPG] = ((specular[i] >> 8) & 0xFF) / 255.0f; - vert[SPB] = ((specular[i] >> 0) & 0xFF) / 255.0f; - - vert[ER] = ((emissive[i] >> 16) & 0xFF) / 255.0f; - vert[EG] = ((emissive[i] >> 8) & 0xFF) / 255.0f; - vert[EB] = ((emissive[i] >> 0) & 0xFF) / 255.0f; - - vert[SHINE] = shininess[i]; - */ - } return data; } + boolean hasBezierVertex() { + for (int i = 0; i < codeCount; i++) { + if (codes[i] == BEZIER_VERTEX) return true; + } + return false; + } + + boolean hasQuadraticVertex() { + for (int i = 0; i < codeCount; i++) { + if (codes[i] == QUADRATIC_VERTEX) return true; + } + return false; + } + + boolean hasCurveVertex() { + for (int i = 0; i < codeCount; i++) { + if (codes[i] == CURVE_VERTEX) return true; + } + return false; + } + // ----------------------------------------------------------------- // // Edges @@ -8332,10 +7697,9 @@ public class PGraphicsOpenGL extends PGraphics { // 3 = isolated edge (start, end) edge[2] = (start ? 1 : 0) + 2 * (end ? 1 : 0); - lastEdge = edgeCount; edgeCount++; - return lastEdge; + return edgeCount - 1; } int closeEdge(int i, int j) { @@ -8344,16 +7708,15 @@ public class PGraphicsOpenGL extends PGraphics { int[] edge = edges[edgeCount]; edge[0] = i; edge[1] = j; - edge[2] = -1; + edge[2] = EDGE_CLOSE; - lastEdge = edgeCount; edgeCount++; - return lastEdge; + return edgeCount - 1; } void addTrianglesEdges() { - for (int i = 0; i < (lastVertex - firstVertex + 1) / 3; i++) { + for (int i = 0; i < vertexCount / 3; i++) { int i0 = 3 * i + 0; int i1 = 3 * i + 1; int i2 = 3 * i + 2; @@ -8366,8 +7729,8 @@ public class PGraphicsOpenGL extends PGraphics { } void addTriangleFanEdges() { - for (int i = firstVertex + 1; i < lastVertex; i++) { - int i0 = firstVertex; + for (int i = 1; i < vertexCount - 1; i++) { + int i0 = 0; int i1 = i; int i2 = i + 1; @@ -8379,7 +7742,7 @@ public class PGraphicsOpenGL extends PGraphics { } void addTriangleStripEdges() { - for (int i = firstVertex + 1; i < lastVertex; i++) { + for (int i = 1; i < vertexCount - 1; i++) { int i0 = i; int i1, i2; if (i % 2 == 0) { @@ -8398,7 +7761,7 @@ public class PGraphicsOpenGL extends PGraphics { } void addQuadsEdges() { - for (int i = 0; i < (lastVertex - firstVertex + 1) / 4; i++) { + for (int i = 0; i < vertexCount / 4; i++) { int i0 = 4 * i + 0; int i1 = 4 * i + 1; int i2 = 4 * i + 2; @@ -8413,11 +7776,11 @@ public class PGraphicsOpenGL extends PGraphics { } void addQuadStripEdges() { - for (int qd = 1; qd < (lastVertex - firstVertex + 1) / 2; qd++) { - int i0 = firstVertex + 2 * (qd - 1); - int i1 = firstVertex + 2 * (qd - 1) + 1; - int i2 = firstVertex + 2 * qd + 1; - int i3 = firstVertex + 2 * qd; + for (int qd = 1; qd < vertexCount / 2; qd++) { + int i0 = 2 * (qd - 1); + int i1 = 2 * (qd - 1) + 1; + int i2 = 2 * qd + 1; + int i3 = 2 * qd; addEdge(i0, i1, true, false); addEdge(i1, i2, false, false); @@ -8427,50 +7790,6 @@ public class PGraphicsOpenGL extends PGraphics { } } - void addPolygonEdges(boolean closed) { - int start = firstVertex; - boolean begin = true; - for (int i = firstVertex + 1; i <= lastVertex; i++) { - if (breaks[i]) { - if (closed) { - // Closing previous contour. - addEdge(i - 1, start, begin, false); - closeEdge(i - 1, start); - } - - // Starting new contour. - start = i; - begin = true; - } else { - if (i == lastVertex) { - if (closed && start + 1 < i) { - // Closing the end of the last contour, if it - // has more than 1 segment. - addEdge(i - 1, i, begin, false); - addEdge(i, start, false, false); - closeEdge(i, start); - } else { - // Leaving the last contour open. - addEdge(i - 1, i, begin, true); - } - } else { - - if (i < lastVertex && breaks[i + 1] && !closed) { - // A new contour starts at the next vertex and - // the polygon is not closed, so this is the last - // segment of the current contour. - addEdge(i - 1, i, begin, true); - } else { - // The current contour does not end at vertex i. - addEdge(i - 1, i, begin, false); - } - } - - begin = false; - } - } - } - // ----------------------------------------------------------------- // // Normal calculation @@ -8530,7 +7849,7 @@ public class PGraphicsOpenGL extends PGraphics { } void calcTrianglesNormals() { - for (int i = 0; i < (lastVertex - firstVertex + 1) / 3; i++) { + for (int i = 0; i < vertexCount / 3; i++) { int i0 = 3 * i + 0; int i1 = 3 * i + 1; int i2 = 3 * i + 2; @@ -8540,8 +7859,8 @@ public class PGraphicsOpenGL extends PGraphics { } void calcTriangleFanNormals() { - for (int i = firstVertex + 1; i < lastVertex; i++) { - int i0 = firstVertex; + for (int i = 1; i < vertexCount - 1; i++) { + int i0 = 0; int i1 = i; int i2 = i + 1; @@ -8550,7 +7869,7 @@ public class PGraphicsOpenGL extends PGraphics { } void calcTriangleStripNormals() { - for (int i = firstVertex + 1; i < lastVertex; i++) { + for (int i = 1; i < vertexCount - 1; i++) { int i1 = i; int i0, i2; if (i % 2 == 0) { @@ -8567,7 +7886,7 @@ public class PGraphicsOpenGL extends PGraphics { } void calcQuadsNormals() { - for (int i = 0; i < (lastVertex - firstVertex + 1) / 4; i++) { + for (int i = 0; i < vertexCount / 4; i++) { int i0 = 4 * i + 0; int i1 = 4 * i + 1; int i2 = 4 * i + 2; @@ -8579,11 +7898,11 @@ public class PGraphicsOpenGL extends PGraphics { } void calcQuadStripNormals() { - for (int qd = 1; qd < (lastVertex - firstVertex + 1) / 2; qd++) { - int i0 = firstVertex + 2 * (qd - 1); - int i1 = firstVertex + 2 * (qd - 1) + 1; - int i2 = firstVertex + 2 * qd; - int i3 = firstVertex + 2 * qd + 1; + for (int qd = 1; qd < vertexCount / 2; qd++) { + int i0 = 2 * (qd - 1); + int i1 = 2 * (qd - 1) + 1; + int i2 = 2 * qd; + int i3 = 2 * qd + 1; calcTriangleNormal(i0, i3, i1); calcTriangleNormal(i0, i2, i3); @@ -8613,14 +7932,14 @@ public class PGraphicsOpenGL extends PGraphics { } void addPoint(float x, float y, float z, boolean fill, boolean stroke) { - addVertex(x, y, z, VERTEX); + addVertex(x, y, z, VERTEX, true); } void addLine(float x1, float y1, float z1, float x2, float y2, float z2, boolean fill, boolean stroke) { - int idx1 = addVertex(x1, y1, z1, VERTEX); - int idx2 = addVertex(x2, y2, z2, VERTEX); + int idx1 = addVertex(x1, y1, z1, VERTEX, true); + int idx2 = addVertex(x2, y2, z2, VERTEX, false); if (stroke) addEdge(idx1, idx2, true, true); } @@ -8628,9 +7947,9 @@ public class PGraphicsOpenGL extends PGraphics { float x2, float y2, float z2, float x3, float y3, float z3, boolean fill, boolean stroke) { - int idx1 = addVertex(x1, y1, z1, VERTEX); - int idx2 = addVertex(x2, y2, z2, VERTEX); - int idx3 = addVertex(x3, y3, z3, VERTEX); + int idx1 = addVertex(x1, y1, z1, VERTEX, true); + int idx2 = addVertex(x2, y2, z2, VERTEX, false); + int idx3 = addVertex(x3, y3, z3, VERTEX, false); if (stroke) { addEdge(idx1, idx2, true, false); addEdge(idx2, idx3, false, false); @@ -8643,11 +7962,11 @@ public class PGraphicsOpenGL extends PGraphics { float x2, float y2, float z2, float x3, float y3, float z3, float x4, float y4, float z4, - boolean fill, boolean stroke) { - int idx1 = addVertex(x1, y1, z1, 0, 0, VERTEX); - int idx2 = addVertex(x2, y2, z2, 1, 0, VERTEX); - int idx3 = addVertex(x3, y3, z3, 1, 1, VERTEX); - int idx4 = addVertex(x4, y4, z4, 0, 1, VERTEX); + boolean stroke) { + int idx1 = addVertex(x1, y1, z1, 0, 0, VERTEX, true); + int idx2 = addVertex(x2, y2, z2, 1, 0, VERTEX, false); + int idx3 = addVertex(x3, y3, z3, 1, 1, VERTEX, false); + int idx4 = addVertex(x4, y4, z4, 0, 1, VERTEX, false); if (stroke) { addEdge(idx1, idx2, true, false); addEdge(idx2, idx3, false, false); @@ -8658,151 +7977,45 @@ public class PGraphicsOpenGL extends PGraphics { } void addRect(float a, float b, float c, float d, - boolean fill, boolean stroke, int rectMode) { - float hradius, vradius; - switch (rectMode) { - case CORNERS: - break; - case CORNER: - c += a; d += b; - break; - case RADIUS: - hradius = c; - vradius = d; - c = a + hradius; - d = b + vradius; - a -= hradius; - b -= vradius; - break; - case CENTER: - hradius = c / 2.0f; - vradius = d / 2.0f; - c = a + hradius; - d = b + vradius; - a -= hradius; - b -= vradius; - } - - if (a > c) { - float temp = a; a = c; c = temp; - } - - if (b > d) { - float temp = b; b = d; d = temp; - } - + boolean stroke) { addQuad(a, b, 0, c, b, 0, c, d, 0, a, d, 0, - fill, stroke); + stroke); } void addRect(float a, float b, float c, float d, float tl, float tr, float br, float bl, - boolean fill, boolean stroke, int detail, int rectMode) { - float hradius, vradius; - switch (rectMode) { - case CORNERS: - break; - case CORNER: - c += a; d += b; - break; - case RADIUS: - hradius = c; - vradius = d; - c = a + hradius; - d = b + vradius; - a -= hradius; - b -= vradius; - break; - case CENTER: - hradius = c / 2.0f; - vradius = d / 2.0f; - c = a + hradius; - d = b + vradius; - a -= hradius; - b -= vradius; - } - - if (a > c) { - float temp = a; a = c; c = temp; - } - - if (b > d) { - float temp = b; b = d; d = temp; - } - - float maxRounding = PApplet.min((c - a) / 2, (d - b) / 2); - if (tl > maxRounding) tl = maxRounding; - if (tr > maxRounding) tr = maxRounding; - if (br > maxRounding) br = maxRounding; - if (bl > maxRounding) bl = maxRounding; - + boolean stroke) { if (nonZero(tr)) { - addVertex(c-tr, b, VERTEX); - addQuadraticVertex(c, b, 0, c, b+tr, 0, - fill, stroke, detail, VERTEX); + addVertex(c-tr, b, VERTEX, true); + addQuadraticVertex(c, b, 0, c, b+tr, 0, false); } else { - addVertex(c, b, VERTEX); + addVertex(c, b, VERTEX, true); } if (nonZero(br)) { - addVertex(c, d-br, VERTEX); - addQuadraticVertex(c, d, 0, c-br, d, 0, - fill, stroke, detail, VERTEX); + addVertex(c, d-br, VERTEX, false); + addQuadraticVertex(c, d, 0, c-br, d, 0, false); } else { - addVertex(c, d, VERTEX); + addVertex(c, d, VERTEX, false); } if (nonZero(bl)) { - addVertex(a+bl, d, VERTEX); - addQuadraticVertex(a, d, 0, a, d-bl, 0, - fill, stroke, detail, VERTEX); + addVertex(a+bl, d, VERTEX, false); + addQuadraticVertex(a, d, 0, a, d-bl, 0, false); } else { - addVertex(a, d, VERTEX); + addVertex(a, d, VERTEX, false); } if (nonZero(tl)) { - addVertex(a, b+tl, VERTEX); - addQuadraticVertex(a, b, 0, a+tl, b, 0, - fill, stroke, detail, VERTEX); + addVertex(a, b+tl, VERTEX, false); + addQuadraticVertex(a, b, 0, a+tl, b, 0, false); } else { - addVertex(a, b, VERTEX); + addVertex(a, b, VERTEX, false); } - - if (stroke) addPolygonEdges(true); } - void addEllipse(float a, float b, float c, float d, - boolean fill, boolean stroke, int ellipseMode) { - float x = a; - float y = b; - float w = c; - float h = d; - - if (ellipseMode == CORNERS) { - w = c - a; - h = d - b; - - } else if (ellipseMode == RADIUS) { - x = a - c; - y = b - d; - w = c * 2; - h = d * 2; - - } else if (ellipseMode == DIAMETER) { - x = a - c/2f; - y = b - d/2f; - } - - if (w < 0) { // undo negative width - x += w; - w = -w; - } - - if (h < 0) { // undo negative height - y += h; - h = -h; - } - + void addEllipse(float x, float y, float w, float h, + boolean fill, boolean stroke) { float radiusH = w / 2; float radiusV = h / 2; @@ -8810,10 +8023,10 @@ public class PGraphicsOpenGL extends PGraphics { float centerY = y + radiusV; // should call screenX/Y using current renderer. - float sx1 = pgCurrent.screenX(x, y); - float sy1 = pgCurrent.screenY(x, y); - float sx2 = pgCurrent.screenX(x + w, y + h); - float sy2 = pgCurrent.screenY(x + w, y + h); + float sx1 = pg.screenX(x, y); + float sy1 = pg.screenY(x, y); + float sx2 = pg.screenX(x + w, y + h); + float sy2 = pg.screenY(x + w, y + h); int accuracy = PApplet.min(MAX_POINT_ACCURACY, PApplet.max(MIN_POINT_ACCURACY, @@ -8822,7 +8035,7 @@ public class PGraphicsOpenGL extends PGraphics { float inc = (float) SINCOS_LENGTH / accuracy; if (fill) { - addVertex(centerX, centerY, VERTEX); + addVertex(centerX, centerY, VERTEX, true); } int idx0, pidx, idx; idx0 = pidx = idx = 0; @@ -8830,7 +8043,7 @@ public class PGraphicsOpenGL extends PGraphics { for (int i = 0; i < accuracy; i++) { idx = addVertex(centerX + cosLUT[(int) val] * radiusH, centerY + sinLUT[(int) val] * radiusV, - VERTEX); + VERTEX, i == 0 && !fill); val = (val + inc) % SINCOS_LENGTH; if (0 < i) { @@ -8844,7 +8057,7 @@ public class PGraphicsOpenGL extends PGraphics { // Back to the beginning addVertex(centerX + cosLUT[0] * radiusH, centerY + sinLUT[0] * radiusV, - VERTEX); + VERTEX, false); if (stroke) { addEdge(idx, idx0, false, false); closeEdge(idx, idx0); @@ -8864,37 +8077,33 @@ public class PGraphicsOpenGL extends PGraphics { int startLUT = (int) (0.5f + (start / TWO_PI) * SINCOS_LENGTH); int stopLUT = (int) (0.5f + (stop / TWO_PI) * SINCOS_LENGTH); - if (fill) { - addVertex(centerX, centerY, VERTEX); - } + int idx0 = addVertex(centerX, centerY, VERTEX, true); int increment = 1; // what's a good algorithm? stopLUT - startLUT; - int idx0, pidx, idx; - idx0 = pidx = idx = 0; + int pidx = 0, idx = 0; for (int i = startLUT; i < stopLUT; i += increment) { int ii = i % SINCOS_LENGTH; // modulo won't make the value positive if (ii < 0) ii += SINCOS_LENGTH; idx = addVertex(centerX + cosLUT[ii] * hr, centerY + sinLUT[ii] * vr, - VERTEX); + VERTEX, i == startLUT && !fill); if (stroke) { if (arcMode == PIE) { addEdge(pidx, idx, i == startLUT, false); } else if (startLUT < i) { addEdge(pidx, idx, i == startLUT + 1, arcMode == 0 && - i == stopLUT - 1); + i == stopLUT - 1); } } - if (startLUT == i) idx0 = idx; pidx = idx; } // draw last point explicitly for accuracy idx = addVertex(centerX + cosLUT[stopLUT % SINCOS_LENGTH] * hr, centerY + sinLUT[stopLUT % SINCOS_LENGTH] * vr, - VERTEX); + VERTEX, false); if (stroke) { if (arcMode == PIE) { addEdge(idx, idx0, false, false); @@ -8909,7 +8118,7 @@ public class PGraphicsOpenGL extends PGraphics { if (ii < 0) ii += SINCOS_LENGTH; idx = addVertex(centerX + cosLUT[ii] * hr, centerY + sinLUT[ii] * vr, - VERTEX); + VERTEX, false); if (stroke && arcMode == CHORD) { addEdge(pidx, idx, false, true); } @@ -8924,12 +8133,12 @@ public class PGraphicsOpenGL extends PGraphics { int idx1 = 0, idx2 = 0, idx3 = 0, idx4 = 0; if (fill || stroke) { - // front face - setNormal(0, 0, 1); - idx1 = addVertex(x1, y1, z1, 0, 0, VERTEX); - idx2 = addVertex(x2, y1, z1, 1, 0, VERTEX); - idx3 = addVertex(x2, y2, z1, 1, 1, VERTEX); - idx4 = addVertex(x1, y2, z1, 0, 1, VERTEX); + // back face + setNormal(0, 0, -1); + idx1 = addVertex(x1, y1, z1, 0, 0, VERTEX, true); + idx2 = addVertex(x2, y1, z1, 1, 0, VERTEX, false); + idx3 = addVertex(x2, y2, z1, 1, 1, VERTEX, false); + idx4 = addVertex(x1, y2, z1, 0, 1, VERTEX, false); if (stroke) { addEdge(idx1, idx2, true, false); addEdge(idx2, idx3, false, false); @@ -8938,12 +8147,12 @@ public class PGraphicsOpenGL extends PGraphics { closeEdge(idx4, idx1); } - // back face - setNormal(0, 0, -1); - idx1 = addVertex(x2, y1, z2, 0, 0, VERTEX); - idx2 = addVertex(x1, y1, z2, 1, 0, VERTEX); - idx3 = addVertex(x1, y2, z2, 1, 1, VERTEX); - idx4 = addVertex(x2, y2, z2, 0, 1, VERTEX); + // front face + setNormal(0, 0, 1); + idx1 = addVertex(x2, y1, z2, 0, 0, VERTEX, false); + idx2 = addVertex(x1, y1, z2, 1, 0, VERTEX, false); + idx3 = addVertex(x1, y2, z2, 1, 1, VERTEX, false); + idx4 = addVertex(x2, y2, z2, 0, 1, VERTEX, false); if (stroke) { addEdge(idx1, idx2, true, false); addEdge(idx2, idx3, false, false); @@ -8954,10 +8163,10 @@ public class PGraphicsOpenGL extends PGraphics { // right face setNormal(1, 0, 0); - idx1 = addVertex(x2, y1, z1, 0, 0, VERTEX); - idx2 = addVertex(x2, y1, z2, 1, 0, VERTEX); - idx3 = addVertex(x2, y2, z2, 1, 1, VERTEX); - idx4 = addVertex(x2, y2, z1, 0, 1, VERTEX); + idx1 = addVertex(x2, y1, z1, 0, 0, VERTEX, false); + idx2 = addVertex(x2, y1, z2, 1, 0, VERTEX, false); + idx3 = addVertex(x2, y2, z2, 1, 1, VERTEX, false); + idx4 = addVertex(x2, y2, z1, 0, 1, VERTEX, false); if (stroke) { addEdge(idx1, idx2, true, false); addEdge(idx2, idx3, false, false); @@ -8968,24 +8177,10 @@ public class PGraphicsOpenGL extends PGraphics { // left face setNormal(-1, 0, 0); - idx1 = addVertex(x1, y1, z2, 0, 0, VERTEX); - idx2 = addVertex(x1, y1, z1, 1, 0, VERTEX); - idx3 = addVertex(x1, y2, z1, 1, 1, VERTEX); - idx4 = addVertex(x1, y2, z2, 0, 1, VERTEX); - if (stroke) { - addEdge(idx1, idx2, true, false); - addEdge(idx2, idx3, false, false); - addEdge(idx3, idx4, false, false); - addEdge(idx4, idx1, false, false); - closeEdge(idx4, idx1); - } - - // top face - setNormal(0, 1, 0); - idx1 = addVertex(x1, y1, z2, 0, 0, VERTEX); - idx2 = addVertex(x2, y1, z2, 1, 0, VERTEX); - idx3 = addVertex(x2, y1, z1, 1, 1, VERTEX); - idx4 = addVertex(x1, y1, z1, 0, 1, VERTEX); + idx1 = addVertex(x1, y1, z2, 0, 0, VERTEX, false); + idx2 = addVertex(x1, y1, z1, 1, 0, VERTEX, false); + idx3 = addVertex(x1, y2, z1, 1, 1, VERTEX, false); + idx4 = addVertex(x1, y2, z2, 0, 1, VERTEX, false); if (stroke) { addEdge(idx1, idx2, true, false); addEdge(idx2, idx3, false, false); @@ -8996,10 +8191,24 @@ public class PGraphicsOpenGL extends PGraphics { // bottom face setNormal(0, -1, 0); - idx1 = addVertex(x1, y2, z1, 0, 0, VERTEX); - idx2 = addVertex(x2, y2, z1, 1, 0, VERTEX); - idx3 = addVertex(x2, y2, z2, 1, 1, VERTEX); - idx4 = addVertex(x1, y2, z2, 0, 1, VERTEX); + idx1 = addVertex(x1, y1, z2, 0, 0, VERTEX, false); + idx2 = addVertex(x2, y1, z2, 1, 0, VERTEX, false); + idx3 = addVertex(x2, y1, z1, 1, 1, VERTEX, false); + idx4 = addVertex(x1, y1, z1, 0, 1, VERTEX, false); + if (stroke) { + addEdge(idx1, idx2, true, false); + addEdge(idx2, idx3, false, false); + addEdge(idx3, idx4, false, false); + addEdge(idx4, idx1, false, false); + closeEdge(idx4, idx1); + } + + // top face + setNormal(0, 1, 0); + idx1 = addVertex(x1, y2, z1, 0, 0, VERTEX, false); + idx2 = addVertex(x2, y2, z1, 1, 0, VERTEX, false); + idx3 = addVertex(x2, y2, z2, 1, 1, VERTEX, false); + idx4 = addVertex(x1, y2, z2, 0, 1, VERTEX, false); if (stroke) { addEdge(idx1, idx2, true, false); addEdge(idx2, idx3, false, false); @@ -9014,13 +8223,6 @@ public class PGraphicsOpenGL extends PGraphics { // any vertex or edge. int[] addSphere(float r, int detailU, int detailV, boolean fill, boolean stroke) { - if ((detailU < 3) || (detailV < 2)) { - sphereDetail(30); - detailU = detailV = 30; - } else { - sphereDetail(detailU, detailV); - } - int nind = 3 * detailU + (6 * detailU + 3) * (detailV - 2) + 3 * detailU; int[] indices = new int[nind]; @@ -9040,21 +8242,23 @@ public class PGraphicsOpenGL extends PGraphics { u = 1; v = 1; for (int i = 0; i < detailU; i++) { setNormal(0, 1, 0); - addVertex(0, r, 0, u , v, VERTEX); + addVertex(0, r, 0, u , v, VERTEX, true); u -= du; } vertCount = detailU; vert0 = vertCount; u = 1; v -= dv; for (int i = 0; i < detailU; i++) { - setNormal(sphereX[i], sphereY[i], sphereZ[i]); - addVertex(r * sphereX[i], r *sphereY[i], r * sphereZ[i], u , v, VERTEX); + setNormal(pg.sphereX[i], pg.sphereY[i], pg.sphereZ[i]); + addVertex(r*pg.sphereX[i], r*pg.sphereY[i], r*pg.sphereZ[i], u , v, + VERTEX, false); u -= du; } vertCount += detailU; vert1 = vertCount; - setNormal(sphereX[0], sphereY[0], sphereZ[0]); - addVertex(r * sphereX[0], r * sphereY[0], r * sphereZ[0], u, v, VERTEX); + setNormal(pg.sphereX[0], pg.sphereY[0], pg.sphereZ[0]); + addVertex(r*pg.sphereX[0], r*pg.sphereY[0], r*pg.sphereZ[0], u, v, + VERTEX, false); vertCount++; for (int i = 0; i < detailU; i++) { @@ -9079,16 +8283,16 @@ public class PGraphicsOpenGL extends PGraphics { u = 1; v -= dv; for (int i = 0; i < detailU; i++) { int ioff = offset + i; - setNormal(sphereX[ioff], sphereY[ioff], sphereZ[ioff]); - addVertex(r * sphereX[ioff], r *sphereY[ioff], r * sphereZ[ioff], - u , v, VERTEX); + setNormal(pg.sphereX[ioff], pg.sphereY[ioff], pg.sphereZ[ioff]); + addVertex(r*pg.sphereX[ioff], r*pg.sphereY[ioff], r*pg.sphereZ[ioff], + u , v, VERTEX, false); u -= du; } vertCount += detailU; vert1 = vertCount; - setNormal(sphereX[offset], sphereY[offset], sphereZ[offset]); - addVertex(r * sphereX[offset], r * sphereY[offset], r * sphereZ[offset], - u, v, VERTEX); + setNormal(pg.sphereX[offset], pg.sphereY[offset], pg.sphereZ[offset]); + addVertex(r*pg.sphereX[offset], r*pg.sphereY[offset], r*pg.sphereZ[offset], + u, v, VERTEX, false); vertCount++; for (int i = 0; i < detailU; i++) { @@ -9125,7 +8329,7 @@ public class PGraphicsOpenGL extends PGraphics { u = 1; v = 0; for (int i = 0; i < detailU; i++) { setNormal(0, -1, 0); - addVertex(0, -r, 0, u , v, VERTEX); + addVertex(0, -r, 0, u , v, VERTEX, false); u -= du; } vertCount += detailU; @@ -9149,8 +8353,9 @@ public class PGraphicsOpenGL extends PGraphics { // Holds tessellated data for polygon, line and point geometry. - protected class TessGeometry { + static protected class TessGeometry { int renderMode; + PGraphicsOpenGL pg; // Tessellated polygon data int polyVertexCount; @@ -9221,7 +8426,8 @@ public class PGraphicsOpenGL extends PGraphics { float[] pointOffsets; short[] pointIndices; - TessGeometry(int mode) { + TessGeometry(PGraphicsOpenGL pg, int mode) { + this.pg = pg; renderMode = mode; allocate(); } @@ -10023,8 +9229,8 @@ public class PGraphicsOpenGL extends PGraphics { float y = in.vertices[index++]; float z = in.vertices[index ]; - if (renderMode == IMMEDIATE && flushMode == FLUSH_WHEN_FULL) { - PMatrix3D mm = modelview; + if (renderMode == IMMEDIATE && pg.flushMode == FLUSH_WHEN_FULL) { + PMatrix3D mm = pg.modelview; index = 4 * tessIdx; pointVertices[index++] = x*mm.m00 + y*mm.m01 + z*mm.m02 + mm.m03; @@ -10046,16 +9252,16 @@ public class PGraphicsOpenGL extends PGraphics { // // Add line geometry - void setLineVertex(int tessIdx, InGeometry in, int inIdx0, int rgba) { + void setLineVertex(int tessIdx, float[] vertices, int inIdx0, int rgba) { int index; index = 3 * inIdx0; - float x0 = in.vertices[index++]; - float y0 = in.vertices[index++]; - float z0 = in.vertices[index ]; + float x0 = vertices[index++]; + float y0 = vertices[index++]; + float z0 = vertices[index ]; - if (renderMode == IMMEDIATE && flushMode == FLUSH_WHEN_FULL) { - PMatrix3D mm = modelview; + if (renderMode == IMMEDIATE && pg.flushMode == FLUSH_WHEN_FULL) { + PMatrix3D mm = pg.modelview; index = 4 * tessIdx; lineVertices[index++] = x0*mm.m00 + y0*mm.m01 + z0*mm.m02 + mm.m03; @@ -10079,27 +9285,27 @@ public class PGraphicsOpenGL extends PGraphics { } // Sets line vertex with index tessIdx using the data from input vertices - //inIdx0 and inIdx1. - void setLineVertex(int tessIdx, InGeometry in, int inIdx0, int inIdx1, + // inIdx0 and inIdx1. + void setLineVertex(int tessIdx, float[] vertices, int inIdx0, int inIdx1, int rgba, float weight) { int index; index = 3 * inIdx0; - float x0 = in.vertices[index++]; - float y0 = in.vertices[index++]; - float z0 = in.vertices[index ]; + float x0 = vertices[index++]; + float y0 = vertices[index++]; + float z0 = vertices[index ]; index = 3 * inIdx1; - float x1 = in.vertices[index++]; - float y1 = in.vertices[index++]; - float z1 = in.vertices[index ]; + float x1 = vertices[index++]; + float y1 = vertices[index++]; + float z1 = vertices[index ]; float dx = x1 - x0; float dy = y1 - y0; float dz = z1 - z0; - if (renderMode == IMMEDIATE && flushMode == FLUSH_WHEN_FULL) { - PMatrix3D mm = modelview; + if (renderMode == IMMEDIATE && pg.flushMode == FLUSH_WHEN_FULL) { + PMatrix3D mm = pg.modelview; index = 4 * tessIdx; lineVertices[index++] = x0*mm.m00 + y0*mm.m01 + z0*mm.m02 + mm.m03; @@ -10164,9 +9370,9 @@ public class PGraphicsOpenGL extends PGraphics { boolean clampXY) { int index; - if (renderMode == IMMEDIATE && flushMode == FLUSH_WHEN_FULL) { - PMatrix3D mm = modelview; - PMatrix3D nm = modelviewInv; + if (renderMode == IMMEDIATE && pg.flushMode == FLUSH_WHEN_FULL) { + PMatrix3D mm = pg.modelview; + PMatrix3D nm = pg.modelviewInv; index = 4 * tessIdx; if (clampXY) { @@ -10212,7 +9418,7 @@ public class PGraphicsOpenGL extends PGraphics { } void addPolyVertices(InGeometry in, boolean clampXY) { - addPolyVertices(in, in.firstVertex, in.lastVertex, clampXY); + addPolyVertices(in, 0, in.vertexCount - 1, clampXY); } void addPolyVertex(InGeometry in, int i, boolean clampXY) { @@ -10225,9 +9431,9 @@ public class PGraphicsOpenGL extends PGraphics { polyVertexCheck(nvert); - if (renderMode == IMMEDIATE && flushMode == FLUSH_WHEN_FULL) { - PMatrix3D mm = modelview; - PMatrix3D nm = modelviewInv; + if (renderMode == IMMEDIATE && pg.flushMode == FLUSH_WHEN_FULL) { + PMatrix3D mm = pg.modelview; + PMatrix3D nm = pg.modelviewInv; for (int i = 0; i < nvert; i++) { int inIdx = i0 + i; @@ -10521,8 +9727,7 @@ public class PGraphicsOpenGL extends PGraphics { } // Generates tessellated geometry given a batch of input vertices. - // Generates tessellated geometry given a batch of input vertices. - protected class Tessellator { + static protected class Tessellator { InGeometry in; TessGeometry tess; TexCache texCache; @@ -10545,6 +9750,7 @@ public class PGraphicsOpenGL extends PGraphics { PMatrix transform; float transformScale; boolean is2D, is3D; + protected PGraphicsOpenGL pg; int[] rawIndices; int rawSize; @@ -10558,9 +9764,22 @@ public class PGraphicsOpenGL extends PGraphics { int firstPointIndexCache; int lastPointIndexCache; + // Accessor arrays to get the geometry data needed to tessellate the + // strokes, it can point to either the input geometry, or the internal + // path vertices generated in the polygon discretization. + float[] strokeVertices; + int[] strokeColors; + float[] strokeWeights; + + // Path vertex data that results from discretizing a polygon (i.e.: turning + // bezier, quadratic, and curve vertices into "regular" vertices). + int pathVertexCount; + float[] pathVertices; + int[] pathColors; + float[] pathWeights; + int beginPath; + public Tessellator() { - callback = new TessellatorCallback(); - gluTess = pgl.createTessellator(callback); rawIndices = new int[512]; accurate2DStrokes = true; transform = null; @@ -10568,6 +9787,13 @@ public class PGraphicsOpenGL extends PGraphics { is3D = true; } + void initGluTess() { + if (gluTess == null) { + callback = new TessellatorCallback(); + gluTess = pg.pgl.createTessellator(callback); + } + } + void setInGeometry(InGeometry in) { this.in = in; @@ -10587,6 +9813,11 @@ public class PGraphicsOpenGL extends PGraphics { this.fill = fill; } + void setTexCache(TexCache texCache, PImage newTexImage) { + this.texCache = texCache; + this.newTexImage = newTexImage; + } + void setStroke(boolean stroke) { this.stroke = stroke; } @@ -10599,23 +9830,20 @@ public class PGraphicsOpenGL extends PGraphics { this.strokeWeight = weight; } - void setStrokeJoin(int strokeJoin) { - this.strokeJoin = strokeJoin; - } - void setStrokeCap(int strokeCap) { this.strokeCap = strokeCap; } + void setStrokeJoin(int strokeJoin) { + this.strokeJoin = strokeJoin; + } + void setAccurate2DStrokes(boolean accurate) { this.accurate2DStrokes = accurate; } - void setTexCache(TexCache texCache, PImage prevTexImage, - PImage newTexImage) { - this.texCache = texCache; - this.prevTexImage = prevTexImage; - this.newTexImage = newTexImage; + protected void setRenderer(PGraphicsOpenGL pg) { + this.pg = pg; } void set3D(boolean value) { @@ -10633,6 +9861,10 @@ public class PGraphicsOpenGL extends PGraphics { transformScale = -1; } + void resetCurveVertexCount() { + pg.curveVertexCount = 0; + } + // ----------------------------------------------------------------- // // Point tessellation @@ -10646,7 +9878,7 @@ public class PGraphicsOpenGL extends PGraphics { } void tessellateRoundPoints() { - int nInVert = in.lastVertex - in.firstVertex + 1; + int nInVert = in.vertexCount; if (stroke && 1 <= nInVert) { // Each point generates a separate triangle fan. // The number of triangles of each fan depends on the @@ -10681,7 +9913,7 @@ public class PGraphicsOpenGL extends PGraphics { IndexCache cache = tess.pointIndexCache; int index = in.renderMode == RETAINED ? cache.addNew() : cache.getLast(); firstPointIndexCache = index; - for (int i = in.firstVertex; i <= in.lastVertex; i++) { + for (int i = 0; i < in.vertexCount; i++) { // Creating the triangle fan for each input vertex. int count = cache.vertexCount[index]; @@ -10741,7 +9973,7 @@ public class PGraphicsOpenGL extends PGraphics { IndexCache cache = tess.polyIndexCache; int index = in.renderMode == RETAINED ? cache.addNew() : cache.getLast(); firstPointIndexCache = index; - for (int i = in.firstVertex; i <= in.lastVertex; i++) { + for (int i = 0; i < in.vertexCount; i++) { int count = cache.vertexCount[index]; if (PGL.MAX_VERTEX_INDEX1 <= count + nPtVert) { // We need to start a new index block for this point. @@ -10784,7 +10016,7 @@ public class PGraphicsOpenGL extends PGraphics { } void tessellateSquarePoints() { - int nInVert = in.lastVertex - in.firstVertex + 1; + int nInVert = in.vertexCount; if (stroke && 1 <= nInVert) { updateTex(); int quadCount = nInVert; // Each point generates a separate quad. @@ -10814,7 +10046,7 @@ public class PGraphicsOpenGL extends PGraphics { IndexCache cache = tess.pointIndexCache; int index = in.renderMode == RETAINED ? cache.addNew() : cache.getLast(); firstPointIndexCache = index; - for (int i = in.firstVertex; i <= in.lastVertex; i++) { + for (int i = 0; i < in.vertexCount; i++) { int nvert = 5; int count = cache.vertexCount[index]; if (PGL.MAX_VERTEX_INDEX1 <= count + nvert) { @@ -10869,7 +10101,7 @@ public class PGraphicsOpenGL extends PGraphics { IndexCache cache = tess.polyIndexCache; int index = in.renderMode == RETAINED ? cache.addNew() : cache.getLast(); firstPointIndexCache = index; - for (int i = in.firstVertex; i <= in.lastVertex; i++) { + for (int i = 0; i < in.vertexCount; i++) { int nvert = 5; int count = cache.vertexCount[index]; if (PGL.MAX_VERTEX_INDEX1 <= count + nvert) { @@ -10909,7 +10141,7 @@ public class PGraphicsOpenGL extends PGraphics { boolean clamp2D() { return is2D && tess.renderMode == IMMEDIATE && - zero(modelview.m01) && zero(modelview.m10); + zero(pg.modelview.m01) && zero(pg.modelview.m10); } boolean clampSquarePoints2D() { @@ -10921,8 +10153,11 @@ public class PGraphicsOpenGL extends PGraphics { // Line tessellation void tessellateLines() { - int nInVert = in.lastVertex - in.firstVertex + 1; + int nInVert = in.vertexCount; if (stroke && 2 <= nInVert) { + strokeVertices = in.vertices; + strokeColors = in.strokeColors; + strokeWeights = in.strokeWeights; updateTex(); int lineCount = nInVert / 2; // Each individual line is formed by two consecutive input vertices. if (is3D) { @@ -10942,15 +10177,14 @@ public class PGraphicsOpenGL extends PGraphics { // require 3 indices to specify their connectivities. int nind = lineCount * 2 * 3; - int first = in.firstVertex; tess.lineVertexCheck(nvert); tess.lineIndexCheck(nind); int index = in.renderMode == RETAINED ? tess.lineIndexCache.addNew() : tess.lineIndexCache.getLast(); firstLineIndexCache = index; for (int ln = 0; ln < lineCount; ln++) { - int i0 = first + 2 * ln + 0; - int i1 = first + 2 * ln + 1; + int i0 = 2 * ln + 0; + int i1 = 2 * ln + 1; index = addLineSegment3D(i0, i1, index, null, false); } lastLineIndexCache = index; @@ -10960,7 +10194,6 @@ public class PGraphicsOpenGL extends PGraphics { int nvert = lineCount * 4; int nind = lineCount * 2 * 3; - int first = in.firstVertex; if (noCapsJoins(nvert)) { tess.polyVertexCheck(nvert); tess.polyIndexCheck(nind); @@ -10970,16 +10203,16 @@ public class PGraphicsOpenGL extends PGraphics { if (firstPolyIndexCache == -1) firstPolyIndexCache = index; // If the geometry has no fill, needs the first poly index. boolean clamp = clampLines2D(lineCount); for (int ln = 0; ln < lineCount; ln++) { - int i0 = first + 2 * ln + 0; - int i1 = first + 2 * ln + 1; + int i0 = 2 * ln + 0; + int i1 = 2 * ln + 1; index = addLineSegment2D(i0, i1, index, false, clamp); } lastLineIndexCache = lastPolyIndexCache = index; } else { // full stroking algorithm LinePath path = new LinePath(LinePath.WIND_NON_ZERO); for (int ln = 0; ln < lineCount; ln++) { - int i0 = first + 2 * ln + 0; - int i1 = first + 2 * ln + 1; + int i0 = 2 * ln + 0; + int i1 = 2 * ln + 1; path.moveTo(in.vertices[3 * i0 + 0], in.vertices[3 * i0 + 1], in.strokeColors[i0]); path.lineTo(in.vertices[3 * i1 + 0], in.vertices[3 * i1 + 1], @@ -10992,10 +10225,9 @@ public class PGraphicsOpenGL extends PGraphics { boolean clampLines2D(int lineCount) { boolean res = clamp2D(); if (res) { - int first = in.firstVertex; for (int ln = 0; ln < lineCount; ln++) { - int i0 = first + 2 * ln + 0; - int i1 = first + 2 * ln + 1; + int i0 = 2 * ln + 0; + int i1 = 2 * ln + 1; res = segmentIsAxisAligned(i0, i1); if (!res) break; } @@ -11004,8 +10236,11 @@ public class PGraphicsOpenGL extends PGraphics { } void tessellateLineStrip() { - int nInVert = in.lastVertex - in.firstVertex + 1; + int nInVert = in.vertexCount; if (stroke && 2 <= nInVert) { + strokeVertices = in.vertices; + strokeColors = in.strokeColors; + strokeWeights = in.strokeWeights; updateTex(); int lineCount = nInVert - 1; if (is3D) { @@ -11028,10 +10263,10 @@ public class PGraphicsOpenGL extends PGraphics { int index = in.renderMode == RETAINED ? tess.lineIndexCache.addNew() : tess.lineIndexCache.getLast(); firstLineIndexCache = index; - int i0 = in.firstVertex; + int i0 = 0; short[] lastInd = {-1, -1}; for (int ln = 0; ln < lineCount; ln++) { - int i1 = in.firstVertex + ln + 1; + int i1 = ln + 1; if (0 < nBevelTr) { index = addLineSegment3D(i0, i1, index, lastInd, false); } else { @@ -11053,21 +10288,19 @@ public class PGraphicsOpenGL extends PGraphics { tess.polyIndexCache.getLast(); firstLineIndexCache = index; if (firstPolyIndexCache == -1) firstPolyIndexCache = index; // If the geometry has no fill, needs the first poly index. - int i0 = in.firstVertex; + int i0 = 0; boolean clamp = clampLineStrip2D(lineCount); for (int ln = 0; ln < lineCount; ln++) { - int i1 = in.firstVertex + ln + 1; + int i1 = ln + 1; index = addLineSegment2D(i0, i1, index, false, clamp); i0 = i1; } lastLineIndexCache = lastPolyIndexCache = index; } else { // full stroking algorithm - int first = in.firstVertex; LinePath path = new LinePath(LinePath.WIND_NON_ZERO); - path.moveTo(in.vertices[3 * first + 0], in.vertices[3 * first + 1], - in.strokeColors[first]); + path.moveTo(in.vertices[0], in.vertices[1], in.strokeColors[0]); for (int ln = 0; ln < lineCount; ln++) { - int i1 = first + ln + 1; + int i1 = ln + 1; path.lineTo(in.vertices[3 * i1 + 0], in.vertices[3 * i1 + 1], in.strokeColors[i1]); } @@ -11078,10 +10311,8 @@ public class PGraphicsOpenGL extends PGraphics { boolean clampLineStrip2D(int lineCount) { boolean res = clamp2D(); if (res) { - int i0 = in.firstVertex; for (int ln = 0; ln < lineCount; ln++) { - int i1 = in.firstVertex + ln + 1; - res = segmentIsAxisAligned(i0, i1); + res = segmentIsAxisAligned(0, ln + 1); if (!res) break; } } @@ -11089,8 +10320,11 @@ public class PGraphicsOpenGL extends PGraphics { } void tessellateLineLoop() { - int nInVert = in.lastVertex - in.firstVertex + 1; + int nInVert = in.vertexCount; if (stroke && 2 <= nInVert) { + strokeVertices = in.vertices; + strokeColors = in.strokeColors; + strokeWeights = in.strokeWeights; updateTex(); int lineCount = nInVert; if (is3D) { @@ -11113,11 +10347,11 @@ public class PGraphicsOpenGL extends PGraphics { int index = in.renderMode == RETAINED ? tess.lineIndexCache.addNew() : tess.lineIndexCache.getLast(); firstLineIndexCache = index; - int i0 = in.firstVertex; + int i0 = 0; short[] lastInd = {-1, -1}; short firstInd = -1; for (int ln = 0; ln < lineCount - 1; ln++) { - int i1 = in.firstVertex + ln + 1; + int i1 = ln + 1; if (0 < nBevelTr) { index = addLineSegment3D(i0, i1, index, lastInd, false); if (ln == 0) firstInd = (short)(lastInd[0] - 2); @@ -11126,10 +10360,9 @@ public class PGraphicsOpenGL extends PGraphics { } i0 = i1; } - index = addLineSegment3D(in.lastVertex, in.firstVertex, index, lastInd, - false); + index = addLineSegment3D(0, in.vertexCount - 1, index, lastInd, false); if (0 < nBevelTr) { - index = addBevel3D(in.firstVertex, index, lastInd, firstInd, false); + index = addBevel3D(0, index, lastInd, firstInd, false); } lastLineIndexCache = index; } @@ -11145,23 +10378,20 @@ public class PGraphicsOpenGL extends PGraphics { tess.polyIndexCache.getLast(); firstLineIndexCache = index; if (firstPolyIndexCache == -1) firstPolyIndexCache = index; // If the geometry has no fill, needs the first poly index. - int i0 = in.firstVertex; + int i0 = 0; boolean clamp = clampLineLoop2D(lineCount); for (int ln = 0; ln < lineCount - 1; ln++) { - int i1 = in.firstVertex + ln + 1; + int i1 = ln + 1; index = addLineSegment2D(i0, i1, index, false, clamp); i0 = i1; } - index = addLineSegment2D(in.lastVertex, in.firstVertex, index, false, - clamp); + index = addLineSegment2D(0, in.vertexCount - 1, index, false, clamp); lastLineIndexCache = lastPolyIndexCache = index; } else { // full stroking algorithm - int first = in.firstVertex; LinePath path = new LinePath(LinePath.WIND_NON_ZERO); - path.moveTo(in.vertices[3 * first + 0], in.vertices[3 * first + 1], - in.strokeColors[first]); + path.moveTo(in.vertices[0], in.vertices[1], in.strokeColors[0]); for (int ln = 0; ln < lineCount - 1; ln++) { - int i1 = first + ln + 1; + int i1 = ln + 1; path.lineTo(in.vertices[3 * i1 + 0], in.vertices[3 * i1 + 1], in.strokeColors[i1]); } @@ -11173,10 +10403,8 @@ public class PGraphicsOpenGL extends PGraphics { boolean clampLineLoop2D(int lineCount) { boolean res = clamp2D(); if (res) { - int i0 = in.firstVertex; for (int ln = 0; ln < lineCount; ln++) { - int i1 = in.firstVertex + ln + 1; - res = segmentIsAxisAligned(i0, i1); + res = segmentIsAxisAligned(0, ln + 1); if (!res) break; } } @@ -11186,6 +10414,9 @@ public class PGraphicsOpenGL extends PGraphics { void tessellateEdges() { if (stroke) { if (in.edgeCount == 0) return; + strokeVertices = in.vertices; + strokeColors = in.strokeColors; + strokeWeights = in.strokeWeights; if (is3D) { tessellateEdges3D(); } else if (is2D) { @@ -11208,7 +10439,7 @@ public class PGraphicsOpenGL extends PGraphics { firstLineIndexCache = index; short[] lastInd = {-1, -1}; short firstInd = -1; - for (int i = in.firstEdge; i <= in.lastEdge; i++) { + for (int i = 0; i <= in.edgeCount - 1; i++) { int[] edge = in.edges[i]; int i0 = edge[0]; int i1 = edge[1]; @@ -11242,7 +10473,7 @@ public class PGraphicsOpenGL extends PGraphics { firstLineIndexCache = index; if (firstPolyIndexCache == -1) firstPolyIndexCache = index; // If the geometry has no fill, needs the first poly index. boolean clamp = clampEdges2D(); - for (int i = in.firstEdge; i <= in.lastEdge; i++) { + for (int i = 0; i <= in.edgeCount - 1; i++) { int[] edge = in.edges[i]; if (edge[2] == EDGE_CLOSE) continue; // ignoring edge closures when not doing caps or joins. int i0 = edge[0]; @@ -11252,34 +10483,34 @@ public class PGraphicsOpenGL extends PGraphics { lastLineIndexCache = lastPolyIndexCache = index; } else { // full stroking algorithm LinePath path = new LinePath(LinePath.WIND_NON_ZERO); - for (int i = in.firstEdge; i <= in.lastEdge; i++) { + for (int i = 0; i <= in.edgeCount - 1; i++) { int[] edge = in.edges[i]; int i0 = edge[0]; int i1 = edge[1]; switch (edge[2]) { case EDGE_MIDDLE: - path.lineTo(in.vertices[3 * i1 + 0], in.vertices[3 * i1 + 1], - in.strokeColors[i1]); + path.lineTo(strokeVertices[3 * i1 + 0], strokeVertices[3 * i1 + 1], + strokeColors[i1]); break; case EDGE_START: - path.moveTo(in.vertices[3 * i0 + 0], in.vertices[3 * i0 + 1], - in.strokeColors[i0]); - path.lineTo(in.vertices[3 * i1 + 0], in.vertices[3 * i1 + 1], - in.strokeColors[i1]); + path.moveTo(strokeVertices[3 * i0 + 0], strokeVertices[3 * i0 + 1], + strokeColors[i0]); + path.lineTo(strokeVertices[3 * i1 + 0], strokeVertices[3 * i1 + 1], + strokeColors[i1]); break; case EDGE_STOP: - path.lineTo(in.vertices[3 * i1 + 0], in.vertices[3 * i1 + 1], - in.strokeColors[i1]); - path.moveTo(in.vertices[3 * i1 + 0], in.vertices[3 * i1 + 1], - in.strokeColors[i1]); + path.lineTo(strokeVertices[3 * i1 + 0], strokeVertices[3 * i1 + 1], + strokeColors[i1]); + path.moveTo(strokeVertices[3 * i1 + 0], strokeVertices[3 * i1 + 1], + strokeColors[i1]); break; case EDGE_SINGLE: - path.moveTo(in.vertices[3 * i0 + 0], in.vertices[3 * i0 + 1], - in.strokeColors[i0]); - path.lineTo(in.vertices[3 * i1 + 0], in.vertices[3 * i1 + 1], - in.strokeColors[i1]); - path.moveTo(in.vertices[3 * i1 + 0], in.vertices[3 * i1 + 1], - in.strokeColors[i1]); + path.moveTo(strokeVertices[3 * i0 + 0], strokeVertices[3 * i0 + 1], + strokeColors[i0]); + path.lineTo(strokeVertices[3 * i1 + 0], strokeVertices[3 * i1 + 1], + strokeColors[i1]); + path.moveTo(strokeVertices[3 * i1 + 0], strokeVertices[3 * i1 + 1], + strokeColors[i1]); break; case EDGE_CLOSE: path.closePath(); @@ -11293,12 +10524,12 @@ public class PGraphicsOpenGL extends PGraphics { boolean clampEdges2D() { boolean res = clamp2D(); if (res) { - for (int i = in.firstEdge; i <= in.lastEdge; i++) { + for (int i = 0; i <= in.edgeCount - 1; i++) { int[] edge = in.edges[i]; if (edge[2] == EDGE_CLOSE) continue; int i0 = edge[0]; int i1 = edge[1]; - res = segmentIsAxisAligned(i0, i1); + res = segmentIsAxisAligned(strokeVertices, i0, i1); if (!res) break; } } @@ -11308,7 +10539,7 @@ public class PGraphicsOpenGL extends PGraphics { // Adding the data that defines a quad starting at vertex i0 and // ending at i1. int addLineSegment3D(int i0, int i1, int index, short[] lastInd, - boolean constStroke) { + boolean constStroke) { IndexCache cache = tess.lineIndexCache; int count = cache.vertexCount[index]; boolean addBevel = lastInd != null && -1 < lastInd[0] && -1 < lastInd[1]; @@ -11324,26 +10555,28 @@ public class PGraphicsOpenGL extends PGraphics { int color, color0; float weight; - color0 = color = constStroke ? strokeColor : in.strokeColors[i0]; - weight = constStroke ? strokeWeight : in.strokeWeights[i0]; + color0 = color = constStroke ? strokeColor : strokeColors[i0]; + weight = constStroke ? strokeWeight : strokeWeights[i0]; + weight *= transformScale(); - tess.setLineVertex(vidx++, in, i0, i1, color, +weight/2); + tess.setLineVertex(vidx++, strokeVertices, i0, i1, color, +weight/2); tess.lineIndices[iidx++] = (short) (count + 0); - tess.setLineVertex(vidx++, in, i0, i1, color, -weight/2); + tess.setLineVertex(vidx++, strokeVertices, i0, i1, color, -weight/2); tess.lineIndices[iidx++] = (short) (count + 1); - color = constStroke ? strokeColor : in.strokeColors[i1]; - weight = constStroke ? strokeWeight : in.strokeWeights[i1]; + color = constStroke ? strokeColor : strokeColors[i1]; + weight = constStroke ? strokeWeight : strokeWeights[i1]; + weight *= transformScale(); - tess.setLineVertex(vidx++, in, i1, i0, color, -weight/2); + tess.setLineVertex(vidx++, strokeVertices, i1, i0, color, -weight/2); tess.lineIndices[iidx++] = (short) (count + 2); // Starting a new triangle re-using prev vertices. tess.lineIndices[iidx++] = (short) (count + 2); tess.lineIndices[iidx++] = (short) (count + 1); - tess.setLineVertex(vidx++, in, i1, i0, color, +weight/2); + tess.setLineVertex(vidx++, strokeVertices, i1, i0, color, +weight/2); tess.lineIndices[iidx++] = (short) (count + 3); cache.incCounts(index, 6, 4); @@ -11351,7 +10584,7 @@ public class PGraphicsOpenGL extends PGraphics { if (lastInd != null) { if (-1 < lastInd[0] && -1 < lastInd[1]) { // Adding bevel triangles - tess.setLineVertex(vidx, in, i0, color0); + tess.setLineVertex(vidx, strokeVertices, i0, color0); if (newCache) { PGraphics.showWarning(TOO_LONG_STROKE_PATH_ERROR); @@ -11399,11 +10632,11 @@ public class PGraphicsOpenGL extends PGraphics { } int iidx = cache.indexOffset[index] + cache.indexCount[index]; int vidx = cache.vertexOffset[index] + cache.vertexCount[index]; - int color0 = constStroke ? strokeColor : in.strokeColors[i0]; + int color0 = constStroke ? strokeColor : strokeColors[i0]; if (lastInd != null) { if (-1 < lastInd[0] && -1 < lastInd[1]) { - tess.setLineVertex(vidx, in, i0, color0); + tess.setLineVertex(vidx, strokeVertices, i0, color0); if (newCache) { PGraphics.showWarning(TOO_LONG_STROKE_PATH_ERROR); @@ -11438,7 +10671,7 @@ public class PGraphicsOpenGL extends PGraphics { // ending at i1, in the case of pure 2D renderers (line geometry // is added to the poly arrays). int addLineSegment2D(int i0, int i1, int index, - boolean constStroke, boolean clamp) { + boolean constStroke, boolean clamp) { IndexCache cache = tess.polyIndexCache; int count = cache.vertexCount[index]; if (PGL.MAX_VERTEX_INDEX1 <= count + 4) { @@ -11449,15 +10682,15 @@ public class PGraphicsOpenGL extends PGraphics { int iidx = cache.indexOffset[index] + cache.indexCount[index]; int vidx = cache.vertexOffset[index] + cache.vertexCount[index]; - int color = constStroke ? strokeColor : in.strokeColors[i0]; - float weight = constStroke ? strokeWeight : in.strokeWeights[i0]; + int color = constStroke ? strokeColor : strokeColors[i0]; + float weight = constStroke ? strokeWeight : strokeWeights[i0]; if (subPixelStroke(weight)) clamp = false; - float x0 = in.vertices[3 * i0 + 0]; - float y0 = in.vertices[3 * i0 + 1]; + float x0 = strokeVertices[3 * i0 + 0]; + float y0 = strokeVertices[3 * i0 + 1]; - float x1 = in.vertices[3 * i1 + 0]; - float y1 = in.vertices[3 * i1 + 1]; + float x1 = strokeVertices[3 * i1 + 0]; + float y1 = strokeVertices[3 * i1 + 1]; // Calculating direction and normal of the line. float dirx = x1 - x0; @@ -11502,8 +10735,8 @@ public class PGraphicsOpenGL extends PGraphics { } if (!constStroke) { - color = in.strokeColors[i1]; - weight = in.strokeWeights[i1]; + color = strokeColors[i1]; + weight = strokeWeights[i1]; normdx = normx * weight/2; normdy = normy * weight/2; if (subPixelStroke(weight)) clamp = false; @@ -11538,7 +10771,7 @@ public class PGraphicsOpenGL extends PGraphics { } void unclampLine2D(int tessIdx, float x, float y) { - PMatrix3D mm = modelview; + PMatrix3D mm = pg.modelview; int index = 4 * tessIdx; tess.polyVertices[index++] = x*mm.m00 + y*mm.m01 + mm.m03; tess.polyVertices[index++] = x*mm.m10 + y*mm.m11 + mm.m13; @@ -11562,7 +10795,7 @@ public class PGraphicsOpenGL extends PGraphics { } boolean noCapsJoins() { - // The stroke weight is scaled so it correspons to the current + // The stroke weight is scaled so it corresponds to the current // "zoom level" being applied on the geometry due to scaling: return tess.renderMode == IMMEDIATE && transformScale() * strokeWeight < PGL.MIN_CAPS_JOINS_WEIGHT; @@ -11599,19 +10832,24 @@ public class PGraphicsOpenGL extends PGraphics { zero(in.vertices[3 * i0 + 1] - in.vertices[3 * i1 + 1]); } + boolean segmentIsAxisAligned(float[] vertices, int i0, int i1) { + return zero(vertices[3 * i0 + 0] - vertices[3 * i1 + 0]) || + zero(vertices[3 * i0 + 1] - vertices[3 * i1 + 1]); + } + // ----------------------------------------------------------------- // // Polygon primitives tessellation void tessellateTriangles() { beginTex(); - int nTri = (in.lastVertex - in.firstVertex + 1) / 3; + int nTri = in.vertexCount / 3; if (fill && 1 <= nTri) { int nInInd = 3 * nTri; setRawSize(nInInd); int idx = 0; boolean clamp = clampTriangles(); - for (int i = in.firstVertex; i < in.firstVertex + 3 * nTri; i++) { + for (int i = 0; i < 3 * nTri; i++) { rawIndices[idx++] = i; } splitRawIndices(clamp); @@ -11623,7 +10861,7 @@ public class PGraphicsOpenGL extends PGraphics { boolean clampTriangles() { boolean res = clamp2D(); if (res) { - int nTri = (in.lastVertex - in.firstVertex + 1) / 3; + int nTri = in.vertexCount / 3; for (int i = 0; i < nTri; i++) { int i0 = 3 * i + 0; int i1 = 3 * i + 1; @@ -11641,7 +10879,7 @@ public class PGraphicsOpenGL extends PGraphics { void tessellateTriangles(int[] indices) { beginTex(); - int nInVert = in.lastVertex - in.firstVertex + 1; + int nInVert = in.vertexCount; if (fill && 3 <= nInVert) { int nInInd = indices.length; setRawSize(nInInd); @@ -11674,14 +10912,14 @@ public class PGraphicsOpenGL extends PGraphics { void tessellateTriangleFan() { beginTex(); - int nInVert = in.lastVertex - in.firstVertex + 1; + int nInVert = in.vertexCount; if (fill && 3 <= nInVert) { int nInInd = 3 * (nInVert - 2); setRawSize(nInInd); int idx = 0; boolean clamp = clampTriangleFan(); - for (int i = in.firstVertex + 1; i < in.lastVertex; i++) { - rawIndices[idx++] = in.firstVertex; + for (int i = 1; i < in.vertexCount - 1; i++) { + rawIndices[idx++] = 0; rawIndices[idx++] = i; rawIndices[idx++] = i + 1; } @@ -11694,8 +10932,8 @@ public class PGraphicsOpenGL extends PGraphics { boolean clampTriangleFan() { boolean res = clamp2D(); if (res) { - for (int i = in.firstVertex + 1; i < in.lastVertex; i++) { - int i0 = in.firstVertex; + for (int i = 1; i < in.vertexCount - 1; i++) { + int i0 = 0; int i1 = i; int i2 = i + 1; int count = 0; @@ -11711,13 +10949,13 @@ public class PGraphicsOpenGL extends PGraphics { void tessellateTriangleStrip() { beginTex(); - int nInVert = in.lastVertex - in.firstVertex + 1; + int nInVert = in.vertexCount; if (fill && 3 <= nInVert) { int nInInd = 3 * (nInVert - 2); setRawSize(nInInd); int idx = 0; boolean clamp = clampTriangleStrip(); - for (int i = in.firstVertex + 1; i < in.lastVertex; i++) { + for (int i = 1; i < in.vertexCount - 1; i++) { rawIndices[idx++] = i; if (i % 2 == 0) { rawIndices[idx++] = i - 1; @@ -11736,7 +10974,7 @@ public class PGraphicsOpenGL extends PGraphics { boolean clampTriangleStrip() { boolean res = clamp2D(); if (res) { - for (int i = in.firstVertex + 1; i < in.lastVertex; i++) { + for (int i = 1; i < in.vertexCount - 1; i++) { int i0 = i; int i1, i2; if (i % 2 == 0) { @@ -11759,17 +10997,17 @@ public class PGraphicsOpenGL extends PGraphics { void tessellateQuads() { beginTex(); - int quadCount = (in.lastVertex - in.firstVertex + 1) / 4; + int quadCount = in.vertexCount / 4; if (fill && 1 <= quadCount) { int nInInd = 6 * quadCount; setRawSize(nInInd); int idx = 0; boolean clamp = clampQuads(quadCount); for (int qd = 0; qd < quadCount; qd++) { - int i0 = in.firstVertex + 4 * qd + 0; - int i1 = in.firstVertex + 4 * qd + 1; - int i2 = in.firstVertex + 4 * qd + 2; - int i3 = in.firstVertex + 4 * qd + 3; + int i0 = 4 * qd + 0; + int i1 = 4 * qd + 1; + int i2 = 4 * qd + 2; + int i3 = 4 * qd + 3; rawIndices[idx++] = i0; rawIndices[idx++] = i1; @@ -11789,10 +11027,10 @@ public class PGraphicsOpenGL extends PGraphics { boolean res = clamp2D(); if (res) { for (int qd = 0; qd < quadCount; qd++) { - int i0 = in.firstVertex + 4 * qd + 0; - int i1 = in.firstVertex + 4 * qd + 1; - int i2 = in.firstVertex + 4 * qd + 2; - int i3 = in.firstVertex + 4 * qd + 3; + int i0 = 4 * qd + 0; + int i1 = 4 * qd + 1; + int i2 = 4 * qd + 2; + int i3 = 4 * qd + 3; res = segmentIsAxisAligned(i0, i1) && segmentIsAxisAligned(i1, i2) && segmentIsAxisAligned(i2, i3); @@ -11804,17 +11042,17 @@ public class PGraphicsOpenGL extends PGraphics { void tessellateQuadStrip() { beginTex(); - int quadCount = (in.lastVertex - in.firstVertex + 1) / 2 - 1; + int quadCount = in.vertexCount / 2 - 1; if (fill && 1 <= quadCount) { int nInInd = 6 * quadCount; setRawSize(nInInd); int idx = 0; boolean clamp = clampQuadStrip(quadCount); for (int qd = 1; qd < quadCount + 1; qd++) { - int i0 = in.firstVertex + 2 * (qd - 1); - int i1 = in.firstVertex + 2 * (qd - 1) + 1; - int i2 = in.firstVertex + 2 * qd + 1; - int i3 = in.firstVertex + 2 * qd; + int i0 = 2 * (qd - 1); + int i1 = 2 * (qd - 1) + 1; + int i2 = 2 * qd + 1; + int i3 = 2 * qd; rawIndices[idx++] = i0; rawIndices[idx++] = i1; @@ -11834,10 +11072,10 @@ public class PGraphicsOpenGL extends PGraphics { boolean res = clamp2D(); if (res) { for (int qd = 1; qd < quadCount + 1; qd++) { - int i0 = in.firstVertex + 2 * (qd - 1); - int i1 = in.firstVertex + 2 * (qd - 1) + 1; - int i2 = in.firstVertex + 2 * qd + 1; - int i3 = in.firstVertex + 2 * qd; + int i0 = 2 * (qd - 1); + int i1 = 2 * (qd - 1) + 1; + int i2 = 2 * qd + 1; + int i3 = 2 * qd; res = segmentIsAxisAligned(i0, i1) && segmentIsAxisAligned(i1, i2) && segmentIsAxisAligned(i2, i3); @@ -11869,7 +11107,7 @@ public class PGraphicsOpenGL extends PGraphics { // Current index and vertex ranges int inInd0 = 0, inInd1 = 0; - int inMaxVert0 = in.firstVertex, inMaxVert1 = in.firstVertex; + int inMaxVert0 = 0, inMaxVert1 = 0; int inMaxVertRef = inMaxVert0; // Reference vertex where last break split occurred int inMaxVertRel = -1; // Position of vertices from last range relative to @@ -12041,7 +11279,6 @@ public class PGraphicsOpenGL extends PGraphics { } void beginNoTex() { - prevTexImage = newTexImage; newTexImage = null; setFirstTexIndex(tess.polyIndexCount, tess.polyIndexCache.size - 1); } @@ -12071,82 +11308,541 @@ public class PGraphicsOpenGL extends PGraphics { texCache.setLastIndex(lastIndex, lastCache); } } + prevTexImage = newTexImage; } // ----------------------------------------------------------------- // - // Polygon tessellation + // Polygon tessellation, includes edge calculation and tessellation. void tessellatePolygon(boolean solid, boolean closed, boolean calcNormals) { beginTex(); - int nInVert = in.lastVertex - in.firstVertex + 1; + int nInVert = in.vertexCount; - if (fill && 3 <= nInVert) { + if (3 <= nInVert) { firstPolyIndexCache = -1; + initGluTess(); boolean clamp = clampPolygon(); callback.init(in.renderMode == RETAINED, false, calcNormals, clamp); - gluTess.beginPolygon(); - - if (solid) { - // Using NONZERO winding rule for solid polygons. - gluTess.setWindingRule(PGL.TESS_WINDING_NONZERO); - } else { - // Using ODD winding rule to generate polygon with holes. - gluTess.setWindingRule(PGL.TESS_WINDING_ODD); + if (fill) { + gluTess.beginPolygon(); + if (solid) { + // Using NONZERO winding rule for solid polygons. + gluTess.setWindingRule(PGL.TESS_WINDING_NONZERO); + } else { + // Using ODD winding rule to generate polygon with holes. + gluTess.setWindingRule(PGL.TESS_WINDING_ODD); + } + gluTess.beginContour(); } - gluTess.beginContour(); + if (stroke) { + beginPolygonStroke(); + beginStrokePath(); + } - // Now, iterate over all input data and send to GLU tessellator.. - for (int i = in.firstVertex; i <= in.lastVertex; i++) { - boolean breakPt = in.breaks[i]; - if (breakPt) { - gluTess.endContour(); - gluTess.beginContour(); + int i = 0; + int c = 0; + while (i < in.vertexCount) { + int code = VERTEX; + boolean brk = false; + if (in.codes != null && c < in.codeCount) { + code = in.codes[c++]; + if (code == BREAK && c < in.codeCount) { + brk = true; + code = in.codes[c++]; + } } - // Separting colors into individual rgba components for interpolation. - int fa = (in.colors[i] >> 24) & 0xFF; - int fr = (in.colors[i] >> 16) & 0xFF; - int fg = (in.colors[i] >> 8) & 0xFF; - int fb = (in.colors[i] >> 0) & 0xFF; + if (brk) { + if (stroke) { + endStrokePath(closed); + beginStrokePath(); + } + if (fill) { + gluTess.endContour(); + gluTess.beginContour(); + } + } - int aa = (in.ambient[i] >> 24) & 0xFF; - int ar = (in.ambient[i] >> 16) & 0xFF; - int ag = (in.ambient[i] >> 8) & 0xFF; - int ab = (in.ambient[i] >> 0) & 0xFF; - - int sa = (in.specular[i] >> 24) & 0xFF; - int sr = (in.specular[i] >> 16) & 0xFF; - int sg = (in.specular[i] >> 8) & 0xFF; - int sb = (in.specular[i] >> 0) & 0xFF; - - int ea = (in.emissive[i] >> 24) & 0xFF; - int er = (in.emissive[i] >> 16) & 0xFF; - int eg = (in.emissive[i] >> 8) & 0xFF; - int eb = (in.emissive[i] >> 0) & 0xFF; - - // Vertex data includes coordinates, colors, normals, texture - // coordinates, and material properties. - double[] vertex = new double[] { - in.vertices [3*i + 0], in.vertices [3*i + 1], in.vertices[3*i + 2], - fa, fr, fg, fb, - in.normals [3*i + 0], in.normals [3*i + 1], in.normals [3*i + 2], - in.texcoords[2*i + 0], in.texcoords[2*i + 1], - aa, ar, ag, ab, sa, sr, sg, sb, ea, er, eg, eb, in.shininess[i]}; - - gluTess.addVertex(vertex); + if (code == BEZIER_VERTEX) { + addBezierVertex(i); + i += 3; + } else if (code == QUADRATIC_VERTEX) { + addQuadraticVertex(i); + i += 2; + } else if (code == CURVE_VERTEX) { + addCurveVertex(i); + i++; + } else { + addVertex(i); + i++; + } + } + if (stroke) { + endStrokePath(closed); + endPolygonStroke(); + } + if (fill) { + gluTess.endContour(); + gluTess.endPolygon(); } - gluTess.endContour(); - - gluTess.endPolygon(); } endTex(); - tessellateEdges(); + if (stroke) tessellateStrokePath(); + } + + void addBezierVertex(int i) { + pg.curveVertexCount = 0; + pg.bezierInitCheck(); + pg.bezierVertexCheck(POLYGON, i); + + PMatrix3D draw = pg.bezierDrawMatrix; + + int i1 = i - 1; + float x1 = in.vertices[3*i1 + 0]; + float y1 = in.vertices[3*i1 + 1]; + float z1 = in.vertices[3*i1 + 2]; + + int strokeColor = 0; + float strokeWeight = 0; + if (stroke) { + strokeColor = in.strokeColors[i]; + strokeWeight = in.strokeWeights[i]; + } + + int fcol = 0, fa = 0, fr = 0, fg = 0, fb = 0; + int acol = 0, aa = 0, ar = 0, ag = 0, ab = 0; + int scol = 0, sa = 0, sr = 0, sg = 0, sb = 0; + int ecol = 0, ea = 0, er = 0, eg = 0, eb = 0; + float nx = 0, ny = 0, nz = 0, u = 0, v = 0, sh = 0; + if (fill) { + fcol = in.colors[i]; + fa = (fcol >> 24) & 0xFF; + fr = (fcol >> 16) & 0xFF; + fg = (fcol >> 8) & 0xFF; + fb = (fcol >> 0) & 0xFF; + + acol = in.ambient[i]; + aa = (acol >> 24) & 0xFF; + ar = (acol >> 16) & 0xFF; + ag = (acol >> 8) & 0xFF; + ab = (acol >> 0) & 0xFF; + + scol = in.specular[i]; + sa = (scol >> 24) & 0xFF; + sr = (scol >> 16) & 0xFF; + sg = (scol >> 8) & 0xFF; + sb = (scol >> 0) & 0xFF; + + ecol = in.emissive[i]; + ea = (ecol >> 24) & 0xFF; + er = (ecol >> 16) & 0xFF; + eg = (ecol >> 8) & 0xFF; + eb = (ecol >> 0) & 0xFF; + + nx = in.normals[3*i + 0]; + ny = in.normals[3*i + 1]; + nz = in.normals[3*i + 2]; + u = in.texcoords[2*i + 0]; + v = in.texcoords[2*i + 1]; + sh = in.shininess[i]; + } + + float x2 = in.vertices[3*i + 0]; + float y2 = in.vertices[3*i + 1]; + float z2 = in.vertices[3*i + 2]; + float x3 = in.vertices[3*(i+1) + 0]; + float y3 = in.vertices[3*(i+1) + 1]; + float z3 = in.vertices[3*(i+1) + 2]; + float x4 = in.vertices[3*(i+2) + 0]; + float y4 = in.vertices[3*(i+2) + 1]; + float z4 = in.vertices[3*(i+2) + 2]; + + float xplot1 = draw.m10*x1 + draw.m11*x2 + draw.m12*x3 + draw.m13*x4; + float xplot2 = draw.m20*x1 + draw.m21*x2 + draw.m22*x3 + draw.m23*x4; + float xplot3 = draw.m30*x1 + draw.m31*x2 + draw.m32*x3 + draw.m33*x4; + + float yplot1 = draw.m10*y1 + draw.m11*y2 + draw.m12*y3 + draw.m13*y4; + float yplot2 = draw.m20*y1 + draw.m21*y2 + draw.m22*y3 + draw.m23*y4; + float yplot3 = draw.m30*y1 + draw.m31*y2 + draw.m32*y3 + draw.m33*y4; + + float zplot1 = draw.m10*z1 + draw.m11*z2 + draw.m12*z3 + draw.m13*z4; + float zplot2 = draw.m20*z1 + draw.m21*z2 + draw.m22*z3 + draw.m23*z4; + float zplot3 = draw.m30*z1 + draw.m31*z2 + draw.m32*z3 + draw.m33*z4; + + for (int j = 0; j < pg.bezierDetail; j++) { + x1 += xplot1; xplot1 += xplot2; xplot2 += xplot3; + y1 += yplot1; yplot1 += yplot2; yplot2 += yplot3; + z1 += zplot1; zplot1 += zplot2; zplot2 += zplot3; + if (fill) { + double[] vertex = new double[] { + x1, y1, z1, + fa, fr, fg, fb, + nx, ny, nz, + u, v, + aa, ar, ag, ab, sa, sr, sg, sb, ea, er, eg, eb, sh}; + gluTess.addVertex(vertex); + } + if (stroke) addStrokeVertex(x1, y1, z1, strokeColor, strokeWeight); + } + } + + void addQuadraticVertex(int i) { + pg.curveVertexCount = 0; + pg.bezierInitCheck(); + pg.bezierVertexCheck(pg.shape, i); + + PMatrix3D draw = pg.bezierDrawMatrix; + + int i1 = i - 1; + float x1 = in.vertices[3*i1 + 0]; + float y1 = in.vertices[3*i1 + 1]; + float z1 = in.vertices[3*i1 + 2]; + + int strokeColor = 0; + float strokeWeight = 0; + if (stroke) { + strokeColor = in.strokeColors[i]; + strokeWeight = in.strokeWeights[i]; + } + + int fcol = 0, fa = 0, fr = 0, fg = 0, fb = 0; + int acol = 0, aa = 0, ar = 0, ag = 0, ab = 0; + int scol = 0, sa = 0, sr = 0, sg = 0, sb = 0; + int ecol = 0, ea = 0, er = 0, eg = 0, eb = 0; + float nx = 0, ny = 0, nz = 0, u = 0, v = 0, sh = 0; + if (fill) { + fcol = in.colors[i]; + fa = (fcol >> 24) & 0xFF; + fr = (fcol >> 16) & 0xFF; + fg = (fcol >> 8) & 0xFF; + fb = (fcol >> 0) & 0xFF; + + acol = in.ambient[i]; + aa = (acol >> 24) & 0xFF; + ar = (acol >> 16) & 0xFF; + ag = (acol >> 8) & 0xFF; + ab = (acol >> 0) & 0xFF; + + scol = in.specular[i]; + sa = (scol >> 24) & 0xFF; + sr = (scol >> 16) & 0xFF; + sg = (scol >> 8) & 0xFF; + sb = (scol >> 0) & 0xFF; + + ecol = in.emissive[i]; + ea = (ecol >> 24) & 0xFF; + er = (ecol >> 16) & 0xFF; + eg = (ecol >> 8) & 0xFF; + eb = (ecol >> 0) & 0xFF; + + nx = in.normals[3*i + 0]; + ny = in.normals[3*i + 1]; + nz = in.normals[3*i + 2]; + u = in.texcoords[2*i + 0]; + v = in.texcoords[2*i + 1]; + sh = in.shininess[i]; + } + + float cx = in.vertices[3*i + 0]; + float cy = in.vertices[3*i + 1]; + float cz = in.vertices[3*i + 2]; + float x = in.vertices[3*(i+1) + 0]; + float y = in.vertices[3*(i+1) + 1]; + float z = in.vertices[3*(i+1) + 2]; + + float x2 = x1 + ((cx-x1)*2/3.0f); + float y2 = y1 + ((cy-y1)*2/3.0f); + float z2 = z1 + ((cz-z1)*2/3.0f); + float x3 = x + ((cx-x)*2/3.0f); + float y3 = y + ((cy-y)*2/3.0f); + float z3 = z + ((cz-z)*2/3.0f); + float x4 = x; + float y4 = y; + float z4 = z; + + float xplot1 = draw.m10*x1 + draw.m11*x2 + draw.m12*x3 + draw.m13*x4; + float xplot2 = draw.m20*x1 + draw.m21*x2 + draw.m22*x3 + draw.m23*x4; + float xplot3 = draw.m30*x1 + draw.m31*x2 + draw.m32*x3 + draw.m33*x4; + + float yplot1 = draw.m10*y1 + draw.m11*y2 + draw.m12*y3 + draw.m13*y4; + float yplot2 = draw.m20*y1 + draw.m21*y2 + draw.m22*y3 + draw.m23*y4; + float yplot3 = draw.m30*y1 + draw.m31*y2 + draw.m32*y3 + draw.m33*y4; + + float zplot1 = draw.m10*z1 + draw.m11*z2 + draw.m12*z3 + draw.m13*z4; + float zplot2 = draw.m20*z1 + draw.m21*z2 + draw.m22*z3 + draw.m23*z4; + float zplot3 = draw.m30*z1 + draw.m31*z2 + draw.m32*z3 + draw.m33*z4; + + for (int j = 0; j < pg.bezierDetail; j++) { + x1 += xplot1; xplot1 += xplot2; xplot2 += xplot3; + y1 += yplot1; yplot1 += yplot2; yplot2 += yplot3; + z1 += zplot1; zplot1 += zplot2; zplot2 += zplot3; + if (fill) { + double[] vertex = new double[] { + x1, y1, z1, + fa, fr, fg, fb, + nx, ny, nz, + u, v, + aa, ar, ag, ab, sa, sr, sg, sb, ea, er, eg, eb, sh}; + gluTess.addVertex(vertex); + } + if (stroke) addStrokeVertex(x1, y1, z1, strokeColor, strokeWeight); + } + } + + void addCurveVertex(int i) { + pg.curveVertexCheck(POLYGON); + + float[] vertex = pg.curveVertices[pg.curveVertexCount]; + vertex[X] = in.vertices[3*i + 0]; + vertex[Y] = in.vertices[3*i + 1]; + vertex[Z] = in.vertices[3*i + 2]; + pg.curveVertexCount++; + + // draw a segment if there are enough points + if (pg.curveVertexCount > 3) { + float[] v1 = pg.curveVertices[pg.curveVertexCount - 4]; + float[] v2 = pg.curveVertices[pg.curveVertexCount - 3]; + float[] v3 = pg.curveVertices[pg.curveVertexCount - 2]; + float[] v4 = pg.curveVertices[pg.curveVertexCount - 1]; + addCurveVertexSegment(i, v1[X], v1[Y], v1[Z], + v2[X], v2[Y], v2[Z], + v3[X], v3[Y], v3[Z], + v4[X], v4[Y], v4[Z]); + } + } + + void addCurveVertexSegment(int i, float x1, float y1, float z1, + float x2, float y2, float z2, + float x3, float y3, float z3, + float x4, float y4, float z4) { + int strokeColor = 0; + float strokeWeight = 0; + if (stroke) { + strokeColor = in.strokeColors[i]; + strokeWeight = in.strokeWeights[i]; + } + + int fcol = 0, fa = 0, fr = 0, fg = 0, fb = 0; + int acol = 0, aa = 0, ar = 0, ag = 0, ab = 0; + int scol = 0, sa = 0, sr = 0, sg = 0, sb = 0; + int ecol = 0, ea = 0, er = 0, eg = 0, eb = 0; + float nx = 0, ny = 0, nz = 0, u = 0, v = 0, sh = 0; + if (fill) { + fcol = in.colors[i]; + fa = (fcol >> 24) & 0xFF; + fr = (fcol >> 16) & 0xFF; + fg = (fcol >> 8) & 0xFF; + fb = (fcol >> 0) & 0xFF; + + acol = in.ambient[i]; + aa = (acol >> 24) & 0xFF; + ar = (acol >> 16) & 0xFF; + ag = (acol >> 8) & 0xFF; + ab = (acol >> 0) & 0xFF; + + scol = in.specular[i]; + sa = (scol >> 24) & 0xFF; + sr = (scol >> 16) & 0xFF; + sg = (scol >> 8) & 0xFF; + sb = (scol >> 0) & 0xFF; + + ecol = in.emissive[i]; + ea = (ecol >> 24) & 0xFF; + er = (ecol >> 16) & 0xFF; + eg = (ecol >> 8) & 0xFF; + eb = (ecol >> 0) & 0xFF; + + nx = in.normals[3*i + 0]; + ny = in.normals[3*i + 1]; + nz = in.normals[3*i + 2]; + u = in.texcoords[2*i + 0]; + v = in.texcoords[2*i + 1]; + sh = in.shininess[i]; + } + + float x = x2; + float y = y2; + float z = z2; + + PMatrix3D draw = pg.curveDrawMatrix; + + float xplot1 = draw.m10*x1 + draw.m11*x2 + draw.m12*x3 + draw.m13*x4; + float xplot2 = draw.m20*x1 + draw.m21*x2 + draw.m22*x3 + draw.m23*x4; + float xplot3 = draw.m30*x1 + draw.m31*x2 + draw.m32*x3 + draw.m33*x4; + + float yplot1 = draw.m10*y1 + draw.m11*y2 + draw.m12*y3 + draw.m13*y4; + float yplot2 = draw.m20*y1 + draw.m21*y2 + draw.m22*y3 + draw.m23*y4; + float yplot3 = draw.m30*y1 + draw.m31*y2 + draw.m32*y3 + draw.m33*y4; + + float zplot1 = draw.m10*z1 + draw.m11*z2 + draw.m12*z3 + draw.m13*z4; + float zplot2 = draw.m20*z1 + draw.m21*z2 + draw.m22*z3 + draw.m23*z4; + float zplot3 = draw.m30*z1 + draw.m31*z2 + draw.m32*z3 + draw.m33*z4; + + if (fill) { + double[] vertex0 = new double[] { + x, y, z, + fa, fr, fg, fb, + nx, ny, nz, + u, v, + aa, ar, ag, ab, sa, sr, sg, sb, ea, er, eg, eb, sh}; + gluTess.addVertex(vertex0); + } + if (stroke) addStrokeVertex(x, y, z, strokeColor, strokeWeight); + + for (int j = 0; j < pg.curveDetail; j++) { + x += xplot1; xplot1 += xplot2; xplot2 += xplot3; + y += yplot1; yplot1 += yplot2; yplot2 += yplot3; + z += zplot1; zplot1 += zplot2; zplot2 += zplot3; + if (fill) { + double[] vertex1 = new double[] { + x, y, z, + fa, fr, fg, fb, + nx, ny, nz, + u, v, + aa, ar, ag, ab, sa, sr, sg, sb, ea, er, eg, eb, sh}; + gluTess.addVertex(vertex1); + } + if (stroke) addStrokeVertex(x, y, z, strokeColor, strokeWeight); + } + } + + void addVertex(int i) { + pg.curveVertexCount = 0; + + float x = in.vertices[3*i + 0]; + float y = in.vertices[3*i + 1]; + float z = in.vertices[3*i + 2]; + + int strokeColor = 0; + float strokeWeight = 0; + if (stroke) { + strokeColor = in.strokeColors[i]; + strokeWeight = in.strokeWeights[i]; + } + + if (fill) { + // Separating colors into individual rgba components for interpolation. + int fcol = in.colors[i]; + int fa = (fcol >> 24) & 0xFF; + int fr = (fcol >> 16) & 0xFF; + int fg = (fcol >> 8) & 0xFF; + int fb = (fcol >> 0) & 0xFF; + + int acol = in.ambient[i]; + int aa = (acol >> 24) & 0xFF; + int ar = (acol >> 16) & 0xFF; + int ag = (acol >> 8) & 0xFF; + int ab = (acol >> 0) & 0xFF; + + int scol = in.specular[i]; + int sa = (scol >> 24) & 0xFF; + int sr = (scol >> 16) & 0xFF; + int sg = (scol >> 8) & 0xFF; + int sb = (scol >> 0) & 0xFF; + + int ecol = in.emissive[i]; + int ea = (ecol >> 24) & 0xFF; + int er = (ecol >> 16) & 0xFF; + int eg = (ecol >> 8) & 0xFF; + int eb = (ecol >> 0) & 0xFF; + + float nx = in.normals[3*i + 0]; + float ny = in.normals[3*i + 1]; + float nz = in.normals[3*i + 2]; + float u = in.texcoords[2*i + 0]; + float v = in.texcoords[2*i + 1]; + float sh = in.shininess[i]; + + double[] vertex = new double[] { + x, y, z, + fa, fr, fg, fb, + nx, ny, nz, + u, v, + aa, ar, ag, ab, sa, sr, sg, sb, ea, er, eg, eb, sh}; + gluTess.addVertex(vertex); + } + if (stroke) addStrokeVertex(x, y, z, strokeColor, strokeWeight); + } + + void beginPolygonStroke() { + pathVertexCount = 0; + if (pathVertices == null) { + pathVertices = new float[3 * PGL.DEFAULT_IN_VERTICES]; + pathColors = new int[PGL.DEFAULT_IN_VERTICES]; + pathWeights = new float[PGL.DEFAULT_IN_VERTICES]; + } + } + + void endPolygonStroke() { + // Nothing to do here. + } + + void beginStrokePath() { + beginPath = pathVertexCount; + } + + void endStrokePath(boolean closed) { + int idx = pathVertexCount; + if (beginPath + 1 < idx) { + boolean begin = beginPath == idx - 2; + boolean end = begin || !closed; + in.addEdge(idx - 2, idx - 1, begin, end); + if (!end) { + in.addEdge(idx - 1, beginPath, false, false); + in.closeEdge(idx - 1, beginPath); + } + } + } + + void addStrokeVertex(float x, float y, float z, int c, float w) { + int idx = pathVertexCount; + if (beginPath + 1 < idx) { + in.addEdge(idx - 2, idx - 1, beginPath == idx - 2, false); + } + + if (pathVertexCount == pathVertices.length / 3) { + int newSize = pathVertexCount << 1; + + float vtemp[] = new float[3 * newSize]; + PApplet.arrayCopy(pathVertices, 0, vtemp, 0, 3 * pathVertexCount); + pathVertices = vtemp; + + int ctemp[] = new int[newSize]; + PApplet.arrayCopy(pathColors, 0, ctemp, 0, pathVertexCount); + pathColors = ctemp; + + float wtemp[] = new float[newSize]; + PApplet.arrayCopy(pathWeights, 0, wtemp, 0, pathVertexCount); + pathWeights = wtemp; + } + + pathVertices[3 * idx + 0] = x; + pathVertices[3 * idx + 1] = y; + pathVertices[3 * idx + 2] = z; + pathColors[idx] = c; + pathWeights[idx] = w; + + pathVertexCount++; + } + + void tessellateStrokePath() { + if (in.edgeCount == 0) return; + strokeVertices = pathVertices; + strokeColors = pathColors; + strokeWeights = pathWeights; + if (is3D) { + tessellateEdges3D(); + } else if (is2D) { + beginNoTex(); + tessellateEdges2D(); + endNoTex(); + } } boolean clampPolygon() { @@ -12157,8 +11853,8 @@ public class PGraphicsOpenGL extends PGraphics { // Based on the opengl stroke hack described here: // http://wiki.processing.org/w/Stroke_attributes_in_OpenGL public void tessellateLinePath(LinePath path) { + initGluTess(); boolean clamp = clampLinePath(); - callback.init(in.renderMode == RETAINED, true, false, clamp); int cap = strokeCap == ROUND ? LinePath.CAP_ROUND : @@ -12224,7 +11920,7 @@ public class PGraphicsOpenGL extends PGraphics { ///////////////////////////////////////// - // Interenting notes about using the GLU tessellator to render thick + // Interesting notes about using the GLU tessellator to render thick // polylines: // http://stackoverflow.com/questions/687173/how-do-i-render-thick-2d-lines-as-polygons // @@ -12245,6 +11941,7 @@ public class PGraphicsOpenGL extends PGraphics { int cacheIndex; int vertFirst; int vertCount; + int vertOffset; int primitive; public void init(boolean addCache, boolean strokeTess, boolean calcNorm, @@ -12269,19 +11966,12 @@ public class PGraphicsOpenGL extends PGraphics { } vertFirst = cache.vertexCount[cacheIndex]; + vertOffset = cache.vertexOffset[cacheIndex]; vertCount = 0; - switch (type) { - case PGL.TRIANGLE_FAN: - primitive = TRIANGLE_FAN; - break; - case PGL.TRIANGLE_STRIP: - primitive = TRIANGLE_STRIP; - break; - case PGL.TRIANGLES: - primitive = TRIANGLES; - break; - } + if (type == PGL.TRIANGLE_FAN) primitive = TRIANGLE_FAN; + else if (type == PGL.TRIANGLE_STRIP) primitive = TRIANGLE_STRIP; + else if (type == PGL.TRIANGLES) primitive = TRIANGLES; } public void end() { @@ -12293,7 +11983,8 @@ public class PGraphicsOpenGL extends PGraphics { // every time a new vertex was emitted (see vertex() below). //tessBlock = tess.addFillIndexBlock(tessBlock); cacheIndex = cache.addNew(); - vertFirst = 0; + vertFirst = cache.vertexCount[cacheIndex]; + vertOffset = cache.vertexOffset[cacheIndex]; } int indCount = 0; @@ -12353,8 +12044,9 @@ public class PGraphicsOpenGL extends PGraphics { } protected void calcTriNormal(int tessIdx0, int tessIdx1, int tessIdx2) { - tess.calcPolyNormal(vertFirst + tessIdx0, vertFirst + tessIdx1, - vertFirst + tessIdx2); + tess.calcPolyNormal(vertFirst + vertOffset + tessIdx0, + vertFirst + vertOffset + tessIdx1, + vertFirst + vertOffset + tessIdx2); } public void vertex(Object data) { @@ -12398,7 +12090,7 @@ public class PGraphicsOpenGL extends PGraphics { } public void error(int errnum) { - String estring = pgl.tessError(errnum); + String estring = pg.pgl.tessError(errnum); PGraphics.showWarning(TESSELLATION_ERROR, estring); } diff --git a/core/src/processing/opengl/PJOGL.java b/core/src/processing/opengl/PJOGL.java new file mode 100644 index 000000000..9957204c8 --- /dev/null +++ b/core/src/processing/opengl/PJOGL.java @@ -0,0 +1,2529 @@ +package processing.opengl; + +import java.awt.BorderLayout; +import java.awt.Canvas; +import java.awt.Color; +import java.awt.FontMetrics; +import java.io.IOException; +import java.net.URL; +import java.nio.Buffer; +import java.nio.ByteBuffer; +import java.nio.FloatBuffer; +import java.nio.IntBuffer; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +import javax.media.opengl.GL; +import javax.media.opengl.GL2; +import javax.media.opengl.GL2ES1; +import javax.media.opengl.GL2ES2; +import javax.media.opengl.GL2ES3; +import javax.media.opengl.GL2GL3; +import javax.media.opengl.GLAutoDrawable; +import javax.media.opengl.GLCapabilities; +import javax.media.opengl.GLCapabilitiesImmutable; +import javax.media.opengl.GLContext; +import javax.media.opengl.GLDrawable; +import javax.media.opengl.GLEventListener; +import javax.media.opengl.GLException; +import javax.media.opengl.GLFBODrawable; +import javax.media.opengl.GLProfile; +import javax.media.opengl.awt.GLCanvas; +import javax.media.opengl.fixedfunc.GLMatrixFunc; +import javax.media.opengl.glu.GLU; + +import processing.core.PApplet; +import processing.core.PConstants; +import processing.core.PGraphics; +import processing.event.KeyEvent; +import processing.event.MouseEvent; + +import com.jogamp.newt.awt.NewtCanvasAWT; +import com.jogamp.newt.event.InputEvent; +import com.jogamp.newt.opengl.GLWindow; +import com.jogamp.opengl.FBObject; + +import java.awt.Font; +import java.awt.Graphics2D; +import java.awt.Shape; +import java.awt.font.FontRenderContext; +import java.awt.font.GlyphVector; +import java.awt.geom.PathIterator; + +import javax.media.opengl.glu.GLUtessellator; +import javax.media.opengl.glu.GLUtessellatorCallbackAdapter; + +public class PJOGL extends PGL { + // OpenGL profile to use (2, 3 or 4) + public static int PROFILE = 2; + + // The two windowing toolkits available to use in JOGL: + public static final int AWT = 0; // http://jogamp.org/wiki/index.php/Using_JOGL_in_AWT_SWT_and_Swing + public static final int NEWT = 1; // http://jogamp.org/jogl/doc/NEWT-Overview.html + + // ........................................................ + + // Public members to access the underlying GL objects and context + + /** Basic GL functionality, common to all profiles */ + public GL gl; + + /** GLU interface **/ + public GLU glu; + + /** The rendering context (holds rendering state info) */ + public GLContext context; + + /** The canvas where OpenGL rendering takes place */ + public Canvas canvas; + + /** Selected GL profile */ + public static GLProfile profile; + + // ........................................................ + + // Additional parameters + + /** Time that the Processing's animation thread will wait for JOGL's rendering + * thread to be done with a single frame. + */ + protected static int DRAW_TIMEOUT_MILLIS = 500; + + // ........................................................ + + // OS-specific configuration + + protected static int WINDOW_TOOLKIT; + protected static int EVENTS_TOOLKIT; + protected static boolean USE_JOGL_FBOLAYER; + static { + if (PApplet.platform == PConstants.WINDOWS) { + // Using AWT on Windows because NEWT displays a black background while + // initializing, and the cursor functions don't work. GLWindow has some + // functions for basic cursor handling (hide/show): + // GLWindow.setPointerVisible(false); + // but apparently nothing to set the cursor icon: + // https://jogamp.org/bugzilla/show_bug.cgi?id=409 + WINDOW_TOOLKIT = AWT; + EVENTS_TOOLKIT = AWT; + USE_FBOLAYER_BY_DEFAULT = false; + USE_JOGL_FBOLAYER = false; + } else if (PApplet.platform == PConstants.MACOSX) { + // Note: The JOGL FBO layer (in 2.0.2) seems incompatible with NEWT. + WINDOW_TOOLKIT = AWT; + EVENTS_TOOLKIT = AWT; + USE_FBOLAYER_BY_DEFAULT = true; + USE_JOGL_FBOLAYER = true; + } else if (PApplet.platform == PConstants.LINUX) { + WINDOW_TOOLKIT = AWT; + EVENTS_TOOLKIT = AWT; + USE_FBOLAYER_BY_DEFAULT = false; + USE_JOGL_FBOLAYER = false; + } else if (PApplet.platform == PConstants.OTHER) { + WINDOW_TOOLKIT = NEWT; // NEWT works on the Raspberry pi? + EVENTS_TOOLKIT = NEWT; + USE_FBOLAYER_BY_DEFAULT = false; + USE_JOGL_FBOLAYER = false; + } + } + + // ........................................................ + + // Protected JOGL-specific objects needed to access the GL profiles + + /** The capabilities of the OpenGL rendering surface */ + protected GLCapabilitiesImmutable capabilities; + + /** The rendering surface */ + protected GLDrawable drawable; + + /** GLES2 functionality (shaders, etc) */ + protected GL2ES2 gl2; + + /** GL3 interface */ + protected GL2GL3 gl3; + + /** GL2 desktop functionality (blit framebuffer, map buffer range, + * multisampled renerbuffers) */ + protected GL2 gl2x; + + /** The AWT-OpenGL canvas */ + protected GLCanvas canvasAWT; + + /** The shared AWT-OpenGL canvas */ +// protected static GLCanvas sharedCanvasAWT; + + /** The NEWT window */ + protected GLWindow windowNEWT; + + /** The shared NEWT window */ +// protected static GLWindow sharedWindowNEWT; + + /** The NEWT-OpenGL canvas */ + protected NewtCanvasAWT canvasNEWT; + + /** The listener that fires the frame rendering in Processing */ + protected PGLListener listener; + + /** This countdown latch is used to maintain the synchronization between + * Processing's drawing thread and JOGL's rendering thread */ + protected CountDownLatch drawLatch; + + /** Flag used to do request final display() call to make sure that the + * buffers are properly swapped. + */ + protected boolean prevCanDraw = false; + + // ........................................................ + + // JOGL's FBO-layer + + /** Back (== draw, current frame) buffer */ + protected FBObject backFBO; + /** Sink buffer, used in the multisampled case */ + protected FBObject sinkFBO; + /** Front (== read, previous frame) buffer */ + protected FBObject frontFBO; + protected FBObject.TextureAttachment backTexAttach; + protected FBObject.TextureAttachment frontTexAttach; + + protected boolean changedFrontTex = false; + protected boolean changedBackTex = false; + + // ........................................................ + + // Utility arrays to copy projection/modelview matrices to GL + + protected float[] projMatrix; + protected float[] mvMatrix; + + // ........................................................ + + // Static initialization for some parameters that need to be different for + // JOGL + + static { + MIN_DIRECT_BUFFER_SIZE = 2; + INDEX_TYPE = GL.GL_UNSIGNED_SHORT; + } + + + /////////////////////////////////////////////////////////////// + + // Initialization, finalization + + + public PJOGL(PGraphicsOpenGL pg) { + super(pg); + glu = new GLU(); + } + + + @Override + public Canvas getCanvas() { + return canvas; + } + + + @Override + protected void setFps(float fps) { + if (!setFps || targetFps != fps) { + if (60 < fps) { + // Disables v-sync + gl.setSwapInterval(0); + } else if (30 < fps) { + gl.setSwapInterval(1); + } else { + gl.setSwapInterval(2); + } + targetFps = currentFps = fps; + setFps = true; + } + } + + + @Override + protected void initSurface(int antialias) { + if (profile == null) { + if (PROFILE == 2) { + try { + profile = GLProfile.getGL2ES1(); + } catch (GLException ex) { + profile = GLProfile.getMaxFixedFunc(true); + } + } else if (PROFILE == 3) { + try { + profile = GLProfile.getGL2GL3(); + } catch (GLException ex) { + profile = GLProfile.getMaxProgrammable(true); + } + if (!profile.isGL3()) { + PGraphics.showWarning("Requested profile GL3 but is not available, got: " + profile); + } + } else if (PROFILE == 4) { + try { + profile = GLProfile.getGL4ES3(); + } catch (GLException ex) { + profile = GLProfile.getMaxProgrammable(true); + } + if (!profile.isGL4()) { + PGraphics.showWarning("Requested profile GL4 but is not available, got: " + profile); + } + } else throw new RuntimeException(UNSUPPORTED_GLPROF_ERROR); + + if (2 < PROFILE) { + texVertShaderSource = convertVertexSource(texVertShaderSource, 120, 150); + tex2DFragShaderSource = convertFragmentSource(tex2DFragShaderSource, 120, 150); + texRectFragShaderSource = convertFragmentSource(texRectFragShaderSource, 120, 150); + } + } + + if (canvasAWT != null || canvasNEWT != null) { + // Restarting... + if (canvasAWT != null) { +// sharedCanvasAWT = null; + canvasAWT.removeGLEventListener(listener); + pg.parent.removeListeners(canvasAWT); + pg.parent.remove(canvasAWT); + } else if (canvasNEWT != null) { +// sharedWindowNEWT = null; + windowNEWT.removeGLEventListener(listener); + pg.parent.remove(canvasNEWT); + } + sinkFBO = backFBO = frontFBO = null; + } + + // Setting up the desired capabilities; + GLCapabilities caps = new GLCapabilities(profile); + caps.setAlphaBits(REQUESTED_ALPHA_BITS); + caps.setDepthBits(REQUESTED_DEPTH_BITS); + caps.setStencilBits(REQUESTED_STENCIL_BITS); + + caps.setBackgroundOpaque(true); + caps.setOnscreen(true); + if (USE_FBOLAYER_BY_DEFAULT) { + if (USE_JOGL_FBOLAYER) { + caps.setPBuffer(false); + caps.setFBO(true); + if (1 < antialias) { + caps.setSampleBuffers(true); + caps.setNumSamples(antialias); + } else { + caps.setSampleBuffers(false); + } + fboLayerRequested = false; + } else { + caps.setPBuffer(false); + caps.setFBO(false); + caps.setSampleBuffers(false); + fboLayerRequested = 1 < antialias; + } + } else { + if (1 < antialias) { + caps.setSampleBuffers(true); + caps.setNumSamples(antialias); + } else { + caps.setSampleBuffers(false); + } + fboLayerRequested = false; + } + caps.setDepthBits(REQUESTED_DEPTH_BITS); + caps.setStencilBits(REQUESTED_STENCIL_BITS); + caps.setAlphaBits(REQUESTED_ALPHA_BITS); + reqNumSamples = qualityToSamples(antialias); + + if (WINDOW_TOOLKIT == AWT) { + canvasAWT = new GLCanvas(caps); +// if (sharedCanvasAWT == null) { +// sharedCanvasAWT = canvasAWT; +// } else { +// canvasAWT.setSharedAutoDrawable(sharedCanvasAWT); +// } + canvasAWT.setBounds(0, 0, pg.width, pg.height); + canvasAWT.setBackground(new Color(pg.backgroundColor, true)); + canvasAWT.setFocusable(true); + + pg.parent.setLayout(new BorderLayout()); + pg.parent.add(canvasAWT, BorderLayout.CENTER); + canvasAWT.requestFocusInWindow(); + + canvas = canvasAWT; + canvasNEWT = null; + } else if (WINDOW_TOOLKIT == NEWT) { + windowNEWT = GLWindow.create(caps); +// if (sharedWindowNEWT == null) { +// sharedWindowNEWT = windowNEWT; +// } else { +// windowNEWT.setSharedAutoDrawable(sharedWindowNEWT); +// } + canvasNEWT = new NewtCanvasAWT(windowNEWT); + canvasNEWT.setBounds(0, 0, pg.width, pg.height); + canvasNEWT.setBackground(new Color(pg.backgroundColor, true)); + canvasNEWT.setFocusable(true); + + pg.parent.setLayout(new BorderLayout()); + pg.parent.add(canvasNEWT, BorderLayout.CENTER); + canvasNEWT.requestFocusInWindow(); + + canvas = canvasNEWT; + canvasAWT = null; + } + + pg.parent.defaultSize = false; + registerListeners(); + + fboLayerCreated = false; + fboLayerInUse = false; + firstFrame = true; + setFps = false; + } + + + @Override + protected void reinitSurface() { + sinkFBO = backFBO = frontFBO = null; + fboLayerCreated = false; + fboLayerInUse = false; + firstFrame = true; + pg.parent.defaultSize = false; + } + + + @Override + protected void registerListeners() { + if (WINDOW_TOOLKIT == AWT) { + pg.parent.addListeners(canvasAWT); + + listener = new PGLListener(); + canvasAWT.addGLEventListener(listener); + } else if (WINDOW_TOOLKIT == NEWT) { + if (EVENTS_TOOLKIT == NEWT) { + NEWTMouseListener mouseListener = new NEWTMouseListener(); + windowNEWT.addMouseListener(mouseListener); + NEWTKeyListener keyListener = new NEWTKeyListener(); + windowNEWT.addKeyListener(keyListener); + NEWTWindowListener winListener = new NEWTWindowListener(); + windowNEWT.addWindowListener(winListener); + } else if (EVENTS_TOOLKIT == AWT) { + pg.parent.addListeners(canvasNEWT); + } + + listener = new PGLListener(); + windowNEWT.addGLEventListener(listener); + } + + if (canvas != null) { + canvas.setFocusTraversalKeysEnabled(false); + } + } + + + @Override + protected void deleteSurface() { + super.deleteSurface(); + + if (canvasAWT != null) { + canvasAWT.removeGLEventListener(listener); + pg.parent.removeListeners(canvasAWT); + } else if (canvasNEWT != null) { + windowNEWT.removeGLEventListener(listener); + } + } + + + @Override + protected int getReadFramebuffer() { + if (fboLayerInUse) { + return glColorFbo.get(0); + } else if (capabilities.isFBO()) { + return context.getDefaultReadFramebuffer(); + } else { + return 0; + } + } + + + @Override + protected int getDrawFramebuffer() { + if (fboLayerInUse) { + if (1 < numSamples) { + return glMultiFbo.get(0); + } else { + return glColorFbo.get(0); + } + } else if (capabilities.isFBO()) { + return context.getDefaultDrawFramebuffer(); + } else { + return 0; + } + } + + + @Override + protected int getDefaultDrawBuffer() { + if (fboLayerInUse) { + return COLOR_ATTACHMENT0; + } else if (capabilities.isFBO()) { + return GL.GL_COLOR_ATTACHMENT0; + } else if (capabilities.getDoubleBuffered()) { + return GL.GL_BACK; + } else { + return GL.GL_FRONT; + } + } + + + @Override + protected int getDefaultReadBuffer() { + if (fboLayerInUse) { + return COLOR_ATTACHMENT0; + } else if (capabilities.isFBO()) { + return GL.GL_COLOR_ATTACHMENT0; + } else if (capabilities.getDoubleBuffered()) { + return GL.GL_BACK; + } else { + return GL.GL_FRONT; + } + } + + + @Override + protected boolean isFBOBacked() { + return super.isFBOBacked() || capabilities.isFBO(); + } + + + @Override + protected int getDepthBits() { + return capabilities.getDepthBits(); + } + + + @Override + protected int getStencilBits() { + return capabilities.getStencilBits(); + } + + + @Override + protected Texture wrapBackTexture(Texture texture) { + if (texture == null || changedBackTex) { + if (USE_JOGL_FBOLAYER) { + texture = new Texture(pg); + texture.init(pg.width, pg.height, + backTexAttach.getName(), TEXTURE_2D, RGBA, + backTexAttach.getWidth(), backTexAttach.getHeight(), + backTexAttach.minFilter, backTexAttach.magFilter, + backTexAttach.wrapS, backTexAttach.wrapT); + texture.invertedY(true); + texture.colorBuffer(true); + pg.setCache(pg, texture); + } else { + texture = super.wrapBackTexture(null); + } + } else { + if (USE_JOGL_FBOLAYER) { + texture.glName = backTexAttach.getName(); + } else { + texture = super.wrapBackTexture(texture); + } + } + return texture; + } + + + @Override + protected Texture wrapFrontTexture(Texture texture) { + if (texture == null || changedFrontTex) { + if (USE_JOGL_FBOLAYER) { + texture = new Texture(pg); + texture.init(pg.width, pg.height, + backTexAttach.getName(), TEXTURE_2D, RGBA, + frontTexAttach.getWidth(), frontTexAttach.getHeight(), + frontTexAttach.minFilter, frontTexAttach.magFilter, + frontTexAttach.wrapS, frontTexAttach.wrapT); + texture.invertedY(true); + texture.colorBuffer(true); + } else { + texture = super.wrapFrontTexture(null); + } + } else { + if (USE_JOGL_FBOLAYER) { + texture.glName = frontTexAttach.getName(); + } else { + texture = super.wrapFrontTexture(texture); + } + } + return texture; + } + + + @Override + protected void bindFrontTexture() { + if (USE_JOGL_FBOLAYER) { + usingFrontTex = true; + if (!texturingIsEnabled(TEXTURE_2D)) { + enableTexturing(TEXTURE_2D); + } + bindTexture(TEXTURE_2D, frontTexAttach.getName()); + } else super.bindFrontTexture(); + } + + + @Override + protected void unbindFrontTexture() { + if (USE_JOGL_FBOLAYER) { + if (textureIsBound(TEXTURE_2D, frontTexAttach.getName())) { + // We don't want to unbind another texture + // that might be bound instead of this one. + if (!texturingIsEnabled(TEXTURE_2D)) { + enableTexturing(TEXTURE_2D); + bindTexture(TEXTURE_2D, 0); + disableTexturing(TEXTURE_2D); + } else { + bindTexture(TEXTURE_2D, 0); + } + } + } else super.unbindFrontTexture(); + } + + + @Override + protected void syncBackTexture() { + if (USE_JOGL_FBOLAYER) { + if (usingFrontTex) needSepFrontTex = true; + if (1 < numSamples && backFBO != null) { + backFBO.syncSamplingSink(gl); + backFBO.bind(gl); + } + } else super.syncBackTexture(); + } + + + @Override + protected void beginDraw(boolean clear0) { + if (!setFps) setFps(targetFps); + if (USE_JOGL_FBOLAYER) return; + super.beginDraw(clear0); + } + + + @Override + protected void endDraw(boolean clear0) { + if (isFBOBacked()) { + if (USE_JOGL_FBOLAYER) { + if (!clear0 && isFBOBacked() && !isMultisampled() && + frontFBO != null && backFBO != null) { + // Draw the back texture into the front texture, which will be used as + // back texture in the next frame. Otherwise flickering will occur if + // the sketch uses "incremental drawing" (background() not called). + frontFBO.bind(gl); + gl.glDisable(GL.GL_BLEND); + drawTexture(TEXTURE_2D, backTexAttach.getName(), + backTexAttach.getWidth(), backTexAttach.getHeight(), + pg.width, pg.height, + 0, 0, pg.width, pg.height, 0, 0, pg.width, pg.height); + backFBO.bind(gl); + } + } else { + super.endDraw(clear0); + } + } + } + + + @Override + protected void getGL(PGL pgl) { + PJOGL pjogl = (PJOGL)pgl; + + this.drawable = pjogl.drawable; + this.context = pjogl.context; + this.glContext = pjogl.glContext; + this.glThread = pjogl.glThread; + + this.gl = pjogl.gl; + this.gl2 = pjogl.gl2; + this.gl2x = pjogl.gl2x; + this.gl3 = pjogl.gl3; + } + + + protected void getGL(GLAutoDrawable glDrawable) { + context = glDrawable.getContext(); + glContext = context.hashCode(); + glThread = Thread.currentThread(); + + gl = context.getGL(); + gl2 = gl.getGL2ES2(); + try { + gl2x = gl.getGL2(); + } catch (javax.media.opengl.GLException e) { + gl2x = null; + } + try { + gl3 = gl.getGL2GL3(); + } catch (javax.media.opengl.GLException e) { + gl3 = null; + } + } + + + @Override + protected boolean canDraw() { + return pg.initialized && pg.parent.isDisplayable(); + } + + + @Override + protected void requestFocus() { } + + + @Override + protected void requestDraw() { + boolean canDraw = pg.parent.canDraw(); + if (pg.initialized && (canDraw || prevCanDraw)) { + try { + drawLatch = new CountDownLatch(1); + if (WINDOW_TOOLKIT == AWT) { + canvasAWT.display(); + } else if (WINDOW_TOOLKIT == NEWT) { + windowNEWT.display(); + } + try { + drawLatch.await(DRAW_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS); + } catch (InterruptedException e) { + e.printStackTrace(); + } + + if (canDraw) prevCanDraw = true; + else prevCanDraw = false; + } catch (GLException e) { + // Unwrap GLException so that only the causing exception is shown. + Throwable tr = e.getCause(); + if (tr instanceof RuntimeException) { + throw (RuntimeException)tr; + } else { + throw new RuntimeException(tr); + } + } + } + } + + + @Override + protected void swapBuffers() { + if (WINDOW_TOOLKIT == AWT) { + canvasAWT.swapBuffers(); + } else if (WINDOW_TOOLKIT == NEWT) { + windowNEWT.swapBuffers(); + } + } + + + @Override + protected void beginGL() { + if (gl2x != null) { + if (projMatrix == null) { + projMatrix = new float[16]; + } + gl2x.glMatrixMode(GLMatrixFunc.GL_PROJECTION); + projMatrix[ 0] = pg.projection.m00; + projMatrix[ 1] = pg.projection.m10; + projMatrix[ 2] = pg.projection.m20; + projMatrix[ 3] = pg.projection.m30; + projMatrix[ 4] = pg.projection.m01; + projMatrix[ 5] = pg.projection.m11; + projMatrix[ 6] = pg.projection.m21; + projMatrix[ 7] = pg.projection.m31; + projMatrix[ 8] = pg.projection.m02; + projMatrix[ 9] = pg.projection.m12; + projMatrix[10] = pg.projection.m22; + projMatrix[11] = pg.projection.m32; + projMatrix[12] = pg.projection.m03; + projMatrix[13] = pg.projection.m13; + projMatrix[14] = pg.projection.m23; + projMatrix[15] = pg.projection.m33; + gl2x.glLoadMatrixf(projMatrix, 0); + + if (mvMatrix == null) { + mvMatrix = new float[16]; + } + gl2x.glMatrixMode(GLMatrixFunc.GL_MODELVIEW); + mvMatrix[ 0] = pg.modelview.m00; + mvMatrix[ 1] = pg.modelview.m10; + mvMatrix[ 2] = pg.modelview.m20; + mvMatrix[ 3] = pg.modelview.m30; + mvMatrix[ 4] = pg.modelview.m01; + mvMatrix[ 5] = pg.modelview.m11; + mvMatrix[ 6] = pg.modelview.m21; + mvMatrix[ 7] = pg.modelview.m31; + mvMatrix[ 8] = pg.modelview.m02; + mvMatrix[ 9] = pg.modelview.m12; + mvMatrix[10] = pg.modelview.m22; + mvMatrix[11] = pg.modelview.m32; + mvMatrix[12] = pg.modelview.m03; + mvMatrix[13] = pg.modelview.m13; + mvMatrix[14] = pg.modelview.m23; + mvMatrix[15] = pg.modelview.m33; + gl2x.glLoadMatrixf(mvMatrix, 0); + } + } + + + @Override + protected boolean hasFBOs() { + if (context.hasBasicFBOSupport()) return true; + else return super.hasFBOs(); + } + + + @Override + protected boolean hasShaders() { + if (context.hasGLSL()) return true; + else return super.hasShaders(); + } + + + /////////////////////////////////////////////////////////// + + // JOGL event listeners + + + protected class PGLListener implements GLEventListener { + public PGLListener() {} + + @Override + public void display(GLAutoDrawable glDrawable) { + getGL(glDrawable); + + if (USE_JOGL_FBOLAYER && capabilities.isFBO()) { + // The onscreen drawing surface is backed by an FBO layer. + GLFBODrawable fboDrawable = null; + + if (WINDOW_TOOLKIT == AWT) { + GLCanvas glCanvas = (GLCanvas)glDrawable; + fboDrawable = (GLFBODrawable)glCanvas.getDelegatedDrawable(); + } else { + GLWindow glWindow = (GLWindow)glDrawable; + fboDrawable = (GLFBODrawable)glWindow.getDelegatedDrawable(); + } + + if (fboDrawable != null) { + backFBO = fboDrawable.getFBObject(GL.GL_BACK); + if (1 < numSamples) { + if (needSepFrontTex) { + // When using multisampled FBO, the back buffer is the MSAA + // surface so it cannot be read from. The sink buffer contains + // the readable 2D texture. + // In this case, we create an auxiliary "front" buffer that it is + // swapped with the sink buffer at the beginning of each frame. + // In this way, we always have a readable copy of the previous + // frame in the front texture, while the back is synchronized + // with the contents of the MSAA back buffer when requested. + if (frontFBO == null) { + // init + frontFBO = new FBObject(); + frontFBO.reset(gl, pg.width, pg.height); + frontFBO.attachTexture2D(gl, 0, true); + sinkFBO = backFBO.getSamplingSinkFBO(); + changedFrontTex = changedBackTex = true; + } else { + // swap + FBObject temp = sinkFBO; + sinkFBO = frontFBO; + frontFBO = temp; + backFBO.setSamplingSink(sinkFBO); + changedFrontTex = changedBackTex = false; + } + backTexAttach = (FBObject.TextureAttachment) sinkFBO. + getColorbuffer(0); + frontTexAttach = (FBObject.TextureAttachment)frontFBO. + getColorbuffer(0); + } else { + changedFrontTex = changedBackTex = sinkFBO == null; + + // Default setting (to save resources): the front and back + // textures are the same. + sinkFBO = backFBO.getSamplingSinkFBO(); + backTexAttach = (FBObject.TextureAttachment) sinkFBO. + getColorbuffer(0); + frontTexAttach = backTexAttach; + } + } else { + // w/out multisampling, rendering is done on the back buffer. + frontFBO = fboDrawable.getFBObject(GL.GL_FRONT); + + backTexAttach = fboDrawable.getTextureBuffer(GL.GL_BACK); + frontTexAttach = fboDrawable.getTextureBuffer(GL.GL_FRONT); + } + } + } + + pg.parent.handleDraw(); + drawLatch.countDown(); + } + + @Override + public void dispose(GLAutoDrawable adrawable) { + } + + @Override + public void init(GLAutoDrawable glDrawable) { + getGL(glDrawable); + + capabilities = glDrawable.getChosenGLCapabilities(); + if (!hasFBOs()) { + throw new RuntimeException(MISSING_FBO_ERROR); + } + if (!hasShaders()) { + throw new RuntimeException(MISSING_GLSL_ERROR); + } + if (USE_JOGL_FBOLAYER && capabilities.isFBO()) { + int maxs = maxSamples(); + numSamples = PApplet.min(capabilities.getNumSamples(), maxs); + } + } + + @Override + public void reshape(GLAutoDrawable glDrawable, int x, int y, int w, int h) { + //getGL(glDrawable); + } + +// private void getGL(GLAutoDrawable glDrawable) { +// drawable = glDrawable; +// context = glDrawable.getContext(); +// glContext = context.hashCode(); +// glThread = Thread.currentThread(); +// +// gl = context.getGL(); +// gl2 = gl.getGL2ES2(); +// try { +// gl2x = gl.getGL2(); +// } catch (javax.media.opengl.GLException e) { +// gl2x = null; +// } +// try { +// gl3 = gl.getGL2GL3(); +// } catch (javax.media.opengl.GLException e) { +// gl3 = null; +// } +// } + } + + protected void nativeMouseEvent(com.jogamp.newt.event.MouseEvent nativeEvent, + int peAction) { + int modifiers = nativeEvent.getModifiers(); + int peModifiers = modifiers & + (InputEvent.SHIFT_MASK | + InputEvent.CTRL_MASK | + InputEvent.META_MASK | + InputEvent.ALT_MASK); + + int peButton = 0; + if ((modifiers & InputEvent.BUTTON1_MASK) != 0) { + peButton = PConstants.LEFT; + } else if ((modifiers & InputEvent.BUTTON2_MASK) != 0) { + peButton = PConstants.CENTER; + } else if ((modifiers & InputEvent.BUTTON3_MASK) != 0) { + peButton = PConstants.RIGHT; + } + + if (PApplet.platform == PConstants.MACOSX) { + //if (nativeEvent.isPopupTrigger()) { + if ((modifiers & InputEvent.CTRL_MASK) != 0) { + peButton = PConstants.RIGHT; + } + } + + int peCount = 0; + if (peAction == MouseEvent.WHEEL) { + peCount = nativeEvent.isShiftDown() ? (int)nativeEvent.getRotation()[0] : + (int)nativeEvent.getRotation()[1]; + } else { + peCount = nativeEvent.getClickCount(); + } + + MouseEvent me = new MouseEvent(nativeEvent, nativeEvent.getWhen(), + peAction, peModifiers, + nativeEvent.getX(), nativeEvent.getY(), + peButton, + peCount); + + pg.parent.postEvent(me); + } + + protected void nativeKeyEvent(com.jogamp.newt.event.KeyEvent nativeEvent, + int peAction) { + int peModifiers = nativeEvent.getModifiers() & + (InputEvent.SHIFT_MASK | + InputEvent.CTRL_MASK | + InputEvent.META_MASK | + InputEvent.ALT_MASK); + + char keyChar; + if (nativeEvent.getKeyChar() == 0) { + keyChar = PConstants.CODED; + } else { + keyChar = nativeEvent.getKeyChar(); + } + + KeyEvent ke = new KeyEvent(nativeEvent, nativeEvent.getWhen(), + peAction, peModifiers, + keyChar, + nativeEvent.getKeyCode()); + + pg.parent.postEvent(ke); + } + + protected class NEWTWindowListener implements com.jogamp.newt.event.WindowListener { + public NEWTWindowListener() { + super(); + } + @Override + public void windowGainedFocus(com.jogamp.newt.event.WindowEvent arg0) { + pg.parent.focusGained(null); + } + + @Override + public void windowLostFocus(com.jogamp.newt.event.WindowEvent arg0) { + pg.parent.focusLost(null); + } + + @Override + public void windowDestroyNotify(com.jogamp.newt.event.WindowEvent arg0) { + } + + @Override + public void windowDestroyed(com.jogamp.newt.event.WindowEvent arg0) { + } + + @Override + public void windowMoved(com.jogamp.newt.event.WindowEvent arg0) { + } + + @Override + public void windowRepaint(com.jogamp.newt.event.WindowUpdateEvent arg0) { + } + + @Override + public void windowResized(com.jogamp.newt.event.WindowEvent arg0) { } + } + + // NEWT mouse listener + protected class NEWTMouseListener extends com.jogamp.newt.event.MouseAdapter { + public NEWTMouseListener() { + super(); + } + @Override + public void mousePressed(com.jogamp.newt.event.MouseEvent e) { + nativeMouseEvent(e, MouseEvent.PRESS); + } + @Override + public void mouseReleased(com.jogamp.newt.event.MouseEvent e) { + nativeMouseEvent(e, MouseEvent.RELEASE); + } + @Override + public void mouseClicked(com.jogamp.newt.event.MouseEvent e) { + nativeMouseEvent(e, MouseEvent.CLICK); + } + @Override + public void mouseDragged(com.jogamp.newt.event.MouseEvent e) { + nativeMouseEvent(e, MouseEvent.DRAG); + } + @Override + public void mouseMoved(com.jogamp.newt.event.MouseEvent e) { + nativeMouseEvent(e, MouseEvent.MOVE); + } + @Override + public void mouseWheelMoved(com.jogamp.newt.event.MouseEvent e) { + nativeMouseEvent(e, MouseEvent.WHEEL); + } + @Override + public void mouseEntered(com.jogamp.newt.event.MouseEvent e) { + nativeMouseEvent(e, MouseEvent.ENTER); + } + @Override + public void mouseExited(com.jogamp.newt.event.MouseEvent e) { + nativeMouseEvent(e, MouseEvent.EXIT); + } + } + + // NEWT key listener + protected class NEWTKeyListener extends com.jogamp.newt.event.KeyAdapter { + public NEWTKeyListener() { + super(); + } + @Override + public void keyPressed(com.jogamp.newt.event.KeyEvent e) { + nativeKeyEvent(e, KeyEvent.PRESS); + } + @Override + public void keyReleased(com.jogamp.newt.event.KeyEvent e) { + nativeKeyEvent(e, KeyEvent.RELEASE); + } + public void keyTyped(com.jogamp.newt.event.KeyEvent e) { + nativeKeyEvent(e, KeyEvent.TYPE); + } + } + + + /////////////////////////////////////////////////////////// + + // Utility functions + + + @Override + protected void enableTexturing(int target) { + if (PROFILE == 2) enable(target); + if (target == TEXTURE_2D) { + texturingTargets[0] = true; + } else if (target == TEXTURE_RECTANGLE) { + texturingTargets[1] = true; + } + } + + + @Override + protected void disableTexturing(int target) { + if (PROFILE == 2) disable(target); + if (target == TEXTURE_2D) { + texturingTargets[0] = false; + } else if (target == TEXTURE_RECTANGLE) { + texturingTargets[1] = false; + } + } + + + + @Override + protected int getFontAscent(Object font) { + FontMetrics metrics = pg.parent.getFontMetrics((Font)font); + return metrics.getAscent(); + } + + + @Override + protected int getFontDescent(Object font) { + FontMetrics metrics = pg.parent.getFontMetrics((Font)font); + return metrics.getDescent(); + } + + + @Override + protected int getTextWidth(Object font, char buffer[], int start, int stop) { + // maybe should use one of the newer/fancier functions for this? + int length = stop - start; + FontMetrics metrics = pg.parent.getFontMetrics((Font)font); + return metrics.charsWidth(buffer, start, length); + } + + + @Override + protected Object getDerivedFont(Object font, float size) { + return ((Font)font).deriveFont(size); + } + + + @Override + protected String[] loadVertexShader(String filename, int version) { + if (2 < PROFILE && version < 150) { + String[] fragSrc0 = pg.parent.loadStrings(filename); + return convertFragmentSource(fragSrc0, version, 150); + } else { + return pg.parent.loadStrings(filename); + } + } + + + @Override + protected String[] loadFragmentShader(String filename, int version) { + if (2 < PROFILE && version < 150) { + String[] vertSrc0 = pg.parent.loadStrings(filename); + return convertVertexSource(vertSrc0, version, 150); + } else { + return pg.parent.loadStrings(filename); + } + } + + + @Override + protected String[] loadFragmentShader(URL url, int version) { + try { + if (2 < PROFILE && version < 150) { + String[] fragSrc0 = PApplet.loadStrings(url.openStream()); + return convertFragmentSource(fragSrc0, version, 150); + } else { + return PApplet.loadStrings(url.openStream()); + } + } catch (IOException e) { + PGraphics.showException("Cannot load fragment shader " + url.getFile()); + } + return null; + } + + + @Override + protected String[] loadVertexShader(URL url, int version) { + try { + if (2 < PROFILE && version < 150) { + String[] vertSrc0 = PApplet.loadStrings(url.openStream()); + return convertVertexSource(vertSrc0, version, 150); + } else { + return PApplet.loadStrings(url.openStream()); + } + } catch (IOException e) { + PGraphics.showException("Cannot load vertex shader " + url.getFile()); + } + return null; + } + + + /////////////////////////////////////////////////////////// + + // Tessellator + + + @Override + protected Tessellator createTessellator(TessellatorCallback callback) { + return new Tessellator(callback); + } + + + protected class Tessellator implements PGL.Tessellator { + protected GLUtessellator tess; + protected TessellatorCallback callback; + protected GLUCallback gluCallback; + + public Tessellator(TessellatorCallback callback) { + this.callback = callback; + tess = GLU.gluNewTess(); + gluCallback = new GLUCallback(); + + GLU.gluTessCallback(tess, GLU.GLU_TESS_BEGIN, gluCallback); + GLU.gluTessCallback(tess, GLU.GLU_TESS_END, gluCallback); + GLU.gluTessCallback(tess, GLU.GLU_TESS_VERTEX, gluCallback); + GLU.gluTessCallback(tess, GLU.GLU_TESS_COMBINE, gluCallback); + GLU.gluTessCallback(tess, GLU.GLU_TESS_ERROR, gluCallback); + } + + @Override + public void beginPolygon() { + GLU.gluTessBeginPolygon(tess, null); + } + + @Override + public void endPolygon() { + GLU.gluTessEndPolygon(tess); + } + + @Override + public void setWindingRule(int rule) { + GLU.gluTessProperty(tess, GLU.GLU_TESS_WINDING_RULE, rule); + } + + @Override + public void beginContour() { + GLU.gluTessBeginContour(tess); + } + + @Override + public void endContour() { + GLU.gluTessEndContour(tess); + } + + @Override + public void addVertex(double[] v) { + GLU.gluTessVertex(tess, v, 0, v); + } + + protected class GLUCallback extends GLUtessellatorCallbackAdapter { + @Override + public void begin(int type) { + callback.begin(type); + } + + @Override + public void end() { + callback.end(); + } + + @Override + public void vertex(Object data) { + callback.vertex(data); + } + + @Override + public void combine(double[] coords, Object[] data, + float[] weight, Object[] outData) { + callback.combine(coords, data, weight, outData); + } + + @Override + public void error(int errnum) { + callback.error(errnum); + } + } + } + + + @Override + protected String tessError(int err) { + return glu.gluErrorString(err); + } + + + /////////////////////////////////////////////////////////// + + // Font outline + + + static { + SHAPE_TEXT_SUPPORTED = true; + SEG_MOVETO = PathIterator.SEG_MOVETO; + SEG_LINETO = PathIterator.SEG_LINETO; + SEG_QUADTO = PathIterator.SEG_QUADTO; + SEG_CUBICTO = PathIterator.SEG_CUBICTO; + SEG_CLOSE = PathIterator.SEG_CLOSE; + } + + + @Override + protected FontOutline createFontOutline(char ch, Object font) { + return new FontOutline(ch, font); + } + + + protected class FontOutline implements PGL.FontOutline { + PathIterator iter; + + public FontOutline(char ch, Object font) { + char textArray[] = new char[] { ch }; + Graphics2D graphics = (Graphics2D) pg.parent.getGraphics(); + FontRenderContext frc = graphics.getFontRenderContext(); + GlyphVector gv = ((Font)font).createGlyphVector(frc, textArray); + Shape shp = gv.getOutline(); + iter = shp.getPathIterator(null); + } + + public boolean isDone() { + return iter.isDone(); + } + + public int currentSegment(float coords[]) { + return iter.currentSegment(coords); + } + + public void next() { + iter.next(); + } + } + + + /////////////////////////////////////////////////////////// + + // Constants + + static { + FALSE = GL.GL_FALSE; + TRUE = GL.GL_TRUE; + + INT = GL2ES2.GL_INT; + BYTE = GL.GL_BYTE; + SHORT = GL.GL_SHORT; + FLOAT = GL.GL_FLOAT; + BOOL = GL2ES2.GL_BOOL; + UNSIGNED_INT = GL.GL_UNSIGNED_INT; + UNSIGNED_BYTE = GL.GL_UNSIGNED_BYTE; + UNSIGNED_SHORT = GL.GL_UNSIGNED_SHORT; + + RGB = GL.GL_RGB; + RGBA = GL.GL_RGBA; + ALPHA = GL.GL_ALPHA; + LUMINANCE = GL.GL_LUMINANCE; + LUMINANCE_ALPHA = GL.GL_LUMINANCE_ALPHA; + + UNSIGNED_SHORT_5_6_5 = GL.GL_UNSIGNED_SHORT_5_6_5; + UNSIGNED_SHORT_4_4_4_4 = GL.GL_UNSIGNED_SHORT_4_4_4_4; + UNSIGNED_SHORT_5_5_5_1 = GL.GL_UNSIGNED_SHORT_5_5_5_1; + + RGBA4 = GL.GL_RGBA4; + RGB5_A1 = GL.GL_RGB5_A1; + RGB565 = GL.GL_RGB565; + RGB8 = GL.GL_RGB8; + RGBA8 = GL.GL_RGBA8; + ALPHA8 = GL.GL_ALPHA8; + + READ_ONLY = GL2GL3.GL_READ_ONLY; + WRITE_ONLY = GL.GL_WRITE_ONLY; + READ_WRITE = GL2GL3.GL_READ_WRITE; + + TESS_WINDING_NONZERO = GLU.GLU_TESS_WINDING_NONZERO; + TESS_WINDING_ODD = GLU.GLU_TESS_WINDING_ODD; + + GENERATE_MIPMAP_HINT = GL.GL_GENERATE_MIPMAP_HINT; + FASTEST = GL.GL_FASTEST; + NICEST = GL.GL_NICEST; + DONT_CARE = GL.GL_DONT_CARE; + + VENDOR = GL.GL_VENDOR; + RENDERER = GL.GL_RENDERER; + VERSION = GL.GL_VERSION; + EXTENSIONS = GL.GL_EXTENSIONS; + SHADING_LANGUAGE_VERSION = GL2ES2.GL_SHADING_LANGUAGE_VERSION; + + MAX_SAMPLES = GL2ES3.GL_MAX_SAMPLES; + SAMPLES = GL.GL_SAMPLES; + + ALIASED_LINE_WIDTH_RANGE = GL.GL_ALIASED_LINE_WIDTH_RANGE; + ALIASED_POINT_SIZE_RANGE = GL.GL_ALIASED_POINT_SIZE_RANGE; + + DEPTH_BITS = GL.GL_DEPTH_BITS; + STENCIL_BITS = GL.GL_STENCIL_BITS; + + CCW = GL.GL_CCW; + CW = GL.GL_CW; + + VIEWPORT = GL.GL_VIEWPORT; + + ARRAY_BUFFER = GL.GL_ARRAY_BUFFER; + ELEMENT_ARRAY_BUFFER = GL.GL_ELEMENT_ARRAY_BUFFER; + + MAX_VERTEX_ATTRIBS = GL2ES2.GL_MAX_VERTEX_ATTRIBS; + + STATIC_DRAW = GL.GL_STATIC_DRAW; + DYNAMIC_DRAW = GL.GL_DYNAMIC_DRAW; + STREAM_DRAW = GL2ES2.GL_STREAM_DRAW; + + BUFFER_SIZE = GL.GL_BUFFER_SIZE; + BUFFER_USAGE = GL.GL_BUFFER_USAGE; + + POINTS = GL.GL_POINTS; + LINE_STRIP = GL.GL_LINE_STRIP; + LINE_LOOP = GL.GL_LINE_LOOP; + LINES = GL.GL_LINES; + TRIANGLE_FAN = GL.GL_TRIANGLE_FAN; + TRIANGLE_STRIP = GL.GL_TRIANGLE_STRIP; + TRIANGLES = GL.GL_TRIANGLES; + + CULL_FACE = GL.GL_CULL_FACE; + FRONT = GL.GL_FRONT; + BACK = GL.GL_BACK; + FRONT_AND_BACK = GL.GL_FRONT_AND_BACK; + + POLYGON_OFFSET_FILL = GL.GL_POLYGON_OFFSET_FILL; + + UNPACK_ALIGNMENT = GL.GL_UNPACK_ALIGNMENT; + PACK_ALIGNMENT = GL.GL_PACK_ALIGNMENT; + + TEXTURE_2D = GL.GL_TEXTURE_2D; + TEXTURE_RECTANGLE = GL2GL3.GL_TEXTURE_RECTANGLE; + + TEXTURE_BINDING_2D = GL.GL_TEXTURE_BINDING_2D; + TEXTURE_BINDING_RECTANGLE = GL2GL3.GL_TEXTURE_BINDING_RECTANGLE; + + MAX_TEXTURE_SIZE = GL.GL_MAX_TEXTURE_SIZE; + TEXTURE_MAX_ANISOTROPY = GL.GL_TEXTURE_MAX_ANISOTROPY_EXT; + MAX_TEXTURE_MAX_ANISOTROPY = GL.GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT; + + MAX_VERTEX_TEXTURE_IMAGE_UNITS = GL2ES2.GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS; + MAX_TEXTURE_IMAGE_UNITS = GL2ES2.GL_MAX_TEXTURE_IMAGE_UNITS; + MAX_COMBINED_TEXTURE_IMAGE_UNITS = GL2ES2.GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS; + + NUM_COMPRESSED_TEXTURE_FORMATS = GL.GL_NUM_COMPRESSED_TEXTURE_FORMATS; + COMPRESSED_TEXTURE_FORMATS = GL.GL_COMPRESSED_TEXTURE_FORMATS; + + NEAREST = GL.GL_NEAREST; + LINEAR = GL.GL_LINEAR; + LINEAR_MIPMAP_NEAREST = GL.GL_LINEAR_MIPMAP_NEAREST; + LINEAR_MIPMAP_LINEAR = GL.GL_LINEAR_MIPMAP_LINEAR; + + CLAMP_TO_EDGE = GL.GL_CLAMP_TO_EDGE; + REPEAT = GL.GL_REPEAT; + + TEXTURE0 = GL.GL_TEXTURE0; + TEXTURE1 = GL.GL_TEXTURE1; + TEXTURE2 = GL.GL_TEXTURE2; + TEXTURE3 = GL.GL_TEXTURE3; + TEXTURE_MIN_FILTER = GL.GL_TEXTURE_MIN_FILTER; + TEXTURE_MAG_FILTER = GL.GL_TEXTURE_MAG_FILTER; + TEXTURE_WRAP_S = GL.GL_TEXTURE_WRAP_S; + TEXTURE_WRAP_T = GL.GL_TEXTURE_WRAP_T; + TEXTURE_WRAP_R = GL2ES2.GL_TEXTURE_WRAP_R; + + TEXTURE_CUBE_MAP = GL.GL_TEXTURE_CUBE_MAP; + TEXTURE_CUBE_MAP_POSITIVE_X = GL.GL_TEXTURE_CUBE_MAP_POSITIVE_X; + TEXTURE_CUBE_MAP_POSITIVE_Y = GL.GL_TEXTURE_CUBE_MAP_POSITIVE_Y; + TEXTURE_CUBE_MAP_POSITIVE_Z = GL.GL_TEXTURE_CUBE_MAP_POSITIVE_Z; + TEXTURE_CUBE_MAP_NEGATIVE_X = GL.GL_TEXTURE_CUBE_MAP_NEGATIVE_X; + TEXTURE_CUBE_MAP_NEGATIVE_Y = GL.GL_TEXTURE_CUBE_MAP_NEGATIVE_Y; + TEXTURE_CUBE_MAP_NEGATIVE_Z = GL.GL_TEXTURE_CUBE_MAP_NEGATIVE_Z; + + VERTEX_SHADER = GL2ES2.GL_VERTEX_SHADER; + FRAGMENT_SHADER = GL2ES2.GL_FRAGMENT_SHADER; + INFO_LOG_LENGTH = GL2ES2.GL_INFO_LOG_LENGTH; + SHADER_SOURCE_LENGTH = GL2ES2.GL_SHADER_SOURCE_LENGTH; + COMPILE_STATUS = GL2ES2.GL_COMPILE_STATUS; + LINK_STATUS = GL2ES2.GL_LINK_STATUS; + VALIDATE_STATUS = GL2ES2.GL_VALIDATE_STATUS; + SHADER_TYPE = GL2ES2.GL_SHADER_TYPE; + DELETE_STATUS = GL2ES2.GL_DELETE_STATUS; + + FLOAT_VEC2 = GL2ES2.GL_FLOAT_VEC2; + FLOAT_VEC3 = GL2ES2.GL_FLOAT_VEC3; + FLOAT_VEC4 = GL2ES2.GL_FLOAT_VEC4; + FLOAT_MAT2 = GL2ES2.GL_FLOAT_MAT2; + FLOAT_MAT3 = GL2ES2.GL_FLOAT_MAT3; + FLOAT_MAT4 = GL2ES2.GL_FLOAT_MAT4; + INT_VEC2 = GL2ES2.GL_INT_VEC2; + INT_VEC3 = GL2ES2.GL_INT_VEC3; + INT_VEC4 = GL2ES2.GL_INT_VEC4; + BOOL_VEC2 = GL2ES2.GL_BOOL_VEC2; + BOOL_VEC3 = GL2ES2.GL_BOOL_VEC3; + BOOL_VEC4 = GL2ES2.GL_BOOL_VEC4; + SAMPLER_2D = GL2ES2.GL_SAMPLER_2D; + SAMPLER_CUBE = GL2ES2.GL_SAMPLER_CUBE; + + LOW_FLOAT = GL2ES2.GL_LOW_FLOAT; + MEDIUM_FLOAT = GL2ES2.GL_MEDIUM_FLOAT; + HIGH_FLOAT = GL2ES2.GL_HIGH_FLOAT; + LOW_INT = GL2ES2.GL_LOW_INT; + MEDIUM_INT = GL2ES2.GL_MEDIUM_INT; + HIGH_INT = GL2ES2.GL_HIGH_INT; + + CURRENT_VERTEX_ATTRIB = GL2ES2.GL_CURRENT_VERTEX_ATTRIB; + + VERTEX_ATTRIB_ARRAY_BUFFER_BINDING = GL2ES2.GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING; + VERTEX_ATTRIB_ARRAY_ENABLED = GL2ES2.GL_VERTEX_ATTRIB_ARRAY_ENABLED; + VERTEX_ATTRIB_ARRAY_SIZE = GL2ES2.GL_VERTEX_ATTRIB_ARRAY_SIZE; + VERTEX_ATTRIB_ARRAY_STRIDE = GL2ES2.GL_VERTEX_ATTRIB_ARRAY_STRIDE; + VERTEX_ATTRIB_ARRAY_TYPE = GL2ES2.GL_VERTEX_ATTRIB_ARRAY_TYPE; + VERTEX_ATTRIB_ARRAY_NORMALIZED = GL2ES2.GL_VERTEX_ATTRIB_ARRAY_NORMALIZED; + VERTEX_ATTRIB_ARRAY_POINTER = GL2ES2.GL_VERTEX_ATTRIB_ARRAY_POINTER; + + BLEND = GL.GL_BLEND; + ONE = GL.GL_ONE; + ZERO = GL.GL_ZERO; + SRC_ALPHA = GL.GL_SRC_ALPHA; + DST_ALPHA = GL.GL_DST_ALPHA; + ONE_MINUS_SRC_ALPHA = GL.GL_ONE_MINUS_SRC_ALPHA; + ONE_MINUS_DST_COLOR = GL.GL_ONE_MINUS_DST_COLOR; + ONE_MINUS_SRC_COLOR = GL.GL_ONE_MINUS_SRC_COLOR; + DST_COLOR = GL.GL_DST_COLOR; + SRC_COLOR = GL.GL_SRC_COLOR; + + SAMPLE_ALPHA_TO_COVERAGE = GL.GL_SAMPLE_ALPHA_TO_COVERAGE; + SAMPLE_COVERAGE = GL.GL_SAMPLE_COVERAGE; + + KEEP = GL.GL_KEEP; + REPLACE = GL.GL_REPLACE; + INCR = GL.GL_INCR; + DECR = GL.GL_DECR; + INVERT = GL.GL_INVERT; + INCR_WRAP = GL.GL_INCR_WRAP; + DECR_WRAP = GL.GL_DECR_WRAP; + NEVER = GL.GL_NEVER; + ALWAYS = GL.GL_ALWAYS; + + EQUAL = GL.GL_EQUAL; + LESS = GL.GL_LESS; + LEQUAL = GL.GL_LEQUAL; + GREATER = GL.GL_GREATER; + GEQUAL = GL.GL_GEQUAL; + NOTEQUAL = GL.GL_NOTEQUAL; + + FUNC_ADD = GL.GL_FUNC_ADD; + FUNC_MIN = GL2ES3.GL_MIN; + FUNC_MAX = GL2ES3.GL_MAX; + FUNC_REVERSE_SUBTRACT = GL.GL_FUNC_REVERSE_SUBTRACT; + FUNC_SUBTRACT = GL.GL_FUNC_SUBTRACT; + + DITHER = GL.GL_DITHER; + + CONSTANT_COLOR = GL2ES2.GL_CONSTANT_COLOR; + CONSTANT_ALPHA = GL2ES2.GL_CONSTANT_ALPHA; + ONE_MINUS_CONSTANT_COLOR = GL2ES2.GL_ONE_MINUS_CONSTANT_COLOR; + ONE_MINUS_CONSTANT_ALPHA = GL2ES2.GL_ONE_MINUS_CONSTANT_ALPHA; + SRC_ALPHA_SATURATE = GL.GL_SRC_ALPHA_SATURATE; + + SCISSOR_TEST = GL.GL_SCISSOR_TEST; + STENCIL_TEST = GL.GL_STENCIL_TEST; + DEPTH_TEST = GL.GL_DEPTH_TEST; + DEPTH_WRITEMASK = GL.GL_DEPTH_WRITEMASK; + ALPHA_TEST = GL2ES1.GL_ALPHA_TEST; + + COLOR_BUFFER_BIT = GL.GL_COLOR_BUFFER_BIT; + DEPTH_BUFFER_BIT = GL.GL_DEPTH_BUFFER_BIT; + STENCIL_BUFFER_BIT = GL.GL_STENCIL_BUFFER_BIT; + + FRAMEBUFFER = GL.GL_FRAMEBUFFER; + COLOR_ATTACHMENT0 = GL.GL_COLOR_ATTACHMENT0; + COLOR_ATTACHMENT1 = GL2ES2.GL_COLOR_ATTACHMENT1; + COLOR_ATTACHMENT2 = GL2ES2.GL_COLOR_ATTACHMENT2; + COLOR_ATTACHMENT3 = GL2ES2.GL_COLOR_ATTACHMENT3; + RENDERBUFFER = GL.GL_RENDERBUFFER; + DEPTH_ATTACHMENT = GL.GL_DEPTH_ATTACHMENT; + STENCIL_ATTACHMENT = GL.GL_STENCIL_ATTACHMENT; + READ_FRAMEBUFFER = GL2ES3.GL_READ_FRAMEBUFFER; + DRAW_FRAMEBUFFER = GL2ES3.GL_DRAW_FRAMEBUFFER; + + RGBA8 = GL.GL_RGBA8; + DEPTH24_STENCIL8 = GL.GL_DEPTH24_STENCIL8; + + DEPTH_COMPONENT = GL2ES2.GL_DEPTH_COMPONENT; + DEPTH_COMPONENT16 = GL.GL_DEPTH_COMPONENT16; + DEPTH_COMPONENT24 = GL.GL_DEPTH_COMPONENT24; + DEPTH_COMPONENT32 = GL.GL_DEPTH_COMPONENT32; + + STENCIL_INDEX = GL2ES2.GL_STENCIL_INDEX; + STENCIL_INDEX1 = GL.GL_STENCIL_INDEX1; + STENCIL_INDEX4 = GL.GL_STENCIL_INDEX4; + STENCIL_INDEX8 = GL.GL_STENCIL_INDEX8; + + DEPTH_STENCIL = GL.GL_DEPTH_STENCIL; + + FRAMEBUFFER_COMPLETE = GL.GL_FRAMEBUFFER_COMPLETE; + FRAMEBUFFER_INCOMPLETE_ATTACHMENT = GL.GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT; + FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT = GL.GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT; + FRAMEBUFFER_INCOMPLETE_DIMENSIONS = GL.GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS; + FRAMEBUFFER_INCOMPLETE_FORMATS = GL.GL_FRAMEBUFFER_INCOMPLETE_FORMATS; + FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER = GL2GL3.GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER; + FRAMEBUFFER_INCOMPLETE_READ_BUFFER = GL2GL3.GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER; + FRAMEBUFFER_UNSUPPORTED = GL.GL_FRAMEBUFFER_UNSUPPORTED; + + FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE = GL.GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE; + FRAMEBUFFER_ATTACHMENT_OBJECT_NAME = GL.GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME; + FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL = GL.GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL; + FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE = GL.GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE; + + RENDERBUFFER_WIDTH = GL.GL_RENDERBUFFER_WIDTH; + RENDERBUFFER_HEIGHT = GL.GL_RENDERBUFFER_HEIGHT; + RENDERBUFFER_RED_SIZE = GL.GL_RENDERBUFFER_RED_SIZE; + RENDERBUFFER_GREEN_SIZE = GL.GL_RENDERBUFFER_GREEN_SIZE; + RENDERBUFFER_BLUE_SIZE = GL.GL_RENDERBUFFER_BLUE_SIZE; + RENDERBUFFER_ALPHA_SIZE = GL.GL_RENDERBUFFER_ALPHA_SIZE; + RENDERBUFFER_DEPTH_SIZE = GL.GL_RENDERBUFFER_DEPTH_SIZE; + RENDERBUFFER_STENCIL_SIZE = GL.GL_RENDERBUFFER_STENCIL_SIZE; + RENDERBUFFER_INTERNAL_FORMAT = GL.GL_RENDERBUFFER_INTERNAL_FORMAT; + + MULTISAMPLE = GL.GL_MULTISAMPLE; + POINT_SMOOTH = GL2ES1.GL_POINT_SMOOTH; + LINE_SMOOTH = GL.GL_LINE_SMOOTH; + POLYGON_SMOOTH = GL2GL3.GL_POLYGON_SMOOTH; + } + + /////////////////////////////////////////////////////////// + + // Special Functions + + @Override + public void flush() { + gl.glFlush(); + } + + @Override + public void finish() { + gl.glFinish(); + } + + @Override + public void hint(int target, int hint) { + gl.glHint(target, hint); + } + + /////////////////////////////////////////////////////////// + + // State and State Requests + + @Override + public void enable(int value) { + if (-1 < value) { + gl.glEnable(value); + } + } + + @Override + public void disable(int value) { + if (-1 < value) { + gl.glDisable(value); + } + } + + @Override + public void getBooleanv(int value, IntBuffer data) { + if (-1 < value) { + if (byteBuffer.capacity() < data.capacity()) { + byteBuffer = allocateDirectByteBuffer(data.capacity()); + } + gl.glGetBooleanv(value, byteBuffer); + for (int i = 0; i < data.capacity(); i++) { + data.put(i, byteBuffer.get(i)); + } + } else { + fillIntBuffer(data, 0, data.capacity() - 1, 0); + } + } + + @Override + public void getIntegerv(int value, IntBuffer data) { + if (-1 < value) { + gl.glGetIntegerv(value, data); + } else { + fillIntBuffer(data, 0, data.capacity() - 1, 0); + } + } + + @Override + public void getFloatv(int value, FloatBuffer data) { + if (-1 < value) { + gl.glGetFloatv(value, data); + } else { + fillFloatBuffer(data, 0, data.capacity() - 1, 0); + } + } + + @Override + public boolean isEnabled(int value) { + return gl.glIsEnabled(value); + } + + @Override + public String getString(int name) { + return gl.glGetString(name); + } + + /////////////////////////////////////////////////////////// + + // Error Handling + + @Override + public int getError() { + return gl.glGetError(); + } + + @Override + public String errorString(int err) { + return glu.gluErrorString(err); + } + + ////////////////////////////////////////////////////////////////////////////// + + // Buffer Objects + + @Override + public void genBuffers(int n, IntBuffer buffers) { + gl.glGenBuffers(n, buffers); + } + + @Override + public void deleteBuffers(int n, IntBuffer buffers) { + gl.glDeleteBuffers(n, buffers); + } + + @Override + public void bindBuffer(int target, int buffer) { + gl.glBindBuffer(target, buffer); + } + + @Override + public void bufferData(int target, int size, Buffer data, int usage) { + gl.glBufferData(target, size, data, usage); + } + + @Override + public void bufferSubData(int target, int offset, int size, Buffer data) { + gl.glBufferSubData(target, offset, size, data); + } + + @Override + public void isBuffer(int buffer) { + gl.glIsBuffer(buffer); + } + + @Override + public void getBufferParameteriv(int target, int value, IntBuffer data) { + gl.glGetBufferParameteriv(target, value, data); + } + + @Override + public ByteBuffer mapBuffer(int target, int access) { + return gl2.glMapBuffer(target, access); + } + + @Override + public ByteBuffer mapBufferRange(int target, int offset, int length, int access) { + if (gl2x != null) { + return gl2x.glMapBufferRange(target, offset, length, access); + } else if (gl3 != null) { + return gl3.glMapBufferRange(target, offset, length, access); + } else { + throw new RuntimeException(String.format(MISSING_GLFUNC_ERROR, "glMapBufferRange()")); + } + } + + @Override + public void unmapBuffer(int target) { + gl2.glUnmapBuffer(target); + } + + ////////////////////////////////////////////////////////////////////////////// + + // Viewport and Clipping + + @Override + public void depthRangef(float n, float f) { + gl.glDepthRangef(n, f); + } + + @Override + public void viewport(int x, int y, int w, int h) { + gl.glViewport(x, y, w, h); + } + + ////////////////////////////////////////////////////////////////////////////// + + // Reading Pixels + + @Override + protected void readPixelsImpl(int x, int y, int width, int height, int format, int type, Buffer buffer) { + gl.glReadPixels(x, y, width, height, format, type, buffer); + } + + ////////////////////////////////////////////////////////////////////////////// + + // Vertices + + @Override + public void vertexAttrib1f(int index, float value) { + gl2.glVertexAttrib1f(index, value); + } + + @Override + public void vertexAttrib2f(int index, float value0, float value1) { + gl2.glVertexAttrib2f(index, value0, value1); + } + + @Override + public void vertexAttrib3f(int index, float value0, float value1, float value2) { + gl2.glVertexAttrib3f(index, value0, value1, value2); + } + + @Override + public void vertexAttrib4f(int index, float value0, float value1, float value2, float value3) { + gl2.glVertexAttrib4f(index, value0, value1, value2, value3); + } + + @Override + public void vertexAttrib1fv(int index, FloatBuffer values) { + gl2.glVertexAttrib1fv(index, values); + } + + @Override + public void vertexAttrib2fv(int index, FloatBuffer values) { + gl2.glVertexAttrib2fv(index, values); + } + + @Override + public void vertexAttrib3fv(int index, FloatBuffer values) { + gl2.glVertexAttrib3fv(index, values); + } + + @Override + public void vertexAttri4fv(int index, FloatBuffer values) { + gl2.glVertexAttrib4fv(index, values); + } + + @Override + public void vertexAttribPointer(int index, int size, int type, boolean normalized, int stride, int offset) { + gl2.glVertexAttribPointer(index, size, type, normalized, stride, offset); + } + + @Override + public void vertexAttribPointer(int index, int size, int type, boolean normalized, int stride, Buffer data) { + if (gl2x != null) { + gl2x.glVertexAttribPointer(index, size, type, normalized, stride, data); + } else { + throw new RuntimeException(String.format(MISSING_GLFUNC_ERROR, "glVertexAttribPointer()")); + } + } + + @Override + public void enableVertexAttribArray(int index) { + gl2.glEnableVertexAttribArray(index); + } + + @Override + public void disableVertexAttribArray(int index) { + gl2.glDisableVertexAttribArray(index); + } + + @Override + public void drawArrays(int mode, int first, int count) { + gl.glDrawArrays(mode, first, count); + } + + @Override + public void drawElements(int mode, int count, int type, int offset) { + gl.glDrawElements(mode, count, type, offset); + } + + @Override + public void drawElements(int mode, int count, int type, Buffer indices) { + if (gl2x != null) { + gl2x.glDrawElements(mode, count, type, indices); + } else { + throw new RuntimeException(String.format(MISSING_GLFUNC_ERROR, "glDrawElements()")); + } + } + + ////////////////////////////////////////////////////////////////////////////// + + // Rasterization + + @Override + public void lineWidth(float width) { + gl.glLineWidth(width); + } + + @Override + public void frontFace(int dir) { + gl.glFrontFace(dir); + } + + @Override + public void cullFace(int mode) { + gl.glCullFace(mode); + } + + @Override + public void polygonOffset(float factor, float units) { + gl.glPolygonOffset(factor, units); + } + + ////////////////////////////////////////////////////////////////////////////// + + // Pixel Rectangles + + @Override + public void pixelStorei(int pname, int param) { + gl.glPixelStorei(pname, param); + } + + /////////////////////////////////////////////////////////// + + // Texturing + + @Override + public void texImage2D(int target, int level, int internalFormat, int width, int height, int border, int format, int type, Buffer data) { + gl.glTexImage2D(target, level, internalFormat, width, height, border, format, type, data); + } + + @Override + public void copyTexImage2D(int target, int level, int internalFormat, int x, int y, int width, int height, int border) { + gl.glCopyTexImage2D(target, level, internalFormat, x, y, width, height, border); + } + + @Override + public void texSubImage2D(int target, int level, int xOffset, int yOffset, int width, int height, int format, int type, Buffer data) { + gl.glTexSubImage2D(target, level, xOffset, yOffset, width, height, format, type, data); + } + + @Override + public void copyTexSubImage2D(int target, int level, int xOffset, int yOffset, int x, int y, int width, int height) { + gl.glCopyTexSubImage2D(target, level, x, y, xOffset, xOffset, width, height); + } + + @Override + public void compressedTexImage2D(int target, int level, int internalFormat, int width, int height, int border, int imageSize, Buffer data) { + gl.glCompressedTexImage2D(target, level, internalFormat, width, height, border, imageSize, data); + } + + @Override + public void compressedTexSubImage2D(int target, int level, int xOffset, int yOffset, int width, int height, int format, int imageSize, Buffer data) { + gl.glCompressedTexSubImage2D(target, level, xOffset, yOffset, width, height, format, imageSize, data); + } + + @Override + public void texParameteri(int target, int pname, int param) { + gl.glTexParameteri(target, pname, param); + } + + @Override + public void texParameterf(int target, int pname, float param) { + gl.glTexParameterf(target, pname, param); + } + + @Override + public void texParameteriv(int target, int pname, IntBuffer params) { + gl.glTexParameteriv(target, pname, params); + } + + @Override + public void texParameterfv(int target, int pname, FloatBuffer params) { + gl.glTexParameterfv(target, pname, params); + } + + @Override + public void generateMipmap(int target) { + gl.glGenerateMipmap(target); + } + + @Override + public void genTextures(int n, IntBuffer textures) { + gl.glGenTextures(n, textures); + } + + @Override + public void deleteTextures(int n, IntBuffer textures) { + gl.glDeleteTextures(n, textures); + } + + @Override + public void getTexParameteriv(int target, int pname, IntBuffer params) { + gl.glGetTexParameteriv(target, pname, params); + } + + @Override + public void getTexParameterfv(int target, int pname, FloatBuffer params) { + gl.glGetTexParameterfv(target, pname, params); + } + + @Override + public boolean isTexture(int texture) { + return gl.glIsTexture(texture); + } + + @Override + protected void activeTextureImpl(int texture) { + gl.glActiveTexture(texture); + } + + @Override + protected void bindTextureImpl(int target, int texture) { + gl.glBindTexture(target, texture); + } + + /////////////////////////////////////////////////////////// + + // Shaders and Programs + + @Override + public int createShader(int type) { + return gl2.glCreateShader(type); + } + + @Override + public void shaderSource(int shader, String source) { + gl2.glShaderSource(shader, 1, new String[] { source }, (int[]) null, 0); + } + + @Override + public void compileShader(int shader) { + gl2.glCompileShader(shader); + } + + @Override + public void releaseShaderCompiler() { + gl2.glReleaseShaderCompiler(); + } + + @Override + public void deleteShader(int shader) { + gl2.glDeleteShader(shader); + } + + @Override + public void shaderBinary(int count, IntBuffer shaders, int binaryFormat, Buffer binary, int length) { + gl2.glShaderBinary(count, shaders, binaryFormat, binary, length); + } + + @Override + public int createProgram() { + return gl2.glCreateProgram(); + } + + @Override + public void attachShader(int program, int shader) { + gl2.glAttachShader(program, shader); + } + + @Override + public void detachShader(int program, int shader) { + gl2.glDetachShader(program, shader); + } + + @Override + public void linkProgram(int program) { + gl2.glLinkProgram(program); + } + + @Override + public void useProgram(int program) { + gl2.glUseProgram(program); + } + + @Override + public void deleteProgram(int program) { + gl2.glDeleteProgram(program); + } + + @Override + public String getActiveAttrib(int program, int index, IntBuffer size, IntBuffer type) { + int[] tmp = {0, 0, 0}; + byte[] namebuf = new byte[1024]; + gl2.glGetActiveAttrib(program, index, 1024, tmp, 0, tmp, 1, tmp, 2, namebuf, 0); + size.put(tmp[1]); + type.put(tmp[2]); + String name = new String(namebuf, 0, tmp[0]); + return name; + } + + @Override + public int getAttribLocation(int program, String name) { + return gl2.glGetAttribLocation(program, name); + } + + @Override + public void bindAttribLocation(int program, int index, String name) { + gl2.glBindAttribLocation(program, index, name); + } + + @Override + public int getUniformLocation(int program, String name) { + return gl2.glGetUniformLocation(program, name); + } + + @Override + public String getActiveUniform(int program, int index, IntBuffer size, IntBuffer type) { + int[] tmp= {0, 0, 0}; + byte[] namebuf = new byte[1024]; + gl2.glGetActiveUniform(program, index, 1024, tmp, 0, tmp, 1, tmp, 2, namebuf, 0); + size.put(tmp[1]); + type.put(tmp[2]); + String name = new String(namebuf, 0, tmp[0]); + return name; + } + + @Override + public void uniform1i(int location, int value) { + gl2.glUniform1i(location, value); + } + + @Override + public void uniform2i(int location, int value0, int value1) { + gl2.glUniform2i(location, value0, value1); + } + + @Override + public void uniform3i(int location, int value0, int value1, int value2) { + gl2.glUniform3i(location, value0, value1, value2); + } + + @Override + public void uniform4i(int location, int value0, int value1, int value2, int value3) { + gl2.glUniform4i(location, value0, value1, value2, value3); + } + + @Override + public void uniform1f(int location, float value) { + gl2.glUniform1f(location, value); + } + + @Override + public void uniform2f(int location, float value0, float value1) { + gl2.glUniform2f(location, value0, value1); + } + + @Override + public void uniform3f(int location, float value0, float value1, float value2) { + gl2.glUniform3f(location, value0, value1, value2); + } + + @Override + public void uniform4f(int location, float value0, float value1, float value2, float value3) { + gl2.glUniform4f(location, value0, value1, value2, value3); + } + + @Override + public void uniform1iv(int location, int count, IntBuffer v) { + gl2.glUniform1iv(location, count, v); + } + + @Override + public void uniform2iv(int location, int count, IntBuffer v) { + gl2.glUniform2iv(location, count, v); + } + + @Override + public void uniform3iv(int location, int count, IntBuffer v) { + gl2.glUniform3iv(location, count, v); + } + + @Override + public void uniform4iv(int location, int count, IntBuffer v) { + gl2.glUniform4iv(location, count, v); + } + + @Override + public void uniform1fv(int location, int count, FloatBuffer v) { + gl2.glUniform1fv(location, count, v); + } + + @Override + public void uniform2fv(int location, int count, FloatBuffer v) { + gl2.glUniform2fv(location, count, v); + } + + @Override + public void uniform3fv(int location, int count, FloatBuffer v) { + gl2.glUniform3fv(location, count, v); + } + + @Override + public void uniform4fv(int location, int count, FloatBuffer v) { + gl2.glUniform4fv(location, count, v); + } + + @Override + public void uniformMatrix2fv(int location, int count, boolean transpose, FloatBuffer mat) { + gl2.glUniformMatrix2fv(location, count, transpose, mat); + } + + @Override + public void uniformMatrix3fv(int location, int count, boolean transpose, FloatBuffer mat) { + gl2.glUniformMatrix3fv(location, count, transpose, mat); + } + + @Override + public void uniformMatrix4fv(int location, int count, boolean transpose, FloatBuffer mat) { + gl2.glUniformMatrix4fv(location, count, transpose, mat); + } + + @Override + public void validateProgram(int program) { + gl2.glValidateProgram(program); + } + + @Override + public boolean isShader(int shader) { + return gl2.glIsShader(shader); + } + + @Override + public void getShaderiv(int shader, int pname, IntBuffer params) { + gl2.glGetShaderiv(shader, pname, params); + } + + @Override + public void getAttachedShaders(int program, int maxCount, IntBuffer count, IntBuffer shaders) { + gl2.glGetAttachedShaders(program, maxCount, count, shaders); + } + + @Override + public String getShaderInfoLog(int shader) { + int[] val = { 0 }; + gl2.glGetShaderiv(shader, GL2ES2.GL_INFO_LOG_LENGTH, val, 0); + int length = val[0]; + + byte[] log = new byte[length]; + gl2.glGetShaderInfoLog(shader, length, val, 0, log, 0); + return new String(log); + } + + @Override + public String getShaderSource(int shader) { + int[] len = {0}; + byte[] buf = new byte[1024]; + gl2.glGetShaderSource(shader, 1024, len, 0, buf, 0); + return new String(buf, 0, len[0]); + } + + @Override + public void getShaderPrecisionFormat(int shaderType, int precisionType, IntBuffer range, IntBuffer precision) { + gl2.glGetShaderPrecisionFormat(shaderType, precisionType, range, precision); + } + + @Override + public void getVertexAttribfv(int index, int pname, FloatBuffer params) { + gl2.glGetVertexAttribfv(index, pname, params); + } + + @Override + public void getVertexAttribiv(int index, int pname, IntBuffer params) { + gl2.glGetVertexAttribiv(index, pname, params); + } + + @Override + public void getVertexAttribPointerv(int index, int pname, ByteBuffer data) { + throw new RuntimeException(String.format(MISSING_GLFUNC_ERROR, "glGetVertexAttribPointerv()")); + } + + @Override + public void getUniformfv(int program, int location, FloatBuffer params) { + gl2.glGetUniformfv(program, location, params); + } + + @Override + public void getUniformiv(int program, int location, IntBuffer params) { + gl2.glGetUniformiv(program, location, params); + } + + @Override + public boolean isProgram(int program) { + return gl2.glIsProgram(program); + } + + @Override + public void getProgramiv(int program, int pname, IntBuffer params) { + gl2.glGetProgramiv(program, pname, params); + } + + @Override + public String getProgramInfoLog(int program) { + int[] val = { 0 }; + gl2.glGetShaderiv(program, GL2ES2.GL_INFO_LOG_LENGTH, val, 0); + int length = val[0]; + + if (0 < length) { + byte[] log = new byte[length]; + gl2.glGetProgramInfoLog(program, length, val, 0, log, 0); + return new String(log); + } else { + return "Unknow error"; + } + } + + /////////////////////////////////////////////////////////// + + // Per-Fragment Operations + + @Override + public void scissor(int x, int y, int w, int h) { + gl.glScissor(x, y, w, h); + } + + @Override + public void sampleCoverage(float value, boolean invert) { + gl2.glSampleCoverage(value, invert); + } + + @Override + public void stencilFunc(int func, int ref, int mask) { + gl2.glStencilFunc(func, ref, mask); + } + + @Override + public void stencilFuncSeparate(int face, int func, int ref, int mask) { + gl2.glStencilFuncSeparate(face, func, ref, mask); + } + + @Override + public void stencilOp(int sfail, int dpfail, int dppass) { + gl2.glStencilOp(sfail, dpfail, dppass); + } + + @Override + public void stencilOpSeparate(int face, int sfail, int dpfail, int dppass) { + gl2.glStencilOpSeparate(face, sfail, dpfail, dppass); + } + + @Override + public void depthFunc(int func) { + gl.glDepthFunc(func); + } + + @Override + public void blendEquation(int mode) { + gl.glBlendEquation(mode); + } + + @Override + public void blendEquationSeparate(int modeRGB, int modeAlpha) { + gl.glBlendEquationSeparate(modeRGB, modeAlpha); + } + + @Override + public void blendFunc(int src, int dst) { + gl.glBlendFunc(src, dst); + } + + @Override + public void blendFuncSeparate(int srcRGB, int dstRGB, int srcAlpha, int dstAlpha) { + gl.glBlendFuncSeparate(srcRGB, dstRGB, srcAlpha, dstAlpha); + } + + @Override + public void blendColor(float red, float green, float blue, float alpha) { + gl2.glBlendColor(red, green, blue, alpha); + } + + @Override + public void alphaFunc(int func, float ref) { + if (gl2x != null) { + gl2x.glAlphaFunc(func, ref); + } else { + throw new RuntimeException(String.format(MISSING_GLFUNC_ERROR, "glAlphaFunc()")); + } + } + + /////////////////////////////////////////////////////////// + + // Whole Framebuffer Operations + + @Override + public void colorMask(boolean r, boolean g, boolean b, boolean a) { + gl.glColorMask(r, g, b, a); + } + + @Override + public void depthMask(boolean mask) { + gl.glDepthMask(mask); + } + + @Override + public void stencilMask(int mask) { + gl.glStencilMask(mask); + } + + @Override + public void stencilMaskSeparate(int face, int mask) { + gl2.glStencilMaskSeparate(face, mask); + } + + @Override + public void clear(int buf) { + gl.glClear(buf); + } + + @Override + public void clearColor(float r, float g, float b, float a) { + gl.glClearColor(r, g, b, a); + } + + @Override + public void clearDepth(float d) { + gl.glClearDepthf(d); + } + + @Override + public void clearStencil(int s) { + gl.glClearStencil(s); + } + + /////////////////////////////////////////////////////////// + + // Framebuffers Objects + + @Override + protected void bindFramebufferImpl(int target, int framebuffer) { + gl.glBindFramebuffer(target, framebuffer); + } + + @Override + public void deleteFramebuffers(int n, IntBuffer framebuffers) { + gl.glDeleteFramebuffers(n, framebuffers); + } + + @Override + public void genFramebuffers(int n, IntBuffer framebuffers) { + gl.glGenFramebuffers(n, framebuffers); + } + + @Override + public void bindRenderbuffer(int target, int renderbuffer) { + gl.glBindRenderbuffer(target, renderbuffer); + } + + @Override + public void deleteRenderbuffers(int n, IntBuffer renderbuffers) { + gl.glDeleteRenderbuffers(n, renderbuffers); + } + + @Override + public void genRenderbuffers(int n, IntBuffer renderbuffers) { + gl.glGenRenderbuffers(n, renderbuffers); + } + + @Override + public void renderbufferStorage(int target, int internalFormat, int width, int height) { + gl.glRenderbufferStorage(target, internalFormat, width, height); + } + + @Override + public void framebufferRenderbuffer(int target, int attachment, int rendbuferfTarget, int renderbuffer) { + gl.glFramebufferRenderbuffer(target, attachment, rendbuferfTarget, renderbuffer); + } + + @Override + public void framebufferTexture2D(int target, int attachment, int texTarget, int texture, int level) { + gl.glFramebufferTexture2D(target, attachment, texTarget, texture, level); + } + + @Override + public int checkFramebufferStatus(int target) { + return gl.glCheckFramebufferStatus(target); + } + + @Override + public boolean isFramebuffer(int framebuffer) { + return gl2.glIsFramebuffer(framebuffer); + } + + @Override + public void getFramebufferAttachmentParameteriv(int target, int attachment, int pname, IntBuffer params) { + gl2.glGetFramebufferAttachmentParameteriv(target, attachment, pname, params); + } + + @Override + public boolean isRenderbuffer(int renderbuffer) { + return gl2.glIsRenderbuffer(renderbuffer); + } + + @Override + public void getRenderbufferParameteriv(int target, int pname, IntBuffer params) { + gl2.glGetRenderbufferParameteriv(target, pname, params); + } + + @Override + public void blitFramebuffer(int srcX0, int srcY0, int srcX1, int srcY1, int dstX0, int dstY0, int dstX1, int dstY1, int mask, int filter) { + if (gl2x != null) { + gl2x.glBlitFramebuffer(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter); + } else if (gl3 != null) { + gl3.glBlitFramebuffer(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter); + } else { + throw new RuntimeException(String.format(MISSING_GLFUNC_ERROR, "glBlitFramebuffer()")); + } + } + + @Override + public void renderbufferStorageMultisample(int target, int samples, int format, int width, int height) { + if (gl2x != null) { + gl2x.glRenderbufferStorageMultisample(target, samples, format, width, height); + } else if (gl3 != null) { + gl3.glRenderbufferStorageMultisample(target, samples, format, width, height); + } else { + throw new RuntimeException(String.format(MISSING_GLFUNC_ERROR, "glRenderbufferStorageMultisample()")); + } + } + + @Override + public void readBuffer(int buf) { + if (gl2x != null) { + gl2x.glReadBuffer(buf); + } else if (gl3 != null) { + gl3.glReadBuffer(buf); + } else { + throw new RuntimeException(String.format(MISSING_GLFUNC_ERROR, "glReadBuffer()")); + } + } + + @Override + public void drawBuffer(int buf) { + if (gl2x != null) { + gl2x.glDrawBuffer(buf); + } else if (gl3 != null) { + gl3.glDrawBuffer(buf); + } else { + throw new RuntimeException(String.format(MISSING_GLFUNC_ERROR, "glDrawBuffer()")); + } + } +} diff --git a/core/src/processing/opengl/PShader.java b/core/src/processing/opengl/PShader.java index 8f751dc2e..a6e37b84e 100644 --- a/core/src/processing/opengl/PShader.java +++ b/core/src/processing/opengl/PShader.java @@ -3,7 +3,7 @@ /* Part of the Processing project - http://processing.org - Copyright (c) 2011-12 Ben Fry and Casey Reas + Copyright (c) 2011-13 Ben Fry and Casey Reas This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public @@ -25,7 +25,6 @@ package processing.opengl; import processing.core.*; -import java.io.IOException; import java.net.URL; import java.nio.FloatBuffer; import java.nio.IntBuffer; @@ -39,27 +38,53 @@ import java.util.HashMap; * * @webref rendering:shaders */ -public class PShader { - // shaders constants - static protected final int COLOR = 0; - static protected final int LIGHT = 1; - static protected final int TEXTURE = 2; - static protected final int TEXLIGHT = 3; - static protected final int LINE = 4; - static protected final int POINT = 5; +public class PShader implements PConstants { + static protected final int POINT = 0; + static protected final int LINE = 1; + static protected final int POLY = 2; + static protected final int COLOR = 3; + static protected final int LIGHT = 4; + static protected final int TEXTURE = 5; + static protected final int TEXLIGHT = 6; + + static protected String pointShaderAttrRegexp = + "attribute *vec2 *offset"; + static protected String lineShaderAttrRegexp = + "attribute *vec4 *direction"; + static protected String pointShaderDefRegexp = + "#define *PROCESSING_POINT_SHADER"; + static protected String lineShaderDefRegexp = + "#define *PROCESSING_LINE_SHADER"; + static protected String colorShaderDefRegexp = + "#define *PROCESSING_COLOR_SHADER"; + static protected String lightShaderDefRegexp = + "#define *PROCESSING_LIGHT_SHADER"; + static protected String texShaderDefRegexp = + "#define *PROCESSING_TEXTURE_SHADER"; + static protected String texlightShaderDefRegexp = + "#define *PROCESSING_TEXLIGHT_SHADER"; + static protected String polyShaderDefRegexp = + "#define *PROCESSING_POLYGON_SHADER"; + static protected String triShaderAttrRegexp = + "#define *PROCESSING_TRIANGLES_SHADER"; + static protected String quadShaderAttrRegexp = + "#define *PROCESSING_QUADS_SHADER"; protected PApplet parent; // The main renderer associated to the parent PApplet. - protected PGraphicsOpenGL pgMain; + //protected PGraphicsOpenGL pgMain; // We need a reference to the renderer since a shader might // be called by different renderers within a single application // (the one corresponding to the main surface, or other offscreen // renderers). - protected PGraphicsOpenGL pgCurrent; - + protected PGraphicsOpenGL primaryPG; + protected PGraphicsOpenGL currentPG; protected PGL pgl; protected int context; // The context that created this shader. + // The shader type: POINT, LINE, POLY, etc. + protected int type; + public int glProgram; public int glVertex; public int glFragment; @@ -70,8 +95,8 @@ public class PShader { protected String vertexFilename; protected String fragmentFilename; - protected String vertexShaderSource; - protected String fragmentShaderSource; + protected String[] vertexShaderSource; + protected String[] fragmentShaderSource; protected boolean bound; @@ -84,9 +109,54 @@ public class PShader { protected IntBuffer intBuffer; protected FloatBuffer floatBuffer; + protected boolean loadedAttributes = false; + protected boolean loadedUniforms = false; + + // Uniforms common to all shader types + protected int transformMatLoc; + protected int modelviewMatLoc; + protected int projectionMatLoc; + protected int ppixelsLoc; + protected int ppixelsUnit; + protected int viewportLoc; + + // Uniforms only for lines and points + protected int perspectiveLoc; + protected int scaleLoc; + + // Lighting uniforms + protected int lightCountLoc; + protected int lightPositionLoc; + protected int lightNormalLoc; + protected int lightAmbientLoc; + protected int lightDiffuseLoc; + protected int lightSpecularLoc; + protected int lightFalloffLoc; + protected int lightSpotLoc; + + // Texturing uniforms + protected Texture texture; + protected int texUnit; + protected int textureLoc; + protected int texMatrixLoc; + protected int texOffsetLoc; + protected float[] tcmat; + + // Vertex attributes + protected int vertexLoc; + protected int colorLoc; + protected int normalLoc; + protected int texCoordLoc; + protected int normalMatLoc; + protected int directionLoc; + protected int offsetLoc; + protected int ambientLoc; + protected int specularLoc; + protected int emissiveLoc; + protected int shininessLoc; + public PShader() { parent = null; - pgMain = null; pgl = null; context = -1; @@ -103,14 +173,16 @@ public class PShader { floatBuffer = PGL.allocateFloatBuffer(1); bound = false; + + type = -1; } public PShader(PApplet parent) { this(); this.parent = parent; - pgMain = (PGraphicsOpenGL) parent.g; - pgl = PGraphicsOpenGL.pgl; + primaryPG = (PGraphicsOpenGL)parent.g; + pgl = primaryPG.pgl; context = pgl.createEmptyContext(); } @@ -125,13 +197,15 @@ public class PShader { */ public PShader(PApplet parent, String vertFilename, String fragFilename) { this.parent = parent; - pgMain = (PGraphicsOpenGL) parent.g; - pgl = PGraphicsOpenGL.pgl; + primaryPG = (PGraphicsOpenGL)parent.g; + pgl = primaryPG.pgl; this.vertexURL = null; this.fragmentURL = null; this.vertexFilename = vertFilename; this.fragmentFilename = fragFilename; + fragmentShaderSource = pgl.loadFragmentShader(fragFilename); + vertexShaderSource = pgl.loadVertexShader(vertFilename); glProgram = 0; glVertex = 0; @@ -139,6 +213,20 @@ public class PShader { intBuffer = PGL.allocateIntBuffer(1); floatBuffer = PGL.allocateFloatBuffer(1); + + int vertType = getShaderType(vertexShaderSource, -1); + int fragType = getShaderType(fragmentShaderSource, -1); + if (vertType == -1 && fragType == -1) { + type = PShader.POLY; + } else if (vertType == -1) { + type = fragType; + } else if (fragType == -1) { + type = vertType; + } else if (fragType == vertType) { + type = vertType; + } else { + PGraphics.showWarning(PGraphicsOpenGL.INCONSISTENT_SHADER_TYPES); + } } @@ -148,13 +236,15 @@ public class PShader { */ public PShader(PApplet parent, URL vertURL, URL fragURL) { this.parent = parent; - pgMain = (PGraphicsOpenGL) parent.g; - pgl = PGraphicsOpenGL.pgl; + primaryPG = (PGraphicsOpenGL)parent.g; + pgl = primaryPG.pgl; this.vertexURL = vertURL; this.fragmentURL = fragURL; this.vertexFilename = null; this.fragmentFilename = null; + fragmentShaderSource = pgl.loadFragmentShader(fragURL); + vertexShaderSource = pgl.loadVertexShader(vertURL); glProgram = 0; glVertex = 0; @@ -162,6 +252,54 @@ public class PShader { intBuffer = PGL.allocateIntBuffer(1); floatBuffer = PGL.allocateFloatBuffer(1); + + int vertType = getShaderType(vertexShaderSource, -1); + int fragType = getShaderType(fragmentShaderSource, -1); + if (vertType == -1 && fragType == -1) { + type = PShader.POLY; + } else if (vertType == -1) { + type = fragType; + } else if (fragType == -1) { + type = vertType; + } else if (fragType == vertType) { + type = vertType; + } else { + PGraphics.showWarning(PGraphicsOpenGL.INCONSISTENT_SHADER_TYPES); + } + } + + public PShader(PApplet parent, String[] vertSource, String[] fragSource) { + this.parent = parent; + primaryPG = (PGraphicsOpenGL)parent.g; + pgl = primaryPG.pgl; + + this.vertexURL = null; + this.fragmentURL = null; + this.vertexFilename = null; + this.fragmentFilename = null; + vertexShaderSource = vertSource; + fragmentShaderSource = fragSource; + + glProgram = 0; + glVertex = 0; + glFragment = 0; + + intBuffer = PGL.allocateIntBuffer(1); + floatBuffer = PGL.allocateFloatBuffer(1); + + int vertType = getShaderType(vertexShaderSource, -1); + int fragType = getShaderType(fragmentShaderSource, -1); + if (vertType == -1 && fragType == -1) { + type = PShader.POLY; + } else if (vertType == -1) { + type = fragType; + } else if (fragType == -1) { + type = vertType; + } else if (fragType == vertType) { + type = vertType; + } else { + PGraphics.showWarning(PGraphicsOpenGL.INCONSISTENT_SHADER_TYPES); + } } @@ -185,21 +323,34 @@ public class PShader { public void setVertexShader(String vertFilename) { this.vertexFilename = vertFilename; + vertexShaderSource = pgl.loadFragmentShader(vertFilename); } public void setVertexShader(URL vertURL) { this.vertexURL = vertURL; + vertexShaderSource = pgl.loadVertexShader(vertURL); + } + + + public void setVertexShader(String[] vertSource) { + vertexShaderSource = vertSource; } public void setFragmentShader(String fragFilename) { this.fragmentFilename = fragFilename; + fragmentShaderSource = pgl.loadVertexShader(fragFilename); } public void setFragmentShader(URL fragURL) { this.fragmentURL = fragURL; + fragmentShaderSource = pgl.loadVertexShader(fragURL); + } + + public void setFragmentShader(String[] fragSource) { + fragmentShaderSource = fragSource; } @@ -214,6 +365,8 @@ public class PShader { consumeUniforms(); bindTextures(); } + + if (hasType()) bindTyped(); } @@ -221,6 +374,8 @@ public class PShader { * Unbinds the shader program. */ public void unbind() { + if (hasType()) unbindTyped(); + if (bound) { unbindTextures(); pgl.useProgram(0); @@ -264,7 +419,7 @@ public class PShader { * @param w fourth component of the variable to modify. The variable has to be declared with an array/vector type in the shader (i.e.: int[4], vec4) */ public void set(String name, int x, int y, int z, int w) { - setUniformImpl(name, UniformValue.INT4, new int[] { x, y, z }); + setUniformImpl(name, UniformValue.INT4, new int[] { x, y, z, w }); } @@ -296,10 +451,34 @@ public class PShader { } + public void set(String name, boolean x) { + setUniformImpl(name, UniformValue.INT1, new int[] { (x)?1:0 }); + } + + + public void set(String name, boolean x, boolean y) { + setUniformImpl(name, UniformValue.INT2, + new int[] { (x)?1:0, (y)?1:0 }); + } + + + public void set(String name, boolean x, boolean y, boolean z) { + setUniformImpl(name, UniformValue.INT3, + new int[] { (x)?1:0, (y)?1:0, (z)?1:0 }); + } + + + public void set(String name, boolean x, boolean y, boolean z, boolean w) { + setUniformImpl(name, UniformValue.INT4, + new int[] { (x)?1:0, (y)?1:0, (z)?1:0, (w)?1:0 }); + } + + public void set(String name, int[] vec) { set(name, vec, 1); } + /** * @param ncoords number of coordinates per element, max 4 */ @@ -343,6 +522,21 @@ public class PShader { } } + + public void set(String name, boolean[] vec) { + set(name, vec, 1); + } + + + public void set(String name, boolean[] boolvec, int ncoords) { + int[] vec = new int[boolvec.length]; + for (int i = 0; i < boolvec.length; i++) { + vec[i] = (boolvec[i])?1:0; + } + set(name, vec, ncoords); + } + + /** * @param mat matrix of values */ @@ -383,6 +577,23 @@ public class PShader { } + /** + * Extra initialization method that can be used by subclasses, called after + * compiling and attaching the vertex and fragment shaders, and before + * linking the shader program. + * + */ + protected void setup() { + } + + + protected void draw(int idxId, int count, int offset) { + pgl.bindBuffer(PGL.ELEMENT_ARRAY_BUFFER, idxId); + pgl.drawElements(PGL.TRIANGLES, count, PGL.INDEX_TYPE, + offset * PGL.SIZEOF_INDEX); + pgl.bindBuffer(PGL.ELEMENT_ARRAY_BUFFER, 0); + } + /** * Returns the ID location of the attribute parameter given its name. @@ -622,7 +833,7 @@ public class PShader { pgl.uniformMatrix4fv(loc, 1, false, floatBuffer); } else if (val.type == UniformValue.SAMPLER2D) { PImage img = (PImage)val.value; - Texture tex = pgMain.getTexture(img); + Texture tex = currentPG.getTexture(img); if (textures == null) textures = new HashMap(); textures.put(loc, tex); @@ -685,54 +896,30 @@ public class PShader { } } - - protected int getLastTexUnit() { - if (texUnits == null) return -1; - else return texUnits.size() - 1; - } - protected void init() { if (glProgram == 0 || contextIsOutdated()) { context = pgl.getCurrentContext(); - glProgram = PGraphicsOpenGL.createGLSLProgramObject(context); - - boolean hasVert = false; - if (vertexFilename != null) { - hasVert = loadVertexShader(vertexFilename); - } else if (vertexURL != null) { - hasVert = loadVertexShader(vertexURL); - } else { - PGraphics.showException("Vertex shader filenames and URLs are " + - "both null!"); - } - - boolean hasFrag = false; - if (fragmentFilename != null) { - hasFrag = loadFragmentShader(fragmentFilename); - } else if (fragmentURL != null) { - hasFrag = loadFragmentShader(fragmentURL); - } else { - PGraphics.showException("Fragment shader filenames and URLs are " + - "both null!"); - } + glProgram = PGraphicsOpenGL.createGLSLProgramObject(context, pgl); boolean vertRes = true; - if (hasVert) { + if (hasVertexShader()) { vertRes = compileVertexShader(); + } else { + PGraphics.showException("Doesn't have a vertex shader"); } boolean fragRes = true; - if (hasFrag) { + if (hasFragmentShader()) { fragRes = compileFragmentShader(); + } else { + PGraphics.showException("Doesn't have a fragment shader"); } if (vertRes && fragRes) { - if (hasVert) { - pgl.attachShader(glProgram, glVertex); - } - if (hasFrag) { - pgl.attachShader(glProgram, glFragment); - } + pgl.attachShader(glProgram, glVertex); + pgl.attachShader(glProgram, glFragment); + setup(); + pgl.linkProgram(glProgram); pgl.getProgramiv(glProgram, PGL.LINK_STATUS, intBuffer); @@ -769,69 +956,22 @@ public class PShader { } - /** - * Loads and compiles the vertex shader contained in file. - * - * @param file String - */ - protected boolean loadVertexShader(String filename) { - vertexShaderSource = PApplet.join(parent.loadStrings(filename), "\n"); - return vertexShaderSource != null; + + protected boolean hasVertexShader() { + return vertexShaderSource != null && 0 < vertexShaderSource.length; } - - /** - * Loads and compiles the vertex shader contained in the URL. - * - * @param file String - */ - protected boolean loadVertexShader(URL url) { - try { - vertexShaderSource = PApplet.join(PApplet.loadStrings(url.openStream()), - "\n"); - return vertexShaderSource != null; - } catch (IOException e) { - PGraphics.showException("Cannot load vertex shader " + url.getFile()); - return false; - } + protected boolean hasFragmentShader() { + return fragmentShaderSource != null && 0 < fragmentShaderSource.length; } - - /** - * Loads and compiles the fragment shader contained in file. - * - * @param file String - */ - protected boolean loadFragmentShader(String filename) { - fragmentShaderSource = PApplet.join(parent.loadStrings(filename), "\n"); - return fragmentShaderSource != null; - } - - - /** - * Loads and compiles the fragment shader contained in the URL. - * - * @param url URL - */ - protected boolean loadFragmentShader(URL url) { - try { - fragmentShaderSource = PApplet.join(PApplet.loadStrings(url.openStream()), - "\n"); - return fragmentShaderSource != null; - } catch (IOException e) { - PGraphics.showException("Cannot load fragment shader " + url.getFile()); - return false; - } - } - - /** * @param shaderSource a string containing the shader's code */ protected boolean compileVertexShader() { - glVertex = PGraphicsOpenGL.createGLSLVertShaderObject(context); + glVertex = PGraphicsOpenGL.createGLSLVertShaderObject(context, pgl); - pgl.shaderSource(glVertex, vertexShaderSource); + pgl.shaderSource(glVertex, PApplet.join(vertexShaderSource, "\n")); pgl.compileShader(glVertex); pgl.getShaderiv(glVertex, PGL.COMPILE_STATUS, intBuffer); @@ -850,9 +990,9 @@ public class PShader { * @param shaderSource a string containing the shader's code */ protected boolean compileFragmentShader() { - glFragment = PGraphicsOpenGL.createGLSLFragShaderObject(context); + glFragment = PGraphicsOpenGL.createGLSLFragShaderObject(context, pgl); - pgl.shaderSource(glFragment, fragmentShaderSource); + pgl.shaderSource(glFragment, PApplet.join(fragmentShaderSource, "\n")); pgl.compileShader(glFragment); pgl.getShaderiv(glFragment, PGL.COMPILE_STATUS, intBuffer); @@ -867,31 +1007,434 @@ public class PShader { } - protected void setRenderer(PGraphicsOpenGL pg) { - pgCurrent = pg; - } - - protected void loadAttributes() { } - - - protected void loadUniforms() { } - - protected void dispose() { if (glVertex != 0) { - PGraphicsOpenGL.deleteGLSLVertShaderObject(glVertex, context); + PGraphicsOpenGL.deleteGLSLVertShaderObject(glVertex, context, pgl); glVertex = 0; } if (glFragment != 0) { - PGraphicsOpenGL.deleteGLSLFragShaderObject(glFragment, context); + PGraphicsOpenGL.deleteGLSLFragShaderObject(glFragment, context, pgl); glFragment = 0; } if (glProgram != 0) { - PGraphicsOpenGL.deleteGLSLProgramObject(glProgram, context); + PGraphicsOpenGL.deleteGLSLProgramObject(glProgram, context, pgl); glProgram = 0; } } + static protected int getShaderType(String[] source, int defaultType) { + for (int i = 0; i < source.length; i++) { + String line = source[i].trim(); + if (PApplet.match(line, pointShaderAttrRegexp) != null) + return PShader.POINT; + else if (PApplet.match(line, lineShaderAttrRegexp) != null) + return PShader.LINE; + else if (PApplet.match(line, pointShaderDefRegexp) != null) + return PShader.POINT; + else if (PApplet.match(line, lineShaderDefRegexp) != null) + return PShader.LINE; + else if (PApplet.match(line, colorShaderDefRegexp) != null) + return PShader.COLOR; + else if (PApplet.match(line, lightShaderDefRegexp) != null) + return PShader.LIGHT; + else if (PApplet.match(line, texShaderDefRegexp) != null) + return PShader.TEXTURE; + else if (PApplet.match(line, texlightShaderDefRegexp) != null) + return PShader.TEXLIGHT; + else if (PApplet.match(line, polyShaderDefRegexp) != null) + return PShader.POLY; + else if (PApplet.match(line, triShaderAttrRegexp) != null) + return PShader.POLY; + else if (PApplet.match(line, quadShaderAttrRegexp) != null) + return PShader.POLY; + } + return defaultType; + } + + + // *************************************************************************** + // + // Processing specific + + + protected int getType() { + return type; + } + + + protected void setType(int type) { + this.type = type; + } + + + protected boolean hasType() { + return POINT <= type && type <= TEXLIGHT; + } + + + protected boolean isPointShader() { + return type == POINT; + } + + + protected boolean isLineShader() { + return type == LINE; + } + + + protected boolean isPolyShader() { + return POLY <= type && type <= TEXLIGHT; + } + + + protected boolean checkPolyType(int type) { + if (getType() == PShader.POLY) return true; + + if (getType() != type) { + if (type == TEXLIGHT) { + PGraphics.showWarning(PGraphicsOpenGL.NO_TEXLIGHT_SHADER_ERROR); + } else if (type == LIGHT) { + PGraphics.showWarning(PGraphicsOpenGL.NO_LIGHT_SHADER_ERROR); + } else if (type == TEXTURE) { + PGraphics.showWarning(PGraphicsOpenGL.NO_TEXTURE_SHADER_ERROR); + } else if (type == COLOR) { + PGraphics.showWarning(PGraphicsOpenGL.NO_COLOR_SHADER_ERROR); + } + return false; + } + + return true; + } + + + protected int getLastTexUnit() { + return texUnits == null ? -1 : texUnits.size() - 1; + } + + + protected void setRenderer(PGraphicsOpenGL pg) { + this.currentPG = pg; + } + + + protected void loadAttributes() { + if (loadedAttributes) return; + + vertexLoc = getAttributeLoc("vertex"); + if (vertexLoc == -1) vertexLoc = getAttributeLoc("position"); + + colorLoc = getAttributeLoc("color"); + texCoordLoc = getAttributeLoc("texCoord"); + normalLoc = getAttributeLoc("normal"); + + ambientLoc = getAttributeLoc("ambient"); + specularLoc = getAttributeLoc("specular"); + emissiveLoc = getAttributeLoc("emissive"); + shininessLoc = getAttributeLoc("shininess"); + + directionLoc = getAttributeLoc("direction"); + + offsetLoc = getAttributeLoc("offset"); + + directionLoc = getAttributeLoc("direction"); + offsetLoc = getAttributeLoc("offset"); + + loadedAttributes = true; + } + + + protected void loadUniforms() { + if (loadedUniforms) return; + transformMatLoc = getUniformLoc("transform"); + if (transformMatLoc == -1) + transformMatLoc = getUniformLoc("transformMatrix"); + + modelviewMatLoc = getUniformLoc("modelview"); + if (modelviewMatLoc == -1) + modelviewMatLoc = getUniformLoc("modelviewMatrix"); + + projectionMatLoc = getUniformLoc("projection"); + if (projectionMatLoc == -1) + projectionMatLoc = getUniformLoc("projectionMatrix"); + + viewportLoc = getUniformLoc("viewport"); + ppixelsLoc = getUniformLoc("ppixels"); + + normalMatLoc = getUniformLoc("normalMatrix"); + + lightCountLoc = getUniformLoc("lightCount"); + lightPositionLoc = getUniformLoc("lightPosition"); + lightNormalLoc = getUniformLoc("lightNormal"); + lightAmbientLoc = getUniformLoc("lightAmbient"); + lightDiffuseLoc = getUniformLoc("lightDiffuse"); + lightSpecularLoc = getUniformLoc("lightSpecular"); + lightFalloffLoc = getUniformLoc("lightFalloff"); + lightSpotLoc = getUniformLoc("lightSpot"); + + textureLoc = getUniformLoc("texture"); + if (textureLoc == -1) { + textureLoc = getUniformLoc("texMap"); + } + + texMatrixLoc = getUniformLoc("texMatrix"); + texOffsetLoc = getUniformLoc("texOffset"); + + perspectiveLoc = getUniformLoc("perspective"); + scaleLoc = getUniformLoc("scale"); + loadedUniforms = true; + } + + + protected void setCommonUniforms() { + if (-1 < transformMatLoc) { + currentPG.updateGLProjmodelview(); + setUniformMatrix(transformMatLoc, currentPG.glProjmodelview); + } + + if (-1 < modelviewMatLoc) { + currentPG.updateGLModelview(); + setUniformMatrix(modelviewMatLoc, currentPG.glModelview); + } + + if (-1 < projectionMatLoc) { + currentPG.updateGLProjection(); + setUniformMatrix(projectionMatLoc, currentPG.glProjection); + } + + if (-1 < viewportLoc) { + float x = currentPG.viewport.get(0); + float y = currentPG.viewport.get(1); + float w = currentPG.viewport.get(2); + float h = currentPG.viewport.get(3); + setUniformValue(viewportLoc, x, y, w, h); + } + + if (-1 < ppixelsLoc) { + ppixelsUnit = getLastTexUnit() + 1; + setUniformValue(ppixelsLoc, ppixelsUnit); + pgl.activeTexture(PGL.TEXTURE0 + ppixelsUnit); + currentPG.bindFrontTexture(); + } else { + ppixelsUnit = -1; + } + } + + protected void bindTyped() { + if (currentPG == null) { + setRenderer(primaryPG.getCurrentPG()); + loadAttributes(); + loadUniforms(); + } + setCommonUniforms(); + + if (-1 < vertexLoc) pgl.enableVertexAttribArray(vertexLoc); + if (-1 < colorLoc) pgl.enableVertexAttribArray(colorLoc); + if (-1 < texCoordLoc) pgl.enableVertexAttribArray(texCoordLoc); + if (-1 < normalLoc) pgl.enableVertexAttribArray(normalLoc); + + if (-1 < normalMatLoc) { + currentPG.updateGLNormal(); + setUniformMatrix(normalMatLoc, currentPG.glNormal); + } + + if (-1 < ambientLoc) pgl.enableVertexAttribArray(ambientLoc); + if (-1 < specularLoc) pgl.enableVertexAttribArray(specularLoc); + if (-1 < emissiveLoc) pgl.enableVertexAttribArray(emissiveLoc); + if (-1 < shininessLoc) pgl.enableVertexAttribArray(shininessLoc); + + int count = currentPG.lightCount; + setUniformValue(lightCountLoc, count); + if (0 < count) { + setUniformVector(lightPositionLoc, currentPG.lightPosition, 4, count); + setUniformVector(lightNormalLoc, currentPG.lightNormal, 3, count); + setUniformVector(lightAmbientLoc, currentPG.lightAmbient, 3, count); + setUniformVector(lightDiffuseLoc, currentPG.lightDiffuse, 3, count); + setUniformVector(lightSpecularLoc, currentPG.lightSpecular, 3, count); + setUniformVector(lightFalloffLoc, currentPG.lightFalloffCoefficients, + 3, count); + setUniformVector(lightSpotLoc, currentPG.lightSpotParameters, 2, count); + } + + if (-1 < directionLoc) pgl.enableVertexAttribArray(directionLoc); + + if (-1 < offsetLoc) pgl.enableVertexAttribArray(offsetLoc); + + if (-1 < perspectiveLoc) { + if (currentPG.getHint(ENABLE_STROKE_PERSPECTIVE) && + currentPG.nonOrthoProjection()) { + setUniformValue(perspectiveLoc, 1); + } else { + setUniformValue(perspectiveLoc, 0); + } + } + + if (-1 < scaleLoc) { + if (currentPG.getHint(DISABLE_OPTIMIZED_STROKE)) { + setUniformValue(scaleLoc, 1.0f, 1.0f, 1.0f); + } else { + float f = PGL.STROKE_DISPLACEMENT; + if (currentPG.orthoProjection()) { + setUniformValue(scaleLoc, 1, 1, f); + } else { + setUniformValue(scaleLoc, f, f, f); + } + } + } + } + + protected void unbindTyped() { + if (-1 < offsetLoc) pgl.disableVertexAttribArray(offsetLoc); + + if (-1 < directionLoc) pgl.disableVertexAttribArray(directionLoc); + + if (-1 < textureLoc && texture != null) { + pgl.activeTexture(PGL.TEXTURE0 + texUnit); + texture.unbind(); + pgl.activeTexture(PGL.TEXTURE0); + texture = null; + } + + if (-1 < ambientLoc) pgl.disableVertexAttribArray(ambientLoc); + if (-1 < specularLoc) pgl.disableVertexAttribArray(specularLoc); + if (-1 < emissiveLoc) pgl.disableVertexAttribArray(emissiveLoc); + if (-1 < shininessLoc) pgl.disableVertexAttribArray(shininessLoc); + + if (-1 < vertexLoc) pgl.disableVertexAttribArray(vertexLoc); + if (-1 < colorLoc) pgl.disableVertexAttribArray(colorLoc); + if (-1 < texCoordLoc) pgl.disableVertexAttribArray(texCoordLoc); + if (-1 < normalLoc) pgl.disableVertexAttribArray(normalLoc); + + if (-1 < ppixelsLoc) { + pgl.requestFBOLayer(); + pgl.activeTexture(PGL.TEXTURE0 + ppixelsUnit); + currentPG.unbindFrontTexture(); + pgl.activeTexture(PGL.TEXTURE0); + } + + pgl.bindBuffer(PGL.ARRAY_BUFFER, 0); + } + + protected void setTexture(Texture tex) { + texture = tex; + + float scaleu = 1; + float scalev = 1; + float dispu = 0; + float dispv = 0; + + if (tex != null) { + if (tex.invertedX()) { + scaleu = -1; + dispu = 1; + } + + if (tex.invertedY()) { + scalev = -1; + dispv = 1; + } + + scaleu *= tex.maxTexcoordU(); + dispu *= tex.maxTexcoordU(); + scalev *= tex.maxTexcoordV(); + dispv *= tex.maxTexcoordV(); + + setUniformValue(texOffsetLoc, 1.0f / tex.width, 1.0f / tex.height); + + if (-1 < textureLoc) { + texUnit = -1 < ppixelsUnit ? ppixelsUnit + 1 : getLastTexUnit() + 1; + setUniformValue(textureLoc, texUnit); + pgl.activeTexture(PGL.TEXTURE0 + texUnit); + tex.bind(); + } + } + + if (-1 < texMatrixLoc) { + if (tcmat == null) { + tcmat = new float[16]; + } + tcmat[0] = scaleu; tcmat[4] = 0; tcmat[ 8] = 0; tcmat[12] = dispu; + tcmat[1] = 0; tcmat[5] = scalev; tcmat[ 9] = 0; tcmat[13] = dispv; + tcmat[2] = 0; tcmat[6] = 0; tcmat[10] = 0; tcmat[14] = 0; + tcmat[3] = 0; tcmat[7] = 0; tcmat[11] = 0; tcmat[15] = 0; + setUniformMatrix(texMatrixLoc, tcmat); + } + } + + + protected boolean supportsTexturing() { + return -1 < textureLoc; + } + + protected boolean supportLighting() { + return -1 < lightCountLoc || -1 < lightPositionLoc || -1 < lightNormalLoc; + } + + protected boolean accessTexCoords() { + return -1 < texCoordLoc; + } + + protected boolean accessNormals() { + return -1 < normalLoc; + } + + protected boolean accessLightAttribs() { + return -1 < ambientLoc || -1 < specularLoc || -1 < emissiveLoc || + -1 < shininessLoc; + } + + protected void setVertexAttribute(int vboId, int size, int type, + int stride, int offset) { + setAttributeVBO(vertexLoc, vboId, size, type, false, stride, offset); + } + + protected void setColorAttribute(int vboId, int size, int type, + int stride, int offset) { + setAttributeVBO(colorLoc, vboId, size, type, true, stride, offset); + } + + protected void setNormalAttribute(int vboId, int size, int type, + int stride, int offset) { + setAttributeVBO(normalLoc, vboId, size, type, false, stride, offset); + } + + protected void setTexcoordAttribute(int vboId, int size, int type, + int stride, int offset) { + setAttributeVBO(texCoordLoc, vboId, size, type, false, stride, offset); + } + + protected void setAmbientAttribute(int vboId, int size, int type, + int stride, int offset) { + setAttributeVBO(ambientLoc, vboId, size, type, true, stride, offset); + } + + protected void setSpecularAttribute(int vboId, int size, int type, + int stride, int offset) { + setAttributeVBO(specularLoc, vboId, size, type, true, stride, offset); + } + + protected void setEmissiveAttribute(int vboId, int size, int type, + int stride, int offset) { + setAttributeVBO(emissiveLoc, vboId, size, type, true, stride, offset); + } + + protected void setShininessAttribute(int vboId, int size, int type, + int stride, int offset) { + setAttributeVBO(shininessLoc, vboId, size, type, false, stride, offset); + } + + protected void setLineAttribute(int vboId, int size, int type, + int stride, int offset) { + setAttributeVBO(directionLoc, vboId, size, type, false, stride, offset); + } + + protected void setPointAttribute(int vboId, int size, int type, + int stride, int offset) { + setAttributeVBO(offsetLoc, vboId, size, type, false, stride, offset); + } + + + // *************************************************************************** + // // Class to store a user-specified value for a uniform parameter // in the shader protected class UniformValue { diff --git a/core/src/processing/opengl/PShapeOpenGL.java b/core/src/processing/opengl/PShapeOpenGL.java index e0fde4a2c..6db7fddca 100644 --- a/core/src/processing/opengl/PShapeOpenGL.java +++ b/core/src/processing/opengl/PShapeOpenGL.java @@ -31,9 +31,6 @@ import processing.core.PMatrix2D; import processing.core.PMatrix3D; import processing.core.PShape; import processing.core.PVector; -import processing.opengl.PGraphicsOpenGL.LineShader; -import processing.opengl.PGraphicsOpenGL.PointShader; -import processing.opengl.PGraphicsOpenGL.BaseShader; import processing.opengl.PGraphicsOpenGL.IndexCache; import processing.opengl.PGraphicsOpenGL.InGeometry; import processing.opengl.PGraphicsOpenGL.TessGeometry; @@ -41,6 +38,7 @@ import processing.opengl.PGraphicsOpenGL.Tessellator; import java.util.Arrays; import java.util.HashSet; +import java.util.Stack; /** * This class holds a 3D model composed of vertices, normals, colors @@ -155,6 +153,7 @@ public class PShapeOpenGL extends PShape { // Geometric transformations. protected PMatrix transform; + protected Stack transformStack; // ........................................................ @@ -162,12 +161,9 @@ public class PShapeOpenGL extends PShape { protected boolean tessellated; protected boolean needBufferInit = false; -// protected boolean polyBuffersCreated = false; -// protected boolean lineBuffersCreated = false; -// protected boolean pointBuffersCreated = false; - protected boolean isSolid; - protected boolean isClosed; + // Flag to indicate if the shape can have holes or not. + protected boolean solid; protected boolean breakShape = false; protected boolean shapeCreated = false; @@ -187,9 +183,13 @@ public class PShapeOpenGL extends PShape { // Bezier and Catmull-Rom curves - protected int bezierDetail = 20; - protected int curveDetail = 20; - protected float curveTightness = 0; + protected int bezierDetail; + protected int curveDetail; + protected float curveTightness; + + protected int savedBezierDetail; + protected int savedCurveDetail; + protected float savedCurveTightness; // ........................................................ @@ -261,14 +261,38 @@ public class PShapeOpenGL extends PShape { protected int firstModifiedPointAttribute; protected int lastModifiedPointAttribute; + // ........................................................ + + // Saved style variables to style can be re-enabled after disableStyle, + // although it won't work if properties are defined on a per-vertex basis. + + protected boolean savedStroke; + protected int savedStrokeColor; + protected float savedStrokeWeight; + protected int savedStrokeCap; + protected int savedStrokeJoin; + + protected boolean savedFill; + protected int savedFillColor; + + protected boolean savedTint; + protected int savedTintColor; + + protected int savedAmbientColor; + protected int savedSpecularColor; + protected int savedEmissiveColor; + protected float savedShininess; + + protected int savedTextureMode; + PShapeOpenGL() { } - public PShapeOpenGL(PApplet parent, int family) { - pg = (PGraphicsOpenGL)parent.g; - pgl = PGraphicsOpenGL.pgl; + public PShapeOpenGL(PGraphicsOpenGL pg, int family) { + this.pg = pg; + pgl = pg.pgl; context = pgl.createEmptyContext(); glPolyVertex = 0; @@ -298,7 +322,7 @@ public class PShapeOpenGL extends PShape { this.tessellated = false; if (family == GEOMETRY || family == PRIMITIVE || family == PATH) { - inGeo = pg.newInGeometry(PGraphicsOpenGL.RETAINED); + inGeo = PGraphicsOpenGL.newInGeometry(pg, PGraphicsOpenGL.RETAINED); } // Style parameters are retrieved from the current values in the renderer. @@ -333,6 +357,15 @@ public class PShapeOpenGL extends PShape { sphereDetailU = pg.sphereDetailU; sphereDetailV = pg.sphereDetailV; + bezierDetail = pg.bezierDetail; + curveDetail = pg.curveDetail; + curveTightness = pg.curveTightness; + + // The rect and ellipse modes are set to CORNER since it is the expected + // mode for svg shapes. + rectMode = CORNER; + ellipseMode = CORNER; + normalX = normalY = 0; normalZ = 1; @@ -340,7 +373,7 @@ public class PShapeOpenGL extends PShape { // To make sure that the first vertex is marked as a break. // Same behavior as in the immediate mode. - breakShape = true; + breakShape = false; if (family == GROUP) { // GROUP shapes are always marked as ended. @@ -535,20 +568,19 @@ public class PShapeOpenGL extends PShape { // Shape creation (temporary hack) - public static PShapeOpenGL createShape3D(PApplet parent, PShape src) { + public static PShapeOpenGL createShape3D(PGraphicsOpenGL pg, PShape src) { PShapeOpenGL dest = null; if (src.getFamily() == GROUP) { - dest = PGraphics3D.createShapeImpl(parent, GROUP); - copyGroup3D(parent, src, dest); + dest = PGraphics3D.createShapeImpl(pg, GROUP); + copyGroup3D(pg, src, dest); } else if (src.getFamily() == PRIMITIVE) { - dest = PGraphics3D.createShapeImpl(parent, src.getKind(), - src.getParams()); + dest = PGraphics3D.createShapeImpl(pg, src.getKind(), src.getParams()); PShape.copyPrimitive(src, dest); } else if (src.getFamily() == GEOMETRY) { - dest = PGraphics3D.createShapeImpl(parent, PShape.GEOMETRY); + dest = PGraphics3D.createShapeImpl(pg, PShape.GEOMETRY); PShape.copyGeometry(src, dest); } else if (src.getFamily() == PATH) { - dest = PGraphics3D.createShapeImpl(parent, PATH); + dest = PGraphics3D.createShapeImpl(pg, PATH); PShape.copyPath(src, dest); } dest.setName(src.getName()); @@ -559,20 +591,19 @@ public class PShapeOpenGL extends PShape { } - static public PShapeOpenGL createShape2D(PApplet parent, PShape src) { + static public PShapeOpenGL createShape2D(PGraphicsOpenGL pg, PShape src) { PShapeOpenGL dest = null; if (src.getFamily() == GROUP) { - dest = PGraphics2D.createShapeImpl(parent, GROUP); - copyGroup2D(parent, src, dest); + dest = PGraphics2D.createShapeImpl(pg, GROUP); + copyGroup2D(pg, src, dest); } else if (src.getFamily() == PRIMITIVE) { - dest = PGraphics2D.createShapeImpl(parent, src.getKind(), - src.getParams()); + dest = PGraphics2D.createShapeImpl(pg, src.getKind(), src.getParams()); PShape.copyPrimitive(src, dest); } else if (src.getFamily() == GEOMETRY) { - dest = PGraphics2D.createShapeImpl(parent, PShape.GEOMETRY); + dest = PGraphics2D.createShapeImpl(pg, PShape.GEOMETRY); PShape.copyGeometry(src, dest); } else if (src.getFamily() == PATH) { - dest = PGraphics2D.createShapeImpl(parent, PATH); + dest = PGraphics2D.createShapeImpl(pg, PATH); PShape.copyPath(src, dest); } dest.setName(src.getName()); @@ -582,25 +613,25 @@ public class PShapeOpenGL extends PShape { } - static public void copyGroup3D(PApplet parent, PShape src, PShape dest) { + static public void copyGroup3D(PGraphicsOpenGL pg, PShape src, PShape dest) { copyMatrix(src, dest); copyStyles(src, dest); copyImage(src, dest); for (int i = 0; i < src.getChildCount(); i++) { - PShape c = createShape3D(parent, src.getChild(i)); + PShape c = createShape3D(pg, src.getChild(i)); dest.addChild(c); } } - static public void copyGroup2D(PApplet parent, PShape src, PShape dest) { + static public void copyGroup2D(PGraphicsOpenGL pg, PShape src, PShape dest) { copyMatrix(src, dest); copyStyles(src, dest); copyImage(src, dest); for (int i = 0; i < src.getChildCount(); i++) { - PShape c = createShape2D(parent, src.getChild(i)); + PShape c = createShape2D(pg, src.getChild(i)); dest.addChild(c); } } @@ -951,14 +982,11 @@ public class PShapeOpenGL extends PShape { child.solid(solid); } } else { - isSolid = solid; + this.solid = solid; } } -//public void beginContour() { -//super.beginContour(); - @Override protected void beginContourImpl() { breakShape = true; @@ -970,10 +998,11 @@ public class PShapeOpenGL extends PShape { } - @Override public void vertex(float x, float y) { vertexImpl(x, y, 0, 0, 0); + if (image != null) + PGraphics.showWarning(PGraphicsOpenGL.MISSING_UV_TEXCOORDS_ERROR); } @@ -986,6 +1015,8 @@ public class PShapeOpenGL extends PShape { @Override public void vertex(float x, float y, float z) { vertexImpl(x, y, z, 0, 0); + if (image != null) + PGraphics.showWarning(PGraphicsOpenGL.MISSING_UV_TEXCOORDS_ERROR); } @@ -1033,24 +1064,23 @@ public class PShapeOpenGL extends PShape { } inGeo.addVertex(x, y, z, - fcolor, - normalX, normalY, normalZ, - u, v, - scolor, sweight, - ambientColor, specularColor, emissiveColor, shininess, - vertexCode()); + fcolor, + normalX, normalY, normalZ, + u, v, + scolor, sweight, + ambientColor, specularColor, emissiveColor, shininess, + VERTEX, vertexBreak()); markForTessellation(); } - protected int vertexCode() { - int code = VERTEX; + protected boolean vertexBreak() { if (breakShape) { - code = BREAK; breakShape = false; + return true; } - return code; + return false; } @@ -1090,7 +1120,7 @@ public class PShapeOpenGL extends PShape { // size, which might lead to arrays larger than the vertex counts. inGeo.trim(); - isClosed = mode == CLOSE; + close = mode == CLOSE; markForTessellation(); shapeCreated = true; } @@ -1132,7 +1162,11 @@ public class PShapeOpenGL extends PShape { @Override public void translate(float tx, float ty) { - transform(TRANSLATE, tx, ty); + if (is3D) { + transform(TRANSLATE, tx, ty, 0); + } else { + transform(TRANSLATE, tx, ty); + } } @@ -1174,13 +1208,21 @@ public class PShapeOpenGL extends PShape { @Override public void scale(float s) { - transform(SCALE, s, s); + if (is3D) { + transform(SCALE, s, s, s); + } else { + transform(SCALE, s, s); + } } @Override public void scale(float x, float y) { - transform(SCALE, x, y); + if (is3D) { + transform(SCALE, x, y, 1); + } else { + transform(SCALE, x, y); + } } @@ -1219,46 +1261,31 @@ public class PShapeOpenGL extends PShape { @Override public void resetMatrix() { - if (shapeCreated && matrix != null) { + if (shapeCreated && matrix != null && transformStack != null) { if (family == GROUP) { updateTessellation(); } - boolean res = matrix.invert(); - if (res) { - if (tessellated) { - applyMatrixImpl(matrix); + if (tessellated) { + PMatrix mat = popTransform(); + while (mat != null) { + boolean res = mat.invert(); + if (res) { + applyMatrixImpl(mat); + } else { + PGraphics.showWarning("Transformation applied on the shape cannot be inverted"); + } + mat = popTransform(); } - matrix = null; - } else { - PGraphics.showWarning("The transformation matrix cannot be inverted"); } + matrix.reset(); + transformStack.clear(); } } protected void transform(int type, float... args) { - int dimensions; - if (type == ROTATE) { - dimensions = args.length == 1 ? 2 : 3; - } else if (type == MATRIX) { - dimensions = args.length == 6 ? 2 : 3; - } else { - dimensions = args.length; - } - transformImpl(type, dimensions, args); - } - - - protected void transformImpl(int type, int ncoords, float... args) { - checkMatrix(ncoords); - calcTransform(type, ncoords, args); - if (tessellated) { - applyMatrixImpl(transform); - } - } - - - protected void calcTransform(int type, int dimensions, float... args) { + int dimensions = is3D ? 3 : 2; + checkMatrix(dimensions); if (transform == null) { if (dimensions == 2) { transform = new PMatrix2D(); @@ -1269,30 +1296,37 @@ public class PShapeOpenGL extends PShape { transform.reset(); } + int ncoords = args.length; + if (type == ROTATE) { + ncoords = args.length == 1 ? 2 : 3; + } else if (type == MATRIX) { + ncoords = args.length == 6 ? 2 : 3; + } + switch (type) { case TRANSLATE: - if (dimensions == 3) { + if (ncoords == 3) { transform.translate(args[0], args[1], args[2]); } else { transform.translate(args[0], args[1]); } break; case ROTATE: - if (dimensions == 3) { + if (ncoords == 3) { transform.rotate(args[0], args[1], args[2], args[3]); } else { transform.rotate(args[0]); } break; case SCALE: - if (dimensions == 3) { + if (ncoords == 3) { transform.scale(args[0], args[1], args[2]); } else { transform.scale(args[0], args[1]); } break; case MATRIX: - if (dimensions == 3) { + if (ncoords == 3) { transform.set(args[ 0], args[ 1], args[ 2], args[ 3], args[ 4], args[ 5], args[ 6], args[ 7], args[ 8], args[ 9], args[10], args[11], @@ -1304,9 +1338,29 @@ public class PShapeOpenGL extends PShape { break; } matrix.apply(transform); + pushTransform(); + if (tessellated) applyMatrixImpl(transform); } + protected void pushTransform() { + if (transformStack == null) transformStack = new Stack(); + PMatrix mat; + if (transform instanceof PMatrix2D) { + mat = new PMatrix2D(); + } else { + mat = new PMatrix3D(); + } + mat.set(transform); + transformStack.push(mat); + } + + + protected PMatrix popTransform() { + if (transformStack == null || transformStack.size() == 0) return null; + return transformStack.pop(); + } + protected void applyMatrixImpl(PMatrix matrix) { if (hasPolys) { tessGeo.applyMatrixOnPolyGeometry(matrix, @@ -1342,7 +1396,10 @@ public class PShapeOpenGL extends PShape { @Override public void bezierDetail(int detail) { bezierDetail = detail; - pg.bezierDetail(detail); + if (0 < inGeo.codeCount) { + markForTessellation(); + } + //pg.bezierDetail(detail); // setting the detail in the renderer, WTF?? } @@ -1373,9 +1430,16 @@ public class PShapeOpenGL extends PShape { ambientColor, specularColor, emissiveColor, shininess); inGeo.setNormal(normalX, normalY, normalZ); inGeo.addBezierVertex(x2, y2, z2, - x3, y3, z3, - x4, y4, z4, - fill, stroke, bezierDetail, vertexCode(), kind); + x3, y3, z3, + x4, y4, z4, vertexBreak()); + +// inGeo.addVertex(x2, y2, z2, BEZIER_VERTEX, vertexBreak()); +// inGeo.addVertex(x3, y3, z3, BEZIER_VERTEX, false); +// inGeo.addVertex(x4, y4, z4, BEZIER_VERTEX, false); +//// inGeo.addBezierVertex(x2, y2, z2, +// x3, y3, z3, +// x4, y4, z4, +// fill, stroke, bezierDetail, vertexCode(), kind); } @@ -1401,8 +1465,12 @@ public class PShapeOpenGL extends PShape { ambientColor, specularColor, emissiveColor, shininess); inGeo.setNormal(normalX, normalY, normalZ); inGeo.addQuadraticVertex(cx, cy, cz, - x3, y3, z3, - fill, stroke, bezierDetail, vertexCode(), kind); + x3, y3, z3, vertexBreak()); +// inGeo.addVertex(cx, cy, cz, QUADRATIC_VERTEX, vertexBreak()); +// inGeo.addVertex(x3, y3, z3, QUADRATIC_VERTEX, false); +// inGeo.addQuadraticVertex(cx, cy, cz, +// x3, y3, z3, +// fill, stroke, bezierDetail, vertexCode(), kind); } @@ -1416,14 +1484,20 @@ public class PShapeOpenGL extends PShape { @Override public void curveDetail(int detail) { curveDetail = detail; - pg.curveDetail(detail); +// pg.curveDetail(detail); + if (0 < inGeo.codeCount) { + markForTessellation(); + } } @Override public void curveTightness(float tightness) { curveTightness = tightness; - pg.curveTightness(tightness); +// pg.curveTightness(tightness); + if (0 < inGeo.codeCount) { + markForTessellation(); + } } @@ -1443,8 +1517,10 @@ public class PShapeOpenGL extends PShape { inGeo.setMaterial(fillColor, strokeColor, strokeWeight, ambientColor, specularColor, emissiveColor, shininess); inGeo.setNormal(normalX, normalY, normalZ); - inGeo.addCurveVertex(x, y, z, - fill, stroke, curveDetail, vertexCode(), kind); + inGeo.addCurveVertex(x, y, z, vertexBreak()); +// inGeo.addVertex(x, y, z, CURVE_VERTEX, vertexBreak()); +// inGeo.addCurveVertex(x, y, z, +// fill, stroke, curveDetail, vertexCode(), kind); } @@ -1816,7 +1892,7 @@ public class PShapeOpenGL extends PShape { } else if (this.stroke != stroke) { if (this.stroke) { // Disabling stroke on a shape previously with - // stroke needs a re-tesellation in order to remove + // stroke needs a re-tessellation in order to remove // the additional geometry of lines and/or points. markForTessellation(); stroke = false; @@ -1937,7 +2013,7 @@ public class PShapeOpenGL extends PShape { root.setModifiedLineAttributes(firstLineVertex, lastLineVertex); } else if (is2D()) { // Changing the stroke weight on a 2D shape needs a - // re-tesellation in order to replace the old line + // re-tessellation in order to replace the old line // geometry. markForTessellation(); } @@ -1951,7 +2027,7 @@ public class PShapeOpenGL extends PShape { root.setModifiedPointAttributes(firstPointVertex, lastPointVertex); } else if (is2D()) { // Changing the stroke weight on a 2D shape needs a - // re-tesellation in order to replace the old point + // re-tessellation in order to replace the old point // geometry. markForTessellation(); } @@ -1987,7 +2063,7 @@ public class PShapeOpenGL extends PShape { } else { if (is2D() && strokeJoin != join) { // Changing the stroke join on a 2D shape needs a - // re-tesellation in order to replace the old join + // re-tessellation in order to replace the old join // geometry. markForTessellation(); } @@ -2011,7 +2087,7 @@ public class PShapeOpenGL extends PShape { } else { if (is2D() && strokeCap != cap) { // Changing the stroke cap on a 2D shape needs a - // re-tesellation in order to replace the old cap + // re-tessellation in order to replace the old cap // geometry. markForTessellation(); } @@ -2271,6 +2347,50 @@ public class PShapeOpenGL extends PShape { markForTessellation(); } + /////////////////////////////////////////////////////////// + + // + + // Vertex codes + + + @Override + public int[] getVertexCodes() { + if (family == GROUP) return null; + else { + if (family == PRIMITIVE || family == PATH) { + // the input geometry of primitive and path shapes is built during + // tessellation + updateTessellation(); + } + if (inGeo.codes == null) return null; + return inGeo.codes; + } + } + + + @Override + public int getVertexCodeCount() { + if (family == GROUP) return 0; + else { + if (family == PRIMITIVE || family == PATH) { + // the input geometry of primitive and path shapes is built during + // tessellation + updateTessellation(); + } + return inGeo.codeCount; + } + } + + + /** + * One of VERTEX, BEZIER_VERTEX, CURVE_VERTEX, or BREAK. + */ + @Override + public int getVertexCode(int index) { + return inGeo.codes[index]; + } + /////////////////////////////////////////////////////////// @@ -2278,6 +2398,7 @@ public class PShapeOpenGL extends PShape { // Tessellated geometry getter. + @Override public PShape getTessellation() { updateTessellation(); @@ -2290,9 +2411,9 @@ public class PShapeOpenGL extends PShape { PShape tess; if (is3D()) { - tess = PGraphics3D.createShapeImpl(pg.parent, PShape.GEOMETRY); + tess = PGraphics3D.createShapeImpl(pg, PShape.GEOMETRY); } else if (is2D()) { - tess = PGraphics2D.createShapeImpl(pg.parent, PShape.GEOMETRY); + tess = PGraphics2D.createShapeImpl(pg, PShape.GEOMETRY); } else { PGraphics.showWarning("This shape is not either 2D or 3D!"); return null; @@ -2373,6 +2494,34 @@ public class PShapeOpenGL extends PShape { } + /////////////////////////////////////////////////////////// + + // + + // Geometry utils + + // http://www.ecse.rpi.edu/Homepages/wrf/Research/Short_Notes/pnpoly.html + @Override + public boolean contains(float x, float y) { + if (family == PATH) { + boolean c = false; + for (int i = 0, j = inGeo.vertexCount-1; i < inGeo.vertexCount; j = i++) { + if (((inGeo.vertices[3 * i + 1] > y) != (inGeo.vertices[3 * j + 1] > y)) && + (x < + (inGeo.vertices[3 * j]-inGeo.vertices[3 * i]) * + (y-inGeo.vertices[3 * i + 1]) / + (inGeo.vertices[3 * j + 1]-inGeo.vertices[3 * i + 1]) + + inGeo.vertices[3 * i])) { + c = !c; + } + } + return c; + } else { + throw new IllegalArgumentException("The contains() method is only implemented for paths."); + } + } + + /////////////////////////////////////////////////////////// // @@ -2452,7 +2601,7 @@ public class PShapeOpenGL extends PShape { protected void tessellate() { if (root == this && parent == null) { if (tessGeo == null) { - tessGeo = pg.newTessGeometry(PGraphicsOpenGL.RETAINED); + tessGeo = PGraphicsOpenGL.newTessGeometry(pg, PGraphicsOpenGL.RETAINED); } tessGeo.clear(); @@ -2492,12 +2641,13 @@ public class PShapeOpenGL extends PShape { tessellator.setInGeometry(inGeo); tessellator.setTessGeometry(tessGeo); tessellator.setFill(fill || image != null); + tessellator.setTexCache(null, null); tessellator.setStroke(stroke); tessellator.setStrokeColor(strokeColor); tessellator.setStrokeWeight(strokeWeight); tessellator.setStrokeCap(strokeCap); tessellator.setStrokeJoin(strokeJoin); - tessellator.setTexCache(null, null, null); + tessellator.setRenderer(pg); tessellator.setTransform(matrix); tessellator.set3D(is3D()); @@ -2531,9 +2681,18 @@ public class PShapeOpenGL extends PShape { if (normalMode == NORMAL_MODE_AUTO) inGeo.calcQuadStripNormals(); tessellator.tessellateQuadStrip(); } else if (kind == POLYGON) { - if (stroke) inGeo.addPolygonEdges(isClosed); - tessellator.tessellatePolygon(isSolid, isClosed, + boolean bez = inGeo.hasBezierVertex(); + boolean quad = inGeo.hasQuadraticVertex(); + boolean curv = inGeo.hasCurveVertex(); + if (bez || quad) saveBezierVertexSettings(); + if (curv) { + saveCurveVertexSettings(); + tessellator.resetCurveVertexCount(); + } + tessellator.tessellatePolygon(solid, close, normalMode == NORMAL_MODE_AUTO); + if (bez ||quad) restoreBezierVertexSettings(); + if (curv) restoreCurveVertexSettings(); } } else if (family == PRIMITIVE) { // The input geometry needs to be cleared because the geometry @@ -2680,7 +2839,7 @@ public class PShapeOpenGL extends PShape { x2, y2, 0, x3, y3, 0, x4, y4, 0, - fill, stroke); + stroke); tessellator.tessellateQuads(); } @@ -2689,20 +2848,18 @@ public class PShapeOpenGL extends PShape { float a = 0, b = 0, c = 0, d = 0; float tl = 0, tr = 0, br = 0, bl = 0; boolean rounded = false; - if (params.length == 4) { + int mode = rectMode; + + if (params.length == 4 || params.length == 5) { + a = params[0]; + b = params[1]; + c = params[2]; + d = params[3]; + if (params.length == 5) { + mode = (int)(params[4]); + } rounded = false; - a = params[0]; - b = params[1]; - c = params[2]; - d = params[3]; - } else if (params.length == 5) { - a = params[0]; - b = params[1]; - c = params[2]; - d = params[3]; - tl = tr = br = bl = params[4]; - rounded = true; - } else if (params.length == 8) { + } else if (params.length == 8 || params.length == 9) { a = params[0]; b = params[1]; c = params[2]; @@ -2711,20 +2868,60 @@ public class PShapeOpenGL extends PShape { tr = params[5]; br = params[6]; bl = params[7]; + if (params.length == 9) { + mode = (int)(params[8]); + } rounded = true; } + float hradius, vradius; + switch (mode) { + case CORNERS: + break; + case CORNER: + c += a; d += b; + break; + case RADIUS: + hradius = c; + vradius = d; + c = a + hradius; + d = b + vradius; + a -= hradius; + b -= vradius; + break; + case CENTER: + hradius = c / 2.0f; + vradius = d / 2.0f; + c = a + hradius; + d = b + vradius; + a -= hradius; + b -= vradius; + } + + if (a > c) { + float temp = a; a = c; c = temp; + } + + if (b > d) { + float temp = b; b = d; d = temp; + } + + float maxRounding = PApplet.min((c - a) / 2, (d - b) / 2); + if (tl > maxRounding) tl = maxRounding; + if (tr > maxRounding) tr = maxRounding; + if (br > maxRounding) br = maxRounding; + if (bl > maxRounding) bl = maxRounding; + inGeo.setMaterial(fillColor, strokeColor, strokeWeight, ambientColor, specularColor, emissiveColor, shininess); inGeo.setNormal(normalX, normalY, normalZ); if (rounded) { - inGeo.addRect(a, b, c, d, - tl, tr, br, bl, - fill, stroke, bezierDetail, CORNER); + saveBezierVertexSettings(); + inGeo.addRect(a, b, c, d, tl, tr, br, bl, stroke); tessellator.tessellatePolygon(false, true, true); + restoreBezierVertexSettings(); } else { - inGeo.addRect(a, b, c, d, - fill, stroke, CORNER); + inGeo.addRect(a, b, c, d, stroke); tessellator.tessellateQuads(); } } @@ -2732,17 +2929,52 @@ public class PShapeOpenGL extends PShape { protected void tessellateEllipse() { float a = 0, b = 0, c = 0, d = 0; - if (params.length == 4) { + int mode = ellipseMode; + + if (4 <= params.length) { a = params[0]; b = params[1]; c = params[2]; d = params[3]; + if (params.length == 5) { + mode = (int)(params[4]); + } + } + + float x = a; + float y = b; + float w = c; + float h = d; + + if (mode == CORNERS) { + w = c - a; + h = d - b; + + } else if (mode == RADIUS) { + x = a - c; + y = b - d; + w = c * 2; + h = d * 2; + + } else if (mode == DIAMETER) { + x = a - c/2f; + y = b - d/2f; + } + + if (w < 0) { // undo negative width + x += w; + w = -w; + } + + if (h < 0) { // undo negative height + y += h; + h = -h; } inGeo.setMaterial(fillColor, strokeColor, strokeWeight, ambientColor, specularColor, emissiveColor, shininess); inGeo.setNormal(normalX, normalY, normalZ); - inGeo.addEllipse(a, b, c, d, fill, stroke, CORNER); + inGeo.addEllipse(x, y, w, h, fill, stroke); tessellator.tessellateTriangleFan(); } @@ -2750,25 +2982,61 @@ public class PShapeOpenGL extends PShape { protected void tessellateArc() { float a = 0, b = 0, c = 0, d = 0; float start = 0, stop = 0; -// int mode = 0; - if (params.length == 6 || params.length == 7) { + int mode = ellipseMode; + + if (6 <= params.length) { a = params[0]; b = params[1]; c = params[2]; d = params[3]; start = params[4]; stop = params[5]; - // Not using arc mode since PShape only uses CORNER -// if (params.length == 7) { -// mode = (int)(params[6]); -// } + if (params.length == 7) { + mode = (int)(params[6]); + } } - inGeo.setMaterial(fillColor, strokeColor, strokeWeight, - ambientColor, specularColor, emissiveColor, shininess); - inGeo.setNormal(normalX, normalY, normalZ); - inGeo.addArc(a, b, c, d, start, stop, fill, stroke, CORNER); - tessellator.tessellateTriangleFan(); + float x = a; + float y = b; + float w = c; + float h = d; + + if (mode == CORNERS) { + w = c - a; + h = d - b; + + } else if (mode == RADIUS) { + x = a - c; + y = b - d; + w = c * 2; + h = d * 2; + + } else if (mode == CENTER) { + x = a - c/2f; + y = b - d/2f; + } + + // make sure the loop will exit before starting while + if (!Float.isInfinite(start) && !Float.isInfinite(stop)) { + // ignore equal and degenerate cases + if (stop > start) { + // make sure that we're starting at a useful point + while (start < 0) { + start += TWO_PI; + stop += TWO_PI; + } + + if (stop - start > TWO_PI) { + start = 0; + stop = TWO_PI; + } + inGeo.setMaterial(fillColor, strokeColor, strokeWeight, + ambientColor, specularColor, emissiveColor, shininess); + inGeo.setNormal(normalX, normalY, normalZ); + inGeo.addArc(x, y, w, h, start, stop, fill, stroke, mode); + tessellator.tessellateTriangleFan(); + } + } } @@ -2803,10 +3071,23 @@ public class PShapeOpenGL extends PShape { } } + if (nu < 3 || nv < 2) { + nu = nv = 30; + } + int savedDetailU = pg.sphereDetailU; + int savedDetailV = pg.sphereDetailV; + if (pg.sphereDetailU != nu || pg.sphereDetailV != nv) { + pg.sphereDetail(nu, nv); + } + inGeo.setMaterial(fillColor, strokeColor, strokeWeight, ambientColor, specularColor, emissiveColor, shininess); int[] indices = inGeo.addSphere(r, nu, nv, fill, stroke); tessellator.tessellateTriangles(indices); + + if (savedDetailU != nu || savedDetailV != nv) { + pg.sphereDetail(savedDetailU, savedDetailV); + } } @@ -2817,18 +3098,19 @@ public class PShapeOpenGL extends PShape { ambientColor, specularColor, emissiveColor, shininess); if (vertexCodeCount == 0) { // each point is a simple vertex - if (vertices[0].length == 2) { // tesellating 2D vertices + if (vertices[0].length == 2) { // tessellating 2D vertices for (int i = 0; i < vertexCount; i++) { - inGeo.addVertex(vertices[i][X], vertices[i][Y], VERTEX); + inGeo.addVertex(vertices[i][X], vertices[i][Y], VERTEX, false); } } else { // drawing 3D vertices for (int i = 0; i < vertexCount; i++) { - inGeo.addVertex(vertices[i][X], vertices[i][Y], vertices[i][Z], VERTEX); + inGeo.addVertex(vertices[i][X], vertices[i][Y], vertices[i][Z], + VERTEX, false); } } } else { // coded set of vertices int idx = 0; - int code = BREAK; + boolean brk = true; if (vertices[0].length == 2) { // tessellating a 2D path @@ -2836,16 +3118,16 @@ public class PShapeOpenGL extends PShape { switch (vertexCodes[j]) { case VERTEX: - inGeo.addVertex(vertices[idx][X], vertices[idx][Y], code); - code = VERTEX; + inGeo.addVertex(vertices[idx][X], vertices[idx][Y], VERTEX, brk); + brk = false; idx++; break; case QUADRATIC_VERTEX: inGeo.addQuadraticVertex(vertices[idx+0][X], vertices[idx+0][Y], 0, vertices[idx+1][X], vertices[idx+1][Y], 0, - fill, stroke, bezierDetail, code); - code = VERTEX; + brk); + brk = false; idx += 2; break; @@ -2853,20 +3135,19 @@ public class PShapeOpenGL extends PShape { inGeo.addBezierVertex(vertices[idx+0][X], vertices[idx+0][Y], 0, vertices[idx+1][X], vertices[idx+1][Y], 0, vertices[idx+2][X], vertices[idx+2][Y], 0, - fill, stroke, bezierDetail, code); - code = VERTEX; + brk); + brk = false; idx += 3; break; case CURVE_VERTEX: - inGeo.addCurveVertex(vertices[idx][X], vertices[idx][Y], 0, - fill, stroke, curveDetail, code); - code = VERTEX; + inGeo.addCurveVertex(vertices[idx][X], vertices[idx][Y], 0, brk); + brk = false; idx++; break; case BREAK: - code = BREAK; + brk = true; } } } else { // tessellating a 3D path @@ -2875,8 +3156,8 @@ public class PShapeOpenGL extends PShape { case VERTEX: inGeo.addVertex(vertices[idx][X], vertices[idx][Y], - vertices[idx][Z], code); - code = VERTEX; + vertices[idx][Z], brk); + brk = false; idx++; break; @@ -2887,12 +3168,11 @@ public class PShapeOpenGL extends PShape { vertices[idx+1][X], vertices[idx+1][Y], vertices[idx+0][Z], - fill, stroke, bezierDetail, code); - code = VERTEX; + brk); + brk = false; idx += 2; break; - case BEZIER_VERTEX: inGeo.addBezierVertex(vertices[idx+0][X], vertices[idx+0][Y], @@ -2903,8 +3183,8 @@ public class PShapeOpenGL extends PShape { vertices[idx+2][X], vertices[idx+2][Y], vertices[idx+2][Z], - fill, stroke, bezierDetail, code); - code = VERTEX; + brk); + brk = false; idx += 3; break; @@ -2912,22 +3192,63 @@ public class PShapeOpenGL extends PShape { inGeo.addCurveVertex(vertices[idx][X], vertices[idx][Y], vertices[idx][Z], - fill, stroke, curveDetail, code); - code = VERTEX; + brk); + brk = false; idx++; break; case BREAK: - code = BREAK; + brk = true; } } } } - if (stroke) inGeo.addPolygonEdges(isClosed); - tessellator.tessellatePolygon(false, isClosed, true); + boolean bez = inGeo.hasBezierVertex(); + boolean quad = inGeo.hasQuadraticVertex(); + boolean curv = inGeo.hasCurveVertex(); + if (bez || quad) saveBezierVertexSettings(); + if (curv) { + saveCurveVertexSettings(); + tessellator.resetCurveVertexCount(); + } + tessellator.tessellatePolygon(false, close, true); + if (bez || quad) restoreBezierVertexSettings(); + if (curv) restoreCurveVertexSettings(); } + protected void saveBezierVertexSettings() { + savedBezierDetail = pg.bezierDetail; + if (pg.bezierDetail != bezierDetail) { + pg.bezierDetail(bezierDetail); + } + } + + protected void restoreBezierVertexSettings() { + if (savedBezierDetail != bezierDetail) { + pg.bezierDetail(savedBezierDetail); + } + } + + protected void saveCurveVertexSettings() { + savedCurveDetail = pg.curveDetail; + savedCurveTightness = pg.curveTightness; + if (pg.curveDetail != curveDetail) { + pg.curveDetail(curveDetail); + } + if (pg.curveTightness != curveTightness) { + pg.curveTightness(curveTightness); + } + } + + protected void restoreCurveVertexSettings() { + if (savedCurveDetail != curveDetail) { + pg.curveDetail(savedCurveDetail); + } + if (savedCurveTightness != curveTightness) { + pg.curveTightness(savedCurveTightness); + } + } /////////////////////////////////////////////////////////// @@ -3018,7 +3339,6 @@ public class PShapeOpenGL extends PShape { if (matrix != null) { // Some geometric transformations were applied on // this shape before tessellation, so they are applied now. - //applyMatrixImpl(matrix); if (hasPolys) { tessGeo.applyMatrixOnPolyGeometry(matrix, firstPolyVertex, lastPolyVertex); @@ -3329,56 +3649,56 @@ public class PShapeOpenGL extends PShape { tessGeo.updatePolyVerticesBuffer(); if (glPolyVertex == 0) - glPolyVertex = PGraphicsOpenGL.createVertexBufferObject(context); + glPolyVertex = PGraphicsOpenGL.createVertexBufferObject(context, pgl); pgl.bindBuffer(PGL.ARRAY_BUFFER, glPolyVertex); pgl.bufferData(PGL.ARRAY_BUFFER, 4 * sizef, tessGeo.polyVerticesBuffer, PGL.STATIC_DRAW); tessGeo.updatePolyColorsBuffer(); if (glPolyColor == 0) - glPolyColor = PGraphicsOpenGL.createVertexBufferObject(context); + glPolyColor = PGraphicsOpenGL.createVertexBufferObject(context, pgl); pgl.bindBuffer(PGL.ARRAY_BUFFER, glPolyColor); pgl.bufferData(PGL.ARRAY_BUFFER, sizei, tessGeo.polyColorsBuffer, PGL.STATIC_DRAW); tessGeo.updatePolyNormalsBuffer(); if (glPolyNormal == 0) - glPolyNormal = PGraphicsOpenGL.createVertexBufferObject(context); + glPolyNormal = PGraphicsOpenGL.createVertexBufferObject(context, pgl); pgl.bindBuffer(PGL.ARRAY_BUFFER, glPolyNormal); pgl.bufferData(PGL.ARRAY_BUFFER, 3 * sizef, tessGeo.polyNormalsBuffer, PGL.STATIC_DRAW); tessGeo.updatePolyTexCoordsBuffer(); if (glPolyTexcoord == 0) - glPolyTexcoord = PGraphicsOpenGL.createVertexBufferObject(context); + glPolyTexcoord = PGraphicsOpenGL.createVertexBufferObject(context, pgl); pgl.bindBuffer(PGL.ARRAY_BUFFER, glPolyTexcoord); pgl.bufferData(PGL.ARRAY_BUFFER, 2 * sizef, tessGeo.polyTexCoordsBuffer, PGL.STATIC_DRAW); tessGeo.updatePolyAmbientBuffer(); if (glPolyAmbient == 0) - glPolyAmbient = PGraphicsOpenGL.createVertexBufferObject(context); + glPolyAmbient = PGraphicsOpenGL.createVertexBufferObject(context, pgl); pgl.bindBuffer(PGL.ARRAY_BUFFER, glPolyAmbient); pgl.bufferData(PGL.ARRAY_BUFFER, sizei, tessGeo.polyAmbientBuffer, PGL.STATIC_DRAW); tessGeo.updatePolySpecularBuffer(); if (glPolySpecular == 0) - glPolySpecular = PGraphicsOpenGL.createVertexBufferObject(context); + glPolySpecular = PGraphicsOpenGL.createVertexBufferObject(context, pgl); pgl.bindBuffer(PGL.ARRAY_BUFFER, glPolySpecular); pgl.bufferData(PGL.ARRAY_BUFFER, sizei, tessGeo.polySpecularBuffer, PGL.STATIC_DRAW); tessGeo.updatePolyEmissiveBuffer(); if (glPolyEmissive == 0) - glPolyEmissive = PGraphicsOpenGL.createVertexBufferObject(context); + glPolyEmissive = PGraphicsOpenGL.createVertexBufferObject(context, pgl); pgl.bindBuffer(PGL.ARRAY_BUFFER, glPolyEmissive); pgl.bufferData(PGL.ARRAY_BUFFER, sizei, tessGeo.polyEmissiveBuffer, PGL.STATIC_DRAW); tessGeo.updatePolyShininessBuffer(); if (glPolyShininess == 0) - glPolyShininess = PGraphicsOpenGL.createVertexBufferObject(context); + glPolyShininess = PGraphicsOpenGL.createVertexBufferObject(context, pgl); pgl.bindBuffer(PGL.ARRAY_BUFFER, glPolyShininess); pgl.bufferData(PGL.ARRAY_BUFFER, sizef, tessGeo.polyShininessBuffer, PGL.STATIC_DRAW); @@ -3387,7 +3707,7 @@ public class PShapeOpenGL extends PShape { tessGeo.updatePolyIndicesBuffer(); if (glPolyIndex == 0) - glPolyIndex = PGraphicsOpenGL.createVertexBufferObject(context); + glPolyIndex = PGraphicsOpenGL.createVertexBufferObject(context, pgl); pgl.bindBuffer(PGL.ELEMENT_ARRAY_BUFFER, glPolyIndex); pgl.bufferData(PGL.ELEMENT_ARRAY_BUFFER, tessGeo.polyIndexCount * PGL.SIZEOF_INDEX, @@ -3404,21 +3724,21 @@ public class PShapeOpenGL extends PShape { tessGeo.updateLineVerticesBuffer(); if (glLineVertex == 0) - glLineVertex = PGraphicsOpenGL.createVertexBufferObject(context); + glLineVertex = PGraphicsOpenGL.createVertexBufferObject(context, pgl); pgl.bindBuffer(PGL.ARRAY_BUFFER, glLineVertex); pgl.bufferData(PGL.ARRAY_BUFFER, 4 * sizef, tessGeo.lineVerticesBuffer, PGL.STATIC_DRAW); tessGeo.updateLineColorsBuffer(); if (glLineColor == 0) - glLineColor = PGraphicsOpenGL.createVertexBufferObject(context); + glLineColor = PGraphicsOpenGL.createVertexBufferObject(context, pgl); pgl.bindBuffer(PGL.ARRAY_BUFFER, glLineColor); pgl.bufferData(PGL.ARRAY_BUFFER, sizei, tessGeo.lineColorsBuffer, PGL.STATIC_DRAW); tessGeo.updateLineDirectionsBuffer(); if (glLineAttrib == 0) - glLineAttrib = PGraphicsOpenGL.createVertexBufferObject(context); + glLineAttrib = PGraphicsOpenGL.createVertexBufferObject(context, pgl); pgl.bindBuffer(PGL.ARRAY_BUFFER, glLineAttrib); pgl.bufferData(PGL.ARRAY_BUFFER, 4 * sizef, tessGeo.lineDirectionsBuffer, PGL.STATIC_DRAW); @@ -3427,7 +3747,7 @@ public class PShapeOpenGL extends PShape { tessGeo.updateLineIndicesBuffer(); if (glLineIndex == 0) - glLineIndex = PGraphicsOpenGL.createVertexBufferObject(context); + glLineIndex = PGraphicsOpenGL.createVertexBufferObject(context, pgl); pgl.bindBuffer(PGL.ELEMENT_ARRAY_BUFFER, glLineIndex); pgl.bufferData(PGL.ELEMENT_ARRAY_BUFFER, tessGeo.lineIndexCount * PGL.SIZEOF_INDEX, @@ -3444,21 +3764,21 @@ public class PShapeOpenGL extends PShape { tessGeo.updatePointVerticesBuffer(); if (glPointVertex == 0) - glPointVertex = PGraphicsOpenGL.createVertexBufferObject(context); + glPointVertex = PGraphicsOpenGL.createVertexBufferObject(context, pgl); pgl.bindBuffer(PGL.ARRAY_BUFFER, glPointVertex); pgl.bufferData(PGL.ARRAY_BUFFER, 4 * sizef, tessGeo.pointVerticesBuffer, PGL.STATIC_DRAW); tessGeo.updatePointColorsBuffer(); if (glPointColor == 0) - glPointColor = PGraphicsOpenGL.createVertexBufferObject(context); + glPointColor = PGraphicsOpenGL.createVertexBufferObject(context, pgl); pgl.bindBuffer(PGL.ARRAY_BUFFER, glPointColor); pgl.bufferData(PGL.ARRAY_BUFFER, sizei, tessGeo.pointColorsBuffer, PGL.STATIC_DRAW); tessGeo.updatePointOffsetsBuffer(); if (glPointAttrib == 0) - glPointAttrib = PGraphicsOpenGL.createVertexBufferObject(context); + glPointAttrib = PGraphicsOpenGL.createVertexBufferObject(context, pgl); pgl.bindBuffer(PGL.ARRAY_BUFFER, glPointAttrib); pgl.bufferData(PGL.ARRAY_BUFFER, 2 * sizef, tessGeo.pointOffsetsBuffer, PGL.STATIC_DRAW); @@ -3467,7 +3787,7 @@ public class PShapeOpenGL extends PShape { tessGeo.updatePointIndicesBuffer(); if (glPointIndex == 0) - glPointIndex = PGraphicsOpenGL.createVertexBufferObject(context); + glPointIndex = PGraphicsOpenGL.createVertexBufferObject(context, pgl); pgl.bindBuffer(PGL.ELEMENT_ARRAY_BUFFER, glPointIndex); pgl.bufferData(PGL.ELEMENT_ARRAY_BUFFER, tessGeo.pointIndexCount * PGL.SIZEOF_INDEX, @@ -3548,47 +3868,47 @@ public class PShapeOpenGL extends PShape { protected void deletePolyBuffers() { if (glPolyVertex != 0) { - PGraphicsOpenGL.deleteVertexBufferObject(glPolyVertex, context); + PGraphicsOpenGL.deleteVertexBufferObject(glPolyVertex, context, pgl); glPolyVertex = 0; } if (glPolyColor != 0) { - PGraphicsOpenGL.deleteVertexBufferObject(glPolyColor, context); + PGraphicsOpenGL.deleteVertexBufferObject(glPolyColor, context, pgl); glPolyColor = 0; } if (glPolyNormal != 0) { - PGraphicsOpenGL.deleteVertexBufferObject(glPolyNormal, context); + PGraphicsOpenGL.deleteVertexBufferObject(glPolyNormal, context, pgl); glPolyNormal = 0; } if (glPolyTexcoord != 0) { - PGraphicsOpenGL.deleteVertexBufferObject(glPolyTexcoord, context); + PGraphicsOpenGL.deleteVertexBufferObject(glPolyTexcoord, context, pgl); glPolyTexcoord = 0; } if (glPolyAmbient != 0) { - PGraphicsOpenGL.deleteVertexBufferObject(glPolyAmbient, context); + PGraphicsOpenGL.deleteVertexBufferObject(glPolyAmbient, context, pgl); glPolyAmbient = 0; } if (glPolySpecular != 0) { - PGraphicsOpenGL.deleteVertexBufferObject(glPolySpecular, context); + PGraphicsOpenGL.deleteVertexBufferObject(glPolySpecular, context, pgl); glPolySpecular = 0; } if (glPolyEmissive != 0) { - PGraphicsOpenGL.deleteVertexBufferObject(glPolyEmissive, context); + PGraphicsOpenGL.deleteVertexBufferObject(glPolyEmissive, context, pgl); glPolyEmissive = 0; } if (glPolyShininess != 0) { - PGraphicsOpenGL.deleteVertexBufferObject(glPolyShininess, context); + PGraphicsOpenGL.deleteVertexBufferObject(glPolyShininess, context, pgl); glPolyShininess = 0; } if (glPolyIndex != 0) { - PGraphicsOpenGL.deleteVertexBufferObject(glPolyIndex, context); + PGraphicsOpenGL.deleteVertexBufferObject(glPolyIndex, context, pgl); glPolyIndex = 0; } } @@ -3596,22 +3916,22 @@ public class PShapeOpenGL extends PShape { protected void deleteLineBuffers() { if (glLineVertex != 0) { - PGraphicsOpenGL.deleteVertexBufferObject(glLineVertex, context); + PGraphicsOpenGL.deleteVertexBufferObject(glLineVertex, context, pgl); glLineVertex = 0; } if (glLineColor != 0) { - PGraphicsOpenGL.deleteVertexBufferObject(glLineColor, context); + PGraphicsOpenGL.deleteVertexBufferObject(glLineColor, context, pgl); glLineColor = 0; } if (glLineAttrib != 0) { - PGraphicsOpenGL.deleteVertexBufferObject(glLineAttrib, context); + PGraphicsOpenGL.deleteVertexBufferObject(glLineAttrib, context, pgl); glLineAttrib = 0; } if (glLineIndex != 0) { - PGraphicsOpenGL.deleteVertexBufferObject(glLineIndex, context); + PGraphicsOpenGL.deleteVertexBufferObject(glLineIndex, context, pgl); glLineIndex = 0; } } @@ -3619,22 +3939,22 @@ public class PShapeOpenGL extends PShape { protected void deletePointBuffers() { if (glPointVertex != 0) { - PGraphicsOpenGL.deleteVertexBufferObject(glPointVertex, context); + PGraphicsOpenGL.deleteVertexBufferObject(glPointVertex, context, pgl); glPointVertex = 0; } if (glPointColor != 0) { - PGraphicsOpenGL.deleteVertexBufferObject(glPointColor, context); + PGraphicsOpenGL.deleteVertexBufferObject(glPointColor, context, pgl); glPointColor = 0; } if (glPointAttrib != 0) { - PGraphicsOpenGL.deleteVertexBufferObject(glPointAttrib, context); + PGraphicsOpenGL.deleteVertexBufferObject(glPointAttrib, context, pgl); glPointAttrib = 0; } if (glPointIndex != 0) { - PGraphicsOpenGL.deleteVertexBufferObject(glPointIndex, context); + PGraphicsOpenGL.deleteVertexBufferObject(glPointIndex, context, pgl); glPointIndex = 0; } } @@ -4055,10 +4375,63 @@ public class PShapeOpenGL extends PShape { return; } + // Saving the current values to use if the style is re-enabled later + savedStroke = stroke; + savedStrokeColor = strokeColor; + savedStrokeWeight = strokeWeight; + savedStrokeCap = strokeCap; + savedStrokeJoin = strokeJoin; + savedFill = fill; + savedFillColor = fillColor; + savedTint = tint; + savedTintColor = tintColor; + savedAmbientColor = ambientColor; + savedSpecularColor = specularColor; + savedEmissiveColor = emissiveColor; + savedShininess = shininess; + savedTextureMode = textureMode; + super.disableStyle(); } + @Override + public void enableStyle() { + if (savedStroke) { + setStroke(true); + setStroke(savedStrokeColor); + setStrokeWeight(savedStrokeWeight); + setStrokeCap(savedStrokeCap); + setStrokeJoin(savedStrokeJoin); + } else { + setStroke(false); + } + + if (savedFill) { + setFill(true); + setFill(savedFillColor); + } else { + setFill(false); + } + + if (savedTint) { + setTint(true); + setTint(savedTintColor); + } + + setAmbient(savedAmbientColor); + setSpecular(savedSpecularColor); + setEmissive(savedEmissiveColor); + setShininess(savedShininess); + + if (image != null) { + setTextureMode(savedTextureMode); + } + + super.enableStyle(); + } + + // Applies the styles of g. @Override protected void styles(PGraphics g) { @@ -4232,10 +4605,14 @@ public class PShapeOpenGL extends PShape { protected void renderPolys(PGraphicsOpenGL g, PImage textureImage) { + boolean customShader = g.polyShader != null; + boolean needNormals = customShader ? g.polyShader.accessNormals() : false; + boolean needTexCoords = customShader ? g.polyShader.accessTexCoords() : false; + Texture tex = textureImage != null ? g.getTexture(textureImage) : null; boolean renderingFill = false, renderingStroke = false; - BaseShader shader = null; + PShader shader = null; IndexCache cache = tessGeo.polyIndexCache; for (int n = firstPolyIndexCache; n <= lastPolyIndexCache; n++) { if (is3D() || (tex != null && (firstLineIndexCache == -1 || @@ -4291,19 +4668,18 @@ public class PShapeOpenGL extends PShape { shader.setShininessAttribute(root.glPolyShininess, 1, PGL.FLOAT, 0, voffset * PGL.SIZEOF_FLOAT); } - - if (tex != null) { + if (g.lights || needNormals) { shader.setNormalAttribute(root.glPolyNormal, 3, PGL.FLOAT, 0, 3 * voffset * PGL.SIZEOF_FLOAT); + } + + if (tex != null || needTexCoords) { shader.setTexcoordAttribute(root.glPolyTexcoord, 2, PGL.FLOAT, 0, 2 * voffset * PGL.SIZEOF_FLOAT); shader.setTexture(tex); } - pgl.bindBuffer(PGL.ELEMENT_ARRAY_BUFFER, root.glPolyIndex); - pgl.drawElements(PGL.TRIANGLES, icount, PGL.INDEX_TYPE, - ioffset * PGL.SIZEOF_INDEX); - pgl.bindBuffer(PGL.ELEMENT_ARRAY_BUFFER, 0); + shader.draw(root.glPolyIndex, icount, ioffset); } if (shader != null && shader.bound()) { @@ -4408,7 +4784,7 @@ public class PShapeOpenGL extends PShape { protected void renderLines(PGraphicsOpenGL g) { - LineShader shader = g.getLineShader(); + PShader shader = g.getLineShader(); shader.bind(); IndexCache cache = tessGeo.lineIndexCache; @@ -4424,10 +4800,7 @@ public class PShapeOpenGL extends PShape { shader.setLineAttribute(root.glLineAttrib, 4, PGL.FLOAT, 0, 4 * voffset * PGL.SIZEOF_FLOAT); - pgl.bindBuffer(PGL.ELEMENT_ARRAY_BUFFER, root.glLineIndex); - pgl.drawElements(PGL.TRIANGLES, icount, PGL.INDEX_TYPE, - ioffset * PGL.SIZEOF_INDEX); - pgl.bindBuffer(PGL.ELEMENT_ARRAY_BUFFER, 0); + shader.draw(root.glLineIndex, icount, ioffset); } shader.unbind(); @@ -4508,7 +4881,7 @@ public class PShapeOpenGL extends PShape { protected void renderPoints(PGraphicsOpenGL g) { - PointShader shader = g.getPointShader(); + PShader shader = g.getPointShader(); shader.bind(); IndexCache cache = tessGeo.pointIndexCache; @@ -4524,10 +4897,7 @@ public class PShapeOpenGL extends PShape { shader.setPointAttribute(root.glPointAttrib, 2, PGL.FLOAT, 0, 2 * voffset * PGL.SIZEOF_FLOAT); - pgl.bindBuffer(PGL.ELEMENT_ARRAY_BUFFER, root.glPointIndex); - pgl.drawElements(PGL.TRIANGLES, icount, PGL.INDEX_TYPE, - ioffset * PGL.SIZEOF_INDEX); - pgl.bindBuffer(PGL.ELEMENT_ARRAY_BUFFER, 0); + shader.draw(root.glPointIndex, icount, ioffset); } shader.unbind(); diff --git a/core/src/processing/opengl/PointVert.glsl b/core/src/processing/opengl/PointVert.glsl index 7523e3326..c677ce75b 100644 --- a/core/src/processing/opengl/PointVert.glsl +++ b/core/src/processing/opengl/PointVert.glsl @@ -20,13 +20,13 @@ #define PROCESSING_POINT_SHADER -uniform mat4 projection; -uniform mat4 modelview; +uniform mat4 projectionMatrix; +uniform mat4 modelviewMatrix; uniform vec4 viewport; uniform int perspective; -attribute vec4 vertex; +attribute vec4 position; attribute vec4 color; attribute vec2 offset; @@ -38,13 +38,13 @@ vec4 windowToClipVector(vec2 window, vec4 viewport, float clipw) { } void main() { - vec4 pos = modelview * vertex; - vec4 clip = projection * pos; + vec4 pos = modelviewMatrix * position; + vec4 clip = projectionMatrix * pos; if (0 < perspective) { // Perspective correction (points will look thiner as they move away // from the view position). - gl_Position = clip + projection * vec4(offset.xy, 0, 0); + gl_Position = clip + projectionMatrix * vec4(offset.xy, 0, 0); } else { // No perspective correction. vec4 offset = windowToClipVector(offset.xy, viewport, clip.w); diff --git a/core/src/processing/opengl/TexlightVert.glsl b/core/src/processing/opengl/TexlightVert.glsl index ff981c59c..6542bf8fb 100644 --- a/core/src/processing/opengl/TexlightVert.glsl +++ b/core/src/processing/opengl/TexlightVert.glsl @@ -20,8 +20,8 @@ #define PROCESSING_TEXLIGHT_SHADER -uniform mat4 modelview; -uniform mat4 transform; +uniform mat4 modelviewMatrix; +uniform mat4 transformMatrix; uniform mat3 normalMatrix; uniform mat4 texMatrix; @@ -34,7 +34,7 @@ uniform vec3 lightSpecular[8]; uniform vec3 lightFalloff[8]; uniform vec2 lightSpot[8]; -attribute vec4 vertex; +attribute vec4 position; attribute vec4 color; attribute vec3 normal; attribute vec2 texCoord; @@ -78,10 +78,10 @@ float blinnPhongFactor(vec3 lightDir, vec3 vertPos, vec3 vecNormal, float shine) void main() { // Vertex in clip coordinates - gl_Position = transform * vertex; + gl_Position = transformMatrix * position; // Vertex in eye coordinates - vec3 ecVertex = vec3(modelview * vertex); + vec3 ecVertex = vec3(modelviewMatrix * position); // Normal vector in eye coordinates vec3 ecNormal = normalize(normalMatrix * normal); diff --git a/core/src/processing/opengl/Texture.java b/core/src/processing/opengl/Texture.java index 323d6f818..5ffd60b0b 100644 --- a/core/src/processing/opengl/Texture.java +++ b/core/src/processing/opengl/Texture.java @@ -1,5 +1,3 @@ -/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */ - /* Part of the Processing project - http://processing.org @@ -38,8 +36,6 @@ import java.util.NoSuchElementException; * */ public class Texture implements PConstants { - // texture constants - /** * Texture with normalized UV. */ @@ -56,15 +52,25 @@ public class Texture implements PConstants { * to linear */ protected static final int LINEAR = 3; /** Bilinear sampling: both magnification filtering is set to linear and - * minification either to linear-mipmap-nearest (linear interplation is used + * minification either to linear-mipmap-nearest (linear interpolation is used * within a mipmap, but not between different mipmaps). */ protected static final int BILINEAR = 4; /** Trilinear sampling: magnification filtering set to linear, minification to * linear-mipmap-linear, which offers the best mipmap quality since linear * interpolation to compute the value in each of two maps and then - * interpolates linearly between these two value. */ + * interpolates linearly between these two values. */ protected static final int TRILINEAR = 5; + + // This constant controls how many times pixelBuffer and rgbaPixels can be + // accessed before they are not released anymore. The idea is that if they + // have been used only a few times, it doesn't make sense to keep them around. + protected static final int MAX_UPDATES = 10; + + // The minimum amount of free JVM's memory (in MB) before pixelBuffer and + // rgbaPixels are released every time after they are used. + protected static final int MIN_MEMORY = 5; + public int width, height; public int glName; @@ -77,6 +83,7 @@ public class Texture implements PConstants { public int glWidth; public int glHeight; + protected PGraphicsOpenGL pg; protected PGL pgl; // The interface between Processing and OpenGL. protected int context; // The context that created this texture. protected boolean colorBuffer; // true if it is the color attachment of @@ -94,6 +101,8 @@ public class Texture implements PConstants { protected int[] rgbaPixels = null; protected IntBuffer pixelBuffer = null; protected FrameBuffer tempFbo = null; + protected int pixBufUpdateCount = 0; + protected int rgbaPixUpdateCount = 0; /** Modified portion of the texture */ protected boolean modified; @@ -110,8 +119,9 @@ public class Texture implements PConstants { // Constructors. - public Texture() { - pgl = PGraphicsOpenGL.pgl; + public Texture(PGraphicsOpenGL pg) { + this.pg = pg; + pgl = pg.pgl; context = pgl.createEmptyContext(); colorBuffer = false; @@ -126,8 +136,8 @@ public class Texture implements PConstants { * @param width int * @param height int */ - public Texture(int width, int height) { - this(width, height, new Parameters()); + public Texture(PGraphicsOpenGL pg, int width, int height) { + this(pg, width, height, new Parameters()); } @@ -138,8 +148,9 @@ public class Texture implements PConstants { * @param height int * @param params Parameters */ - public Texture(int width, int height, Object params) { - pgl = PGraphicsOpenGL.pgl; + public Texture(PGraphicsOpenGL pg, int width, int height, Object params) { + this.pg = pg; + pgl = pg.pgl; context = pgl.createEmptyContext(); colorBuffer = false; @@ -241,7 +252,7 @@ public class Texture implements PConstants { dispose(); // Creating new texture with the appropriate size. - Texture tex = new Texture(wide, high, getParameters()); + Texture tex = new Texture(pg, wide, high, getParameters()); // Copying the contents of this texture into tex. tex.set(this); @@ -318,8 +329,7 @@ public class Texture implements PConstants { return; } - if (pixels.length == 0) { - // Nothing to do (means that w == h == 0) but not an erroneous situation + if (pixels.length == 0 || w == 0 || h == 0) { return; } @@ -334,7 +344,7 @@ public class Texture implements PConstants { if (PGraphicsOpenGL.autoMipmapGenSupported) { // Automatic mipmap generation. loadPixels(w * h); - convertToRGBA(pixels, rgbaPixels, format, w, h); + convertToRGBA(pixels, format, w, h); updatePixelBuffer(rgbaPixels); pgl.texSubImage2D(glTarget, 0, x, y, w, h, PGL.RGBA, PGL.UNSIGNED_BYTE, pixelBuffer); @@ -395,14 +405,14 @@ public class Texture implements PConstants { */ loadPixels(w * h); - convertToRGBA(pixels, rgbaPixels, format, w, h); + convertToRGBA(pixels, format, w, h); updatePixelBuffer(rgbaPixels); pgl.texSubImage2D(glTarget, 0, x, y, w, h, PGL.RGBA, PGL.UNSIGNED_BYTE, pixelBuffer); } } else { loadPixels(w * h); - convertToRGBA(pixels, rgbaPixels, format, w, h); + convertToRGBA(pixels, format, w, h); updatePixelBuffer(rgbaPixels); pgl.texSubImage2D(glTarget, 0, x, y, w, h, PGL.RGBA, PGL.UNSIGNED_BYTE, pixelBuffer); @@ -413,6 +423,9 @@ public class Texture implements PConstants { pgl.disableTexturing(glTarget); } + releasePixelBuffer(); + releaseRGBAPixels(); + updateTexels(x, y, w, h); } @@ -430,6 +443,7 @@ public class Texture implements PConstants { public void setNative(int[] pixels, int x, int y, int w, int h) { updatePixelBuffer(pixels); setNative(pixelBuffer, x, y, w, h); + releasePixelBuffer(); } @@ -499,17 +513,17 @@ public class Texture implements PConstants { } if (tempFbo == null) { - tempFbo = new FrameBuffer(glWidth, glHeight); + tempFbo = new FrameBuffer(pg, glWidth, glHeight); } // Attaching the texture to the color buffer of a FBO, binding the FBO and // reading the pixels from the current draw buffer (which is the color // buffer of the FBO). tempFbo.setColorBuffer(this); - PGraphicsOpenGL.pushFramebuffer(); - PGraphicsOpenGL.setFramebuffer(tempFbo); + pg.pushFramebuffer(); + pg.setFramebuffer(tempFbo); tempFbo.readPixels(); - PGraphicsOpenGL.popFramebuffer(); + pg.popFramebuffer(); tempFbo.getPixels(pixels); convertToARGB(pixels); @@ -815,6 +829,7 @@ public class Texture implements PConstants { protected void updatePixelBuffer(int[] pixels) { pixelBuffer = PGL.updateIntBuffer(pixelBuffer, pixels, true); + pixBufUpdateCount++; } @@ -866,20 +881,35 @@ public class Texture implements PConstants { } public void getBufferPixels(int[] pixels) { + // We get the buffer either from the used buffers or the cache, giving + // priority to the used buffers. Why? Because the used buffer was already + // transferred to the texture, so the pixels should be in sync with the + // texture. BufferData data = null; if (usedBuffers != null && 0 < usedBuffers.size()) { - // the last used buffer is the one currently stored in the opengl the - // texture data = usedBuffers.getLast(); } else if (bufferCache != null && 0 < bufferCache.size()) { - // The first buffer in the cache will be uploaded to the opengl texture - // the next time it is rendered - data = bufferCache.getFirst(); + data = bufferCache.getLast(); } if (data != null) { + if ((data.w != width) || (data.h != height)) { + init(data.w, data.h); + } + data.rgbBuf.rewind(); data.rgbBuf.get(pixels); convertToARGB(pixels); + + // In order to avoid a cached buffer to overwrite the texture when the + // renderer draws the texture, and hence put the pixels put of sync, we + // simply empty the cache. + if (usedBuffers == null) { + usedBuffers = new LinkedList(); + } + while (0 < bufferCache.size()) { + data = bufferCache.remove(0); + usedBuffers.add(data); + } } } @@ -993,38 +1023,36 @@ public class Texture implements PConstants { /** * Reorders a pixel array in the given format into the order required by - * OpenGL (RGBA). Both arrays are assumed to be of the same length. The width - * and height parameters are used in the YUV420 to RBGBA conversion. - * @param intArray int[] - * @param tIntArray int[] - * @param arrayFormat int + * OpenGL (RGBA) and stores it into rgbaPixels. The width and height + * parameters are used in the YUV420 to RBGBA conversion. + * @param pixels int[] + * @param format int * @param w int * @param h int */ - protected void convertToRGBA(int[] intArray, int[] tIntArray, int arrayFormat, - int w, int h) { + protected void convertToRGBA(int[] pixels, int format, int w, int h) { if (PGL.BIG_ENDIAN) { - switch (arrayFormat) { + switch (format) { case ALPHA: // Converting from xxxA into RGBA. RGB is set to white // (0xFFFFFF, i.e.: (255, 255, 255)) - for (int i = 0; i< intArray.length; i++) { - tIntArray[i] = 0xFFFFFF00 | intArray[i]; + for (int i = 0; i< pixels.length; i++) { + rgbaPixels[i] = 0xFFFFFF00 | pixels[i]; } break; case RGB: // Converting xRGB into RGBA. A is set to 0xFF (255, full opacity). - for (int i = 0; i< intArray.length; i++) { - int pixel = intArray[i]; - tIntArray[i] = (pixel << 8) | 0xFF; + for (int i = 0; i< pixels.length; i++) { + int pixel = pixels[i]; + rgbaPixels[i] = (pixel << 8) | 0xFF; } break; case ARGB: // Converting ARGB into RGBA. Shifting RGB to 8 bits to the left, // and bringing A to the first byte. - for (int i = 0; i< intArray.length; i++) { - int pixel = intArray[i]; - tIntArray[i] = (pixel << 8) | ((pixel >> 24) & 0xFF); + for (int i = 0; i< pixels.length; i++) { + int pixel = pixels[i]; + rgbaPixels[i] = (pixel << 8) | ((pixel >> 24) & 0xFF); } break; } @@ -1034,43 +1062,44 @@ public class Texture implements PConstants { // for the most part just need to swap two components here // the sun.cpu.endian here might be "false", oddly enough.. // (that's why just using an "else", rather than check for "little") - switch (arrayFormat) { + switch (format) { case ALPHA: // Converting xxxA into ARGB, with RGB set to white. - for (int i = 0; i< intArray.length; i++) { - tIntArray[i] = (intArray[i] << 24) | 0x00FFFFFF; + for (int i = 0; i< pixels.length; i++) { + rgbaPixels[i] = (pixels[i] << 24) | 0x00FFFFFF; } break; case RGB: // We need to convert xRGB into ABGR, // so R and B must be swapped, and the x just made 0xFF. - for (int i = 0; i< intArray.length; i++) { - int pixel = intArray[i]; - tIntArray[i] = 0xFF000000 | - ((pixel & 0xFF) << 16) | ((pixel & 0xFF0000) >> 16) | - (pixel & 0x0000FF00); + for (int i = 0; i< pixels.length; i++) { + int pixel = pixels[i]; + rgbaPixels[i] = 0xFF000000 | + ((pixel & 0xFF) << 16) | ((pixel & 0xFF0000) >> 16) | + (pixel & 0x0000FF00); } break; case ARGB: // We need to convert ARGB into ABGR, // so R and B must be swapped, A and G just brought back in. - for (int i = 0; i < intArray.length; i++) { - int pixel = intArray[i]; - tIntArray[i] = ((pixel & 0xFF) << 16) | ((pixel & 0xFF0000) >> 16) | - (pixel & 0xFF00FF00); + for (int i = 0; i < pixels.length; i++) { + int pixel = pixels[i]; + rgbaPixels[i] = ((pixel & 0xFF) << 16) | ((pixel & 0xFF0000) >> 16) | + (pixel & 0xFF00FF00); } break; } } + rgbaPixUpdateCount++; } /** * Reorders an OpenGL pixel array (RGBA) into ARGB. The array must be * of size width * height. - * @param intArray int[] + * @param pixels int[] */ - protected void convertToARGB(int[] intArray) { + protected void convertToARGB(int[] pixels) { int t = 0; int p = 0; if (PGL.BIG_ENDIAN) { @@ -1078,8 +1107,8 @@ public class Texture implements PConstants { // and placing A 24 bits to the left. for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { - int pixel = intArray[p++]; - intArray[t++] = (pixel >>> 8) | ((pixel << 24) & 0xFF000000); + int pixel = pixels[p++]; + pixels[t++] = (pixel >>> 8) | ((pixel << 24) & 0xFF000000); } } } else { @@ -1087,8 +1116,8 @@ public class Texture implements PConstants { // A and G just brought back in. for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { - int pixel = intArray[p++]; - intArray[t++] = ((pixel & 0xFF) << 16) | ((pixel & 0xFF0000) >> 16) | + int pixel = pixels[p++]; + pixels[t++] = ((pixel & 0xFF) << 16) | ((pixel & 0xFF0000) >> 16) | (pixel & 0xFF00FF00); } } @@ -1145,7 +1174,7 @@ public class Texture implements PConstants { } context = pgl.getCurrentContext(); - glName = PGraphicsOpenGL.createTextureObject(context); + glName = PGraphicsOpenGL.createTextureObject(context, pgl); pgl.bindTexture(glTarget, glName); pgl.texParameteri(glTarget, PGL.TEXTURE_MIN_FILTER, glMinFilter); @@ -1225,7 +1254,7 @@ public class Texture implements PConstants { } if (tempFbo == null) { - tempFbo = new FrameBuffer(glWidth, glHeight); + tempFbo = new FrameBuffer(pg, glWidth, glHeight); } // This texture is the color (destination) buffer of the FBO. @@ -1233,8 +1262,8 @@ public class Texture implements PConstants { tempFbo.disableDepthTest(); // FBO copy: - PGraphicsOpenGL.pushFramebuffer(); - PGraphicsOpenGL.setFramebuffer(tempFbo); + pg.pushFramebuffer(); + pg.setFramebuffer(tempFbo); // Clear the color buffer to make sure that the alpha channel is set to // full transparency pgl.clearColor(0, 0, 0, 0); @@ -1244,7 +1273,7 @@ public class Texture implements PConstants { // to cover the entire destination region. pgl.drawTexture(tex.glTarget, tex.glName, tex.glWidth, tex.glHeight, tempFbo.width, tempFbo.height, - x, y, w, h, 0, 0, width, height); + x, y, x + w, y + h, 0, 0, width, height); } else { // Rendering tex into "this" but without scaling so the contents @@ -1252,9 +1281,9 @@ public class Texture implements PConstants { // destination. pgl.drawTexture(tex.glTarget, tex.glName, tex.glWidth, tex.glHeight, tempFbo.width, tempFbo.height, - x, y, w, h, x, y, w, h); + x, y, x + w, y + h, x, y, x + w, y + h); } - PGraphicsOpenGL.popFramebuffer(); + pg.popFramebuffer(); updateTexels(x, y, w, h); } @@ -1265,7 +1294,7 @@ public class Texture implements PConstants { int texWidth, int texHeight, int x, int y, int w, int h, boolean scale) { if (tempFbo == null) { - tempFbo = new FrameBuffer(glWidth, glHeight); + tempFbo = new FrameBuffer(pg, glWidth, glHeight); } // This texture is the color (destination) buffer of the FBO. @@ -1273,8 +1302,8 @@ public class Texture implements PConstants { tempFbo.disableDepthTest(); // FBO copy: - PGraphicsOpenGL.pushFramebuffer(); - PGraphicsOpenGL.setFramebuffer(tempFbo); + pg.pushFramebuffer(); + pg.setFramebuffer(tempFbo); if (scale) { // Rendering tex into "this", and scaling the source rectangle // to cover the entire destination region. @@ -1290,7 +1319,7 @@ public class Texture implements PConstants { texWidth, texHeight, tempFbo.width, tempFbo.height, x, y, w, h, x, y, w, h); } - PGraphicsOpenGL.popFramebuffer(); + pg.popFramebuffer(); updateTexels(x, y, w, h); } @@ -1322,6 +1351,26 @@ public class Texture implements PConstants { } + // Releases the memory used by pixelBuffer either if the buffer hasn't been + // used many times yet, or if the JVM is running low in free memory. + protected void releasePixelBuffer() { + double freeMB = Runtime.getRuntime().freeMemory() / 1E6; + if (pixBufUpdateCount < MAX_UPDATES || freeMB < MIN_MEMORY) { + pixelBuffer = null; + } + } + + + // Releases the memory used by rgbaPixels either if the array hasn't been + // used many times yet, or if the JVM is running low in free memory. + protected void releaseRGBAPixels() { + double freeMB = Runtime.getRuntime().freeMemory() / 1E6; + if (rgbaPixUpdateCount < MAX_UPDATES || freeMB < MIN_MEMORY) { + rgbaPixels = null; + } + } + + /////////////////////////////////////////////////////////// // Parameter handling @@ -1404,21 +1453,27 @@ public class Texture implements PConstants { throw new RuntimeException("Unknown texture format"); } + boolean mipmaps = params.mipmaps && PGL.MIPMAPS_ENABLED; + if (mipmaps && !PGraphicsOpenGL.autoMipmapGenSupported) { + PGraphics.showWarning("Mipmaps were requested but automatic mipmap " + + "generation is not supported and manual " + + "generation still not implemented, so mipmaps " + + "will be disabled."); + mipmaps = false; + } + if (params.sampling == POINT) { glMagFilter = PGL.NEAREST; glMinFilter = PGL.NEAREST; } else if (params.sampling == LINEAR) { glMagFilter = PGL.NEAREST; - glMinFilter = params.mipmaps && PGL.MIPMAPS_ENABLED ? - PGL.LINEAR_MIPMAP_NEAREST : PGL.LINEAR; + glMinFilter = mipmaps ? PGL.LINEAR_MIPMAP_NEAREST : PGL.LINEAR; } else if (params.sampling == BILINEAR) { glMagFilter = PGL.LINEAR; - glMinFilter = params.mipmaps && PGL.MIPMAPS_ENABLED ? - PGL.LINEAR_MIPMAP_NEAREST : PGL.LINEAR; + glMinFilter = mipmaps ? PGL.LINEAR_MIPMAP_NEAREST : PGL.LINEAR; } else if (params.sampling == TRILINEAR) { glMagFilter = PGL.LINEAR; - glMinFilter = params.mipmaps && PGL.MIPMAPS_ENABLED ? - PGL.LINEAR_MIPMAP_LINEAR : PGL.LINEAR; + glMinFilter = mipmaps ? PGL.LINEAR_MIPMAP_LINEAR : PGL.LINEAR; } else { throw new RuntimeException("Unknown texture filtering mode"); } diff --git a/core/src/processing/opengl/TextureVert.glsl b/core/src/processing/opengl/TextureVert.glsl index 5260321fb..87f101536 100644 --- a/core/src/processing/opengl/TextureVert.glsl +++ b/core/src/processing/opengl/TextureVert.glsl @@ -20,10 +20,10 @@ #define PROCESSING_TEXTURE_SHADER -uniform mat4 transform; +uniform mat4 transformMatrix; uniform mat4 texMatrix; -attribute vec4 vertex; +attribute vec4 position; attribute vec4 color; attribute vec2 texCoord; @@ -31,7 +31,7 @@ varying vec4 vertColor; varying vec4 vertTexCoord; void main() { - gl_Position = transform * vertex; + gl_Position = transformMatrix * position; vertColor = color; vertTexCoord = texMatrix * vec4(texCoord, 1.0, 1.0); diff --git a/core/todo.txt b/core/todo.txt index de76d6a2e..ea362845a 100644 --- a/core/todo.txt +++ b/core/todo.txt @@ -1,64 +1,70 @@ -0221 core -X fix options parsing on loadTable() to handle spaces -X add static versions of loadJSONObject and loadJSONArray that take File inputs +0228 core -andres -X blendMode() change causes OpenGL renderer to be very slow -X https://github.com/processing/processing/issues/2021 -X serious OpenGL performance issues on OS X (fixed earlier?) -X https://github.com/processing/processing/issues/1714 -X fixed with a recent JOGL update -X P2D low quality text rendering -X https://github.com/processing/processing/issues/1972 -X Rendering artifacts on the diagonal line (topleft to bottomright) in P2D -X https://github.com/processing/processing/issues/1964 -X Fix issues with slow text rendering and OpenGL -X https://github.com/processing/processing/issues/2025 -X loadShape doesn't load OBJ files in subdirectories properly -X https://github.com/processing/processing/issues/2003 -X more OpenGL issues fixed by JOGL or newer drivers -X https://github.com/processing/processing/issues/1986 -X Vertical offset when sketch height is indivisible by 2 -X https://github.com/processing/processing/issues/1985 -X ellipse() causes RuntimeException: java.lang.OutOfMemoryError -X https://github.com/processing/processing/issues/1941 -X beginShape()..endShape() lines look wrong at joins/caps with P2D -X https://github.com/processing/processing/issues/1927 -X Corrupted text with large font and OpenGL -X https://github.com/processing/processing/issues/1869 -X loadFont hangs on Processing 2.0 with any OpenGL renderer -X https://github.com/processing/processing/issues/1854 - -video -X problem with bit shifting -X https://github.com/processing/processing/pull/2023 -X https://github.com/processing/processing/pull/2022 -X https://github.com/processing/processing/issues/2021 - - -critical -_ loadPixels doesn't set alpha value for pixels on Java2D -_ https://github.com/processing/processing/issues/2030 high +_ pull for image resize and alpha issues +_ https://github.com/processing/processing/pull/2324 +_ dataPath() not working when app is not run from app dir on Linux +_ https://github.com/processing/processing/issues/2195 +_ "Buffers have not been created" error for sketches w/o draw() +_ https://github.com/processing/processing/issues/2469 +_ could not reproduce +_ Sketch runs with default size if size() is followed by large memory allocation +_ some sort of threading issue happening here +_ https://github.com/processing/processing/issues/1672 +_ https://github.com/processing/processing/issues/2039 (dupe) +_ https://github.com/processing/processing/issues/2294 (dupe) +_ point() rendering differently in 2.0.3 and 2.1 +_ https://github.com/processing/processing/issues/2278 +_ internally, we probably have to call set() if it's a 1 pixel point +_ but that's going to be a mess.. need to first check the CTM +_ tint() not working in PDF (regression between 2.0.3 and 2.1) +_ https://github.com/processing/processing/issues/2428 +_ default font fixes +_ https://github.com/processing/processing/issues/2331 +_ https://github.com/processing/processing/pull/2338 +_ add print() method to other data types (not just IntList) +_ Sort out blending differences with P2D/P3D +_ might be that compatible images not setting alpha mode correctly +_ image = gc.createCompatibleVolatileImage(source.width, source.height, Transparency.TRANSLUCENT); +_ https://github.com/processing/processing/issues/1844 +_ 'collector' class.. Dict that points to a list +_ String as a key, int/float/string list as values _ blendMode(ADD) is broken with default renderer _ https://github.com/processing/processing/issues/2012 +_ may have been introduced between 2.0b7 and 2.0b8 +_ https://github.com/processing/processing/issues/2275 +_ https://github.com/processing/processing/issues/2276 +_ https://github.com/processing/processing/issues/2483 _ add option to have full screen span across screens _ display=all in cmd line _ sketchDisplay() -> 0 for all, or 1, 2, 3... -_ test PGraphicsRetina2D w/ 7u40 -_ make sure that 7u40 doesn't reintroduce starvation issue on retina Macs -_ draw() called again before finishing on OS X (retina issue) -_ https://github.com/processing/processing/issues/1709 -_ screen stops updating sometimes with retina -_ https://github.com/processing/processing/issues/1699 -_ Linux sometimes not responding correctly w/ the size() command -_ https://github.com/processing/processing/issues/1672 _ clean up requestFocus() stuff _ make sure it works with retina/canvas/strategy as well _ finish PFont.getShape() implementation _ needs to have a way to set width/height properly _ draw(s) doesn't work on the returned PShape +_ TGA files writing strangely +_ https://github.com/processing/processing/issues/2096 + +hidpi +_ saveFrame() with retina render is making black images +_ zero alpha values still a problem with retina renderer +_ https://github.com/processing/processing/issues/2030 +_ how to name the retina pixel stuff +_ hint(ENABLE_RETINA_PIXELS) or hint(ENABLE_HIDPI_PIXELS) +_ hint(ENABLE_2X_PIXELS)? +_ hidpi is Apple's name as well +_ no high-res display support for OpenGL +_ no high-dpi support for core on Windows +_ https://github.com/processing/processing/issues/2411 +_ retina sketches slow to start +_ https://github.com/processing/processing/issues/2357 + +cantfix +_ crash on startup when "Mirror Displays" selected +_ suspect that this is a specific chipset since Oracle didn't reproduce +_ https://github.com/processing/processing/issues/2186 table _ addRow() is not efficient, probably need to do the doubling @@ -72,6 +78,8 @@ _ save the constructor for the version that actually copies data _ the table pointer version will be speedy and allow chaining decisions/misc +_ make join() work with Iterable? +_ will this collide with the current String[] version? _ add options for image.save() (or saveImage?) _ add quality=[0,1] for jpeg images _ add dpi=[0,n] for for png images @@ -80,7 +88,7 @@ _ possible addition for 'implementation' variable X http://code.google.com/p/processing/issues/detail?id=281 _ https://github.com/processing/processing/issues/320 _ should map() actually constrain to the low and high values? -_ or have an alternate version that does that? (boolean param at end?) +_ or have an alternate version that does that? (or boolean param at end?) _ decide whether to keep: _ public float textWidth(char[] chars, int start, int length) _ add version of math functions that use doubles? @@ -109,14 +117,6 @@ _ nasty errors when loadImage/Font/createFont/etc used outside _ decision: add error messages where possible _ idea: set frameCount to -1 when setup not run yet? _ then set frameCount to 0 when setup() starts? -_ how much of com.benfry.* should go in? -_ Table? StringIntPairs? JSON? MD5? Integrator? ColorIntegrator? -_ decision: depends on if we can think of a good name -_ check on DXFWriter, since it used to subclass P3D -_ at least implement is3D? -_ sleep time needs to be set *much* higher for dormant applets -_ 10s should be fine--no need to keep spinning (bad for android too) -_ just call interrupt() when it's time to get back to work _ need to clean up the hints in the reference/source _ sort out edge + 1 issue on stroke/fill for rectangles _ http://code.google.com/p/processing/issues/detail?id=509 @@ -155,14 +155,6 @@ _ online is there but deprecated _ doesn't test net connection to see if 'online' _ only tests whether running inside an applet viewer (not relevant) _ remove 'online' from the docs -_ createGraphics() with no renderer param to point to JAVA2D -_ docs: P2D and P3D are now OpenGL variations -_ shader support - make decisions, Andres email, etc -_ setAntiAlias() should instead just use parent.smooth -_ antialias -> smoothMode(), smoothQuality(), quality() -_ NEAREST, BILINEAR, BICUBIC, or 0, 2, 4? (need 8x too, so maybe numbers) -_ final decision on pg.setQuality(sketchQuality()) -_ should probably be setQuality(parent.sketchQuality()) _ add reference/docs for urlEncode() and urlDecode() _ verify (and document) public access members of PApplet _ http://code.google.com/p/processing/issues/detail?id=83 @@ -178,6 +170,9 @@ _ OpenGL offscreen requires primary surface to be OpenGL _ explain the new PGL interface _ can't really change the smoothing/options on offscreen _ is this still true? +_ decide how disconnectEvent should actually be handled (and name?) +_ was disconnect always there? +_ will need documentation @@ -352,6 +347,8 @@ _ http://code.google.com/p/processing/issues/detail?id=182 CORE / PShape +_ major refactoring for PShape/PShapeOpenGL +_ https://github.com/processing/processing/issues/1623 _ PShape should be cached as well _ major surgery to put loadShape() back into PApplet/PGraphics _ then have the OpenGL or Java2D versions as cached objects @@ -479,6 +476,8 @@ _ Updating graphics drivers may prevent the problem _ ellipse scaling method isn't great _ http://code.google.com/p/processing/issues/detail?id=87 _ improve hint(ENABLE_DEPTH_SORT) to use proper painter's algo +_ https://github.com/processing/processing/issues/90 +_ for begin/endRaw: https://github.com/processing/processing/issues/2235 _ http://code.google.com/p/processing/issues/detail?id=51 _ polygon z-order depth sorting with alpha in opengl _ complete the implementation of hint() with proper implementation @@ -530,8 +529,6 @@ _ touchEvent(), gestureEvent()? LIBRARIES / PDF -_ pdf not rendering unicode with beginRecord() -_ http://code.google.com/p/processing/issues/detail?id=90 _ beginRecord() doesn't use current settings of the sketch _ sometimes reported as a bug, but probably not a good way to _ consistently carry these over diff --git a/done.txt b/done.txt index 51ff8262d..225f3aa3a 100644 --- a/done.txt +++ b/done.txt @@ -1,3 +1,440 @@ +0227 pde (2.2.1) +X use mouseReleased() instead of mousePressed() in color selector +X otherwise it registers the release as a click in the color window +X https://github.com/processing/processing/issues/2514 +X missing 'version' in contrib causes NPE +X https://github.com/processing/processing/issues/2517 +X bring back setIcon(Frame) for PDE X and others +X https://github.com/processing/processing-experimental/issues/64 +X how was PDE X able to crash 2.2? +X add additional code to rework how this is handled +X Auto Format patch mess +X https://github.com/processing/processing/pull/2271 +X why is the JDK path showing up as a ._ feller in OS X? +X https://github.com/processing/processing/issues/2520 +X "Archive Sketch" Tool doesn't force a .zip file extension +X https://github.com/processing/processing/issues/2526 + +python +J modifications for captureEvent and Python +J https://github.com/processing/processing/pull/2527 +J Permit implementing movieEvent without having to link to Movie at build time +J https://github.com/processing/processing/pull/2528 +J implement serial events without having to link to Serial at build time +J https://github.com/processing/processing/pull/2529 + + +0226 pde (2.2) +X sketches only starting once, or half-starting and hanging +X https://github.com/processing/processing/issues/2402 +X https://github.com/processing/processing/pull/2455 +X reopen current sketch in new mode editor if file extension is compatible +X https://github.com/processing/processing/pull/2457 +X https://github.com/processing/processing/issues/2456 +X crash in the 'recent' menu on startup +X https://github.com/processing/processing/issues/2463 +X sketchbook location is set to an actual sketch (huh?) +X sketch sometimes simply does not launch +X https://github.com/processing/processing/issues/2402 +X https://github.com/processing/processing/pull/2455 +X helpful fix contributed by David Fokkema +X remove the google code uploader +X JNA conflicts can be avoided with "-Djna.nosys=true" +X https://github.com/processing/processing/issues/2239 +X fix for Windows launchers +X fix for Windows export +X fix for Windows export 64-bit +X fix for Windows command line +X fix for Linux launcher +X fix for Linux export +X fix for Linux command line +X fix for OS X launcher +X fix for OS X export +X fix for OS X command line +X import static causes exception (with fix) +X https://github.com/processing/processing/issues/8 +o https://github.com/processing/processing/pull/2273 +X improve handling of tool loading +X QuickReference tool was able to bring down the environment +X https://github.com/processing/processing/issues/2229 +X save the previous open dialog so that we return to the directory +X https://github.com/processing/processing/pull/2366 +X "if-else" block formatting doesn't follow Processing conventions +X https://github.com/processing/processing/issues/364 +X https://github.com/processing/processing/pull/2477 +X tab characters not recognized/drawn in the editor (2.1) +X https://github.com/processing/processing/issues/2180 +X https://github.com/processing/processing/issues/2183 +o udp library has tabs in the text +X decision to be made on nextTabStop() inside TextAreaPainter +X Chinese text is overlapped in Processing 2.1 editor +X https://github.com/processing/processing/issues/2173 +X https://github.com/processing/processing/pull/2318 +X https://github.com/processing/processing/pull/2323 +o maybe user prefs should only cover things that've changed? +o how to balance colors/etc being stored elsewhere +o ton of work to maintain this... +X yeah, no +X remove video for macosx32 from the repo permanently +o fix for various net issues +o https://github.com/processing/processing/pull/2475 +X incorporates other unrelated code, had to close + +earlier (2.1.2) +X added get/set methods for status lines (Manindra) +X https://github.com/processing/processing/issues/2430 +X https://github.com/processing/processing/pull/2433 +X allow non-pde file extensions (JDF) +X https://github.com/processing/processing/issues/2420 + +export +X exported apps on Windows 64 not working? +X https://github.com/processing/processing/issues/2468 +X just needed to add the local path for Java +X when exporting with local Java embedded, use that version +X https://github.com/processing/processing/issues/2349 +X (we can do this now since we're actually doing the embedding) +o export application folder location (for Manindra) +X https://github.com/processing/processing/issues/2399 +X incorporate new launch4j 3.4 +X http://sourceforge.net/projects/launch4j/files/launch4j-3/3.4/ +X change Windows export to use launch4j instead of the launcher.cpp file +X actually call ant from inside p5? +X re-implement an icon for exported applications on Windows +X make sure that Windows export uses the local Java +X double-checked with a clean XP install +X make sure Windows export includes library DLLs +X remove build/windows/export from repo +o make sure launch4j export isn't printing to console unless trouble +X OS X is doing this, though Windows is pushing some stuff through +X bring back multi-platform export? +X embed Java only works for the current platform +o OS X applications can only be exported from OS X +X actually it's just the embedding, which is a problem on any platform +X add all sorts of language to the export dialog +X make available the background colors for present mode, stop button color +X isolate color chooser into a simpler/smaller class outside tools +X then can also use from inside processing applications as well +X http://code.google.com/p/processing/issues/detail?id=30 +X https://github.com/processing/processing/issues/69 +X exported apps reporting as "damaged" on OS X +X https://github.com/processing/processing/issues/2095 +X implement a call to codesign, and a message box re: installing Xcode +X use launch4j for export and p5 app itself +X perhaps even calling it through an ant task +X windows exported exe problems (pcho) +o updated launch4j 3.1 beta +o http://sourceforge.net/projects/launch4j/files/launch4j-3/ +X exe instead of bat to make exported apps run in 64-bit +X http://code.google.com/p/processing/issues/detail?id=885 +X https://github.com/processing/processing/issues/923 + + +0225 pde (2.1.2) +X Fix exception caused by Runner when it can't find location +X https://github.com/processing/processing/issues/2346 +X https://github.com/processing/processing/pull/2359 +G Serial: Update to latest upstream (fixes potential port handle leak) +G https://github.com/processing/processing/pull/2361 +J add affordance for mode developers to run from Eclipse +J https://github.com/processing/processing/pull/2422 +J non-pde extensions for modes cause a crash +J https://github.com/processing/processing/issues/2419 +J some hardcoding for .pde still exists +J https://github.com/processing/processing/issues/2420 +X the PDE uses 15% of CPU while just sitting idle (thx to David Fokkema) +X https://github.com/processing/processing/issues/1561 +X https://github.com/processing/processing/pull/2451 +X Update code signing for Processing.app for Mavericks changes +X https://github.com/processing/processing/issues/2453 +o use --deep for codesign to work? (nope) +o http://furbo.org/2013/10/17/code-signing-and-mavericks/ +o http://brockerhoff.net/RB/AppCheckerLite/ +J permit modes to specify alternate extension (.py for .pyde stuff) +J https://github.com/processing/processing/pull/2452 +X sketchPath() returns user.home in exported apps on OSX +X https://github.com/processing/processing/issues/2181 + + +0224 pde (2.1.1) +M fix infinite loop in Find/Replace +M https://github.com/processing/processing/issues/2082 +X updated to Minim 2.2 +X https://github.com/processing/processing/pull/2250 +X minor change to bracket handling +X https://github.com/processing/processing/pull/2313 +X app is called procesing.app.Base +X https://github.com/processing/processing/issues/2217 +X noJavaArg set to --export +X https://github.com/processing/processing/issues/2182 +X export not working on Windows +X https://github.com/processing/processing/issues/2219 +X right-click on selection is a problem on Windows +X https://github.com/processing/processing/issues/2210 +G Add Contents/Java to java.library.path for loadLibrary to find .jnilib files +X jnilib not loading on OS X because Contents/Java needs to be added to path +X https://github.com/processing/processing/pull/2269 + +serial +G readStringUntil() missing from new serial library +G https://github.com/processing/processing/issues/2174 +G updates to serial library +G https://github.com/processing/processing/pull/2265 +G serial running slowly +G https://github.com/processing/processing/issues/2249 +G https://github.com/processing/processing/issues/2214 +G Only read a single character at a time to emulate RXTX behavior +G https://github.com/processing/processing/pull/2240 +G Add basic tests for throughput and latency +G https://github.com/processing/processing/pull/2225 +G Add a debug() method to Serial +G https://github.com/processing/processing/pull/2237 +G Switch the examples over to printArray() +G https://github.com/processing/processing/pull/2226 +G Handle the UnsatisfiedLinkError when loading the native library fails +G https://github.com/processing/processing/pull/2266 + +fixed in 2.1 +X init() not called on tools until later +X https://github.com/processing/processing/issues/1859 +X Finish changes so the PDE can use an unmodified JRE +X https://github.com/processing/processing/issues/1840 + + +0223 pde (2.1) +X reset font smoothing for everyone to its default by changing the pref +X To reset everyone's default, replaced editor.antialias with editor.smooth +X for 2.1. Fonts are unusably gross on OS X (and Linux) w/o smoothing and +X the Oracle JVM, and many longtime users have anti-aliasing turned off. +X https://github.com/processing/processing/issues/2164 +X https://github.com/processing/processing/issues/2160 +X Add option to not embed the Java runtime (saves space, but breakage) +X return code needs to be 1 instead of 0 w/ Commander +X https://github.com/processing/processing/issues/1798#issuecomment-26711847 +X additional font tweaks due to decreased legibility with Oracle Java +X type looks a little feeble on OS X with non-retina machines +X https://github.com/processing/processing/issues/2135 +X should we increase the size of the mode dropdown? +X processing-java broken in 2.1 beta 1 on OS X +X https://github.com/processing/processing/issues/2159 +X need to use the embedded Java, different classpath, etc +X also might be time to put something in to check the version + + +0222 pde (2.1b1) +X MovieMaker needs to be compiling as 1.6 +X deal with null/missing folders for Tools and Modes +X https://github.com/processing/processing/issues/2068 +o bad JS mode causing crash on startup +X https://github.com/processing/processing/issues/2088 +X looks like issue that was covered in 2.0.3 changes +X non-compliant libraries cause crash on "Add Library" +X https://github.com/processing/processing/issues/2026 +X Open new PDE maximized when current PDE is maximized +X https://github.com/processing/processing/issues/1984 +X https://github.com/processing/processing/pull/2037 +X incorporate the new serial library +X https://github.com/processing/processing/pull/2093 +X cmd-left is bringing up the text area popup +X https://github.com/processing/processing/issues/2103 +X still having right-click issues (re-opened) +X https://github.com/processing/processing/issues/2103 +X bad tool brings down the environment +X http://code.google.com/p/processing/issues/detail?id=798 +X https://github.com/processing/processing/issues/836 + +cleaning +o the first time someone hides a tab, put up a msg explaining what it does +o "don't warn me about this anymore" +X removed this feature a while back +o document the move of the auto format menu +o in the book(s)? in the reference? +o jer: opengl2 tutorial +o jer: android tutorial +o probably later: examples into categories based on difficulty +o add ratings/difficult level to examples online and in the pde +o go through examples, figure out how to do many on the site w/ js instead +X import p5 reference into the javadoc +o freeze after splash screen on OS X (looks like core.jar in the path) +X https://github.com/processing/processing/issues/1872 +X can't really fix this + +fonts/prefs +X update with bold version of Source Code Pro +X http://www.google.com/fonts#UsePlace:use/Collection:Source+Code+Pro +X instead of semibold, which wouldn't correctly connect to the other fonts +X does editor line status work? +X not sure what this one was, but added anti-aliasing to the status +X Editor.applyPreferences() -> painter.setFont() removed +X need to instead update defaults, then run from there +X then call repaint() on the text area? or invalidate()? or the painter? +X make sure font family change is working +X make sure fonts can actually update size/etc in prefs +X slightly gray background +X bgcolor wasn't getting set (since fgcolor was set elsewhere) +X spacing problem with large sizes (on retina?) +X not just retina, was problem with non-mono text from Java +X control text size in console +o why aren't prefs from theme.txt showing up in preferences.txt? hrm +o or rather, why can't they be overridden? +X because theme.txt data is a different animal / that's part of the point +X should fonts at least be in prefs.txt? +X http://code.google.com/p/processing/issues/detail?id=226 +X https://github.com/processing/processing/issues/265 +X console font in EditorConsole +X Font font = Preferences.getFont("console.font"); +o fix console font on Windows and Linux with 7u40 +X couldn't reproduce, but shouldn't be a problem with the rewrite +X the message area text also looks ugly.. can we fix? +X add pref to select PDE font (so that CJKV languages work better) +X https://github.com/processing/processing/issues/2078 +X should we embed the PDE font into the JRE? +X this would allow it to show up in the menus, etc +X type in the status area is gross on retina displays and 7u40 +X no longer require restart of Processing when changing the font + +serial +X closing several bugs because no longer relevant +X need 64-bit version of RXTX library +X http://code.google.com/p/processing/issues/detail?id=1237 +X https://github.com/processing/processing/issues/1275 +X serial still causing problems on OS X +X http://code.google.com/p/processing/issues/detail?id=52 +X had already been closed +X serial ports missing from list (OS X) +X http://code.google.com/p/processing/issues/detail?id=52 +X also was marked fixed... +X Serial.write problem with Bluetooth SPP virtual serial port +X http://code.google.com/p/processing/issues/detail?id=318 +X was marked duplicate of #52 +X Serial silently fails when invalid port entered as string +o https://github.com/processing/processing/issues/2114 +X Serial Issue for RPi (and others) +o https://github.com/processing/processing/issues/2066 +X RXTX-2.1-7 doesn't list ports created in /dev with VirtualSerialPortApp +o https://github.com/processing/processing/issues/1460 +X Bluetooth serial problems on Windows when connecting to Arduino device +o https://github.com/processing/processing/issues/1374 + +movie maker +o moviemaker video sizes / usability +o http://processing.org/discourse/yabb_beta/YaBB.cgi?board=Exhibition;action=display;num=1211318862 +X add gamma, better image options, etc to MovieMaker +X TGA files cause Movie Maker to not work properly +X https://github.com/processing/processing/issues/1933 +o move Movie Maker out to its own separate tool package (with separate build) +X http://code.google.com/p/processing/issues/detail?id=837 +X https://github.com/processing/processing/issues/875 +X basically done in more recent releases +X fix file selection dialog with MovieMaker +X copied from PApplet, but not importing PApplet + +build +X remove video library for other platforms in download +X update apple.jar file with new version +X https://developer.apple.com/legacy/library/samplecode/AppleJavaExtensions/Introduction/Intro.html +X remove Mangler from tools/Mangler +o update tools/howto.txt to just point at the correct online location +X just remove the howto file +X appbundler fixes/changes +X the "Classes" folder is included +X appears to be line 138 of main.m +o maybe this is a holdover from OS X Java? don't know. +X icon location uses path, even when embedded +X add indents to the Info.plist output file +X inside writeInfoPlist from AppBundlerTask.java +o use Contents/Resources/Java instead of Contents/Java? +o this is in main.m. why the change? +X doesn't make any difference, just use Contents/Java +X any missing args from our app (copyrights/versions?) +X add MinimumSystemVersion for 10.7.3 +X https://developer.apple.com/library/ios/documentation/general/Reference/InfoPlistKeyReference/Articles/LaunchServicesKeys.html#//apple_ref/doc/uid/20001431-113253 +X copy GenericDocumentIcon.icns for placeholder icon +X from /System/Library/CoreServices/CoreTypes.bundle/Contents/Resources/ +X the javadoc includes java.io.* and java.lang.* prefixes.. why? +X re-run and and check in +X upload javadoc updates +X need to require JDK 7u40 to be installed on OS X +X remove JAVA_HOME requirement from build.xml +X what's needed on OS X? just the JDK 7u40? +X remove Java FX from OS X build +X remove Java FX during Linux builds +X remove Java FX during Windows builds +X remove javafx from the embed +X more about optional files: +X http://www.oracle.com/technetwork/java/javase/jdk-7-readme-429198.html + +build instructions and other doc +X update the build instructions page +X http://code.google.com/p/processing/wiki/BuildInstructions +X update github instructions +X https://github.com/processing/processing/issues/1629 +X https://github.com/processing/processing/wiki/Build-Instructions +X only JRE needed at this point +X switched over to Java 7 +o "unable to locate tools.jar" (Windows) can be ignored +X JAVA_HOME warnings from Ant can also be ignored +X updates for macosx instructions +X JDK 7u45 is needed (or whatever version currently in the build) +X to build appbundler, you'll need Xcode +X and the command line tools Preferences > Downloads > Command Line Tools +X appbundler will have an NPE if the osx binary isn't built +X also need to have 10.8 version of the SDK (old Xcode won't work) +o add notes to build instructions re: building core with eclipse +X changing JRE might be a problem for fonts on Linux +X where the JRE is often replaced +X and where the font is needed most +X make note of this on the platforms page +X also make note re: only JRE needed (instead of JDK) +X http://wiki.processing.org/index.php?title=Supported_Platforms&action=edit§ion=4 +X now Info.plist.tmpl instead of template.plist +X can be embedded in a sketch +X name change due to major modifications + +7u40 macosx +X make OS X launch from its local JRE +X also need to have a central menubar +X add the offscreen window hack +X otherwise mode change causes "do you want to quit?" on OS X +X remove 32- and 64-bit preference from Preferences on OS X +o try the bundle on Mac Mini running 10.6 +X we become full 64-bit on OS X +X meaning that the macosx32 video library goes away +X and the preference for launching in 32- or 64-bit mode +X package Java 7u40 version of the app +X docs.oracle.com/javase/7/docs/technotes/guides/jweb/packagingAppsForMac.html +X http://www.intransitione.com/blog/take-java-to-app-store/ +X retina support http://bugs.sun.com/view_bug.do?bug_id=8009754 +X useful retina digging/findings for Oracle Java +X http://bulenkov.com/2013/06/23/retina-support-in-oracle-jdk-1-7/ +X 7u40 target release is "late August" +X http://openjdk.java.net/projects/jdk7u/releases/7u40.html +X Contents/Java/Classes folder is added to java.class.path +X so the folder must exist otherwise the ECJ compiler will crash +X once fixed, remove notes from JavaBuild.java +X "Are you sure you want to quit?" when switching modes on Oracle JVM +X default menu bar is still broken +X http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=8007267 +X add appbundler.jar, otherwise folks have to include Xcode + +export +X change app stub in OS X exported application +X make note re: app export being slower, and resulting app much larger +X change how export is handled +X remove ability to export cross-platform apps +X add ability to embed the current JRE +X only going to embed always... consider option to disable it later +o the case for the embedded JRE +o https://github.com/processing/processing/issues/2104 +X major edits to http://wiki.processing.org/w/Export_Info_and_Tips + + +0221 pde (2.0.3) +X add double quotes to readlink call, fixes issue w/ paths and spaces +X https://github.com/processing/processing/pull/2027 +X fix submitted by hamoid + + 0220 pde (2.0.2) X fix "less less" typo X https://github.com/processing/processing/issues/1928 diff --git a/experimental/.classpath b/experimental/.classpath deleted file mode 100644 index 5bb5a9eb3..000000000 --- a/experimental/.classpath +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - diff --git a/experimental/bin/processing/mode/experimental/ArrayFieldNode.class b/experimental/bin/processing/mode/experimental/ArrayFieldNode.class new file mode 100644 index 000000000..d810c99f6 Binary files /dev/null and b/experimental/bin/processing/mode/experimental/ArrayFieldNode.class differ diff --git a/experimental/bin/processing/mode/experimental/ClassLoadListener.class b/experimental/bin/processing/mode/experimental/ClassLoadListener.class new file mode 100644 index 000000000..7bc9c8c57 Binary files /dev/null and b/experimental/bin/processing/mode/experimental/ClassLoadListener.class differ diff --git a/experimental/bin/processing/mode/experimental/Compiler$1.class b/experimental/bin/processing/mode/experimental/Compiler$1.class new file mode 100644 index 000000000..9d42778a3 Binary files /dev/null and b/experimental/bin/processing/mode/experimental/Compiler$1.class differ diff --git a/experimental/bin/processing/mode/experimental/Compiler.class b/experimental/bin/processing/mode/experimental/Compiler.class new file mode 100644 index 000000000..a14e4885a Binary files /dev/null and b/experimental/bin/processing/mode/experimental/Compiler.class differ diff --git a/experimental/bin/processing/mode/experimental/DebugBuild.class b/experimental/bin/processing/mode/experimental/DebugBuild.class new file mode 100644 index 000000000..4a663d33e Binary files /dev/null and b/experimental/bin/processing/mode/experimental/DebugBuild.class differ diff --git a/experimental/bin/processing/mode/experimental/DebugEditor$1.class b/experimental/bin/processing/mode/experimental/DebugEditor$1.class new file mode 100644 index 000000000..bd9e889f2 Binary files /dev/null and b/experimental/bin/processing/mode/experimental/DebugEditor$1.class differ diff --git a/experimental/bin/processing/mode/experimental/DebugEditor$2.class b/experimental/bin/processing/mode/experimental/DebugEditor$2.class new file mode 100644 index 000000000..a5ed608e0 Binary files /dev/null and b/experimental/bin/processing/mode/experimental/DebugEditor$2.class differ diff --git a/experimental/bin/processing/mode/experimental/DebugEditor$3.class b/experimental/bin/processing/mode/experimental/DebugEditor$3.class new file mode 100644 index 000000000..bdb47ccb8 Binary files /dev/null and b/experimental/bin/processing/mode/experimental/DebugEditor$3.class differ diff --git a/experimental/bin/processing/mode/experimental/DebugEditor$4.class b/experimental/bin/processing/mode/experimental/DebugEditor$4.class new file mode 100644 index 000000000..5601253fb Binary files /dev/null and b/experimental/bin/processing/mode/experimental/DebugEditor$4.class differ diff --git a/experimental/bin/processing/mode/experimental/DebugEditor$5.class b/experimental/bin/processing/mode/experimental/DebugEditor$5.class new file mode 100644 index 000000000..5d4c1998b Binary files /dev/null and b/experimental/bin/processing/mode/experimental/DebugEditor$5.class differ diff --git a/experimental/bin/processing/mode/experimental/DebugEditor$6.class b/experimental/bin/processing/mode/experimental/DebugEditor$6.class new file mode 100644 index 000000000..1020dc4d7 Binary files /dev/null and b/experimental/bin/processing/mode/experimental/DebugEditor$6.class differ diff --git a/experimental/bin/processing/mode/experimental/DebugEditor$7.class b/experimental/bin/processing/mode/experimental/DebugEditor$7.class new file mode 100644 index 000000000..f09aa01f5 Binary files /dev/null and b/experimental/bin/processing/mode/experimental/DebugEditor$7.class differ diff --git a/experimental/bin/processing/mode/experimental/DebugEditor.class b/experimental/bin/processing/mode/experimental/DebugEditor.class new file mode 100644 index 000000000..aa284462e Binary files /dev/null and b/experimental/bin/processing/mode/experimental/DebugEditor.class differ diff --git a/experimental/bin/processing/mode/experimental/DebugRunner.class b/experimental/bin/processing/mode/experimental/DebugRunner.class new file mode 100644 index 000000000..44b0e4c33 Binary files /dev/null and b/experimental/bin/processing/mode/experimental/DebugRunner.class differ diff --git a/experimental/bin/processing/mode/experimental/DebugToolbar.class b/experimental/bin/processing/mode/experimental/DebugToolbar.class new file mode 100644 index 000000000..0e43e41cc Binary files /dev/null and b/experimental/bin/processing/mode/experimental/DebugToolbar.class differ diff --git a/experimental/bin/processing/mode/experimental/Debugger$1.class b/experimental/bin/processing/mode/experimental/Debugger$1.class new file mode 100644 index 000000000..9fb972059 Binary files /dev/null and b/experimental/bin/processing/mode/experimental/Debugger$1.class differ diff --git a/experimental/bin/processing/mode/experimental/Debugger$2.class b/experimental/bin/processing/mode/experimental/Debugger$2.class new file mode 100644 index 000000000..11bb9aca4 Binary files /dev/null and b/experimental/bin/processing/mode/experimental/Debugger$2.class differ diff --git a/experimental/bin/processing/mode/experimental/Debugger$3.class b/experimental/bin/processing/mode/experimental/Debugger$3.class new file mode 100644 index 000000000..f46084b84 Binary files /dev/null and b/experimental/bin/processing/mode/experimental/Debugger$3.class differ diff --git a/experimental/bin/processing/mode/experimental/Debugger.class b/experimental/bin/processing/mode/experimental/Debugger.class new file mode 100644 index 000000000..37f813aa1 Binary files /dev/null and b/experimental/bin/processing/mode/experimental/Debugger.class differ diff --git a/experimental/bin/processing/mode/experimental/ErrorBar$1.class b/experimental/bin/processing/mode/experimental/ErrorBar$1.class new file mode 100644 index 000000000..fc02ee6e8 Binary files /dev/null and b/experimental/bin/processing/mode/experimental/ErrorBar$1.class differ diff --git a/experimental/bin/processing/mode/experimental/ErrorBar$2$1.class b/experimental/bin/processing/mode/experimental/ErrorBar$2$1.class new file mode 100644 index 000000000..2e9331c51 Binary files /dev/null and b/experimental/bin/processing/mode/experimental/ErrorBar$2$1.class differ diff --git a/experimental/bin/processing/mode/experimental/ErrorBar$2.class b/experimental/bin/processing/mode/experimental/ErrorBar$2.class new file mode 100644 index 000000000..33292428e Binary files /dev/null and b/experimental/bin/processing/mode/experimental/ErrorBar$2.class differ diff --git a/experimental/bin/processing/mode/experimental/ErrorBar$3$1.class b/experimental/bin/processing/mode/experimental/ErrorBar$3$1.class new file mode 100644 index 000000000..59f997ae5 Binary files /dev/null and b/experimental/bin/processing/mode/experimental/ErrorBar$3$1.class differ diff --git a/experimental/bin/processing/mode/experimental/ErrorBar$3.class b/experimental/bin/processing/mode/experimental/ErrorBar$3.class new file mode 100644 index 000000000..79949bd9a Binary files /dev/null and b/experimental/bin/processing/mode/experimental/ErrorBar$3.class differ diff --git a/experimental/bin/processing/mode/experimental/ErrorBar.class b/experimental/bin/processing/mode/experimental/ErrorBar.class new file mode 100644 index 000000000..1cb4fa38a Binary files /dev/null and b/experimental/bin/processing/mode/experimental/ErrorBar.class differ diff --git a/experimental/bin/processing/mode/experimental/ErrorCheckerService$1.class b/experimental/bin/processing/mode/experimental/ErrorCheckerService$1.class new file mode 100644 index 000000000..8aa36c468 Binary files /dev/null and b/experimental/bin/processing/mode/experimental/ErrorCheckerService$1.class differ diff --git a/experimental/bin/processing/mode/experimental/ErrorCheckerService$2.class b/experimental/bin/processing/mode/experimental/ErrorCheckerService$2.class new file mode 100644 index 000000000..0fb2f0358 Binary files /dev/null and b/experimental/bin/processing/mode/experimental/ErrorCheckerService$2.class differ diff --git a/experimental/bin/processing/mode/experimental/ErrorCheckerService.class b/experimental/bin/processing/mode/experimental/ErrorCheckerService.class new file mode 100644 index 000000000..3f6d985ca Binary files /dev/null and b/experimental/bin/processing/mode/experimental/ErrorCheckerService.class differ diff --git a/experimental/bin/processing/mode/experimental/ErrorMarker.class b/experimental/bin/processing/mode/experimental/ErrorMarker.class new file mode 100644 index 000000000..03082b261 Binary files /dev/null and b/experimental/bin/processing/mode/experimental/ErrorMarker.class differ diff --git a/experimental/bin/processing/mode/experimental/ErrorWindow$1.class b/experimental/bin/processing/mode/experimental/ErrorWindow$1.class new file mode 100644 index 000000000..960e8e87f Binary files /dev/null and b/experimental/bin/processing/mode/experimental/ErrorWindow$1.class differ diff --git a/experimental/bin/processing/mode/experimental/ErrorWindow$2.class b/experimental/bin/processing/mode/experimental/ErrorWindow$2.class new file mode 100644 index 000000000..debae2269 Binary files /dev/null and b/experimental/bin/processing/mode/experimental/ErrorWindow$2.class differ diff --git a/experimental/bin/processing/mode/experimental/ErrorWindow$3.class b/experimental/bin/processing/mode/experimental/ErrorWindow$3.class new file mode 100644 index 000000000..e1e0263b4 Binary files /dev/null and b/experimental/bin/processing/mode/experimental/ErrorWindow$3.class differ diff --git a/experimental/bin/processing/mode/experimental/ErrorWindow$4.class b/experimental/bin/processing/mode/experimental/ErrorWindow$4.class new file mode 100644 index 000000000..01edf192a Binary files /dev/null and b/experimental/bin/processing/mode/experimental/ErrorWindow$4.class differ diff --git a/experimental/bin/processing/mode/experimental/ErrorWindow$DockTool2Base.class b/experimental/bin/processing/mode/experimental/ErrorWindow$DockTool2Base.class new file mode 100644 index 000000000..209f57f8a Binary files /dev/null and b/experimental/bin/processing/mode/experimental/ErrorWindow$DockTool2Base.class differ diff --git a/experimental/bin/processing/mode/experimental/ErrorWindow.class b/experimental/bin/processing/mode/experimental/ErrorWindow.class new file mode 100644 index 000000000..7fdede082 Binary files /dev/null and b/experimental/bin/processing/mode/experimental/ErrorWindow.class differ diff --git a/experimental/bin/processing/mode/experimental/ExperimentalMode.class b/experimental/bin/processing/mode/experimental/ExperimentalMode.class new file mode 100644 index 000000000..4b3b49906 Binary files /dev/null and b/experimental/bin/processing/mode/experimental/ExperimentalMode.class differ diff --git a/experimental/bin/processing/mode/experimental/FieldNode.class b/experimental/bin/processing/mode/experimental/FieldNode.class new file mode 100644 index 000000000..47df32561 Binary files /dev/null and b/experimental/bin/processing/mode/experimental/FieldNode.class differ diff --git a/experimental/bin/processing/mode/experimental/ImportStatement.class b/experimental/bin/processing/mode/experimental/ImportStatement.class new file mode 100644 index 000000000..b7cd604b5 Binary files /dev/null and b/experimental/bin/processing/mode/experimental/ImportStatement.class differ diff --git a/experimental/bin/processing/mode/experimental/LineBreakpoint.class b/experimental/bin/processing/mode/experimental/LineBreakpoint.class new file mode 100644 index 000000000..584e0731b Binary files /dev/null and b/experimental/bin/processing/mode/experimental/LineBreakpoint.class differ diff --git a/experimental/bin/processing/mode/experimental/LineHighlight.class b/experimental/bin/processing/mode/experimental/LineHighlight.class new file mode 100644 index 000000000..dc2af19f9 Binary files /dev/null and b/experimental/bin/processing/mode/experimental/LineHighlight.class differ diff --git a/experimental/bin/processing/mode/experimental/LineID.class b/experimental/bin/processing/mode/experimental/LineID.class new file mode 100644 index 000000000..2999a5254 Binary files /dev/null and b/experimental/bin/processing/mode/experimental/LineID.class differ diff --git a/experimental/bin/processing/mode/experimental/LineListener.class b/experimental/bin/processing/mode/experimental/LineListener.class new file mode 100644 index 000000000..b61b03aa6 Binary files /dev/null and b/experimental/bin/processing/mode/experimental/LineListener.class differ diff --git a/experimental/bin/processing/mode/experimental/LocalVariableNode.class b/experimental/bin/processing/mode/experimental/LocalVariableNode.class new file mode 100644 index 000000000..c1bca7c71 Binary files /dev/null and b/experimental/bin/processing/mode/experimental/LocalVariableNode.class differ diff --git a/experimental/bin/processing/mode/experimental/Problem.class b/experimental/bin/processing/mode/experimental/Problem.class new file mode 100644 index 000000000..647bebe1f Binary files /dev/null and b/experimental/bin/processing/mode/experimental/Problem.class differ diff --git a/experimental/bin/processing/mode/experimental/TextArea$MouseHandler.class b/experimental/bin/processing/mode/experimental/TextArea$MouseHandler.class new file mode 100644 index 000000000..1443acd29 Binary files /dev/null and b/experimental/bin/processing/mode/experimental/TextArea$MouseHandler.class differ diff --git a/experimental/bin/processing/mode/experimental/TextArea.class b/experimental/bin/processing/mode/experimental/TextArea.class new file mode 100644 index 000000000..35092f97c Binary files /dev/null and b/experimental/bin/processing/mode/experimental/TextArea.class differ diff --git a/experimental/bin/processing/mode/experimental/TextAreaPainter.class b/experimental/bin/processing/mode/experimental/TextAreaPainter.class new file mode 100644 index 000000000..e30560f62 Binary files /dev/null and b/experimental/bin/processing/mode/experimental/TextAreaPainter.class differ diff --git a/experimental/bin/processing/mode/experimental/VMEventListener.class b/experimental/bin/processing/mode/experimental/VMEventListener.class new file mode 100644 index 000000000..9c3388a89 Binary files /dev/null and b/experimental/bin/processing/mode/experimental/VMEventListener.class differ diff --git a/experimental/bin/processing/mode/experimental/VMEventReader.class b/experimental/bin/processing/mode/experimental/VMEventReader.class new file mode 100644 index 000000000..f82358344 Binary files /dev/null and b/experimental/bin/processing/mode/experimental/VMEventReader.class differ diff --git a/experimental/bin/processing/mode/experimental/VariableInspector$1.class b/experimental/bin/processing/mode/experimental/VariableInspector$1.class new file mode 100644 index 000000000..8a600c79d Binary files /dev/null and b/experimental/bin/processing/mode/experimental/VariableInspector$1.class differ diff --git a/experimental/bin/processing/mode/experimental/VariableInspector$ExpansionHandler.class b/experimental/bin/processing/mode/experimental/VariableInspector$ExpansionHandler.class new file mode 100644 index 000000000..fce996cc7 Binary files /dev/null and b/experimental/bin/processing/mode/experimental/VariableInspector$ExpansionHandler.class differ diff --git a/experimental/bin/processing/mode/experimental/VariableInspector$LocalHidesThisFilter.class b/experimental/bin/processing/mode/experimental/VariableInspector$LocalHidesThisFilter.class new file mode 100644 index 000000000..d05f3f2d3 Binary files /dev/null and b/experimental/bin/processing/mode/experimental/VariableInspector$LocalHidesThisFilter.class differ diff --git a/experimental/bin/processing/mode/experimental/VariableInspector$OutlineRenderer.class b/experimental/bin/processing/mode/experimental/VariableInspector$OutlineRenderer.class new file mode 100644 index 000000000..bcb9d6730 Binary files /dev/null and b/experimental/bin/processing/mode/experimental/VariableInspector$OutlineRenderer.class differ diff --git a/experimental/bin/processing/mode/experimental/VariableInspector$P5BuiltinsFilter.class b/experimental/bin/processing/mode/experimental/VariableInspector$P5BuiltinsFilter.class new file mode 100644 index 000000000..d33057608 Binary files /dev/null and b/experimental/bin/processing/mode/experimental/VariableInspector$P5BuiltinsFilter.class differ diff --git a/experimental/bin/processing/mode/experimental/VariableInspector$ThisFilter.class b/experimental/bin/processing/mode/experimental/VariableInspector$ThisFilter.class new file mode 100644 index 000000000..d3eca6f36 Binary files /dev/null and b/experimental/bin/processing/mode/experimental/VariableInspector$ThisFilter.class differ diff --git a/experimental/bin/processing/mode/experimental/VariableInspector$ValueCellEditor.class b/experimental/bin/processing/mode/experimental/VariableInspector$ValueCellEditor.class new file mode 100644 index 000000000..ea0c75006 Binary files /dev/null and b/experimental/bin/processing/mode/experimental/VariableInspector$ValueCellEditor.class differ diff --git a/experimental/bin/processing/mode/experimental/VariableInspector$ValueCellRenderer.class b/experimental/bin/processing/mode/experimental/VariableInspector$ValueCellRenderer.class new file mode 100644 index 000000000..066a623dd Binary files /dev/null and b/experimental/bin/processing/mode/experimental/VariableInspector$ValueCellRenderer.class differ diff --git a/experimental/bin/processing/mode/experimental/VariableInspector$VariableNodeFilter.class b/experimental/bin/processing/mode/experimental/VariableInspector$VariableNodeFilter.class new file mode 100644 index 000000000..8f4cc601b Binary files /dev/null and b/experimental/bin/processing/mode/experimental/VariableInspector$VariableNodeFilter.class differ diff --git a/experimental/bin/processing/mode/experimental/VariableInspector$VariableRowModel.class b/experimental/bin/processing/mode/experimental/VariableInspector$VariableRowModel.class new file mode 100644 index 000000000..e0fe9c45c Binary files /dev/null and b/experimental/bin/processing/mode/experimental/VariableInspector$VariableRowModel.class differ diff --git a/experimental/bin/processing/mode/experimental/VariableInspector.class b/experimental/bin/processing/mode/experimental/VariableInspector.class new file mode 100644 index 000000000..0d6855fbd Binary files /dev/null and b/experimental/bin/processing/mode/experimental/VariableInspector.class differ diff --git a/experimental/src/processing/mode/experimental/VariableInspector.form b/experimental/bin/processing/mode/experimental/VariableInspector.form similarity index 100% rename from experimental/src/processing/mode/experimental/VariableInspector.form rename to experimental/bin/processing/mode/experimental/VariableInspector.form diff --git a/experimental/bin/processing/mode/experimental/VariableNode.class b/experimental/bin/processing/mode/experimental/VariableNode.class new file mode 100644 index 000000000..4f9319a7f Binary files /dev/null and b/experimental/bin/processing/mode/experimental/VariableNode.class differ diff --git a/experimental/bin/processing/mode/experimental/XQConsoleToggle.class b/experimental/bin/processing/mode/experimental/XQConsoleToggle.class new file mode 100644 index 000000000..1cb46898d Binary files /dev/null and b/experimental/bin/processing/mode/experimental/XQConsoleToggle.class differ diff --git a/experimental/bin/processing/mode/experimental/XQErrorTable$1.class b/experimental/bin/processing/mode/experimental/XQErrorTable$1.class new file mode 100644 index 000000000..0293992a3 Binary files /dev/null and b/experimental/bin/processing/mode/experimental/XQErrorTable$1.class differ diff --git a/experimental/bin/processing/mode/experimental/XQErrorTable$2.class b/experimental/bin/processing/mode/experimental/XQErrorTable$2.class new file mode 100644 index 000000000..9801e5bbf Binary files /dev/null and b/experimental/bin/processing/mode/experimental/XQErrorTable$2.class differ diff --git a/experimental/bin/processing/mode/experimental/XQErrorTable$3.class b/experimental/bin/processing/mode/experimental/XQErrorTable$3.class new file mode 100644 index 000000000..72b64419d Binary files /dev/null and b/experimental/bin/processing/mode/experimental/XQErrorTable$3.class differ diff --git a/experimental/bin/processing/mode/experimental/XQErrorTable.class b/experimental/bin/processing/mode/experimental/XQErrorTable.class new file mode 100644 index 000000000..d092c182c Binary files /dev/null and b/experimental/bin/processing/mode/experimental/XQErrorTable.class differ diff --git a/experimental/bin/processing/mode/experimental/XQPreprocessor$XQASTVisitor.class b/experimental/bin/processing/mode/experimental/XQPreprocessor$XQASTVisitor.class new file mode 100644 index 000000000..14124898a Binary files /dev/null and b/experimental/bin/processing/mode/experimental/XQPreprocessor$XQASTVisitor.class differ diff --git a/experimental/bin/processing/mode/experimental/XQPreprocessor.class b/experimental/bin/processing/mode/experimental/XQPreprocessor.class new file mode 100644 index 000000000..81a3e361f Binary files /dev/null and b/experimental/bin/processing/mode/experimental/XQPreprocessor.class differ diff --git a/experimental/build.xml b/experimental/build.xml deleted file mode 100644 index 2e0f18baa..000000000 --- a/experimental/build.xml +++ /dev/null @@ -1,59 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/experimental/mode/.gitignore b/experimental/mode/.gitignore deleted file mode 100644 index 9acf0e7e9..000000000 --- a/experimental/mode/.gitignore +++ /dev/null @@ -1 +0,0 @@ -experimental.jar diff --git a/experimental/mode/readme.txt b/experimental/mode/readme.txt deleted file mode 100644 index 2152b63de..000000000 --- a/experimental/mode/readme.txt +++ /dev/null @@ -1,5 +0,0 @@ -Packages from Eclipse 4.2.1: -http://download.eclipse.org/eclipse/downloads/ - -The jdi.jar and jdimodel.jar files are unpacked -from the org.eclipse.jdt.debug JAR file. diff --git a/experimental/src/processing/mode/experimental/ArrayFieldNode.java b/experimental/src/processing/mode/experimental/ArrayFieldNode.java deleted file mode 100755 index 04b3fb111..000000000 --- a/experimental/src/processing/mode/experimental/ArrayFieldNode.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright (C) 2012 Martin Leopold - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program 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 for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ -package processing.mode.experimental; - -import com.sun.jdi.ArrayReference; -import com.sun.jdi.ClassNotLoadedException; -import com.sun.jdi.InvalidTypeException; -import com.sun.jdi.Value; -import java.util.logging.Level; -import java.util.logging.Logger; - -/** - * Specialized {@link VariableNode} for representing single fields in an array. - * Overrides {@link #setValue} to properly change the value of the encapsulated - * array field. - * - * @author Martin Leopold - */ -public class ArrayFieldNode extends VariableNode { - - protected ArrayReference array; - protected int index; - - /** - * Construct an {@link ArrayFieldNode}. - * - * @param name the name - * @param type the type - * @param value the value - * @param array a reference to the array - * @param index the index inside the array - */ - public ArrayFieldNode(String name, String type, Value value, ArrayReference array, int index) { - super(name, type, value); - this.array = array; - this.index = index; - } - - @Override - public void setValue(Value value) { - try { - array.setValue(index, value); - } catch (InvalidTypeException ex) { - Logger.getLogger(ArrayFieldNode.class.getName()).log(Level.SEVERE, null, ex); - } catch (ClassNotLoadedException ex) { - Logger.getLogger(ArrayFieldNode.class.getName()).log(Level.SEVERE, null, ex); - } - this.value = value; - } -} diff --git a/experimental/src/processing/mode/experimental/ClassLoadListener.java b/experimental/src/processing/mode/experimental/ClassLoadListener.java deleted file mode 100755 index 64bd5516b..000000000 --- a/experimental/src/processing/mode/experimental/ClassLoadListener.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (C) 2012 Martin Leopold - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program 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 for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ -package processing.mode.experimental; - -import com.sun.jdi.ReferenceType; - -/** - * Listener to be notified when a class is loaded in the debugger. Used by - * {@link LineBreakpoint}s to activate themselves as soon as the respective - * class is loaded. - * - * @author Martin Leopold - */ -public interface ClassLoadListener { - - /** - * Event handler called when a class is loaded. - * - * @param theClass the class - */ - public void classLoaded(ReferenceType theClass); -} diff --git a/experimental/src/processing/mode/experimental/Compiler.java b/experimental/src/processing/mode/experimental/Compiler.java deleted file mode 100755 index 1a85c6a29..000000000 --- a/experimental/src/processing/mode/experimental/Compiler.java +++ /dev/null @@ -1,367 +0,0 @@ -/* - * Copyright (C) 2012 Martin Leopold - * - * This program is free software; you can redistribute it and/or modify it under - * the terms of the GNU General Public License as published by the Free Software - * Foundation; either version 2 of the License, or (at your option) any later - * version. - * - * This program 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 for more - * details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., 59 Temple - * Place - Suite 330, Boston, MA 02111-1307, USA. - */ -package processing.mode.experimental; - -import java.io.*; -import java.lang.reflect.Method; -import processing.app.Base; -import processing.app.SketchException; -import processing.core.PApplet; - -/** - * Copied from processing.mode.java.Compiler, just added -g switch to generate - * debugging info. - * - * @author Martin Leopold - */ -public class Compiler extends processing.mode.java.Compiler { - /** - * Compile with ECJ. See http://j.mp/8paifz for documentation. - * - * @return true if successful. - * @throws RunnerException Only if there's a problem. Only then. - */ -// public boolean compile(Sketch sketch, -// File srcFolder, -// File binFolder, -// String primaryClassName, -// String sketchClassPath, -// String bootClassPath) throws RunnerException { - static public boolean compile(DebugBuild build) throws SketchException { - - // This will be filled in if anyone gets angry - SketchException exception = null; - boolean success = false; - - String baseCommand[] = new String[] { - "-g", - "-Xemacs", - //"-noExit", // not necessary for ecj - "-source", "1.6", - "-target", "1.6", - "-classpath", build.getClassPath(), - "-nowarn", // we're not currently interested in warnings (works in ecj) - "-d", build.getBinFolder().getAbsolutePath() // output the classes in the buildPath - }; - //PApplet.println(baseCommand); - - // make list of code files that need to be compiled -// String[] sourceFiles = new String[sketch.getCodeCount()]; -// int sourceCount = 0; -// sourceFiles[sourceCount++] = -// new File(buildPath, primaryClassName + ".java").getAbsolutePath(); -// -// for (SketchCode code : sketch.getCode()) { -// if (code.isExtension("java")) { -// String path = new File(buildPath, code.getFileName()).getAbsolutePath(); -// sourceFiles[sourceCount++] = path; -// } -// } - String[] sourceFiles = Base.listFiles(build.getSrcFolder(), false, ".java"); - -// String[] command = new String[baseCommand.length + sourceFiles.length]; -// System.arraycopy(baseCommand, 0, command, 0, baseCommand.length); -// // append each of the files to the command string -// System.arraycopy(sourceFiles, 0, command, baseCommand.length, sourceCount); - String[] command = PApplet.concat(baseCommand, sourceFiles); - - //PApplet.println(command); - - try { - // Load errors into a local StringBuffer - final StringBuffer errorBuffer = new StringBuffer(); - - // Create single method dummy writer class to slurp errors from ecj - Writer internalWriter = new Writer() { - public void write(char[] buf, int off, int len) { - errorBuffer.append(buf, off, len); - } - - public void flush() { } - - public void close() { } - }; - // Wrap as a PrintWriter since that's what compile() wants - PrintWriter writer = new PrintWriter(internalWriter); - - //result = com.sun.tools.javac.Main.compile(command, writer); - - PrintWriter outWriter = new PrintWriter(System.out); - - // Version that's not dynamically loaded - //CompilationProgress progress = null; - //success = BatchCompiler.compile(command, outWriter, writer, progress); - - // Version that *is* dynamically loaded. First gets the mode class loader - // so that it can grab the compiler JAR files from it. - ClassLoader loader = build.getMode().getJavaModeClassLoader(); - //ClassLoader loader = build.getMode().getClassLoader(); - try { - Class batchClass = - Class.forName("org.eclipse.jdt.core.compiler.batch.BatchCompiler", false, loader); - Class progressClass = - Class.forName("org.eclipse.jdt.core.compiler.CompilationProgress", false, loader); - Class[] compileArgs = - new Class[] { String[].class, PrintWriter.class, PrintWriter.class, progressClass }; - Method compileMethod = batchClass.getMethod("compile", compileArgs); - success = (Boolean) - compileMethod.invoke(null, new Object[] { command, outWriter, writer, null }); - } catch (Exception e) { - e.printStackTrace(); - throw new SketchException("Unknown error inside the compiler."); - } - - // Close out the stream for good measure - writer.flush(); - writer.close(); - - BufferedReader reader = - new BufferedReader(new StringReader(errorBuffer.toString())); - //System.err.println(errorBuffer.toString()); - - String line = null; - while ((line = reader.readLine()) != null) { - //System.out.println("got line " + line); // debug - - // get first line, which contains file name, line number, - // and at least the first line of the error message - String errorFormat = "([\\w\\d_]+.java):(\\d+):\\s*(.*):\\s*(.*)\\s*"; - String[] pieces = PApplet.match(line, errorFormat); - //PApplet.println(pieces); - - // if it's something unexpected, die and print the mess to the console - if (pieces == null) { - exception = new SketchException("Cannot parse error text: " + line); - exception.hideStackTrace(); - // Send out the rest of the error message to the console. - System.err.println(line); - while ((line = reader.readLine()) != null) { - System.err.println(line); - } - break; - } - - // translate the java filename and line number into a un-preprocessed - // location inside a source file or tab in the environment. - String dotJavaFilename = pieces[1]; - // Line numbers are 1-indexed from javac - int dotJavaLineIndex = PApplet.parseInt(pieces[2]) - 1; - String errorMessage = pieces[4]; - - exception = build.placeException(errorMessage, - dotJavaFilename, - dotJavaLineIndex); - /* - int codeIndex = 0; //-1; - int codeLine = -1; - - // first check to see if it's a .java file - for (int i = 0; i < sketch.getCodeCount(); i++) { - SketchCode code = sketch.getCode(i); - if (code.isExtension("java")) { - if (dotJavaFilename.equals(code.getFileName())) { - codeIndex = i; - codeLine = dotJavaLineIndex; - } - } - } - - // if it's not a .java file, codeIndex will still be 0 - if (codeIndex == 0) { // main class, figure out which tab - //for (int i = 1; i < sketch.getCodeCount(); i++) { - for (int i = 0; i < sketch.getCodeCount(); i++) { - SketchCode code = sketch.getCode(i); - - if (code.isExtension("pde")) { - if (code.getPreprocOffset() <= dotJavaLineIndex) { - codeIndex = i; - //System.out.println("i'm thinkin file " + i); - codeLine = dotJavaLineIndex - code.getPreprocOffset(); - } - } - } - } - //System.out.println("code line now " + codeLine); - exception = new RunnerException(errorMessage, codeIndex, codeLine, -1, false); - */ - - if (exception == null) { - exception = new SketchException(errorMessage); - } - - // for a test case once message parsing is implemented, - // use new Font(...) since that wasn't getting picked up properly. - - /* - if (errorMessage.equals("cannot find symbol")) { - handleCannotFindSymbol(reader, exception); - - } else if (errorMessage.indexOf("is already defined") != -1) { - reader.readLine(); // repeats the line of code w/ error - int codeColumn = caretColumn(reader.readLine()); - exception = new RunnerException(errorMessage, - codeIndex, codeLine, codeColumn); - - } else if (errorMessage.startsWith("package") && - errorMessage.endsWith("does not exist")) { - // Because imports are stripped out and re-added to the 0th line of - // the preprocessed code, codeLine will always be wrong for imports. - exception = new RunnerException("P" + errorMessage.substring(1) + - ". You might be missing a library."); - } else { - exception = new RunnerException(errorMessage); - } - */ - if (errorMessage.startsWith("The import ") && - errorMessage.endsWith("cannot be resolved")) { - // The import poo cannot be resolved - //import poo.shoe.blah.*; - //String what = errorMessage.substring("The import ".length()); - String[] m = PApplet.match(errorMessage, "The import (.*) cannot be resolved"); - //what = what.substring(0, what.indexOf(' ')); - if (m != null) { -// System.out.println("'" + m[1] + "'"); - if (m[1].equals("processing.xml")) { - exception.setMessage("processing.xml no longer exists, this code needs to be updated for 2.0."); - System.err.println("The processing.xml library has been replaced " + - "with a new 'XML' class that's built-in."); - handleCrustyCode(); - - } else { - exception.setMessage("The package " + - "\u201C" + m[1] + "\u201D" + - " does not exist. " + - "You might be missing a library."); - System.err.println("Libraries must be " + - "installed in a folder named 'libraries' " + - "inside the 'sketchbook' folder."); - } - } - -// // Actually create the folder and open it for the user -// File sketchbookLibraries = Base.getSketchbookLibrariesFolder(); -// if (!sketchbookLibraries.exists()) { -// if (sketchbookLibraries.mkdirs()) { -// Base.openFolder(sketchbookLibraries); -// } -// } - - } else if (errorMessage.endsWith("cannot be resolved to a type")) { - // xxx cannot be resolved to a type - //xxx c; - - String what = errorMessage.substring(0, errorMessage.indexOf(' ')); - - if (what.equals("BFont") || - what.equals("BGraphics") || - what.equals("BImage")) { - exception.setMessage(what + " has been replaced with P" + what.substring(1)); - handleCrustyCode(); - - } else { - exception.setMessage("Cannot find a class or type " + - "named \u201C" + what + "\u201D"); - } - - } else if (errorMessage.endsWith("cannot be resolved")) { - // xxx cannot be resolved - //println(xxx); - - String what = errorMessage.substring(0, errorMessage.indexOf(' ')); - - if (what.equals("LINE_LOOP") || - what.equals("LINE_STRIP")) { - exception.setMessage("LINE_LOOP and LINE_STRIP are not available, " + - "please update your code."); - handleCrustyCode(); - - } else if (what.equals("framerate")) { - exception.setMessage("framerate should be changed to frameRate."); - handleCrustyCode(); - - } else if (what.equals("screen")) { - exception.setMessage("Change screen.width and screen.height to " + - "displayWidth and displayHeight."); - handleCrustyCode(); - - } else if (what.equals("screenWidth") || - what.equals("screenHeight")) { - exception.setMessage("Change screenWidth and screenHeight to " + - "displayWidth and displayHeight."); - handleCrustyCode(); - - } else { - exception.setMessage("Cannot find anything " + - "named \u201C" + what + "\u201D"); - } - - } else if (errorMessage.startsWith("Duplicate")) { - // "Duplicate nested type xxx" - // "Duplicate local variable xxx" - - } else { - String[] parts = null; - - // The method xxx(String) is undefined for the type Temporary_XXXX_XXXX - //xxx("blah"); - // The method xxx(String, int) is undefined for the type Temporary_XXXX_XXXX - //xxx("blah", 34); - // The method xxx(String, int) is undefined for the type PApplet - //PApplet.sub("ding"); - String undefined = - "The method (\\S+\\(.*\\)) is undefined for the type (.*)"; - parts = PApplet.match(errorMessage, undefined); - if (parts != null) { - if (parts[1].equals("framerate(int)")) { - exception.setMessage("framerate() no longer exists, use frameRate() instead."); - handleCrustyCode(); - - } else if (parts[1].equals("push()")) { - exception.setMessage("push() no longer exists, use pushMatrix() instead."); - handleCrustyCode(); - - } else if (parts[1].equals("pop()")) { - exception.setMessage("pop() no longer exists, use popMatrix() instead."); - handleCrustyCode(); - - } else { - String mess = "The function " + parts[1] + " does not exist."; - exception.setMessage(mess); - } - break; - } - } - if (exception != null) { - // The stack trace just shows that this happened inside the compiler, - // which is a red herring. Don't ever show it for compiler stuff. - exception.hideStackTrace(); - break; - } - } - } catch (IOException e) { - String bigSigh = "Error while compiling. (" + e.getMessage() + ")"; - exception = new SketchException(bigSigh); - e.printStackTrace(); - success = false; - } - // In case there was something else. - if (exception != null) throw exception; - - return success; - } -} diff --git a/experimental/src/processing/mode/experimental/DebugBuild.java b/experimental/src/processing/mode/experimental/DebugBuild.java deleted file mode 100755 index fdb9b34e3..000000000 --- a/experimental/src/processing/mode/experimental/DebugBuild.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright (C) 2012 Martin Leopold - * - * This program is free software; you can redistribute it and/or modify it under - * the terms of the GNU General Public License as published by the Free Software - * Foundation; either version 2 of the License, or (at your option) any later - * version. - * - * This program 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 for more - * details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., 59 Temple - * Place - Suite 330, Boston, MA 02111-1307, USA. - */ -package processing.mode.experimental; - -import java.io.File; -import processing.app.Sketch; -import processing.app.SketchException; -import processing.mode.java.JavaBuild; - -/** - * Copied from processing.mode.java.JavaBuild, just changed compiler. - * - * @author Martin Leopold - */ -public class DebugBuild extends JavaBuild { - - public DebugBuild(Sketch sketch) { - super(sketch); - } - - /** - * Preprocess and compile sketch. Copied from - * processing.mode.java.JavaBuild, just changed compiler. - * - * @param srcFolder - * @param binFolder - * @param sizeWarning - * @return main class name or null on compile failure - * @throws SketchException - */ - @Override - public String build(File srcFolder, File binFolder, boolean sizeWarning) throws SketchException { - this.srcFolder = srcFolder; - this.binFolder = binFolder; - -// Base.openFolder(srcFolder); -// Base.openFolder(binFolder); - - // run the preprocessor - String classNameFound = preprocess(srcFolder, sizeWarning); - - // compile the program. errors will happen as a RunnerException - // that will bubble up to whomever called build(). -// Compiler compiler = new Compiler(this); -// String bootClasses = System.getProperty("sun.boot.class.path"); -// if (compiler.compile(this, srcFolder, binFolder, primaryClassName, getClassPath(), bootClasses)) { - - if (Compiler.compile(this)) { // use compiler with debug info enabled (-g switch flicked) - sketchClassName = classNameFound; - return classNameFound; - } - return null; - } - - public ExperimentalMode getMode() { - return (ExperimentalMode)mode; - } -} diff --git a/experimental/src/processing/mode/experimental/DebugEditor.java b/experimental/src/processing/mode/experimental/DebugEditor.java deleted file mode 100755 index 91bf862b9..000000000 --- a/experimental/src/processing/mode/experimental/DebugEditor.java +++ /dev/null @@ -1,1141 +0,0 @@ -/* - * Copyright (C) 2012 Martin Leopold - * - * This program is free software; you can redistribute it and/or modify it under - * the terms of the GNU General Public License as published by the Free Software - * Foundation; either version 2 of the License, or (at your option) any later - * version. - * - * This program 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 for more - * details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., 59 Temple - * Place - Suite 330, Boston, MA 02111-1307, USA. - */ -package processing.mode.experimental; - -import java.awt.BorderLayout; -import java.awt.CardLayout; -import java.awt.Color; -import java.awt.EventQueue; -import java.awt.Frame; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.awt.event.KeyEvent; -import java.io.File; -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; -import java.util.logging.Level; -import java.util.logging.Logger; - -import javax.swing.Box; -import javax.swing.JCheckBoxMenuItem; -import javax.swing.JMenu; -import javax.swing.JMenuItem; -import javax.swing.JOptionPane; -import javax.swing.JPanel; -import javax.swing.JScrollPane; -import javax.swing.border.EtchedBorder; -import javax.swing.table.TableModel; -import javax.swing.text.Document; -import processing.app.*; -import processing.app.syntax.JEditTextArea; -import processing.app.syntax.PdeTextAreaDefaults; -import processing.core.PApplet; -import processing.mode.java.JavaEditor; - -/** - * Main View Class. Handles the editor window including tool bar and menu. Has - * access to the Sketch. Provides line highlighting (for breakpoints and the - * debuggers current line). - * - * @author Martin Leopold - * @author Manindra Moharana <me@mkmoharana.com> - * - * - */ -public class DebugEditor extends JavaEditor implements ActionListener { - // important fields from superclass - //protected Sketch sketch; - //private JMenu fileMenu; - //protected EditorToolbar toolbar; - - // highlighting - protected Color breakpointColor = new Color(240, 240, 240); // the background color for highlighting lines - protected Color currentLineColor = new Color(255, 255, 150); // the background color for highlighting lines - protected Color breakpointMarkerColor = new Color(74, 84, 94); // the color of breakpoint gutter markers - protected Color currentLineMarkerColor = new Color(226, 117, 0); // the color of current line gutter markers - protected List breakpointedLines = new ArrayList(); // breakpointed lines - protected LineHighlight currentLine; // line the debugger is currently suspended at - protected final String breakpointMarkerComment = " //<>//"; // breakpoint marker comment - // menus - protected JMenu debugMenu; // the debug menu - // debugger control - protected JMenuItem debugMenuItem; - protected JMenuItem continueMenuItem; - protected JMenuItem stopMenuItem; - // breakpoints - protected JMenuItem toggleBreakpointMenuItem; - protected JMenuItem listBreakpointsMenuItem; - // stepping - protected JMenuItem stepOverMenuItem; - protected JMenuItem stepIntoMenuItem; - protected JMenuItem stepOutMenuItem; - // info - protected JMenuItem printStackTraceMenuItem; - protected JMenuItem printLocalsMenuItem; - protected JMenuItem printThisMenuItem; - protected JMenuItem printSourceMenuItem; - protected JMenuItem printThreads; - // variable inspector - protected JMenuItem toggleVariableInspectorMenuItem; - // references - protected ExperimentalMode dmode; // the mode - protected Debugger dbg; // the debugger - protected VariableInspector vi; // the variable inspector frame - protected TextArea ta; // the text area - - - protected ErrorBar errorBar; - /** - * Show Console button - */ - protected XQConsoleToggle btnShowConsole; - - /** - * Show Problems button - */ - protected XQConsoleToggle btnShowErrors; - - /** - * Scroll pane for Error Table - */ - protected JScrollPane errorTableScrollPane; - - /** - * Panel with card layout which contains the p5 console and Error Table - * panes - */ - protected JPanel consoleProblemsPane; - - protected XQErrorTable errorTable; - - /** - * Enable/Disable compilation checking - */ - protected boolean compilationCheckEnabled = true; - - /** - * Show warnings menu item - */ - protected JCheckBoxMenuItem showWarnings; - - /** - * Check box menu item for show/hide Problem Window - */ - public JCheckBoxMenuItem problemWindowMenuCB; - - public DebugEditor(Base base, String path, EditorState state, Mode mode) { - super(base, path, state, mode); - - // get mode - dmode = (ExperimentalMode) mode; - - // init controller class - dbg = new Debugger(this); - - // variable inspector window - vi = new VariableInspector(this); - - // access to customized (i.e. subclassed) text area - ta = (TextArea) textarea; - - // set action on frame close -// addWindowListener(new WindowAdapter() { -// @Override -// public void windowClosing(WindowEvent e) { -// onWindowClosing(e); -// } -// }); - - // load settings from theme.txt - ExperimentalMode theme = dmode; - breakpointColor = theme.getThemeColor("breakpoint.bgcolor", breakpointColor); - breakpointMarkerColor = theme.getThemeColor("breakpoint.marker.color", breakpointMarkerColor); - currentLineColor = theme.getThemeColor("currentline.bgcolor", currentLineColor); - currentLineMarkerColor = theme.getThemeColor("currentline.marker.color", currentLineMarkerColor); - - // set breakpoints from marker comments - for (LineID lineID : stripBreakpointComments()) { - //System.out.println("setting: " + lineID); - dbg.setBreakpoint(lineID); - } - getSketch().setModified(false); // setting breakpoints will flag sketch as modified, so override this here - - checkForJavaTabs(); - initializeErrorChecker(); - ta.setECSandThemeforTextArea(errorCheckerService, dmode); - addXQModeUI(); - } - - private void addXQModeUI(){ - - // Adding ErrorBar - JPanel textAndError = new JPanel(); - Box box = (Box) textarea.getParent(); - box.remove(2); // Remove textArea from it's container, i.e Box - textAndError.setLayout(new BorderLayout()); - errorBar = new ErrorBar(this, textarea.getMinimumSize().height, dmode); - textAndError.add(errorBar, BorderLayout.EAST); - textarea.setBounds(0, 0, errorBar.getX() - 1, textarea.getHeight()); - textAndError.add(textarea); - box.add(textAndError); - - // Adding Error Table in a scroll pane - errorTableScrollPane = new JScrollPane(); - errorTable = new XQErrorTable(errorCheckerService); - // errorTableScrollPane.setBorder(new EmptyBorder(2, 2, 2, 2)); - errorTableScrollPane.setBorder(new EtchedBorder()); - errorTableScrollPane.setViewportView(errorTable); - - // Adding toggle console button - consolePanel.remove(2); - JPanel lineStatusPanel = new JPanel(); - lineStatusPanel.setLayout(new BorderLayout()); - btnShowConsole = new XQConsoleToggle(this, - XQConsoleToggle.text[0], lineStatus.getHeight()); - btnShowErrors = new XQConsoleToggle(this, - XQConsoleToggle.text[1], lineStatus.getHeight()); - btnShowConsole.addMouseListener(btnShowConsole); - - // lineStatusPanel.add(btnShowConsole, BorderLayout.EAST); - // lineStatusPanel.add(btnShowErrors); - btnShowErrors.addMouseListener(btnShowErrors); - - JPanel toggleButtonPanel = new JPanel(new BorderLayout()); - toggleButtonPanel.add(btnShowConsole, BorderLayout.EAST); - toggleButtonPanel.add(btnShowErrors, BorderLayout.WEST); - lineStatusPanel.add(toggleButtonPanel, BorderLayout.EAST); - lineStatus.setBounds(0, 0, toggleButtonPanel.getX() - 1, - toggleButtonPanel.getHeight()); - lineStatusPanel.add(lineStatus); - consolePanel.add(lineStatusPanel, BorderLayout.SOUTH); - lineStatusPanel.repaint(); - - // Adding JPanel with CardLayout for Console/Problems Toggle - consolePanel.remove(1); - consoleProblemsPane = new JPanel(new CardLayout()); - consoleProblemsPane.add(errorTableScrollPane, XQConsoleToggle.text[1]); - consoleProblemsPane.add(console, XQConsoleToggle.text[0]); - consolePanel.add(consoleProblemsPane, BorderLayout.CENTER); - } - -// /** -// * Event handler called when closing the editor window. Kills the variable -// * inspector window. -// * -// * @param e the event object -// */ -// protected void onWindowClosing(WindowEvent e) { -// // remove var.inspector -// vi.dispose(); -// // quit running debug session -// dbg.stopDebug(); -// } - /** - * Used instead of the windowClosing event handler, since it's not called on - * mode switch. Called when closing the editor window. Stops running debug - * sessions and kills the variable inspector window. - */ - @Override - public void dispose() { - //System.out.println("window dispose"); - // quit running debug session - dbg.stopDebug(); - // remove var.inspector - vi.dispose(); - // original dispose - super.dispose(); - } - - /** - * Overrides sketch menu creation to change keyboard shortcuts from "Run". - * - * @return the sketch menu - */ - @Override - public JMenu buildSketchMenu() { - JMenuItem runItem = Toolkit.newJMenuItemShift(DebugToolbar.getTitle(DebugToolbar.RUN, false), KeyEvent.VK_R); - runItem.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - handleRun(); - } - }); - - JMenuItem presentItem = new JMenuItem(DebugToolbar.getTitle(DebugToolbar.RUN, true)); - presentItem.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - handlePresent(); - } - }); - - JMenuItem stopItem = new JMenuItem(DebugToolbar.getTitle(DebugToolbar.STOP, false)); - stopItem.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - handleStop(); - } - }); - return buildSketchMenu(new JMenuItem[]{runItem, presentItem, stopItem}); - } - - /** - * Creates the debug menu. Includes ActionListeners for the menu items. - * Intended for adding to the menu bar. - * - * @return The debug menu - */ - protected JMenu buildDebugMenu() { - debugMenu = new JMenu("Debug"); - - debugMenuItem = Toolkit.newJMenuItem("Debug", KeyEvent.VK_R); - debugMenuItem.addActionListener(this); - continueMenuItem = Toolkit.newJMenuItem("Continue", KeyEvent.VK_U); - continueMenuItem.addActionListener(this); - stopMenuItem = new JMenuItem("Stop"); - stopMenuItem.addActionListener(this); - - toggleBreakpointMenuItem = Toolkit.newJMenuItem("Toggle Breakpoint", KeyEvent.VK_B); - toggleBreakpointMenuItem.addActionListener(this); - listBreakpointsMenuItem = new JMenuItem("List Breakpoints"); - listBreakpointsMenuItem.addActionListener(this); - - stepOverMenuItem = Toolkit.newJMenuItem("Step", KeyEvent.VK_J); - stepOverMenuItem.addActionListener(this); - stepIntoMenuItem = Toolkit.newJMenuItemShift("Step Into", KeyEvent.VK_J); - stepIntoMenuItem.addActionListener(this); - stepOutMenuItem = Toolkit.newJMenuItemAlt("Step Out", KeyEvent.VK_J); - stepOutMenuItem.addActionListener(this); - - printStackTraceMenuItem = new JMenuItem("Print Stack Trace"); - printStackTraceMenuItem.addActionListener(this); - printLocalsMenuItem = new JMenuItem("Print Locals"); - printLocalsMenuItem.addActionListener(this); - printThisMenuItem = new JMenuItem("Print Fields"); - printThisMenuItem.addActionListener(this); - printSourceMenuItem = new JMenuItem("Print Source Location"); - printSourceMenuItem.addActionListener(this); - printThreads = new JMenuItem("Print Threads"); - printThreads.addActionListener(this); - - toggleVariableInspectorMenuItem = Toolkit.newJMenuItem("Toggle Variable Inspector", KeyEvent.VK_I); - toggleVariableInspectorMenuItem.addActionListener(this); - - debugMenu.add(debugMenuItem); - debugMenu.add(continueMenuItem); - debugMenu.add(stopMenuItem); - debugMenu.addSeparator(); - debugMenu.add(toggleBreakpointMenuItem); - debugMenu.add(listBreakpointsMenuItem); - debugMenu.addSeparator(); - debugMenu.add(stepOverMenuItem); - debugMenu.add(stepIntoMenuItem); - debugMenu.add(stepOutMenuItem); - debugMenu.addSeparator(); - debugMenu.add(printStackTraceMenuItem); - debugMenu.add(printLocalsMenuItem); - debugMenu.add(printThisMenuItem); - debugMenu.add(printSourceMenuItem); - debugMenu.add(printThreads); - debugMenu.addSeparator(); - debugMenu.add(toggleVariableInspectorMenuItem); - debugMenu.addSeparator(); - - // XQMode menu items - - JCheckBoxMenuItem item; - final DebugEditor thisEditor = this; - item = new JCheckBoxMenuItem("Error Checker Enabled"); - item.setSelected(true); - item.addActionListener(new ActionListener() { - - @Override - public void actionPerformed(ActionEvent e) { - - if (!((JCheckBoxMenuItem) e.getSource()).isSelected()) { - // unticked Menu Item - errorCheckerService.pauseThread(); - System.out.println(thisEditor.getSketch().getName() - + " - Error Checker paused."); - errorBar.errorPoints.clear(); - errorCheckerService.problemsList.clear(); - errorCheckerService.updateErrorTable(); - errorCheckerService.updateEditorStatus(); - getTextArea().repaint(); - errorBar.repaint(); - } else { - errorCheckerService.resumeThread(); - System.out.println(thisEditor.getSketch().getName() - + " - Error Checker resumed."); - } - } - }); - debugMenu.add(item); - - problemWindowMenuCB = new JCheckBoxMenuItem("Show Problem Window"); - // problemWindowMenuCB.setSelected(true); - problemWindowMenuCB.addActionListener(new ActionListener() { - - @Override - public void actionPerformed(ActionEvent e) { - if (errorCheckerService.errorWindow == null) { - return; - } - errorCheckerService.errorWindow - .setVisible(((JCheckBoxMenuItem) e.getSource()) - .isSelected()); - // switch to console, now that Error Window is open - toggleView(XQConsoleToggle.text[0]); - } - }); - debugMenu.add(problemWindowMenuCB); - - showWarnings = new JCheckBoxMenuItem("Warnings Enabled"); - showWarnings.setSelected(true); - showWarnings.addActionListener(new ActionListener() { - - @Override - public void actionPerformed(ActionEvent e) { - errorCheckerService.warningsEnabled = ((JCheckBoxMenuItem) e - .getSource()).isSelected(); - } - }); - debugMenu.add(showWarnings); - - - return debugMenu; - } - - @Override - public JMenu buildModeMenu() { - return buildDebugMenu(); - } - - /** - * Callback for menu items. Implementation of Swing ActionListener. - * - * @param ae Action event - */ - @Override - public void actionPerformed(ActionEvent ae) { - //System.out.println("ActionEvent: " + ae.toString()); - - JMenuItem source = (JMenuItem) ae.getSource(); - if (source == debugMenuItem) { - Logger.getLogger(DebugEditor.class.getName()).log(Level.INFO, "Invoked 'Debug' menu item"); - //dmode.handleDebug(sketch, this); - dbg.startDebug(); - } else if (source == stopMenuItem) { - Logger.getLogger(DebugEditor.class.getName()).log(Level.INFO, "Invoked 'Stop' menu item"); - //dmode.handleDebug(sketch, this); - dbg.stopDebug(); - } else if (source == continueMenuItem) { - Logger.getLogger(DebugEditor.class.getName()).log(Level.INFO, "Invoked 'Continue' menu item"); - //dmode.handleDebug(sketch, this); - dbg.continueDebug(); - } else if (source == stepOverMenuItem) { - Logger.getLogger(DebugEditor.class.getName()).log(Level.INFO, "Invoked 'Step Over' menu item"); - dbg.stepOver(); - } else if (source == stepIntoMenuItem) { - Logger.getLogger(DebugEditor.class.getName()).log(Level.INFO, "Invoked 'Step Into' menu item"); - dbg.stepInto(); - } else if (source == stepOutMenuItem) { - Logger.getLogger(DebugEditor.class.getName()).log(Level.INFO, "Invoked 'Step Out' menu item"); - dbg.stepOut(); - } else if (source == printStackTraceMenuItem) { - Logger.getLogger(DebugEditor.class.getName()).log(Level.INFO, "Invoked 'Print Stack Trace' menu item"); - dbg.printStackTrace(); - } else if (source == printLocalsMenuItem) { - Logger.getLogger(DebugEditor.class.getName()).log(Level.INFO, "Invoked 'Print Locals' menu item"); - dbg.printLocals(); - } else if (source == printThisMenuItem) { - Logger.getLogger(DebugEditor.class.getName()).log(Level.INFO, "Invoked 'Print This' menu item"); - dbg.printThis(); - } else if (source == printSourceMenuItem) { - Logger.getLogger(DebugEditor.class.getName()).log(Level.INFO, "Invoked 'Print Source' menu item"); - dbg.printSource(); - } else if (source == printThreads) { - Logger.getLogger(DebugEditor.class.getName()).log(Level.INFO, "Invoked 'Print Threads' menu item"); - dbg.printThreads(); - } else if (source == toggleBreakpointMenuItem) { - Logger.getLogger(DebugEditor.class.getName()).log(Level.INFO, "Invoked 'Toggle Breakpoint' menu item"); - dbg.toggleBreakpoint(); - } else if (source == listBreakpointsMenuItem) { - Logger.getLogger(DebugEditor.class.getName()).log(Level.INFO, "Invoked 'List Breakpoints' menu item"); - dbg.listBreakpoints(); - } else if (source == toggleVariableInspectorMenuItem) { - Logger.getLogger(DebugEditor.class.getName()).log(Level.INFO, "Invoked 'Toggle Variable Inspector' menu item"); - toggleVariableInspector(); - } - } - -// @Override -// public void handleRun() { -// dbg.continueDebug(); -// } - /** - * Event handler called when hitting the stop button. Stops a running debug - * session or performs standard stop action if not currently debugging. - */ - @Override - public void handleStop() { - if (dbg.isStarted()) { - dbg.stopDebug(); - } else { - super.handleStop(); - } - } - - /** - * Event handler called when loading another sketch in this editor. Clears - * breakpoints of previous sketch. - * - * @param path - * @return true if a sketch was opened, false if aborted - */ - @Override - protected boolean handleOpenInternal(String path) { - boolean didOpen = super.handleOpenInternal(path); - if (didOpen && dbg != null) { - // should already been stopped (open calls handleStop) - dbg.clearBreakpoints(); - clearBreakpointedLines(); // force clear breakpoint highlights - variableInspector().reset(); // clear contents of variable inspector - } - return didOpen; - } - - /** - * Extract breakpointed lines from source code marker comments. This removes - * marker comments from the editor text. Intended to be called on loading a - * sketch, since re-setting the sketches contents after removing the markers - * will clear all breakpoints. - * - * @return the list of {@link LineID}s where breakpoint marker comments were - * removed from. - */ - protected List stripBreakpointComments() { - List bps = new ArrayList(); - // iterate over all tabs - Sketch sketch = getSketch(); - for (int i = 0; i < sketch.getCodeCount(); i++) { - SketchCode tab = sketch.getCode(i); - String code = tab.getProgram(); - String lines[] = code.split("\\r?\\n"); // newlines not included - //System.out.println(code); - - // scan code for breakpoint comments - int lineIdx = 0; - for (String line : lines) { - //System.out.println(line); - if (line.endsWith(breakpointMarkerComment)) { - LineID lineID = new LineID(tab.getFileName(), lineIdx); - bps.add(lineID); - //System.out.println("found breakpoint: " + lineID); - // got a breakpoint - //dbg.setBreakpoint(lineID); - int index = line.lastIndexOf(breakpointMarkerComment); - lines[lineIdx] = line.substring(0, index); - } - lineIdx++; - } - //tab.setProgram(code); - code = PApplet.join(lines, "\n"); - setTabContents(tab.getFileName(), code); - } - return bps; - } - - /** - * Add breakpoint marker comments to the source file of a specific tab. This - * acts on the source file on disk, not the editor text. Intended to be - * called just after saving the sketch. - * - * @param tabFilename the tab file name - */ - protected void addBreakpointComments(String tabFilename) { - SketchCode tab = getTab(tabFilename); - List bps = dbg.getBreakpoints(tab.getFileName()); - - // load the source file - File sourceFile = new File(sketch.getFolder(), tab.getFileName()); - //System.out.println("file: " + sourceFile); - try { - String code = Base.loadFile(sourceFile); - //System.out.println("code: " + code); - String lines[] = code.split("\\r?\\n"); // newlines not included - for (LineBreakpoint bp : bps) { - //System.out.println("adding bp: " + bp.lineID()); - lines[bp.lineID().lineIdx()] += breakpointMarkerComment; - } - code = PApplet.join(lines, "\n"); - //System.out.println("new code: " + code); - Base.saveFile(code, sourceFile); - } catch (IOException ex) { - Logger.getLogger(DebugEditor.class.getName()).log(Level.SEVERE, null, ex); - } - } - - @Override - public boolean handleSave(boolean immediately) { - //System.out.println("handleSave " + immediately); - - // note modified tabs - final List modified = new ArrayList(); - for (int i = 0; i < getSketch().getCodeCount(); i++) { - SketchCode tab = getSketch().getCode(i); - if (tab.isModified()) { - modified.add(tab.getFileName()); - } - } - - boolean saved = super.handleSave(immediately); - if (saved) { - if (immediately) { - for (String tabFilename : modified) { - addBreakpointComments(tabFilename); - } - } else { - EventQueue.invokeLater(new Runnable() { - @Override - public void run() { - for (String tabFilename : modified) { - addBreakpointComments(tabFilename); - } - } - }); - } - } - return saved; - } - - @Override - public boolean handleSaveAs() { - //System.out.println("handleSaveAs"); - String oldName = getSketch().getCode(0).getFileName(); - //System.out.println("old name: " + oldName); - boolean saved = super.handleSaveAs(); - if (saved) { - // re-set breakpoints in first tab (name has changed) - List bps = dbg.getBreakpoints(oldName); - dbg.clearBreakpoints(oldName); - String newName = getSketch().getCode(0).getFileName(); - //System.out.println("new name: " + newName); - for (LineBreakpoint bp : bps) { - LineID line = new LineID(newName, bp.lineID().lineIdx()); - //System.out.println("setting: " + line); - dbg.setBreakpoint(line); - } - // add breakpoint marker comments to source file - for (int i = 0; i < getSketch().getCodeCount(); i++) { - addBreakpointComments(getSketch().getCode(i).getFileName()); - } - - // set new name of variable inspector - vi.setTitle(getSketch().getName()); - } - return saved; - } - - /** - * Set text contents of a specific tab. Updates underlying document and text - * area. Clears Breakpoints. - * - * @param tabFilename the tab file name - * @param code the text to set - */ - protected void setTabContents(String tabFilename, String code) { - // remove all breakpoints of this tab - dbg.clearBreakpoints(tabFilename); - - SketchCode currentTab = getCurrentTab(); - - // set code of tab - SketchCode tab = getTab(tabFilename); - if (tab != null) { - tab.setProgram(code); - // this updates document and text area - // TODO: does this have any negative effects? (setting the doc to null) - tab.setDocument(null); - setCode(tab); - - // switch back to original tab - setCode(currentTab); - } - } - - /** - * Clear the console. - */ - public void clearConsole() { - console.clear(); - } - - /** - * Clear current text selection. - */ - public void clearSelection() { - setSelection(getCaretOffset(), getCaretOffset()); - } - - /** - * Select a line in the current tab. - * - * @param lineIdx 0-based line number - */ - public void selectLine(int lineIdx) { - setSelection(getLineStartOffset(lineIdx), getLineStopOffset(lineIdx)); - } - - /** - * Set the cursor to the start of a line. - * - * @param lineIdx 0-based line number - */ - public void cursorToLineStart(int lineIdx) { - setSelection(getLineStartOffset(lineIdx), getLineStartOffset(lineIdx)); - } - - /** - * Set the cursor to the end of a line. - * - * @param lineIdx 0-based line number - */ - public void cursorToLineEnd(int lineIdx) { - setSelection(getLineStopOffset(lineIdx), getLineStopOffset(lineIdx)); - } - - /** - * Switch to a tab. - * - * @param tabFileName the file name identifying the tab. (as in - * {@link SketchCode#getFileName()}) - */ - public void switchToTab(String tabFileName) { - Sketch s = getSketch(); - for (int i = 0; i < s.getCodeCount(); i++) { - if (tabFileName.equals(s.getCode(i).getFileName())) { - s.setCurrentCode(i); - break; - } - } - } - - /** - * Access the debugger. - * - * @return the debugger controller object - */ - public Debugger dbg() { - return dbg; - } - - /** - * Access the mode. - * - * @return the mode object - */ - public ExperimentalMode mode() { - return dmode; - } - - /** - * Access the custom text area object. - * - * @return the text area object - */ - public TextArea textArea() { - return ta; - } - - /** - * Access variable inspector window. - * - * @return the variable inspector object - */ - public VariableInspector variableInspector() { - return vi; - } - - public DebugToolbar toolbar() { - return (DebugToolbar) toolbar; - } - - /** - * Show the variable inspector window. - */ - public void showVariableInspector() { - vi.setVisible(true); - } - - /** - * Set visibility of the variable inspector window. - * - * @param visible true to set the variable inspector visible, false for - * invisible. - */ - public void showVariableInspector(boolean visible) { - vi.setVisible(visible); - } - - /** - * Hide the variable inspector window. - */ - public void hideVariableInspector() { - vi.setVisible(true); - } - - /** - * Toggle visibility of the variable inspector window. - */ - public void toggleVariableInspector() { - vi.setFocusableWindowState(false); // to not get focus when set visible - vi.setVisible(!vi.isVisible()); - vi.setFocusableWindowState(true); // allow to get focus again - } - - /** - * Text area factory method. Instantiates the customized TextArea. - * - * @return the customized text area object - */ - @Override - protected JEditTextArea createTextArea() { - //System.out.println("overriding creation of text area"); - return new TextArea(new PdeTextAreaDefaults(mode), this); - } - - /** - * Set the line to highlight as currently suspended at. Will override the - * breakpoint color, if set. Switches to the appropriate tab and scroll to - * the line by placing the cursor there. - * - * @param line the line to highlight as current suspended line - */ - public void setCurrentLine(LineID line) { - clearCurrentLine(); - if (line == null) { - return; // safety, e.g. when no line mapping is found and the null line is used. - } - switchToTab(line.fileName()); - // scroll to line, by setting the cursor - cursorToLineStart(line.lineIdx()); - // highlight line - currentLine = new LineHighlight(line.lineIdx(), currentLineColor, this); - currentLine.setMarker(ta.currentLineMarker, currentLineMarkerColor); - currentLine.setPriority(10); // fixes current line being hidden by the breakpoint when moved down - } - - /** - * Clear the highlight for the debuggers current line. - */ - public void clearCurrentLine() { - if (currentLine != null) { - currentLine.clear(); - currentLine.dispose(); - - // revert to breakpoint color if any is set on this line - for (LineHighlight hl : breakpointedLines) { - if (hl.lineID().equals(currentLine.lineID())) { - hl.paint(); - break; - } - } - currentLine = null; - } - } - - /** - * Add highlight for a breakpointed line. - * - * @param lineID the line id to highlight as breakpointed - */ - public void addBreakpointedLine(LineID lineID) { - LineHighlight hl = new LineHighlight(lineID, breakpointColor, this); - hl.setMarker(ta.breakpointMarker, breakpointMarkerColor); - breakpointedLines.add(hl); - // repaint current line if it's on this line - if (currentLine != null && currentLine.lineID().equals(lineID)) { - currentLine.paint(); - } - } - - /** - * Add highlight for a breakpointed line on the current tab. - * - * @param lineIdx the line index on the current tab to highlight as - * breakpointed - */ - //TODO: remove and replace by {@link #addBreakpointedLine(LineID lineID)} - public void addBreakpointedLine(int lineIdx) { - addBreakpointedLine(getLineIDInCurrentTab(lineIdx)); - } - - /** - * Remove a highlight for a breakpointed line. Needs to be on the current - * tab. - * - * @param lineIdx the line index on the current tab to remove a breakpoint - * highlight from - */ - public void removeBreakpointedLine(int lineIdx) { - LineID line = getLineIDInCurrentTab(lineIdx); - //System.out.println("line id: " + line.fileName() + " " + line.lineIdx()); - LineHighlight foundLine = null; - for (LineHighlight hl : breakpointedLines) { - if (hl.lineID.equals(line)) { - foundLine = hl; - break; - } - } - if (foundLine != null) { - foundLine.clear(); - breakpointedLines.remove(foundLine); - foundLine.dispose(); - // repaint current line if it's on this line - if (currentLine != null && currentLine.lineID().equals(line)) { - currentLine.paint(); - } - } - } - - /** - * Remove all highlights for breakpointed lines. - */ - public void clearBreakpointedLines() { - for (LineHighlight hl : breakpointedLines) { - hl.clear(); - hl.dispose(); - } - breakpointedLines.clear(); // remove all breakpoints - // fix highlights not being removed when tab names have changed due to opening a new sketch in same editor - ta.clearLineBgColors(); // force clear all highlights - ta.clearGutterText(); - - // repaint current line - if (currentLine != null) { - currentLine.paint(); - } - } - - /** - * Retrieve a {@link LineID} object for a line on the current tab. - * - * @param lineIdx the line index on the current tab - * @return the {@link LineID} object representing a line index on the - * current tab - */ - public LineID getLineIDInCurrentTab(int lineIdx) { - return new LineID(getSketch().getCurrentCode().getFileName(), lineIdx); - } - - /** - * Retrieve line of sketch where the cursor currently resides. - * - * @return the current {@link LineID} - */ - protected LineID getCurrentLineID() { - String tab = getSketch().getCurrentCode().getFileName(); - int lineNo = getTextArea().getCaretLine(); - return new LineID(tab, lineNo); - } - - /** - * Check whether a {@link LineID} is on the current tab. - * - * @param line the {@link LineID} - * @return true, if the {@link LineID} is on the current tab. - */ - public boolean isInCurrentTab(LineID line) { - return line.fileName().equals(getSketch().getCurrentCode().getFileName()); - } - - /** - * Event handler called when switching between tabs. Loads all line - * background colors set for the tab. - * - * @param code tab to switch to - */ - @Override - protected void setCode(SketchCode code) { - //System.out.println("tab switch: " + code.getFileName()); - super.setCode(code); // set the new document in the textarea, etc. need to do this first - - // set line background colors for tab - if (ta != null) { // can be null when setCode is called the first time (in constructor) - // clear all line backgrounds - ta.clearLineBgColors(); - // clear all gutter text - ta.clearGutterText(); - // load appropriate line backgrounds for tab - // first paint breakpoints - for (LineHighlight hl : breakpointedLines) { - if (isInCurrentTab(hl.lineID())) { - hl.paint(); - } - } - // now paint current line (if any) - if (currentLine != null) { - if (isInCurrentTab(currentLine.lineID())) { - currentLine.paint(); - } - } - } - if (dbg() != null && dbg().isStarted()) { - dbg().startTrackingLineChanges(); - } - } - - /** - * Get a tab by its file name. - * - * @param fileName the filename to search for. - * @return the {@link SketchCode} object representing the tab, or null if - * not found - */ - public SketchCode getTab(String fileName) { - Sketch s = getSketch(); - for (SketchCode c : s.getCode()) { - if (c.getFileName().equals(fileName)) { - return c; - } - } - return null; - } - - /** - * Retrieve the current tab. - * - * @return the {@link SketchCode} representing the current tab - */ - public SketchCode getCurrentTab() { - return getSketch().getCurrentCode(); - } - - /** - * Access the currently edited document. - * - * @return the document object - */ - public Document currentDocument() { - //return ta.getDocument(); - return getCurrentTab().getDocument(); - } - - /** - * Factory method for the editor toolbar. Instantiates the customized - * toolbar. - * - * @return the toolbar - */ - @Override - public EditorToolbar createToolbar() { - return new DebugToolbar(this, base); - } - - /** - * Event Handler for double clicking in the left hand gutter area. - * - * @param lineIdx the line (0-based) that was double clicked - */ - public void gutterDblClicked(int lineIdx) { - if (dbg != null) { - dbg.toggleBreakpoint(lineIdx); - } - } - - public void statusBusy() { - statusNotice("Debugger busy..."); - } - - public void statusHalted() { - statusNotice("Debugger halted."); - } - - ErrorCheckerService errorCheckerService; - - /** - * Initializes and starts Error Checker Service - */ - private void initializeErrorChecker() { - Thread errorCheckerThread = null; - - if (errorCheckerThread == null) { - errorCheckerService = new ErrorCheckerService(this); - errorCheckerThread = new Thread(errorCheckerService); - try { - errorCheckerThread.start(); - } catch (Exception e) { - System.err - .println("Error Checker Service not initialized [XQEditor]: " - + e); - // e.printStackTrace(); - } - // System.out.println("Error Checker Service initialized."); - } - - } - - /** - * Updates the error bar - * @param problems - */ - public void updateErrorBar(ArrayList problems) { - errorBar.updateErrorPoints(problems); - } - - /** - * Toggle between Console and Errors List - * - * @param buttonName - * - Button Label - */ - public void toggleView(String buttonName) { - CardLayout cl = (CardLayout) consoleProblemsPane.getLayout(); - cl.show(consoleProblemsPane, buttonName); - } - - /** - * Updates the error table - * @param tableModel - * @return - */ - synchronized public boolean updateTable(final TableModel tableModel) { - return errorTable.updateTable(tableModel); - } - - /** - * Checks if the sketch contains java tabs. If it does, XQMode ain't built - * for it, yet. Also, user should really start looking at Eclipse. Disable - * compilation check. - */ - private void checkForJavaTabs() { - for (int i = 0; i < this.getSketch().getCodeCount(); i++) { - if (this.getSketch().getCode(i).getExtension().equals("java")) { - compilationCheckEnabled = false; - JOptionPane.showMessageDialog(new Frame(), this - .getSketch().getName() - + " contains .java tabs. Live compilation error checking isn't " - + "supported for java tabs. Only " - + "syntax errors will be reported for .pde tabs."); - break; - } - } - } -} diff --git a/experimental/src/processing/mode/experimental/DebugRunner.java b/experimental/src/processing/mode/experimental/DebugRunner.java deleted file mode 100755 index 1f810d964..000000000 --- a/experimental/src/processing/mode/experimental/DebugRunner.java +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright (C) 2012 Martin Leopold - * - * This program is free software; you can redistribute it and/or modify it under - * the terms of the GNU General Public License as published by the Free Software - * Foundation; either version 2 of the License, or (at your option) any later - * version. - * - * This program 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 for more - * details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., 59 Temple - * Place - Suite 330, Boston, MA 02111-1307, USA. - */ -package processing.mode.experimental; - -import com.sun.jdi.VirtualMachine; -import processing.app.RunnerListener; -import processing.app.SketchException; -import processing.app.exec.StreamRedirectThread; -import processing.mode.java.JavaBuild; -import processing.mode.java.runner.MessageSiphon; - -/** - * Runs a {@link JavaBuild}. Launches the build in a new debuggee VM. - * - * @author Martin Leopold - */ -public class DebugRunner extends processing.mode.java.runner.Runner { - - // important inherited fields - // protected VirtualMachine vm; - public DebugRunner(JavaBuild build, RunnerListener listener) throws SketchException { - super(build, listener); - } - - /** - * Launch the virtual machine. Simple non-blocking launch. VM starts - * suspended. - * - * @return debuggee VM or null on failure - */ - public VirtualMachine launch() { -// String[] machineParamList = getMachineParams(); -// String[] sketchParamList = getSketchParams(false); -// /* -// * System.out.println("vm launch sketch params:"); for (int i=0; -// * i - * - * This program is free software; you can redistribute it and/or modify it under - * the terms of the GNU General Public License as published by the Free Software - * Foundation; either version 2 of the License, or (at your option) any later - * version. - * - * This program 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 for more - * details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., 59 Temple - * Place - Suite 330, Boston, MA 02111-1307, USA. - */ -package processing.mode.experimental; - -import java.awt.Image; -import java.awt.event.MouseEvent; -import java.util.logging.Level; -import java.util.logging.Logger; -import processing.app.Base; -import processing.app.Editor; -import processing.mode.java.JavaToolbar; - -/** - * Custom toolbar for the editor window. Preserves original button numbers - * ({@link JavaToolbar#RUN}, {@link JavaToolbar#STOP}, {@link JavaToolbar#NEW}, - * {@link JavaToolbar#OPEN}, {@link JavaToolbar#SAVE}, {@link JavaToolbar#EXPORT}) - * which can be used e.g. in {@link #activate} and - * {@link #deactivate}. - * - * @author Martin Leopold - */ -public class DebugToolbar extends JavaToolbar { - // preserve original button id's, but re-define so they are accessible - // (they are used by DebugEditor, so they want to be public) - - static protected final int RUN = 100; // change this, to be able to get it's name via getTitle() - static protected final int DEBUG = JavaToolbar.RUN; - - static protected final int CONTINUE = 101; - static protected final int STEP = 102; - static protected final int TOGGLE_BREAKPOINT = 103; - static protected final int TOGGLE_VAR_INSPECTOR = 104; - - static protected final int STOP = JavaToolbar.STOP; - - static protected final int NEW = JavaToolbar.NEW; - static protected final int OPEN = JavaToolbar.OPEN; - static protected final int SAVE = JavaToolbar.SAVE; - static protected final int EXPORT = JavaToolbar.EXPORT; - - - // the sequence of button ids. (this maps button position = index to button ids) - static protected final int[] buttonSequence = { - DEBUG, CONTINUE, STEP, STOP, TOGGLE_BREAKPOINT, TOGGLE_VAR_INSPECTOR, - NEW, OPEN, SAVE, EXPORT - }; - - - public DebugToolbar(Editor editor, Base base) { - super(editor, base); - } - - - /** - * Initialize buttons. Loads images and adds the buttons to the toolbar. - */ - @Override - public void init() { - Image[][] images = loadImages(); - for (int idx = 0; idx < buttonSequence.length; idx++) { - int id = buttonId(idx); - addButton(getTitle(id, false), getTitle(id, true), images[idx], id == NEW || id == TOGGLE_BREAKPOINT); - } - } - - - /** - * Get the title for a toolbar button. Displayed in the toolbar when - * hovering over a button. - * @param id id of the toolbar button - * @param shift true if shift is pressed - * @return the title - */ - public static String getTitle(int id, boolean shift) { - switch (id) { - case DebugToolbar.RUN: - return JavaToolbar.getTitle(JavaToolbar.RUN, shift); - case STOP: - return JavaToolbar.getTitle(JavaToolbar.STOP, shift); - case NEW: - return JavaToolbar.getTitle(JavaToolbar.NEW, shift); - case OPEN: - return JavaToolbar.getTitle(JavaToolbar.OPEN, shift); - case SAVE: - return JavaToolbar.getTitle(JavaToolbar.SAVE, shift); - case EXPORT: - return JavaToolbar.getTitle(JavaToolbar.EXPORT, shift); - case DEBUG: - if (shift) { - return "Run"; - } else { - return "Debug"; - } - case CONTINUE: - return "Continue"; - case TOGGLE_BREAKPOINT: - return "Toggle Breakpoint"; - case STEP: - if (shift) { - return "Step Into"; - } else { - return "Step"; - } - case TOGGLE_VAR_INSPECTOR: - return "Variable Inspector"; - } - return null; - } - - - /** - * Event handler called when a toolbar button is clicked. - * @param e the mouse event - * @param idx index (i.e. position) of the toolbar button clicked - */ - @Override - public void handlePressed(MouseEvent e, int idx) { - boolean shift = e.isShiftDown(); - DebugEditor deditor = (DebugEditor) editor; - int id = buttonId(idx); // convert index/position to button id - - switch (id) { -// case DebugToolbar.RUN: -// super.handlePressed(e, JavaToolbar.RUN); -// break; - case STOP: - Logger.getLogger(DebugToolbar.class.getName()).log(Level.INFO, "Invoked 'Stop' toolbar button"); - super.handlePressed(e, JavaToolbar.STOP); - break; - case NEW: - Logger.getLogger(DebugToolbar.class.getName()).log(Level.INFO, "Invoked 'New' toolbar button"); - super.handlePressed(e, JavaToolbar.NEW); - break; - case OPEN: - Logger.getLogger(DebugToolbar.class.getName()).log(Level.INFO, "Invoked 'Open' toolbar button"); - super.handlePressed(e, JavaToolbar.OPEN); - break; - case SAVE: - Logger.getLogger(DebugToolbar.class.getName()).log(Level.INFO, "Invoked 'Save' toolbar button"); - super.handlePressed(e, JavaToolbar.SAVE); - break; - case EXPORT: - Logger.getLogger(DebugToolbar.class.getName()).log(Level.INFO, "Invoked 'Export' toolbar button"); - super.handlePressed(e, JavaToolbar.EXPORT); - break; - case DEBUG: - deditor.handleStop(); // Close any running sketches - if (shift) { - Logger.getLogger(DebugToolbar.class.getName()).log(Level.INFO, "Invoked 'Run' toolbar button"); - deditor.handleRun(); - } else { - Logger.getLogger(DebugToolbar.class.getName()).log(Level.INFO, "Invoked 'Debug' toolbar button"); - deditor.dbg.startDebug(); - } - break; - case CONTINUE: - Logger.getLogger(DebugToolbar.class.getName()).log(Level.INFO, "Invoked 'Continue' toolbar button"); - deditor.dbg.continueDebug(); - break; - case TOGGLE_BREAKPOINT: - Logger.getLogger(DebugToolbar.class.getName()).log(Level.INFO, "Invoked 'Toggle Breakpoint' toolbar button"); - deditor.dbg.toggleBreakpoint(); - break; - case STEP: - if (shift) { - Logger.getLogger(DebugToolbar.class.getName()).log(Level.INFO, "Invoked 'Step Into' toolbar button"); - deditor.dbg.stepInto(); - } else { - Logger.getLogger(DebugToolbar.class.getName()).log(Level.INFO, "Invoked 'Step' toolbar button"); - deditor.dbg.stepOver(); - } - break; -// case STEP_INTO: -// deditor.dbg.stepInto(); -// break; -// case STEP_OUT: -// deditor.dbg.stepOut(); -// break; - case TOGGLE_VAR_INSPECTOR: - Logger.getLogger(DebugToolbar.class.getName()).log(Level.INFO, "Invoked 'Variable Inspector' toolbar button"); - deditor.toggleVariableInspector(); - break; - } - } - - - /** - * Activate (light up) a button. - * @param id the button id - */ - @Override - public void activate(int id) { - //System.out.println("activate button idx: " + buttonIndex(id)); - super.activate(buttonIndex(id)); - } - - - /** - * Set a button to be inactive. - * @param id the button id - */ - @Override - public void deactivate(int id) { - //System.out.println("deactivate button idx: " + buttonIndex(id)); - super.deactivate(buttonIndex(id)); - } - - - /** - * Get button position (index) from it's id. - * @param buttonId the button id - * ({@link #RUN}, {@link #DEBUG}, {@link #CONTINUE}), {@link #STEP}, ...) - * @return the button index - */ - protected int buttonIndex(int buttonId) { - for (int i = 0; i < buttonSequence.length; i++) { - if (buttonSequence[i] == buttonId) { - return i; - } - } - return -1; - } - - - /** - * Get the button id from its position (index). - * @param buttonIdx the button index - * @return the button id - * ({@link #RUN}, {@link #DEBUG}, {@link #CONTINUE}), {@link #STEP}, ...) - */ - protected int buttonId(int buttonIdx) { - return buttonSequence[buttonIdx]; - } -} diff --git a/experimental/src/processing/mode/experimental/Debugger.java b/experimental/src/processing/mode/experimental/Debugger.java deleted file mode 100755 index 758f6cfae..000000000 --- a/experimental/src/processing/mode/experimental/Debugger.java +++ /dev/null @@ -1,1364 +0,0 @@ -/* - * Copyright (C) 2012 Martin Leopold - * - * This program is free software; you can redistribute it and/or modify it under - * the terms of the GNU General Public License as published by the Free Software - * Foundation; either version 2 of the License, or (at your option) any later - * version. - * - * This program 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 for more - * details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., 59 Temple - * Place - Suite 330, Boston, MA 02111-1307, USA. - */ -package processing.mode.experimental; - -import com.sun.jdi.*; -import com.sun.jdi.event.*; -import com.sun.jdi.request.*; -import java.io.*; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Set; -import java.util.logging.Level; -import java.util.logging.Logger; -import javax.swing.JTree; // needed for javadocs -import javax.swing.tree.DefaultMutableTreeNode; -import processing.app.Sketch; -import processing.app.SketchCode; - -/** - * Main controller class for debugging mode. Mainly works with DebugEditor as - * the corresponding "view". Uses DebugRunner to launch a VM. - * - * @author Martin Leopold - */ -public class Debugger implements VMEventListener { - - protected DebugEditor editor; // editor window, acting as main view - protected DebugRunner runtime; // the runtime, contains debuggee VM - protected boolean started = false; // debuggee vm has started, VMStartEvent received, main class loaded - protected boolean paused = false; // currently paused at breakpoint or step - protected ThreadReference currentThread; // thread the last breakpoint or step occured in - protected String mainClassName; // name of the main class that's currently being debugged - protected ReferenceType mainClass; // the debuggee's main class - protected Set classes = new HashSet(); // holds all loaded classes in the debuggee VM - protected List classLoadListeners = new ArrayList(); // listeners for class load events - protected String srcPath; // path to the src folder of the current build - protected List breakpoints = new ArrayList(); // list of current breakpoints - protected StepRequest requestedStep; // the step request we are currently in, or null if not in a step - protected Map runtimeLineChanges = new HashMap(); // maps line number changes at runtime (orig -> changed) - protected Set runtimeTabsTracked = new HashSet(); // contains tab filenames which already have been tracked for runtime changes - - /** - * Construct a Debugger object. - * - * @param editor The Editor that will act as primary view - */ - public Debugger(DebugEditor editor) { - this.editor = editor; - } - - /** - * Access the VM. - * - * @return the virtual machine object or null if not available. - */ - public VirtualMachine vm() { - if (runtime != null) { - return runtime.vm(); - } else { - return null; - } - } - - /** - * Access the editor associated with this debugger. - * - * @return the editor object - */ - public DebugEditor editor() { - return editor; - } - - /** - * Retrieve the main class of the debuggee VM. - * - * @return the main classes {@link ReferenceType} or null if the debugger is - * not started. - */ - public ReferenceType getMainClass() { - if (isStarted()) { - return mainClass; - } else { - return null; - } - - } - - /** - * Get the {@link ReferenceType} for a class name. - * - * @param name the class name - * @return the {@link ReferenceType} or null if not found (e.g. not yet - * loaded) - */ - public ReferenceType getClass(String name) { - if (name == null) { - return null; - } - if (name.equals(mainClassName)) { - return mainClass; - } - for (ReferenceType rt : classes) { - if (rt.name().equals(name)) { - return rt; - } - } - return null; - } - - /** - * Add a class load listener. Will be notified when a class is loaded in the - * debuggee VM. - * - * @param listener the {@link ClassLoadListener} - */ - public void addClassLoadListener(ClassLoadListener listener) { - classLoadListeners.add(listener); - } - - /** - * Remove a class load listener. Cease to be notified when classes are - * loaded in the debuggee VM. - * - * @param listener {@link ClassLoadListener} - */ - public void removeClassLoadListener(ClassLoadListener listener) { - classLoadListeners.remove(listener); - } - - /** - * Start a debugging session. Builds the sketch and launches a VM to run it. - * VM starts suspended. Should produce a VMStartEvent. - */ - public synchronized void startDebug() { - //stopDebug(); // stop any running sessions - if (isStarted()) { - return; // do nothing - } - - // we are busy now - editor.statusBusy(); - - // clear console - editor.clearConsole(); - - // clear variable inspector (also resets expanded states) - editor.variableInspector().reset(); - - // load edits into sketch obj, etc... - editor.prepareRun(); - - editor.toolbar().activate(DebugToolbar.DEBUG); // after prepareRun, since this removes highlights - - try { - Sketch sketch = editor.getSketch(); - DebugBuild build = new DebugBuild(sketch); - - Logger.getLogger(Debugger.class.getName()).log(Level.INFO, "building sketch: {0}", sketch.getName()); - //LineMapping.addLineNumbers(sketch); // annotate - mainClassName = build.build(false); - //LineMapping.removeLineNumbers(sketch); // annotate - Logger.getLogger(Debugger.class.getName()).log(Level.INFO, "class: {0}", mainClassName); - - // folder with assembled/preprocessed src - srcPath = build.getSrcFolder().getPath(); - Logger.getLogger(Debugger.class.getName()).log(Level.INFO, "build src: {0}", srcPath); - // folder with compiled code (.class files) - Logger.getLogger(Debugger.class.getName()).log(Level.INFO, "build bin: {0}", build.getBinFolder().getPath()); - - if (mainClassName != null) { - // generate the source line mapping - //lineMap = LineMapping.generateMapping(srcPath + File.separator + mainClassName + ".java"); - - Logger.getLogger(Debugger.class.getName()).log(Level.INFO, "launching debuggee runtime"); - runtime = new DebugRunner(build, editor); - VirtualMachine vm = runtime.launch(); // non-blocking - if (vm == null) { - Logger.getLogger(Debugger.class.getName()).log(Level.SEVERE, "error 37: launch failed"); - } - - // start receiving vm events - VMEventReader eventThread = new VMEventReader(vm.eventQueue(), this); - eventThread.start(); - - //return runtime; - - /* - * // launch runner in new thread new Thread(new Runnable() { - * - * @Override public void run() { runtime.launch(false); // this - * blocks until finished } }).start(); return runtime; - */ - - startTrackingLineChanges(); - editor.statusBusy(); - } - } catch (Exception e) { - editor.statusError(e); - } - } - - /** - * End debugging session. Stops and disconnects VM. Should produce - * VMDisconnectEvent. - */ - public synchronized void stopDebug() { - editor.variableInspector().lock(); - if (runtime != null) { - Logger.getLogger(Debugger.class.getName()).log(Level.INFO, "closing runtime"); - runtime.close(); - runtime = null; - //build = null; - classes.clear(); - // need to clear highlight here because, VMDisconnectedEvent seems to be unreliable. TODO: likely synchronization problem - editor.clearCurrentLine(); - } - stopTrackingLineChanges(); - started = false; - editor.toolbar().deactivate(DebugToolbar.DEBUG); - editor.toolbar().deactivate(DebugToolbar.CONTINUE); - editor.toolbar().deactivate(DebugToolbar.STEP); - editor.statusEmpty(); - } - - /** - * Resume paused debugging session. Resumes VM. - */ - public synchronized void continueDebug() { - editor.toolbar().activate(DebugToolbar.CONTINUE); - editor.variableInspector().lock(); - //editor.clearSelection(); - //clearHighlight(); - editor.clearCurrentLine(); - if (!isStarted()) { - startDebug(); - } else if (isPaused()) { - runtime.vm().resume(); - paused = false; - editor.statusBusy(); - } - } - - /** - * Step through source code lines. - * - * @param stepDepth the step depth ({@link StepRequest#STEP_OVER}, - * {@link StepRequest#STEP_INTO} or {@link StepRequest#STEP_OUT}) - */ - protected void step(int stepDepth) { - if (!isStarted()) { - startDebug(); - } else if (isPaused()) { - editor.variableInspector().lock(); - editor.toolbar().activate(DebugToolbar.STEP); - - // use global to mark that there is a step request pending - requestedStep = runtime.vm().eventRequestManager().createStepRequest(currentThread, StepRequest.STEP_LINE, stepDepth); - requestedStep.addCountFilter(1); // valid for one step only - requestedStep.enable(); - paused = false; - runtime.vm().resume(); - editor.statusBusy(); - } - } - - /** - * Step over current statement. - */ - public synchronized void stepOver() { - step(StepRequest.STEP_OVER); - } - - /** - * Step into current statement. - */ - public synchronized void stepInto() { - step(StepRequest.STEP_INTO); - } - - /** - * Step out of current function. - */ - public synchronized void stepOut() { - step(StepRequest.STEP_OUT); - } - - /** - * Print the current stack trace. - */ - public synchronized void printStackTrace() { - if (isStarted()) { - printStackTrace(currentThread); - } - } - - /** - * Print local variables. Outputs type, name and value of each variable. - */ - public synchronized void printLocals() { - if (isStarted()) { - printLocalVariables(currentThread); - } - } - - /** - * Print fields of current {@code this}-object. Outputs type, name and value - * of each field. - */ - public synchronized void printThis() { - if (isStarted()) { - printThis(currentThread); - } - } - - /** - * Print a source code snippet of the current location. - */ - public synchronized void printSource() { - if (isStarted()) { - printSourceLocation(currentThread); - } - } - - /** - * Set a breakpoint on the current line. - */ - public synchronized void setBreakpoint() { - setBreakpoint(editor.getCurrentLineID()); - } - - /** - * Set a breakpoint on a line in the current tab. - * - * @param lineIdx the line index (0-based) of the current tab to set the - * breakpoint on - */ - public synchronized void setBreakpoint(int lineIdx) { - setBreakpoint(editor.getLineIDInCurrentTab(lineIdx)); - } - - /** - * Set a breakpoint. - * - * @param line the line id to set the breakpoint on - */ - public synchronized void setBreakpoint(LineID line) { - // do nothing if we are kinda busy - if (isStarted() && !isPaused()) { - return; - } - // do nothing if there already is a breakpoint on this line - if (hasBreakpoint(line)) { - return; - } - breakpoints.add(new LineBreakpoint(line, this)); - Logger.getLogger(Debugger.class.getName()).log(Level.INFO, "set breakpoint on line {0}", line); - } - - /** - * Remove a breakpoint from the current line (if set). - */ - public synchronized void removeBreakpoint() { - removeBreakpoint(editor.getCurrentLineID().lineIdx()); - } - - /** - * Remove a breakpoint from a line in the current tab. - * - * @param lineIdx the line index (0-based) in the current tab to remove the - * breakpoint from - */ - protected void removeBreakpoint(int lineIdx) { - // do nothing if we are kinda busy - if (isBusy()) { - return; - } - - LineBreakpoint bp = breakpointOnLine(editor.getLineIDInCurrentTab(lineIdx)); - if (bp != null) { - bp.remove(); - breakpoints.remove(bp); - Logger.getLogger(Debugger.class.getName()).log(Level.INFO, "removed breakpoint {0}", bp); - } - } - - /** - * Remove all breakpoints. - */ - public synchronized void clearBreakpoints() { - //TODO: handle busy-ness correctly - if (isBusy()) { - Logger.getLogger(Debugger.class.getName()).log(Level.WARNING, "busy"); - return; - } - - for (LineBreakpoint bp : breakpoints) { - bp.remove(); - } - breakpoints.clear(); - } - - /** - * Clear breakpoints in a specific tab. - * - * @param tabFilename the tab's file name - */ - public synchronized void clearBreakpoints(String tabFilename) { - //TODO: handle busy-ness correctly - if (isBusy()) { - Logger.getLogger(Debugger.class.getName()).log(Level.WARNING, "busy"); - return; - } - - Iterator i = breakpoints.iterator(); - while (i.hasNext()) { - LineBreakpoint bp = i.next(); - if (bp.lineID().fileName().equals(tabFilename)) { - bp.remove(); - i.remove(); - } - } - } - - /** - * Get the breakpoint on a certain line, if set. - * - * @param line the line to get the breakpoint from - * @return the breakpoint, or null if no breakpoint is set on the specified - * line. - */ - protected LineBreakpoint breakpointOnLine(LineID line) { - for (LineBreakpoint bp : breakpoints) { - if (bp.isOnLine(line)) { - return bp; - } - } - return null; - } - - /** - * Toggle a breakpoint on the current line. - */ - public synchronized void toggleBreakpoint() { - toggleBreakpoint(editor.getCurrentLineID().lineIdx()); - } - - /** - * Toggle a breakpoint on a line in the current tab. - * - * @param lineIdx the line index (0-based) in the current tab - */ - public synchronized void toggleBreakpoint(int lineIdx) { - LineID line = editor.getLineIDInCurrentTab(lineIdx); - if (!hasBreakpoint(line)) { - setBreakpoint(line.lineIdx()); - } else { - removeBreakpoint(line.lineIdx()); - } - } - - /** - * Check if there's a breakpoint on a particular line. - * - * @param line the line id - * @return true if a breakpoint is set on the given line, otherwise false - */ - protected boolean hasBreakpoint(LineID line) { - LineBreakpoint bp = breakpointOnLine(line); - return bp != null; - } - - /** - * Print a list of currently set breakpoints. - */ - public synchronized void listBreakpoints() { - if (breakpoints.isEmpty()) { - System.out.println("no breakpoints"); - } else { - System.out.println("line breakpoints:"); - for (LineBreakpoint bp : breakpoints) { - System.out.println(bp); - } - } - } - - /** - * Retrieve a list of breakpoint in a particular tab. - * - * @param tabFilename the tab's file name - * @return the list of breakpoints in the given tab - */ - public synchronized List getBreakpoints(String tabFilename) { - List list = new ArrayList(); - for (LineBreakpoint bp : breakpoints) { - if (bp.lineID().fileName().equals(tabFilename)) { - list.add(bp); - } - } - return list; - } - - /** - * Callback for VM events. Will be called from another thread. - * ({@link VMEventReader}) - * - * @param es Incoming set of events from VM - */ - @Override - public synchronized void vmEvent(EventSet es) { - for (Event e : es) { - Logger.getLogger(Debugger.class.getName()).log(Level.INFO, "*** VM Event: {0}", e.toString()); - if (e instanceof VMStartEvent) { - //initialThread = ((VMStartEvent) e).thread(); -// ThreadReference t = ((VMStartEvent) e).thread(); - //printStackTrace(t); - - // break on main class load - Logger.getLogger(Debugger.class.getName()).log(Level.INFO, "requesting event on main class load: {0}", mainClassName); - ClassPrepareRequest mainClassPrepare = runtime.vm().eventRequestManager().createClassPrepareRequest(); - mainClassPrepare.addClassFilter(mainClassName); - mainClassPrepare.enable(); - - // break on loading custom classes - for (SketchCode tab : editor.getSketch().getCode()) { - if (tab.isExtension("java")) { - Logger.getLogger(Debugger.class.getName()).log(Level.INFO, "requesting event on class load: {0}", tab.getPrettyName()); - ClassPrepareRequest customClassPrepare = runtime.vm().eventRequestManager().createClassPrepareRequest(); - customClassPrepare.addClassFilter(tab.getPrettyName()); - customClassPrepare.enable(); - } - } - - runtime.vm().resume(); - } else if (e instanceof ClassPrepareEvent) { - ClassPrepareEvent ce = (ClassPrepareEvent) e; - ReferenceType rt = ce.referenceType(); - currentThread = ce.thread(); - paused = true; // for now we're paused - - if (rt.name().equals(mainClassName)) { - //printType(rt); - mainClass = rt; - Logger.getLogger(Debugger.class.getName()).log(Level.INFO, "main class load: {0}", rt.name()); - started = true; // now that main class is loaded, we're started - } else { - classes.add(rt); // save loaded classes - Logger.getLogger(Debugger.class.getName()).log(Level.INFO, "class load: {0}", rt.name()); - } - - // notify listeners - for (ClassLoadListener listener : classLoadListeners) { - if (listener != null) { - listener.classLoaded(rt); - } - } - - paused = false; // resuming now - runtime.vm().resume(); - } else if (e instanceof BreakpointEvent) { - BreakpointEvent be = (BreakpointEvent) e; - currentThread = be.thread(); // save this thread -// BreakpointRequest br = (BreakpointRequest) be.request(); - - //printSourceLocation(currentThread); - updateVariableInspector(currentThread); // this is already on the EDT - final LineID newCurrentLine = locationToLineID(be.location()); - javax.swing.SwingUtilities.invokeLater(new Runnable() { - @Override - public void run() { - editor.setCurrentLine(newCurrentLine); - editor.toolbar().deactivate(DebugToolbar.STEP); - editor.toolbar().deactivate(DebugToolbar.CONTINUE); - } - }); - - // hit a breakpoint during a step, need to cancel the step. - if (requestedStep != null) { - runtime.vm().eventRequestManager().deleteEventRequest(requestedStep); - requestedStep = null; - } - - // fix canvas update issue - // TODO: is this a good solution? - resumeOtherThreads(currentThread); - - paused = true; - editor.statusHalted(); - } else if (e instanceof StepEvent) { - StepEvent se = (StepEvent) e; - currentThread = se.thread(); - - //printSourceLocation(currentThread); - updateVariableInspector(currentThread); // this is already on the EDT - final LineID newCurrentLine = locationToLineID(se.location()); - javax.swing.SwingUtilities.invokeLater(new Runnable() { - @Override - public void run() { - editor.setCurrentLine(newCurrentLine); - editor.toolbar().deactivate(DebugToolbar.STEP); - editor.toolbar().deactivate(DebugToolbar.CONTINUE); - } - }); - - // delete the steprequest that triggered this step so new ones can be placed (only one per thread) - EventRequestManager mgr = runtime.vm().eventRequestManager(); - mgr.deleteEventRequest(se.request()); - requestedStep = null; // mark that there is no step request pending - paused = true; - editor.statusHalted(); - - // disallow stepping into invisible lines - if (!locationIsVisible(se.location())) { - stepOutIntoViewOrContinue(); // TODO: this leads to stepping, should it run on the EDT? - } - } else if (e instanceof VMDisconnectEvent) { -// started = false; -// // clear line highlight -// editor.clearCurrentLine(); - stopDebug(); - } else if (e instanceof VMDeathEvent) { - started = false; - editor.statusEmpty(); - } - } - } - - /** - * Check whether a location corresponds to a code line in the editor. - * - * @param l the location - * @return true if the location corresponds to a line in the editor - */ - protected boolean locationIsVisible(Location l) { - return locationToLineID(l) != null; - } - - /** - * Step out if this results in a visible location, otherwise continue. - */ - protected void stepOutIntoViewOrContinue() { - try { - List frames = currentThread.frames(); - if (frames.size() > 1) { - if (locationIsVisible(frames.get(1).location())) { - //System.out.println("stepping out to: " + locationToString(frames.get(1).location())); - stepOut(); - return; - } - } - continueDebug(); - -// //Step out to the next visible location on the stack frame -// if (thread.frames(i, i1)) -// for (StackFrame f : thread.frames()) { -// Location l = f.location(); -// if (locationIsVisible(l)) { -// System.out.println("need to step out to: " + locationToString(l)); -// } -// } - } catch (IncompatibleThreadStateException ex) { - Logger.getLogger(Debugger.class.getName()).log(Level.SEVERE, null, ex); - } - } - - /** - * Check whether a debugging session is running. i.e. the debugger is - * connected to a debuggee VM, VMStartEvent has been received and main class - * is loaded. - * - * @return true if the debugger is started. - */ - public synchronized boolean isStarted() { - return started && runtime != null && runtime.vm() != null; - } - - /** - * Check whether the debugger is paused. i.e. it is currently suspended at a - * breakpoint or step. - * - * @return true if the debugger is paused, false otherwise or if not started - * ({@link #isStarted()}) - */ - public synchronized boolean isPaused() { - return isStarted() && paused && currentThread != null && currentThread.isSuspended(); - } - - /** - * Check whether the debugger is currently busy. i.e. running (not - * suspended). - * - * @return true if the debugger is currently running and not suspended. - */ - public synchronized boolean isBusy() { - return isStarted() && !isPaused(); - } - - /** - * Print call stack trace of a thread. Only works on suspended threads. - * - * @param t suspended thread to print stack trace of - */ - protected void printStackTrace(ThreadReference t) { - if (!t.isSuspended()) { - return; - } - try { - System.out.println("stack trace for thread " + t.name() + ":"); - int i = 0; - for (StackFrame f : t.frames()) { -// Location l = f.location(); - System.out.println(i++ + ": " + f.toString()); - } - } catch (IncompatibleThreadStateException ex) { - Logger.getLogger(Debugger.class.getName()).log(Level.SEVERE, null, ex); - } - } - - /** - * Resume all other threads except the one given as parameter. Useful e.g. - * to just keep the thread suspended a breakpoint occurred in. - * - * @param t the thread not to resume - */ - protected void resumeOtherThreads(ThreadReference t) { - if (!isStarted()) { - return; - } - for (ThreadReference other : vm().allThreads()) { - if (!other.equals(t) && other.isSuspended()) { - other.resume(); - } - } - } - - /** - * Print info about all current threads. Includes name, status, isSuspended, - * isAtBreakpoint. - */ - public synchronized void printThreads() { - if (!isPaused()) { - return; - } - System.out.println("threads:"); - for (ThreadReference t : vm().allThreads()) { - printThread(t); - } - } - - /** - * Print info about a thread. Includes name, status, isSuspended, - * isAtBreakpoint. - * - * @param t the thread to print info about - */ - protected void printThread(ThreadReference t) { - System.out.println(t.name()); - System.out.println(" is suspended: " + t.isSuspended()); - System.out.println(" is at breakpoint: " + t.isAtBreakpoint()); - System.out.println(" status: " + threadStatusToString(t.status())); - } - - /** - * Convert a status code returned by {@link ThreadReference#status() } to a - * human readable form. - * - * @param status {@link ThreadReference#THREAD_STATUS_MONITOR}, - * {@link ThreadReference#THREAD_STATUS_NOT_STARTED}, - * {@link ThreadReference#THREAD_STATUS_RUNNING}, - * {@link ThreadReference#THREAD_STATUS_SLEEPING}, - * {@link ThreadReference#THREAD_STATUS_UNKNOWN}, - * {@link ThreadReference#THREAD_STATUS_WAIT} or - * {@link ThreadReference#THREAD_STATUS_ZOMBIE} - * @return String containing readable status code. - */ - protected String threadStatusToString(int status) { - switch (status) { - case ThreadReference.THREAD_STATUS_MONITOR: - return "THREAD_STATUS_MONITOR"; - case ThreadReference.THREAD_STATUS_NOT_STARTED: - return "THREAD_STATUS_NOT_STARTED"; - case ThreadReference.THREAD_STATUS_RUNNING: - return "THREAD_STATUS_RUNNING"; - case ThreadReference.THREAD_STATUS_SLEEPING: - return "THREAD_STATUS_SLEEPING"; - case ThreadReference.THREAD_STATUS_UNKNOWN: - return "THREAD_STATUS_UNKNOWN"; - case ThreadReference.THREAD_STATUS_WAIT: - return "THREAD_STATUS_WAIT"; - case ThreadReference.THREAD_STATUS_ZOMBIE: - return "THREAD_STATUS_ZOMBIE"; - default: - return ""; - } - } - - /** - * Print local variables on a suspended thread. Takes the topmost stack - * frame and lists all local variables and their values. - * - * @param t suspended thread - */ - protected void printLocalVariables(ThreadReference t) { - if (!t.isSuspended()) { - return; - } - try { - if (t.frameCount() == 0) { - System.out.println("call stack empty"); - } else { - StackFrame sf = t.frame(0); - List locals = sf.visibleVariables(); - if (locals.isEmpty()) { - System.out.println("no local variables"); - return; - } - for (LocalVariable lv : locals) { - System.out.println(lv.typeName() + " " + lv.name() + " = " + sf.getValue(lv)); - } - } - } catch (IncompatibleThreadStateException ex) { - Logger.getLogger(Debugger.class.getName()).log(Level.SEVERE, null, ex); - } catch (AbsentInformationException ex) { - System.out.println("local variable information not available"); - } - } - - /** - * Update variable inspector window. Displays local variables and this - * fields. - * - * @param t suspended thread to retrieve locals and this - */ - protected void updateVariableInspector(ThreadReference t) { - if (!t.isSuspended()) { - return; - } - try { - if (t.frameCount() == 0) { - // TODO: needs to be handled in a better way: - Logger.getLogger(Debugger.class.getName()).log(Level.WARNING, "call stack empty"); - } else { - final VariableInspector vi = editor.variableInspector(); - // first get data - final List stackTrace = getStackTrace(t); - final List locals = getLocals(t, 0); - final String currentLocation = currentLocation(t); - final List thisFields = getThisFields(t, 0, true); - final List declaredThisFields = getThisFields(t, 0, false); - final String thisName = thisName(t); - // now update asynchronously - javax.swing.SwingUtilities.invokeLater(new Runnable() { - @Override - public void run() { - //System.out.println("updating vi. from EDT: " + javax.swing.SwingUtilities.isEventDispatchThread()); - vi.updateCallStack(stackTrace, "Call Stack"); - vi.updateLocals(locals, "Locals at " + currentLocation); - vi.updateThisFields(thisFields, "Class " + thisName); - vi.updateDeclaredThisFields(declaredThisFields, "Class " + thisName); - vi.unlock(); // need to do this before rebuilding, otherwise we get these ... dots in the labels - vi.rebuild(); - } - }); - } - } catch (IncompatibleThreadStateException ex) { - Logger.getLogger(Debugger.class.getName()).log(Level.SEVERE, null, ex); - } - } - - /** - * Get the class name of the current this object in a suspended thread. - * - * @param t a suspended thread - * @return the class name of this - */ - protected String thisName(ThreadReference t) { - try { - if (!t.isSuspended() || t.frameCount() == 0) { - return ""; - } - return t.frame(0).thisObject().referenceType().name(); - } catch (IncompatibleThreadStateException ex) { - Logger.getLogger(Debugger.class.getName()).log(Level.SEVERE, null, ex); - return ""; - } - } - - /** - * Get a description of the current location in a suspended thread. Format: - * class.method:translated_line_number - * - * @param t a suspended thread - * @return descriptive string for the given location - */ - protected String currentLocation(ThreadReference t) { - try { - if (!t.isSuspended() || t.frameCount() == 0) { - return ""; - } - return locationToString(t.frame(0).location()); - } catch (IncompatibleThreadStateException ex) { - Logger.getLogger(Debugger.class.getName()).log(Level.SEVERE, null, ex); - return ""; - } - } - - /** - * Get a string describing a location. Format: - * class.method:translated_line_number - * - * @param l a location - * @return descriptive string for the given location - */ - protected String locationToString(Location l) { - LineID line = locationToLineID(l); - int lineNumber; - if (line != null) { - lineNumber = line.lineIdx() + 1; - } else { - lineNumber = l.lineNumber(); - } - return l.declaringType().name() + "." + l.method().name() + ":" + lineNumber; - } - - /** - * Compile a list of current locals usable for insertion into a - * {@link JTree}. Recursively resolves object references. - * - * @param t the suspended thread to get locals for - * @param depth how deep to resolve nested object references. 0 will not - * resolve nested objects. - * @return the list of current locals - */ - protected List getLocals(ThreadReference t, int depth) { - //System.out.println("getting locals"); - List vars = new ArrayList(); - try { - if (t.frameCount() > 0) { - StackFrame sf = t.frame(0); - for (LocalVariable lv : sf.visibleVariables()) { - //System.out.println("local var: " + lv.name()); - Value val = sf.getValue(lv); - VariableNode var = new LocalVariableNode(lv.name(), lv.typeName(), val, lv, sf); - if (depth > 0) { - var.addChildren(getFields(val, depth - 1, true)); - } - vars.add(var); - } - } - } catch (IncompatibleThreadStateException ex) { - Logger.getLogger(Debugger.class.getName()).log(Level.SEVERE, null, ex); - } catch (AbsentInformationException ex) { - Logger.getLogger(Debugger.class.getName()).log(Level.WARNING, "local variable information not available", ex); - } - return vars; - } - - /** - * Compile a list of fields in the current this object usable for insertion - * into a {@link JTree}. Recursively resolves object references. - * - * @param t the suspended thread to get locals for - * @param depth how deep to resolve nested object references. 0 will not - * resolve nested objects. - * @return the list of fields in the current this object - */ - protected List getThisFields(ThreadReference t, int depth, boolean includeInherited) { - //System.out.println("getting this"); - try { - if (t.frameCount() > 0) { - StackFrame sf = t.frame(0); - ObjectReference thisObj = sf.thisObject(); - return getFields(thisObj, depth, includeInherited); - } - } catch (IncompatibleThreadStateException ex) { - Logger.getLogger(Debugger.class.getName()).log(Level.SEVERE, null, ex); - } - return new ArrayList(); - } - - /** - * Recursively get the fields of a {@link Value} for insertion into a - * {@link JTree}. - * - * @param value must be an instance of {@link ObjectReference} - * @param depth the current depth - * @param maxDepth the depth to stop at (inclusive) - * @return list of child fields of the given value - */ - protected List getFields(Value value, int depth, int maxDepth, boolean includeInherited) { - // remember: Value <- ObjectReference, ArrayReference - List vars = new ArrayList(); - if (depth <= maxDepth) { - if (value instanceof ArrayReference) { - return getArrayFields((ArrayReference) value); - } else if (value instanceof ObjectReference) { - ObjectReference obj = (ObjectReference) value; - // get the fields of this object - List fields = includeInherited ? obj.referenceType().visibleFields() : obj.referenceType().fields(); - for (Field field : fields) { - Value val = obj.getValue(field); // get the value, may be null - VariableNode var = new FieldNode(field.name(), field.typeName(), val, field, obj); - // recursively add children - if (val != null) { - var.addChildren(getFields(val, depth + 1, maxDepth, includeInherited)); - } - vars.add(var); - } - } - } - return vars; - } - - /** - * Recursively get the fields of a {@link Value} for insertion into a - * {@link JTree}. - * - * @param value must be an instance of {@link ObjectReference} - * @param maxDepth max recursion depth. 0 will give only direct children - * @return list of child fields of the given value - */ - protected List getFields(Value value, int maxDepth, boolean includeInherited) { - return getFields(value, 0, maxDepth, includeInherited); - } - - /** - * Get the fields of an array for insertion into a {@link JTree}. - * - * @param array the array reference - * @return list of array fields - */ - protected List getArrayFields(ArrayReference array) { - List fields = new ArrayList(); - if (array != null) { - String arrayType = array.type().name(); - if (arrayType.endsWith("[]")) { - arrayType = arrayType.substring(0, arrayType.length() - 2); - } - int i = 0; - for (Value val : array.getValues()) { - VariableNode var = new ArrayFieldNode("[" + i + "]", arrayType, val, array, i); - fields.add(var); - i++; - } - } - return fields; - } - - /** - * Get the current call stack trace usable for insertion into a - * {@link JTree}. - * - * @param t the suspended thread to retrieve the call stack from - * @return call stack as list of {@link DefaultMutableTreeNode}s - */ - protected List getStackTrace(ThreadReference t) { - List stack = new ArrayList(); - try { -// int i = 0; - for (StackFrame f : t.frames()) { - stack.add(new DefaultMutableTreeNode(locationToString(f.location()))); - } - } catch (IncompatibleThreadStateException ex) { - Logger.getLogger(Debugger.class.getName()).log(Level.SEVERE, null, ex); - } - return stack; - } - - /** - * Print visible fields of current "this" object on a suspended thread. - * Prints type, name and value. - * - * @param t suspended thread - */ - protected void printThis(ThreadReference t) { - if (!t.isSuspended()) { - return; - } - try { - if (t.frameCount() == 0) { - // TODO: needs to be handled in a better way - System.out.println("call stack empty"); - } else { - StackFrame sf = t.frame(0); - ObjectReference thisObject = sf.thisObject(); - if (this != null) { - ReferenceType type = thisObject.referenceType(); - System.out.println("fields in this (" + type.name() + "):"); - for (Field f : type.visibleFields()) { - System.out.println(f.typeName() + " " + f.name() + " = " + thisObject.getValue(f)); - } - } else { - System.out.println("can't get this (in native or static method)"); - } - } - } catch (IncompatibleThreadStateException ex) { - Logger.getLogger(Debugger.class.getName()).log(Level.SEVERE, null, ex); - } - } - - /** - * Print source code snippet of current location in a suspended thread. - * - * @param t suspended thread - */ - protected void printSourceLocation(ThreadReference t) { - try { - if (t.frameCount() == 0) { - // TODO: needs to be handled in a better way - System.out.println("call stack empty"); - } else { - Location l = t.frame(0).location(); // current stack frame location - printSourceLocation(l); - } - } catch (IncompatibleThreadStateException ex) { - Logger.getLogger(Debugger.class.getName()).log(Level.SEVERE, null, ex); - } - } - - /** - * Print source code snippet. - * - * @param l {@link Location} object to print source code for - */ - protected void printSourceLocation(Location l) { - try { - //System.out.println(l.sourceName() + ":" + l.lineNumber()); - System.out.println("in method " + l.method() + ":"); - System.out.println(getSourceLine(l.sourcePath(), l.lineNumber(), 2)); - - } catch (AbsentInformationException ex) { - Logger.getLogger(Debugger.class.getName()).log(Level.SEVERE, null, ex); - } - } - - /** - * Read a line from the given file in the builds src folder. 1-based i.e. - * first line has line no. 1 - * - * @param filePath - * @param lineNo - * @return the requested source line - */ - protected String getSourceLine(String filePath, int lineNo, int radius) { - if (lineNo == -1) { - Logger.getLogger(Debugger.class.getName()).log(Level.SEVERE, "invalid line number: {0}", lineNo); - return ""; - } - //System.out.println("getting line: " + lineNo); - File f = new File(srcPath + File.separator + filePath); - String output = ""; - try { - BufferedReader r = new BufferedReader(new FileReader(f)); - int i = 1; - //String line = ""; - while (i <= lineNo + radius) { - String line = r.readLine(); // line no. i - if (line == null) { - break; // end of file - } - if (i >= lineNo - radius) { - if (i > lineNo - radius) { - output += "\n"; // add newlines before all lines but the first - } - output += f.getName() + ":" + i + (i == lineNo ? " => " : " ") + line; - } - i++; - } - r.close(); - return output; - } catch (FileNotFoundException ex) { - //System.err.println(ex); - return f.getName() + ":" + lineNo; - } catch (IOException ex) { - Logger.getLogger(Debugger.class.getName()).log(Level.SEVERE, null, ex); - return ""; - } - } - - /** - * Print info about a ReferenceType. Prints class name, source file name, - * lists methods. - * - * @param rt the reference type to print out - */ - protected void printType(ReferenceType rt) { - System.out.println("ref.type: " + rt); - System.out.println("name: " + rt.name()); - try { - System.out.println("sourceName: " + rt.sourceName()); - } catch (AbsentInformationException ex) { - System.out.println("sourceName: unknown"); - } - System.out.println("methods:"); - for (Method m : rt.methods()) { - System.out.println(m.toString()); - } - } - - /** - * Translate a java source location to a sketch line id. - * - * @param l the location to translate - * @return the corresponding line id, or null if not found - */ - protected LineID locationToLineID(Location l) { - try { - //return lineMap.get(LineID.create(l.sourceName(), l.lineNumber() - 1)); - return javaToSketchLine(new LineID(l.sourceName(), l.lineNumber() - 1)); - - } catch (AbsentInformationException ex) { - Logger.getLogger(Debugger.class.getName()).log(Level.SEVERE, null, ex); - return null; - } - } - - /** - * Translate a line (index) from java space to sketch space. - * - * @param javaLine the java line id - * @return the corresponding sketch line id or null if failed to translate - */ - public LineID javaToSketchLine(LineID javaLine) { - Sketch sketch = editor.getSketch(); - - // it may belong to a pure java file created in the sketch - // try to find an exact filename match and check the extension - SketchCode tab = editor.getTab(javaLine.fileName()); - if (tab != null && tab.isExtension("java")) { - // can translate 1:1 - return originalToRuntimeLine(javaLine); - } - - // check if it is the preprocessed/assembled file for this sketch - // java file name needs to match the sketches filename - if (!javaLine.fileName().equals(sketch.getName() + ".java")) { - return null; - } - - // find the tab (.pde file) this line belongs to - // get the last tab that has an offset not greater than the java line number - for (int i = sketch.getCodeCount() - 1; i >= 0; i--) { - tab = sketch.getCode(i); - // ignore .java files - // the tab's offset must not be greater than the java line number - if (tab.isExtension("pde") && tab.getPreprocOffset() <= javaLine.lineIdx()) { - return originalToRuntimeLine(new LineID(tab.getFileName(), javaLine.lineIdx() - tab.getPreprocOffset())); - } - } - - return null; - } - - /** - * Get the runtime-changed line id for an original sketch line. Used to - * translate line numbers from the VM (which runs on the original line - * numbers) to their current (possibly changed) counterparts. - * - * @param line the original line id (at compile time) - * @return the changed version or the line given as parameter if not found - */ - protected LineID originalToRuntimeLine(LineID line) { - LineID transformed = runtimeLineChanges.get(line); - if (transformed == null) { - return line; - } - return transformed; - } - - /** - * Get the original line id for a sketch line that was changed at runtime. - * Used to translate line numbers from the UI at runtime (which can differ - * from the ones the VM runs on) to their original counterparts. - * - * @param line the (possibly) changed runtime line - * @return the original line or the line given as parameter if not found - */ - protected LineID runtimeToOriginalLine(LineID line) { - for (Entry entry : runtimeLineChanges.entrySet()) { - if (entry.getValue().equals(line)) { - return entry.getKey(); - } - } - return line; - } - - /** - * Translate a line (index) from sketch space to java space. - * - * @param sketchLine the sketch line id - * @return the corresponding java line id or null if failed to translate - */ - public LineID sketchToJavaLine(LineID sketchLine) { - sketchLine = runtimeToOriginalLine(sketchLine); // transform back to orig (before changes at runtime) - - // check if there is a tab for this line - SketchCode tab = editor.getTab(sketchLine.fileName()); - if (tab == null) { - return null; - } - - // check if the tab is a pure java file anyway - if (tab.isExtension("java")) { - // 1:1 translation - return sketchLine; - } - - // the java file has a name sketchname.java - // just add the tab's offset to get the java name - LineID javaLine = new LineID(editor.getSketch().getName() + ".java", sketchLine.lineIdx() + tab.getPreprocOffset()); - return javaLine; - } - - /** - * Start tracking all line changes (due to edits) in the current tab. - */ - // TODO: maybe move this to the editor? - protected void startTrackingLineChanges() { - SketchCode tab = editor.getSketch().getCurrentCode(); - if (runtimeTabsTracked.contains(tab.getFileName())) { - return; - } - - for (int i = 0; i < tab.getLineCount(); i++) { - LineID old = new LineID(tab.getFileName(), i); - LineID tracked = new LineID(tab.getFileName(), i); - tracked.startTracking(editor.currentDocument()); - runtimeLineChanges.put(old, tracked); - } - runtimeTabsTracked.add(tab.getFileName()); - //System.out.println("tracking tab: " + tab.getFileName()); - } - - /** - * Stop tracking line changes in all tabs. - */ - protected void stopTrackingLineChanges() { - //System.out.println("stop tracking line changes"); - for (LineID tracked : runtimeLineChanges.values()) { - tracked.stopTracking(); - } - runtimeLineChanges.clear(); - runtimeTabsTracked.clear(); - } -} diff --git a/experimental/src/processing/mode/experimental/ErrorBar.java b/experimental/src/processing/mode/experimental/ErrorBar.java deleted file mode 100755 index f92232cc3..000000000 --- a/experimental/src/processing/mode/experimental/ErrorBar.java +++ /dev/null @@ -1,384 +0,0 @@ -/* - Part of the XQMode project - https://github.com/Manindra29/XQMode - - Under Google Summer of Code 2012 - - http://www.google-melange.com/gsoc/homepage/google/gsoc2012 - - Copyright (C) 2012 Manindra Moharana - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License version 2 - as published by the Free Software Foundation. - - This program 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 for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software Foundation, - Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -package processing.mode.experimental; - -import java.awt.Color; -import java.awt.Cursor; -import java.awt.Dimension; -import java.awt.Graphics; -import java.awt.Graphics2D; -import java.awt.RenderingHints; -import java.awt.event.MouseAdapter; -import java.awt.event.MouseEvent; -import java.awt.event.MouseMotionListener; -import java.util.ArrayList; - -import javax.swing.JPanel; -import javax.swing.SwingWorker; - -import processing.app.Base; -import processing.app.SketchCode; - -/** - * The bar on the left of the text area which displays all errors as rectangles.
- *
- * All errors and warnings of a sketch are drawn on the bar, clicking on one, - * scrolls to the tab and location. Error messages displayed on hover. Markers - * are not in sync with the error line. Similar to eclipse's right error bar - * which displays the overall errors in a document - * - * @author Manindra Moharana <me@mkmoharana.com> - * - */ -public class ErrorBar extends JPanel { - /** - * Preferred height of the component - */ - protected int preferredHeight; - - /** - * Preferred height of the component - */ - protected int preferredWidth = 12; - - /** - * Height of marker - */ - public static final int errorMarkerHeight = 4; - - /** - * Color of Error Marker - */ - public Color errorColor = new Color(0xED2630); - - /** - * Color of Warning Marker - */ - public Color warningColor = new Color(0xFFC30E); - - /** - * Background color of the component - */ - public Color backgroundColor = new Color(0x2C343D); - - /** - * DebugEditor instance - */ - protected DebugEditor editor; - - /** - * ErrorCheckerService instance - */ - protected ErrorCheckerService errorCheckerService; - - /** - * Stores error markers displayed PER TAB along the error bar. - */ - protected ArrayList errorPoints = new ArrayList(); - - /** - * Stores previous list of error markers. - */ - protected ArrayList errorPointsOld = new ArrayList(); - - public void paintComponent(Graphics g) { - Graphics2D g2d = (Graphics2D) g; - g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, - RenderingHints.VALUE_ANTIALIAS_ON); - g.setColor(backgroundColor); - g.fillRect(0, 0, getWidth(), getHeight()); - - for (ErrorMarker emarker : errorPoints) { - if (emarker.type == ErrorMarker.Error) { - g.setColor(errorColor); - } else { - g.setColor(warningColor); - } - g.fillRect(2, emarker.y, (getWidth() - 3), errorMarkerHeight); - } - } - - public Dimension getPreferredSize() { - return new Dimension(preferredWidth, preferredHeight); - } - - public Dimension getMinimumSize() { - return getPreferredSize(); - } - - public ErrorBar(DebugEditor editor, int height, ExperimentalMode mode) { - this.editor = editor; - this.preferredHeight = height; - this.errorCheckerService = editor.errorCheckerService; - errorColor = mode.getThemeColor("errorbar.errorcolor", errorColor); - warningColor = mode - .getThemeColor("errorbar.warningcolor", warningColor); - backgroundColor = mode.getThemeColor("errorbar.backgroundcolor", - backgroundColor); - addListeners(); - } - - /** - * Update error markers in the error bar. - * - * @param problems - * - List of problems. - */ - synchronized public void updateErrorPoints(final ArrayList problems) { - - // NOTE TO SELF: ErrorMarkers are calculated for the present tab only - // Error Marker index in the arraylist is LOCALIZED for current tab. - // Also, need to do the update in the UI thread to prevent concurrency issues. - final int fheight = this.getHeight(); - SwingWorker worker = new SwingWorker() { - - protected Object doInBackground() throws Exception { - return null; - } - - protected void done() { - int totalLines = 0; - int currentTab = 0; - for (SketchCode sc : editor.getSketch().getCode()) { - if (sc.isExtension("pde")) { - try { - if (editor.getSketch().getCurrentCode().equals(sc)) { - // Adding + 1 to len because \n gets appended - // for each - // sketchcode extracted during processPDECode() - totalLines = Base.countLines(sc.getDocument() - .getText(0, - sc.getDocument().getLength())) + 1; - break; - } - } catch (Exception e) { - e.printStackTrace(); - } - } - currentTab++; - } - // System.out.println("Total lines: " + totalLines); - - errorPointsOld.clear(); - for (ErrorMarker marker : errorPoints) { - errorPointsOld.add(marker); - } - errorPoints.clear(); - - // Each problem.getSourceLine() will have an extra line added - // because of - // class declaration in the beginning - for (Problem problem : problems) { - if (problem.tabIndex == currentTab) { - // Ratio of error line to total lines - float y = problem.lineNumber / ((float) totalLines); - // Ratio multiplied by height of the error bar - y *= fheight - 15; // -15 is just a vertical offset - errorPoints.add(new ErrorMarker(problem, (int) y, - problem.isError() ? ErrorMarker.Error - : ErrorMarker.Warning)); - // System.out.println("Y: " + y); - } - } - - repaint(); - } - }; - - try { - worker.execute(); // I eat concurrency bugs for breakfast. - } catch (Exception exp) { - System.out.println("Errorbar update markers is slacking." - + exp.getMessage()); - // e.printStackTrace(); - } - } - - /** - * Check if new errors have popped up in the sketch since the last check - * - * @return true - if errors have changed - */ - public boolean errorPointsChanged() { - if (errorPointsOld.size() != errorPoints.size()) { - editor.getTextArea().repaint(); - // System.out.println("2 Repaint " + System.currentTimeMillis()); - return true; - } - - else { - for (int i = 0; i < errorPoints.size(); i++) { - if (errorPoints.get(i).y != errorPointsOld.get(i).y) { - editor.getTextArea().repaint(); - // System.out.println("3 Repaint " + - // System.currentTimeMillis()); - return true; - } - } - } - return false; - } - - /** - * Add various mouse listeners. - */ - protected void addListeners() { - - this.addMouseListener(new MouseAdapter() { - - // Find out which error/warning the user has clicked - // and then scroll to that - @SuppressWarnings("rawtypes") - @Override - public void mouseClicked(final MouseEvent e) { - SwingWorker worker = new SwingWorker() { - - protected Object doInBackground() throws Exception { - return null; - } - - protected void done() { - for (ErrorMarker eMarker : errorPoints) { - // -2 and +2 are extra allowance, clicks in the - // vicinity of the markers register that way - if (e.getY() >= eMarker.y - 2 - && e.getY() <= eMarker.y + 2 - + errorMarkerHeight) { - int currentTabErrorIndex = errorPoints - .indexOf(eMarker); - // System.out.println("Index: " + - // currentTabErrorIndex); - int currentTab = editor.getSketch() - .getCodeIndex( - editor.getSketch() - .getCurrentCode()); - - int totalErrorIndex = currentTabErrorIndex; - - for (int i = 0; i < errorCheckerService.problemsList - .size(); i++) { - Problem p = errorCheckerService.problemsList - .get(i); - if (p.tabIndex < currentTab) { - totalErrorIndex++; - } - if (p.tabIndex == currentTab) { - break; - } - } - errorCheckerService - .scrollToErrorLine(totalErrorIndex); - } - } - - } - }; - - try { - worker.execute(); - } catch (Exception exp) { - System.out.println("Errorbar mouseClicked is slacking." - + exp.getMessage()); - // e.printStackTrace(); - } - - } - }); - - // Tooltip on hover - this.addMouseMotionListener(new MouseMotionListener() { - - @SuppressWarnings("rawtypes") - @Override - public void mouseMoved(final MouseEvent e) { - // System.out.println(e); - SwingWorker worker = new SwingWorker() { - - protected Object doInBackground() throws Exception { - return null; - } - - protected void done() { - - for (ErrorMarker eMarker : errorPoints) { - if (e.getY() >= eMarker.y - 2 - && e.getY() <= eMarker.y + 2 - + errorMarkerHeight) { - // System.out.println("Index: " + - // errorPoints.indexOf(y)); - int currentTab = editor.getSketch() - .getCodeIndex( - editor.getSketch() - .getCurrentCode()); - int currentTabErrorCount = 0; - - for (int i = 0; i < errorPoints.size(); i++) { - Problem p = errorPoints.get(i).problem; - if (p.tabIndex == currentTab) { - if (currentTabErrorCount == errorPoints - .indexOf(eMarker)) { - // System.out.println("Roger that."); - String msg = (p.isError() ? "Error: " - : "Warning: ") - + p.message; - setToolTipText(msg); - setCursor(Cursor - .getPredefinedCursor(Cursor.HAND_CURSOR)); - return; - } else { - currentTabErrorCount++; - // System.out.println("Still looking.."); - } - } - - } - } - // Reset cursor and tooltip - else { - setToolTipText(""); - setCursor(Cursor - .getPredefinedCursor(Cursor.DEFAULT_CURSOR)); - } - } - - } - }; - try { - worker.execute(); - } catch (Exception exp) { - System.out - .println("Errorbar mousemoved Worker is slacking." - + exp.getMessage()); - // e.printStackTrace(); - } - } - - @Override - public void mouseDragged(MouseEvent arg0) { - - } - }); - - } - -} diff --git a/experimental/src/processing/mode/experimental/ErrorCheckerService.java b/experimental/src/processing/mode/experimental/ErrorCheckerService.java deleted file mode 100755 index b196bbf26..000000000 --- a/experimental/src/processing/mode/experimental/ErrorCheckerService.java +++ /dev/null @@ -1,1113 +0,0 @@ -package processing.mode.experimental; - -import java.awt.EventQueue; -import java.io.File; -import java.io.FileFilter; -import java.lang.reflect.Method; -import java.net.MalformedURLException; -import java.net.URL; -import java.net.URLClassLoader; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.Map; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import javax.swing.table.DefaultTableModel; - -import org.eclipse.jdt.core.JavaCore; -import org.eclipse.jdt.core.compiler.IProblem; -import org.eclipse.jdt.core.dom.AST; -import org.eclipse.jdt.core.dom.ASTParser; -import org.eclipse.jdt.core.dom.CompilationUnit; -import org.eclipse.jdt.internal.compiler.impl.CompilerOptions; -import org.eclipse.jdt.internal.compiler.problem.DefaultProblem; - -import processing.app.Base; -import processing.app.Library; -import processing.app.SketchCode; -import processing.core.PApplet; - -public class ErrorCheckerService implements Runnable{ - - private DebugEditor editor; - /** - * Error check happens every sleepTime milliseconds - */ - public static final int sleepTime = 1000; - - /** - * The amazing eclipse ast parser - */ - private ASTParser parser; - - /** - * Used to indirectly stop the Error Checker Thread - */ - public boolean stopThread = false; - - /** - * If true, Error Checking is paused. Calls to checkCode() become useless. - */ - private boolean pauseThread = false; - - protected ErrorWindow errorWindow; - - /** - * IProblem[] returned by parser stored in here - */ - private IProblem[] problems; - - /** - * Class name of current sketch - */ - protected String className; - - /** - * Source code of current sketch - */ - protected String sourceCode; - - /** - * URLs of extra imports jar files stored here. - */ - protected URL[] classpath; - - /** - * Stores all Problems in the sketch - */ - public ArrayList problemsList; - - /** - * How many lines are present till the initial class declaration? In static - * mode, this would include imports, class declaration and setup - * declaration. In nomral mode, this would include imports, class - * declaration only. It's fate is decided inside preprocessCode() - */ - public int mainClassOffset; - - /** - * Is the sketch running in static mode or active mode? - */ - public boolean staticMode = false; - - /** - * Compilation Unit for current sketch - */ - protected CompilationUnit cu; - - /** - * If true, compilation checker will be reloaded with updated classpath - * items. - */ - private boolean loadCompClass = true; - - /** - * Compiler Checker class. Note that methods for compilation checking are - * called from the compilationChecker object, not from this - */ - protected Class checkerClass; - - /** - * Compilation Checker object. - */ - protected Object compilationChecker; - - - /** - * List of jar files to be present in compilation checker's classpath - */ - protected ArrayList classpathJars; - - /** - * Timestamp - for measuring total overhead - */ - private long lastTimeStamp = System.currentTimeMillis(); - - /** - * Used for displaying the rotating slash on the Problem Window title bar - */ - private String[] slashAnimation = { "|", "/", "--", "\\", "|", "/", "--", - "\\" }; - private int slashAnimationIndex = 0; - - /** - * Used to detect if the current tab index has changed and thus repaint the - * textarea. - */ - public int currentTab = 0, lastTab = 0; - - /** - * Stores the current import statements in the program. Used to compare for - * changed import statements and update classpath if needed. - */ - protected ArrayList programImports; - - /** - * List of imports when sketch was last checked. Used for checking for - * changed imports - */ - protected ArrayList previousImports = new ArrayList(); - - /** - * Teh Preprocessor - */ - protected XQPreprocessor xqpreproc; - - /** - * Regexp for import statements. (Used from Processing source) - */ - final public String importRegexp = "(?:^|;)\\s*(import\\s+)((?:static\\s+)?\\S+)(\\s*;)"; - - /** - * Regexp for function declarations. (Used from Processing source) - */ - final Pattern FUNCTION_DECL = Pattern - .compile("(^|;)\\s*((public|private|protected|final|static)\\s+)*" - + "(void|int|float|double|String|char|byte)" - + "(\\s*\\[\\s*\\])?\\s+[a-zA-Z0-9]+\\s*\\(", Pattern.MULTILINE); - - public ErrorCheckerService(DebugEditor debugEditor) { - this.editor = debugEditor; - initParser(); - initializeErrorWindow(); - xqpreproc = new XQPreprocessor(); - } - - /** - * Initializes ASTParser - */ - private void initParser() { - try { - parser = ASTParser.newParser(AST.JLS4); - } catch (Exception e) { - System.err.println("Experimental Mode initialization failed. " - + "Are you running the right version of Processing? "); - pauseThread(); - } catch (Error e) { - System.err.println("Experimental Mode initialization failed. "); - e.printStackTrace(); - pauseThread(); - } - } - - /** - * Initialiazes the Error Window - */ - public void initializeErrorWindow() { - - if (editor == null) { - return; - } - - if (errorWindow != null) { - return; - } - - final ErrorCheckerService thisService = this; - final DebugEditor thisEditor = editor; - EventQueue.invokeLater(new Runnable() { - public void run() { - try { - errorWindow = new ErrorWindow(thisEditor, thisService); - // errorWindow.setVisible(true); - editor.toFront(); - errorWindow.errorTable.setFocusable(false); - editor.setSelection(0, 0); - } catch (Exception e) { - e.printStackTrace(); - } - } - }); - } - - /** - * checkCode() only on text area update - */ - protected AtomicInteger textModified = new AtomicInteger(0); - - public void run() { - stopThread = false; - - checkCode(); - while (!stopThread) { - try { - // Take a nap. - Thread.sleep(sleepTime); - } catch (Exception e) { - System.out.println("Oops! [ErrorCheckerThreaded]: " + e); - // e.printStackTrace(); - } - - if (pauseThread) - continue; - - updatePaintedThingy(); - - if(textModified.get() == 0) - continue; - - // Check every x seconds - checkCode(); - - } - } - - - - - private boolean checkCode() { - - lastTimeStamp = System.currentTimeMillis(); - try { - sourceCode = preprocessCode(editor.getSketch().getMainProgram()); - - syntaxCheck(); - - // No syntax errors, proceed for compilation check, Stage 2. - if (problems.length == 0 && editor.compilationCheckEnabled) { - sourceCode = xqpreproc.doYourThing(sourceCode, programImports); - prepareCompilerClasspath(); - mainClassOffset = xqpreproc.mainClassOffset; // tiny, but - // significant - if (staticMode) { - mainClassOffset++; // Extra line for setup() decl. - } - // System.out.println(sourceCode); - // System.out.println("--------------------------"); - compileCheck(); - } - - - updateErrorTable(); - editor.updateErrorBar(problemsList); - updateEditorStatus(); - // updatePaintedThingy(); - int x = textModified.get(); - //System.out.println("TM " + x); - if(x>=3){ - textModified.set(3); - x = 3; - } - - if(x>0) - textModified.set(x - 1); - else - textModified.set(0); - return true; - - } catch (Exception e) { - System.out.println("Oops! [ErrorCheckerService.checkCode]: " + e); - e.printStackTrace(); - } - return false; - } - - private void syntaxCheck() { - parser.setSource(sourceCode.toCharArray()); - parser.setKind(ASTParser.K_COMPILATION_UNIT); - - @SuppressWarnings("unchecked") - Map options = JavaCore.getOptions(); - - JavaCore.setComplianceOptions(JavaCore.VERSION_1_6, options); - options.put(JavaCore.COMPILER_SOURCE, JavaCore.VERSION_1_6); - parser.setCompilerOptions(options); - cu = (CompilationUnit) parser.createAST(null); - - // Store errors returned by the ast parser - problems = cu.getProblems(); - // System.out.println("Problem Count: " + problems.length); - // Populate the probList - problemsList = new ArrayList(); - for (int i = 0; i < problems.length; i++) { - int a[] = calculateTabIndexAndLineNumber(problems[i]); - Problem p = new Problem(problems[i], a[0], a[1]); - problemsList.add(p); - // System.out.println(p.toString()); - } - } - - private void compileCheck() { - - // Currently (Sept, 2012) I'm using Java's reflection api to load the - // CompilationChecker class(from CompilationChecker.jar) that houses the - // Eclispe JDT compiler and call its getErrorsAsObj method to obtain - // errors. This way, I'm able to add the paths of contributed libraries - // to the classpath of CompilationChecker, dynamically. - - try { - - // NOTE TO SELF: If classpath contains null Strings - // URLClassLoader gets angry. Drops NPE bombs. - - // If imports have changed, reload classes with new classpath. - if (loadCompClass) { - - // if (classpathJars.size() > 0) - // System.out - // .println("Experimental Mode: Loading contributed libraries referenced by import statements."); - - File f = Base.getContentFile("modes" + File.separator + "experimental" - + File.separator + "mode"); - - if(!f.exists()) { - System.err.println("Could not locate the files required for on-the-fly error checking. Bummer."); - return; - } - - FileFilter fileFilter = new FileFilter() { - public boolean accept(File file) { - return (file.getName().endsWith(".jar") && !file - .getName().startsWith("experimental")); - } - }; - - File[] jarFiles = f.listFiles(fileFilter); - // System.out.println( "Jar files found? " + (jarFiles != null)); - for (File jarFile : jarFiles) { - classpathJars.add(jarFile.toURI().toURL()); - } - - classpath = new URL[classpathJars.size()]; // + 1 for - // Compilation - // Checker class - for (int i = 0; i < classpathJars.size(); i++) { - classpath[i] = classpathJars.get(i); - } - - // System.out.println("CP Len -- " + classpath.length); - URLClassLoader classLoader = new URLClassLoader(classpath); - // System.out.println("1."); - checkerClass = Class.forName("CompilationChecker", true, - classLoader); - // System.out.println("2."); - compilationChecker = checkerClass.newInstance(); - loadCompClass = false; - } - - if (compilerSettings == null) { - prepareCompilerSetting(); - } - Method getErrors = checkerClass.getMethod("getErrorsAsObjArr", - new Class[] { String.class, String.class, Map.class }); - - Object[][] errorList = (Object[][]) getErrors - .invoke(compilationChecker, className, sourceCode, - compilerSettings); - - if (errorList == null) { - return; - } - - problems = new DefaultProblem[errorList.length]; - - for (int i = 0; i < errorList.length; i++) { - - // for (int j = 0; j < errorList[i].length; j++) - // System.out.print(errorList[i][j] + ", "); - - problems[i] = new DefaultProblem((char[]) errorList[i][0], - (String) errorList[i][1], - ((Integer) errorList[i][2]).intValue(), - (String[]) errorList[i][3], - ((Integer) errorList[i][4]).intValue(), - ((Integer) errorList[i][5]).intValue(), - ((Integer) errorList[i][6]).intValue(), - ((Integer) errorList[i][7]).intValue(), 0); - - // System.out - // .println("ECS: " + problems[i].getMessage() + "," - // + problems[i].isError() + "," - // + problems[i].isWarning()); - - IProblem problem = problems[i]; - - int a[] = calculateTabIndexAndLineNumber(problem); - Problem p = new Problem(problem, a[0], a[1]); - if ((Boolean) errorList[i][8]) { - p.setType(Problem.ERROR); - } - - if ((Boolean) errorList[i][9]) { - p.setType(Problem.WARNING); - } - - // If warnings are disabled, skip 'em - if (p.isWarning() && !warningsEnabled) { - continue; - } - problemsList.add(p); - } - - } catch (ClassNotFoundException e) { - System.err.println("Compiltation Checker files couldn't be found! " - + e + " compileCheck() problem."); - stopThread(); - } catch (MalformedURLException e) { - System.err.println("Compiltation Checker files couldn't be found! " - + e + " compileCheck() problem."); - stopThread(); - } catch (Exception e) { - System.err.println("compileCheck() problem." + e); - e.printStackTrace(); - stopThread(); - } catch (NoClassDefFoundError e) { - System.err - .println(e - + " compileCheck() problem. Somebody tried to mess with Experimental Mode files."); - stopThread(); - } - // System.out.println("Compilecheck, Done."); - } - - /** - * Processes import statements to obtain classpaths of contributed - * libraries. This would be needed for compilation check. Also, adds - * stuff(jar files, class files, candy) from the code folder. And it looks - * messed up. - * - */ - private void prepareCompilerClasspath() { - if (!loadCompClass) { - return; - } - - // System.out.println("1.."); - classpathJars = new ArrayList(); - String entry = ""; - boolean codeFolderChecked = false; - for (ImportStatement impstat : programImports) { - String item = impstat.importName; - int dot = item.lastIndexOf('.'); - entry = (dot == -1) ? item : item.substring(0, dot); - - entry = entry.substring(6).trim(); - // System.out.println("Entry--" + entry); - if (ignorableImport(entry)) { - // System.out.println("Ignoring: " + entry); - continue; - } - Library library = null; - - // Try to get the library classpath and add it to the list - try { - library = editor.getMode().getLibrary(entry); - // System.out.println("lib->" + library.getClassPath() + "<-"); - String libraryPath[] = PApplet.split(library.getClassPath() - .substring(1).trim(), File.pathSeparatorChar); - for (int i = 0; i < libraryPath.length; i++) { - // System.out.println(entry + " ::" - // + new File(libraryPath[i]).toURI().toURL()); - classpathJars.add(new File(libraryPath[i]).toURI().toURL()); - } - // System.out.println("-- "); - // classpath[count] = (new File(library.getClassPath() - // .substring(1))).toURI().toURL(); - // System.out.println(" found "); - // System.out.println(library.getClassPath().substring(1)); - } catch (Exception e) { - if (library == null && !codeFolderChecked) { - // System.out.println(1); - // Look around in the code folder for jar files - if (editor.getSketch().hasCodeFolder()) { - File codeFolder = editor.getSketch().getCodeFolder(); - - // get a list of .jar files in the "code" folder - // (class files in subfolders should also be picked up) - String codeFolderClassPath = Base - .contentsToClassPath(codeFolder); - codeFolderChecked = true; - if (codeFolderClassPath.equalsIgnoreCase("")) { - System.err.println("Experimental Mode: Yikes! Can't find \"" - + entry - + "\" library! Line: " - + impstat.lineNumber - + " in tab: " - + editor.getSketch().getCode(impstat.tab) - .getPrettyName()); - System.out - .println("Please make sure that the library is present in /libraries folder or in the code folder of your sketch"); - - } - String codeFolderPath[] = PApplet.split( - codeFolderClassPath.substring(1).trim(), - File.pathSeparatorChar); - try { - for (int i = 0; i < codeFolderPath.length; i++) { - classpathJars.add(new File(codeFolderPath[i]) - .toURI().toURL()); - } - - } catch (Exception e2) { - System.out - .println("Yikes! codefolder, prepareImports(): " - + e2); - } - } else { - System.err.println("Experimental Mode: Yikes! Can't find \"" - + entry - + "\" library! Line: " - + impstat.lineNumber - + " in tab: " - + editor.getSketch().getCode(impstat.tab) - .getPrettyName()); - System.out - .println("Please make sure that the library is present in /libraries folder or in the code folder of your sketch"); - } - - } else { - System.err - .println("Yikes! There was some problem in prepareImports(): " - + e); - System.err.println("I was processing: " + entry); - - // e.printStackTrace(); - } - } - - } - - } - - /** - * Ignore processing packages, java.*.*. etc. - * - * @param packageName - * @return boolean - */ - protected boolean ignorableImport(String packageName) { - // packageName.startsWith("processing.") - // || - if (packageName.startsWith("java.") || packageName.startsWith("javax.")) { - return true; - } - return false; - } - - /** - * Various option for JDT Compiler - */ - @SuppressWarnings("rawtypes") - protected Map compilerSettings; - - /** - * Enable/Disable warnings from being shown - */ - public boolean warningsEnabled = true; - - /** - * Sets compiler options for JDT Compiler - */ - @SuppressWarnings({ "unchecked", "rawtypes" }) - protected void prepareCompilerSetting() { - compilerSettings = new HashMap(); - - compilerSettings.put(CompilerOptions.OPTION_LineNumberAttribute, - CompilerOptions.GENERATE); - compilerSettings.put(CompilerOptions.OPTION_SourceFileAttribute, - CompilerOptions.GENERATE); - compilerSettings.put(CompilerOptions.OPTION_Source, - CompilerOptions.VERSION_1_6); - compilerSettings.put(CompilerOptions.OPTION_ReportUnusedImport, - CompilerOptions.IGNORE); - compilerSettings.put(CompilerOptions.OPTION_ReportMissingSerialVersion, - CompilerOptions.IGNORE); - compilerSettings.put(CompilerOptions.OPTION_ReportRawTypeReference, - CompilerOptions.IGNORE); - compilerSettings.put( - CompilerOptions.OPTION_ReportUncheckedTypeOperation, - CompilerOptions.IGNORE); - } - - - /** - * Updates the error table in the Error Window. - */ - synchronized public void updateErrorTable() { - - try { - String[][] errorData = new String[problemsList.size()][3]; - for (int i = 0; i < problemsList.size(); i++) { - errorData[i][0] = problemsList.get(i).message; - errorData[i][1] = editor.getSketch() - .getCode(problemsList.get(i).tabIndex).getPrettyName(); - errorData[i][2] = problemsList.get(i).lineNumber + ""; - } - - if (errorWindow != null) { - DefaultTableModel tm = new DefaultTableModel(errorData, - XQErrorTable.columnNames); - if (errorWindow.isVisible()) { - errorWindow.updateTable(tm); - } - - // Update error table in the editor - editor.updateTable(tm); - - // A rotating slash animation on the title bar to show - // that error checker thread is running - - slashAnimationIndex++; - if (slashAnimationIndex == slashAnimation.length) { - slashAnimationIndex = 0; - } - if (editor != null) { - String info = slashAnimation[slashAnimationIndex] + " T:" - + (System.currentTimeMillis() - lastTimeStamp) - + "ms"; - errorWindow.setTitle("Problems - " - + editor.getSketch().getName() + " " + info); - } - } - - } catch (Exception e) { - System.out.println("Exception at updateErrorTable() " + e); - e.printStackTrace(); - stopThread(); - } - - } - - /** - * Repaints the textarea if required - */ - public void updatePaintedThingy() { - editor.getTextArea().repaint(); - updateEditorStatus(); - currentTab = editor.getSketch().getCodeIndex( - editor.getSketch().getCurrentCode()); - if (currentTab != lastTab) { - lastTab = currentTab; - editor.updateErrorBar(problemsList); - return; - } - - } - - /** - * Updates editor status bar, depending on whether the caret is on an error - * line or not - */ - public void updateEditorStatus() { - // editor.statusNotice("Position: " + - // editor.getTextArea().getCaretLine()); - boolean notFound = true; - for (ErrorMarker emarker : editor.errorBar.errorPoints) { - if (emarker.problem.lineNumber == editor.getTextArea() - .getCaretLine() + 1) { - if (emarker.type == ErrorMarker.Warning) { - editor.statusNotice(emarker.problem.message); - } - else { - editor.statusError(emarker.problem.message); - } - return; - } - } - if (notFound) { - editor.statusEmpty(); - } - } - - /** - * Calculates the tab number and line number of the error in that particular - * tab. Provides mapping between pure java and pde code. - * - * @param problem - * - IProblem - * @return int[0] - tab number, int[1] - line number - */ - public int[] calculateTabIndexAndLineNumber(IProblem problem) { - // String[] lines = {};// = PApplet.split(sourceString, '\n'); - int codeIndex = 0; - - int x = problem.getSourceLineNumber() - mainClassOffset; - if (x < 0) { - // System.out.println("Negative line number " - // + problem.getSourceLineNumber() + " , offset " - // + mainClassOffset); - x = problem.getSourceLineNumber() - 2; // Another -1 for 0 index - if (x < programImports.size() && x >= 0) { - ImportStatement is = programImports.get(x); - // System.out.println(is.importName + ", " + is.tab + ", " - // + is.lineNumber); - return new int[] { is.tab, is.lineNumber }; - } else { - - // Some seriously ugly stray error, just can't find the source - // line! Simply return first line for first tab. - return new int[] { 0, 1 }; - } - - } - - try { - for (SketchCode sc : editor.getSketch().getCode()) { - if (sc.isExtension("pde")) { - int len = 0; - if (editor.getSketch().getCurrentCode().equals(sc)) { - len = Base.countLines(sc.getDocument().getText(0, - sc.getDocument().getLength())) + 1; - } else { - len = Base.countLines(sc.getProgram()) + 1; - } - - // System.out.println("x,len, CI: " + x + "," + len + "," - // + codeIndex); - - if (x >= len) { - - // We're in the last tab and the line count is greater - // than the no. - // of lines in the tab, - if (codeIndex >= editor.getSketch().getCodeCount() - 1) { - // System.out.println("Exceeds lc " + x + "," + len - // + problem.toString()); - // x = len - x = editor.getSketch().getCode(codeIndex) - .getLineCount(); - // TODO: Obtain line having last non-white space - // character in the code. - break; - } else { - x -= len; - codeIndex++; - } - } else { - - if (codeIndex >= editor.getSketch().getCodeCount()) { - codeIndex = editor.getSketch().getCodeCount() - 1; - } - break; - } - - } - } - } catch (Exception e) { - System.err - .println("Things got messed up in ErrorCheckerService.calculateTabIndexAndLineNumber()"); - } - - return new int[] { codeIndex, x }; - } - - /** - * Fetches code from the editor tabs and pre-processes it into parsable pure - * java source. And there's a difference between parsable and compilable. - * XQPrerocessor.java makes this code compilable.
- * Handles:

  • Removal of import statements
  • Conversion of int(), - * char(), etc to (int)(), (char)(), etc.
  • Replacing '#' with 0xff for - * color representation
  • Converts all 'color' datatypes to int - * (experimental)
  • Appends class declaration statement after determining - * the mode the sketch is in - ACTIVE or STATIC - * - * @return String - Pure java representation of PDE code. Note that this - * code is not yet compile ready. - */ - - private String preprocessCode(String pdeCode) { - - programImports = new ArrayList(); - - StringBuffer rawCode = new StringBuffer(); - - try { - - for (SketchCode sc : editor.getSketch().getCode()) { - if (sc.isExtension("pde")) { - - try { - - if (editor.getSketch().getCurrentCode().equals(sc)) { - - rawCode.append(scrapImportStatements(sc.getDocument() - .getText(0, - sc.getDocument() - .getLength()), - editor.getSketch() - .getCodeIndex(sc))); - } else { - - rawCode.append(scrapImportStatements(sc.getProgram(), editor - .getSketch().getCodeIndex(sc))); - - } - rawCode.append('\n'); - } catch (Exception e) { - System.err.println("Exception in preprocessCode() - bigCode " - + e.toString()); - } - rawCode.append('\n'); - } - } - - } catch (Exception e) { - System.out.println("Exception in preprocessCode()"); - } - String sourceAlt = rawCode.toString(); - // Replace comments with whitespaces - // sourceAlt = scrubComments(sourceAlt); - - // Find all int(*), replace with PApplet.parseInt(*) - - // \bint\s*\(\s*\b , i.e all exclusive "int(" - - String dataTypeFunc[] = { "int", "char", "float", "boolean", "byte" }; - - for (String dataType : dataTypeFunc) { - String dataTypeRegexp = "\\b" + dataType + "\\s*\\("; - Pattern pattern = Pattern.compile(dataTypeRegexp); - Matcher matcher = pattern.matcher(sourceAlt); - - // while (matcher.find()) { - // System.out.print("Start index: " + matcher.start()); - // System.out.println(" End index: " + matcher.end() + " "); - // System.out.println("-->" + matcher.group() + "<--"); - // } - sourceAlt = matcher.replaceAll("PApplet.parse" - + Character.toUpperCase(dataType.charAt(0)) - + dataType.substring(1) + "("); - - } - - // Find all #[web color] and replace with 0xff[webcolor] - // Should be 6 digits only. - final String webColorRegexp = "#{1}[A-F|a-f|0-9]{6}\\W"; - Pattern webPattern = Pattern.compile(webColorRegexp); - Matcher webMatcher = webPattern.matcher(sourceAlt); - while (webMatcher.find()) { - // System.out.println("Found at: " + webMatcher.start()); - String found = sourceAlt.substring(webMatcher.start(), - webMatcher.end()); - // System.out.println("-> " + found); - sourceAlt = webMatcher.replaceFirst("0xff" + found.substring(1)); - webMatcher = webPattern.matcher(sourceAlt); - } - - // Replace all color data types with int - // Regex, Y U SO powerful? - final String colorTypeRegex = "color(?![a-zA-Z0-9_])(?=\\[*)(?!(\\s*\\())"; - Pattern colorPattern = Pattern.compile(colorTypeRegex); - Matcher colorMatcher = colorPattern.matcher(sourceAlt); - sourceAlt = colorMatcher.replaceAll("int"); - - checkForChangedImports(); - - className = (editor == null) ? "DefaultClass" : editor.getSketch() - .getName(); - - // Check whether the code is being written in STATIC mode(no function - // declarations) - append class declaration and void setup() declaration - Matcher matcher = FUNCTION_DECL.matcher(sourceAlt); - if (!matcher.find()) { - sourceAlt = "public class " + className + " extends PApplet {\n" - + "public void setup() {\n" + sourceAlt - + "\nnoLoop();\n}\n" + "\n}\n"; - staticMode = true; - mainClassOffset = 2; - - } else { - sourceAlt = "public class " + className + " extends PApplet {\n" - + sourceAlt + "\n}"; - staticMode = false; - mainClassOffset = 1; - } - - // Handle unicode characters - sourceAlt = substituteUnicode(sourceAlt); - -// System.out.println("-->\n" + sourceAlt + "\n<--"); -// System.out.println("PDE code processed - " -// + editor.getSketch().getName()); - sourceCode = sourceAlt; - return sourceAlt; - - } - - /** - * Scrolls to the error source in code. And selects the line text. Used by - * XQErrorTable and ErrorBar - * - * @param errorIndex - * - index of error - */ - public void scrollToErrorLine(int errorIndex) { - if (editor == null) { - return; - } - - if (errorIndex < problemsList.size() && errorIndex >= 0) { - Problem p = problemsList.get(errorIndex); - try { - editor.toFront(); - editor.getSketch().setCurrentCode(p.tabIndex); - editor.getTextArea().scrollTo(p.lineNumber - 1, 0); - editor.setSelection(editor.getTextArea() - .getLineStartNonWhiteSpaceOffset(p.lineNumber - 1) - + editor.getTextArea().getLineText(p.lineNumber - 1) - .trim().length(), editor.getTextArea() - .getLineStartNonWhiteSpaceOffset(p.lineNumber - 1)); - editor.repaint(); - } catch (Exception e) { - System.err - .println(e - + " : Error while selecting text in scrollToErrorLine()"); - // e.printStackTrace(); - } - // System.out.println("---"); - - } - } - - /** - * Checks if import statements in the sketch have changed. If they have, - * compiler classpath needs to be updated. - */ - private void checkForChangedImports() { - // System.out.println("Imports: " + programImports.size() + - // " Prev Imp: " - // + previousImports.size()); - if (programImports.size() != previousImports.size()) { - // System.out.println(1); - loadCompClass = true; - previousImports = programImports; - } else { - for (int i = 0; i < programImports.size(); i++) { - if (!programImports.get(i).importName.equals(previousImports - .get(i).importName)) { - // System.out.println(2); - loadCompClass = true; - previousImports = programImports; - break; - } - } - } - // System.out.println("load..? " + loadCompClass); - } - - /** - * Removes import statements from tabSource, replaces each with white spaces - * and adds the import to the list of program imports - * - * @param tabProgram - * - Code in a tab - * @param tabNumber - * - index of the tab - * @return String - Tab code with imports replaced with white spaces - */ - private String scrapImportStatements(String tabProgram, int tabNumber) { - - String tabSource = new String(tabProgram); - do { - // System.out.println("-->\n" + sourceAlt + "\n<--"); - String[] pieces = PApplet.match(tabSource, importRegexp); - - // Stop the loop if we've removed all the import lines - if (pieces == null) { - break; - } - - String piece = pieces[1] + pieces[2] + pieces[3]; - int len = piece.length(); // how much to trim out - - // programImports.add(piece); // the package name - - // find index of this import in the program - int idx = tabSource.indexOf(piece); - // System.out.print("Import -> " + piece); - // System.out.println(" - " - // + Base.countLines(tabSource.substring(0, idx)) + " tab " - // + tabNumber); - programImports.add(new ImportStatement(piece, tabNumber, Base - .countLines(tabSource.substring(0, idx)))); - // Remove the import from the main program - // Substitue with white spaces - String whiteSpace = ""; - for (int j = 0; j < piece.length(); j++) { - whiteSpace += " "; - } - tabSource = tabSource.substring(0, idx) + whiteSpace - + tabSource.substring(idx + len); - - } while (true); - // System.out.println(tabSource); - return tabSource; - } - - /** - * Replaces non-ascii characters with their unicode escape sequences and - * stuff. Used as it is from - * processing.src.processing.mode.java.preproc.PdePreprocessor - * - * @param program - * - Input String containing non ascii characters - * @return String - Converted String - */ - public static String substituteUnicode(String program) { - // check for non-ascii chars (these will be/must be in unicode format) - char p[] = program.toCharArray(); - int unicodeCount = 0; - for (int i = 0; i < p.length; i++) { - if (p[i] > 127) { - unicodeCount++; - } - } - if (unicodeCount == 0) { - return program; - } - // if non-ascii chars are in there, convert to unicode escapes - // add unicodeCount * 5.. replacing each unicode char - // with six digit uXXXX sequence (xxxx is in hex) - // (except for nbsp chars which will be a replaced with a space) - int index = 0; - char p2[] = new char[p.length + unicodeCount * 5]; - for (int i = 0; i < p.length; i++) { - if (p[i] < 128) { - p2[index++] = p[i]; - } else if (p[i] == 160) { // unicode for non-breaking space - p2[index++] = ' '; - } else { - int c = p[i]; - p2[index++] = '\\'; - p2[index++] = 'u'; - char str[] = Integer.toHexString(c).toCharArray(); - // add leading zeros, so that the length is 4 - // for (int i = 0; i < 4 - str.length; i++) p2[index++] = '0'; - for (int m = 0; m < 4 - str.length; m++) - p2[index++] = '0'; - System.arraycopy(str, 0, p2, index, str.length); - index += str.length; - } - } - return new String(p2, 0, index); - } - - /** - * Stops the Error Checker Service thread - */ - public void stopThread() { - stopThread = true; - } - - /** - * Pauses the Error Checker Service thread - */ - public void pauseThread() { - pauseThread = true; - } - - /** - * Resumes the Error Checker Service thread - */ - public void resumeThread() { - pauseThread = false; - } - - public DebugEditor getEditor() { - return editor; - } -} diff --git a/experimental/src/processing/mode/experimental/ErrorMarker.java b/experimental/src/processing/mode/experimental/ErrorMarker.java deleted file mode 100755 index bc244f7a1..000000000 --- a/experimental/src/processing/mode/experimental/ErrorMarker.java +++ /dev/null @@ -1,36 +0,0 @@ -package processing.mode.experimental; -/** - * Error markers displayed on the Error Bar. - * - * @author Manindra Moharana <me@mkmoharana.com> - * - */ - public class ErrorMarker { - /** - * y co-ordinate of the marker - */ - public int y; - /** - * Type of marker: Error or Warning? - */ - public int type = -1; - /** - * Error Type constant - */ - public static final int Error = 1; - /** - * Warning Type constant - */ - public static final int Warning = 2; - /** - * Problem that the error marker represents - * @see Problem - */ - public Problem problem; - - public ErrorMarker(Problem problem, int y, int type) { - this.problem = problem; - this.y = y; - this.type = type; - } - } \ No newline at end of file diff --git a/experimental/src/processing/mode/experimental/ErrorWindow.java b/experimental/src/processing/mode/experimental/ErrorWindow.java deleted file mode 100755 index db4e2a8c8..000000000 --- a/experimental/src/processing/mode/experimental/ErrorWindow.java +++ /dev/null @@ -1,374 +0,0 @@ -/* - Part of the XQMode project - https://github.com/Manindra29/XQMode - - Under Google Summer of Code 2012 - - http://www.google-melange.com/gsoc/homepage/google/gsoc2012 - - Copyright (C) 2012 Manindra Moharana - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License version 2 - as published by the Free Software Foundation. - - This program 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 for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software Foundation, - Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -package processing.mode.experimental; - -import java.awt.BorderLayout; -import java.awt.Frame; -import java.awt.Point; -import java.awt.event.ComponentEvent; -import java.awt.event.ComponentListener; -import java.awt.event.WindowAdapter; -import java.awt.event.WindowEvent; - -import javax.swing.JFrame; -import javax.swing.JPanel; -import javax.swing.JScrollPane; -import javax.swing.WindowConstants; -import javax.swing.border.EmptyBorder; -import javax.swing.table.TableModel; - -import processing.app.Editor; -import processing.app.Toolkit; - -/** - * Error Window that displays a tablular list of errors. Clicking on an error - * scrolls to its location in the code. - * - * @author Manindra Moharana <me@mkmoharana.com> - * - */ -public class ErrorWindow extends JFrame { - - private JPanel contentPane; - /** - * The table displaying the errors - */ - protected XQErrorTable errorTable; - /** - * Scroll pane that contains the Error Table - */ - protected JScrollPane scrollPane; - - protected DebugEditor thisEditor; - private JFrame thisErrorWindow; - - /** - * Handles the sticky Problem window - */ - private DockTool2Base Docker; - - protected ErrorCheckerService errorCheckerService; - - /** - * Preps up ErrorWindow - * - * @param editor - * - Editor - * @param ecs - ErrorCheckerService - */ - public ErrorWindow(DebugEditor editor, ErrorCheckerService ecs) { - thisErrorWindow = this; - errorCheckerService = ecs; - thisEditor = editor; - setTitle("Problems"); - prepareFrame(); - } - - /** - * Sets up ErrorWindow - */ - protected void prepareFrame() { - Toolkit.setIcon(this); - setDefaultCloseOperation(WindowConstants.HIDE_ON_CLOSE); - // Default size: setBounds(100, 100, 458, 160); - setBounds(100, 100, 458, 160); // Yeah, I hardcode such things sometimes. Hate me. - - contentPane = new JPanel(); - contentPane.setBorder(new EmptyBorder(5, 5, 5, 5)); - setContentPane(contentPane); - contentPane.setLayout(new BorderLayout(0, 0)); - - scrollPane = new JScrollPane(); - contentPane.add(scrollPane); - - errorTable = new XQErrorTable(errorCheckerService); - scrollPane.setViewportView(errorTable); - - try { - Docker = new DockTool2Base(); - addListeners(); - } catch (Exception e) { - System.out.println("addListeners() acted silly."); - e.printStackTrace(); - } - - if (thisEditor != null) { - setLocation(new Point(thisEditor.getLocation().x - + thisEditor.getWidth(), thisEditor.getLocation().y)); - } - - } - - /** - * Updates the error table with new data(Table Model). Called from Error - * Checker Service. - * - * @param tableModel - * - Table Model - * @return True - If error table was updated successfully. - */ - synchronized public boolean updateTable(final TableModel tableModel) { - // XQErrorTable handles evrything now - return errorTable.updateTable(tableModel); - } - - /** - * Adds various listeners to components of EditorWindow and to the Editor - * window - */ - protected void addListeners() { - - if (thisErrorWindow == null) - System.out.println("ERW null"); - - thisErrorWindow.addComponentListener(new ComponentListener() { - - @Override - public void componentShown(ComponentEvent e) { - - } - - @Override - public void componentResized(ComponentEvent e) { - Docker.tryDocking(); - } - - @Override - public void componentMoved(ComponentEvent e) { - Docker.tryDocking(); - } - - @Override - public void componentHidden(ComponentEvent e) { - - } - }); - - thisErrorWindow.addWindowListener(new WindowAdapter() { - - @Override - public void windowClosing(WindowEvent e) { - thisEditor.problemWindowMenuCB.setSelected(false); - } - - @Override - public void windowDeiconified(WindowEvent e) { - thisEditor.setExtendedState(Frame.NORMAL); - } - - }); - - if (thisEditor == null) { - System.out.println("Editor null"); - return; - } - - thisEditor.addWindowListener(new WindowAdapter() { - - @Override - public void windowClosing(WindowEvent e) { - - } - - @Override - public void windowClosed(WindowEvent e) { - errorCheckerService.pauseThread(); - errorCheckerService.stopThread(); // Bye bye thread. - thisErrorWindow.dispose(); - } - - @Override - public void windowIconified(WindowEvent e) { - thisErrorWindow.setExtendedState(Frame.ICONIFIED); - } - - @Override - public void windowDeiconified(WindowEvent e) { - thisErrorWindow.setExtendedState(Frame.NORMAL); - } - - }); - - thisEditor.addComponentListener(new ComponentListener() { - - @Override - public void componentShown(ComponentEvent e) { - - } - - @Override - public void componentResized(ComponentEvent e) { - if (Docker.isDocked()) { - Docker.dock(); - } else { - Docker.tryDocking(); - } - } - - @Override - public void componentMoved(ComponentEvent e) { - - if (Docker.isDocked()) { - Docker.dock(); - } else { - Docker.tryDocking(); - } - - } - - @Override - public void componentHidden(ComponentEvent e) { - // System.out.println("ed hidden"); - } - }); - - } - - - /** - * Implements the docking feature of the tool - The frame sticks to the - * editor and once docked, moves along with it as the editor is resized, - * moved, or closed. - * - * This class has been borrowed from Tab Manager tool by Thomas Diewald. It - * has been slightly modified and used here. - * - * @author Thomas Diewald , http://thomasdiewald.com - */ - private class DockTool2Base { - - private int docking_border = 0; - private int dock_on_editor_y_offset_ = 0; - private int dock_on_editor_x_offset_ = 0; - - // /////////////////////////////// - // ____2____ - // | | - // | | - // 0 | editor | 1 - // | | - // |_________| - // 3 - // /////////////////////////////// - - // public void reset() { - // dock_on_editor_y_offset_ = 0; - // dock_on_editor_x_offset_ = 0; - // docking_border = 0; - // } - - public boolean isDocked() { - return (docking_border >= 0); - } - - private final int MAX_GAP_ = 20; - - // - public void tryDocking() { - if (thisEditor == null) - return; - Editor editor = thisEditor; - Frame frame = thisErrorWindow; - - int ex = editor.getX(); - int ey = editor.getY(); - int ew = editor.getWidth(); - int eh = editor.getHeight(); - - int fx = frame.getX(); - int fy = frame.getY(); - int fw = frame.getWidth(); - int fh = frame.getHeight(); - - if (((fy > ey) && (fy < ey + eh)) - || ((fy + fh > ey) && (fy + fh < ey + eh))) { - int dis_border_left = Math.abs(ex - (fx + fw)); - int dis_border_right = Math.abs((ex + ew) - (fx)); - - if (dis_border_left < MAX_GAP_ || dis_border_right < MAX_GAP_) { - docking_border = (dis_border_left < dis_border_right) ? 0 - : 1; - dock_on_editor_y_offset_ = fy - ey; - dock(); - return; - } - } - - if (((fx > ex) && (fx < ex + ew)) - || ((fx + fw > ey) && (fx + fw < ex + ew))) { - int dis_border_top = Math.abs(ey - (fy + fh)); - int dis_border_bot = Math.abs((ey + eh) - (fy)); - - if (dis_border_top < MAX_GAP_ || dis_border_bot < MAX_GAP_) { - docking_border = (dis_border_top < dis_border_bot) ? 2 : 3; - dock_on_editor_x_offset_ = fx - ex; - dock(); - return; - } - } - docking_border = -1; - } - - public void dock() { - if (thisEditor == null) - return; - Editor editor = thisEditor; - Frame frame = thisErrorWindow; - - int ex = editor.getX(); - int ey = editor.getY(); - int ew = editor.getWidth(); - int eh = editor.getHeight(); - - // int fx = frame.getX(); - // int fy = frame.getY(); - int fw = frame.getWidth(); - int fh = frame.getHeight(); - - int x = 0, y = 0; - if (docking_border == -1) { - return; - } - - if (docking_border == 0) { - x = ex - fw; - y = ey + dock_on_editor_y_offset_; - } - if (docking_border == 1) { - x = ex + ew; - y = ey + dock_on_editor_y_offset_; - } - - if (docking_border == 2) { - x = ex + dock_on_editor_x_offset_; - y = ey - fh; - } - if (docking_border == 3) { - x = ex + dock_on_editor_x_offset_; - y = ey + eh; - } - frame.setLocation(x, y); - } - - } -} diff --git a/experimental/src/processing/mode/experimental/ExperimentalMode.java b/experimental/src/processing/mode/experimental/ExperimentalMode.java deleted file mode 100755 index 50d7ce4b3..000000000 --- a/experimental/src/processing/mode/experimental/ExperimentalMode.java +++ /dev/null @@ -1,171 +0,0 @@ -/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */ - -/* - Part of the Processing project - http://processing.org - - Copyright (c) 2012 The Processing Foundation - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License version 2 - as published by the Free Software Foundation. - - This program 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 for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software Foundation, - Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -*/ - -package processing.mode.experimental; - -import java.awt.Color; -import java.io.File; -import java.io.IOException; -import java.util.logging.FileHandler; -import java.util.logging.Handler; -import java.util.logging.Level; -import java.util.logging.Logger; -import processing.app.Base; -import processing.app.Editor; -import processing.app.EditorState; -import processing.app.Mode; -import processing.mode.java.JavaMode; - - -/** - * Experimental Mode for Processing, combines Debug Mode and XQMode and - * starts us working toward our next generation editor/debugger setup. - */ -public class ExperimentalMode extends JavaMode { - public static final boolean VERBOSE_LOGGING = true; - //public static final boolean VERBOSE_LOGGING = false; - public static final int LOG_SIZE = 512 * 1024; // max log file size (in bytes) - - - public ExperimentalMode(Base base, File folder) { - super(base, folder); - - // use libraries folder from javamode. will make sketches using core libraries work, as well as import libraries and examples menus - for (Mode m : base.getModeList()) { - if (m.getClass() == JavaMode.class) { - JavaMode jMode = (JavaMode) m; - librariesFolder = jMode.getLibrariesFolder(); - rebuildLibraryList(); - break; - } - } - - // Fetch examples and reference from java mode - // thx to Manindra (https://github.com/martinleopold/DebugMode/issues/4) - examplesFolder = Base.getContentFile("modes/java/examples"); - // https://github.com/martinleopold/DebugMode/issues/6 - referenceFolder = Base.getContentFile("modes/java/reference"); - - // set logging level - Logger globalLogger = Logger.getLogger(""); - //Logger logger = Logger.getLogger(Logger.GLOBAL_LOGGER_NAME); // doesn't work on os x - if (VERBOSE_LOGGING) { - globalLogger.setLevel(Level.INFO); - } else { - globalLogger.setLevel(Level.WARNING); - } - - // enable logging to file - try { - // settings is writable for built-in modes, mode folder is not writable - File logFolder = Base.getSettingsFile("debug"); - if (!logFolder.exists()) { - logFolder.mkdir(); - } - File logFile = new File(logFolder, "DebugMode.%g.log"); - Handler handler = new FileHandler(logFile.getAbsolutePath(), LOG_SIZE, 10, false); - globalLogger.addHandler(handler); - - } catch (IOException ex) { - Logger.getLogger(ExperimentalMode.class.getName()).log(Level.SEVERE, null, ex); - } catch (SecurityException ex) { - Logger.getLogger(ExperimentalMode.class.getName()).log(Level.SEVERE, null, ex); - } - - // disable initial chattiness for now -// // output version from manifest file -// Package p = ExperimentalMode.class.getPackage(); -// String titleAndVersion = p.getImplementationTitle() + " (v" + p.getImplementationVersion() + ")"; -// //System.out.println(titleAndVersion); -// Logger.getLogger(ExperimentalMode.class.getName()).log(Level.INFO, titleAndVersion); - } - - - @Override - public String getTitle() { - return "Experimental"; - } - - - public File[] getKeywordFiles() { - return new File[] { - Base.getContentFile("modes/java/keywords.txt") - }; - } - - - /** - * Create a new editor associated with this mode. - */ - @Override - public Editor createEditor(Base base, String path, EditorState state) { - return new DebugEditor(base, path, state, this); - } - - - /** - * Load a String value from theme.txt - * - * @param attribute the attribute key to load - * @param defaultValue the default value - * @return the attributes value, or the default value if the attribute - * couldn't be loaded - */ - public String loadThemeString(String attribute, String defaultValue) { - String newString = theme.get(attribute); - if (newString != null) { - return newString; - } - Logger.getLogger(getClass().getName()).log(Level.WARNING, "Error loading String: {0}", attribute); - return defaultValue; - } - - - /** - * Load a Color value from theme.txt - * - * @param attribute the attribute key to load - * @param defaultValue the default value - * @return the attributes value, or the default value if the attribute - * couldn't be loaded - */ - public Color getThemeColor(String attribute, Color defaultValue) { - Color newColor = theme.getColor(attribute); - if (newColor != null) { - return newColor; - } - System.out.println("error loading color: " + attribute); - Logger.getLogger(ExperimentalMode.class.getName()).log(Level.WARNING, "Error loading Color: {0}", attribute); - return defaultValue; - } - - - public ClassLoader getJavaModeClassLoader() { - for (Mode m : base.getModeList()) { - if (m.getClass() == JavaMode.class) { - JavaMode jMode = (JavaMode) m; - return jMode.getClassLoader(); - } - } - // badness - return null; - } -} diff --git a/experimental/src/processing/mode/experimental/FieldNode.java b/experimental/src/processing/mode/experimental/FieldNode.java deleted file mode 100755 index dbe9d4fd9..000000000 --- a/experimental/src/processing/mode/experimental/FieldNode.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright (C) 2012 Martin Leopold - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program 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 for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ -package processing.mode.experimental; - -import com.sun.jdi.ClassNotLoadedException; -import com.sun.jdi.Field; -import com.sun.jdi.InvalidTypeException; -import com.sun.jdi.ObjectReference; -import com.sun.jdi.Value; -import java.util.logging.Level; -import java.util.logging.Logger; - -/** - * Specialized {@link VariableNode} for representing fields. Overrides - * {@link #setValue} to properly change the value of the encapsulated field. - * - * @author Martin Leopold - */ -public class FieldNode extends VariableNode { - - protected Field field; - protected ObjectReference obj; - - /** - * Construct a {@link FieldNode}. - * - * @param name the name - * @param type the type - * @param value the value - * @param field the field - * @param obj a reference to the object containing the field - */ - public FieldNode(String name, String type, Value value, Field field, ObjectReference obj) { - super(name, type, value); - this.field = field; - this.obj = obj; - } - - @Override - public void setValue(Value value) { - try { - obj.setValue(field, value); - } catch (InvalidTypeException ex) { - Logger.getLogger(FieldNode.class.getName()).log(Level.SEVERE, null, ex); - } catch (ClassNotLoadedException ex) { - Logger.getLogger(FieldNode.class.getName()).log(Level.SEVERE, null, ex); - } - this.value = value; - } -} diff --git a/experimental/src/processing/mode/experimental/ImportStatement.java b/experimental/src/processing/mode/experimental/ImportStatement.java deleted file mode 100755 index 03f323a54..000000000 --- a/experimental/src/processing/mode/experimental/ImportStatement.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - Part of the XQMode project - https://github.com/Manindra29/XQMode - - Under Google Summer of Code 2012 - - http://www.google-melange.com/gsoc/homepage/google/gsoc2012 - - Copyright (C) 2012 Manindra Moharana - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License version 2 - as published by the Free Software Foundation. - - This program 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 for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software Foundation, - Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -package processing.mode.experimental; - -/** - * Wrapper for import statements - * - * @author Manindra Moharana <me@mkmoharana.com> - * - */ -public class ImportStatement { - /** - * Ex: processing.opengl.*, java.util.* - */ - String importName; - /** - * Which tab does it belong to? - */ - int tab; - - /** - * Line number(pde code) of the import - */ - int lineNumber; - - /** - * - * @param importName - Ex: processing.opengl.*, java.util.* - * @param tab - Which tab does it belong to? - * @param lineNumber - Line number(pde code) of the import - */ - public ImportStatement(String importName, int tab, int lineNumber) { - this.importName = importName; - this.tab = tab; - this.lineNumber = lineNumber; - } -} \ No newline at end of file diff --git a/experimental/src/processing/mode/experimental/LineBreakpoint.java b/experimental/src/processing/mode/experimental/LineBreakpoint.java deleted file mode 100755 index 8d006fd9d..000000000 --- a/experimental/src/processing/mode/experimental/LineBreakpoint.java +++ /dev/null @@ -1,218 +0,0 @@ -/* - * Copyright (C) 2012 Martin Leopold - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program 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 for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ -package processing.mode.experimental; - -import com.sun.jdi.AbsentInformationException; -import com.sun.jdi.Location; -import com.sun.jdi.ReferenceType; -import com.sun.jdi.request.BreakpointRequest; -import java.util.List; -import java.util.logging.Level; -import java.util.logging.Logger; - -/** - * Model/Controller of a line breakpoint. Can be set before or while debugging. - * Adds a highlight using the debuggers view ({@link DebugEditor}). - * - * @author Martin Leopold - */ -public class LineBreakpoint implements ClassLoadListener { - - protected Debugger dbg; // the debugger - protected LineID line; // the line this breakpoint is set on - protected BreakpointRequest bpr; // the request on the VM's event request manager - protected ReferenceType theClass; // the class containing this breakpoint, null when not yet loaded - - /** - * Create a {@link LineBreakpoint}. If in a debug session, will try to - * immediately set the breakpoint. If not in a debug session or the - * corresponding class is not yet loaded the breakpoint will activate on - * class load. - * - * @param line the line id to create the breakpoint on - * @param dbg the {@link Debugger} - */ - public LineBreakpoint(LineID line, Debugger dbg) { - this.line = line; - line.startTracking(dbg.editor().getTab(line.fileName()).getDocument()); - this.dbg = dbg; - theClass = dbg.getClass(className()); // try to get the class immediately, may return null if not yet loaded - set(); // activate the breakpoint (show highlight, attach if debugger is running) - } - - /** - * Create a {@link LineBreakpoint} on a line in the current tab. - * - * @param lineIdx the line index of the current tab to create the breakpoint - * on - * @param dbg the {@link Debugger} - */ - // TODO: remove and replace by {@link #LineBreakpoint(LineID line, Debugger dbg)} - public LineBreakpoint(int lineIdx, Debugger dbg) { - this(dbg.editor().getLineIDInCurrentTab(lineIdx), dbg); - } - - /** - * Get the line id this breakpoint is on. - * - * @return the line id - */ - public LineID lineID() { - return line; - } - - /** - * Test if this breakpoint is on a certain line. - * - * @param testLine the line id to test - * @return true if this breakpoint is on the given line - */ - public boolean isOnLine(LineID testLine) { - return line.equals(testLine); - } - - /** - * Attach this breakpoint to the VM. Creates and enables a - * {@link BreakpointRequest}. VM needs to be paused. - */ - protected void attach() { - if (!dbg.isPaused()) { - Logger.getLogger(LineBreakpoint.class.getName()).log(Level.WARNING, "can't attach breakpoint, debugger not paused"); - return; - } - - if (theClass == null) { - Logger.getLogger(LineBreakpoint.class.getName()).log(Level.WARNING, "can't attach breakpoint, class not loaded: {0}", className()); - return; - } - - // find line in java space - LineID javaLine = dbg.sketchToJavaLine(line); - if (javaLine == null) { - Logger.getLogger(LineBreakpoint.class.getName()).log(Level.WARNING, "couldn't find line {0} in the java code", line); - return; - } - try { - List locations = theClass.locationsOfLine(javaLine.lineIdx() + 1); - if (locations.isEmpty()) { - Logger.getLogger(LineBreakpoint.class.getName()).log(Level.WARNING, "no location found for line {0} -> {1}", new Object[]{line, javaLine}); - return; - } - // use first found location - bpr = dbg.vm().eventRequestManager().createBreakpointRequest(locations.get(0)); - bpr.enable(); - Logger.getLogger(LineBreakpoint.class.getName()).log(Level.INFO, "attached breakpoint to {0} -> {1}", new Object[]{line, javaLine}); - } catch (AbsentInformationException ex) { - Logger.getLogger(Debugger.class.getName()).log(Level.SEVERE, null, ex); - } - } - - /** - * Detach this breakpoint from the VM. Deletes the - * {@link BreakpointRequest}. - */ - protected void detach() { - if (bpr != null) { - dbg.vm().eventRequestManager().deleteEventRequest(bpr); - bpr = null; - } - } - - /** - * Set this breakpoint. Adds the line highlight. If Debugger is paused also - * attaches the breakpoint by calling {@link #attach()}. - */ - protected void set() { - dbg.addClassLoadListener(this); // class may not yet be loaded - dbg.editor().addBreakpointedLine(line); - if (theClass != null && dbg.isPaused()) { // class is loaded - // immediately activate the breakpoint - attach(); - } - if (dbg.editor().isInCurrentTab(line)) { - dbg.editor().getSketch().setModified(true); - } - } - - /** - * Remove this breakpoint. Clears the highlight and detaches the breakpoint - * if the debugger is paused. - */ - public void remove() { - dbg.removeClassLoadListener(this); - //System.out.println("removing " + line.lineIdx()); - dbg.editor().removeBreakpointedLine(line.lineIdx()); - if (dbg.isPaused()) { - // immediately remove the breakpoint - detach(); - } - line.stopTracking(); - if (dbg.editor().isInCurrentTab(line)) { - dbg.editor().getSketch().setModified(true); - } - } - -// public void enable() { -// } -// -// public void disable() { -// } - @Override - public String toString() { - return line.toString(); - } - - /** - * Get the name of the class this breakpoint belongs to. Needed for fetching - * the right location to create a breakpoint request. - * - * @return the class name - */ - protected String className() { - if (line.fileName().endsWith(".pde")) { - // standard tab - ReferenceType mainClass = dbg.getMainClass(); - if (mainClass == null) { - return null; - } - return dbg.getMainClass().name(); - } - - if (line.fileName().endsWith(".java")) { - // pure java tab - return line.fileName().substring(0, line.fileName().lastIndexOf(".java")); - } - - return null; - } - - /** - * Event handler called when a class is loaded in the debugger. Causes the - * breakpoint to be attached, if its class was loaded. - * - * @param theClass the class that was just loaded. - */ - @Override - public void classLoaded(ReferenceType theClass) { - // check if our class is being loaded - if (theClass.name().equals(className())) { - this.theClass = theClass; - attach(); - } - } -} diff --git a/experimental/src/processing/mode/experimental/LineHighlight.java b/experimental/src/processing/mode/experimental/LineHighlight.java deleted file mode 100755 index c1789cf24..000000000 --- a/experimental/src/processing/mode/experimental/LineHighlight.java +++ /dev/null @@ -1,196 +0,0 @@ -/* - * Copyright (C) 2012 Martin Leopold - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program 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 for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ -package processing.mode.experimental; - -import java.awt.Color; -import java.util.HashSet; -import java.util.Set; - -/** - * Model/Controller for a highlighted source code line. Implements a custom - * background color and a text based marker placed in the left-hand gutter area. - * - * @author Martin Leopold - */ -public class LineHighlight implements LineListener { - - protected DebugEditor editor; // the view, used for highlighting lines by setting a background color - protected Color bgColor; // the background color for highlighting lines - protected LineID lineID; // the id of the line - protected String marker; // - protected Color markerColor; - protected int priority = 0; - protected static Set allHighlights = new HashSet(); - - protected static boolean isHighestPriority(LineHighlight hl) { - for (LineHighlight check : allHighlights) { - if (check.lineID().equals(hl.lineID()) && check.priority() > hl.priority()) { - return false; - } - } - return true; - } - - /** - * Create a {@link LineHighlight}. - * - * @param lineID the line id to highlight - * @param bgColor the background color used for highlighting - * @param editor the {@link DebugEditor} - */ - public LineHighlight(LineID lineID, Color bgColor, DebugEditor editor) { - this.lineID = lineID; - this.bgColor = bgColor; - this.editor = editor; - lineID.addListener(this); - lineID.startTracking(editor.getTab(lineID.fileName()).getDocument()); // TODO: overwrite a previous doc? - paint(); // already checks if on current tab - allHighlights.add(this); - } - - public void setPriority(int p) { - this.priority = p; - } - - public int priority() { - return priority; - } - - /** - * Create a {@link LineHighlight} on the current tab. - * - * @param lineIdx the line index on the current tab to highlight - * @param bgColor the background color used for highlighting - * @param editor the {@link DebugEditor} - */ - // TODO: Remove and replace by {@link #LineHighlight(LineID lineID, Color bgColor, DebugEditor editor)} - public LineHighlight(int lineIdx, Color bgColor, DebugEditor editor) { - this(editor.getLineIDInCurrentTab(lineIdx), bgColor, editor); - } - - /** - * Set a text based marker displayed in the left hand gutter area of this - * highlighted line. - * - * @param marker the marker text - */ - public void setMarker(String marker) { - this.marker = marker; - paint(); - } - - /** - * Set a text based marker displayed in the left hand gutter area of this - * highlighted line. Also use a custom text color. - * - * @param marker the marker text - * @param markerColor the text color - */ - public void setMarker(String marker, Color markerColor) { - this.markerColor = markerColor; - setMarker(marker); - } - - /** - * Retrieve the line id of this {@link LineHighlight}. - * - * @return the line id - */ - public LineID lineID() { - return lineID; - } - - /** - * Retrieve the color for highlighting this line. - * - * @return the highlight color. - */ - public Color getColor() { - return bgColor; - } - - /** - * Test if this highlight is on a certain line. - * - * @param testLine the line to test - * @return true if this highlight is on the given line - */ - public boolean isOnLine(LineID testLine) { - return lineID.equals(testLine); - } - - /** - * Event handler for line number changes (due to editing). Will remove the - * highlight from the old line number and repaint it at the new location. - * - * @param line the line that has changed - * @param oldLineIdx the old line index (0-based) - * @param newLineIdx the new line index (0-based) - */ - @Override - public void lineChanged(LineID line, int oldLineIdx, int newLineIdx) { - // clear old line - if (editor.isInCurrentTab(new LineID(line.fileName(), oldLineIdx))) { - editor.textArea().clearLineBgColor(oldLineIdx); - editor.textArea().clearGutterText(oldLineIdx); - } - - // paint new line - // but only if it's on top -> fixes current line being hidden by breakpoint moving it down. - // lineChanged events seem to come in inverse order of startTracking the LineID. (and bp is created first...) - if (LineHighlight.isHighestPriority(this)) { - paint(); - } - } - - /** - * Notify this line highlight that it is no longer used. Call this for - * cleanup before the {@link LineHighlight} is discarded. - */ - public void dispose() { - lineID.removeListener(this); - lineID.stopTracking(); - allHighlights.remove(this); - } - - /** - * (Re-)paint this line highlight. - */ - public void paint() { - if (editor.isInCurrentTab(lineID)) { - editor.textArea().setLineBgColor(lineID.lineIdx(), bgColor); - if (marker != null) { - if (markerColor != null) { - editor.textArea().setGutterText(lineID.lineIdx(), marker, markerColor); - } else { - editor.textArea().setGutterText(lineID.lineIdx(), marker); - } - } - } - } - - /** - * Clear this line highlight. - */ - public void clear() { - if (editor.isInCurrentTab(lineID)) { - editor.textArea().clearLineBgColor(lineID.lineIdx()); - editor.textArea().clearGutterText(lineID.lineIdx()); - } - } -} diff --git a/experimental/src/processing/mode/experimental/LineID.java b/experimental/src/processing/mode/experimental/LineID.java deleted file mode 100755 index a08d21296..000000000 --- a/experimental/src/processing/mode/experimental/LineID.java +++ /dev/null @@ -1,269 +0,0 @@ -/* - * Copyright (C) 2012 Martin Leopold - * - * This program is free software; you can redistribute it and/or modify it under - * the terms of the GNU General Public License as published by the Free Software - * Foundation; either version 2 of the License, or (at your option) any later - * version. - * - * This program 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 for more - * details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., 59 Temple - * Place - Suite 330, Boston, MA 02111-1307, USA. - */ -package processing.mode.experimental; - -import java.util.HashSet; -import java.util.Set; -import java.util.logging.Level; -import java.util.logging.Logger; -import javax.swing.event.DocumentEvent; -import javax.swing.event.DocumentListener; -import javax.swing.text.BadLocationException; -import javax.swing.text.Document; -import javax.swing.text.Element; -import javax.swing.text.Position; - -/** - * Describes an ID for a code line. Comprised of a file name and a (0-based) - * line number. Can track changes to the line number due to text editing by - * attaching a {@link Document}. Registered {@link LineListener}s are notified - * of changes to the line number. - * - * @author Martin Leopold - */ -public class LineID implements DocumentListener { - - protected String fileName; // the filename - protected int lineIdx; // the line number, 0-based - protected Document doc; // the Document to use for line number tracking - protected Position pos; // the Position acquired during line number tracking - protected Set listeners = new HashSet(); // listeners for line number changes - - public LineID(String fileName, int lineIdx) { - this.fileName = fileName; - this.lineIdx = lineIdx; - } - - /** - * Get the file name of this line. - * - * @return the file name - */ - public String fileName() { - return fileName; - } - - /** - * Get the (0-based) line number of this line. - * - * @return the line index (i.e. line number, starting at 0) - */ - public synchronized int lineIdx() { - return lineIdx; - } - - @Override - public int hashCode() { - return toString().hashCode(); - } - - /** - * Test whether this {@link LineID} is equal to another object. Two - * {@link LineID}'s are equal when both their fileName and lineNo are equal. - * - * @param obj the object to test for equality - * @return {@code true} if equal - */ - @Override - public boolean equals(Object obj) { - if (obj == null) { - return false; - } - if (getClass() != obj.getClass()) { - return false; - } - final LineID other = (LineID) obj; - if ((this.fileName == null) ? (other.fileName != null) : !this.fileName.equals(other.fileName)) { - return false; - } - if (this.lineIdx != other.lineIdx) { - return false; - } - return true; - } - - /** - * Output a string representation in the form fileName:lineIdx+1. Note this - * uses a 1-based line number as is customary for human-readable line - * numbers. - * - * @return the string representation of this line ID - */ - @Override - public String toString() { - return fileName + ":" + (lineIdx + 1); - } - -// /** -// * Retrieve a copy of this line ID. -// * -// * @return the copy -// */ -// @Override -// public LineID clone() { -// return new LineID(fileName, lineIdx); -// } - - /** - * Attach a {@link Document} to enable line number tracking when editing. - * The position to track is before the first non-whitespace character on the - * line. Edits happening before that position will cause the line number to - * update accordingly. Multiple {@link #startTracking} calls will replace - * the tracked document. Whoever wants a tracked line should track it and - * add itself as listener if necessary. - * ({@link LineHighlight}, {@link LineBreakpoint}) - * - * @param doc the {@link Document} to use for line number tracking - */ - public synchronized void startTracking(Document doc) { - //System.out.println("tracking: " + this); - if (doc == null) { - return; // null arg - } - if (doc == this.doc) { - return; // already tracking that doc - } - try { - Element line = doc.getDefaultRootElement().getElement(lineIdx); - if (line == null) { - return; // line doesn't exist - } - String lineText = doc.getText(line.getStartOffset(), line.getEndOffset() - line.getStartOffset()); - // set tracking position at (=before) first non-white space character on line - pos = doc.createPosition(line.getStartOffset() + nonWhiteSpaceOffset(lineText)); - this.doc = doc; - doc.addDocumentListener(this); - } catch (BadLocationException ex) { - Logger.getLogger(LineID.class.getName()).log(Level.SEVERE, null, ex); - pos = null; - this.doc = null; - } - } - - /** - * Notify this {@link LineID} that it is no longer in use. Will stop - * position tracking. Call this when this {@link LineID} is no longer - * needed. - */ - public synchronized void stopTracking() { - if (doc != null) { - doc.removeDocumentListener(this); - doc = null; - } - } - - /** - * Update the tracked position. Will notify listeners if line number has - * changed. - */ - protected synchronized void updatePosition() { - if (doc != null && pos != null) { - // track position - int offset = pos.getOffset(); - int oldLineIdx = lineIdx; - lineIdx = doc.getDefaultRootElement().getElementIndex(offset); // offset to lineNo - if (lineIdx != oldLineIdx) { - for (LineListener l : listeners) { - if (l != null) { - l.lineChanged(this, oldLineIdx, lineIdx); - } else { - listeners.remove(l); // remove null listener - } - } - } - } - } - - /** - * Add listener to be notified when the line number changes. - * - * @param l the listener to add - */ - public void addListener(LineListener l) { - listeners.add(l); - } - - /** - * Remove a listener for line number changes. - * - * @param l the listener to remove - */ - public void removeListener(LineListener l) { - listeners.remove(l); - } - - /** - * Calculate the offset of the first non-whitespace character in a string. - * - * @param str the string to examine - * @return offset of first non-whitespace character in str - */ - protected static int nonWhiteSpaceOffset(String str) { - for (int i = 0; i < str.length(); i++) { - if (!Character.isWhitespace(str.charAt(i))) { - return i; - } - } - return str.length(); - } - - /** - * Called when the {@link Document} registered using {@link #startTracking} - * is edited. This happens when text is inserted or removed. - * - * @param de - */ - protected void editEvent(DocumentEvent de) { - //System.out.println("document edit @ " + de.getOffset()); - if (de.getOffset() <= pos.getOffset()) { - updatePosition(); - //System.out.println("updating, new line no: " + lineNo); - } - } - - /** - * {@link DocumentListener} callback. Called when text is inserted. - * - * @param de - */ - @Override - public void insertUpdate(DocumentEvent de) { - editEvent(de); - } - - /** - * {@link DocumentListener} callback. Called when text is removed. - * - * @param de - */ - @Override - public void removeUpdate(DocumentEvent de) { - editEvent(de); - } - - /** - * {@link DocumentListener} callback. Called when attributes are changed. - * Not used. - * - * @param de - */ - @Override - public void changedUpdate(DocumentEvent de) { - // not needed. - } -} diff --git a/experimental/src/processing/mode/experimental/LineListener.java b/experimental/src/processing/mode/experimental/LineListener.java deleted file mode 100755 index c6c3ae1b0..000000000 --- a/experimental/src/processing/mode/experimental/LineListener.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (C) 2012 Martin Leopold - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program 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 for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ -package processing.mode.experimental; - -/** - * A Listener for line number changes. - * - * @author Martin Leopold - */ -public interface LineListener { - - /** - * Event handler for line number changes (due to editing). - * - * @param line the line that has changed - * @param oldLineIdx the old line index (0-based) - * @param newLineIdx the new line index (0-based) - */ - void lineChanged(LineID line, int oldLineIdx, int newLineIdx); -} diff --git a/experimental/src/processing/mode/experimental/LocalVariableNode.java b/experimental/src/processing/mode/experimental/LocalVariableNode.java deleted file mode 100755 index d1bdb2092..000000000 --- a/experimental/src/processing/mode/experimental/LocalVariableNode.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright (C) 2012 Martin Leopold - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program 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 for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ -package processing.mode.experimental; - -import com.sun.jdi.ClassNotLoadedException; -import com.sun.jdi.InvalidTypeException; -import com.sun.jdi.LocalVariable; -import com.sun.jdi.StackFrame; -import com.sun.jdi.Value; -import java.util.logging.Level; -import java.util.logging.Logger; - -/** - * Specialized {@link VariableNode} for representing local variables. Overrides - * {@link #setValue} to properly change the value of the encapsulated local - * variable. - * - * @author Martin Leopold - */ -public class LocalVariableNode extends VariableNode { - - protected LocalVariable var; - protected StackFrame frame; - - /** - * Construct a {@link LocalVariableNode}. - * - * @param name the name - * @param type the type - * @param value the value - * @param var the local variable - * @param frame the stack frame containing the local variable - */ - public LocalVariableNode(String name, String type, Value value, LocalVariable var, StackFrame frame) { - super(name, type, value); - this.var = var; - this.frame = frame; - } - - @Override - public void setValue(Value value) { - try { - frame.setValue(var, value); - } catch (InvalidTypeException ex) { - Logger.getLogger(LocalVariableNode.class.getName()).log(Level.SEVERE, null, ex); - } catch (ClassNotLoadedException ex) { - Logger.getLogger(LocalVariableNode.class.getName()).log(Level.SEVERE, null, ex); - } - this.value = value; - } -} diff --git a/experimental/src/processing/mode/experimental/Problem.java b/experimental/src/processing/mode/experimental/Problem.java deleted file mode 100755 index 2d206b606..000000000 --- a/experimental/src/processing/mode/experimental/Problem.java +++ /dev/null @@ -1,160 +0,0 @@ -/* - Part of the XQMode project - https://github.com/Manindra29/XQMode - - Under Google Summer of Code 2012 - - http://www.google-melange.com/gsoc/homepage/google/gsoc2012 - - Copyright (C) 2012 Manindra Moharana - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License version 2 - as published by the Free Software Foundation. - - This program 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 for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software Foundation, - Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -package processing.mode.experimental; - -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import org.eclipse.jdt.core.compiler.IProblem; - -/** - * Wrapper class for IProblem. - * - * Stores the tabIndex and line number according to its tab, including the - * original IProblem object - * - * @author Manindra Moharana <me@mkmoharana.com> - * - */ -public class Problem { - /** - * The IProblem which is being wrapped - */ - private IProblem iProblem; - /** - * The tab number to which the error belongs to - */ - public int tabIndex; - /** - * Line number(pde code) of the error - */ - public int lineNumber; - - /** - * Error Message. Processed form of IProblem.getMessage() - */ - public String message; - - /** - * The type of error - WARNING or ERROR. - */ - public int type; - - public static final int ERROR = 1, WARNING = 2; - - /** - * - * @param iProblem - The IProblem which is being wrapped - * @param tabIndex - The tab number to which the error belongs to - * @param lineNumber - Line number(pde code) of the error - */ - public Problem(IProblem iProblem, int tabIndex, int lineNumber) { - this.iProblem = iProblem; - if(iProblem.isError()) { - type = ERROR; - } - else if(iProblem.isWarning()) { - type = WARNING; - } - this.tabIndex = tabIndex; - this.lineNumber = lineNumber; - this.message = process(iProblem); - } - - public String toString() { - return new String("TAB " + tabIndex + ",LN " + lineNumber + ",PROB: " - + message); - } - - public boolean isError(){ - return type == ERROR; - } - - public boolean isWarning(){ - return type == WARNING; - } - - public String getMessage(){ - return message; - } - - public IProblem getIProblem(){ - return iProblem; - } - - public void setType(int ProblemType){ - if(ProblemType == ERROR) - type = ERROR; - else if(ProblemType == WARNING) - type = WARNING; - else throw new IllegalArgumentException("Illegal Problem type passed to Problem.setType(int)"); - } - - private static Pattern pattern; - private static Matcher matcher; - - private static final String tokenRegExp = "\\b token\\b"; - - public static String process(IProblem problem) { - return process(problem.getMessage()); - } - - /** - * Processes error messages and attempts to make them a bit more english like. - * Currently performs: - *
  • Remove all instances of token. "Syntax error on token 'blah', delete this token" - * becomes "Syntax error on 'blah', delete this" - * @param message - The message to be processed - * @return String - The processed message - */ - public static String process(String message) { - // Remove all instances of token - // "Syntax error on token 'blah', delete this token" - - pattern = Pattern.compile(tokenRegExp); - matcher = pattern.matcher(message); - message = matcher.replaceAll(""); - - return message; - } - - // Split camel case words into separate words. - // "VaraibleDeclaration" becomes "Variable Declaration" - // But sadly "PApplet" become "P Applet" and so on. - public static String splitCamelCaseWord(String word) { - String newWord = ""; - for (int i = 1; i < word.length(); i++) { - if (Character.isUpperCase(word.charAt(i))) { - // System.out.println(word.substring(0, i) + " " - // + word.substring(i)); - newWord += word.substring(0, i) + " "; - word = word.substring(i); - i = 1; - } - } - newWord += word; - // System.out.println(newWord); - return newWord.trim(); - } - -} diff --git a/experimental/src/processing/mode/experimental/TextArea.java b/experimental/src/processing/mode/experimental/TextArea.java deleted file mode 100755 index 87722498a..000000000 --- a/experimental/src/processing/mode/experimental/TextArea.java +++ /dev/null @@ -1,350 +0,0 @@ -/* - * Copyright (C) 2012 Martin Leopold - * - * This program is free software; you can redistribute it and/or modify it under - * the terms of the GNU General Public License as published by the Free Software - * Foundation; either version 2 of the License, or (at your option) any later - * version. - * - * This program 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 for more - * details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., 59 Temple - * Place - Suite 330, Boston, MA 02111-1307, USA. - */ -package processing.mode.experimental; - -import java.awt.Color; -import java.awt.Cursor; -import java.awt.FontMetrics; -import java.awt.event.ComponentListener; -import java.awt.event.KeyEvent; -import java.awt.event.MouseEvent; -import java.awt.event.MouseListener; -import java.awt.event.MouseMotionListener; -import java.util.HashMap; -import java.util.Map; - -import processing.app.syntax.JEditTextArea; -import processing.app.syntax.TextAreaDefaults; - -/** - * Customized text area. Adds support for line background colors. - * - * @author Martin Leopold - */ -public class TextArea extends JEditTextArea { - - protected MouseListener[] mouseListeners; // cached mouselisteners, these are wrapped by MouseHandler - protected DebugEditor editor; // the editor - // line properties - protected Map lineColors = new HashMap(); // contains line background colors - // left-hand gutter properties - protected int gutterPadding = 3; // [px] space added to the left and right of gutter chars - protected Color gutterBgColor = new Color(252, 252, 252); // gutter background color - protected Color gutterLineColor = new Color(233, 233, 233); // color of vertical separation line - protected String breakpointMarker = "<>"; // the text marker for highlighting breakpoints in the gutter - protected String currentLineMarker = "->"; // the text marker for highlighting the current line in the gutter - protected Map gutterText = new HashMap(); // maps line index to gutter text - protected Map gutterTextColors = new HashMap(); // maps line index to gutter text color - protected TextAreaPainter customPainter; - protected ErrorCheckerService errorCheckerService; - - public TextArea(TextAreaDefaults defaults, DebugEditor editor) { - super(defaults); - this.editor = editor; - - // replace the painter: - // first save listeners, these are package-private in JEditTextArea, so not accessible - ComponentListener[] componentListeners = painter.getComponentListeners(); - mouseListeners = painter.getMouseListeners(); - MouseMotionListener[] mouseMotionListeners = painter.getMouseMotionListeners(); - - remove(painter); - - // set new painter - customPainter = new TextAreaPainter(this, defaults); - painter = customPainter; - - // set listeners - for (ComponentListener cl : componentListeners) { - painter.addComponentListener(cl); - } - - for (MouseMotionListener mml : mouseMotionListeners) { - painter.addMouseMotionListener(mml); - } - - // use a custom mouse handler instead of directly using mouseListeners - MouseHandler mouseHandler = new MouseHandler(); - painter.addMouseListener(mouseHandler); - painter.addMouseMotionListener(mouseHandler); - - add(CENTER, painter); - - // load settings from theme.txt - ExperimentalMode theme = (ExperimentalMode) editor.getMode(); - gutterBgColor = theme.getThemeColor("gutter.bgcolor", gutterBgColor); - gutterLineColor = theme.getThemeColor("gutter.linecolor", gutterLineColor); - gutterPadding = theme.getInteger("gutter.padding"); - breakpointMarker = theme.loadThemeString("breakpoint.marker", breakpointMarker); - currentLineMarker = theme.loadThemeString("currentline.marker", currentLineMarker); - } - - /** - * Sets ErrorCheckerService and loads theme for TextArea(XQMode) - * @param ecs - * @param mode - */ - public void setECSandThemeforTextArea(ErrorCheckerService ecs, ExperimentalMode mode) - { - errorCheckerService = ecs; - customPainter.setECSandTheme(ecs, mode); - } - - public void processKeyEvent(KeyEvent evt) { - super.processKeyEvent(evt); - if(evt.getID() == KeyEvent.KEY_TYPED){ - errorCheckerService.textModified.incrementAndGet(); - } - } - - /** - * Retrieve the total width of the gutter area. - * - * @return gutter width in pixels - */ - protected int getGutterWidth() { - FontMetrics fm = painter.getFontMetrics(); -// System.out.println("fm: " + (fm == null)); -// System.out.println("editor: " + (editor == null)); - //System.out.println("BPBPBPBPB: " + (editor.breakpointMarker == null)); - - int textWidth = Math.max(fm.stringWidth(breakpointMarker), fm.stringWidth(currentLineMarker)); - return textWidth + 2 * gutterPadding; - } - - /** - * Retrieve the width of margins applied to the left and right of the gutter - * text. - * - * @return margins in pixels - */ - protected int getGutterMargins() { - return gutterPadding; - } - - /** - * Set the gutter text of a specific line. - * - * @param lineIdx the line index (0-based) - * @param text the text - */ - public void setGutterText(int lineIdx, String text) { - gutterText.put(lineIdx, text); - painter.invalidateLine(lineIdx); - } - - /** - * Set the gutter text and color of a specific line. - * - * @param lineIdx the line index (0-based) - * @param text the text - * @param textColor the text color - */ - public void setGutterText(int lineIdx, String text, Color textColor) { - gutterTextColors.put(lineIdx, textColor); - setGutterText(lineIdx, text); - } - - /** - * Clear the gutter text of a specific line. - * - * @param lineIdx the line index (0-based) - */ - public void clearGutterText(int lineIdx) { - gutterText.remove(lineIdx); - painter.invalidateLine(lineIdx); - } - - /** - * Clear all gutter text. - */ - public void clearGutterText() { - for (int lineIdx : gutterText.keySet()) { - painter.invalidateLine(lineIdx); - } - gutterText.clear(); - } - - /** - * Retrieve the gutter text of a specific line. - * - * @param lineIdx the line index (0-based) - * @return the gutter text - */ - public String getGutterText(int lineIdx) { - return gutterText.get(lineIdx); - } - - /** - * Retrieve the gutter text color for a specific line. - * - * @param lineIdx the line index - * @return the gutter text color - */ - public Color getGutterTextColor(int lineIdx) { - return gutterTextColors.get(lineIdx); - } - - /** - * Set the background color of a line. - * - * @param lineIdx 0-based line number - * @param col the background color to set - */ - public void setLineBgColor(int lineIdx, Color col) { - lineColors.put(lineIdx, col); - painter.invalidateLine(lineIdx); - } - - /** - * Clear the background color of a line. - * - * @param lineIdx 0-based line number - */ - public void clearLineBgColor(int lineIdx) { - lineColors.remove(lineIdx); - painter.invalidateLine(lineIdx); - } - - /** - * Clear all line background colors. - */ - public void clearLineBgColors() { - for (int lineIdx : lineColors.keySet()) { - painter.invalidateLine(lineIdx); - } - lineColors.clear(); - } - - /** - * Get a lines background color. - * - * @param lineIdx 0-based line number - * @return the color or null if no color was set for the specified line - */ - public Color getLineBgColor(int lineIdx) { - return lineColors.get(lineIdx); - } - - /** - * Convert a character offset to a horizontal pixel position inside the text - * area. Overridden to take gutter width into account. - * - * @param line the 0-based line number - * @param offset the character offset (0 is the first character on a line) - * @return the horizontal position - */ - @Override - public int _offsetToX(int line, int offset) { - return super._offsetToX(line, offset) + getGutterWidth(); - } - - /** - * Convert a horizontal pixel position to a character offset. Overridden to - * take gutter width into account. - * - * @param line the 0-based line number - * @param x the horizontal pixel position - * @return he character offset (0 is the first character on a line) - */ - @Override - public int xToOffset(int line, int x) { - return super.xToOffset(line, x - getGutterWidth()); - } - - /** - * Custom mouse handler. Implements double clicking in the gutter area to - * toggle breakpoints, sets default cursor (instead of text cursor) in the - * gutter area. - */ - protected class MouseHandler implements MouseListener, MouseMotionListener { - - protected int lastX; // previous horizontal positon of the mouse cursor - - @Override - public void mouseClicked(MouseEvent me) { - // forward to standard listeners - for (MouseListener ml : mouseListeners) { - ml.mouseClicked(me); - } - } - - @Override - public void mousePressed(MouseEvent me) { - // check if this happened in the gutter area - if (me.getX() < getGutterWidth()) { - if (me.getButton() == MouseEvent.BUTTON1 && me.getClickCount() == 2) { - int line = me.getY() / painter.getFontMetrics().getHeight() + firstLine; - if (line >= 0 && line <= getLineCount() - 1) { - editor.gutterDblClicked(line); - } - } - } else { - // forward to standard listeners - for (MouseListener ml : mouseListeners) { - ml.mousePressed(me); - } - } - } - - @Override - public void mouseReleased(MouseEvent me) { - // forward to standard listeners - for (MouseListener ml : mouseListeners) { - ml.mouseReleased(me); - } - } - - @Override - public void mouseEntered(MouseEvent me) { - // forward to standard listeners - for (MouseListener ml : mouseListeners) { - ml.mouseEntered(me); - } - } - - @Override - public void mouseExited(MouseEvent me) { - // forward to standard listeners - for (MouseListener ml : mouseListeners) { - ml.mouseExited(me); - } - } - - @Override - public void mouseDragged(MouseEvent me) { - // No need to forward since the standard MouseMotionListeners are called anyway - // nop - } - - @Override - public void mouseMoved(MouseEvent me) { - // No need to forward since the standard MouseMotionListeners are called anyway - if (me.getX() < getGutterWidth()) { - if (lastX >= getGutterWidth()) { - painter.setCursor(new Cursor(Cursor.DEFAULT_CURSOR)); - } - } else { - if (lastX < getGutterWidth()) { - painter.setCursor(new Cursor(Cursor.TEXT_CURSOR)); - } - } - lastX = me.getX(); - } - } -} diff --git a/experimental/src/processing/mode/experimental/TextAreaPainter.java b/experimental/src/processing/mode/experimental/TextAreaPainter.java deleted file mode 100755 index 8c93978f2..000000000 --- a/experimental/src/processing/mode/experimental/TextAreaPainter.java +++ /dev/null @@ -1,318 +0,0 @@ -/* - * Copyright (C) 2012 Martin Leopold - * - * This program is free software; you can redistribute it and/or modify it under - * the terms of the GNU General Public License as published by the Free Software - * Foundation; either version 2 of the License, or (at your option) any later - * version. - * - * This program 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 for more - * details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., 59 Temple - * Place - Suite 330, Boston, MA 02111-1307, USA. - */ -package processing.mode.experimental; - -import java.awt.Color; -import java.awt.Graphics; - -import javax.swing.text.BadLocationException; -import javax.swing.text.Segment; -import javax.swing.text.Utilities; - -import processing.app.syntax.TextAreaDefaults; -import processing.app.syntax.TokenMarker; - -/** - * Customized line painter. Adds support for background colors, left hand gutter - * area with background color and text. - * - * @author Martin Leopold - */ -public class TextAreaPainter extends processing.app.syntax.TextAreaPainter { - - protected TextArea ta; // we need the subclassed textarea - protected ErrorCheckerService errorCheckerService; - - /** - * Error line underline color - */ - public Color errorColor = new Color(0xED2630); - - /** - * Warning line underline color - */ - - public Color warningColor = new Color(0xFFC30E); - - /** - * Color of Error Marker - */ - public Color errorMarkerColor = new Color(0xED2630); - - /** - * Color of Warning Marker - */ - public Color warningMarkerColor = new Color(0xFFC30E); - - public TextAreaPainter(TextArea textArea, TextAreaDefaults defaults) { - super(textArea, defaults); - ta = textArea; - } - - private void loadTheme(ExperimentalMode mode){ - errorColor = mode.getThemeColor("editor.errorcolor", errorColor); - warningColor = mode.getThemeColor("editor.warningcolor", - warningColor); - errorMarkerColor = mode.getThemeColor("editor.errormarkercolor", - errorMarkerColor); - warningMarkerColor = mode.getThemeColor( - "editor.warningmarkercolor", warningMarkerColor); - } - - /** - * Paint a line. Paints the gutter (with background color and text) then the - * line (background color and text). - * - * @param gfx the graphics context - * @param tokenMarker - * @param line 0-based line number - * @param x horizontal position - */ - @Override - protected void paintLine(Graphics gfx, TokenMarker tokenMarker, - int line, int x) { - - // paint gutter - paintGutterBg(gfx, line, x); - - paintLineBgColor(gfx, line, x + ta.getGutterWidth()); - - paintGutterLine(gfx, line, x); - - // paint gutter symbol - paintGutterText(gfx, line, x); - - paintErrorLine(gfx, line, x); - - super.paintLine(gfx, tokenMarker, line, x + ta.getGutterWidth()); - } - - /** - * Paint the gutter background (solid color). - * - * @param gfx the graphics context - * @param line 0-based line number - * @param x horizontal position - */ - protected void paintGutterBg(Graphics gfx, int line, int x) { - gfx.setColor(ta.gutterBgColor); - int y = ta.lineToY(line) + fm.getLeading() + fm.getMaxDescent(); - gfx.fillRect(0, y, ta.getGutterWidth(), fm.getHeight()); - } - - /** - * Paint the vertical gutter separator line. - * - * @param gfx the graphics context - * @param line 0-based line number - * @param x horizontal position - */ - protected void paintGutterLine(Graphics gfx, int line, int x) { - int y = ta.lineToY(line) + fm.getLeading() + fm.getMaxDescent(); - gfx.setColor(ta.gutterLineColor); - gfx.drawLine(ta.getGutterWidth(), y, ta.getGutterWidth(), y + fm.getHeight()); - } - - /** - * Paint the gutter text. - * - * @param gfx the graphics context - * @param line 0-based line number - * @param x horizontal position - */ - protected void paintGutterText(Graphics gfx, int line, int x) { - String text = ta.getGutterText(line); - if (text == null) { - return; - } - - gfx.setFont(getFont()); - Color textColor = ta.getGutterTextColor(line); - if (textColor == null) { - gfx.setColor(getForeground()); - } else { - gfx.setColor(textColor); - } - int y = ta.lineToY(line) + fm.getHeight(); - - // draw 4 times to make it appear bold, displaced 1px to the right, to the bottom and bottom right. - //int len = text.length() > ta.gutterChars ? ta.gutterChars : text.length(); - Utilities.drawTabbedText(new Segment(text.toCharArray(), 0, text.length()), ta.getGutterMargins(), y, gfx, this, 0); - Utilities.drawTabbedText(new Segment(text.toCharArray(), 0, text.length()), ta.getGutterMargins() + 1, y, gfx, this, 0); - Utilities.drawTabbedText(new Segment(text.toCharArray(), 0, text.length()), ta.getGutterMargins(), y + 1, gfx, this, 0); - Utilities.drawTabbedText(new Segment(text.toCharArray(), 0, text.length()), ta.getGutterMargins() + 1, y + 1, gfx, this, 0); - } - - /** - * Paint the background color of a line. - * - * @param gfx the graphics context - * @param line 0-based line number - * @param x - */ - protected void paintLineBgColor(Graphics gfx, int line, int x) { - int y = ta.lineToY(line); - y += fm.getLeading() + fm.getMaxDescent(); - int height = fm.getHeight(); - - // get the color - Color col = ta.getLineBgColor(line); - //System.out.print("bg line " + line + ": "); - // no need to paint anything - if (col == null) { - //System.out.println("none"); - return; - } - // paint line background - gfx.setColor(col); - gfx.fillRect(0, y, getWidth(), height); - } - - /** - * Paints the underline for an error/warning line - * - * @param gfx - * the graphics context - * @param tokenMarker - * @param line - * 0-based line number: NOTE - * @param x - */ - protected void paintErrorLine(Graphics gfx, int line, int x) { - - if (errorCheckerService == null) { - return; - } - - if (errorCheckerService.problemsList== null) { - return; - } - - boolean notFound = true; - boolean isWarning = false; - - // Check if current line contains an error. If it does, find if it's an - // error or warning - for (ErrorMarker emarker : errorCheckerService.getEditor().errorBar.errorPoints) { - if (emarker.problem.lineNumber == line + 1) { - notFound = false; - if (emarker.type == ErrorMarker.Warning) { - isWarning = true; - } - break; - } - } - - if (notFound) { - return; - } - - // Determine co-ordinates - // System.out.println("Hoff " + ta.getHorizontalOffset() + ", " + - // horizontalAdjustment); - int y = ta.lineToY(line); - y += fm.getLeading() + fm.getMaxDescent(); - int height = fm.getHeight(); - int start = ta.getLineStartOffset(line); - - try { - String linetext = null; - - try { - linetext = ta.getDocument().getText(start, - ta.getLineStopOffset(line) - start - 1); - } catch (BadLocationException bl) { - // Error in the import statements or end of code. - // System.out.print("BL caught. " + ta.getLineCount() + " ," - // + line + " ,"); - // System.out.println((ta.getLineStopOffset(line) - start - 1)); - return; - } - - // Take care of offsets - int aw = fm.stringWidth(trimRight(linetext)) - + ta.getHorizontalOffset(); // apparent width. Whitespaces - // to the left of line + text - // width - int rw = fm.stringWidth(linetext.trim()); // real width - int x1 = 0 + (aw - rw), y1 = y + fm.getHeight() - 2, x2 = x1 + rw; - // Adding offsets for the gutter - x1 += 20; - x2 += 20; - - // gfx.fillRect(x1, y, rw, height); - - // Let the painting begin! - gfx.setColor(errorMarkerColor); - if (isWarning) { - gfx.setColor(warningMarkerColor); - } - gfx.fillRect(1, y + 2, 3, height - 2); - - gfx.setColor(errorColor); - if (isWarning) { - gfx.setColor(warningColor); - } - int xx = x1; - - // Draw the jagged lines - while (xx < x2) { - gfx.drawLine(xx, y1, xx + 2, y1 + 1); - xx += 2; - gfx.drawLine(xx, y1 + 1, xx + 2, y1); - xx += 2; - } - } catch (Exception e) { - System.out - .println("Looks like I messed up! XQTextAreaPainter.paintLine() : " - + e); - //e.printStackTrace(); - } - - // Won't highlight the line. Select the text instead. - // gfx.setColor(Color.RED); - // gfx.fillRect(2, y, 3, height); - } - - /** - * Trims out trailing whitespaces (to the right) - * - * @param string - * @return - String - */ - private String trimRight(String string) { - String newString = ""; - for (int i = 0; i < string.length(); i++) { - if (string.charAt(i) != ' ') { - newString = string.substring(0, i) + string.trim(); - break; - } - } - return newString; - } - - /** - * Sets ErrorCheckerService and loads theme for TextAreaPainter(XQMode) - * @param ecs - * @param mode - */ - public void setECSandTheme(ErrorCheckerService ecs, ExperimentalMode mode){ - this.errorCheckerService = ecs; - loadTheme(mode); - } -} diff --git a/experimental/src/processing/mode/experimental/VMEventListener.java b/experimental/src/processing/mode/experimental/VMEventListener.java deleted file mode 100755 index 4cc648802..000000000 --- a/experimental/src/processing/mode/experimental/VMEventListener.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (C) 2012 Martin Leopold - * - * This program is free software; you can redistribute it and/or modify it under - * the terms of the GNU General Public License as published by the Free Software - * Foundation; either version 2 of the License, or (at your option) any later - * version. - * - * This program 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 for more - * details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., 59 Temple - * Place - Suite 330, Boston, MA 02111-1307, USA. - */ -package processing.mode.experimental; - -import com.sun.jdi.event.EventSet; - -/** - * Interface for VM callbacks. - * - * @author Martin Leopold - */ -public interface VMEventListener { - - /** - * Receive an event from the VM. Events are sent in batches. See - * documentation of EventSet for more information. - * - * @param es Set of events - */ - void vmEvent(EventSet es); -} diff --git a/experimental/src/processing/mode/experimental/VMEventReader.java b/experimental/src/processing/mode/experimental/VMEventReader.java deleted file mode 100755 index c4d05ddf9..000000000 --- a/experimental/src/processing/mode/experimental/VMEventReader.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright (C) 2012 Martin Leopold - * - * This program is free software; you can redistribute it and/or modify it under - * the terms of the GNU General Public License as published by the Free Software - * Foundation; either version 2 of the License, or (at your option) any later - * version. - * - * This program 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 for more - * details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., 59 Temple - * Place - Suite 330, Boston, MA 02111-1307, USA. - */ -package processing.mode.experimental; - -import com.sun.jdi.VMDisconnectedException; -import com.sun.jdi.event.EventQueue; -import com.sun.jdi.event.EventSet; -import java.util.logging.Level; -import java.util.logging.Logger; - -/** - * Reader Thread for VM Events. Constantly monitors a VMs EventQueue for new - * events and forwards them to an VMEventListener. - * - * @author Martin Leopold - */ -public class VMEventReader extends Thread { - - EventQueue eventQueue; - VMEventListener listener; - - /** - * Construct a VMEventReader. Needs to be kicked off with start() once - * constructed. - * - * @param eventQueue The queue to read events from. Can be obtained from a - * VirtualMachine via eventQueue(). - * @param listener the listener to forward events to. - */ - public VMEventReader(EventQueue eventQueue, VMEventListener listener) { - super("VM Event Thread"); - this.eventQueue = eventQueue; - this.listener = listener; - } - - @Override - public void run() { - try { - while (true) { - EventSet eventSet = eventQueue.remove(); - listener.vmEvent(eventSet); - /* - * for (Event e : eventSet) { System.out.println("VM Event: " + - * e.toString()); } - */ - } - } catch (VMDisconnectedException e) { - Logger.getLogger(VMEventReader.class.getName()).log(Level.INFO, "VMEventReader quit on VM disconnect"); - } catch (Exception e) { - Logger.getLogger(VMEventReader.class.getName()).log(Level.SEVERE, "VMEventReader quit", e); - } - } -} diff --git a/experimental/src/processing/mode/experimental/VariableInspector.java b/experimental/src/processing/mode/experimental/VariableInspector.java deleted file mode 100755 index a38c19a8f..000000000 --- a/experimental/src/processing/mode/experimental/VariableInspector.java +++ /dev/null @@ -1,929 +0,0 @@ -/* - * Copyright (C) 2012 Martin Leopold - * - * This program is free software; you can redistribute it and/or modify it under - * the terms of the GNU General Public License as published by the Free Software - * Foundation; either version 2 of the License, or (at your option) any later - * version. - * - * This program 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 for more - * details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., 59 Temple - * Place - Suite 330, Boston, MA 02111-1307, USA. - */ -package processing.mode.experimental; - -import com.sun.jdi.Value; -import java.awt.Color; -import java.awt.Component; -import java.awt.Font; -import java.awt.Graphics; -import java.awt.Image; -import java.awt.image.BufferedImage; -import java.io.File; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.logging.Level; -import java.util.logging.Logger; -import javax.swing.DefaultCellEditor; -import javax.swing.GrayFilter; -import javax.swing.Icon; -import javax.swing.ImageIcon; -import javax.swing.JTable; -import javax.swing.JTextField; -import javax.swing.UIDefaults; -import javax.swing.UIManager; -import javax.swing.event.TreeExpansionEvent; -import javax.swing.event.TreeExpansionListener; -import javax.swing.table.TableColumn; -import javax.swing.tree.DefaultMutableTreeNode; -import javax.swing.tree.DefaultTreeModel; -import javax.swing.tree.ExpandVetoException; -import javax.swing.tree.MutableTreeNode; -import javax.swing.tree.TreeNode; -import javax.swing.tree.TreePath; -import org.netbeans.swing.outline.DefaultOutlineCellRenderer; -import org.netbeans.swing.outline.DefaultOutlineModel; -import org.netbeans.swing.outline.ExtTreeWillExpandListener; -import org.netbeans.swing.outline.OutlineModel; -import org.netbeans.swing.outline.RenderDataProvider; -import org.netbeans.swing.outline.RowModel; - -/** - * Variable Inspector window. - * - * @author Martin Leopold - */ -public class VariableInspector extends javax.swing.JFrame { - - protected DefaultMutableTreeNode rootNode; // the root node (invisible) - protected DefaultMutableTreeNode builtins; // node for Processing built-in variables - protected DefaultTreeModel treeModel; // data model for the tree column - protected OutlineModel model; // data model for the whole Outline (tree and other columns) - protected List callStack; // the call stack - protected List locals; // current local variables - protected List thisFields; // all fields of the current this-object - protected List declaredThisFields; // declared i.e. non-inherited fields of this - protected DebugEditor editor; // the editor - protected Debugger dbg; // the debugger - protected List expandedNodes = new ArrayList(); // list of expanded tree paths. (using list to maintain the order of expansion) - protected boolean p5mode = true; // processing / "advanced" mode flag (currently not used - - /** - * Creates new form VariableInspector - */ - public VariableInspector(DebugEditor editor) { - this.editor = editor; - this.dbg = editor.dbg(); - - initComponents(); - - // setup Outline - rootNode = new DefaultMutableTreeNode("root"); - builtins = new DefaultMutableTreeNode("Processing"); - treeModel = new DefaultTreeModel(rootNode); // model for the tree column - model = DefaultOutlineModel.createOutlineModel(treeModel, new VariableRowModel(), true, "Name"); // model for all columns - - ExpansionHandler expansionHandler = new ExpansionHandler(); - model.getTreePathSupport().addTreeWillExpandListener(expansionHandler); - model.getTreePathSupport().addTreeExpansionListener(expansionHandler); - tree.setModel(model); - tree.setRootVisible(false); - tree.setRenderDataProvider(new OutlineRenderer()); - tree.setColumnHidingAllowed(false); // disable visible columns button (shows by default when right scroll bar is visible) - tree.setAutoscrolls(false); - - // set custom renderer and editor for value column, since we are using a custom class for values (VariableNode) - TableColumn valueColumn = tree.getColumnModel().getColumn(1); - valueColumn.setCellRenderer(new ValueCellRenderer()); - valueColumn.setCellEditor(new ValueCellEditor()); - - //System.out.println("renderer: " + tree.getDefaultRenderer(String.class).getClass()); - //System.out.println("editor: " + tree.getDefaultEditor(String.class).getClass()); - - callStack = new ArrayList(); - locals = new ArrayList(); - thisFields = new ArrayList(); - declaredThisFields = new ArrayList(); - - this.setTitle(editor.getSketch().getName()); - -// for (Entry entry : UIManager.getDefaults().entrySet()) { -// System.out.println(entry.getKey()); -// } - } - - @Override - public void setTitle(String title) { - super.setTitle(title + " | Variable Inspector"); - } - - /** - * Model for a Outline Row (excluding the tree column). Column 0 is "Value". - * Column 1 is "Type". Handles setting and getting values. TODO: Maybe use a - * TableCellRenderer instead of this to also have a different icon based on - * expanded state. See: - * http://kickjava.com/src/org/netbeans/swing/outline/DefaultOutlineCellRenderer.java.htm - */ - protected class VariableRowModel implements RowModel { - - protected String[] columnNames = {"Value", "Type"}; - protected int[] editableTypes = {VariableNode.TYPE_BOOLEAN, VariableNode.TYPE_FLOAT, VariableNode.TYPE_INTEGER, VariableNode.TYPE_STRING, VariableNode.TYPE_FLOAT, VariableNode.TYPE_DOUBLE, VariableNode.TYPE_LONG, VariableNode.TYPE_SHORT, VariableNode.TYPE_CHAR}; - - @Override - public int getColumnCount() { - if (p5mode) { - return 1; // only show value in p5 mode - } else { - return 2; - } - } - - @Override - public Object getValueFor(Object o, int i) { - if (o instanceof VariableNode) { - VariableNode var = (VariableNode) o; - switch (i) { - case 0: - return var; // will be converted to an appropriate String by ValueCellRenderer - case 1: - return var.getTypeName(); - default: - return ""; - } - } else { - return ""; - } - } - - @Override - public Class getColumnClass(int i) { - if (i == 0) { - return VariableNode.class; - } - return String.class; - } - - @Override - public boolean isCellEditable(Object o, int i) { - if (i == 0 && o instanceof VariableNode) { - VariableNode var = (VariableNode) o; - //System.out.println("type: " + var.getTypeName()); - for (int type : editableTypes) { - if (var.getType() == type) { - return true; - } - } - } - return false; - } - - @Override - public void setValueFor(Object o, int i, Object o1) { - VariableNode var = (VariableNode) o; - String stringValue = (String) o1; - - Value value = null; - try { - switch (var.getType()) { - case VariableNode.TYPE_INTEGER: - value = dbg.vm().mirrorOf(Integer.parseInt(stringValue)); - break; - case VariableNode.TYPE_BOOLEAN: - value = dbg.vm().mirrorOf(Boolean.parseBoolean(stringValue)); - break; - case VariableNode.TYPE_FLOAT: - value = dbg.vm().mirrorOf(Float.parseFloat(stringValue)); - break; - case VariableNode.TYPE_STRING: - value = dbg.vm().mirrorOf(stringValue); - break; - case VariableNode.TYPE_LONG: - value = dbg.vm().mirrorOf(Long.parseLong(stringValue)); - break; - case VariableNode.TYPE_BYTE: - value = dbg.vm().mirrorOf(Byte.parseByte(stringValue)); - break; - case VariableNode.TYPE_DOUBLE: - value = dbg.vm().mirrorOf(Double.parseDouble(stringValue)); - break; - case VariableNode.TYPE_SHORT: - value = dbg.vm().mirrorOf(Short.parseShort(stringValue)); - break; - case VariableNode.TYPE_CHAR: - // TODO: better char support - if (stringValue.length() > 0) { - value = dbg.vm().mirrorOf(stringValue.charAt(0)); - } - break; - } - } catch (NumberFormatException ex) { - Logger.getLogger(VariableRowModel.class.getName()).log(Level.INFO, "invalid value entered for {0}: {1}", new Object[]{var.getName(), stringValue}); - } - if (value != null) { - var.setValue(value); - Logger.getLogger(VariableRowModel.class.getName()).log(Level.INFO, "new value set: {0}", var.getStringValue()); - } - } - - @Override - public String getColumnName(int i) { - return columnNames[i]; - } - } - - /** - * Renderer for the tree portion of the outline component. Handles icons, - * text color and tool tips. - */ - protected class OutlineRenderer implements RenderDataProvider { - - protected Icon[][] icons; - protected static final int ICON_SIZE = 16; // icon size (square, size=width=height) - - public OutlineRenderer() { - // load icons - icons = loadIcons("theme/var-icons.gif"); - } - - /** - * Load multiple icons (horizotal) with multiple states (vertical) from - * a single file. - * - * @param fileName file path in the mode folder. - * @return a nested array (first index: icon, second index: state) or - * null if the file wasn't found. - */ - protected ImageIcon[][] loadIcons(String fileName) { - ExperimentalMode mode = editor.mode(); - File file = mode.getContentFile(fileName); - if (!file.exists()) { - Logger.getLogger(OutlineRenderer.class.getName()).log(Level.SEVERE, "icon file not found: {0}", file.getAbsolutePath()); - return null; - } - Image allIcons = mode.loadImage(fileName); - int cols = allIcons.getWidth(null) / ICON_SIZE; - int rows = allIcons.getHeight(null) / ICON_SIZE; - ImageIcon[][] iconImages = new ImageIcon[cols][rows]; - - for (int i = 0; i < cols; i++) { - for (int j = 0; j < rows; j++) { - //Image image = createImage(ICON_SIZE, ICON_SIZE); - Image image = new BufferedImage(ICON_SIZE, ICON_SIZE, BufferedImage.TYPE_INT_ARGB); - Graphics g = image.getGraphics(); - g.drawImage(allIcons, -i * ICON_SIZE, -j * ICON_SIZE, null); - iconImages[i][j] = new ImageIcon(image); - } - } - return iconImages; - } - - protected Icon getIcon(int type, int state) { - if (type < 0 || type > icons.length - 1) { - return null; - } - return icons[type][state]; - } - - protected VariableNode toVariableNode(Object o) { - if (o instanceof VariableNode) { - return (VariableNode) o; - } else { - return null; - } - } - - protected Icon toGray(Icon icon) { - if (icon instanceof ImageIcon) { - Image grayImage = GrayFilter.createDisabledImage(((ImageIcon) icon).getImage()); - return new ImageIcon(grayImage); - } - // Cannot convert - return icon; - } - - @Override - public String getDisplayName(Object o) { - return o.toString(); // VariableNode.toString() returns name; (for sorting) -// VariableNode var = toVariableNode(o); -// if (var != null) { -// return var.getName(); -// } else { -// return o.toString(); -// } - } - - @Override - public boolean isHtmlDisplayName(Object o) { - return false; - } - - @Override - public Color getBackground(Object o) { - return null; - } - - @Override - public Color getForeground(Object o) { - if (tree.isEnabled()) { - return null; // default - } else { - return Color.GRAY; - } - } - - @Override - public String getTooltipText(Object o) { - VariableNode var = toVariableNode(o); - if (var != null) { - return var.getDescription(); - } else { - return ""; - } - } - - @Override - public Icon getIcon(Object o) { - VariableNode var = toVariableNode(o); - if (var != null) { - if (tree.isEnabled()) { - return getIcon(var.getType(), 0); - } else { - return getIcon(var.getType(), 1); - } - } else { - if (o instanceof TreeNode) { -// TreeNode node = (TreeNode) o; -// AbstractLayoutCache layout = tree.getLayoutCache(); - UIDefaults defaults = UIManager.getDefaults(); - - boolean isLeaf = model.isLeaf(o); - Icon icon; - if (isLeaf) { - icon = defaults.getIcon("Tree.leafIcon"); - } else { - icon = defaults.getIcon("Tree.closedIcon"); - } - - if (!tree.isEnabled()) { - return toGray(icon); - } - return icon; - } - } - return null; // use standard icon - //UIManager.getIcon(o); - } - } - - // TODO: could probably extend the simpler javax.swing.table.DefaultTableCellRenderer here - /** - * Renderer for the value column. Uses an italic font for null values and - * Object values ("instance of ..."). Uses a gray color when tree is not - * enabled. - */ - protected class ValueCellRenderer extends DefaultOutlineCellRenderer { - - public ValueCellRenderer() { - super(); - } - - protected void setItalic(boolean on) { - if (on) { - setFont(new Font(getFont().getName(), Font.ITALIC, getFont().getSize())); - } else { - setFont(new Font(getFont().getName(), Font.PLAIN, getFont().getSize())); - } - } - - @Override - public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { - Component c = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); - - if (!tree.isEnabled()) { - setForeground(Color.GRAY); - } else { - setForeground(Color.BLACK); - } - - if (value instanceof VariableNode) { - VariableNode var = (VariableNode) value; - - if (var.getValue() == null || var.getType() == VariableNode.TYPE_OBJECT) { - setItalic(true); - } else { - setItalic(false); - } - value = var.getStringValue(); - } - - setValue(value); - return c; - } - } - - /** - * Editor for the value column. Will show an empty string when editing - * String values that are null. - */ - protected class ValueCellEditor extends DefaultCellEditor { - - public ValueCellEditor() { - super(new JTextField()); - } - - @Override - public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) { - if (!(value instanceof VariableNode)) { - return super.getTableCellEditorComponent(table, value, isSelected, row, column); - } - VariableNode var = (VariableNode) value; - if (var.getType() == VariableNode.TYPE_STRING && var.getValue() == null) { - return super.getTableCellEditorComponent(table, "", isSelected, row, column); - } else { - return super.getTableCellEditorComponent(table, var.getStringValue(), isSelected, row, column); - } - } - } - - /** - * Handler for expanding and collapsing tree nodes. Implements lazy loading - * of tree data (on expand). - */ - protected class ExpansionHandler implements ExtTreeWillExpandListener, TreeExpansionListener { - - @Override - public void treeWillExpand(TreeExpansionEvent tee) throws ExpandVetoException { - //System.out.println("will expand"); - Object last = tee.getPath().getLastPathComponent(); - if (!(last instanceof VariableNode)) { - return; - } - VariableNode var = (VariableNode) last; - // load children -// if (!dbg.isPaused()) { -// System.out.println("throwing veto"); -// //throw new ExpandVetoException(tee, "Debugger busy"); -// } else { - var.removeAllChildren(); // TODO: should we only load it once? - // TODO: don't filter in advanced mode - //System.out.println("loading children for: " + var); - // true means include inherited - var.addChildren(filterNodes(dbg.getFields(var.getValue(), 0, true), new ThisFilter())); -// } - } - - @Override - public void treeWillCollapse(TreeExpansionEvent tee) throws ExpandVetoException { - //throw new UnsupportedOperationException("Not supported yet."); - } - - @Override - public void treeExpanded(TreeExpansionEvent tee) { - //System.out.println("expanded: " + tee.getPath()); - if (!expandedNodes.contains(tee.getPath())) { - expandedNodes.add(tee.getPath()); - } - -// TreePath newPath = tee.getPath(); -// if (expandedLast != null) { -// // test each node of the path for equality -// for (int i = 0; i < expandedLast.getPathCount(); i++) { -// if (i < newPath.getPathCount()) { -// Object last = expandedLast.getPathComponent(i); -// Object cur = newPath.getPathComponent(i); -// System.out.println(last + " =? " + cur + ": " + last.equals(cur) + "/" + (last.hashCode() == cur.hashCode())); -// } -// } -// } -// System.out.println("path equality: " + newPath.equals(expandedLast)); -// expandedLast = newPath; - } - - @Override - public void treeCollapsed(TreeExpansionEvent tee) { - //System.out.println("collapsed: " + tee.getPath()); - - // first remove all children of collapsed path - // this makes sure children do not appear before parents in the list. - // (children can't be expanded before their parents) - List removalList = new ArrayList(); - for (TreePath path : expandedNodes) { - if (path.getParentPath().equals(tee.getPath())) { - removalList.add(path); - } - } - for (TreePath path : removalList) { - expandedNodes.remove(path); - } - // remove collapsed path - expandedNodes.remove(tee.getPath()); - } - - @Override - public void treeExpansionVetoed(TreeExpansionEvent tee, ExpandVetoException eve) { - //System.out.println("expansion vetoed"); - // nop - } - } - - /** - * This method is called from within the constructor to initialize the form. - * WARNING: Do NOT modify this code. The content of this method is always - * regenerated by the Form Editor. - */ - @SuppressWarnings("unchecked") - // //GEN-BEGIN:initComponents - private void initComponents() { - - scrollPane = new javax.swing.JScrollPane(); - tree = new org.netbeans.swing.outline.Outline(); - - scrollPane.setViewportView(tree); - - javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane()); - getContentPane().setLayout(layout); - layout.setHorizontalGroup( - layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGap(0, 400, Short.MAX_VALUE) - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(scrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 400, Short.MAX_VALUE)) - ); - layout.setVerticalGroup( - layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGap(0, 300, Short.MAX_VALUE) - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(scrollPane, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, 300, Short.MAX_VALUE)) - ); - - pack(); - }// //GEN-END:initComponents - -// /** -// * @param args the command line arguments -// */ -// public static void main(String args[]) { -// /* -// * Set the Nimbus look and feel -// */ -// // -// /* -// * If Nimbus (introduced in Java SE 6) is not available, stay with the -// * default look and feel. For details see -// * http://download.oracle.com/javase/tutorial/uiswing/lookandfeel/plaf.html -// */ -// try { -// javax.swing.UIManager.setLookAndFeel(javax.swing.UIManager.getSystemLookAndFeelClassName()); -// } catch (ClassNotFoundException ex) { -// java.util.logging.Logger.getLogger(VariableInspector.class.getName()).log(java.util.logging.Level.SEVERE, null, ex); -// } catch (InstantiationException ex) { -// java.util.logging.Logger.getLogger(VariableInspector.class.getName()).log(java.util.logging.Level.SEVERE, null, ex); -// } catch (IllegalAccessException ex) { -// java.util.logging.Logger.getLogger(VariableInspector.class.getName()).log(java.util.logging.Level.SEVERE, null, ex); -// } catch (javax.swing.UnsupportedLookAndFeelException ex) { -// java.util.logging.Logger.getLogger(VariableInspector.class.getName()).log(java.util.logging.Level.SEVERE, null, ex); -// } -// // -// -// /* -// * Create and display the form -// */ -// run(new VariableInspector()); -// } - protected static void run(final VariableInspector vi) { - /* - * Create and display the form - */ - java.awt.EventQueue.invokeLater(new Runnable() { - @Override - public void run() { - vi.setVisible(true); - } - }); - } - // Variables declaration - do not modify//GEN-BEGIN:variables - private javax.swing.JScrollPane scrollPane; - protected org.netbeans.swing.outline.Outline tree; - // End of variables declaration//GEN-END:variables - - /** - * Access the root node of the tree. - * - * @return the root node - */ - public DefaultMutableTreeNode getRootNode() { - return rootNode; - } - - /** - * Unlock the inspector window. Rebuild after this to avoid ... dots in the - * trees labels - */ - public void unlock() { - tree.setEnabled(true); - } - - /** - * Lock the inspector window. Cancels open edits. - */ - public void lock() { - if (tree.getCellEditor() != null) { - //tree.getCellEditor().stopCellEditing(); // force quit open edit - tree.getCellEditor().cancelCellEditing(); // cancel an open edit - } - tree.setEnabled(false); - } - - /** - * Reset the inspector windows data. Rebuild after this to make changes - * visible. - */ - public void reset() { - rootNode.removeAllChildren(); - // clear local data for good measure (in case someone rebuilds) - callStack.clear(); - locals.clear(); - thisFields.clear(); - declaredThisFields.clear(); - expandedNodes.clear(); - // update - treeModel.nodeStructureChanged(rootNode); - } - -// public void setAdvancedMode() { -// p5mode = false; -// } -// -// public void setP5Mode() { -// p5mode = true; -// } -// -// public void toggleMode() { -// if (p5mode) { -// setAdvancedMode(); -// } else { -// setP5Mode(); -// } -// } - /** - * Update call stack data. - * - * @param nodes a list of nodes that represent the call stack. - * @param title the title to be used when labeling or otherwise grouping - * call stack data. - */ - public void updateCallStack(List nodes, String title) { - callStack = nodes; - } - - /** - * Update locals data. - * - * @param nodes a list of {@link VariableNode} to be shown as local - * variables in the inspector. - * @param title the title to be used when labeling or otherwise grouping - * locals data. - */ - public void updateLocals(List nodes, String title) { - locals = nodes; - } - - /** - * Update this-fields data. - * - * @param nodes a list of {@link VariableNode}s to be shown as this-fields - * in the inspector. - * @param title the title to be used when labeling or otherwise grouping - * this-fields data. - */ - public void updateThisFields(List nodes, String title) { - thisFields = nodes; - } - - /** - * Update declared (non-inherited) this-fields data. - * - * @param nodes a list of {@link VariableNode}s to be shown as declared - * this-fields in the inspector. - * @param title the title to be used when labeling or otherwise grouping - * declared this-fields data. - */ - public void updateDeclaredThisFields(List nodes, String title) { - declaredThisFields = nodes; - } - - /** - * Rebuild the outline tree from current data. Uses the data provided by - * {@link #updateCallStack}, {@link #updateLocals}, {@link #updateThisFields} - * and {@link #updateDeclaredThisFields} - */ - public void rebuild() { - rootNode.removeAllChildren(); - if (p5mode) { - // add all locals to root - addAllNodes(rootNode, locals); - - // add non-inherited this fields - addAllNodes(rootNode, filterNodes(declaredThisFields, new LocalHidesThisFilter(locals, LocalHidesThisFilter.MODE_PREFIX))); - - // add p5 builtins in a new folder - builtins.removeAllChildren(); - addAllNodes(builtins, filterNodes(thisFields, new P5BuiltinsFilter())); - if (builtins.getChildCount() > 0) { // skip builtins in certain situations e.g. in pure java tabs. - rootNode.add(builtins); - } - - // notify tree (using model) changed a node and its children - // http://stackoverflow.com/questions/2730851/how-to-update-jtree-elements - // needs to be done before expanding paths! - treeModel.nodeStructureChanged(rootNode); - - // handle node expansions - for (TreePath path : expandedNodes) { - //System.out.println("re-expanding: " + path); - path = synthesizePath(path); - if (path != null) { - tree.expandPath(path); - } else { - //System.out.println("couldn't synthesize path"); - } - } - - // this expansion causes problems when sorted and stepping - //tree.expandPath(new TreePath(new Object[]{rootNode, builtins})); - - } else { - // TODO: implement advanced mode here - } - } - - /** - * Re-build a {@link TreePath} from a previous path using equals-checks - * starting at the root node. This is used to use paths from previous trees - * for use on the current tree. - * - * @param path the path to synthesize. - * @return the rebuilt path, usable on the current tree. - */ - protected TreePath synthesizePath(TreePath path) { - //System.out.println("synthesizing: " + path); - if (path.getPathCount() == 0 || !rootNode.equals(path.getPathComponent(0))) { - return null; - } - Object[] newPath = new Object[path.getPathCount()]; - newPath[0] = rootNode; - TreeNode currentNode = rootNode; - for (int i = 0; i < path.getPathCount() - 1; i++) { - // get next node - for (int j = 0; j < currentNode.getChildCount(); j++) { - TreeNode nextNode = currentNode.getChildAt(j); - if (nextNode.equals(path.getPathComponent(i + 1))) { - currentNode = nextNode; - newPath[i + 1] = nextNode; - //System.out.println("found node " + (i+1) + ": " + nextNode); - break; - } - } - if (newPath[i + 1] == null) { - //System.out.println("didn't find node"); - return null; - } - } - return new TreePath(newPath); - } - - /** - * Filter a list of nodes using a {@link VariableNodeFilter}. - * - * @param nodes the list of nodes to filter. - * @param filter the filter to be used. - * @return the filtered list. - */ - protected List filterNodes(List nodes, VariableNodeFilter filter) { - List filtered = new ArrayList(); - for (VariableNode node : nodes) { - if (filter.accept(node)) { - filtered.add(node); - } - } - return filtered; - } - - /** - * Add all nodes in a list to a root node. - * - * @param root the root node to add to. - * @param nodes the list of nodes to add. - */ - protected void addAllNodes(DefaultMutableTreeNode root, List nodes) { - for (MutableTreeNode node : nodes) { - root.add(node); - } - } - - /** - * A filter for {@link VariableNode}s. - */ - public interface VariableNodeFilter { - - /** - * Check whether the filter accepts a {@link VariableNode}. - * - * @param var the input node - * @return true when the filter accepts the input node otherwise false. - */ - public boolean accept(VariableNode var); - } - - /** - * A {@link VariableNodeFilter} that accepts Processing built-in variable - * names. - */ - public class P5BuiltinsFilter implements VariableNodeFilter { - - protected String[] p5Builtins = { - "focused", - "frameCount", - "frameRate", - "height", - "online", - "screen", - "width", - "mouseX", - "mouseY", - "pmouseX", - "pmouseY", - "key", - "keyCode", - "keyPressed" - }; - - @Override - public boolean accept(VariableNode var) { - return Arrays.asList(p5Builtins).contains(var.getName()); - } - } - - /** - * A {@link VariableNodeFilter} that rejects implicit this references. - * (Names starting with "this$") - */ - public class ThisFilter implements VariableNodeFilter { - - @Override - public boolean accept(VariableNode var) { - return !var.getName().startsWith("this$"); - } - } - - /** - * A {@link VariableNodeFilter} that either rejects this-fields if hidden by - * a local, or prefixes its name with "this." - */ - public class LocalHidesThisFilter implements VariableNodeFilter { - - /** - * Reject a this-field if hidden by a local. - */ - public static final int MODE_HIDE = 0; // don't show hidden this fields - /** - * Prefix a this-fields name with "this." if hidden by a local. - */ - public static final int MODE_PREFIX = 1; // prefix hidden this fields with "this." - protected List locals; - protected int mode; - - /** - * Construct a {@link LocalHidesThisFilter}. - * - * @param locals a list of locals to check against - * @param mode either {@link #MODE_HIDE} or {@link #MODE_PREFIX} - */ - public LocalHidesThisFilter(List locals, int mode) { - this.locals = locals; - this.mode = mode; - } - - @Override - public boolean accept(VariableNode var) { - // check if the same name appears in the list of locals i.e. the local hides the field - for (VariableNode local : locals) { - if (var.getName().equals(local.getName())) { - switch (mode) { - case MODE_PREFIX: - var.setName("this." + var.getName()); - return true; - case MODE_HIDE: - return false; - } - } - } - return true; - } - } -} diff --git a/experimental/src/processing/mode/experimental/VariableNode.java b/experimental/src/processing/mode/experimental/VariableNode.java deleted file mode 100755 index 66b0575d7..000000000 --- a/experimental/src/processing/mode/experimental/VariableNode.java +++ /dev/null @@ -1,358 +0,0 @@ -/* - * Copyright (C) 2012 Martin Leopold - * - * This program is free software; you can redistribute it and/or modify it under - * the terms of the GNU General Public License as published by the Free Software - * Foundation; either version 2 of the License, or (at your option) any later - * version. - * - * This program 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 for more - * details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., 59 Temple - * Place - Suite 330, Boston, MA 02111-1307, USA. - */ -package processing.mode.experimental; - -import com.sun.jdi.ArrayReference; -import com.sun.jdi.ObjectReference; -import com.sun.jdi.StringReference; -import com.sun.jdi.Value; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Enumeration; -import java.util.List; -import javax.swing.tree.MutableTreeNode; -import javax.swing.tree.TreeNode; - -/** - * Model for a variable in the variable inspector. Has a type and name and - * optionally a value. Can have sub-variables (as is the case for objects, and - * arrays). - * - * @author Martin Leopold - */ -public class VariableNode implements MutableTreeNode { - - public static final int TYPE_UNKNOWN = -1; - public static final int TYPE_OBJECT = 0; - public static final int TYPE_ARRAY = 1; - public static final int TYPE_INTEGER = 2; - public static final int TYPE_FLOAT = 3; - public static final int TYPE_BOOLEAN = 4; - public static final int TYPE_CHAR = 5; - public static final int TYPE_STRING = 6; - public static final int TYPE_LONG = 7; - public static final int TYPE_DOUBLE = 8; - public static final int TYPE_BYTE = 9; - public static final int TYPE_SHORT = 10; - public static final int TYPE_VOID = 11; - protected String type; - protected String name; - protected Value value; - protected List children = new ArrayList(); - protected MutableTreeNode parent; - - /** - * Construct a {@link VariableNode}. - * @param name the name - * @param type the type - * @param value the value - */ - public VariableNode(String name, String type, Value value) { - this.name = name; - this.type = type; - this.value = value; - } - - public void setValue(Value value) { - this.value = value; - } - - public Value getValue() { - return value; - } - - /** - * Get a String representation of this variable nodes value. - * - * @return a String representing the value. - */ - public String getStringValue() { - String str; - if (value != null) { - if (getType() == TYPE_OBJECT) { - str = "instance of " + type; - } else if (getType() == TYPE_ARRAY) { - //instance of int[5] (id=998) --> instance of int[5] - str = value.toString().substring(0, value.toString().lastIndexOf(" ")); - } else if (getType() == TYPE_STRING) { - str = ((StringReference) value).value(); // use original string value (without quotes) - } else { - str = value.toString(); - } - } else { - str = "null"; - } - return str; - } - - public String getTypeName() { - return type; - } - - public int getType() { - if (type == null) { - return TYPE_UNKNOWN; - } - if (type.endsWith("[]")) { - return TYPE_ARRAY; - } - if (type.equals("int")) { - return TYPE_INTEGER; - } - if (type.equals("long")) { - return TYPE_LONG; - } - if (type.equals("byte")) { - return TYPE_BYTE; - } - if (type.equals("short")) { - return TYPE_SHORT; - } - if (type.equals("float")) { - return TYPE_FLOAT; - } - if (type.equals("double")) { - return TYPE_DOUBLE; - } - if (type.equals("char")) { - return TYPE_CHAR; - } - if (type.equals("java.lang.String")) { - return TYPE_STRING; - } - if (type.equals("boolean")) { - return TYPE_BOOLEAN; - } - if (type.equals("void")) { - return TYPE_VOID; //TODO: check if this is correct - } - return TYPE_OBJECT; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - /** - * Add a {@link VariableNode} as child. - * - * @param c the {@link VariableNode} to add. - */ - public void addChild(VariableNode c) { - children.add(c); - c.setParent(this); - } - - /** - * Add multiple {@link VariableNode}s as children. - * - * @param children the list of {@link VariableNode}s to add. - */ - public void addChildren(List children) { - for (VariableNode child : children) { - addChild(child); - } - } - - @Override - public TreeNode getChildAt(int i) { - return children.get(i); - } - - @Override - public int getChildCount() { - return children.size(); - } - - @Override - public TreeNode getParent() { - return parent; - } - - @Override - public int getIndex(TreeNode tn) { - return children.indexOf(tn); - } - - @Override - public boolean getAllowsChildren() { - if (value == null) { - return false; - } - - // handle strings - if (getType() == TYPE_STRING) { - return false; - } - - // handle arrays - if (getType() == TYPE_ARRAY) { - ArrayReference array = (ArrayReference) value; - return array.length() > 0; - } - // handle objects - if (getType() == TYPE_OBJECT) { // this also rules out null - // check if this object has any fields - ObjectReference obj = (ObjectReference) value; - return !obj.referenceType().visibleFields().isEmpty(); - } - - return false; - } - - /** - * This controls the default icon and disclosure triangle. - * - * @return true, will show "folder" icon and disclosure triangle. - */ - @Override - public boolean isLeaf() { - //return children.size() == 0; - return !getAllowsChildren(); - } - - @Override - public Enumeration children() { - return Collections.enumeration(children); - } - - /** - * Get a String representation of this {@link VariableNode}. - * - * @return the name of the variable (for sorting to work). - */ - @Override - public String toString() { - return getName(); // for sorting - } - - /** - * Get a String description of this {@link VariableNode}. Contains the type, - * name and value. - * - * @return the description - */ - public String getDescription() { - String str = ""; - if (type != null) { - str += type + " "; - } - str += name; - str += " = " + getStringValue(); - return str; - } - - @Override - public void insert(MutableTreeNode mtn, int i) { - children.add(i, this); - } - - @Override - public void remove(int i) { - MutableTreeNode mtn = children.remove(i); - if (mtn != null) { - mtn.setParent(null); - } - } - - @Override - public void remove(MutableTreeNode mtn) { - children.remove(mtn); - mtn.setParent(null); - } - - /** - * Remove all children from this {@link VariableNode}. - */ - public void removeAllChildren() { - for (MutableTreeNode mtn : children) { - mtn.setParent(null); - } - children.clear(); - } - - @Override - public void setUserObject(Object o) { - if (o instanceof Value) { - value = (Value) o; - } - } - - @Override - public void removeFromParent() { - parent.remove(this); - this.parent = null; - } - - @Override - public void setParent(MutableTreeNode mtn) { - parent = mtn; - } - - /** - * Test for equality. To be equal, two {@link VariableNode}s need to have - * equal type, name and value. - * - * @param obj the object to test for equality with this {@link VariableNode} - * @return true if the given object is equal to this {@link VariableNode} - */ - @Override - public boolean equals(Object obj) { - if (obj == null) { - return false; - } - if (getClass() != obj.getClass()) { - return false; - } - final VariableNode other = (VariableNode) obj; - if ((this.type == null) ? (other.type != null) : !this.type.equals(other.type)) { - //System.out.println("type not equal"); - return false; - } - if ((this.name == null) ? (other.name != null) : !this.name.equals(other.name)) { - //System.out.println("name not equal"); - return false; - } - if (this.value != other.value && (this.value == null || !this.value.equals(other.value))) { - //System.out.println("value not equal"); - return false; - } -// if (this.parent != other.parent && (this.parent == null || !this.parent.equals(other.parent))) { -// System.out.println("parent not equal: " + this.parent + "/" + other.parent); -// return false; -// } - return true; - } - - /** - * Returns a hash code based on type, name and value. - */ - @Override - public int hashCode() { - int hash = 3; - hash = 97 * hash + (this.type != null ? this.type.hashCode() : 0); - hash = 97 * hash + (this.name != null ? this.name.hashCode() : 0); - hash = 97 * hash + (this.value != null ? this.value.hashCode() : 0); -// hash = 97 * hash + (this.parent != null ? this.parent.hashCode() : 0); - return hash; - } -} diff --git a/experimental/src/processing/mode/experimental/XQConsoleToggle.java b/experimental/src/processing/mode/experimental/XQConsoleToggle.java deleted file mode 100755 index 3b135e971..000000000 --- a/experimental/src/processing/mode/experimental/XQConsoleToggle.java +++ /dev/null @@ -1,131 +0,0 @@ -package processing.mode.experimental; - -/* - Part of the XQMode project - https://github.com/Manindra29/XQMode - - Under Google Summer of Code 2012 - - http://www.google-melange.com/gsoc/homepage/google/gsoc2012 - - Copyright (C) 2012 Manindra Moharana - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License version 2 - as published by the Free Software Foundation. - - This program 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 for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software Foundation, - Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -import java.awt.Color; -import java.awt.Dimension; -import java.awt.Graphics; -import java.awt.Graphics2D; -import java.awt.RenderingHints; -import java.awt.event.MouseEvent; -import java.awt.event.MouseListener; - -import javax.swing.JPanel; - -/** - * Toggle Button displayed in the editor line status panel for toggling bewtween - * console and problems list. Glorified JPanel. - * - * @author Manindra Moharana <me@mkmoharana.com> - * - */ - -public class XQConsoleToggle extends JPanel implements MouseListener { - public static final String[] text = { "Console", "Errors" }; - - private boolean toggleText = true; - private boolean toggleBG = true; - - /** - * Height of the component - */ - protected int height; - protected DebugEditor editor; - protected String buttonName; - - public XQConsoleToggle(DebugEditor editor, String buttonName, int height) { - this.editor = editor; - this.height = height; - this.buttonName = buttonName; - } - - public Dimension getPreferredSize() { - return new Dimension(70, height); - } - - public Dimension getMinimumSize() { - return getPreferredSize(); - } - - public Dimension getMaximumSize() { - return getPreferredSize(); - } - - public void paintComponent(Graphics g) { - Graphics2D g2d = (Graphics2D) g; - g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, - RenderingHints.VALUE_TEXT_ANTIALIAS_ON); - - // On mouse hover, text and background color are changed. - if (toggleBG) { - g.setColor(new Color(0xff9DA7B0)); - g.fillRect(0, 0, this.getWidth(), this.getHeight()); - g.setColor(new Color(0xff29333D)); - g.fillRect(0, 0, 4, this.getHeight()); - g.setColor(Color.BLACK); - } else { - g.setColor(Color.DARK_GRAY); - g.fillRect(0, 0, this.getWidth(), this.getHeight()); - g.setColor(new Color(0xff29333D)); - g.fillRect(0, 0, 4, this.getHeight()); - g.setColor(Color.WHITE); - } - - g.drawString(buttonName, getWidth() / 2 + 2 // + 2 is a offset - - getFontMetrics(getFont()).stringWidth(buttonName) / 2, - this.getHeight() - 6); - } - - @Override - public void mouseClicked(MouseEvent arg0) { - - this.repaint(); - try { - editor.toggleView(buttonName); - } catch (Exception e) { - System.out.println(e); - // e.printStackTrace(); - } - toggleText = !toggleText; - } - - @Override - public void mouseEntered(MouseEvent arg0) { - toggleBG = !toggleBG; - this.repaint(); - } - - @Override - public void mouseExited(MouseEvent arg0) { - toggleBG = !toggleBG; - this.repaint(); - } - - @Override - public void mousePressed(MouseEvent arg0) { - } - - @Override - public void mouseReleased(MouseEvent arg0) { - } -} \ No newline at end of file diff --git a/experimental/src/processing/mode/experimental/XQErrorTable.java b/experimental/src/processing/mode/experimental/XQErrorTable.java deleted file mode 100755 index 282f7d8e4..000000000 --- a/experimental/src/processing/mode/experimental/XQErrorTable.java +++ /dev/null @@ -1,166 +0,0 @@ -package processing.mode.experimental; - -/* - Part of the XQMode project - https://github.com/Manindra29/XQMode - - Under Google Summer of Code 2012 - - http://www.google-melange.com/gsoc/homepage/google/gsoc2012 - - Copyright (C) 2012 Manindra Moharana - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License version 2 - as published by the Free Software Foundation. - - This program 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 for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software Foundation, - Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -import java.awt.event.MouseAdapter; -import java.awt.event.MouseEvent; - -import javax.swing.JTable; -import javax.swing.SwingWorker; -import javax.swing.table.JTableHeader; -import javax.swing.table.TableModel; - -/** - * Custom JTable implementation for XQMode. Minor tweaks and addtions. - * - * @author Manindra Moharana <me@mkmoharana.com> - * - */ -public class XQErrorTable extends JTable { - - /** - * Column Names of JTable - */ - public static final String[] columnNames = { "Problem", "Tab", "Line" }; - - /** - * Column Widths of JTable. - */ - public int[] columnWidths = { 600, 100, 50 }; // Default Values - - /** - * Is the column being resized? - */ - private boolean columnResizing = false; - - /** - * ErrorCheckerService instance - */ - protected ErrorCheckerService errorCheckerService; - - @Override - public boolean isCellEditable(int rowIndex, int colIndex) { - return false; // Disallow the editing of any cell - } - - public XQErrorTable(final ErrorCheckerService errorCheckerService) { - this.errorCheckerService = errorCheckerService; - for (int i = 0; i < this.getColumnModel().getColumnCount(); i++) { - this.getColumnModel().getColumn(i) - .setPreferredWidth(columnWidths[i]); - } - - this.getTableHeader().setReorderingAllowed(false); - - this.addMouseListener(new MouseAdapter() { - @Override - synchronized public void mouseReleased(MouseEvent e) { - try { - errorCheckerService.scrollToErrorLine(((XQErrorTable) e - .getSource()).getSelectedRow()); - // System.out.print("Row clicked: " - // + ((XQErrorTable) e.getSource()).getSelectedRow()); - } catch (Exception e1) { - System.out.println("Exception XQErrorTable mouseReleased " - + e); - } - } - }); - - // Handles the resizing of columns. When mouse press is detected on - // table header, Stop updating the table, store new values of column - // widths,and resume updating. Updating is disabled as long as - // columnResizing is true - this.getTableHeader().addMouseListener(new MouseAdapter() { - - @Override - public void mousePressed(MouseEvent e) { - columnResizing = true; - } - - @Override - public void mouseReleased(MouseEvent e) { - columnResizing = false; - for (int i = 0; i < ((JTableHeader) e.getSource()) - .getColumnModel().getColumnCount(); i++) { - columnWidths[i] = ((JTableHeader) e.getSource()) - .getColumnModel().getColumn(i).getWidth(); - // System.out.println("nw " + columnWidths[i]); - } - } - }); - } - - - /** - * Updates table contents with new data - * @param tableModel - TableModel - * @return boolean - If table data was updated - */ - @SuppressWarnings("rawtypes") - synchronized public boolean updateTable(final TableModel tableModel) { - - // If problems list is not visible, no need to update - if (!this.isVisible()) { - return false; - } - - SwingWorker worker = new SwingWorker() { - - protected Object doInBackground() throws Exception { - return null; - } - - protected void done() { - - try { - setModel(tableModel); - - // Set column widths to user defined widths - for (int i = 0; i < getColumnModel().getColumnCount(); i++) { - getColumnModel().getColumn(i).setPreferredWidth( - columnWidths[i]); - } - getTableHeader().setReorderingAllowed(false); - validate(); - repaint(); - } catch (Exception e) { - System.out.println("Exception at XQErrorTable.updateTable " + e); - // e.printStackTrace(); - } - } - }; - - try { - if (!columnResizing) { - worker.execute(); - } - } catch (Exception e) { - System.out.println("ErrorTable updateTable Worker's slacking." - + e.getMessage()); - // e.printStackTrace(); - } - return true; - } - -} diff --git a/experimental/src/processing/mode/experimental/XQPreprocessor.java b/experimental/src/processing/mode/experimental/XQPreprocessor.java deleted file mode 100755 index 278ac275e..000000000 --- a/experimental/src/processing/mode/experimental/XQPreprocessor.java +++ /dev/null @@ -1,242 +0,0 @@ -/* - Part of the XQMode project - https://github.com/Manindra29/XQMode - - Under Google Summer of Code 2012 - - http://www.google-melange.com/gsoc/homepage/google/gsoc2012 - - Copyright (C) 2012 Manindra Moharana - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License version 2 - as published by the Free Software Foundation. - - This program 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 for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software Foundation, - Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -package processing.mode.experimental; - -import java.util.ArrayList; -import java.util.List; -import java.util.Map; - -import org.eclipse.jdt.core.JavaCore; -import org.eclipse.jdt.core.dom.AST; -import org.eclipse.jdt.core.dom.ASTParser; -import org.eclipse.jdt.core.dom.ASTVisitor; -import org.eclipse.jdt.core.dom.CompilationUnit; -import org.eclipse.jdt.core.dom.MethodDeclaration; -import org.eclipse.jdt.core.dom.Modifier; -import org.eclipse.jdt.core.dom.NumberLiteral; -import org.eclipse.jdt.core.dom.SimpleType; -import org.eclipse.jdt.core.dom.rewrite.ASTRewrite; -import org.eclipse.jface.text.BadLocationException; -import org.eclipse.jface.text.Document; -import org.eclipse.text.edits.MalformedTreeException; -import org.eclipse.text.edits.TextEdit; - -import processing.mode.java.preproc.PdePreprocessor; - -/** - * My implementation of P5 preprocessor. Uses Eclipse JDT features instead of - * ANTLR. Performance gains mostly and full control over debug output. But needs - * lots and lots of testing. There will always an option to switch back to PDE - * preproc. - * - * @author Manindra Moharana <me@mkmoharana.com> - * - */ -public class XQPreprocessor { - - private ASTRewrite rewrite = null; - public int mainClassOffset = 0; - private ArrayList imports; - private ArrayList extraImports; - - private String[] coreImports, defaultImports; - - public XQPreprocessor() { - PdePreprocessor p = new PdePreprocessor(null); - defaultImports = p.getDefaultImports(); - coreImports = p.getCoreImports(); - } - - /** - * The main method that performs preprocessing. Converts code into compilable java. - * @param source - String - * @param programImports - List of import statements - * @return String - Compile ready java code - */ - public String doYourThing(String source, - ArrayList programImports) { - this.extraImports = programImports; - source = prepareImports() + source; - Document doc = new Document(source); - - ASTParser parser = ASTParser.newParser(AST.JLS4); - parser.setSource(doc.get().toCharArray()); - parser.setKind(ASTParser.K_COMPILATION_UNIT); - - @SuppressWarnings("unchecked") - Map options = JavaCore.getOptions(); - - // Ben has decided to move on to 1.6. Yay! - JavaCore.setComplianceOptions(JavaCore.VERSION_1_6, options); - options.put(JavaCore.COMPILER_SOURCE, JavaCore.VERSION_1_6); - parser.setCompilerOptions(options); - CompilationUnit cu = (CompilationUnit) parser.createAST(null); - cu.recordModifications(); - rewrite = ASTRewrite.create(cu.getAST()); - cu.accept(new XQASTVisitor()); - - TextEdit edits = cu.rewrite(doc, null); - try { - edits.apply(doc); - } catch (MalformedTreeException e) { - e.printStackTrace(); - } catch (BadLocationException e) { - e.printStackTrace(); - } - // System.out.println("------------XQPreProc-----------------"); - // System.out.println(doc.get()); - // System.out.println("------------XQPreProc End-----------------"); - - // Calculate main class offset - int position = doc.get().indexOf("{") + 1; - int lines = 0; - for (int i = 0; i < position; i++) { - if (doc.get().charAt(i) == '\n') { - lines++; - } - } - lines += 2; - // System.out.println("Lines: " + lines); - mainClassOffset = lines; - - return doc.get(); - } - - /** - * Returns all import statements as lines of code - * - * @return String - All import statements combined. Each import in a separate line. - */ - public String prepareImports() { - imports = new ArrayList(); - for (int i = 0; i < extraImports.size(); i++) { - imports.add(new String(extraImports.get(i).importName)); - } - imports.add(new String("// Default Imports")); - for (int i = 0; i < coreImports.length; i++) { - imports.add(new String("import " + coreImports[i] + ";")); - } - for (int i = 0; i < defaultImports.length; i++) { - imports.add(new String("import " + defaultImports[i] + ";")); - } - String totalImports = ""; - for (int i = 0; i < imports.size(); i++) { - totalImports += imports.get(i) + "\n"; - } - totalImports += "\n"; - return totalImports; - } - - /** - * Visitor implementation that does all the substitution dirty work.
    - *
  • Any function not specified as being protected or private will be made - * 'public'. This means that void setup() becomes - * public void setup(). - * - *
  • Converts doubles into floats, i.e. 12.3 becomes 12.3f so that people - * don't have to add f after their numbers all the time since it's confusing - * for beginners. Also, most functions of p5 core deal with floats only. - * - * @author Manindra Moharana - * - */ - private class XQASTVisitor extends ASTVisitor { - @SuppressWarnings({ "unchecked", "rawtypes" }) - public boolean visit(MethodDeclaration node) { - if (node.getReturnType2() != null) { - // if return type is color, make it int - // if (node.getReturnType2().toString().equals("color")) { - // System.err.println("color type detected!"); - // node.setReturnType2(rewrite.getAST().newPrimitiveType( - // PrimitiveType.INT)); - // } - - // The return type is not void, no need to make it public - // if (!node.getReturnType2().toString().equals("void")) - // return true; - } - - // Simple method, make it public - if (node.modifiers().size() == 0 && !node.isConstructor()) { - // rewrite.set(node, node.getModifiersProperty(), - // Modifier.PUBLIC, - // null); - // rewrite.getListRewrite(node, - // node.getModifiersProperty()).insertLast(Modifier., null) - List newMod = rewrite.getAST().newModifiers(Modifier.PUBLIC); - node.modifiers().add(newMod.get(0)); - } - - return true; - } - - public boolean visit(NumberLiteral node) { - if (!node.getToken().endsWith("f") - && !node.getToken().endsWith("d")) { - for (int i = 0; i < node.getToken().length(); i++) { - if (node.getToken().charAt(i) == '.') { - - String s = node.getToken() + "f"; - node.setToken(s); - break; - } - } - } - return true; - } - - // public boolean visit(FieldDeclaration node) { - // if (node.getType().toString().equals("color")){ - // System.err.println("color type detected!"); - // node.setType(rewrite.getAST().newPrimitiveType( - // PrimitiveType.INT)); - // } - // return true; - // } - // - // public boolean visit(VariableDeclarationStatement node) { - // if (node.getType().toString().equals("color")){ - // System.err.println("color type detected!"); - // node.setType(rewrite.getAST().newPrimitiveType( - // PrimitiveType.INT)); - // } - // return true; - // } - - /** - * This is added just for debugging purposes - to make sure that all - * instances of color type have been substituded as in by the regex - * search in ErrorCheckerService.preprocessCode(). - */ - public boolean visit(SimpleType node) { - if (node.toString().equals("color")) { - System.err - .println("color type detected! \nThis shouldn't be happening! Please report this as an issue."); - } - return true; - - } - - } - -} diff --git a/experimental/theme/theme.txt b/experimental/theme/theme.txt deleted file mode 100755 index 1a6179f5c..000000000 --- a/experimental/theme/theme.txt +++ /dev/null @@ -1,34 +0,0 @@ -# DEBUGGER - -# breakpointed line background color -breakpoint.bgcolor = #f0f0f0 -# marker for breakpointed lines in left hand gutter (2 ascii characters) -breakpoint.marker = <> -breakpoint.marker.color = #4a545e - -# current line background color -currentline.bgcolor = #ffff96 -# marker for the current line in left hand gutter (2 ascii characters) -currentline.marker = -> -currentline.marker.color = #e27500 - -# left hand gutter background color -gutter.bgcolor = #fcfcfc -# color of vertical separation line -gutter.linecolor = #e9e9e9 -# space (in px) added to left and right of gutter markers -gutter.padding = 3 - - -# XQMODE - -# underline colors -editor.errorcolor = #ed2630 -editor.warningcolor = #ffc30e -editor.errormarkercolor = #ed2630 -editor.warningmarkercolor = #ffc30e - -# ERROR BAR - error bar on the right that shows the markers -errorbar.errorcolor = #ed2630 -errorbar.warningcolor = #ffc30e -errorbar.backgroundcolor = #2c343d diff --git a/experimental/theme/var-icons.gif b/experimental/theme/var-icons.gif deleted file mode 100755 index 1d0086a38..000000000 Binary files a/experimental/theme/var-icons.gif and /dev/null differ diff --git a/java/application/Info.plist.tmpl b/java/application/Info.plist.tmpl new file mode 100644 index 000000000..9cc224c08 --- /dev/null +++ b/java/application/Info.plist.tmpl @@ -0,0 +1,74 @@ + + + + + CFBundleDevelopmentRegion + English + CFBundleExecutable + @@sketch@@ + CFBundleIconFile + sketch.icns + CFBundleIdentifier + @@sketch@@ + CFBundleDisplayName + @@sketch@@ + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + @@sketch@@ + CFBundlePackageType + APPL + + + CFBundleShortVersionString + 1 + CFBundleVersion + 1 + CFBundleSignature + ???? + NSHumanReadableCopyright + Your copyright here + CFBundleGetInfoString + Created with Processing + + + @@jvm_runtime@@ + + JVMMainClassName + @@sketch@@ + + LSMinimumSystemVersion + 10.7.3 + + NSHighResolutionCapable + + + LSArchitecturePriority + + x86_64 + + + LSEnvironment + + LC_CTYPE + UTF-8 + + + LSUIPresentationMode + @@lsuipresentationmode@@ + + JVMOptions + +@@jvm_options_list@@ + -Xdock:icon=Contents/Resources/sketch.icns + -Dapple.laf.useScreenMenuBar=true + -Dcom.apple.macos.use-file-dialog-packages=true + -Dcom.apple.macos.useScreenMenuBar=true + -Dcom.apple.mrj.application.apple.menu.about.name=@@sketch@@ + -Dcom.apple.smallTabs=true + + JVMArguments + + + + diff --git a/build/shared/launch4j/LICENSE.txt b/java/application/launch4j/LICENSE.txt similarity index 93% rename from build/shared/launch4j/LICENSE.txt rename to java/application/launch4j/LICENSE.txt index 0ade5f46f..d09ff0065 100644 --- a/build/shared/launch4j/LICENSE.txt +++ b/java/application/launch4j/LICENSE.txt @@ -1,7 +1,7 @@ Launch4j (http://launch4j.sourceforge.net/) Cross-platform Java application wrapper for creating Windows native executables. -Copyright (c) 2004, 2008 Grzegorz Kowal +Copyright (c) 2004, 2014 Grzegorz Kowal All rights reserved. diff --git a/build/shared/launch4j/bin/LICENSE.txt b/java/application/launch4j/bin/LICENSE.txt similarity index 100% rename from build/shared/launch4j/bin/LICENSE.txt rename to java/application/launch4j/bin/LICENSE.txt diff --git a/java/application/launch4j/bin/ld-linux b/java/application/launch4j/bin/ld-linux new file mode 100755 index 000000000..2e2983e88 Binary files /dev/null and b/java/application/launch4j/bin/ld-linux differ diff --git a/java/application/launch4j/bin/ld-macosx b/java/application/launch4j/bin/ld-macosx new file mode 100755 index 000000000..3defcfa52 Binary files /dev/null and b/java/application/launch4j/bin/ld-macosx differ diff --git a/java/application/launch4j/bin/ld-windows.exe b/java/application/launch4j/bin/ld-windows.exe new file mode 100644 index 000000000..5985c0e27 Binary files /dev/null and b/java/application/launch4j/bin/ld-windows.exe differ diff --git a/java/application/launch4j/bin/windres-linux b/java/application/launch4j/bin/windres-linux new file mode 100755 index 000000000..5b83b9349 Binary files /dev/null and b/java/application/launch4j/bin/windres-linux differ diff --git a/java/application/launch4j/bin/windres-macosx b/java/application/launch4j/bin/windres-macosx new file mode 100755 index 000000000..95405de0c Binary files /dev/null and b/java/application/launch4j/bin/windres-macosx differ diff --git a/java/application/launch4j/bin/windres-windows.exe b/java/application/launch4j/bin/windres-windows.exe new file mode 100644 index 000000000..855b195b0 Binary files /dev/null and b/java/application/launch4j/bin/windres-windows.exe differ diff --git a/build/shared/launch4j/head/LICENSE.txt b/java/application/launch4j/head/LICENSE.txt similarity index 95% rename from build/shared/launch4j/head/LICENSE.txt rename to java/application/launch4j/head/LICENSE.txt index 536488e61..c30f34a8e 100644 --- a/build/shared/launch4j/head/LICENSE.txt +++ b/java/application/launch4j/head/LICENSE.txt @@ -1,4 +1,4 @@ -Copyright (c) 2004, 2007 Grzegorz Kowal +Copyright (c) 2004, 2014 Grzegorz Kowal Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/build/shared/launch4j/head/consolehead.o b/java/application/launch4j/head/consolehead.o similarity index 100% rename from build/shared/launch4j/head/consolehead.o rename to java/application/launch4j/head/consolehead.o diff --git a/build/shared/launch4j/head/guihead.o b/java/application/launch4j/head/guihead.o similarity index 100% rename from build/shared/launch4j/head/guihead.o rename to java/application/launch4j/head/guihead.o diff --git a/java/application/launch4j/head/head.o b/java/application/launch4j/head/head.o new file mode 100644 index 000000000..32c147675 Binary files /dev/null and b/java/application/launch4j/head/head.o differ diff --git a/build/shared/launch4j/lib/LICENSE.txt b/java/application/launch4j/lib/LICENSE.txt similarity index 100% rename from build/shared/launch4j/lib/LICENSE.txt rename to java/application/launch4j/lib/LICENSE.txt diff --git a/build/shared/launch4j/w32api/LICENSE.txt b/java/application/launch4j/w32api/LICENSE.txt similarity index 100% rename from build/shared/launch4j/w32api/LICENSE.txt rename to java/application/launch4j/w32api/LICENSE.txt diff --git a/build/shared/launch4j/w32api/crt2.o b/java/application/launch4j/w32api/crt2.o similarity index 100% rename from build/shared/launch4j/w32api/crt2.o rename to java/application/launch4j/w32api/crt2.o diff --git a/build/shared/launch4j/w32api/libadvapi32.a b/java/application/launch4j/w32api/libadvapi32.a similarity index 100% rename from build/shared/launch4j/w32api/libadvapi32.a rename to java/application/launch4j/w32api/libadvapi32.a diff --git a/build/shared/launch4j/w32api/libgcc.a b/java/application/launch4j/w32api/libgcc.a similarity index 100% rename from build/shared/launch4j/w32api/libgcc.a rename to java/application/launch4j/w32api/libgcc.a diff --git a/build/shared/launch4j/w32api/libkernel32.a b/java/application/launch4j/w32api/libkernel32.a similarity index 100% rename from build/shared/launch4j/w32api/libkernel32.a rename to java/application/launch4j/w32api/libkernel32.a diff --git a/build/shared/launch4j/w32api/libmingw32.a b/java/application/launch4j/w32api/libmingw32.a similarity index 100% rename from build/shared/launch4j/w32api/libmingw32.a rename to java/application/launch4j/w32api/libmingw32.a diff --git a/build/shared/launch4j/w32api/libmsvcrt.a b/java/application/launch4j/w32api/libmsvcrt.a similarity index 100% rename from build/shared/launch4j/w32api/libmsvcrt.a rename to java/application/launch4j/w32api/libmsvcrt.a diff --git a/build/shared/launch4j/w32api/libshell32.a b/java/application/launch4j/w32api/libshell32.a similarity index 100% rename from build/shared/launch4j/w32api/libshell32.a rename to java/application/launch4j/w32api/libshell32.a diff --git a/build/shared/launch4j/w32api/libuser32.a b/java/application/launch4j/w32api/libuser32.a similarity index 100% rename from build/shared/launch4j/w32api/libuser32.a rename to java/application/launch4j/w32api/libuser32.a diff --git a/java/application/sketch.icns b/java/application/sketch.icns new file mode 100644 index 000000000..2bdb4dfc2 Binary files /dev/null and b/java/application/sketch.icns differ diff --git a/java/application/sketch.ico b/java/application/sketch.ico new file mode 100644 index 000000000..c7dbbe92b Binary files /dev/null and b/java/application/sketch.ico differ diff --git a/java/application/template.exe b/java/application/template.exe deleted file mode 100755 index 03d8d5753..000000000 Binary files a/java/application/template.exe and /dev/null differ diff --git a/java/application/template.plist b/java/application/template.plist deleted file mode 100755 index a9e22b9c5..000000000 --- a/java/application/template.plist +++ /dev/null @@ -1,77 +0,0 @@ - - - - - CFBundleName - @@sketch@@ - CFBundleVersion - 1.0 - CFBundleAllowMixedLocalizations - true - CFBundleExecutable - JavaApplicationStub - CFBundleDevelopmentRegion - English - CFBundlePackageType - APPL - CFBundleSignature - ???? - CFBundleInfoDictionaryVersion - 6.0 - CFBundleIconFile - sketch.icns - CFBundleIdentifier - @@sketch@@ - - - LSUIPresentationMode - @@lsuipresentationmode@@ - - LSArchitecturePriority - - @@lsarchitecturepriority@@ - - - Java - - VMOptions - @@vmoptions@@ - - MainClass - @@sketch@@ - - - JVMVersion - 1.6* - - ClassPath - @@classpath@@ - - - Properties - - apple.laf.useScreenMenuBar - true - apple.awt.showGrowBox - false - com.apple.smallTabs - true - apple.awt.Antialiasing - false - apple.awt.TextAntialiasing - true - com.apple.hwaccel - true - - apple.awt.use-file-dialog-packages - true - - - - diff --git a/java/examples/Basics/Typography/Letters/Letters.pde b/java/examples/Basics/Typography/Letters/Letters.pde index 781d2aac7..60b3b5ef4 100644 --- a/java/examples/Basics/Typography/Letters/Letters.pde +++ b/java/examples/Basics/Typography/Letters/Letters.pde @@ -12,7 +12,7 @@ void setup() { background(0); // Create the font - println(PFont.list()); + printArray(PFont.list()); f = createFont("Georgia", 24); textFont(f); textAlign(CENTER, CENTER); diff --git a/java/examples/Basics/Typography/Words/Words.pde b/java/examples/Basics/Typography/Words/Words.pde index c86bdcbb6..145641412 100644 --- a/java/examples/Basics/Typography/Words/Words.pde +++ b/java/examples/Basics/Typography/Words/Words.pde @@ -12,7 +12,7 @@ void setup() { size(640, 360); // Create the font - println(PFont.list()); + printArray(PFont.list()); f = createFont("Georgia", 24); textFont(f); } diff --git a/java/examples/Basics/Web/LoadingImages/LoadingImages.pde b/java/examples/Basics/Web/LoadingImages/LoadingImages.pde index fee3d2c1e..9f2f3fa04 100644 --- a/java/examples/Basics/Web/LoadingImages/LoadingImages.pde +++ b/java/examples/Basics/Web/LoadingImages/LoadingImages.pde @@ -12,7 +12,7 @@ PImage img; void setup() { size(640, 360); - img = loadImage("http://processing.org/img/processing.gif"); + img = loadImage("http://processing.org/img/processing-web.png"); noLoop(); } diff --git a/java/examples/Books/Processing Handbook/Illustrations/page_228/page_228.pde b/java/examples/Books/Processing Handbook/Illustrations/page_228/page_228.pde index 81ce7bfd2..e7c782ebd 100755 --- a/java/examples/Books/Processing Handbook/Illustrations/page_228/page_228.pde +++ b/java/examples/Books/Processing Handbook/Illustrations/page_228/page_228.pde @@ -1,6 +1,7 @@ // Based on code 26-04 (p. 231) +import processing.pdf.*; int dragX, dragY, moveX, moveY; boolean record = false; @@ -42,4 +43,4 @@ void mouseDragged() { // Move black circle void keyReleased() { record = true; } - + diff --git a/java/examples/Books/Processing Handbook/Illustrations/page_326/page_326.pde b/java/examples/Books/Processing Handbook/Illustrations/page_326/page_326.pde index c94986e78..efbbbfa1b 100755 --- a/java/examples/Books/Processing Handbook/Illustrations/page_326/page_326.pde +++ b/java/examples/Books/Processing Handbook/Illustrations/page_326/page_326.pde @@ -1,6 +1,7 @@ // Based on code 36-07 (p. 331) +import processing.pdf.*; PFont f; String s = "012345678901234567890123456789"; @@ -95,4 +96,4 @@ void draw() { void keyPressed() { record = true; -} +} diff --git a/java/examples/Books/Processing Handbook/Illustrations/page_394/page_394.pde b/java/examples/Books/Processing Handbook/Illustrations/page_394/page_394.pde index 03f9fe007..23f66b34e 100755 --- a/java/examples/Books/Processing Handbook/Illustrations/page_394/page_394.pde +++ b/java/examples/Books/Processing Handbook/Illustrations/page_394/page_394.pde @@ -1,6 +1,7 @@ // Based on code 43-02 (p. 409) +import processing.pdf.*; Ring[] rings; // Declare the array int numRings = 50; @@ -77,4 +78,4 @@ class Ring { ellipse(x, y, diameter, diameter); } } -} +} diff --git a/java/examples/Books/Processing Handbook/Illustrations/page_476/page_476.pde b/java/examples/Books/Processing Handbook/Illustrations/page_476/page_476.pde index 6b7e3006e..f08f7efe8 100755 --- a/java/examples/Books/Processing Handbook/Illustrations/page_476/page_476.pde +++ b/java/examples/Books/Processing Handbook/Illustrations/page_476/page_476.pde @@ -2,6 +2,8 @@ // Based on code 50-12 (p. 486) // Requires Particle, ArrowParticle classes +import processing.pdf.*; + int num = 900; ArrowParticle[] p = new ArrowParticle[num]; float radius = 1.2; @@ -40,4 +42,4 @@ void mousePressed() { record = true; } - + diff --git a/java/examples/Demos/Graphics/LowLevelGL/LowLevelGL.pde b/java/examples/Demos/Graphics/LowLevelGL/LowLevelGL.pde index d9db7537a..0e7d6a628 100644 --- a/java/examples/Demos/Graphics/LowLevelGL/LowLevelGL.pde +++ b/java/examples/Demos/Graphics/LowLevelGL/LowLevelGL.pde @@ -2,7 +2,7 @@ import java.nio.*; PGL pgl; -PShader flatShader; +PShader sh; int vertLoc; int colorLoc; @@ -18,7 +18,7 @@ void setup() { // Loads a shader to render geometry w/out // textures and lights. - flatShader = loadShader("frag.glsl", "vert.glsl"); + sh = loadShader("frag.glsl", "vert.glsl"); vertices = new float[12]; vertData = allocateDirectFloatBuffer(12); @@ -37,10 +37,10 @@ void draw() { updateGeometry(); pgl = beginPGL(); - flatShader.bind(); + sh.bind(); - vertLoc = pgl.getAttribLocation(flatShader.glProgram, "vertex"); - colorLoc = pgl.getAttribLocation(flatShader.glProgram, "color"); + vertLoc = pgl.getAttribLocation(sh.glProgram, "vertex"); + colorLoc = pgl.getAttribLocation(sh.glProgram, "color"); pgl.enableVertexAttribArray(vertLoc); pgl.enableVertexAttribArray(colorLoc); @@ -53,7 +53,7 @@ void draw() { pgl.disableVertexAttribArray(vertLoc); pgl.disableVertexAttribArray(colorLoc); - flatShader.unbind(); + sh.unbind(); endPGL(); } diff --git a/java/examples/Demos/Graphics/LowLevelGL/data/vert.glsl b/java/examples/Demos/Graphics/LowLevelGL/data/vert.glsl index 1a0177008..27c255ee5 100644 --- a/java/examples/Demos/Graphics/LowLevelGL/data/vert.glsl +++ b/java/examples/Demos/Graphics/LowLevelGL/data/vert.glsl @@ -18,8 +18,6 @@ Boston, MA 02111-1307 USA */ -#define PROCESSING_COLOR_SHADER - uniform mat4 transform; attribute vec4 vertex; diff --git a/java/examples/Demos/Tests/EmbedFrameTest/Arcball.pde b/java/examples/Demos/Tests/EmbedFrameTest/Arcball.pde new file mode 100644 index 000000000..914e0d1d6 --- /dev/null +++ b/java/examples/Demos/Tests/EmbedFrameTest/Arcball.pde @@ -0,0 +1,194 @@ +// Ariel and V3ga's arcball class with a couple tiny mods by Robert Hodgin + +class Arcball { + PApplet parent; + float center_x, center_y, radius; + Vec3 v_down, v_drag; + Quat q_now, q_down, q_drag; + Vec3[] axisSet; + int axis; + float mxv, myv; + float x, y; + + Arcball(PApplet parent, float radius){ + this.parent = parent; + this.radius = radius; + + v_down = new Vec3(); + v_drag = new Vec3(); + + q_now = new Quat(); + q_down = new Quat(); + q_drag = new Quat(); + + axisSet = new Vec3[] {new Vec3(1.0f, 0.0f, 0.0f), new Vec3(0.0f, 1.0f, 0.0f), new Vec3(0.0f, 0.0f, 1.0f)}; + axis = -1; // no constraints... + } + + void mousePressed(){ + v_down = mouse_to_sphere(parent.mouseX, parent.mouseY); + q_down.set(q_now); + q_drag.reset(); + } + + void mouseDragged(){ + v_drag = mouse_to_sphere(parent.mouseX, parent.mouseY); + q_drag.set(Vec3.dot(v_down, v_drag), Vec3.cross(v_down, v_drag)); + } + + void run(){ + center_x = parent.width/2.0; + center_y = parent.height/2.0; + + q_now = Quat.mul(q_drag, q_down); + parent.translate(center_x, center_y); + applyQuat2Matrix(q_now); + + x += mxv; + y += myv; + mxv -= mxv * .01; + myv -= myv * .01; + } + + Vec3 mouse_to_sphere(float x, float y){ + Vec3 v = new Vec3(); + v.x = (x - center_x) / radius; + v.y = (y - center_y) / radius; + + float mag = v.x * v.x + v.y * v.y; + if (mag > 1.0f){ + v.normalize(); + } else { + v.z = sqrt(1.0f - mag); + } + + return (axis == -1) ? v : constrain_vector(v, axisSet[axis]); + } + + Vec3 constrain_vector(Vec3 vector, Vec3 axis){ + Vec3 res = new Vec3(); + res.sub(vector, Vec3.mul(axis, Vec3.dot(axis, vector))); + res.normalize(); + return res; + } + + void applyQuat2Matrix(Quat q){ + // instead of transforming q into a matrix and applying it... + + float[] aa = q.getValue(); + parent.rotate(aa[0], aa[1], aa[2], aa[3]); + } +} + +static class Vec3{ + float x, y, z; + + Vec3(){ + } + + Vec3(float x, float y, float z){ + this.x = x; + this.y = y; + this.z = z; + } + + void normalize(){ + float length = length(); + x /= length; + y /= length; + z /= length; + } + + float length(){ + return (float) Math.sqrt(x * x + y * y + z * z); + } + + static Vec3 cross(Vec3 v1, Vec3 v2){ + Vec3 res = new Vec3(); + res.x = v1.y * v2.z - v1.z * v2.y; + res.y = v1.z * v2.x - v1.x * v2.z; + res.z = v1.x * v2.y - v1.y * v2.x; + return res; + } + + static float dot(Vec3 v1, Vec3 v2){ + return v1.x * v2.x + v1.y * v2.y + v1.z * v2.z; + } + + static Vec3 mul(Vec3 v, float d){ + Vec3 res = new Vec3(); + res.x = v.x * d; + res.y = v.y * d; + res.z = v.z * d; + return res; + } + + void sub(Vec3 v1, Vec3 v2){ + x = v1.x - v2.x; + y = v1.y - v2.y; + z = v1.z - v2.z; + } +} + +static class Quat{ + float w, x, y, z; + + Quat(){ + reset(); + } + + Quat(float w, float x, float y, float z){ + this.w = w; + this.x = x; + this.y = y; + this.z = z; + } + + void reset(){ + w = 1.0f; + x = 0.0f; + y = 0.0f; + z = 0.0f; + } + + void set(float w, Vec3 v){ + this.w = w; + x = v.x; + y = v.y; + z = v.z; + } + + void set(Quat q){ + w = q.w; + x = q.x; + y = q.y; + z = q.z; + } + + static Quat mul(Quat q1, Quat q2){ + Quat res = new Quat(); + res.w = q1.w * q2.w - q1.x * q2.x - q1.y * q2.y - q1.z * q2.z; + res.x = q1.w * q2.x + q1.x * q2.w + q1.y * q2.z - q1.z * q2.y; + res.y = q1.w * q2.y + q1.y * q2.w + q1.z * q2.x - q1.x * q2.z; + res.z = q1.w * q2.z + q1.z * q2.w + q1.x * q2.y - q1.y * q2.x; + return res; + } + + float[] getValue(){ + // transforming this quat into an angle and an axis vector... + + float[] res = new float[4]; + + float sa = (float) Math.sqrt(1.0f - w * w); + if (sa < EPSILON){ + sa = 1.0f; + } + + res[0] = (float) Math.acos(w) * 2.0f; + res[1] = x / sa; + res[2] = y / sa; + res[3] = z / sa; + + return res; + } +} diff --git a/java/examples/Demos/Tests/EmbedFrameTest/EmbedFrameTest.pde b/java/examples/Demos/Tests/EmbedFrameTest/EmbedFrameTest.pde new file mode 100644 index 000000000..2dda37a4e --- /dev/null +++ b/java/examples/Demos/Tests/EmbedFrameTest/EmbedFrameTest.pde @@ -0,0 +1,103 @@ +// Based on code by GeneKao (https://github.com/GeneKao) + +import javax.swing.JFrame; +import java.awt.BorderLayout; +import java.awt.Insets; +EmbeddedSketch eSketch; +ChildApplet child = new ChildApplet(); +boolean mousePressedOnParent = false; +Arcball arcball, arcball2; + +void setup() { + size(320, 240, P3D); + arcball = new Arcball(this, 300); + eSketch = new EmbeddedSketch(child); + smooth(); +} + +void draw() { + background(250); + arcball.run(); + if (mousePressed) { + fill(0); + text("Mouse pressed on parent.", 10, 10); + fill(0, 240, 0); + ellipse(mouseX, mouseY, 60, 60); + mousePressedOnParent = true; + } else { + fill(20); + ellipse(width/2, height/2, 60, 60); + mousePressedOnParent = false; + } + box(100); + if (eSketch.sketch.mousePressed) { + text("Mouse pressed on child.", 10, 30); + } +} + +void mousePressed(){ + arcball.mousePressed(); +} + +void mouseDragged(){ + arcball.mouseDragged(); +} + +//The JFrame which will contain the child applet +class EmbeddedSketch extends JFrame { + PApplet sketch; + EmbeddedSketch(PApplet p) { + int w = 400; + int h = 400; + sketch = p; + setVisible(true); + + setLayout(new BorderLayout()); + add(p, BorderLayout.CENTER); + p.init(); + + Insets insets = getInsets(); + setSize(insets.left + w, insets.top + h); + p.setBounds(insets.left, insets.top, w, h); + + setLocation(500, 200); + setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + } +} + +class ChildApplet extends PApplet { + void setup() { + size(400, 400, P3D); + smooth(); + arcball2 = new Arcball(this, 300); + } + + void draw() { + background(0); + arcball2.run(); + if (mousePressed) { + fill(240, 0, 0); + ellipse(mouseX, mouseY, 20, 20); + fill(255); + text("Mouse pressed on child.", 10, 30); + } else { + fill(255); + ellipse(width/2, height/2, 20, 20); + } + + box(100, 200, 100); + if (mousePressedOnParent) { + fill(255); + text("Mouse pressed on parent", 20, 20); + } + } + + void mousePressed(){ + arcball2.mousePressed(); + } + + void mouseDragged(){ + arcball2.mouseDragged(); + } +} + diff --git a/java/examples/Topics/Advanced Data/HashMapClass/HashMapClass.pde b/java/examples/Topics/Advanced Data/HashMapClass/HashMapClass.pde index 966641e24..bf8959bc2 100644 --- a/java/examples/Topics/Advanced Data/HashMapClass/HashMapClass.pde +++ b/java/examples/Topics/Advanced Data/HashMapClass/HashMapClass.pde @@ -8,8 +8,8 @@ * If you are familiar with associative arrays from other languages, * this is the same idea. * - * A simpler example is CountingStrings which uses IntHash instead of - * HashMap. The Processing classes IntHash, FloatHash, and StringHash + * A simpler example is CountingStrings which uses IntDict instead of + * HashMap. The Processing classes IntDict, FloatDict, and StringDict * offer a simpler way of pairing Strings with numbers or other Strings. * Here we use a HashMap because we want to pair a String with a custom * object, in this case a "Word" object that stores two numbers. diff --git a/java/examples/Topics/Animation/Sequential/Sequential.pde b/java/examples/Topics/Animation/Sequential/Sequential.pde index 290801fb1..89a1f3b91 100644 --- a/java/examples/Topics/Animation/Sequential/Sequential.pde +++ b/java/examples/Topics/Animation/Sequential/Sequential.pde @@ -13,7 +13,7 @@ PT_anim0004.gif, PT_anim0005.gif, PT_anim0006.gif, PT_anim0007.gif, PT_anim0008. PT_anim0009.gif, PT_anim0010.gif, PT_anim0011.gif"; */ int numFrames = 12; // The number of frames in the animation -int frame = 0; +int currentFrame = 0; PImage[] images = new PImage[numFrames]; void setup() { @@ -46,12 +46,12 @@ void setup() { void draw() { background(0); - frame = (frame+1) % numFrames; // Use % to cycle through frames + currentFrame = (currentFrame+1) % numFrames; // Use % to cycle through frames int offset = 0; for (int x = -100; x < width; x += images[0].width) { - image(images[(frame+offset) % numFrames], x, -20); + image(images[(currentFrame+offset) % numFrames], x, -20); offset+=2; - image(images[(frame+offset) % numFrames], x, height/2); + image(images[(currentFrame+offset) % numFrames], x, height/2); offset+=2; } } diff --git a/java/examples/Topics/Cellular Automata/Wolfram/CA.pde b/java/examples/Topics/Cellular Automata/Wolfram/CA.pde index 9d468253b..4beca4edc 100644 --- a/java/examples/Topics/Cellular Automata/Wolfram/CA.pde +++ b/java/examples/Topics/Cellular Automata/Wolfram/CA.pde @@ -13,13 +13,6 @@ class CA { restart(); } - CA() { - scl = 1; - cells = new int[width/scl]; - randomize(); - restart(); - } - // Set the rules of the CA void setRules(int[] r) { rules = r; diff --git a/java/examples/Topics/Cellular Automata/Wolfram/Wolfram.pde b/java/examples/Topics/Cellular Automata/Wolfram/Wolfram.pde index 328e6b9b9..936bcc3e9 100644 --- a/java/examples/Topics/Cellular Automata/Wolfram/Wolfram.pde +++ b/java/examples/Topics/Cellular Automata/Wolfram/Wolfram.pde @@ -10,11 +10,10 @@ CA ca; // An instance object to describe the Wolfram basic Cellular Automata void setup() { - size(640, 360, P2D); - frameRate(30); - background(0); + size(640, 360); int[] ruleset = {0,1,0,1,1,0,1,0}; // An initial rule system ca = new CA(ruleset); // Initialize CA + background(0); } void draw() { diff --git a/java/examples/Topics/Create Shapes/PrimitivePShape/PrimitivePShape.pde b/java/examples/Topics/Create Shapes/PrimitivePShape/PrimitivePShape.pde index 9ae00c12a..6e24680ec 100644 --- a/java/examples/Topics/Create Shapes/PrimitivePShape/PrimitivePShape.pde +++ b/java/examples/Topics/Create Shapes/PrimitivePShape/PrimitivePShape.pde @@ -12,8 +12,7 @@ void setup() { size(640, 360, P2D); smooth(); // Creating the PShape as an ellipse - // The corner is -50,-50 so that the center is at 0,0 - circle = createShape(ELLIPSE, -50, -25, 100, 50); + circle = createShape(ELLIPSE, 0, 0, 100, 50); } void draw() { @@ -27,4 +26,3 @@ void draw() { // Drawing the PShape shape(circle); } - diff --git a/java/examples/Topics/Shaders/Conway/Conway.pde b/java/examples/Topics/Shaders/Conway/Conway.pde index 472b6c758..529f821b0 100644 --- a/java/examples/Topics/Shaders/Conway/Conway.pde +++ b/java/examples/Topics/Shaders/Conway/Conway.pde @@ -1,7 +1,7 @@ // GLSL version of Conway's game of life, ported from GLSL sandbox: // http://glsl.heroku.com/e#207.3 -// Exemplifies the use of the buffer uniform in the shader, that gives -// access to the previous frame. +// Exemplifies the use of the ppixels uniform in the shader, that gives +// access to the pixels of the previous frame. PShader conway; PGraphics pg; diff --git a/java/examples/Topics/Shaders/Conway/data/conway.glsl b/java/examples/Topics/Shaders/Conway/data/conway.glsl index dca8dfd7f..694491ef0 100644 --- a/java/examples/Topics/Shaders/Conway/data/conway.glsl +++ b/java/examples/Topics/Shaders/Conway/data/conway.glsl @@ -9,7 +9,7 @@ precision highp float; uniform float time; uniform vec2 mouse; uniform vec2 resolution; -uniform sampler2D buffer; +uniform sampler2D ppixels; vec4 live = vec4(0.5,1.0,0.7,1.); vec4 dead = vec4(0.,0.,0.,1.); @@ -28,15 +28,15 @@ void main( void ) { } } else { float sum = 0.; - sum += texture2D(buffer, position + pixel * vec2(-1., -1.)).g; - sum += texture2D(buffer, position + pixel * vec2(-1., 0.)).g; - sum += texture2D(buffer, position + pixel * vec2(-1., 1.)).g; - sum += texture2D(buffer, position + pixel * vec2(1., -1.)).g; - sum += texture2D(buffer, position + pixel * vec2(1., 0.)).g; - sum += texture2D(buffer, position + pixel * vec2(1., 1.)).g; - sum += texture2D(buffer, position + pixel * vec2(0., -1.)).g; - sum += texture2D(buffer, position + pixel * vec2(0., 1.)).g; - vec4 me = texture2D(buffer, position); + sum += texture2D(ppixels, position + pixel * vec2(-1., -1.)).g; + sum += texture2D(ppixels, position + pixel * vec2(-1., 0.)).g; + sum += texture2D(ppixels, position + pixel * vec2(-1., 1.)).g; + sum += texture2D(ppixels, position + pixel * vec2(1., -1.)).g; + sum += texture2D(ppixels, position + pixel * vec2(1., 0.)).g; + sum += texture2D(ppixels, position + pixel * vec2(1., 1.)).g; + sum += texture2D(ppixels, position + pixel * vec2(0., -1.)).g; + sum += texture2D(ppixels, position + pixel * vec2(0., 1.)).g; + vec4 me = texture2D(ppixels, position); if (me.g <= 0.1) { if ((sum >= 2.9) && (sum <= 3.1)) { diff --git a/java/examples/Topics/Shaders/CustomBlend/CustomBlend.pde b/java/examples/Topics/Shaders/CustomBlend/CustomBlend.pde new file mode 100644 index 000000000..c6fb40b1d --- /dev/null +++ b/java/examples/Topics/Shaders/CustomBlend/CustomBlend.pde @@ -0,0 +1,110 @@ +/** + * Custom Blend + * + * The OpenGL-based renderers (P2D and P3D) only support some of the + * blending modes available in the default renderer. The reason for this + * is that the blend equations in OpenGL allow for combinations of the + * form dest_factor * dest_color + src_factor * src_color of the source and + * destination colors (see this page http://www.opengl.org/wiki/Blending + * for an extensive discussion of blending in OpenGL). + * Complex blending modes typically available in photo editing tools, + * like hard light or dodge, cannot be modeled with those equations. + * However, we can implement virtually any blending math directly in the + * fragment shader. + * + * This example shows how custom blend shaders can be loaded and used in + * Processing. + * For detailed information on how to implement Photoshop-like blending modes, + * check the following pages (a bit old but still useful): + * http://www.pegtop.net/delphi/articles/blendmodes/index.htm + * http://mouaif.wordpress.com/2009/01/05/photoshop-math-with-glsl-shaders/ + * + */ + +PImage destImage; +PImage srcImage; +PShader dodge; +PShader burn; +PShader overlay; +PShader difference; + +void setup() { + size(640, 360, P2D); + destImage = loadImage("leaves.jpg"); + srcImage = loadImage("moonwalk.jpg"); + + initShaders(); +} + +void draw() { + background(0); + + shader(dodge); + drawOutput(0, 0, width/2, height/2); + shader(burn); + drawOutput(width/2, 0, width/2, height/2); + shader(overlay); + drawOutput(0, height/2, width/2, height/2); + shader(difference); + drawOutput(width/2, height/2, width/2, height/2); + + noLoop(); +} + +void initShaders() { + dodge = loadShader("dodge.glsl"); + burn = loadShader("burn.glsl"); + overlay = loadShader("overlay.glsl"); + difference = loadShader("difference.glsl"); + + // The names destination and source come from the OpenGL terminology: + // destination from the image already in the framebuffer, or "base layer", + // and source for the image that will be blended into the framebuffer, or + // "blend layer": + dodge.set("destSampler", destImage); + dodge.set("srcSampler", srcImage); + burn.set("destSampler", destImage); + burn.set("srcSampler", srcImage); + overlay.set("destSampler", destImage); + overlay.set("srcSampler", srcImage); + difference.set("destSampler", destImage); + difference.set("srcSampler", srcImage); + + // We set the sizes of de st and src images, and the rectangular areas + // from the images that we will use for blending: + dodge.set("destSize", 640, 360); + dodge.set("destRect", 100, 50, 200, 200); + burn.set("destSize", 640, 360); + burn.set("destRect", 100, 50, 200, 200); + overlay.set("destSize", 640, 360); + overlay.set("destRect", 100, 50, 200, 200); + difference.set("destSize", 640, 360); + difference.set("destRect", 100, 50, 200, 200); + + dodge.set("srcSize", 640, 360); + dodge.set("srcRect", 0, 0, 640, 360); + burn.set("srcSize", 640, 360); + burn.set("srcRect", 0, 0, 640, 360); + overlay.set("srcSize", 640, 360); + overlay.set("srcRect", 0, 0, 640, 360); + difference.set("srcSize", 640, 360); + difference.set("srcRect", 0, 0, 640, 360); +} + +void drawOutput(float x, float y, float w, float h) { + pushMatrix(); + translate(x, y); + noStroke(); + beginShape(QUAD); + // Although we are not associating a texture to + // this shape, the uv coordinates will be stored + // anyways so they can be used in the fragment + // shader to access the destination and source + // images. + vertex(0, 0, 0, 0); + vertex(w, 0, 1, 0); + vertex(w, h, 1, 1); + vertex(0, h, 0, 1); + endShape(); + popMatrix(); +} diff --git a/java/examples/Topics/Shaders/CustomBlend/data/burn.glsl b/java/examples/Topics/Shaders/CustomBlend/data/burn.glsl new file mode 100644 index 000000000..0d090a6ac --- /dev/null +++ b/java/examples/Topics/Shaders/CustomBlend/data/burn.glsl @@ -0,0 +1,22 @@ +uniform sampler2D destSampler; +uniform sampler2D srcSampler; + +uniform ivec2 destSize; +uniform ivec4 destRect; + +uniform ivec2 srcSize; +uniform ivec4 srcRect; + +varying vec4 vertTexCoord; + +void main() { + vec2 st = vertTexCoord.st; + + vec2 dest = vec2(destRect.xy) / vec2(destSize) + st * vec2(destRect.zw) / vec2(destSize); + vec2 src = vec2(srcRect.xy) / vec2(srcSize) + st * vec2(srcRect.zw) / vec2(srcSize); + + vec3 destColor = texture2D(destSampler, dest).rgb; + vec3 srcColor = texture2D(srcSampler, src).rgb; + + gl_FragColor = vec4(1.0 - (1.0 - destColor) / srcColor, 1.0); +} \ No newline at end of file diff --git a/java/examples/Topics/Shaders/CustomBlend/data/difference.glsl b/java/examples/Topics/Shaders/CustomBlend/data/difference.glsl new file mode 100644 index 000000000..7f0040a22 --- /dev/null +++ b/java/examples/Topics/Shaders/CustomBlend/data/difference.glsl @@ -0,0 +1,22 @@ +uniform sampler2D destSampler; +uniform sampler2D srcSampler; + +uniform ivec2 destSize; +uniform ivec4 destRect; + +uniform ivec2 srcSize; +uniform ivec4 srcRect; + +varying vec4 vertTexCoord; + +void main() { + vec2 st = vertTexCoord.st; + + vec2 dest = vec2(destRect.xy) / vec2(destSize) + st * vec2(destRect.zw) / vec2(destSize); + vec2 src = vec2(srcRect.xy) / vec2(srcSize) + st * vec2(srcRect.zw) / vec2(srcSize); + + vec3 destColor = texture2D(destSampler, dest).rgb; + vec3 srcColor = texture2D(srcSampler, src).rgb; + + gl_FragColor = vec4(abs(srcColor - destColor), 1.0); +} \ No newline at end of file diff --git a/java/examples/Topics/Shaders/CustomBlend/data/dodge.glsl b/java/examples/Topics/Shaders/CustomBlend/data/dodge.glsl new file mode 100644 index 000000000..115dbaa09 --- /dev/null +++ b/java/examples/Topics/Shaders/CustomBlend/data/dodge.glsl @@ -0,0 +1,22 @@ +uniform sampler2D destSampler; +uniform sampler2D srcSampler; + +uniform ivec2 destSize; +uniform ivec4 destRect; + +uniform ivec2 srcSize; +uniform ivec4 srcRect; + +varying vec4 vertTexCoord; + +void main() { + vec2 st = vertTexCoord.st; + + vec2 dest = vec2(destRect.xy) / vec2(destSize) + st * vec2(destRect.zw) / vec2(destSize); + vec2 src = vec2(srcRect.xy) / vec2(srcSize) + st * vec2(srcRect.zw) / vec2(srcSize); + + vec3 destColor = texture2D(destSampler, dest).rgb; + vec3 srcColor = texture2D(srcSampler, src).rgb; + + gl_FragColor = vec4(destColor / (1.0 - srcColor), 1.0); +} \ No newline at end of file diff --git a/java/examples/Topics/Shaders/CustomBlend/data/overlay.glsl b/java/examples/Topics/Shaders/CustomBlend/data/overlay.glsl new file mode 100644 index 000000000..f7d28efac --- /dev/null +++ b/java/examples/Topics/Shaders/CustomBlend/data/overlay.glsl @@ -0,0 +1,28 @@ +uniform sampler2D destSampler; +uniform sampler2D srcSampler; + +uniform ivec2 destSize; +uniform ivec4 destRect; + +uniform ivec2 srcSize; +uniform ivec4 srcRect; + +varying vec4 vertTexCoord; + +void main() { + vec2 st = vertTexCoord.st; + + vec2 dest = vec2(destRect.xy) / vec2(destSize) + st * vec2(destRect.zw) / vec2(destSize); + vec2 src = vec2(srcRect.xy) / vec2(srcSize) + st * vec2(srcRect.zw) / vec2(srcSize); + + vec3 destColor = texture2D(destSampler, dest).rgb; + vec3 srcColor = texture2D(srcSampler, src).rgb; + + float luminance = dot(vec3(0.2126, 0.7152, 0.0722), destColor); + + if (luminance < 0.5) { + gl_FragColor = vec4(2.0 * destColor * srcColor, 1.0); + } else { + gl_FragColor = vec4(1.0 - 2.0 * (1.0 - destColor) * (1.0 - srcColor), 1); + } +} \ No newline at end of file diff --git a/java/examples/Topics/Shaders/DomeProjection/CubeMapUtils.pde b/java/examples/Topics/Shaders/DomeProjection/CubeMapUtils.pde new file mode 100644 index 000000000..49c822eb7 --- /dev/null +++ b/java/examples/Topics/Shaders/DomeProjection/CubeMapUtils.pde @@ -0,0 +1,99 @@ +void initCubeMap() { + sphereDetail(50); + domeSphere = createShape(SPHERE, height/2.0f); + domeSphere.rotateX(HALF_PI); + domeSphere.setStroke(false); + + PGL pgl = beginPGL(); + + envMapTextureID = IntBuffer.allocate(1); + pgl.genTextures(1, envMapTextureID); + pgl.bindTexture(PGL.TEXTURE_CUBE_MAP, envMapTextureID.get(0)); + pgl.texParameteri(PGL.TEXTURE_CUBE_MAP, PGL.TEXTURE_WRAP_S, PGL.CLAMP_TO_EDGE); + pgl.texParameteri(PGL.TEXTURE_CUBE_MAP, PGL.TEXTURE_WRAP_T, PGL.CLAMP_TO_EDGE); + pgl.texParameteri(PGL.TEXTURE_CUBE_MAP, PGL.TEXTURE_WRAP_R, PGL.CLAMP_TO_EDGE); + pgl.texParameteri(PGL.TEXTURE_CUBE_MAP, PGL.TEXTURE_MIN_FILTER, PGL.NEAREST); + pgl.texParameteri(PGL.TEXTURE_CUBE_MAP, PGL.TEXTURE_MAG_FILTER, PGL.NEAREST); + for (int i = PGL.TEXTURE_CUBE_MAP_POSITIVE_X; i < PGL.TEXTURE_CUBE_MAP_POSITIVE_X + 6; i++) { + pgl.texImage2D(i, 0, PGL.RGBA8, envMapSize, envMapSize, 0, PGL.RGBA, PGL.UNSIGNED_BYTE, null); + } + + // Init fbo, rbo + fbo = IntBuffer.allocate(1); + rbo = IntBuffer.allocate(1); + pgl.genFramebuffers(1, fbo); + pgl.bindFramebuffer(PGL.FRAMEBUFFER, fbo.get(0)); + pgl.framebufferTexture2D(PGL.FRAMEBUFFER, PGL.COLOR_ATTACHMENT0, PGL.TEXTURE_CUBE_MAP_POSITIVE_X, envMapTextureID.get(0), 0); + + pgl.genRenderbuffers(1, rbo); + pgl.bindRenderbuffer(PGL.RENDERBUFFER, rbo.get(0)); + pgl.renderbufferStorage(PGL.RENDERBUFFER, PGL.DEPTH_COMPONENT24, envMapSize, envMapSize); + + // Attach depth buffer to FBO + pgl.framebufferRenderbuffer(PGL.FRAMEBUFFER, PGL.DEPTH_ATTACHMENT, PGL.RENDERBUFFER, rbo.get(0)); + + endPGL(); + + // Load cubemap shader. + cubemapShader = loadShader("cubemapfrag.glsl", "cubemapvert.glsl"); + cubemapShader.set("cubemap", 1); +} + +void drawCubeMap() { + PGL pgl = beginPGL(); + pgl.activeTexture(PGL.TEXTURE1); + pgl.enable(PGL.TEXTURE_CUBE_MAP); + pgl.bindTexture(PGL.TEXTURE_CUBE_MAP, envMapTextureID.get(0)); + regenerateEnvMap(pgl); + endPGL(); + + drawDomeMaster(); + + pgl.disable(PGL.TEXTURE_CUBE_MAP); + pgl.bindTexture(PGL.TEXTURE_CUBE_MAP, 0); +} + +void drawDomeMaster() { + camera(); + ortho(0, width, 0, height); + resetMatrix(); + shader(cubemapShader); + shape(domeSphere); + resetShader(); +} + +// Called to regenerate the envmap +void regenerateEnvMap(PGL pgl) { + // bind fbo + pgl.bindFramebuffer(PGL.FRAMEBUFFER, fbo.get(0)); + + // generate 6 views from origin(0, 0, 0) + pgl.viewport(0, 0, envMapSize, envMapSize); + perspective(90.0f * DEG_TO_RAD, 1.0f, 1.0f, 1025.0f); + for (int face = PGL.TEXTURE_CUBE_MAP_POSITIVE_X; face < + PGL.TEXTURE_CUBE_MAP_NEGATIVE_Z; face++) { + resetMatrix(); + + if (face == PGL.TEXTURE_CUBE_MAP_POSITIVE_X) { + camera(0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, -1.0f, 0.0f); + } else if (face == PGL.TEXTURE_CUBE_MAP_NEGATIVE_X) { + camera(0.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, 0.0f, -1.0f, 0.0f); + } else if (face == PGL.TEXTURE_CUBE_MAP_POSITIVE_Y) { + camera(0.0f, 0.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, 0.0f, -1.0f); + } else if (face == PGL.TEXTURE_CUBE_MAP_NEGATIVE_Y) { + camera(0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f); + } else if (face == PGL.TEXTURE_CUBE_MAP_POSITIVE_Z) { + camera(0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, -1.0f, 0.0f); + } + + scale(-1, 1, -1); + translate(-width * 0.5f, -height * 0.5f, -500); + + pgl.framebufferTexture2D(PGL.FRAMEBUFFER, PGL.COLOR_ATTACHMENT0, face, envMapTextureID.get(0), 0); + + drawScene(); // Draw objects in the scene + flush(); // Make sure that the geometry in the scene is pushed to the GPU + noLights(); // Disabling lights to avoid adding many times + pgl.framebufferTexture2D(PGL.FRAMEBUFFER, PGL.COLOR_ATTACHMENT0, face, 0, 0); + } +} diff --git a/java/examples/Topics/Shaders/DomeProjection/DomeProjection.pde b/java/examples/Topics/Shaders/DomeProjection/DomeProjection.pde new file mode 100644 index 000000000..2c39c08cc --- /dev/null +++ b/java/examples/Topics/Shaders/DomeProjection/DomeProjection.pde @@ -0,0 +1,52 @@ +/** + * DomeProjection + * + * This sketch uses use environmental mapping to render the output + * on a full spherical dome. + * + * Based on the FullDomeTemplate code from Christopher Warnow: + * https://github.com/mphasize/FullDome + * + */ + +import java.nio.IntBuffer; + +PShader cubemapShader; +PShape domeSphere; + +IntBuffer fbo; +IntBuffer rbo; +IntBuffer envMapTextureID; + +int envMapSize = 1024; + +void setup() { + size(640, 640, P3D); + initCubeMap(); +} + +void draw() { + background(0); + drawCubeMap(); +} + +void drawScene() { + background(0); + + stroke(255, 0, 0); + strokeWeight(2); + for (int i = -width; i < 2 * width; i += 50) { + line(i, -height, -100, i, 2 *height, -100); + } + for (int i = -height; i < 2 * height; i += 50) { + line(-width, i, -100, 2 * width, i, -100); + } + + lights(); + noStroke(); + translate(mouseX, mouseY, 200); + rotateX(frameCount * 0.01); + rotateY(frameCount * 0.01); + box(100); +} + diff --git a/java/examples/Topics/Shaders/DomeProjection/data/cubemapfrag.glsl b/java/examples/Topics/Shaders/DomeProjection/data/cubemapfrag.glsl new file mode 100644 index 000000000..0d47ce133 --- /dev/null +++ b/java/examples/Topics/Shaders/DomeProjection/data/cubemapfrag.glsl @@ -0,0 +1 @@ +uniform samplerCube cubemap; varying vec3 reflectDir; void main() { vec3 color = vec3(textureCube(cubemap, reflectDir)); gl_FragColor = vec4(color, 1.0); } \ No newline at end of file diff --git a/java/examples/Topics/Shaders/DomeProjection/data/cubemapvert.glsl b/java/examples/Topics/Shaders/DomeProjection/data/cubemapvert.glsl new file mode 100644 index 000000000..1d96e9b52 --- /dev/null +++ b/java/examples/Topics/Shaders/DomeProjection/data/cubemapvert.glsl @@ -0,0 +1 @@ +uniform mat4 transform; uniform mat4 modelview; uniform mat3 normalMatrix; attribute vec4 vertex; attribute vec3 normal; varying vec3 reflectDir; void main() { gl_Position = transform * vertex; vec3 ecNormal = normalize(normalMatrix * normal); // Vertex in eye coordinates vec3 ecVertex = vec3(modelview * vertex); // Normal vector in eye coordinates vec3 eyeDir = ecVertex.xyz; reflectDir = reflect(eyeDir, ecNormal); } \ No newline at end of file diff --git a/java/examples/Topics/Shaders/FishEye/FishEye.pde b/java/examples/Topics/Shaders/FishEye/FishEye.pde deleted file mode 100644 index 1453cc278..000000000 --- a/java/examples/Topics/Shaders/FishEye/FishEye.pde +++ /dev/null @@ -1,52 +0,0 @@ -/** - * Fish Eye - * - * This fish-eye shader is useful for dome projection. - */ - -PShader fisheye; -PGraphics canvas; -PImage img; - -boolean useFishEye = true; - -void setup() { - size(640, 640, P3D); - canvas = createGraphics(width, height, P3D); - - fisheye = loadShader("FishEye.glsl"); - fisheye.set("aperture", 180.0); -} - -void draw() { - canvas.beginDraw(); - canvas.background(0); - canvas.stroke(255, 0, 0); - for (int i = 0; i < width; i += 10) { - canvas.line(i, 0, i, height); - } - for (int i = 0; i < height; i += 10) { - canvas.line(0, i, width, i); - } - canvas.lights(); - canvas.noStroke(); - canvas.translate(mouseX, mouseY, 100); - canvas.rotateX(frameCount * 0.01); - canvas.rotateY(frameCount * 0.01); - canvas.box(100); - canvas.endDraw(); - - if (useFishEye == true) { - shader(fisheye); - } - image(canvas, 0, 0, width, height); -} - -void mousePressed() { - if (useFishEye) { - useFishEye = false; - resetShader(); - } else { - useFishEye = true; - } -} diff --git a/java/examples/Topics/Shaders/FishEye/data/FishEye.glsl b/java/examples/Topics/Shaders/FishEye/data/FishEye.glsl deleted file mode 100644 index 9865603e6..000000000 --- a/java/examples/Topics/Shaders/FishEye/data/FishEye.glsl +++ /dev/null @@ -1,59 +0,0 @@ -// Inspired by the "Angular Fisheye à la Bourke" sketch from -// Jonathan Cremieux, as shown in the OpenProcessing website: -// http://openprocessing.org/visuals/?visualID=12140 -// Using the inverse transform of the angular fisheye as -// explained in Paul Bourke's website: -// http://paulbourke.net/miscellaneous/domefisheye/fisheye/ - -#ifdef GL_ES -precision mediump float; -precision mediump int; -#endif - -#define PROCESSING_TEXTURE_SHADER - -uniform sampler2D texture; -uniform mat4 texMatrix; - -varying vec4 vertColor; -varying vec4 vertTexCoord; - -uniform float aperture; - -const float PI = 3.1415926535; - -void main(void) { - float apertureHalf = 0.5 * aperture * (PI / 180.0); - - // This factor ajusts the coordinates in the case that - // the aperture angle is less than 180 degrees, in which - // case the area displayed is not the entire half-sphere. - float maxFactor = sin(apertureHalf); - - // The st factor takes into account the situation when non-pot - // textures are not supported, so that the maximum texture - // coordinate to cover the entire image might not be 1. - vec2 stFactor = vec2(1.0 / abs(texMatrix[0][0]), 1.0 / abs(texMatrix[1][1])); - vec2 pos = (2.0 * vertTexCoord.st * stFactor - 1.0); - - float l = length(pos); - if (l > 1.0) { - gl_FragColor = vec4(0, 0, 0, 1); - } else { - float x = maxFactor * pos.x; - float y = maxFactor * pos.y; - - float n = length(vec2(x, y)); - - float z = sqrt(1.0 - n * n); - - float r = atan(n, z) / PI; - - float phi = atan(y, x); - - float u = r * cos(phi) + 0.5; - float v = r * sin(phi) + 0.5; - - gl_FragColor = texture2D(texture, vec2(u, v) / stFactor) * vertColor; - } -} \ No newline at end of file diff --git a/java/keywords.txt b/java/keywords.txt index 5ba33d3ab..1a3505f3e 100644 --- a/java/keywords.txt +++ b/java/keywords.txt @@ -661,6 +661,7 @@ popMatrix FUNCTION1 popMatrix_ popStyle FUNCTION1 popStyle_ pow FUNCTION1 pow_ print FUNCTION1 print_ +printArray FUNCTION1 printArray_ printCamera FUNCTION1 printCamera_ println FUNCTION1 println_ printMatrix FUNCTION1 printMatrix_ @@ -670,6 +671,7 @@ PShader FUNCTION2 PShader_set_ PShape KEYWORD5 PShape addChild FUNCTION2 PShape_addChild_ beginContour FUNCTION2 PShape_beginContour_ +beginShape FUNCTION2 PShape_beginShape_ disableStyle FUNCTION2 PShape_disableStyle_ enableStyle FUNCTION2 PShape_enableStyle_ endContour FUNCTION2 PShape_endContour_ diff --git a/java/libraries/dxf/build.xml b/java/libraries/dxf/build.xml old mode 100644 new mode 100755 index f059571e3..4e77c39aa --- a/java/libraries/dxf/build.xml +++ b/java/libraries/dxf/build.xml @@ -3,7 +3,7 @@ - + @@ -18,7 +18,11 @@ srcdir="src" destdir="bin" encoding="UTF-8" includeAntRuntime="false" - classpath="../../../core/library/core.jar" /> + classpath="../../../core/library/core.jar" + nowarn="true" + compiler="org.eclipse.jdt.core.JDTCompilerAdapter"> + + diff --git a/java/libraries/glw/.classpath b/java/libraries/glw/.classpath new file mode 100644 index 000000000..614f216d5 --- /dev/null +++ b/java/libraries/glw/.classpath @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/java/libraries/glw/.gitignore b/java/libraries/glw/.gitignore new file mode 100644 index 000000000..5e56e040e --- /dev/null +++ b/java/libraries/glw/.gitignore @@ -0,0 +1 @@ +/bin diff --git a/java/libraries/glw/.project b/java/libraries/glw/.project new file mode 100644 index 000000000..2a1ccb06f --- /dev/null +++ b/java/libraries/glw/.project @@ -0,0 +1,17 @@ + + + processing-glw + + + + + + org.eclipse.jdt.core.javabuilder + + + + + + org.eclipse.jdt.core.javanature + + diff --git a/java/libraries/glw/build.xml b/java/libraries/glw/build.xml new file mode 100755 index 000000000..0b161fe8f --- /dev/null +++ b/java/libraries/glw/build.xml @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/java/libraries/glw/examples/LargeStage/LargeStage.pde b/java/libraries/glw/examples/LargeStage/LargeStage.pde new file mode 100644 index 000000000..c40f9b0bc --- /dev/null +++ b/java/libraries/glw/examples/LargeStage/LargeStage.pde @@ -0,0 +1,25 @@ +import processing.glw.*; + +PGraphics stage; + +void setup() { + // The main window will be hidden, only GLW.RENDERER + // can be used in size() + size(100, 100, GLW.RENDERER); + + stage = createGraphics(2560, 1440, GLW.P2D); + GLW.createWindow(stage); + frameRate(180); +} + +void draw() { + // The draw() method is used to update the offscreen surfaces, + // but not to draw directly to the screen. + stage.beginDraw(); + stage.background(200); + stage.fill(255); + stage.ellipse(mouseX, mouseY, 50, 50); + stage.fill(0); + stage.text(frameRate, 100, 100); + stage.endDraw(); +} \ No newline at end of file diff --git a/java/libraries/glw/examples/MultipleWindows/MultipleWindows.pde b/java/libraries/glw/examples/MultipleWindows/MultipleWindows.pde new file mode 100644 index 000000000..902fdbcd4 --- /dev/null +++ b/java/libraries/glw/examples/MultipleWindows/MultipleWindows.pde @@ -0,0 +1,28 @@ +import processing.glw.*; + +PGraphics canvas1; +PGraphics canvas2; + +void setup() { + size(100, 100, GLW.RENDERER); + canvas1 = createGraphics(320, 240, GLW.P2D); + canvas2 = createGraphics(320, 240, GLW.P2D); + GLW.createWindow(canvas1); + GLW.createWindow(canvas2); +} + +void draw() { + canvas1.beginDraw(); + canvas1.background(200); + canvas1.ellipse(mouseX, mouseY, 100, 100); + canvas1.endDraw(); + + canvas2.beginDraw(); + canvas2.background(170); + canvas2.ellipse(mouseX, mouseY, 50, 50); + canvas2.endDraw(); +} + +void keyPressed() { + GLW.getFocusedWindow().setVisible(false); +} \ No newline at end of file diff --git a/java/libraries/glw/library/.gitignore b/java/libraries/glw/library/.gitignore new file mode 100644 index 000000000..f7094d12e --- /dev/null +++ b/java/libraries/glw/library/.gitignore @@ -0,0 +1 @@ +/glw.jar diff --git a/java/libraries/glw/src/processing/glw/GLW.java b/java/libraries/glw/src/processing/glw/GLW.java new file mode 100644 index 000000000..5968461e7 --- /dev/null +++ b/java/libraries/glw/src/processing/glw/GLW.java @@ -0,0 +1,52 @@ +package processing.glw; + +import processing.core.PGraphics; + +import com.jogamp.newt.opengl.GLWindow; + +import java.util.HashMap; + +public class GLW { + static public final String RENDERER = "processing.glw.PGraphicsGLW"; + static public final String OPENGL = "processing.glw.PGraphicsGLW"; + + static public final String P2D = "processing.glw.PGraphics2D"; + static public final String P3D = "processing.glw.PGraphics3D"; + + static protected HashMap windows = + new HashMap(); + + public GLW() { + } + + static public void createWindow(PGraphics pg) { + if (pg instanceof PGraphics2D || pg instanceof PGraphics3D) { + windows.put(pg, null); + } else { + throw new RuntimeException("Only GLW.P2D or GLW.P3D surfaces can be attached to a window"); + } + } + + static public GLWindow getWindow(PGraphics pg) { + return windows.get(pg); + } + + static public boolean isFocused(PGraphics pg) { + GLWindow win = windows.get(pg); + return win != null && win.hasFocus(); + } + + static public PGraphics getFocusedGraphics() { + for (PGraphics pg: windows.keySet()) { + if (isFocused(pg)) return pg; + } + return null; + } + + static public GLWindow getFocusedWindow() { + for (PGraphics pg: windows.keySet()) { + if (isFocused(pg)) return windows.get(pg); + } + return null; + } +} diff --git a/java/libraries/glw/src/processing/glw/PGraphics2D.java b/java/libraries/glw/src/processing/glw/PGraphics2D.java new file mode 100644 index 000000000..90b52899c --- /dev/null +++ b/java/libraries/glw/src/processing/glw/PGraphics2D.java @@ -0,0 +1,10 @@ +package processing.glw; + +import processing.opengl.PGL; +import processing.opengl.PGraphicsOpenGL; + +public class PGraphics2D extends processing.opengl.PGraphics2D { + protected PGL createPGL(PGraphicsOpenGL pg) { + return new PNEWT(pg); + } +} diff --git a/java/libraries/glw/src/processing/glw/PGraphics3D.java b/java/libraries/glw/src/processing/glw/PGraphics3D.java new file mode 100644 index 000000000..c4e011a98 --- /dev/null +++ b/java/libraries/glw/src/processing/glw/PGraphics3D.java @@ -0,0 +1,10 @@ +package processing.glw; + +import processing.opengl.PGL; +import processing.opengl.PGraphicsOpenGL; + +public class PGraphics3D extends processing.opengl.PGraphics3D { + protected PGL createPGL(PGraphicsOpenGL pg) { + return new PNEWT(pg); + } +} diff --git a/java/libraries/glw/src/processing/glw/PGraphicsGLW.java b/java/libraries/glw/src/processing/glw/PGraphicsGLW.java new file mode 100644 index 000000000..34c334fce --- /dev/null +++ b/java/libraries/glw/src/processing/glw/PGraphicsGLW.java @@ -0,0 +1,85 @@ +/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + Part of the Processing project - http://processing.org + + Copyright (c) 2004-12 Ben Fry and Casey Reas + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License version 2.1 as published by the Free Software Foundation. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General + Public License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + */ + +package processing.glw; + +import processing.opengl.PGL; +import processing.opengl.PGraphicsOpenGL; + +/** + * GLW renderer. It's only role is to drive the main animation loop by calling + * requestDraw() and so allowing the offscreen canvases to be drawn inside the + * draw() method of the sketch. Currently, it cannot be used to draw into. + * + */ +public class PGraphicsGLW extends PGraphicsOpenGL { + protected PGL createPGL(PGraphicsOpenGL pg) { + return new PNEWT(pg); + } + + public void beginDraw() { + if (primarySurface) { + setCurrentPG(this); + } else { + throw new RuntimeException("GLW renderer cannot be used as an offscreen surface"); + } + + report("top beginDraw()"); + + if (!checkGLThread()) { + return; + } + + if (drawing) { + return; + } + + if (!glParamsRead) { + getGLParameters(); + } + + drawing = true; + + report("bot beginDraw()"); + } + + public void endDraw() { + report("top endDraw()"); + + if (!drawing) { + return; + } + + if (primarySurface) { + setCurrentPG(null); + } else { + throw new RuntimeException("GLW renderer cannot be used as an offscreen surface."); + } + drawing = false; + + report("bot endDraw()"); + } + + protected void vertexImpl(float x, float y, float z, float u, float v) { + throw new RuntimeException("The main GLW renderer cannot be used to draw to."); + } +} diff --git a/java/libraries/glw/src/processing/glw/PNEWT.java b/java/libraries/glw/src/processing/glw/PNEWT.java new file mode 100644 index 000000000..08d3de581 --- /dev/null +++ b/java/libraries/glw/src/processing/glw/PNEWT.java @@ -0,0 +1,267 @@ +/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + Part of the Processing project - http://processing.org + + Copyright (c) 2011-12 Ben Fry and Casey Reas + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General + Public License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA +*/ + +package processing.glw; + + +import javax.media.opengl.GLAutoDrawable; +import javax.media.opengl.GLCapabilities; +import javax.media.opengl.GLDrawableFactory; +import javax.media.opengl.GLEventListener; +import javax.media.opengl.GLException; +import javax.media.opengl.GLProfile; + +import com.jogamp.newt.opengl.GLWindow; +import com.jogamp.newt.event.WindowAdapter; +import com.jogamp.newt.event.WindowEvent; + +import processing.core.PApplet; +import processing.core.PGraphics; +import processing.opengl.PGL; +import processing.opengl.PGraphicsOpenGL; +import processing.opengl.PJOGL; +import processing.opengl.Texture; + + +public class PNEWT extends PJOGL { + + static { + WINDOW_TOOLKIT = NEWT; + EVENTS_TOOLKIT = NEWT; + USE_FBOLAYER_BY_DEFAULT = false; + USE_JOGL_FBOLAYER = false; + } + + protected static GLCapabilities sharedCaps; + protected static GLAutoDrawable sharedDrawable; + + + public PNEWT(PGraphicsOpenGL pg) { + super(pg); + } + + + protected void initSurface(int antialias) { + if (!(pg instanceof PGraphicsGLW)) { + throw new RuntimeException("GLW.RENDERER is the only option in size() when using the GLW library."); + } + + if (profile == null) { + if (PROFILE == 2) { + try { + profile = GLProfile.getGL2ES1(); + } catch (GLException ex) { + profile = GLProfile.getMaxFixedFunc(true); + } + } else if (PROFILE == 3) { + try { + profile = GLProfile.getGL2GL3(); + } catch (GLException ex) { + profile = GLProfile.getMaxProgrammable(true); + } + if (!profile.isGL3()) { + PGraphics.showWarning("Requested profile GL3 but is not available, got: " + profile); + } + } else if (PROFILE == 4) { + try { + profile = GLProfile.getGL4ES3(); + } catch (GLException ex) { + profile = GLProfile.getMaxProgrammable(true); + } + if (!profile.isGL4()) { + PGraphics.showWarning("Requested profile GL4 but is not available, got: " + profile); + } + } else throw new RuntimeException(UNSUPPORTED_GLPROF_ERROR); + + if (2 < PROFILE) { + texVertShaderSource = convertVertexSource(texVertShaderSource, 120, 150); + tex2DFragShaderSource = convertFragmentSource(tex2DFragShaderSource, 120, 150); + texRectFragShaderSource = convertFragmentSource(texRectFragShaderSource, 120, 150); + } + } + + // Setting up the desired capabilities; + sharedCaps = new GLCapabilities(profile); + sharedCaps.setAlphaBits(REQUESTED_ALPHA_BITS); + sharedCaps.setDepthBits(REQUESTED_DEPTH_BITS); + sharedCaps.setStencilBits(REQUESTED_STENCIL_BITS); + + sharedCaps.setPBuffer(false); + sharedCaps.setFBO(false); + sharedCaps.setSampleBuffers(false); + + fboLayerRequested = false; + sharedDrawable = GLDrawableFactory.getFactory(profile).createDummyAutoDrawable(null, true, sharedCaps, null); + sharedDrawable.display(); // triggers GLContext object creation and native realization. + DummyListener listener = new DummyListener(); + sharedDrawable.addGLEventListener(listener); + + pg.parent.frame.setVisible(false); + } + + + protected boolean displayable() { + return false; + } + + + protected void beginDraw(boolean clear0) { + } + + + protected void endDraw(boolean clear0) { + } + + + protected void requestDraw() { + createWindows(); + + // Calling display() so the main draw() method is triggered, where the + // offscreen GLW canvases can be updated. + sharedDrawable.display(); + + displayWindows(); + } + + private void createWindows() { + for (PGraphics pg: GLW.windows.keySet()) { + GLWindow win = GLW.windows.get(pg); + if (win == null) { + win = GLWindow.create(sharedCaps); + win.setSharedAutoDrawable(sharedDrawable); + win.setSize(pg.width, pg.height); + win.setTitle("TEST"); + win.setVisible(true); + GLW.windows.put(pg, win); + + NEWTListener listener = new NEWTListener(pg); + win.addGLEventListener(listener); + + NEWTMouseListener mouseListener = new NEWTMouseListener(); + win.addMouseListener(mouseListener); + NEWTKeyListener keyListener = new NEWTKeyListener(); + win.addKeyListener(keyListener); + NEWTWindowListener winListener = new NEWTWindowListener(); + win.addWindowListener(winListener); + + win.addWindowListener(new WindowAdapter() { + @Override + public void windowDestroyNotify(final WindowEvent e) { + } + }); + } + } + } + + + private void displayWindows() { + int totalCount = 0; + int realizedCount = 0; + for (GLWindow win: GLW.windows.values()) { + if (win != null) { + totalCount++; + if (win.isRealized()) realizedCount++; + win.display(); + } + } + + if (0 < totalCount && realizedCount == 0) { + // All windows where closed, exit the application + sharedDrawable.destroy(); + System.exit(0); + } + } + + + protected class DummyListener implements GLEventListener { + public DummyListener() { + } + + @Override + public void display(GLAutoDrawable glDrawable) { + getGL(glDrawable); + pg.parent.handleDraw(); + } + + @Override + public void dispose(GLAutoDrawable adrawable) { + } + + @Override + public void init(GLAutoDrawable glDrawable) { + getGL(glDrawable); + + capabilities = glDrawable.getChosenGLCapabilities(); + if (!hasFBOs()) { + throw new RuntimeException(MISSING_FBO_ERROR); + } + if (!hasShaders()) { + throw new RuntimeException(MISSING_GLSL_ERROR); + } + if (USE_JOGL_FBOLAYER && capabilities.isFBO()) { + int maxs = maxSamples(); + numSamples = PApplet.min(capabilities.getNumSamples(), maxs); + } + } + + @Override + public void reshape(GLAutoDrawable glDrawable, int x, int y, int w, int h) { + } + } + + + protected class NEWTListener implements GLEventListener { + PGraphicsOpenGL pg; + PNEWT pgl; + + public NEWTListener(PGraphics pg) { + this.pg = (PGraphicsOpenGL)pg; + pgl = (PNEWT)this.pg.pgl; + } + + @Override + public void display(GLAutoDrawable glDrawable) { + pgl.getGL(glDrawable); + Texture tex = pg.getTexture(false); + if (tex != null) { + pgl.disable(PGL.BLEND); + pgl.drawTexture(tex.glTarget, tex.glName, + tex.glWidth, tex.glHeight, + 0, 0, pg.width, pg.height); + pgl.enable(PGL.BLEND); + } + } + + @Override + public void dispose(GLAutoDrawable adrawable) { + } + + @Override + public void init(GLAutoDrawable glDrawable) { + } + + @Override + public void reshape(GLAutoDrawable glDrawable, int x, int y, int w, int h) { + } + } +} diff --git a/java/libraries/lwjgl/build.xml b/java/libraries/lwjgl/build.xml old mode 100644 new mode 100755 index f9e186a30..280c0ca07 --- a/java/libraries/lwjgl/build.xml +++ b/java/libraries/lwjgl/build.xml @@ -18,7 +18,11 @@ srcdir="src" destdir="bin" encoding="UTF-8" includeAntRuntime="false" - classpath="../../../core/library/core.jar; library/lwjgl.jar; library/lwjgl_util.jar" /> + classpath="../../../core/library/core.jar; library/lwjgl.jar; library/lwjgl_util.jar" + nowarn="true" + compiler="org.eclipse.jdt.core.JDTCompilerAdapter"> + + diff --git a/java/libraries/lwjgl/src/processing/lwjgl/PGL.java b/java/libraries/lwjgl/src/processing/lwjgl/PGL.java deleted file mode 100644 index f42c1e754..000000000 --- a/java/libraries/lwjgl/src/processing/lwjgl/PGL.java +++ /dev/null @@ -1,3374 +0,0 @@ -/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */ - -/* - Part of the Processing project - http://processing.org - - Copyright (c) 2011-12 Ben Fry and Casey Reas - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library 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 - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General - Public License along with this library; if not, write to the - Free Software Foundation, Inc., 59 Temple Place, Suite 330, - Boston, MA 02111-1307 USA -*/ - -package processing.lwjgl; - -import java.awt.BorderLayout; -import java.awt.Canvas; -import java.awt.Color; -import java.nio.Buffer; - -import java.nio.ByteBuffer; -import java.nio.ByteOrder; -import java.nio.FloatBuffer; -import java.nio.IntBuffer; -import java.nio.ShortBuffer; -import java.util.Arrays; - -import org.lwjgl.BufferUtils; -import org.lwjgl.LWJGLException; -import org.lwjgl.input.Keyboard; -import org.lwjgl.input.Mouse; -import org.lwjgl.opengl.Display; -import org.lwjgl.opengl.DisplayMode; -import org.lwjgl.opengl.EXTFramebufferObject; -import org.lwjgl.opengl.EXTTextureFilterAnisotropic; -import org.lwjgl.opengl.GL11; -import org.lwjgl.opengl.GL12; -import org.lwjgl.opengl.GL13; -import org.lwjgl.opengl.GL14; -import org.lwjgl.opengl.GL15; -import org.lwjgl.opengl.GL20; -import org.lwjgl.opengl.GL30; -import org.lwjgl.opengl.GL31; -import org.lwjgl.util.glu.GLU; -import org.lwjgl.util.glu.GLUtessellator; -import org.lwjgl.util.glu.GLUtessellatorCallbackAdapter; -import org.lwjgl.opengl.PixelFormat; - -import processing.core.PApplet; -import processing.core.PConstants; -import processing.event.Event; -import processing.event.KeyEvent; -import processing.event.MouseEvent; -import processing.opengl.PGraphicsOpenGL; -import processing.opengl.Texture; - -/** - * Processing-OpenGL abstraction layer. - * - * Warnings are suppressed for static access because presumably on Android, - * the GL2 vs GL distinctions are necessary, whereas on desktop they are not. - * - * This version of PGL uses LWJGL, see some issues with it: - * http://lwjgl.org/forum/index.php/topic,4711.0.html - * http://www.java-gaming.org/topics/cannot-add-mouselistener-to-java-awt-canvas-with-lwjgl-on-windows/24650/view.html - * - */ -@SuppressWarnings("static-access") -public class PGL extends processing.opengl.PGL { - /////////////////////////////////////////////////////////// - - // Public members to access the underlying GL objects and context - - /** GLU interface **/ - public static GLU glu; - - /** The canvas where OpenGL rendering takes place */ - public static Canvas canvas; - - /////////////////////////////////////////////////////////// - - // Parameters - - protected static boolean FORCE_SCREEN_FBO = false; - protected static final boolean USE_DIRECT_BUFFERS = true; - protected static final int MIN_DIRECT_BUFFER_SIZE = 16; - protected static final boolean SAVE_SURFACE_TO_PIXELS = true; - - /** Enables/disables mipmap use. **/ - protected static final boolean MIPMAPS_ENABLED = true; - - /** Initial sizes for arrays of input and tessellated data. */ - protected static final int DEFAULT_IN_VERTICES = 64; - protected static final int DEFAULT_IN_EDGES = 128; - protected static final int DEFAULT_IN_TEXTURES = 64; - protected static final int DEFAULT_TESS_VERTICES = 64; - protected static final int DEFAULT_TESS_INDICES = 128; - - /** Maximum lights by default is 8, the minimum defined by OpenGL. */ - protected static final int MAX_LIGHTS = 8; - - /** Maximum index value of a tessellated vertex. GLES restricts the vertex - * indices to be of type unsigned short. Since Java only supports signed - * shorts as primitive type we have 2^15 = 32768 as the maximum number of - * vertices that can be referred to within a single VBO. */ - protected static final int MAX_VERTEX_INDEX = 32767; - protected static final int MAX_VERTEX_INDEX1 = MAX_VERTEX_INDEX + 1; - - /** Count of tessellated fill, line or point vertices that will - * trigger a flush in the immediate mode. It doesn't necessarily - * be equal to MAX_VERTEX_INDEX1, since the number of vertices can - * be effectively much large since the renderer uses offsets to - * refer to vertices beyond the MAX_VERTEX_INDEX limit. - */ - protected static final int FLUSH_VERTEX_COUNT = MAX_VERTEX_INDEX1; - - /** Minimum/maximum dimensions of a texture used to hold font data. **/ - protected static final int MIN_FONT_TEX_SIZE = 256; - protected static final int MAX_FONT_TEX_SIZE = 1024; - - /** Minimum stroke weight needed to apply the full path stroking - * algorithm that properly generates caps and joins. - */ - protected static final float MIN_CAPS_JOINS_WEIGHT = 2f; - - /** Maximum length of linear paths to be stroked with the - * full algorithm that generates accurate caps and joins. - */ - protected static final int MAX_CAPS_JOINS_LENGTH = 5000; - - /** Minimum array size to use arrayCopy method(). **/ - protected static final int MIN_ARRAYCOPY_SIZE = 2; - - /** Factor used to displace the stroke vertices towards the camera in - * order to make sure the lines are always on top of the fill geometry **/ - protected static final float STROKE_DISPLACEMENT = 0.999f; - - protected static int request_depth_bits = 24; - protected static int request_stencil_bits = 8; - protected static int request_alpha_bits = 8; - - protected static final int SIZEOF_SHORT = Short.SIZE / 8; - protected static final int SIZEOF_INT = Integer.SIZE / 8; - protected static final int SIZEOF_FLOAT = Float.SIZE / 8; - protected static final int SIZEOF_BYTE = Byte.SIZE / 8; - protected static final int SIZEOF_INDEX = SIZEOF_SHORT; - protected static final int INDEX_TYPE = GL11.GL_UNSIGNED_SHORT; - - /** Machine Epsilon for float precision. **/ - protected static float FLOAT_EPS = Float.MIN_VALUE; - // Calculation of the Machine Epsilon for float precision. From: - // http://en.wikipedia.org/wiki/Machine_epsilon#Approximation_using_Java - static { - float eps = 1.0f; - - do { - eps /= 2.0f; - } while ((float)(1.0 + (eps / 2.0)) != 1.0); - - FLOAT_EPS = eps; - } - - /** - * Set to true if the host system is big endian (PowerPC, MIPS, SPARC), false - * if little endian (x86 Intel for Mac or PC). - */ - protected static boolean BIG_ENDIAN = - ByteOrder.nativeOrder() == ByteOrder.BIG_ENDIAN; - - protected static final String SHADER_PREPROCESSOR_DIRECTIVE = - "#ifdef GL_ES\n" + - "precision mediump float;\n" + - "precision mediump int;\n" + - "#endif\n"; - - /** OpenGL thread */ - protected static Thread glThread; - - /** Just holds a unique ID */ - protected static int context; - - /** The PGraphics object using this interface */ - protected PGraphicsOpenGL pg; - - /** Poller threads to get the keyboard/mouse events from LWJGL */ - protected static KeyPoller keyPoller; - protected static MousePoller mousePoller; - - /** Desired target framerate */ - protected float targetFps = 60; - protected float currentFps = 60; - protected boolean setFps = false; - protected int fcount, lastm; - protected int fint = 3; - - /** Which texturing targets are enabled */ - protected static boolean[] texturingTargets = { false, false }; - - /** Which textures are bound to each target */ - protected static int[] boundTextures = { 0, 0 }; - - /////////////////////////////////////////////////////////// - - // FBO layer - - protected static boolean fboLayerByDefault = FORCE_SCREEN_FBO; - protected static boolean fboLayerCreated = false; - protected static boolean fboLayerInUse = false; - protected static boolean firstFrame = true; - protected static int reqNumSamples; - protected static int numSamples; - protected static IntBuffer glColorFbo; - protected static IntBuffer glMultiFbo; - protected static IntBuffer glColorBuf; - protected static IntBuffer glColorTex; - protected static IntBuffer glDepthStencil; - protected static IntBuffer glDepth; - protected static IntBuffer glStencil; - protected static int fboWidth, fboHeight; - protected static int backTex, frontTex; - - protected static boolean needToClearBuffers; - - /////////////////////////////////////////////////////////// - - // Texture rendering - - protected static boolean loadedTex2DShader = false; - protected static int tex2DShaderProgram; - protected static int tex2DVertShader; - protected static int tex2DFragShader; - protected static int tex2DShaderContext; - protected static int tex2DVertLoc; - protected static int tex2DTCoordLoc; - - protected static boolean loadedTexRectShader = false; - protected static int texRectShaderProgram; - protected static int texRectVertShader; - protected static int texRectFragShader; - protected static int texRectShaderContext; - protected static int texRectVertLoc; - protected static int texRectTCoordLoc; - - protected static float[] texCoords = { - // X, Y, U, V - -1.0f, -1.0f, 0.0f, 0.0f, - +1.0f, -1.0f, 1.0f, 0.0f, - -1.0f, +1.0f, 0.0f, 1.0f, - +1.0f, +1.0f, 1.0f, 1.0f - }; - protected static FloatBuffer texData; - - protected static String texVertShaderSource = - "attribute vec2 inVertex;" + - "attribute vec2 inTexcoord;" + - "varying vec2 vertTexcoord;" + - "void main() {" + - " gl_Position = vec4(inVertex, 0, 1);" + - " vertTexcoord = inTexcoord;" + - "}"; - - protected static String tex2DFragShaderSource = - SHADER_PREPROCESSOR_DIRECTIVE + - "uniform sampler2D textureSampler;" + - "varying vec2 vertTexcoord;" + - "void main() {" + - " gl_FragColor = texture2D(textureSampler, vertTexcoord.st);" + - "}"; - - protected static String texRectFragShaderSource = - SHADER_PREPROCESSOR_DIRECTIVE + - "uniform sampler2DRect textureSampler;" + - "varying vec2 vertTexcoord;" + - "void main() {" + - " gl_FragColor = texture2DRect(textureSampler, vertTexcoord.st);" + - "}"; - - /////////////////////////////////////////////////////////// - - // Utilities - - protected ByteBuffer byteBuffer; - protected IntBuffer intBuffer; - - protected IntBuffer colorBuffer; - protected FloatBuffer depthBuffer; - protected ByteBuffer stencilBuffer; - - - /////////////////////////////////////////////////////////// - - // Error messages - - protected static final String MISSING_FBO_ERROR = - "Framebuffer objects are not supported by this hardware (or driver)"; - - protected static final String MISSING_GLSL_ERROR = - "GLSL shaders are not supported by this hardware (or driver)"; - - protected static final String MISSING_GLFUNC_ERROR = - "GL function %1$s is not available on this hardware (or driver)"; - - - /////////////////////////////////////////////////////////// - - // Initialization, finalization - - - public PGL(PGraphicsOpenGL pg) { - this.pg = pg; - if (glu == null) { - glu = new GLU(); - } - if (glColorTex == null) { - glColorTex = allocateIntBuffer(2); - glColorFbo = allocateIntBuffer(1); - glMultiFbo = allocateIntBuffer(1); - glColorBuf = allocateIntBuffer(1); - glDepthStencil = allocateIntBuffer(1); - glDepth = allocateIntBuffer(1); - glStencil = allocateIntBuffer(1); - - fboLayerCreated = false; - fboLayerInUse = false; - firstFrame = false; - needToClearBuffers = false; - } - - byteBuffer = allocateByteBuffer(1); - intBuffer = allocateIntBuffer(1); - } - - - protected void setFps(float fps) { - if (!setFps || targetFps != fps) { - if (60 < fps) { - // Disables v-sync - Display.setVSyncEnabled(false); - Display.sync((int)fps); - } else { - Display.setVSyncEnabled(true); - } - targetFps = currentFps = fps; - setFps = true; - } - } - - - protected void initSurface(int antialias) { - if (canvas != null) { - keyPoller.requestStop(); - mousePoller.requestStop(); - - try { - Display.setParent(null); - } catch (LWJGLException e) { - e.printStackTrace(); - } - Display.destroy(); - - pg.parent.remove(canvas); - } - - canvas = new Canvas(); - canvas.setFocusable(true); - canvas.requestFocus(); - canvas.setBackground(new Color(pg.backgroundColor, true)); - canvas.setBounds(0, 0, pg.parent.width, pg.parent.height); - - pg.parent.setLayout(new BorderLayout()); - pg.parent.add(canvas, BorderLayout.CENTER); - - try { - DisplayMode[] modes = Display.getAvailableDisplayModes(); - int bpp = 0; - for (int i = 0; i < modes.length; i++) { - bpp = PApplet.max(modes[i].getBitsPerPixel(), bpp); - } - PixelFormat format = new PixelFormat(bpp, request_alpha_bits, - request_depth_bits, - request_stencil_bits, 1); - Display.setDisplayMode(new DisplayMode(pg.parent.width, pg.parent.height)); - int argb = pg.backgroundColor; - float r = ((argb >> 16) & 0xff) / 255.0f; - float g = ((argb >> 8) & 0xff) / 255.0f; - float b = ((argb) & 0xff) / 255.0f; - Display.setInitialBackground(r, g, b); - Display.setParent(canvas); - Display.create(format); - - // Might be useful later to specify the context attributes. - // http://lwjgl.org/javadoc/org/lwjgl/opengl/ContextAttribs.html -// ContextAttribs contextAtrributes = new ContextAttribs(4, 0); -// contextAtrributes.withForwardCompatible(true); -// contextAtrributes.withProfileCore(true); -// Display.create(pixelFormat, contextAtrributes); - } catch (LWJGLException e) { - e.printStackTrace(); - } - - context = Display.getDrawable().hashCode(); - - keyPoller = new KeyPoller(pg.parent); - keyPoller.start(); - - mousePoller = new MousePoller(pg.parent); - mousePoller.start(); - - reqNumSamples = qualityToSamples(antialias); - fboLayerCreated = false; - fboLayerInUse = false; - firstFrame = true; - needToClearBuffers = true; - - setFps = false; - } - - - protected void deleteSurface() { - if (glColorTex != null) { - deleteTextures(2, glColorTex); - deleteFramebuffers(1, glColorFbo); - deleteFramebuffers(1, glMultiFbo); - deleteRenderbuffers(1, glColorBuf); - deleteRenderbuffers(1, glDepthStencil); - deleteRenderbuffers(1, glDepth); - deleteRenderbuffers(1, glStencil); - } - fboLayerCreated = false; - fboLayerInUse = false; - firstFrame = false; - needToClearBuffers = false; - } - - - protected void update() { - if (!setFps) setFps(targetFps); - - if (!fboLayerCreated) { - if (!hasFBOs()) { - throw new RuntimeException("Framebuffer objects are not supported by this hardware (or driver)"); - } - if (!hasShaders()) { - throw new RuntimeException("GLSL shaders are not supported by this hardware (or driver)"); - } - - String ext = getString(EXTENSIONS); - if (-1 < ext.indexOf("texture_non_power_of_two")) { - fboWidth = pg.width; - fboHeight = pg.height; - } else { - fboWidth = nextPowerOfTwo(pg.width); - fboHeight = nextPowerOfTwo(pg.height); - } - - if (-1 < ext.indexOf("_framebuffer_multisample")) { - numSamples = reqNumSamples; - } else { - numSamples = 1; - } - boolean multisample = 1 < numSamples; - - boolean packed = ext.indexOf("packed_depth_stencil") != -1; - int depthBits = getDepthBits(); - int stencilBits = getStencilBits(); - - genTextures(2, glColorTex); - for (int i = 0; i < 2; i++) { - bindTexture(TEXTURE_2D, glColorTex.get(i)); - texParameteri(TEXTURE_2D, TEXTURE_MIN_FILTER, NEAREST); - texParameteri(TEXTURE_2D, TEXTURE_MAG_FILTER, NEAREST); - texParameteri(TEXTURE_2D, TEXTURE_WRAP_S, CLAMP_TO_EDGE); - texParameteri(TEXTURE_2D, TEXTURE_WRAP_T, CLAMP_TO_EDGE); - texImage2D(TEXTURE_2D, 0, RGBA, fboWidth, fboHeight, 0, - RGBA, UNSIGNED_BYTE, null); - initTexture(TEXTURE_2D, RGBA, fboWidth, fboHeight, pg.backgroundColor); - } - bindTexture(TEXTURE_2D, 0); - - backTex = 0; - frontTex = 1; - - genFramebuffers(1, glColorFbo); - bindFramebuffer(FRAMEBUFFER, glColorFbo.get(0)); - framebufferTexture2D(FRAMEBUFFER, COLOR_ATTACHMENT0, TEXTURE_2D, - glColorTex.get(backTex), 0); - - if (multisample) { - // Creating multisampled FBO - genFramebuffers(1, glMultiFbo); - bindFramebuffer(FRAMEBUFFER, glMultiFbo.get(0)); - - // color render buffer... - genRenderbuffers(1, glColorBuf); - bindRenderbuffer(RENDERBUFFER, glColorBuf.get(0)); - renderbufferStorageMultisample(RENDERBUFFER, numSamples, - RGBA8, fboWidth, fboHeight); - framebufferRenderbuffer(FRAMEBUFFER, COLOR_ATTACHMENT0, - RENDERBUFFER, glColorBuf.get(0)); - } - - // Creating depth and stencil buffers - if (packed && depthBits == 24 && stencilBits == 8) { - // packed depth+stencil buffer - genRenderbuffers(1, glDepthStencil); - bindRenderbuffer(RENDERBUFFER, glDepthStencil.get(0)); - if (multisample) { - renderbufferStorageMultisample(RENDERBUFFER, numSamples, - DEPTH24_STENCIL8, fboWidth, fboHeight); - } else { - renderbufferStorage(RENDERBUFFER, DEPTH24_STENCIL8, - fboWidth, fboHeight); - } - framebufferRenderbuffer(FRAMEBUFFER, DEPTH_ATTACHMENT, RENDERBUFFER, - glDepthStencil.get(0)); - framebufferRenderbuffer(FRAMEBUFFER, STENCIL_ATTACHMENT, RENDERBUFFER, - glDepthStencil.get(0)); - } else { - // separate depth and stencil buffers - if (0 < depthBits) { - int depthComponent = DEPTH_COMPONENT16; - if (depthBits == 32) { - depthComponent = DEPTH_COMPONENT32; - } else if (depthBits == 24) { - depthComponent = DEPTH_COMPONENT24; - } else if (depthBits == 16) { - depthComponent = DEPTH_COMPONENT16; - } - - genRenderbuffers(1, glDepth); - bindRenderbuffer(RENDERBUFFER, glDepth.get(0)); - if (multisample) { - renderbufferStorageMultisample(RENDERBUFFER, numSamples, - depthComponent, fboWidth, fboHeight); - } else { - renderbufferStorage(RENDERBUFFER, depthComponent, - fboWidth, fboHeight); - } - framebufferRenderbuffer(FRAMEBUFFER, DEPTH_ATTACHMENT, - RENDERBUFFER, glDepth.get(0)); - } - - if (0 < stencilBits) { - int stencilIndex = STENCIL_INDEX1; - if (stencilBits == 8) { - stencilIndex = STENCIL_INDEX8; - } else if (stencilBits == 4) { - stencilIndex = STENCIL_INDEX4; - } else if (stencilBits == 1) { - stencilIndex = STENCIL_INDEX1; - } - - genRenderbuffers(1, glStencil); - bindRenderbuffer(RENDERBUFFER, glStencil.get(0)); - if (multisample) { - renderbufferStorageMultisample(RENDERBUFFER, numSamples, - stencilIndex, fboWidth, fboHeight); - } else { - renderbufferStorage(RENDERBUFFER, stencilIndex, - fboWidth, fboHeight); - } - framebufferRenderbuffer(FRAMEBUFFER, STENCIL_ATTACHMENT, - RENDERBUFFER, glStencil.get(0)); - } - } - - validateFramebuffer(); - - // Clear all buffers. - clearDepth(1); - clearStencil(0); - int argb = pg.backgroundColor; - float a = ((argb >> 24) & 0xff) / 255.0f; - float r = ((argb >> 16) & 0xff) / 255.0f; - float g = ((argb >> 8) & 0xff) / 255.0f; - float b = ((argb) & 0xff) / 255.0f; - clearColor(r, g, b, a); - clear(DEPTH_BUFFER_BIT | STENCIL_BUFFER_BIT | COLOR_BUFFER_BIT); - - bindFramebuffer(FRAMEBUFFER, 0); - - fboLayerCreated = true; - } - } - - - protected int getReadFramebuffer() { - if (fboLayerInUse) { - return glColorFbo.get(0); - } else { - return 0; - } - } - - - protected int getDrawFramebuffer() { - if (fboLayerInUse) { - if (1 < numSamples) { - return glMultiFbo.get(0); - } else { - return glColorFbo.get(0); - } - } else { - return 0; - } - } - - - protected int getDefaultDrawBuffer() { - if (fboLayerInUse) { - return COLOR_ATTACHMENT0; - } else { - return BACK; - } - } - - - protected int getDefaultReadBuffer() { - if (fboLayerInUse) { - return COLOR_ATTACHMENT0; - } else { - return FRONT; - } - } - - - protected boolean isFBOBacked() { - return fboLayerInUse; - } - - - protected void requestFBOLayer() { - FORCE_SCREEN_FBO = true; - } - - - protected boolean isMultisampled() { - return 1 < numSamples; - } - - - protected int getDepthBits() { - intBuffer.rewind(); - getIntegerv(DEPTH_BITS, intBuffer); - return intBuffer.get(0); - } - - - protected int getStencilBits() { - intBuffer.rewind(); - getIntegerv(STENCIL_BITS, intBuffer); - return intBuffer.get(0); - } - - - protected boolean getDepthTest() { - intBuffer.rewind(); - getBooleanv(DEPTH_TEST, intBuffer); - return intBuffer.get(0) == 0 ? false : true; - } - - - protected boolean getDepthWriteMask() { - intBuffer.rewind(); - getBooleanv(DEPTH_WRITEMASK, intBuffer); - return intBuffer.get(0) == 0 ? false : true; - } - - - protected Texture wrapBackTexture() { - Texture tex = new Texture(); - tex.init(pg.width, pg.height, - glColorTex.get(backTex), TEXTURE_2D, RGBA, - fboWidth, fboHeight, NEAREST, NEAREST, - CLAMP_TO_EDGE, CLAMP_TO_EDGE); - tex.invertedY(true); - tex.colorBuffer(true); - pg.setCache(pg, tex); - return tex; - } - - - protected Texture wrapFrontTexture() { - Texture tex = new Texture(); - tex.init(pg.width, pg.height, - glColorTex.get(frontTex), TEXTURE_2D, RGBA, - fboWidth, fboHeight, NEAREST, NEAREST, - CLAMP_TO_EDGE, CLAMP_TO_EDGE); - tex.invertedY(true); - tex.colorBuffer(true); - return tex; - } - - - protected int getBackTextureName() { - return glColorTex.get(backTex); - } - - - protected int getFrontTextureName() { - return glColorTex.get(frontTex); - } - - - protected void bindFrontTexture() { - if (!texturingIsEnabled(TEXTURE_2D)) { - enableTexturing(TEXTURE_2D); - } - bindTexture(TEXTURE_2D, glColorTex.get(frontTex)); - } - - - protected void unbindFrontTexture() { - if (textureIsBound(TEXTURE_2D, glColorTex.get(frontTex))) { - // We don't want to unbind another texture - // that might be bound instead of this one. - if (!texturingIsEnabled(TEXTURE_2D)) { - enableTexturing(TEXTURE_2D); - bindTexture(TEXTURE_2D, 0); - disableTexturing(TEXTURE_2D); - } else { - bindTexture(TEXTURE_2D, 0); - } - } - } - - - protected void syncBackTexture() { - if (1 < numSamples) { - bindFramebuffer(READ_FRAMEBUFFER, glMultiFbo.get(0)); - bindFramebuffer(DRAW_FRAMEBUFFER, glColorFbo.get(0)); - blitFramebuffer(0, 0, fboWidth, fboHeight, - 0, 0, fboWidth, fboHeight, - COLOR_BUFFER_BIT, NEAREST); - } - } - - - protected int qualityToSamples(int quality) { - if (quality <= 1) { - return 1; - } else { - // Number of samples is always an even number: - int n = 2 * (quality / 2); - return n; - } - } - - - /////////////////////////////////////////////////////////// - - // Frame rendering - - - protected void beginDraw(boolean clear0) { - if (needFBOLayer(clear0)) { - bindFramebuffer(FRAMEBUFFER, glColorFbo.get(0)); - framebufferTexture2D(FRAMEBUFFER, COLOR_ATTACHMENT0, - TEXTURE_2D, glColorTex.get(backTex), 0); - - if (1 < numSamples) { - bindFramebuffer(FRAMEBUFFER, glMultiFbo.get(0)); - } - - if (firstFrame) { - // No need to draw back color buffer because we are in the first frame. - int argb = pg.backgroundColor; - float a = ((argb >> 24) & 0xff) / 255.0f; - float r = ((argb >> 16) & 0xff) / 255.0f; - float g = ((argb >> 8) & 0xff) / 255.0f; - float b = ((argb) & 0xff) / 255.0f; - clearColor(r, g, b, a); - clear(COLOR_BUFFER_BIT); - } else if (!clear0) { - // Render previous back texture (now is the front) as background, - // because no background() is being used ("incremental drawing") - drawTexture(TEXTURE_2D, glColorTex.get(frontTex), - fboWidth, fboHeight, 0, 0, pg.width, pg.height, - 0, 0, pg.width, pg.height); - } - - fboLayerInUse = true; - } else { - fboLayerInUse = false; - } - - if (firstFrame) { - firstFrame = false; - } - - if (!fboLayerByDefault) { - // The result of this assignment is the following: if the user requested - // at some point the use of the FBO layer, but subsequently didn't do - // request it again, then the rendering won't use the FBO layer if not - // needed, since it is slower than simple onscreen rendering. - FORCE_SCREEN_FBO = false; - } - } - - - protected void endDraw(boolean clear0) { - if (fboLayerInUse) { - syncBackTexture(); - - // Draw the contents of the back texture to the screen framebuffer. - bindFramebuffer(FRAMEBUFFER, 0); - - clearDepth(1); - clearColor(0, 0, 0, 0); - clear(COLOR_BUFFER_BIT | DEPTH_BUFFER_BIT); - - // Render current back texture to screen, without blending. - disable(BLEND); - drawTexture(TEXTURE_2D, glColorTex.get(backTex), - fboWidth, fboHeight, 0, 0, pg.width, pg.height, - 0, 0, pg.width, pg.height); - - // Swapping front and back textures. - int temp = frontTex; - frontTex = backTex; - backTex = temp; - } - - // call (gl)finish() only if the rendering of each frame is taking too long, - // to make sure that commands are not accumulating in the GL command queue. - fcount += 1; - int m = pg.parent.millis(); - if (m - lastm > 1000 * fint) { - currentFps = (float)(fcount) / fint; - fcount = 0; - lastm = m; - } - if (currentFps < 0.5f * targetFps) { - finish(); - } - } - - - protected boolean canDraw() { - return pg.initialized && pg.parent.isDisplayable(); - } - - - protected void requestDraw() { - if (pg.initialized) { - glThread = Thread.currentThread(); - pg.parent.handleDraw(); - Display.update(); - } - } - - - protected boolean threadIsCurrent() { - return Thread.currentThread() == glThread; - } - - - protected boolean needFBOLayer(boolean clear0) { - boolean cond = !clear0 || FORCE_SCREEN_FBO || 1 < numSamples; - return cond && glColorFbo.get(0) != 0; - } - - - /////////////////////////////////////////////////////////// - - // Context interface - - - protected int createEmptyContext() { - return -1; - } - - - protected int getCurrentContext() { - return context; - } - - - /////////////////////////////////////////////////////////// - - // Tessellator interface - - - protected Tessellator createTessellator(TessellatorCallback callback) { - return new Tessellator(callback); - } - - - protected class Tessellator { - protected GLUtessellator tess; - protected TessellatorCallback callback; - protected GLUCallback gluCallback; - - public Tessellator(TessellatorCallback callback) { - this.callback = callback; - tess = GLU.gluNewTess(); - gluCallback = new GLUCallback(); - - tess.gluTessCallback(GLU.GLU_TESS_BEGIN, gluCallback); - tess.gluTessCallback(GLU.GLU_TESS_END, gluCallback); - tess.gluTessCallback(GLU.GLU_TESS_VERTEX, gluCallback); - tess.gluTessCallback(GLU.GLU_TESS_COMBINE, gluCallback); - tess.gluTessCallback(GLU.GLU_TESS_ERROR, gluCallback); - } - - public void beginPolygon() { - tess.gluTessBeginPolygon(null); - } - - public void endPolygon() { - tess.gluTessEndPolygon(); - } - - public void setWindingRule(int rule) { - tess.gluTessProperty(GLU.GLU_TESS_WINDING_RULE, rule); - } - - public void beginContour() { - tess.gluTessBeginContour(); - } - - public void endContour() { - tess.gluTessEndContour(); - } - - public void addVertex(double[] v) { - tess.gluTessVertex(v, 0, v); - } - - protected class GLUCallback extends GLUtessellatorCallbackAdapter { - @Override - public void begin(int type) { - callback.begin(type); - } - - @Override - public void end() { - callback.end(); - } - - @Override - public void vertex(Object data) { - callback.vertex(data); - } - - @Override - public void combine(double[] coords, Object[] data, - float[] weight, Object[] outData) { - callback.combine(coords, data, weight, outData); - } - - @Override - public void error(int errnum) { - callback.error(errnum); - } - } - } - - - protected String tessError(int err) { - return glu.gluErrorString(err); - } - - protected interface TessellatorCallback { - public void begin(int type); - public void end(); - public void vertex(Object data); - public void combine(double[] coords, Object[] data, - float[] weight, Object[] outData); - public void error(int errnum); - } - - - /////////////////////////////////////////////////////////// - - // Utility functions - - - protected boolean contextIsCurrent(int other) { - return other == -1 || other == context; - } - - - protected void enableTexturing(int target) { - enable(target); - if (target == TEXTURE_2D) { - texturingTargets[0] = true; - } else if (target == TEXTURE_RECTANGLE) { - texturingTargets[1] = true; - } - } - - - protected void disableTexturing(int target) { - disable(target); - if (target == TEXTURE_2D) { - texturingTargets[0] = false; - } else if (target == TEXTURE_RECTANGLE) { - texturingTargets[1] = false; - } - } - - - protected boolean texturingIsEnabled(int target) { - if (target == TEXTURE_2D) { - return texturingTargets[0]; - } else if (target == TEXTURE_RECTANGLE) { - return texturingTargets[1]; - } else { - return false; - } - } - - - protected boolean textureIsBound(int target, int id) { - if (target == TEXTURE_2D) { - return boundTextures[0] == id; - } else if (target == TEXTURE_RECTANGLE) { - return boundTextures[1] == id; - } else { - return false; - } - } - - - protected void initTexture(int target, int format, int width, int height) { - initTexture(target, format, width, height, 0); - } - - - protected void initTexture(int target, int format, int width, int height, - int initColor) { - int[] glcolor = new int[16 * 16]; - Arrays.fill(glcolor, javaToNativeARGB(initColor)); - IntBuffer texels = PGL.allocateDirectIntBuffer(16 * 16); - texels.put(glcolor); - texels.rewind(); - for (int y = 0; y < height; y += 16) { - int h = PApplet.min(16, height - y); - for (int x = 0; x < width; x += 16) { - int w = PApplet.min(16, width - x); - texSubImage2D(target, 0, x, y, w, h, format, UNSIGNED_BYTE, texels); - } - } - } - - - protected void copyToTexture(int target, int format, int id, int x, int y, - int w, int h, IntBuffer buffer) { - activeTexture(TEXTURE0); - boolean enabledTex = false; - if (!texturingIsEnabled(target)) { - enableTexturing(target); - enabledTex = true; - } - bindTexture(target, id); - texSubImage2D(target, 0, x, y, w, h, format, UNSIGNED_BYTE, buffer); - bindTexture(target, 0); - if (enabledTex) { - disableTexturing(target); - } - } - - - public void drawTexture(int target, int id, int width, int height, - int X0, int Y0, int X1, int Y1) { - drawTexture(target, id, width, height, X0, Y0, X1, Y1, X0, Y0, X1, Y1); - } - - - public void drawTexture(int target, int id, int width, int height, - int texX0, int texY0, int texX1, int texY1, - int scrX0, int scrY0, int scrX1, int scrY1) { - if (target == TEXTURE_2D) { - drawTexture2D(id, width, height, - texX0, texY0, texX1, texY1, - scrX0, scrY0, scrX1, scrY1); - } else if (target == TEXTURE_RECTANGLE) { - drawTextureRect(id, width, height, - texX0, texY0, texX1, texY1, - scrX0, scrY0, scrX1, scrY1); - } - } - - - protected void drawTexture2D(int id, int width, int height, - int texX0, int texY0, int texX1, int texY1, - int scrX0, int scrY0, int scrX1, int scrY1) { - if (!loadedTex2DShader || tex2DShaderContext != context) { - tex2DVertShader = createShader(VERTEX_SHADER, texVertShaderSource); - tex2DFragShader = createShader(FRAGMENT_SHADER, tex2DFragShaderSource); - if (0 < tex2DVertShader && 0 < tex2DFragShader) { - tex2DShaderProgram = createProgram(tex2DVertShader, tex2DFragShader); - } - if (0 < tex2DShaderProgram) { - tex2DVertLoc = getAttribLocation(tex2DShaderProgram, "inVertex"); - tex2DTCoordLoc = getAttribLocation(tex2DShaderProgram, "inTexcoord"); - } - loadedTex2DShader = true; - tex2DShaderContext = context; - } - - if (texData == null) { - texData = allocateDirectFloatBuffer(texCoords.length); - } - - if (0 < tex2DShaderProgram) { - // The texture overwrites anything drawn earlier. - boolean depthTest = getDepthTest(); - disable(DEPTH_TEST); - - // When drawing the texture we don't write to the - // depth mask, so the texture remains in the background - // and can be occluded by anything drawn later, even if - // if it is behind it. - boolean depthMask = getDepthWriteMask(); - depthMask(false); - - useProgram(tex2DShaderProgram); - - enableVertexAttribArray(tex2DVertLoc); - enableVertexAttribArray(tex2DTCoordLoc); - - // Vertex coordinates of the textured quad are specified - // in normalized screen space (-1, 1): - // Corner 1 - texCoords[ 0] = 2 * (float)scrX0 / pg.width - 1; - texCoords[ 1] = 2 * (float)scrY0 / pg.height - 1; - texCoords[ 2] = (float)texX0 / width; - texCoords[ 3] = (float)texY0 / height; - // Corner 2 - texCoords[ 4] = 2 * (float)scrX1 / pg.width - 1; - texCoords[ 5] = 2 * (float)scrY0 / pg.height - 1; - texCoords[ 6] = (float)texX1 / width; - texCoords[ 7] = (float)texY0 / height; - // Corner 3 - texCoords[ 8] = 2 * (float)scrX0 / pg.width - 1; - texCoords[ 9] = 2 * (float)scrY1 / pg.height - 1; - texCoords[10] = (float)texX0 / width; - texCoords[11] = (float)texY1 / height; - // Corner 4 - texCoords[12] = 2 * (float)scrX1 / pg.width - 1; - texCoords[13] = 2 * (float)scrY1 / pg.height - 1; - texCoords[14] = (float)texX1 / width; - texCoords[15] = (float)texY1 / height; - - texData.rewind(); - texData.put(texCoords); - - activeTexture(TEXTURE0); - boolean enabledTex = false; - if (!texturingIsEnabled(TEXTURE_2D)) { - enableTexturing(TEXTURE_2D); - enabledTex = true; - } - bindTexture(TEXTURE_2D, id); - - bindBuffer(ARRAY_BUFFER, 0); // Making sure that no VBO is bound at this point. - - texData.position(0); - vertexAttribPointer(tex2DVertLoc, 2, FLOAT, false, 4 * SIZEOF_FLOAT, - texData); - texData.position(2); - vertexAttribPointer(tex2DTCoordLoc, 2, FLOAT, false, 4 * SIZEOF_FLOAT, - texData); - - drawArrays(TRIANGLE_STRIP, 0, 4); - - bindTexture(TEXTURE_2D, 0); - if (enabledTex) { - disableTexturing(TEXTURE_2D); - } - - disableVertexAttribArray(tex2DVertLoc); - disableVertexAttribArray(tex2DTCoordLoc); - - useProgram(0); - - if (depthTest) { - enable(DEPTH_TEST); - } else { - disable(DEPTH_TEST); - } - depthMask(depthMask); - } - } - - - protected void drawTextureRect(int id, int width, int height, - int texX0, int texY0, int texX1, int texY1, - int scrX0, int scrY0, int scrX1, int scrY1) { - if (!loadedTexRectShader || texRectShaderContext != context) { - texRectVertShader = createShader(VERTEX_SHADER, texVertShaderSource); - texRectFragShader = createShader(FRAGMENT_SHADER, texRectFragShaderSource); - if (0 < texRectVertShader && 0 < texRectFragShader) { - texRectShaderProgram = createProgram(texRectVertShader, - texRectFragShader); - } - if (0 < texRectShaderProgram) { - texRectVertLoc = getAttribLocation(texRectShaderProgram, "inVertex"); - texRectTCoordLoc = getAttribLocation(texRectShaderProgram, "inTexcoord"); - } - loadedTexRectShader = true; - texRectShaderContext = context; - } - - if (texData == null) { - texData = allocateDirectFloatBuffer(texCoords.length); - } - - if (0 < texRectShaderProgram) { - // The texture overwrites anything drawn earlier. - boolean depthTest = getDepthTest(); - disable(DEPTH_TEST); - - // When drawing the texture we don't write to the - // depth mask, so the texture remains in the background - // and can be occluded by anything drawn later, even if - // if it is behind it. - boolean depthMask = getDepthWriteMask(); - depthMask(false); - - useProgram(texRectShaderProgram); - - enableVertexAttribArray(texRectVertLoc); - enableVertexAttribArray(texRectTCoordLoc); - - // Vertex coordinates of the textured quad are specified - // in normalized screen space (-1, 1): - // Corner 1 - texCoords[ 0] = 2 * (float)scrX0 / pg.width - 1; - texCoords[ 1] = 2 * (float)scrY0 / pg.height - 1; - texCoords[ 2] = texX0; - texCoords[ 3] = texY0; - // Corner 2 - texCoords[ 4] = 2 * (float)scrX1 / pg.width - 1; - texCoords[ 5] = 2 * (float)scrY0 / pg.height - 1; - texCoords[ 6] = texX1; - texCoords[ 7] = texY0; - // Corner 3 - texCoords[ 8] = 2 * (float)scrX0 / pg.width - 1; - texCoords[ 9] = 2 * (float)scrY1 / pg.height - 1; - texCoords[10] = texX0; - texCoords[11] = texY1; - // Corner 4 - texCoords[12] = 2 * (float)scrX1 / pg.width - 1; - texCoords[13] = 2 * (float)scrY1 / pg.height - 1; - texCoords[14] = texX1; - texCoords[15] = texY1; - - texData.rewind(); - texData.put(texCoords); - - activeTexture(TEXTURE0); - boolean enabledTex = false; - if (!texturingIsEnabled(TEXTURE_RECTANGLE)) { - enableTexturing(TEXTURE_RECTANGLE); - enabledTex = true; - } - bindTexture(TEXTURE_RECTANGLE, id); - - bindBuffer(ARRAY_BUFFER, 0); // Making sure that no VBO is bound at this point. - - texData.position(0); - vertexAttribPointer(texRectVertLoc, 2, FLOAT, false, 4 * SIZEOF_FLOAT, - texData); - texData.position(2); - vertexAttribPointer(texRectTCoordLoc, 2, FLOAT, false, 4 * SIZEOF_FLOAT, - texData); - - drawArrays(TRIANGLE_STRIP, 0, 4); - - bindTexture(TEXTURE_RECTANGLE, 0); - if (enabledTex) { - disableTexturing(TEXTURE_RECTANGLE); - } - - disableVertexAttribArray(texRectVertLoc); - disableVertexAttribArray(texRectTCoordLoc); - - useProgram(0); - - if (depthTest) { - enable(DEPTH_TEST); - } else { - disable(DEPTH_TEST); - } - depthMask(depthMask); - } - } - - - protected int getColorValue(int scrX, int scrY) { - if (colorBuffer == null) { - colorBuffer = IntBuffer.allocate(1); - } - colorBuffer.rewind(); - readPixels(scrX, pg.height - scrY - 1, 1, 1, RGBA, UNSIGNED_BYTE, - colorBuffer); - return colorBuffer.get(); - } - - - protected float getDepthValue(int scrX, int scrY) { - if (depthBuffer == null) { - depthBuffer = FloatBuffer.allocate(1); - } - depthBuffer.rewind(); - readPixels(scrX, pg.height - scrY - 1, 1, 1, DEPTH_COMPONENT, FLOAT, - depthBuffer); - return depthBuffer.get(0); - } - - - protected byte getStencilValue(int scrX, int scrY) { - if (stencilBuffer == null) { - stencilBuffer = ByteBuffer.allocate(1); - } - readPixels(scrX, pg.height - scrY - 1, 1, 1, STENCIL_INDEX, - UNSIGNED_BYTE, stencilBuffer); - return stencilBuffer.get(0); - } - - - // bit shifting this might be more efficient - protected static int nextPowerOfTwo(int val) { - int ret = 1; - while (ret < val) { - ret <<= 1; - } - return ret; - } - - - /** - * Converts input native OpenGL value (RGBA on big endian, ABGR on little - * endian) to Java ARGB. - */ - protected static int nativeToJavaARGB(int color) { - if (BIG_ENDIAN) { // RGBA to ARGB - return (color & 0xff000000) | - ((color >> 8) & 0x00ffffff); - } else { // ABGR to ARGB - return (color & 0xff000000) | - ((color << 16) & 0xff0000) | - (color & 0xff00) | - ((color >> 16) & 0xff); - } - } - - - /** - * Converts input array of native OpenGL values (RGBA on big endian, ABGR on - * little endian) representing an image of width x height resolution to Java - * ARGB. It also rearranges the elements in the array so that the image is - * flipped vertically. - */ - protected static void nativeToJavaARGB(int[] pixels, int width, int height) { - int index = 0; - int yindex = (height - 1) * width; - for (int y = 0; y < height / 2; y++) { - if (BIG_ENDIAN) { // RGBA to ARGB - for (int x = 0; x < width; x++) { - int temp = pixels[index]; - pixels[index] = (pixels[yindex] & 0xff000000) | - ((pixels[yindex] >> 8) & 0x00ffffff); - pixels[yindex] = (temp & 0xff000000) | - ((temp >> 8) & 0x00ffffff); - index++; - yindex++; - } - } else { // ABGR to ARGB - for (int x = 0; x < width; x++) { - int temp = pixels[index]; - pixels[index] = (pixels[yindex] & 0xff000000) | - ((pixels[yindex] << 16) & 0xff0000) | - (pixels[yindex] & 0xff00) | - ((pixels[yindex] >> 16) & 0xff); - pixels[yindex] = (temp & 0xff000000) | - ((temp << 16) & 0xff0000) | - (temp & 0xff00) | - ((temp >> 16) & 0xff); - index++; - yindex++; - } - } - yindex -= width * 2; - } - - // Flips image - if ((height % 2) == 1) { - index = (height / 2) * width; - if (BIG_ENDIAN) { // RGBA to ARGB - for (int x = 0; x < width; x++) { - pixels[index] = (pixels[index] & 0xff000000) | - ((pixels[index] >> 8) & 0x00ffffff); - index++; - } - } else { // ABGR to ARGB - for (int x = 0; x < width; x++) { - pixels[index] = (pixels[index] & 0xff000000) | - ((pixels[index] << 16) & 0xff0000) | - (pixels[index] & 0xff00) | - ((pixels[index] >> 16) & 0xff); - index++; - } - } - } - } - - - /** - * Converts input native OpenGL value (RGBA on big endian, ABGR on little - * endian) to Java RGB, so that the alpha component of the result is set - * to opaque (255). - */ - protected static int nativeToJavaRGB(int color) { - if (BIG_ENDIAN) { // RGBA to ARGB - return ((color << 8) & 0xffffff00) | 0xff; - } else { // ABGR to ARGB - return 0xff000000 | ((color << 16) & 0xff0000) | - (color & 0xff00) | - ((color >> 16) & 0xff); - } - } - - - /** - * Converts input array of native OpenGL values (RGBA on big endian, ABGR on - * little endian) representing an image of width x height resolution to Java - * RGB, so that the alpha component of all pixels is set to opaque (255). It - * also rearranges the elements in the array so that the image is flipped - * vertically. - */ - protected static void nativeToJavaRGB(int[] pixels, int width, int height) { - int index = 0; - int yindex = (height - 1) * width; - for (int y = 0; y < height / 2; y++) { - if (BIG_ENDIAN) { // RGBA to ARGB - for (int x = 0; x < width; x++) { - int temp = pixels[index]; - pixels[index] = 0xff000000 | ((pixels[yindex] >> 8) & 0x00ffffff); - pixels[yindex] = 0xff000000 | ((temp >> 8) & 0x00ffffff); - index++; - yindex++; - } - } else { // ABGR to ARGB - for (int x = 0; x < width; x++) { - int temp = pixels[index]; - pixels[index] = 0xff000000 | ((pixels[yindex] << 16) & 0xff0000) | - (pixels[yindex] & 0xff00) | - ((pixels[yindex] >> 16) & 0xff); - pixels[yindex] = 0xff000000 | ((temp << 16) & 0xff0000) | - (temp & 0xff00) | - ((temp >> 16) & 0xff); - index++; - yindex++; - } - } - yindex -= width * 2; - } - - // Flips image - if ((height % 2) == 1) { - index = (height / 2) * width; - if (BIG_ENDIAN) { // RGBA to ARGB - for (int x = 0; x < width; x++) { - pixels[index] = 0xff000000 | ((pixels[index] >> 8) & 0x00ffffff); - index++; - } - } else { // ABGR to ARGB - for (int x = 0; x < width; x++) { - pixels[index] = 0xff000000 | ((pixels[index] << 16) & 0xff0000) | - (pixels[index] & 0xff00) | - ((pixels[index] >> 16) & 0xff); - index++; - } - } - } - } - - - /** - * Converts input Java ARGB value to native OpenGL format (RGBA on big endian, - * BGRA on little endian). - */ - protected static int javaToNativeARGB(int color) { - if (BIG_ENDIAN) { // ARGB to RGBA - return ((color >> 24) & 0xff) | - ((color << 8) & 0xffffff00); - } else { // ARGB to ABGR - return (color & 0xff000000) | - ((color << 16) & 0xff0000) | - (color & 0xff00) | - ((color >> 16) & 0xff); - } - } - - - /** - * Converts input array of Java ARGB values representing an image of width x - * height resolution to native OpenGL format (RGBA on big endian, BGRA on - * little endian). It also rearranges the elements in the array so that the - * image is flipped vertically. - */ - protected static void javaToNativeARGB(int[] pixels, int width, int height) { - int index = 0; - int yindex = (height - 1) * width; - for (int y = 0; y < height / 2; y++) { - if (BIG_ENDIAN) { // ARGB to RGBA - for (int x = 0; x < width; x++) { - int temp = pixels[index]; - pixels[index] = ((pixels[yindex] >> 24) & 0xff) | - ((pixels[yindex] << 8) & 0xffffff00); - pixels[yindex] = ((temp >> 24) & 0xff) | - ((temp << 8) & 0xffffff00); - index++; - yindex++; - } - - } else { // ARGB to ABGR - for (int x = 0; x < width; x++) { - int temp = pixels[index]; - pixels[index] = (pixels[yindex] & 0xff000000) | - ((pixels[yindex] << 16) & 0xff0000) | - (pixels[yindex] & 0xff00) | - ((pixels[yindex] >> 16) & 0xff); - pixels[yindex] = (pixels[yindex] & 0xff000000) | - ((temp << 16) & 0xff0000) | - (temp & 0xff00) | - ((temp >> 16) & 0xff); - index++; - yindex++; - } - } - yindex -= width * 2; - } - - // Flips image - if ((height % 2) == 1) { - index = (height / 2) * width; - if (BIG_ENDIAN) { // ARGB to RGBA - for (int x = 0; x < width; x++) { - pixels[index] = ((pixels[index] >> 24) & 0xff) | - ((pixels[index] << 8) & 0xffffff00); - index++; - } - } else { // ARGB to ABGR - for (int x = 0; x < width; x++) { - pixels[index] = (pixels[index] & 0xff000000) | - ((pixels[index] << 16) & 0xff0000) | - (pixels[index] & 0xff00) | - ((pixels[index] >> 16) & 0xff); - index++; - } - } - } - } - - - /** - * Converts input Java ARGB value to native OpenGL format (RGBA on big endian, - * BGRA on little endian), setting alpha component to opaque (255). - */ - protected static int javaToNativeRGB(int color) { - if (BIG_ENDIAN) { // ARGB to RGBA - return ((color << 8) & 0xffffff00) | 0xff; - } else { // ARGB to ABGR - return 0xff000000 | ((color << 16) & 0xff0000) | - (color & 0xff00) | - ((color >> 16) & 0xff); - } - } - - - /** - * Converts input array of Java ARGB values representing an image of width x - * height resolution to native OpenGL format (RGBA on big endian, BGRA on - * little endian), while setting alpha component of all pixels to opaque - * (255). It also rearranges the elements in the array so that the image is - * flipped vertically. - */ - protected static void javaToNativeRGB(int[] pixels, int width, int height) { - int index = 0; - int yindex = (height - 1) * width; - for (int y = 0; y < height / 2; y++) { - if (BIG_ENDIAN) { // ARGB to RGBA - for (int x = 0; x < width; x++) { - int temp = pixels[index]; - pixels[index] = ((pixels[yindex] << 8) & 0xffffff00) | 0xff; - pixels[yindex] = ((temp << 8) & 0xffffff00) | 0xff; - index++; - yindex++; - } - - } else { - for (int x = 0; x < width; x++) { // ARGB to ABGR - int temp = pixels[index]; - pixels[index] = 0xff000000 | ((pixels[yindex] << 16) & 0xff0000) | - (pixels[yindex] & 0xff00) | - ((pixels[yindex] >> 16) & 0xff); - pixels[yindex] = 0xff000000 | ((temp << 16) & 0xff0000) | - (temp & 0xff00) | - ((temp >> 16) & 0xff); - index++; - yindex++; - } - } - yindex -= width * 2; - } - - // Flips image - if ((height % 2) == 1) { // ARGB to RGBA - index = (height / 2) * width; - if (BIG_ENDIAN) { - for (int x = 0; x < width; x++) { - pixels[index] = ((pixels[index] << 8) & 0xffffff00) | 0xff; - index++; - } - } else { // ARGB to ABGR - for (int x = 0; x < width; x++) { - pixels[index] = 0xff000000 | ((pixels[index] << 16) & 0xff0000) | - (pixels[index] & 0xff00) | - ((pixels[index] >> 16) & 0xff); - index++; - } - } - } - } - - - protected int createShader(int shaderType, String source) { - int shader = createShader(shaderType); - if (shader != 0) { - shaderSource(shader, source); - compileShader(shader); - if (!compiled(shader)) { - System.err.println("Could not compile shader " + shaderType + ":"); - System.err.println(getShaderInfoLog(shader)); - deleteShader(shader); - shader = 0; - } - } - return shader; - } - - - protected int createProgram(int vertexShader, int fragmentShader) { - int program = createProgram(); - if (program != 0) { - attachShader(program, vertexShader); - attachShader(program, fragmentShader); - linkProgram(program); - if (!linked(program)) { - System.err.println("Could not link program: "); - System.err.println(getProgramInfoLog(program)); - deleteProgram(program); - program = 0; - } - } - return program; - } - - - protected boolean compiled(int shader) { - intBuffer.rewind(); - getShaderiv(shader, COMPILE_STATUS, intBuffer); - return intBuffer.get(0) == 0 ? false : true; - } - - - protected boolean linked(int program) { - intBuffer.rewind(); - getProgramiv(program, LINK_STATUS, intBuffer); - return intBuffer.get(0) == 0 ? false : true; - } - - - protected boolean validateFramebuffer() { - int status = checkFramebufferStatus(FRAMEBUFFER); - if (status == FRAMEBUFFER_COMPLETE) { - return true; - } else if (status == FRAMEBUFFER_INCOMPLETE_ATTACHMENT) { - throw new RuntimeException( - "GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT (" + - Integer.toHexString(status) + ")"); - } else if (status == FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT) { - throw new RuntimeException( - "GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT (" + - Integer.toHexString(status) + ")"); - } else if (status == FRAMEBUFFER_INCOMPLETE_DIMENSIONS) { - throw new RuntimeException("GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS (" + - Integer.toHexString(status) + ")"); - } else if (status == FRAMEBUFFER_INCOMPLETE_FORMATS) { - throw new RuntimeException("GL_FRAMEBUFFER_INCOMPLETE_FORMATS (" + - Integer.toHexString(status) + ")"); - } else if (status == FRAMEBUFFER_UNSUPPORTED) { - throw new RuntimeException("GL_FRAMEBUFFER_UNSUPPORTED" + - Integer.toHexString(status)); - } else { - throw new RuntimeException("unknown framebuffer error (" + - Integer.toHexString(status) + ")"); - } - } - - - protected int[] getGLVersion() { - String version = getString(VERSION).trim(); - int[] res = {0, 0, 0}; - String[] parts = version.split(" "); - for (int i = 0; i < parts.length; i++) { - if (0 < parts[i].indexOf(".")) { - String nums[] = parts[i].split("\\."); - try { - res[0] = Integer.parseInt(nums[0]); - } catch (NumberFormatException e) { } - if (1 < nums.length) { - try { - res[1] = Integer.parseInt(nums[1]); - } catch (NumberFormatException e) { } - } - if (2 < nums.length) { - try { - res[2] = Integer.parseInt(nums[2]); - } catch (NumberFormatException e) { } - } - break; - } - } - return res; - } - - - protected boolean hasFBOs() { - int major = getGLVersion()[0]; - if (major < 2) { - String ext = getString(EXTENSIONS); - return ext.indexOf("_framebuffer_object") != -1; - } - return true; // Assuming FBOs are always available for OpenGL >= 2.0 - } - - - protected boolean hasShaders() { - // GLSL might still be available through extensions. For instance, - // GLContext.hasGLSL() gives false for older intel integrated chipsets on - // OSX, where OpenGL is 1.4 but shaders are available. - int major = getGLVersion()[0]; - if (major < 2) { - String ext = getString(EXTENSIONS); - return ext.indexOf("_fragment_shader") != -1 && - ext.indexOf("_vertex_shader") != -1 && - ext.indexOf("_shader_objects") != -1 && - ext.indexOf("_shading_language") != -1; - } - return true; // Assuming shaders are always available for OpenGL >= 2.0 - } - - - protected static ByteBuffer allocateDirectByteBuffer(int size) { - int bytes = PApplet.max(MIN_DIRECT_BUFFER_SIZE, size) * SIZEOF_BYTE; - return ByteBuffer.allocateDirect(bytes).order(ByteOrder.nativeOrder()); - } - - - protected static ByteBuffer allocateByteBuffer(int size) { - if (USE_DIRECT_BUFFERS) { - return allocateDirectByteBuffer(size); - } else { - return ByteBuffer.allocate(size); - } - } - - - protected static ByteBuffer allocateByteBuffer(byte[] arr) { - if (USE_DIRECT_BUFFERS) { - return PGL.allocateDirectByteBuffer(arr.length); - } else { - return ByteBuffer.wrap(arr); - } - } - - - protected static ByteBuffer updateByteBuffer(ByteBuffer buf, byte[] arr, - boolean wrap) { - if (USE_DIRECT_BUFFERS) { - if (buf == null || buf.capacity() < arr.length) { - buf = PGL.allocateDirectByteBuffer(arr.length); - } - buf.position(0); - buf.put(arr); - buf.rewind(); - } else { - if (wrap) { - buf = ByteBuffer.wrap(arr); - } else { - if (buf == null || buf.capacity() < arr.length) { - buf = ByteBuffer.allocate(arr.length); - } - buf.position(0); - buf.put(arr); - buf.rewind(); - } - } - return buf; - } - - - protected static void getByteArray(ByteBuffer buf, byte[] arr) { - if (!buf.hasArray() || buf.array() != arr) { - buf.position(0); - buf.get(arr); - buf.rewind(); - } - } - - - protected static void putByteArray(ByteBuffer buf, byte[] arr) { - if (!buf.hasArray() || buf.array() != arr) { - buf.position(0); - buf.put(arr); - buf.rewind(); - } - } - - - protected static void fillByteBuffer(ByteBuffer buf, int i0, int i1, - byte val) { - int n = i1 - i0; - byte[] temp = new byte[n]; - Arrays.fill(temp, 0, n, val); - buf.position(i0); - buf.put(temp, 0, n); - buf.rewind(); - } - - - protected static ShortBuffer allocateDirectShortBuffer(int size) { - int bytes = PApplet.max(MIN_DIRECT_BUFFER_SIZE, size) * SIZEOF_SHORT; - return ByteBuffer.allocateDirect(bytes).order(ByteOrder.nativeOrder()). - asShortBuffer(); - } - - - protected static ShortBuffer allocateShortBuffer(int size) { - if (USE_DIRECT_BUFFERS) { - return allocateDirectShortBuffer(size); - } else { - return ShortBuffer.allocate(size); - } - } - - - protected static ShortBuffer allocateShortBuffer(short[] arr) { - if (USE_DIRECT_BUFFERS) { - return PGL.allocateDirectShortBuffer(arr.length); - } else { - return ShortBuffer.wrap(arr); - } - } - - - protected static ShortBuffer updateShortBuffer(ShortBuffer buf, short[] arr, - boolean wrap) { - if (USE_DIRECT_BUFFERS) { - if (buf == null || buf.capacity() < arr.length) { - buf = PGL.allocateDirectShortBuffer(arr.length); - } - buf.position(0); - buf.put(arr); - buf.rewind(); - } else { - if (wrap) { - buf = ShortBuffer.wrap(arr); - } else { - if (buf == null || buf.capacity() < arr.length) { - buf = ShortBuffer.allocate(arr.length); - } - buf.position(0); - buf.put(arr); - buf.rewind(); - } - } - return buf; - } - - - protected static void getShortArray(ShortBuffer buf, short[] arr) { - if (!buf.hasArray() || buf.array() != arr) { - buf.position(0); - buf.get(arr); - buf.rewind(); - } - } - - - protected static void putShortArray(ShortBuffer buf, short[] arr) { - if (!buf.hasArray() || buf.array() != arr) { - buf.position(0); - buf.put(arr); - buf.rewind(); - } - } - - - protected static void fillShortBuffer(ShortBuffer buf, int i0, int i1, - short val) { - int n = i1 - i0; - short[] temp = new short[n]; - Arrays.fill(temp, 0, n, val); - buf.position(i0); - buf.put(temp, 0, n); - buf.rewind(); - } - - - protected static IntBuffer allocateDirectIntBuffer(int size) { - int bytes = PApplet.max(MIN_DIRECT_BUFFER_SIZE, size) * SIZEOF_INT; - return ByteBuffer.allocateDirect(bytes).order(ByteOrder.nativeOrder()). - asIntBuffer(); - } - - - protected static IntBuffer allocateIntBuffer(int size) { - if (USE_DIRECT_BUFFERS) { - return allocateDirectIntBuffer(size); - } else { - return IntBuffer.allocate(size); - } - } - - - protected static IntBuffer allocateIntBuffer(int[] arr) { - if (USE_DIRECT_BUFFERS) { - return PGL.allocateDirectIntBuffer(arr.length); - } else { - return IntBuffer.wrap(arr); - } - } - - - protected static IntBuffer updateIntBuffer(IntBuffer buf, int[] arr, - boolean wrap) { - if (USE_DIRECT_BUFFERS) { - if (buf == null || buf.capacity() < arr.length) { - buf = PGL.allocateDirectIntBuffer(arr.length); - } - buf.position(0); - buf.put(arr); - buf.rewind(); - } else { - if (wrap) { - buf = IntBuffer.wrap(arr); - } else { - if (buf == null || buf.capacity() < arr.length) { - buf = IntBuffer.allocate(arr.length); - } - buf.position(0); - buf.put(arr); - buf.rewind(); - } - } - return buf; - } - - - protected static void getIntArray(IntBuffer buf, int[] arr) { - if (!buf.hasArray() || buf.array() != arr) { - buf.position(0); - buf.get(arr); - buf.rewind(); - } - } - - - protected static void putIntArray(IntBuffer buf, int[] arr) { - if (!buf.hasArray() || buf.array() != arr) { - buf.position(0); - buf.put(arr); - buf.rewind(); - } - } - - - protected static void fillIntBuffer(IntBuffer buf, int i0, int i1, int val) { - int n = i1 - i0; - int[] temp = new int[n]; - Arrays.fill(temp, 0, n, val); - buf.position(i0); - buf.put(temp, 0, n); - buf.rewind(); - } - - - protected static FloatBuffer allocateDirectFloatBuffer(int size) { - int bytes = PApplet.max(MIN_DIRECT_BUFFER_SIZE, size) * SIZEOF_FLOAT; - return ByteBuffer.allocateDirect(bytes).order(ByteOrder.nativeOrder()). - asFloatBuffer(); - } - - - protected static FloatBuffer allocateFloatBuffer(int size) { - if (USE_DIRECT_BUFFERS) { - return allocateDirectFloatBuffer(size); - } else { - return FloatBuffer.allocate(size); - } - } - - - protected static FloatBuffer allocateFloatBuffer(float[] arr) { - if (USE_DIRECT_BUFFERS) { - return PGL.allocateDirectFloatBuffer(arr.length); - } else { - return FloatBuffer.wrap(arr); - } - } - - - protected static FloatBuffer updateFloatBuffer(FloatBuffer buf, float[] arr, - boolean wrap) { - if (USE_DIRECT_BUFFERS) { - if (buf == null || buf.capacity() < arr.length) { - buf = PGL.allocateDirectFloatBuffer(arr.length); - } - buf.position(0); - buf.put(arr); - buf.rewind(); - } else { - if (wrap) { - buf = FloatBuffer.wrap(arr); - } else { - if (buf == null || buf.capacity() < arr.length) { - buf = FloatBuffer.allocate(arr.length); - } - buf.position(0); - buf.put(arr); - buf.rewind(); - } - } - return buf; - } - - - protected static void getFloatArray(FloatBuffer buf, float[] arr) { - if (!buf.hasArray() || buf.array() != arr) { - buf.position(0); - buf.get(arr); - buf.rewind(); - } - } - - - protected static void putFloatArray(FloatBuffer buf, float[] arr) { - if (!buf.hasArray() || buf.array() != arr) { - buf.position(0); - buf.put(arr); - buf.rewind(); - } - } - - - protected static void fillFloatBuffer(FloatBuffer buf, int i0, int i1, - float val) { - int n = i1 - i0; - float[] temp = new float[n]; - Arrays.fill(temp, 0, n, val); - buf.position(i0); - buf.put(temp, 0, n); - buf.rewind(); - } - - - /////////////////////////////////////////////////////////// - - // Java specific stuff - - - protected class KeyPoller extends Thread { - protected PApplet parent; - protected boolean stopRequested; - protected boolean[] pressedKeys; - protected char[] charCheys; - - KeyPoller(PApplet parent) { - this.parent = parent; - stopRequested = false; - } - - @Override - public void run() { - pressedKeys = new boolean[256]; - charCheys = new char[256]; - Keyboard.enableRepeatEvents(true); - while (true) { - if (stopRequested) break; - - Keyboard.poll(); - while (Keyboard.next()) { - if (stopRequested) break; - - long millis = Keyboard.getEventNanoseconds() / 1000000L; - char keyChar = Keyboard.getEventCharacter(); - int keyCode = Keyboard.getEventKey(); - - if (keyCode >= pressedKeys.length) continue; - - int modifiers = 0; - if (Keyboard.isKeyDown(Keyboard.KEY_LSHIFT) || - Keyboard.isKeyDown(Keyboard.KEY_RSHIFT)) { - modifiers |= Event.SHIFT; - } - if (Keyboard.isKeyDown(Keyboard.KEY_LCONTROL) || - Keyboard.isKeyDown(Keyboard.KEY_RCONTROL)) { - modifiers |= Event.CTRL; - } - if (Keyboard.isKeyDown(Keyboard.KEY_LMETA) || - Keyboard.isKeyDown(Keyboard.KEY_RMETA)) { - modifiers |= Event.META; - } - if (Keyboard.isKeyDown(Keyboard.KEY_LMENU) || - Keyboard.isKeyDown(Keyboard.KEY_RMENU)) { - // LWJGL maps the menu key and the alt key to the same value. - modifiers |= Event.ALT; - } - - int keyPCode = LWJGLtoAWTCode(keyCode); - if (keyChar == 0) { - keyChar = PConstants.CODED; - } - - int action = 0; - if (Keyboard.getEventKeyState()) { - action = KeyEvent.PRESS; - KeyEvent ke = new KeyEvent(null, millis, - action, modifiers, - keyChar, keyPCode); - parent.postEvent(ke); - pressedKeys[keyCode] = true; - charCheys[keyCode] = keyChar; - keyPCode = 0; - action = KeyEvent.TYPE; - } else if (pressedKeys[keyCode]) { - keyChar = charCheys[keyCode]; - pressedKeys[keyCode] = false; - action = KeyEvent.RELEASE; - } - - KeyEvent ke = new KeyEvent(null, millis, - action, modifiers, - keyChar, keyPCode); - parent.postEvent(ke); - } - try { - Thread.sleep(10); - } catch (InterruptedException e) { - e.printStackTrace(); - } - } - } - - public void requestStop() { - stopRequested = true; - } - } - - - protected class MousePoller extends Thread { - protected PApplet parent; - protected boolean stopRequested; - protected boolean pressed; - protected boolean inside; - protected long startedClickTime; - protected int startedClickButton; - - MousePoller(PApplet parent) { - this.parent = parent; - stopRequested = false; - } - - @Override - public void run() { - while (true) { - if (stopRequested) break; - - Mouse.poll(); - while (Mouse.next()) { - if (stopRequested) break; - - long millis = Mouse.getEventNanoseconds() / 1000000L; - - int modifiers = 0; - if (Keyboard.isKeyDown(Keyboard.KEY_LSHIFT) || - Keyboard.isKeyDown(Keyboard.KEY_RSHIFT)) { - modifiers |= Event.SHIFT; - } - if (Keyboard.isKeyDown(Keyboard.KEY_LCONTROL) || - Keyboard.isKeyDown(Keyboard.KEY_RCONTROL)) { - modifiers |= Event.CTRL; - } - if (Keyboard.isKeyDown(Keyboard.KEY_LMETA) || - Keyboard.isKeyDown(Keyboard.KEY_RMETA)) { - modifiers |= Event.META; - } - if (Keyboard.isKeyDown(Keyboard.KEY_LMENU) || - Keyboard.isKeyDown(Keyboard.KEY_RMENU)) { - // LWJGL maps the menu key and the alt key to the same value. - modifiers |= Event.ALT; - } - - int x = Mouse.getX(); - int y = parent.height - Mouse.getY(); - int button = 0; - if (Mouse.isButtonDown(0)) { - button = PConstants.LEFT; - } else if (Mouse.isButtonDown(1)) { - button = PConstants.RIGHT; - } else if (Mouse.isButtonDown(2)) { - button = PConstants.CENTER; - } - - int action = 0; - if (button != 0) { - if (pressed) { - action = MouseEvent.DRAG; - } else { - action = MouseEvent.PRESS; - pressed = true; - } - } else if (pressed) { - action = MouseEvent.RELEASE; - pressed = false; - } else { - action = MouseEvent.MOVE; - } - - if (inside) { - if (!Mouse.isInsideWindow()) { - inside = false; - action = MouseEvent.EXIT; - } - } else { - if (Mouse.isInsideWindow()) { - inside = true; - action = MouseEvent.ENTER; - } - } - - int count = 0; - if (Mouse.getEventButtonState()) { - startedClickTime = millis; - startedClickButton = button; - } else { - if (action == MouseEvent.RELEASE) { - boolean clickDetected = millis - startedClickTime < 500; - if (clickDetected) { - // post a RELEASE event, in addition to the CLICK event. - MouseEvent me = new MouseEvent(null, millis, action, modifiers, - x, y, button, count); - parent.postEvent(me); - action = MouseEvent.CLICK; - count = 1; - } - } - } - - MouseEvent me = new MouseEvent(null, millis, action, modifiers, - x, y, button, count); - parent.postEvent(me); - } - try { - Thread.sleep(10); - } catch (InterruptedException e) { - e.printStackTrace(); - } - } - } - - public void requestStop() { - stopRequested = true; - } - } - - // To complete later... - // http://docs.oracle.com/javase/6/docs/api/java/awt/event/KeyEvent.html - // http://processing.org/reference/keyCode.html - protected int LWJGLtoAWTCode(int code) { - switch (code) { - case Keyboard.KEY_0: - return java.awt.event.KeyEvent.VK_0; - case Keyboard.KEY_1: - return java.awt.event.KeyEvent.VK_1; - case Keyboard.KEY_2: - return java.awt.event.KeyEvent.VK_2; - case Keyboard.KEY_3: - return java.awt.event.KeyEvent.VK_3; - case Keyboard.KEY_4: - return java.awt.event.KeyEvent.VK_4; - case Keyboard.KEY_5: - return java.awt.event.KeyEvent.VK_5; - case Keyboard.KEY_6: - return java.awt.event.KeyEvent.VK_6; - case Keyboard.KEY_7: - return java.awt.event.KeyEvent.VK_7; - case Keyboard.KEY_8: - return java.awt.event.KeyEvent.VK_8; - case Keyboard.KEY_9: - return java.awt.event.KeyEvent.VK_9; - case Keyboard.KEY_A: - return java.awt.event.KeyEvent.VK_A; - case Keyboard.KEY_B: - return java.awt.event.KeyEvent.VK_B; - case Keyboard.KEY_C: - return java.awt.event.KeyEvent.VK_C; - case Keyboard.KEY_D: - return java.awt.event.KeyEvent.VK_D; - case Keyboard.KEY_E: - return java.awt.event.KeyEvent.VK_E; - case Keyboard.KEY_F: - return java.awt.event.KeyEvent.VK_F; - case Keyboard.KEY_G: - return java.awt.event.KeyEvent.VK_G; - case Keyboard.KEY_H: - return java.awt.event.KeyEvent.VK_H; - case Keyboard.KEY_I: - return java.awt.event.KeyEvent.VK_I; - case Keyboard.KEY_J: - return java.awt.event.KeyEvent.VK_J; - case Keyboard.KEY_K: - return java.awt.event.KeyEvent.VK_K; - case Keyboard.KEY_L: - return java.awt.event.KeyEvent.VK_L; - case Keyboard.KEY_M: - return java.awt.event.KeyEvent.VK_M; - case Keyboard.KEY_N: - return java.awt.event.KeyEvent.VK_N; - case Keyboard.KEY_O: - return java.awt.event.KeyEvent.VK_O; - case Keyboard.KEY_P: - return java.awt.event.KeyEvent.VK_P; - case Keyboard.KEY_Q: - return java.awt.event.KeyEvent.VK_Q; - case Keyboard.KEY_R: - return java.awt.event.KeyEvent.VK_R; - case Keyboard.KEY_S: - return java.awt.event.KeyEvent.VK_S; - case Keyboard.KEY_T: - return java.awt.event.KeyEvent.VK_T; - case Keyboard.KEY_U: - return java.awt.event.KeyEvent.VK_U; - case Keyboard.KEY_V: - return java.awt.event.KeyEvent.VK_V; - case Keyboard.KEY_W: - return java.awt.event.KeyEvent.VK_W; - case Keyboard.KEY_X: - return java.awt.event.KeyEvent.VK_X; - case Keyboard.KEY_Y: - return java.awt.event.KeyEvent.VK_Y; - case Keyboard.KEY_Z: - return java.awt.event.KeyEvent.VK_Z; - case Keyboard.KEY_ADD: - return java.awt.event.KeyEvent.VK_ADD; - case Keyboard.KEY_APOSTROPHE: - return java.awt.event.KeyEvent.VK_ASTERISK; - case Keyboard.KEY_AT: - return java.awt.event.KeyEvent.VK_AT; - case Keyboard.KEY_BACK: - return java.awt.event.KeyEvent.VK_BACK_SPACE; - case Keyboard.KEY_BACKSLASH: - return java.awt.event.KeyEvent.VK_BACK_SLASH; - case Keyboard.KEY_CAPITAL: - return java.awt.event.KeyEvent.VK_CAPS_LOCK; - case Keyboard.KEY_CIRCUMFLEX: - return java.awt.event.KeyEvent.VK_CIRCUMFLEX; - case Keyboard.KEY_COLON: - return java.awt.event.KeyEvent.VK_COLON; - case Keyboard.KEY_COMMA: - return java.awt.event.KeyEvent.VK_COMMA; - case Keyboard.KEY_CONVERT: - return java.awt.event.KeyEvent.VK_CONVERT; - case Keyboard.KEY_DECIMAL: - return java.awt.event.KeyEvent.VK_DECIMAL; - case Keyboard.KEY_DELETE: - return java.awt.event.KeyEvent.VK_DELETE; - case Keyboard.KEY_DIVIDE: - return java.awt.event.KeyEvent.VK_DIVIDE; - case Keyboard.KEY_DOWN: - return java.awt.event.KeyEvent.VK_DOWN; - case Keyboard.KEY_END: - return java.awt.event.KeyEvent.VK_END; - case Keyboard.KEY_EQUALS: - return java.awt.event.KeyEvent.VK_EQUALS; - case Keyboard.KEY_ESCAPE: - return java.awt.event.KeyEvent.VK_ESCAPE; - case Keyboard.KEY_F1: - return java.awt.event.KeyEvent.VK_F1; - case Keyboard.KEY_F10: - return java.awt.event.KeyEvent.VK_F10; - case Keyboard.KEY_F11: - return java.awt.event.KeyEvent.VK_F11; - case Keyboard.KEY_F12: - return java.awt.event.KeyEvent.VK_F12; - case Keyboard.KEY_F13: - return java.awt.event.KeyEvent.VK_F13; - case Keyboard.KEY_F14: - return java.awt.event.KeyEvent.VK_F14; - case Keyboard.KEY_F15: - return java.awt.event.KeyEvent.VK_F15; - case Keyboard.KEY_F2: - return java.awt.event.KeyEvent.VK_F2; - case Keyboard.KEY_F3: - return java.awt.event.KeyEvent.VK_F3; - case Keyboard.KEY_F4: - return java.awt.event.KeyEvent.VK_F4; - case Keyboard.KEY_F5: - return java.awt.event.KeyEvent.VK_F5; - case Keyboard.KEY_F6: - return java.awt.event.KeyEvent.VK_F6; - case Keyboard.KEY_F7: - return java.awt.event.KeyEvent.VK_F7; - case Keyboard.KEY_F8: - return java.awt.event.KeyEvent.VK_F8; - case Keyboard.KEY_F9: - return java.awt.event.KeyEvent.VK_F9; -// case Keyboard.KEY_GRAVE: - case Keyboard.KEY_HOME: - return java.awt.event.KeyEvent.VK_HOME; - case Keyboard.KEY_INSERT: - return java.awt.event.KeyEvent.VK_INSERT; - case Keyboard.KEY_LBRACKET: - return java.awt.event.KeyEvent.VK_BRACELEFT; - case Keyboard.KEY_LCONTROL: - return java.awt.event.KeyEvent.VK_CONTROL; - case Keyboard.KEY_LEFT: - return java.awt.event.KeyEvent.VK_LEFT; - case Keyboard.KEY_LMENU: - return java.awt.event.KeyEvent.VK_ALT; - case Keyboard.KEY_LMETA: - return java.awt.event.KeyEvent.VK_META; - case Keyboard.KEY_LSHIFT: - return java.awt.event.KeyEvent.VK_SHIFT; - case Keyboard.KEY_MINUS: - return java.awt.event.KeyEvent.VK_MINUS; - case Keyboard.KEY_MULTIPLY: - return java.awt.event.KeyEvent.VK_MULTIPLY; -// case Keyboard.KEY_NEXT: - case Keyboard.KEY_NUMLOCK: - return java.awt.event.KeyEvent.VK_NUM_LOCK; - case Keyboard.KEY_NUMPAD0: - return java.awt.event.KeyEvent.VK_NUMPAD0; - case Keyboard.KEY_NUMPAD1: - return java.awt.event.KeyEvent.VK_NUMPAD1; - case Keyboard.KEY_NUMPAD2: - return java.awt.event.KeyEvent.VK_NUMPAD2; - case Keyboard.KEY_NUMPAD3: - return java.awt.event.KeyEvent.VK_NUMPAD3; - case Keyboard.KEY_NUMPAD4: - return java.awt.event.KeyEvent.VK_NUMPAD4; - case Keyboard.KEY_NUMPAD5: - return java.awt.event.KeyEvent.VK_NUMPAD5; - case Keyboard.KEY_NUMPAD6: - return java.awt.event.KeyEvent.VK_NUMPAD6; - case Keyboard.KEY_NUMPAD7: - return java.awt.event.KeyEvent.VK_NUMPAD7; - case Keyboard.KEY_NUMPAD8: - return java.awt.event.KeyEvent.VK_NUMPAD8; - case Keyboard.KEY_NUMPAD9: - return java.awt.event.KeyEvent.VK_NUMPAD9; -// case Keyboard.KEY_NUMPADCOMMA: -// case Keyboard.KEY_NUMPADENTER: -// case Keyboard.KEY_NUMPADEQUALS: - case Keyboard.KEY_PAUSE: - return java.awt.event.KeyEvent.VK_PAUSE; - case Keyboard.KEY_PERIOD: - return java.awt.event.KeyEvent.VK_PERIOD; -// case Keyboard.KEY_POWER: -// case Keyboard.KEY_PRIOR: - case Keyboard.KEY_RBRACKET: - return java.awt.event.KeyEvent.VK_BRACERIGHT; - case Keyboard.KEY_RCONTROL: - return java.awt.event.KeyEvent.VK_CONTROL; - case Keyboard.KEY_RETURN: - return java.awt.event.KeyEvent.VK_ENTER; - case Keyboard.KEY_RIGHT: - return java.awt.event.KeyEvent.VK_RIGHT; -// case Keyboard.KEY_RMENU: - case Keyboard.KEY_RMETA: - return java.awt.event.KeyEvent.VK_META; - case Keyboard.KEY_RSHIFT: - return java.awt.event.KeyEvent.VK_SHIFT; -// case Keyboard.KEY_SCROLL: - case Keyboard.KEY_SEMICOLON: - return java.awt.event.KeyEvent.VK_SEMICOLON; - case Keyboard.KEY_SLASH: - return java.awt.event.KeyEvent.VK_SLASH; -// case Keyboard.KEY_SLEEP: - case Keyboard.KEY_SPACE: - return java.awt.event.KeyEvent.VK_SPACE; - case Keyboard.KEY_STOP: - return java.awt.event.KeyEvent.VK_STOP; - case Keyboard.KEY_SUBTRACT: - return java.awt.event.KeyEvent.VK_SUBTRACT; - case Keyboard.KEY_TAB: - return java.awt.event.KeyEvent.VK_TAB; -// case Keyboard.KEY_UNDERLINE: - case Keyboard.KEY_UP: - return java.awt.event.KeyEvent.VK_UP; - default: - return 0; - } - } - - - - - // OPENGL API: Still need to add all the missing functions to expose the entire - // GLES 2.0 API - - /////////////////////////////////////////////////////////// - - // OpenGL constants - - public static final int FALSE = GL11.GL_FALSE; - public static final int TRUE = GL11.GL_TRUE; - - public static final int LESS = GL11.GL_LESS; - public static final int LEQUAL = GL11.GL_LEQUAL; - - public static final int CCW = GL11.GL_CCW; - public static final int CW = GL11.GL_CW; - - public static final int CULL_FACE = GL11.GL_CULL_FACE; - public static final int FRONT = GL11.GL_FRONT; - public static final int BACK = GL11.GL_BACK; - public static final int FRONT_AND_BACK = GL11.GL_FRONT_AND_BACK; - - public static final int VIEWPORT = GL11.GL_VIEWPORT; - - public static final int SCISSOR_TEST = GL11.GL_SCISSOR_TEST; - public static final int DEPTH_TEST = GL11.GL_DEPTH_TEST; - public static final int DEPTH_WRITEMASK = GL11.GL_DEPTH_WRITEMASK; - - public static final int COLOR_BUFFER_BIT = GL11.GL_COLOR_BUFFER_BIT; - public static final int DEPTH_BUFFER_BIT = GL11.GL_DEPTH_BUFFER_BIT; - public static final int STENCIL_BUFFER_BIT = GL11.GL_STENCIL_BUFFER_BIT; - - public static final int FUNC_ADD = GL14.GL_FUNC_ADD; - public static final int FUNC_MIN = GL14.GL_MIN; - public static final int FUNC_MAX = GL14.GL_MAX; - public static final int FUNC_REVERSE_SUBTRACT = GL14.GL_FUNC_REVERSE_SUBTRACT; - - public static final int TEXTURE_2D = GL11.GL_TEXTURE_2D; - public static final int TEXTURE_RECTANGLE = GL31.GL_TEXTURE_RECTANGLE; - - public static final int TEXTURE_BINDING_2D = GL11.GL_TEXTURE_BINDING_2D; - public static final int TEXTURE_BINDING_RECTANGLE = - GL31.GL_TEXTURE_BINDING_RECTANGLE; - - public static final int RGB = GL11.GL_RGB; - public static final int RGBA = GL11.GL_RGBA; - public static final int ALPHA = GL11.GL_ALPHA; - public static final int UNSIGNED_INT = GL11.GL_UNSIGNED_INT; - public static final int UNSIGNED_BYTE = GL11.GL_UNSIGNED_BYTE; - public static final int UNSIGNED_SHORT = GL11.GL_UNSIGNED_SHORT; - public static final int FLOAT = GL11.GL_FLOAT; - - public static final int NEAREST = GL11.GL_NEAREST; - public static final int LINEAR = GL11.GL_LINEAR; - public static final int LINEAR_MIPMAP_NEAREST = GL11.GL_LINEAR_MIPMAP_NEAREST; - public static final int LINEAR_MIPMAP_LINEAR = GL11.GL_LINEAR_MIPMAP_LINEAR; - - public static final int TEXTURE_MAX_ANISOTROPY = - EXTTextureFilterAnisotropic.GL_TEXTURE_MAX_ANISOTROPY_EXT; - public static final int MAX_TEXTURE_MAX_ANISOTROPY = - EXTTextureFilterAnisotropic.GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT; - - public static final int CLAMP_TO_EDGE = GL12.GL_CLAMP_TO_EDGE; - public static final int REPEAT = GL11.GL_REPEAT; - - public static final int RGBA8 = GL11.GL_RGBA8; - public static final int DEPTH24_STENCIL8 = GL30.GL_DEPTH24_STENCIL8; - - public static final int DEPTH_COMPONENT = GL11.GL_DEPTH_COMPONENT; - public static final int DEPTH_COMPONENT16 = GL14.GL_DEPTH_COMPONENT16; - public static final int DEPTH_COMPONENT24 = GL14.GL_DEPTH_COMPONENT24; - public static final int DEPTH_COMPONENT32 = GL14.GL_DEPTH_COMPONENT32; - - public static final int STENCIL_INDEX = GL11.GL_STENCIL_INDEX; - public static final int STENCIL_INDEX1 = GL30.GL_STENCIL_INDEX1; - public static final int STENCIL_INDEX4 = GL30.GL_STENCIL_INDEX4; - public static final int STENCIL_INDEX8 = GL30.GL_STENCIL_INDEX8; - - public static final int ARRAY_BUFFER = GL15.GL_ARRAY_BUFFER; - public static final int ELEMENT_ARRAY_BUFFER = GL15.GL_ELEMENT_ARRAY_BUFFER; - - public static final int SAMPLES = GL13.GL_SAMPLES; - - public static final int FRAMEBUFFER_COMPLETE = - GL30.GL_FRAMEBUFFER_COMPLETE; - public static final int FRAMEBUFFER_INCOMPLETE_ATTACHMENT = - GL30.GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT; - public static final int FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT = - GL30.GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT; - public static final int FRAMEBUFFER_INCOMPLETE_DIMENSIONS = - EXTFramebufferObject.GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT; - public static final int FRAMEBUFFER_INCOMPLETE_FORMATS = - EXTFramebufferObject.GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT; - public static final int FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER = - GL30.GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER; - public static final int FRAMEBUFFER_INCOMPLETE_READ_BUFFER = - GL30.GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER; - public static final int FRAMEBUFFER_UNSUPPORTED = - GL30.GL_FRAMEBUFFER_UNSUPPORTED; - - public static final int STATIC_DRAW = GL15.GL_STATIC_DRAW; - public static final int DYNAMIC_DRAW = GL15.GL_DYNAMIC_DRAW; - public static final int STREAM_DRAW = GL15.GL_STREAM_DRAW; - - public static final int READ_ONLY = GL15.GL_READ_ONLY; - public static final int WRITE_ONLY = GL15.GL_WRITE_ONLY; - public static final int READ_WRITE = GL15.GL_READ_WRITE; - - public static final int TRIANGLE_FAN = GL11.GL_TRIANGLE_FAN; - public static final int TRIANGLE_STRIP = GL11.GL_TRIANGLE_STRIP; - public static final int TRIANGLES = GL11.GL_TRIANGLES; - - public static final int VENDOR = GL11.GL_VENDOR; - public static final int RENDERER = GL11.GL_RENDERER; - public static final int VERSION = GL11.GL_VERSION; - public static final int EXTENSIONS = GL11.GL_EXTENSIONS; - public static final int SHADING_LANGUAGE_VERSION = - GL20.GL_SHADING_LANGUAGE_VERSION; - - public static final int MAX_TEXTURE_SIZE = GL11.GL_MAX_TEXTURE_SIZE; - public static final int MAX_SAMPLES = GL30.GL_MAX_SAMPLES; - public static final int ALIASED_LINE_WIDTH_RANGE = - GL12.GL_ALIASED_LINE_WIDTH_RANGE; - public static final int ALIASED_POINT_SIZE_RANGE = - GL12.GL_ALIASED_POINT_SIZE_RANGE; - public static final int DEPTH_BITS = GL11.GL_DEPTH_BITS; - public static final int STENCIL_BITS = GL11.GL_STENCIL_BITS; - - public static final int TESS_WINDING_NONZERO = GLU.GLU_TESS_WINDING_NONZERO; - public static final int TESS_WINDING_ODD = GLU.GLU_TESS_WINDING_ODD; - - public static final int TEXTURE0 = GL13.GL_TEXTURE0; - public static final int TEXTURE1 = GL13.GL_TEXTURE1; - public static final int TEXTURE2 = GL13.GL_TEXTURE2; - public static final int TEXTURE3 = GL13.GL_TEXTURE3; - public static final int TEXTURE_MIN_FILTER = GL11.GL_TEXTURE_MIN_FILTER; - public static final int TEXTURE_MAG_FILTER = GL11.GL_TEXTURE_MAG_FILTER; - public static final int TEXTURE_WRAP_S = GL11.GL_TEXTURE_WRAP_S; - public static final int TEXTURE_WRAP_T = GL11.GL_TEXTURE_WRAP_T; - - public static final int BLEND = GL11.GL_BLEND; - public static final int ONE = GL11.GL_ONE; - public static final int ZERO = GL11.GL_ZERO; - public static final int SRC_ALPHA = GL11.GL_SRC_ALPHA; - public static final int DST_ALPHA = GL11.GL_DST_ALPHA; - public static final int ONE_MINUS_SRC_ALPHA = GL11.GL_ONE_MINUS_SRC_ALPHA; - public static final int ONE_MINUS_DST_COLOR = GL11.GL_ONE_MINUS_DST_COLOR; - public static final int ONE_MINUS_SRC_COLOR = GL11.GL_ONE_MINUS_SRC_COLOR; - public static final int DST_COLOR = GL11.GL_DST_COLOR; - public static final int SRC_COLOR = GL11.GL_SRC_COLOR; - - public static final int FRAMEBUFFER = GL30.GL_FRAMEBUFFER; - public static final int COLOR_ATTACHMENT0 = GL30.GL_COLOR_ATTACHMENT0; - public static final int COLOR_ATTACHMENT1 = GL30.GL_COLOR_ATTACHMENT1; - public static final int COLOR_ATTACHMENT2 = GL30.GL_COLOR_ATTACHMENT2; - public static final int COLOR_ATTACHMENT3 = GL30.GL_COLOR_ATTACHMENT3; - public static final int RENDERBUFFER = GL30.GL_RENDERBUFFER; - public static final int DEPTH_ATTACHMENT = GL30.GL_DEPTH_ATTACHMENT; - public static final int STENCIL_ATTACHMENT = GL30.GL_STENCIL_ATTACHMENT; - public static final int READ_FRAMEBUFFER = GL30.GL_READ_FRAMEBUFFER; - public static final int DRAW_FRAMEBUFFER = GL30.GL_DRAW_FRAMEBUFFER; - - public static final int VERTEX_SHADER = GL20.GL_VERTEX_SHADER; - public static final int FRAGMENT_SHADER = GL20.GL_FRAGMENT_SHADER; - public static final int INFO_LOG_LENGTH = GL20.GL_INFO_LOG_LENGTH; - public static final int SHADER_SOURCE_LENGTH = GL20.GL_SHADER_SOURCE_LENGTH; - public static final int COMPILE_STATUS = GL20.GL_COMPILE_STATUS; - public static final int LINK_STATUS = GL20.GL_LINK_STATUS; - public static final int VALIDATE_STATUS = GL20.GL_VALIDATE_STATUS; - - public static final int MULTISAMPLE = GL13.GL_MULTISAMPLE; - public static final int POINT_SMOOTH = GL11.GL_POINT_SMOOTH; - public static final int LINE_SMOOTH = GL11.GL_LINE_SMOOTH; - public static final int POLYGON_SMOOTH = GL11.GL_POLYGON_SMOOTH; - - - ////////////////////////////////////////////////////////////////////////////// - - // Caps query - - - public String getString(int name) { - return GL11.glGetString(name); - } - - - public void getIntegerv(int name, IntBuffer values) { - if (-1 < name) { - GL11.glGetInteger(name, values); - } else { - fillIntBuffer(values, 0, values.capacity() - 1, 0); - } - } - - - public void getFloatv(int name, FloatBuffer values) { - if (-1 < name) { - GL11.glGetFloat(name, values); - } else { - fillFloatBuffer(values, 0, values.capacity() - 1, 0); - } - } - - - public void getBooleanv(int name, IntBuffer values) { - if (-1 < name) { - if (byteBuffer.capacity() < values.capacity()) { - byteBuffer = allocateDirectByteBuffer(values.capacity()); - } - GL11.glGetBoolean(name, byteBuffer); - for (int i = 0; i < values.capacity(); i++) { - values.put(i, byteBuffer.get(i)); - } - } else { - fillIntBuffer(values, 0, values.capacity() - 1, 0); - } - } - - - /////////////////////////////////////////////////////////// - - // Enable/disable caps - - - public void enable(int cap) { - if (-1 < cap) { - GL11.glEnable(cap); - } - } - - - public void disable(int cap) { - if (-1 < cap) { - GL11.glDisable(cap); - } - } - - - /////////////////////////////////////////////////////////// - - // Render control - - - public void flush() { - GL11.glFlush(); - } - - - public void finish() { - GL11.glFinish(); - } - - - /////////////////////////////////////////////////////////// - - // Error handling - - - public int getError() { - return GL11.glGetError(); - } - - - public String errorString(int err) { - return glu.gluErrorString(err); - } - - - /////////////////////////////////////////////////////////// - - // Rendering options - - - public void frontFace(int mode) { - GL11.glFrontFace(mode); - } - - - public void cullFace(int mode) { - GL11.glCullFace(mode); - } - - - public void depthMask(boolean flag) { - GL11.glDepthMask(flag); - } - - - public void depthFunc(int func) { - GL11.glDepthFunc(func); - } - - - /////////////////////////////////////////////////////////// - - // Textures - - - public void genTextures(int n, IntBuffer ids) { - GL11.glGenTextures(ids); - } - - - public void deleteTextures(int n, IntBuffer ids) { - GL11.glDeleteTextures(ids); - } - - - public void activeTexture(int unit) { - GL13.glActiveTexture(unit); - } - - - public void bindTexture(int target, int id) { - GL11.glBindTexture(target, id); - if (target == TEXTURE_2D) { - boundTextures[0] = id; - } else if (target == TEXTURE_RECTANGLE) { - boundTextures[1] = id; - } - } - - - public void texImage2D(int target, int level, int internalFormat, - int width, int height, int border, int format, - int type, Buffer data) { - GL11.glTexImage2D(target, level, internalFormat, - width, height, border, format, type, (IntBuffer)data); - } - - - public void texSubImage2D(int target, int level, int xOffset, int yOffset, - int width, int height, int format, - int type, Buffer data) { - GL11.glTexSubImage2D(target, level, xOffset, yOffset, - width, height, format, type, (IntBuffer)data); - } - - - public void texParameteri(int target, int param, int value) { - GL11.glTexParameteri(target, param, value); - } - - - public void texParameterf(int target, int param, float value) { - GL11.glTexParameterf(target, param, value); - } - - - public void getTexParameteriv(int target, int param, IntBuffer values) { - GL11.glGetTexParameter(target, param, values); - } - - - public void generateMipmap(int target) { - GL30.glGenerateMipmap(target); - } - - - /////////////////////////////////////////////////////////// - - // Vertex Buffers - - - public void genBuffers(int n, IntBuffer ids) { - GL15.glGenBuffers(ids); - } - - - public void deleteBuffers(int n, IntBuffer ids) { - GL15.glDeleteBuffers(ids); - } - - - public void bindBuffer(int target, int id) { - GL15.glBindBuffer(target, id); - } - - - public void bufferData(int target, int size, Buffer data, int usage) { - if (data == null) { - FloatBuffer empty = BufferUtils.createFloatBuffer(size); - GL15.glBufferData(target, empty, usage); - } else { - if (data instanceof ByteBuffer) { - GL15.glBufferData(target, (ByteBuffer)data, usage); - } else if (data instanceof ShortBuffer) { - GL15.glBufferData(target, (ShortBuffer)data, usage); - } else if (data instanceof IntBuffer) { - GL15.glBufferData(target, (IntBuffer)data, usage); - } else if (data instanceof FloatBuffer) { - GL15.glBufferData(target, (FloatBuffer)data, usage); - } - } - } - - - public void bufferSubData(int target, int offset, int size, Buffer data) { - if (data instanceof ByteBuffer) { - GL15.glBufferSubData(target, offset, (ByteBuffer)data); - } else if (data instanceof ShortBuffer) { - GL15.glBufferSubData(target, offset, (ShortBuffer)data); - } else if (data instanceof IntBuffer) { - GL15.glBufferSubData(target, offset, (IntBuffer)data); - } else if (data instanceof FloatBuffer) { - GL15.glBufferSubData(target, offset, (FloatBuffer)data); - } - } - - - public void drawArrays(int mode, int first, int count) { - GL11.glDrawArrays(mode, first, count); - } - - - public void drawElements(int mode, int count, int type, int offset) { - GL11.glDrawElements(mode, count, type, offset); - } - - - public void enableVertexAttribArray(int loc) { - GL20.glEnableVertexAttribArray(loc); - } - - - public void disableVertexAttribArray(int loc) { - GL20.glDisableVertexAttribArray(loc); - } - - - public void vertexAttribPointer(int loc, int size, int type, - boolean normalized, int stride, int offset) { - GL20.glVertexAttribPointer(loc, size, type, normalized, stride, offset); - } - - - public void vertexAttribPointer(int loc, int size, int type, - boolean normalized, int stride, Buffer data) { - if (type == UNSIGNED_INT) { - GL20.glVertexAttribPointer(loc, size, true, normalized, stride, (IntBuffer)data); - } else if (type == UNSIGNED_BYTE) { - GL20.glVertexAttribPointer(loc, size, true, normalized, stride, (ByteBuffer)data); - } else if (type == UNSIGNED_SHORT) { - GL20.glVertexAttribPointer(loc, size, true, normalized, stride, (ShortBuffer)data); - } else if (type == FLOAT) { - GL20.glVertexAttribPointer(loc, size, normalized, stride, (FloatBuffer)data); - } - } - - - public ByteBuffer mapBuffer(int target, int access) { - return GL15.glMapBuffer(target, access, null); - } - - - public ByteBuffer mapBufferRange(int target, int offset, int length, - int access) { - return GL30.glMapBufferRange(target, offset, length, access, null); - } - - - public void unmapBuffer(int target) { - GL15.glUnmapBuffer(target); - } - - - /////////////////////////////////////////////////////////// - - // Framebuffers, renderbuffers - - - public void genFramebuffers(int n, IntBuffer ids) { - GL30.glGenFramebuffers(ids); - } - - - public void deleteFramebuffers(int n, IntBuffer ids) { - GL30.glDeleteFramebuffers(ids); - } - - - public void genRenderbuffers(int n, IntBuffer ids) { - GL30.glGenRenderbuffers(ids); - } - - - public void deleteRenderbuffers(int n, IntBuffer ids) { - GL30.glDeleteRenderbuffers(ids); - } - - - public void bindFramebuffer(int target, int id) { - GL30.glBindFramebuffer(target, id); - } - - - public void blitFramebuffer(int srcX0, int srcY0, int srcX1, int srcY1, - int dstX0, int dstY0, int dstX1, int dstY1, - int mask, int filter) { - GL30.glBlitFramebuffer(srcX0, srcY0, srcX1, srcY1, - dstX0, dstY0, dstX1, dstY1, mask, filter); - } - - - public void framebufferTexture2D(int target, int attachment, int texTarget, - int texId, int level) { - GL30.glFramebufferTexture2D(target, attachment, texTarget, texId, level); - } - - - public void bindRenderbuffer(int target, int id) { - GL30.glBindRenderbuffer(target, id); - } - - - public void renderbufferStorageMultisample(int target, int samples, - int format, int width, int height){ - GL30.glRenderbufferStorageMultisample(target, samples, format, - width, height); - } - - - public void renderbufferStorage(int target, int format, - int width, int height) { - GL30.glRenderbufferStorage(target, format, width, height); - } - - - public void framebufferRenderbuffer(int target, int attachment, - int rendbufTarget, int rendbufId) { - GL30.glFramebufferRenderbuffer(target, attachment, rendbufTarget, rendbufId); - } - - - public int checkFramebufferStatus(int target) { - return GL30.glCheckFramebufferStatus(target); - } - - - /////////////////////////////////////////////////////////// - - // Shaders - - - public int createProgram() { - return GL20.glCreateProgram(); - } - - - public void deleteProgram(int id) { - GL20.glDeleteProgram(id); - } - - - public int createShader(int type) { - return GL20.glCreateShader(type); - } - - - public void deleteShader(int id) { - GL20.glDeleteShader(id); - } - - - public void linkProgram(int prog) { - GL20.glLinkProgram(prog); - } - - - public void validateProgram(int prog) { - GL20.glValidateProgram(prog); - } - - - public void useProgram(int prog) { - GL20.glUseProgram(prog); - } - - - public int getAttribLocation(int prog, String name) { - return GL20.glGetAttribLocation(prog, name); - } - - - public int getUniformLocation(int prog, String name) { - return GL20.glGetUniformLocation(prog, name); - } - - - public void uniform1i(int loc, int value) { - GL20.glUniform1i(loc, value); - } - - - public void uniform2i(int loc, int value0, int value1) { - GL20.glUniform2i(loc, value0, value1); - } - - - public void uniform3i(int loc, int value0, int value1, int value2) { - GL20.glUniform3i(loc, value0, value1, value2); - } - - - public void uniform4i(int loc, int value0, int value1, int value2, - int value3) { - GL20.glUniform4i(loc, value0, value1, value2, value3); - } - - - public void uniform1f(int loc, float value) { - GL20.glUniform1f(loc, value); - } - - - public void uniform2f(int loc, float value0, float value1) { - GL20.glUniform2f(loc, value0, value1); - } - - - public void uniform3f(int loc, float value0, float value1, float value2) { - GL20.glUniform3f(loc, value0, value1, value2); - } - - - public void uniform4f(int loc, float value0, float value1, float value2, - float value3) { - GL20.glUniform4f(loc, value0, value1, value2, value3); - } - - - public void uniform1iv(int loc, int count, IntBuffer v) { - v.limit(count); - GL20.glUniform1(loc, v); - v.clear(); - } - - - public void uniform2iv(int loc, int count, IntBuffer v) { - v.limit(2 * count); - GL20.glUniform2(loc, v); - v.clear(); - } - - - public void uniform3iv(int loc, int count, IntBuffer v) { - v.limit(3 * count); - GL20.glUniform3(loc, v); - v.clear(); - } - - - public void uniform4iv(int loc, int count, IntBuffer v) { - v.limit(4 * count); - GL20.glUniform4(loc, v); - v.clear(); - } - - - public void uniform1fv(int loc, int count, FloatBuffer v) { - v.limit(count); - GL20.glUniform1(loc, v); - v.clear(); - } - - - public void uniform2fv(int loc, int count, FloatBuffer v) { - v.limit(2 * count); - GL20.glUniform2(loc, v); - v.clear(); - } - - - public void uniform3fv(int loc, int count, FloatBuffer v) { - v.limit(3 * count); - GL20.glUniform3(loc, v); - v.clear(); - } - - - public void uniform4fv(int loc, int count, FloatBuffer v) { - v.limit(4 * count); - GL20.glUniform4(loc, v); - v.clear(); - } - - - public void uniformMatrix2fv(int loc, int count, boolean transpose, - FloatBuffer mat) { - mat.limit(4); - GL20.glUniformMatrix2(loc, transpose, mat); - mat.clear(); - } - - - public void uniformMatrix3fv(int loc, int count, boolean transpose, - FloatBuffer mat) { - mat.limit(9); - GL20.glUniformMatrix3(loc, transpose, mat); - mat.clear(); - } - - - public void uniformMatrix4fv(int loc, int count, boolean transpose, - FloatBuffer mat) { - mat.limit(16); - GL20.glUniformMatrix4(loc, transpose, mat); - mat.clear(); - } - - - public void vertexAttrib1f(int loc, float value) { - GL20.glVertexAttrib1f(loc, value); - } - - - public void vertexAttrib2f(int loc, float value0, float value1) { - GL20.glVertexAttrib2f(loc, value0, value1); - } - - - public void vertexAttrib3f(int loc, float value0, float value1, float value2){ - GL20.glVertexAttrib3f(loc, value0, value1, value2); - } - - - public void vertexAttrib4f(int loc, float value0, float value1, float value2, - float value3) { - GL20.glVertexAttrib4f(loc, value0, value1, value2, value3); - } - - - public void shaderSource(int id, String source) { - GL20.glShaderSource(id, source); - } - - - public void compileShader(int id) { - GL20.glCompileShader(id); - } - - - public void attachShader(int prog, int shader) { - GL20.glAttachShader(prog, shader); - } - - - public void getShaderiv(int shader, int pname, IntBuffer params) { - GL20.glGetShader(shader, pname, params); - } - - - public String getShaderInfoLog(int shader) { - int len = GL20.glGetShaderi(shader, GL20.GL_INFO_LOG_LENGTH); - return GL20.glGetShaderInfoLog(shader, len); - } - - - public void getProgramiv(int prog, int pname, IntBuffer params) { - GL20.glGetProgram(prog, pname, params); - } - - - public String getProgramInfoLog(int prog) { - int len = GL20.glGetProgrami(prog, GL20.GL_INFO_LOG_LENGTH); - return GL20.glGetProgramInfoLog(prog, len); - } - - - /////////////////////////////////////////////////////////// - - // Viewport - - - public void viewport(int x, int y, int width, int height) { - GL11.glViewport(x, y, width, height); - } - - - /////////////////////////////////////////////////////////// - - // Clipping (scissor test) - - - public void scissor(int x, int y, int w, int h) { - GL11.glScissor(x, y, w, h); - } - - - /////////////////////////////////////////////////////////// - - // Blending - - - public void blendEquation(int eq) { - GL14.glBlendEquation(eq); - } - - - public void blendFunc(int srcFactor, int dstFactor) { - GL11.glBlendFunc(srcFactor, dstFactor); - } - - - /////////////////////////////////////////////////////////// - - // Pixels - - - public void readBuffer(int buf) { - GL11.glReadBuffer(buf); - } - - - public void readPixels(int x, int y, int width, int height, int format, - int type, Buffer buffer) { - - GL11.glReadPixels(x, y, width, height, format, type, (IntBuffer)buffer); - } - - - public void drawBuffer(int buf) { - GL11.glDrawBuffer(buf); - } - - - public void clearDepth(float d) { - GL11.glClearDepth(d); - } - - - public void clearStencil(int s) { - GL11.glClearStencil(s); - } - - - public void colorMask(boolean wr, boolean wg, boolean wb, boolean wa) { - GL11.glColorMask(wr, wg, wb, wa); - } - - - public void clearColor(float r, float g, float b, float a) { - GL11.glClearColor(r, g, b, a); - } - - - public void clear(int mask) { - GL11.glClear(mask); - } -} diff --git a/java/libraries/lwjgl/src/processing/lwjgl/PGraphics2D.java b/java/libraries/lwjgl/src/processing/lwjgl/PGraphics2D.java index d1f41c691..501de2ba0 100644 --- a/java/libraries/lwjgl/src/processing/lwjgl/PGraphics2D.java +++ b/java/libraries/lwjgl/src/processing/lwjgl/PGraphics2D.java @@ -1,22 +1,10 @@ package processing.lwjgl; +import processing.opengl.PGL; +import processing.opengl.PGraphicsOpenGL; + public class PGraphics2D extends processing.opengl.PGraphics2D { - - public PGraphics2D() { - pgl = new PGL(this); - - if (tessellator == null) { - tessellator = new Tessellator(); - } - - intBuffer = PGL.allocateIntBuffer(2); - floatBuffer = PGL.allocateFloatBuffer(2); - viewport = PGL.allocateIntBuffer(4); - - inGeo = newInGeometry(IMMEDIATE); - tessGeo = newTessGeometry(IMMEDIATE); - texCache = newTexCache(); - - initialized = false; - } + protected PGL createPGL(PGraphicsOpenGL pg) { + return new PLWJGL(pg); + } } diff --git a/java/libraries/lwjgl/src/processing/lwjgl/PGraphics3D.java b/java/libraries/lwjgl/src/processing/lwjgl/PGraphics3D.java index 6efd89237..14bbc5c31 100644 --- a/java/libraries/lwjgl/src/processing/lwjgl/PGraphics3D.java +++ b/java/libraries/lwjgl/src/processing/lwjgl/PGraphics3D.java @@ -1,22 +1,10 @@ package processing.lwjgl; +import processing.opengl.PGL; +import processing.opengl.PGraphicsOpenGL; + public class PGraphics3D extends processing.opengl.PGraphics3D { - - public PGraphics3D() { - pgl = new PGL(this); - - if (tessellator == null) { - tessellator = new Tessellator(); - } - - intBuffer = PGL.allocateIntBuffer(2); - floatBuffer = PGL.allocateFloatBuffer(2); - viewport = PGL.allocateIntBuffer(4); - - inGeo = newInGeometry(IMMEDIATE); - tessGeo = newTessGeometry(IMMEDIATE); - texCache = newTexCache(); - - initialized = false; + protected PGL createPGL(PGraphicsOpenGL pg) { + return new PLWJGL(pg); } } diff --git a/java/libraries/lwjgl/src/processing/lwjgl/PGraphicsLWJGL.java b/java/libraries/lwjgl/src/processing/lwjgl/PGraphicsLWJGL.java index 8c79ec133..b34d4d2a3 100644 --- a/java/libraries/lwjgl/src/processing/lwjgl/PGraphicsLWJGL.java +++ b/java/libraries/lwjgl/src/processing/lwjgl/PGraphicsLWJGL.java @@ -22,6 +22,7 @@ package processing.lwjgl; +import processing.opengl.PGL; import processing.opengl.PGraphicsOpenGL; /** @@ -29,27 +30,7 @@ import processing.opengl.PGraphicsOpenGL; * */ public class PGraphicsLWJGL extends PGraphicsOpenGL { - - ////////////////////////////////////////////////////////////// - - // INIT/ALLOCATE/FINISH - - - public PGraphicsLWJGL() { - pgl = new PGL(this); - - if (tessellator == null) { - tessellator = new Tessellator(); - } - - intBuffer = PGL.allocateIntBuffer(2); - floatBuffer = PGL.allocateFloatBuffer(2); - viewport = PGL.allocateIntBuffer(4); - - inGeo = newInGeometry(IMMEDIATE); - tessGeo = newTessGeometry(IMMEDIATE); - texCache = newTexCache(); - - initialized = false; + protected PGL createPGL(PGraphicsOpenGL pg) { + return new PLWJGL(pg); } } diff --git a/java/libraries/lwjgl/src/processing/lwjgl/PLWJGL.java b/java/libraries/lwjgl/src/processing/lwjgl/PLWJGL.java new file mode 100644 index 000000000..3b3ef6aeb --- /dev/null +++ b/java/libraries/lwjgl/src/processing/lwjgl/PLWJGL.java @@ -0,0 +1,2004 @@ +/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + Part of the Processing project - http://processing.org + + Copyright (c) 2011-12 Ben Fry and Casey Reas + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General + Public License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA +*/ + +package processing.lwjgl; + +import java.awt.BorderLayout; +import java.awt.Canvas; +import java.awt.Color; +import java.awt.Font; +import java.awt.FontMetrics; +import java.awt.Graphics2D; +import java.awt.Shape; +import java.awt.font.FontRenderContext; +import java.awt.font.GlyphVector; +import java.awt.geom.PathIterator; +import java.nio.Buffer; + +import java.nio.ByteBuffer; +import java.nio.FloatBuffer; +import java.nio.IntBuffer; +import java.nio.ShortBuffer; + +import org.lwjgl.BufferUtils; +import org.lwjgl.LWJGLException; +import org.lwjgl.input.Keyboard; +import org.lwjgl.input.Mouse; +import org.lwjgl.opengl.ARBES2Compatibility; +import org.lwjgl.opengl.Display; +import org.lwjgl.opengl.DisplayMode; +import org.lwjgl.opengl.EXTFramebufferObject; +import org.lwjgl.opengl.EXTTextureFilterAnisotropic; +import org.lwjgl.opengl.GL11; +import org.lwjgl.opengl.GL12; +import org.lwjgl.opengl.GL13; +import org.lwjgl.opengl.GL14; +import org.lwjgl.opengl.GL15; +import org.lwjgl.opengl.GL20; +import org.lwjgl.opengl.GL30; +import org.lwjgl.opengl.GL31; +import org.lwjgl.util.glu.GLU; +import org.lwjgl.util.glu.GLUtessellator; +import org.lwjgl.util.glu.GLUtessellatorCallbackAdapter; +import org.lwjgl.opengl.PixelFormat; + +import processing.core.PApplet; +import processing.core.PConstants; +import processing.event.Event; +import processing.event.KeyEvent; +import processing.event.MouseEvent; +import processing.opengl.PGL; +import processing.opengl.PGraphicsOpenGL; + +/** + * Processing-OpenGL abstraction layer. LWJGL implementation. + * + * Some issues: + * http://lwjgl.org/forum/index.php/topic,4711.0.html + * http://www.java-gaming.org/topics/cannot-add-mouselistener-to-java-awt-canvas-with-lwjgl-on-windows/24650/view.html + * + */ +public class PLWJGL extends PGL { + // ........................................................ + + // Public members to access the underlying GL objects and canvas + + /** GLU interface **/ + public static GLU glu; + + /** The canvas where OpenGL rendering takes place */ + public static Canvas canvas; + + // ........................................................ + + // Event handling + + /** Poller threads to get the keyboard/mouse events from LWJGL */ + protected static KeyPoller keyPoller; + protected static MousePoller mousePoller; + + // ........................................................ + + // Utility buffers to copy projection/modelview matrices to GL + + protected FloatBuffer projMatrix; + protected FloatBuffer mvMatrix; + + // ........................................................ + + // Static initialization for some parameters that need to be different for + // LWJGL + + static { + MIN_DIRECT_BUFFER_SIZE = 16; + INDEX_TYPE = GL11.GL_UNSIGNED_SHORT; + } + + + /////////////////////////////////////////////////////////// + + // Initialization, finalization + + + public PLWJGL(PGraphicsOpenGL pg) { + super(pg); + if (glu == null) glu = new GLU(); + } + + + public Canvas getCanvas() { + return canvas; + } + + + protected void setFps(float fps) { + if (!setFps || targetFps != fps) { + if (60 < fps) { + // Disables v-sync + Display.setVSyncEnabled(false); + Display.sync((int)fps); + } else { + Display.setVSyncEnabled(true); + } + targetFps = currentFps = fps; + setFps = true; + } + } + + + protected void initSurface(int antialias) { + if (canvas != null) { + keyPoller.requestStop(); + mousePoller.requestStop(); + + try { + Display.setParent(null); + } catch (LWJGLException e) { + e.printStackTrace(); + } + Display.destroy(); + + pg.parent.remove(canvas); + } + + canvas = new Canvas(); + canvas.setFocusable(true); + canvas.requestFocus(); + canvas.setBackground(new Color(pg.backgroundColor, true)); + canvas.setBounds(0, 0, pg.parent.width, pg.parent.height); + + pg.parent.setLayout(new BorderLayout()); + pg.parent.add(canvas, BorderLayout.CENTER); + + try { + DisplayMode[] modes = Display.getAvailableDisplayModes(); + int bpp = 0; + for (int i = 0; i < modes.length; i++) { + bpp = PApplet.max(modes[i].getBitsPerPixel(), bpp); + } + + PixelFormat format; + if (USE_FBOLAYER_BY_DEFAULT) { + format = new PixelFormat(bpp, REQUESTED_ALPHA_BITS, + REQUESTED_DEPTH_BITS, + REQUESTED_STENCIL_BITS, 1); + reqNumSamples = qualityToSamples(antialias); + fboLayerRequested = true; + } else { + format = new PixelFormat(bpp, REQUESTED_ALPHA_BITS, + REQUESTED_DEPTH_BITS, + REQUESTED_STENCIL_BITS, antialias); + fboLayerRequested = false; + } + + Display.setDisplayMode(new DisplayMode(pg.parent.width, pg.parent.height)); + int argb = pg.backgroundColor; + float r = ((argb >> 16) & 0xff) / 255.0f; + float g = ((argb >> 8) & 0xff) / 255.0f; + float b = ((argb) & 0xff) / 255.0f; + Display.setInitialBackground(r, g, b); + Display.setParent(canvas); + Display.create(format); + + // Might be useful later to specify the context attributes. + // http://lwjgl.org/javadoc/org/lwjgl/opengl/ContextAttribs.html +// ContextAttribs contextAtrributes = new ContextAttribs(4, 0); +// contextAtrributes.withForwardCompatible(true); +// contextAtrributes.withProfileCore(true); +// Display.create(pixelFormat, contextAtrributes); + } catch (LWJGLException e) { + e.printStackTrace(); + } + + glContext = Display.getDrawable().hashCode(); + + registerListeners(); + + fboLayerCreated = false; + fboLayerInUse = false; + firstFrame = true; + setFps = false; + } + + + protected void reinitSurface() { } + + + protected void registerListeners() { + keyPoller = new KeyPoller(pg.parent); + keyPoller.start(); + + mousePoller = new MousePoller(pg.parent); + mousePoller.start(); + } + + + /////////////////////////////////////////////////////////// + + // Frame rendering + + + protected boolean canDraw() { + return pg.initialized && pg.parent.isDisplayable(); + } + + + protected void requestFocus() { } + + + protected void requestDraw() { + if (pg.initialized) { + glThread = Thread.currentThread(); + pg.parent.handleDraw(); + Display.update(); + } + } + + + protected void swapBuffers() { + try { + Display.swapBuffers(); + } catch (LWJGLException e) { + e.printStackTrace(); + } + } + + + @Override + protected void beginGL() { + if (projMatrix == null) { + projMatrix = allocateFloatBuffer(16); + } + GL11.glMatrixMode(GL11.GL_PROJECTION); + projMatrix.rewind(); + projMatrix.put(pg.projection.m00); + projMatrix.put(pg.projection.m10); + projMatrix.put(pg.projection.m20); + projMatrix.put(pg.projection.m30); + projMatrix.put(pg.projection.m01); + projMatrix.put(pg.projection.m11); + projMatrix.put(pg.projection.m21); + projMatrix.put(pg.projection.m31); + projMatrix.put(pg.projection.m02); + projMatrix.put(pg.projection.m12); + projMatrix.put(pg.projection.m22); + projMatrix.put(pg.projection.m32); + projMatrix.put(pg.projection.m03); + projMatrix.put(pg.projection.m13); + projMatrix.put(pg.projection.m23); + projMatrix.put(pg.projection.m33); + projMatrix.rewind(); + GL11.glLoadMatrix(projMatrix); + + if (mvMatrix == null) { + mvMatrix = allocateFloatBuffer(16); + } + GL11.glMatrixMode(GL11.GL_MODELVIEW); + mvMatrix.rewind(); + mvMatrix.put(pg.modelview.m00); + mvMatrix.put(pg.modelview.m10); + mvMatrix.put(pg.modelview.m20); + mvMatrix.put(pg.modelview.m30); + mvMatrix.put(pg.modelview.m01); + mvMatrix.put(pg.modelview.m11); + mvMatrix.put(pg.modelview.m21); + mvMatrix.put(pg.modelview.m31); + mvMatrix.put(pg.modelview.m02); + mvMatrix.put(pg.modelview.m12); + mvMatrix.put(pg.modelview.m22); + mvMatrix.put(pg.modelview.m32); + mvMatrix.put(pg.modelview.m03); + mvMatrix.put(pg.modelview.m13); + mvMatrix.put(pg.modelview.m23); + mvMatrix.put(pg.modelview.m33); + mvMatrix.rewind(); + GL11.glLoadMatrix(mvMatrix); + } + + + /////////////////////////////////////////////////////////// + + // Utility functions + + + protected static ByteBuffer allocateDirectByteBuffer(int size) { + return BufferUtils.createByteBuffer(size); + } + + + protected static ShortBuffer allocateDirectShortBuffer(int size) { + return BufferUtils.createShortBuffer(size); + } + + + protected static IntBuffer allocateDirectIntBuffer(int size) { + return BufferUtils.createIntBuffer(size); + } + + + protected static FloatBuffer allocateDirectFloatBuffer(int size) { + return BufferUtils.createFloatBuffer(size); + } + + + @Override + protected int getFontAscent(Object font) { + FontMetrics metrics = pg.parent.getFontMetrics((Font)font); + return metrics.getAscent(); + } + + + @Override + protected int getFontDescent(Object font) { + FontMetrics metrics = pg.parent.getFontMetrics((Font)font); + return metrics.getDescent(); + } + + + @Override + protected int getTextWidth(Object font, char buffer[], int start, int stop) { + // maybe should use one of the newer/fancier functions for this? + int length = stop - start; + FontMetrics metrics = pg.parent.getFontMetrics((Font)font); + return metrics.charsWidth(buffer, start, length); + } + + + @Override + protected Object getDerivedFont(Object font, float size) { + return ((Font)font).deriveFont(size); + } + + + /////////////////////////////////////////////////////////// + + // LWJGL event handling + + + protected class KeyPoller extends Thread { + protected PApplet parent; + protected boolean stopRequested; + protected boolean[] pressedKeys; + protected char[] charCheys; + + KeyPoller(PApplet parent) { + this.parent = parent; + stopRequested = false; + } + + @Override + public void run() { + pressedKeys = new boolean[256]; + charCheys = new char[256]; + Keyboard.enableRepeatEvents(true); + while (true) { + if (stopRequested) break; + + Keyboard.poll(); + while (Keyboard.next()) { + if (stopRequested) break; + + long millis = Keyboard.getEventNanoseconds() / 1000000L; + char keyChar = Keyboard.getEventCharacter(); + int keyCode = Keyboard.getEventKey(); + + if (keyCode >= pressedKeys.length) continue; + + int modifiers = 0; + if (Keyboard.isKeyDown(Keyboard.KEY_LSHIFT) || + Keyboard.isKeyDown(Keyboard.KEY_RSHIFT)) { + modifiers |= Event.SHIFT; + } + if (Keyboard.isKeyDown(Keyboard.KEY_LCONTROL) || + Keyboard.isKeyDown(Keyboard.KEY_RCONTROL)) { + modifiers |= Event.CTRL; + } + if (Keyboard.isKeyDown(Keyboard.KEY_LMETA) || + Keyboard.isKeyDown(Keyboard.KEY_RMETA)) { + modifiers |= Event.META; + } + if (Keyboard.isKeyDown(Keyboard.KEY_LMENU) || + Keyboard.isKeyDown(Keyboard.KEY_RMENU)) { + // LWJGL maps the menu key and the alt key to the same value. + modifiers |= Event.ALT; + } + + int keyPCode = LWJGLtoAWTCode(keyCode); + if (keyChar == 0) { + keyChar = PConstants.CODED; + } + + int action = 0; + if (Keyboard.getEventKeyState()) { + action = KeyEvent.PRESS; + KeyEvent ke = new KeyEvent(null, millis, + action, modifiers, + keyChar, keyPCode); + parent.postEvent(ke); + pressedKeys[keyCode] = true; + charCheys[keyCode] = keyChar; + keyPCode = 0; + action = KeyEvent.TYPE; + } else if (pressedKeys[keyCode]) { + keyChar = charCheys[keyCode]; + pressedKeys[keyCode] = false; + action = KeyEvent.RELEASE; + } + + KeyEvent ke = new KeyEvent(null, millis, + action, modifiers, + keyChar, keyPCode); + parent.postEvent(ke); + } + try { + Thread.sleep(10); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + } + + public void requestStop() { + stopRequested = true; + } + } + + + protected class MousePoller extends Thread { + protected PApplet parent; + protected boolean stopRequested; + protected boolean pressed; + protected boolean inside; + protected long startedClickTime; + protected int startedClickButton; + + MousePoller(PApplet parent) { + this.parent = parent; + stopRequested = false; + } + + @Override + public void run() { + while (true) { + if (stopRequested) break; + + Mouse.poll(); + while (Mouse.next()) { + if (stopRequested) break; + + long millis = Mouse.getEventNanoseconds() / 1000000L; + + int modifiers = 0; + if (Keyboard.isKeyDown(Keyboard.KEY_LSHIFT) || + Keyboard.isKeyDown(Keyboard.KEY_RSHIFT)) { + modifiers |= Event.SHIFT; + } + if (Keyboard.isKeyDown(Keyboard.KEY_LCONTROL) || + Keyboard.isKeyDown(Keyboard.KEY_RCONTROL)) { + modifiers |= Event.CTRL; + } + if (Keyboard.isKeyDown(Keyboard.KEY_LMETA) || + Keyboard.isKeyDown(Keyboard.KEY_RMETA)) { + modifiers |= Event.META; + } + if (Keyboard.isKeyDown(Keyboard.KEY_LMENU) || + Keyboard.isKeyDown(Keyboard.KEY_RMENU)) { + // LWJGL maps the menu key and the alt key to the same value. + modifiers |= Event.ALT; + } + + int x = Mouse.getX(); + int y = parent.height - Mouse.getY(); + int button = 0; + if (Mouse.isButtonDown(0)) { + button = PConstants.LEFT; + } else if (Mouse.isButtonDown(1)) { + button = PConstants.RIGHT; + } else if (Mouse.isButtonDown(2)) { + button = PConstants.CENTER; + } + + int action = 0; + if (button != 0) { + if (pressed) { + action = MouseEvent.DRAG; + } else { + action = MouseEvent.PRESS; + pressed = true; + } + } else if (pressed) { + action = MouseEvent.RELEASE; + pressed = false; + } else { + action = MouseEvent.MOVE; + } + + if (inside) { + if (!Mouse.isInsideWindow()) { + inside = false; + action = MouseEvent.EXIT; + } + } else { + if (Mouse.isInsideWindow()) { + inside = true; + action = MouseEvent.ENTER; + } + } + + int count = 0; + if (Mouse.getEventButtonState()) { + startedClickTime = millis; + startedClickButton = button; + } else { + if (action == MouseEvent.RELEASE) { + boolean clickDetected = millis - startedClickTime < 500; + if (clickDetected) { + // post a RELEASE event, in addition to the CLICK event. + MouseEvent me = new MouseEvent(null, millis, action, modifiers, + x, y, button, count); + parent.postEvent(me); + action = MouseEvent.CLICK; + count = 1; + } + } + } + + MouseEvent me = new MouseEvent(null, millis, action, modifiers, + x, y, button, count); + parent.postEvent(me); + } + try { + Thread.sleep(10); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + } + + public void requestStop() { + stopRequested = true; + } + } + + // To complete later... + // http://docs.oracle.com/javase/6/docs/api/java/awt/event/KeyEvent.html + // http://processing.org/reference/keyCode.html + protected int LWJGLtoAWTCode(int code) { + switch (code) { + case Keyboard.KEY_0: + return java.awt.event.KeyEvent.VK_0; + case Keyboard.KEY_1: + return java.awt.event.KeyEvent.VK_1; + case Keyboard.KEY_2: + return java.awt.event.KeyEvent.VK_2; + case Keyboard.KEY_3: + return java.awt.event.KeyEvent.VK_3; + case Keyboard.KEY_4: + return java.awt.event.KeyEvent.VK_4; + case Keyboard.KEY_5: + return java.awt.event.KeyEvent.VK_5; + case Keyboard.KEY_6: + return java.awt.event.KeyEvent.VK_6; + case Keyboard.KEY_7: + return java.awt.event.KeyEvent.VK_7; + case Keyboard.KEY_8: + return java.awt.event.KeyEvent.VK_8; + case Keyboard.KEY_9: + return java.awt.event.KeyEvent.VK_9; + case Keyboard.KEY_A: + return java.awt.event.KeyEvent.VK_A; + case Keyboard.KEY_B: + return java.awt.event.KeyEvent.VK_B; + case Keyboard.KEY_C: + return java.awt.event.KeyEvent.VK_C; + case Keyboard.KEY_D: + return java.awt.event.KeyEvent.VK_D; + case Keyboard.KEY_E: + return java.awt.event.KeyEvent.VK_E; + case Keyboard.KEY_F: + return java.awt.event.KeyEvent.VK_F; + case Keyboard.KEY_G: + return java.awt.event.KeyEvent.VK_G; + case Keyboard.KEY_H: + return java.awt.event.KeyEvent.VK_H; + case Keyboard.KEY_I: + return java.awt.event.KeyEvent.VK_I; + case Keyboard.KEY_J: + return java.awt.event.KeyEvent.VK_J; + case Keyboard.KEY_K: + return java.awt.event.KeyEvent.VK_K; + case Keyboard.KEY_L: + return java.awt.event.KeyEvent.VK_L; + case Keyboard.KEY_M: + return java.awt.event.KeyEvent.VK_M; + case Keyboard.KEY_N: + return java.awt.event.KeyEvent.VK_N; + case Keyboard.KEY_O: + return java.awt.event.KeyEvent.VK_O; + case Keyboard.KEY_P: + return java.awt.event.KeyEvent.VK_P; + case Keyboard.KEY_Q: + return java.awt.event.KeyEvent.VK_Q; + case Keyboard.KEY_R: + return java.awt.event.KeyEvent.VK_R; + case Keyboard.KEY_S: + return java.awt.event.KeyEvent.VK_S; + case Keyboard.KEY_T: + return java.awt.event.KeyEvent.VK_T; + case Keyboard.KEY_U: + return java.awt.event.KeyEvent.VK_U; + case Keyboard.KEY_V: + return java.awt.event.KeyEvent.VK_V; + case Keyboard.KEY_W: + return java.awt.event.KeyEvent.VK_W; + case Keyboard.KEY_X: + return java.awt.event.KeyEvent.VK_X; + case Keyboard.KEY_Y: + return java.awt.event.KeyEvent.VK_Y; + case Keyboard.KEY_Z: + return java.awt.event.KeyEvent.VK_Z; + case Keyboard.KEY_ADD: + return java.awt.event.KeyEvent.VK_ADD; + case Keyboard.KEY_APOSTROPHE: + return java.awt.event.KeyEvent.VK_ASTERISK; + case Keyboard.KEY_AT: + return java.awt.event.KeyEvent.VK_AT; + case Keyboard.KEY_BACK: + return java.awt.event.KeyEvent.VK_BACK_SPACE; + case Keyboard.KEY_BACKSLASH: + return java.awt.event.KeyEvent.VK_BACK_SLASH; + case Keyboard.KEY_CAPITAL: + return java.awt.event.KeyEvent.VK_CAPS_LOCK; + case Keyboard.KEY_CIRCUMFLEX: + return java.awt.event.KeyEvent.VK_CIRCUMFLEX; + case Keyboard.KEY_COLON: + return java.awt.event.KeyEvent.VK_COLON; + case Keyboard.KEY_COMMA: + return java.awt.event.KeyEvent.VK_COMMA; + case Keyboard.KEY_CONVERT: + return java.awt.event.KeyEvent.VK_CONVERT; + case Keyboard.KEY_DECIMAL: + return java.awt.event.KeyEvent.VK_DECIMAL; + case Keyboard.KEY_DELETE: + return java.awt.event.KeyEvent.VK_DELETE; + case Keyboard.KEY_DIVIDE: + return java.awt.event.KeyEvent.VK_DIVIDE; + case Keyboard.KEY_DOWN: + return java.awt.event.KeyEvent.VK_DOWN; + case Keyboard.KEY_END: + return java.awt.event.KeyEvent.VK_END; + case Keyboard.KEY_EQUALS: + return java.awt.event.KeyEvent.VK_EQUALS; + case Keyboard.KEY_ESCAPE: + return java.awt.event.KeyEvent.VK_ESCAPE; + case Keyboard.KEY_F1: + return java.awt.event.KeyEvent.VK_F1; + case Keyboard.KEY_F10: + return java.awt.event.KeyEvent.VK_F10; + case Keyboard.KEY_F11: + return java.awt.event.KeyEvent.VK_F11; + case Keyboard.KEY_F12: + return java.awt.event.KeyEvent.VK_F12; + case Keyboard.KEY_F13: + return java.awt.event.KeyEvent.VK_F13; + case Keyboard.KEY_F14: + return java.awt.event.KeyEvent.VK_F14; + case Keyboard.KEY_F15: + return java.awt.event.KeyEvent.VK_F15; + case Keyboard.KEY_F2: + return java.awt.event.KeyEvent.VK_F2; + case Keyboard.KEY_F3: + return java.awt.event.KeyEvent.VK_F3; + case Keyboard.KEY_F4: + return java.awt.event.KeyEvent.VK_F4; + case Keyboard.KEY_F5: + return java.awt.event.KeyEvent.VK_F5; + case Keyboard.KEY_F6: + return java.awt.event.KeyEvent.VK_F6; + case Keyboard.KEY_F7: + return java.awt.event.KeyEvent.VK_F7; + case Keyboard.KEY_F8: + return java.awt.event.KeyEvent.VK_F8; + case Keyboard.KEY_F9: + return java.awt.event.KeyEvent.VK_F9; +// case Keyboard.KEY_GRAVE: + case Keyboard.KEY_HOME: + return java.awt.event.KeyEvent.VK_HOME; + case Keyboard.KEY_INSERT: + return java.awt.event.KeyEvent.VK_INSERT; + case Keyboard.KEY_LBRACKET: + return java.awt.event.KeyEvent.VK_BRACELEFT; + case Keyboard.KEY_LCONTROL: + return java.awt.event.KeyEvent.VK_CONTROL; + case Keyboard.KEY_LEFT: + return java.awt.event.KeyEvent.VK_LEFT; + case Keyboard.KEY_LMENU: + return java.awt.event.KeyEvent.VK_ALT; + case Keyboard.KEY_LMETA: + return java.awt.event.KeyEvent.VK_META; + case Keyboard.KEY_LSHIFT: + return java.awt.event.KeyEvent.VK_SHIFT; + case Keyboard.KEY_MINUS: + return java.awt.event.KeyEvent.VK_MINUS; + case Keyboard.KEY_MULTIPLY: + return java.awt.event.KeyEvent.VK_MULTIPLY; +// case Keyboard.KEY_NEXT: + case Keyboard.KEY_NUMLOCK: + return java.awt.event.KeyEvent.VK_NUM_LOCK; + case Keyboard.KEY_NUMPAD0: + return java.awt.event.KeyEvent.VK_NUMPAD0; + case Keyboard.KEY_NUMPAD1: + return java.awt.event.KeyEvent.VK_NUMPAD1; + case Keyboard.KEY_NUMPAD2: + return java.awt.event.KeyEvent.VK_NUMPAD2; + case Keyboard.KEY_NUMPAD3: + return java.awt.event.KeyEvent.VK_NUMPAD3; + case Keyboard.KEY_NUMPAD4: + return java.awt.event.KeyEvent.VK_NUMPAD4; + case Keyboard.KEY_NUMPAD5: + return java.awt.event.KeyEvent.VK_NUMPAD5; + case Keyboard.KEY_NUMPAD6: + return java.awt.event.KeyEvent.VK_NUMPAD6; + case Keyboard.KEY_NUMPAD7: + return java.awt.event.KeyEvent.VK_NUMPAD7; + case Keyboard.KEY_NUMPAD8: + return java.awt.event.KeyEvent.VK_NUMPAD8; + case Keyboard.KEY_NUMPAD9: + return java.awt.event.KeyEvent.VK_NUMPAD9; +// case Keyboard.KEY_NUMPADCOMMA: +// case Keyboard.KEY_NUMPADENTER: +// case Keyboard.KEY_NUMPADEQUALS: + case Keyboard.KEY_PAUSE: + return java.awt.event.KeyEvent.VK_PAUSE; + case Keyboard.KEY_PERIOD: + return java.awt.event.KeyEvent.VK_PERIOD; +// case Keyboard.KEY_POWER: +// case Keyboard.KEY_PRIOR: + case Keyboard.KEY_RBRACKET: + return java.awt.event.KeyEvent.VK_BRACERIGHT; + case Keyboard.KEY_RCONTROL: + return java.awt.event.KeyEvent.VK_CONTROL; + case Keyboard.KEY_RETURN: + return java.awt.event.KeyEvent.VK_ENTER; + case Keyboard.KEY_RIGHT: + return java.awt.event.KeyEvent.VK_RIGHT; +// case Keyboard.KEY_RMENU: + case Keyboard.KEY_RMETA: + return java.awt.event.KeyEvent.VK_META; + case Keyboard.KEY_RSHIFT: + return java.awt.event.KeyEvent.VK_SHIFT; +// case Keyboard.KEY_SCROLL: + case Keyboard.KEY_SEMICOLON: + return java.awt.event.KeyEvent.VK_SEMICOLON; + case Keyboard.KEY_SLASH: + return java.awt.event.KeyEvent.VK_SLASH; +// case Keyboard.KEY_SLEEP: + case Keyboard.KEY_SPACE: + return java.awt.event.KeyEvent.VK_SPACE; + case Keyboard.KEY_STOP: + return java.awt.event.KeyEvent.VK_STOP; + case Keyboard.KEY_SUBTRACT: + return java.awt.event.KeyEvent.VK_SUBTRACT; + case Keyboard.KEY_TAB: + return java.awt.event.KeyEvent.VK_TAB; +// case Keyboard.KEY_UNDERLINE: + case Keyboard.KEY_UP: + return java.awt.event.KeyEvent.VK_UP; + default: + return 0; + } + } + + + /////////////////////////////////////////////////////////// + + // Tessellator interface + + + protected Tessellator createTessellator(TessellatorCallback callback) { + return new Tessellator(callback); + } + + + protected class Tessellator implements PGL.Tessellator { + protected GLUtessellator tess; + protected TessellatorCallback callback; + protected GLUCallback gluCallback; + + public Tessellator(TessellatorCallback callback) { + this.callback = callback; + tess = GLU.gluNewTess(); + gluCallback = new GLUCallback(); + + tess.gluTessCallback(GLU.GLU_TESS_BEGIN, gluCallback); + tess.gluTessCallback(GLU.GLU_TESS_END, gluCallback); + tess.gluTessCallback(GLU.GLU_TESS_VERTEX, gluCallback); + tess.gluTessCallback(GLU.GLU_TESS_COMBINE, gluCallback); + tess.gluTessCallback(GLU.GLU_TESS_ERROR, gluCallback); + } + + public void beginPolygon() { + tess.gluTessBeginPolygon(null); + } + + public void endPolygon() { + tess.gluTessEndPolygon(); + } + + public void setWindingRule(int rule) { + tess.gluTessProperty(GLU.GLU_TESS_WINDING_RULE, rule); + } + + public void beginContour() { + tess.gluTessBeginContour(); + } + + public void endContour() { + tess.gluTessEndContour(); + } + + public void addVertex(double[] v) { + tess.gluTessVertex(v, 0, v); + } + + protected class GLUCallback extends GLUtessellatorCallbackAdapter { + @Override + public void begin(int type) { + callback.begin(type); + } + + @Override + public void end() { + callback.end(); + } + + @Override + public void vertex(Object data) { + callback.vertex(data); + } + + @Override + public void combine(double[] coords, Object[] data, + float[] weight, Object[] outData) { + callback.combine(coords, data, weight, outData); + } + + @Override + public void error(int errnum) { + callback.error(errnum); + } + } + } + + + protected String tessError(int err) { + return GLU.gluErrorString(err); + } + + + /////////////////////////////////////////////////////////// + + // Font outline + + + static { + SHAPE_TEXT_SUPPORTED = true; + SEG_MOVETO = PathIterator.SEG_MOVETO; + SEG_LINETO = PathIterator.SEG_LINETO; + SEG_QUADTO = PathIterator.SEG_QUADTO; + SEG_CUBICTO = PathIterator.SEG_CUBICTO; + SEG_CLOSE = PathIterator.SEG_CLOSE; + } + + + protected FontOutline createFontOutline(char ch, Object font) { + return new FontOutline(ch, font); + } + + + protected class FontOutline implements PGL.FontOutline { + PathIterator iter; + + public FontOutline(char ch, Object font) { + char textArray[] = new char[] { ch }; + Graphics2D graphics = (Graphics2D) pg.parent.getGraphics(); + FontRenderContext frc = graphics.getFontRenderContext(); + GlyphVector gv = ((Font)font).createGlyphVector(frc, textArray); + Shape shp = gv.getOutline(); + iter = shp.getPathIterator(null); + } + + public boolean isDone() { + return iter.isDone(); + } + + public int currentSegment(float coords[]) { + return iter.currentSegment(coords); + } + + public void next() { + iter.next(); + } + } + + + /////////////////////////////////////////////////////////// + + // Constants + + static { + FALSE = GL11.GL_FALSE; + TRUE = GL11.GL_TRUE; + + INT = GL11.GL_INT; + BYTE = GL11.GL_BYTE; + SHORT = GL11.GL_SHORT; + FLOAT = GL11.GL_FLOAT; + BOOL = GL20.GL_BOOL; + UNSIGNED_INT = GL11.GL_UNSIGNED_INT; + UNSIGNED_BYTE = GL11.GL_UNSIGNED_BYTE; + UNSIGNED_SHORT = GL11.GL_UNSIGNED_SHORT; + + RGB = GL11.GL_RGB; + RGBA = GL11.GL_RGBA; + ALPHA = GL11.GL_ALPHA; + LUMINANCE = GL11.GL_LUMINANCE; + LUMINANCE_ALPHA = GL11.GL_LUMINANCE_ALPHA; + + UNSIGNED_SHORT_5_6_5 = GL12.GL_UNSIGNED_SHORT_5_6_5; + UNSIGNED_SHORT_4_4_4_4 = GL12.GL_UNSIGNED_SHORT_4_4_4_4; + UNSIGNED_SHORT_5_5_5_1 = GL12.GL_UNSIGNED_SHORT_5_5_5_1; + + RGBA4 = GL11.GL_RGBA4; + RGB5_A1 = GL11.GL_RGB5_A1; + RGB565 = ARBES2Compatibility.GL_RGB565; + RGB8 = GL11.GL_RGB8; + RGBA8 = GL11.GL_RGBA8; + ALPHA8 = GL11.GL_ALPHA8; + + READ_ONLY = GL15.GL_READ_ONLY; + WRITE_ONLY = GL15.GL_WRITE_ONLY; + READ_WRITE = GL15.GL_READ_WRITE; + + TESS_WINDING_NONZERO = GLU.GLU_TESS_WINDING_NONZERO; + TESS_WINDING_ODD = GLU.GLU_TESS_WINDING_ODD; + + GENERATE_MIPMAP_HINT = GL14.GL_GENERATE_MIPMAP_HINT; + FASTEST = GL11.GL_FASTEST; + NICEST = GL11.GL_NICEST; + DONT_CARE = GL11.GL_DONT_CARE; + + VENDOR = GL11.GL_VENDOR; + RENDERER = GL11.GL_RENDERER; + VERSION = GL11.GL_VERSION; + EXTENSIONS = GL11.GL_EXTENSIONS; + SHADING_LANGUAGE_VERSION = GL20.GL_SHADING_LANGUAGE_VERSION; + + MAX_SAMPLES = GL30.GL_MAX_SAMPLES; + SAMPLES = GL13.GL_SAMPLES; + + ALIASED_LINE_WIDTH_RANGE = GL12.GL_ALIASED_LINE_WIDTH_RANGE; + ALIASED_POINT_SIZE_RANGE = GL12.GL_ALIASED_POINT_SIZE_RANGE; + + DEPTH_BITS = GL11.GL_DEPTH_BITS; + STENCIL_BITS = GL11.GL_STENCIL_BITS; + + CCW = GL11.GL_CCW; + CW = GL11.GL_CW; + + VIEWPORT = GL11.GL_VIEWPORT; + + ARRAY_BUFFER = GL15.GL_ARRAY_BUFFER; + ELEMENT_ARRAY_BUFFER = GL15.GL_ELEMENT_ARRAY_BUFFER; + + MAX_VERTEX_ATTRIBS = GL20.GL_MAX_VERTEX_ATTRIBS; + + STATIC_DRAW = GL15.GL_STATIC_DRAW; + DYNAMIC_DRAW = GL15.GL_DYNAMIC_DRAW; + STREAM_DRAW = GL15.GL_STREAM_DRAW; + + BUFFER_SIZE = GL15.GL_BUFFER_SIZE; + BUFFER_USAGE = GL15.GL_BUFFER_USAGE; + + POINTS = GL11.GL_POINTS; + LINE_STRIP = GL11.GL_LINE_STRIP; + LINE_LOOP = GL11.GL_LINE_LOOP; + LINES = GL11.GL_LINES; + TRIANGLE_FAN = GL11.GL_TRIANGLE_FAN; + TRIANGLE_STRIP = GL11.GL_TRIANGLE_STRIP; + TRIANGLES = GL11.GL_TRIANGLES; + + CULL_FACE = GL11.GL_CULL_FACE; + FRONT = GL11.GL_FRONT; + BACK = GL11.GL_BACK; + FRONT_AND_BACK = GL11.GL_FRONT_AND_BACK; + + POLYGON_OFFSET_FILL = GL11.GL_POLYGON_OFFSET_FILL; + + UNPACK_ALIGNMENT = GL11.GL_UNPACK_ALIGNMENT; + PACK_ALIGNMENT = GL11.GL_PACK_ALIGNMENT; + + TEXTURE_2D = GL11.GL_TEXTURE_2D; + TEXTURE_RECTANGLE = GL31.GL_TEXTURE_RECTANGLE; + + TEXTURE_BINDING_2D = GL11.GL_TEXTURE_BINDING_2D; + TEXTURE_BINDING_RECTANGLE = GL31.GL_TEXTURE_BINDING_RECTANGLE; + + MAX_TEXTURE_SIZE = GL11.GL_MAX_TEXTURE_SIZE; + TEXTURE_MAX_ANISOTROPY = EXTTextureFilterAnisotropic.GL_TEXTURE_MAX_ANISOTROPY_EXT; + MAX_TEXTURE_MAX_ANISOTROPY = EXTTextureFilterAnisotropic.GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT; + + MAX_VERTEX_TEXTURE_IMAGE_UNITS = GL20.GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS; + MAX_TEXTURE_IMAGE_UNITS = GL20.GL_MAX_TEXTURE_IMAGE_UNITS; + MAX_COMBINED_TEXTURE_IMAGE_UNITS = GL20.GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS; + + NUM_COMPRESSED_TEXTURE_FORMATS = GL13.GL_NUM_COMPRESSED_TEXTURE_FORMATS; + COMPRESSED_TEXTURE_FORMATS = GL13.GL_COMPRESSED_TEXTURE_FORMATS; + + NEAREST = GL11.GL_NEAREST; + LINEAR = GL11.GL_LINEAR; + LINEAR_MIPMAP_NEAREST = GL11.GL_LINEAR_MIPMAP_NEAREST; + LINEAR_MIPMAP_LINEAR = GL11.GL_LINEAR_MIPMAP_LINEAR; + + CLAMP_TO_EDGE = GL12.GL_CLAMP_TO_EDGE; + REPEAT = GL11.GL_REPEAT; + + TEXTURE0 = GL13.GL_TEXTURE0; + TEXTURE1 = GL13.GL_TEXTURE1; + TEXTURE2 = GL13.GL_TEXTURE2; + TEXTURE3 = GL13.GL_TEXTURE3; + TEXTURE_MIN_FILTER = GL11.GL_TEXTURE_MIN_FILTER; + TEXTURE_MAG_FILTER = GL11.GL_TEXTURE_MAG_FILTER; + TEXTURE_WRAP_S = GL11.GL_TEXTURE_WRAP_S; + TEXTURE_WRAP_T = GL11.GL_TEXTURE_WRAP_T; + TEXTURE_WRAP_R = GL12.GL_TEXTURE_WRAP_R; + + TEXTURE_CUBE_MAP = GL13.GL_TEXTURE_CUBE_MAP; + TEXTURE_CUBE_MAP_POSITIVE_X = GL13.GL_TEXTURE_CUBE_MAP_POSITIVE_X; + TEXTURE_CUBE_MAP_POSITIVE_Y = GL13.GL_TEXTURE_CUBE_MAP_POSITIVE_Y; + TEXTURE_CUBE_MAP_POSITIVE_Z = GL13.GL_TEXTURE_CUBE_MAP_POSITIVE_Z; + TEXTURE_CUBE_MAP_NEGATIVE_X = GL13.GL_TEXTURE_CUBE_MAP_NEGATIVE_X; + TEXTURE_CUBE_MAP_NEGATIVE_Y = GL13.GL_TEXTURE_CUBE_MAP_NEGATIVE_Y; + TEXTURE_CUBE_MAP_NEGATIVE_Z = GL13.GL_TEXTURE_CUBE_MAP_NEGATIVE_Z; + + VERTEX_SHADER = GL20.GL_VERTEX_SHADER; + FRAGMENT_SHADER = GL20.GL_FRAGMENT_SHADER; + INFO_LOG_LENGTH = GL20.GL_INFO_LOG_LENGTH; + SHADER_SOURCE_LENGTH = GL20.GL_SHADER_SOURCE_LENGTH; + COMPILE_STATUS = GL20.GL_COMPILE_STATUS; + LINK_STATUS = GL20.GL_LINK_STATUS; + VALIDATE_STATUS = GL20.GL_VALIDATE_STATUS; + SHADER_TYPE = GL20.GL_SHADER_TYPE; + DELETE_STATUS = GL20.GL_DELETE_STATUS; + + FLOAT_VEC2 = GL20.GL_FLOAT_VEC2; + FLOAT_VEC3 = GL20.GL_FLOAT_VEC3; + FLOAT_VEC4 = GL20.GL_FLOAT_VEC4; + FLOAT_MAT2 = GL20.GL_FLOAT_MAT2; + FLOAT_MAT3 = GL20.GL_FLOAT_MAT3; + FLOAT_MAT4 = GL20.GL_FLOAT_MAT4; + INT_VEC2 = GL20.GL_INT_VEC2; + INT_VEC3 = GL20.GL_INT_VEC3; + INT_VEC4 = GL20.GL_INT_VEC4; + BOOL_VEC2 = GL20.GL_BOOL_VEC2; + BOOL_VEC3 = GL20.GL_BOOL_VEC3; + BOOL_VEC4 = GL20.GL_BOOL_VEC4; + SAMPLER_2D = GL20.GL_SAMPLER_2D; + SAMPLER_CUBE = GL20.GL_SAMPLER_CUBE; + + LOW_FLOAT = ARBES2Compatibility.GL_LOW_FLOAT; + MEDIUM_FLOAT = ARBES2Compatibility.GL_MEDIUM_FLOAT; + HIGH_FLOAT = ARBES2Compatibility.GL_HIGH_FLOAT; + LOW_INT = ARBES2Compatibility.GL_LOW_INT; + MEDIUM_INT = ARBES2Compatibility.GL_MEDIUM_INT; + HIGH_INT = ARBES2Compatibility.GL_HIGH_INT; + + CURRENT_VERTEX_ATTRIB = GL20.GL_CURRENT_VERTEX_ATTRIB; + + VERTEX_ATTRIB_ARRAY_BUFFER_BINDING = GL15.GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING; + VERTEX_ATTRIB_ARRAY_ENABLED = GL20.GL_VERTEX_ATTRIB_ARRAY_ENABLED; + VERTEX_ATTRIB_ARRAY_SIZE = GL20.GL_VERTEX_ATTRIB_ARRAY_SIZE; + VERTEX_ATTRIB_ARRAY_STRIDE = GL20.GL_VERTEX_ATTRIB_ARRAY_STRIDE; + VERTEX_ATTRIB_ARRAY_TYPE = GL20.GL_VERTEX_ATTRIB_ARRAY_TYPE; + VERTEX_ATTRIB_ARRAY_NORMALIZED = GL20.GL_VERTEX_ATTRIB_ARRAY_NORMALIZED; + VERTEX_ATTRIB_ARRAY_POINTER = GL20.GL_VERTEX_ATTRIB_ARRAY_POINTER; + + BLEND = GL11.GL_BLEND; + ONE = GL11.GL_ONE; + ZERO = GL11.GL_ZERO; + SRC_ALPHA = GL11.GL_SRC_ALPHA; + DST_ALPHA = GL11.GL_DST_ALPHA; + ONE_MINUS_SRC_ALPHA = GL11.GL_ONE_MINUS_SRC_ALPHA; + ONE_MINUS_DST_COLOR = GL11.GL_ONE_MINUS_DST_COLOR; + ONE_MINUS_SRC_COLOR = GL11.GL_ONE_MINUS_SRC_COLOR; + DST_COLOR = GL11.GL_DST_COLOR; + SRC_COLOR = GL11.GL_SRC_COLOR; + + SAMPLE_ALPHA_TO_COVERAGE = GL13.GL_SAMPLE_ALPHA_TO_COVERAGE; + SAMPLE_COVERAGE = GL13.GL_SAMPLE_COVERAGE; + + KEEP = GL11.GL_KEEP; + REPLACE = GL11.GL_REPLACE; + INCR = GL11.GL_INCR; + DECR = GL11.GL_DECR; + INVERT = GL11.GL_INVERT; + INCR_WRAP = GL14.GL_INCR_WRAP; + DECR_WRAP = GL14.GL_DECR_WRAP; + NEVER = GL11.GL_NEVER; + ALWAYS = GL11.GL_ALWAYS; + + EQUAL = GL11.GL_EQUAL; + LESS = GL11.GL_LESS; + LEQUAL = GL11.GL_LEQUAL; + GREATER = GL11.GL_GREATER; + GEQUAL = GL11.GL_GEQUAL; + NOTEQUAL = GL11.GL_NOTEQUAL; + + FUNC_ADD = GL14.GL_FUNC_ADD; + FUNC_MIN = GL14.GL_MIN; + FUNC_MAX = GL14.GL_MAX; + FUNC_REVERSE_SUBTRACT = GL14.GL_FUNC_REVERSE_SUBTRACT; + FUNC_SUBTRACT = GL14.GL_FUNC_SUBTRACT; + + DITHER = GL11.GL_DITHER; + + CONSTANT_COLOR = GL11.GL_CONSTANT_COLOR; + CONSTANT_ALPHA = GL11.GL_CONSTANT_ALPHA; + ONE_MINUS_CONSTANT_COLOR = GL11.GL_ONE_MINUS_CONSTANT_COLOR; + ONE_MINUS_CONSTANT_ALPHA = GL11.GL_ONE_MINUS_CONSTANT_ALPHA; + SRC_ALPHA_SATURATE = GL11.GL_SRC_ALPHA_SATURATE; + + SCISSOR_TEST = GL11.GL_SCISSOR_TEST; + STENCIL_TEST = GL11.GL_STENCIL_TEST; + DEPTH_TEST = GL11.GL_DEPTH_TEST; + DEPTH_WRITEMASK = GL11.GL_DEPTH_WRITEMASK; + ALPHA_TEST = GL11.GL_ALPHA_TEST; + + COLOR_BUFFER_BIT = GL11.GL_COLOR_BUFFER_BIT; + DEPTH_BUFFER_BIT = GL11.GL_DEPTH_BUFFER_BIT; + STENCIL_BUFFER_BIT = GL11.GL_STENCIL_BUFFER_BIT; + + FRAMEBUFFER = GL30.GL_FRAMEBUFFER; + COLOR_ATTACHMENT0 = GL30.GL_COLOR_ATTACHMENT0; + COLOR_ATTACHMENT1 = GL30.GL_COLOR_ATTACHMENT1; + COLOR_ATTACHMENT2 = GL30.GL_COLOR_ATTACHMENT2; + COLOR_ATTACHMENT3 = GL30.GL_COLOR_ATTACHMENT3; + RENDERBUFFER = GL30.GL_RENDERBUFFER; + DEPTH_ATTACHMENT = GL30.GL_DEPTH_ATTACHMENT; + STENCIL_ATTACHMENT = GL30.GL_STENCIL_ATTACHMENT; + READ_FRAMEBUFFER = GL30.GL_READ_FRAMEBUFFER; + DRAW_FRAMEBUFFER = GL30.GL_DRAW_FRAMEBUFFER; + + DEPTH24_STENCIL8 = GL30.GL_DEPTH24_STENCIL8; + + DEPTH_COMPONENT = GL11.GL_DEPTH_COMPONENT; + DEPTH_COMPONENT16 = GL14.GL_DEPTH_COMPONENT16; + DEPTH_COMPONENT24 = GL14.GL_DEPTH_COMPONENT24; + DEPTH_COMPONENT32 = GL14.GL_DEPTH_COMPONENT32; + + STENCIL_INDEX = GL11.GL_STENCIL_INDEX; + STENCIL_INDEX1 = GL30.GL_STENCIL_INDEX1; + STENCIL_INDEX4 = GL30.GL_STENCIL_INDEX4; + STENCIL_INDEX8 = GL30.GL_STENCIL_INDEX8; + + DEPTH_STENCIL = GL30.GL_DEPTH_STENCIL; + + FRAMEBUFFER_COMPLETE = GL30.GL_FRAMEBUFFER_COMPLETE; + FRAMEBUFFER_INCOMPLETE_ATTACHMENT = GL30.GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT; + FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT = GL30.GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT; + FRAMEBUFFER_INCOMPLETE_DIMENSIONS = EXTFramebufferObject.GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT; + FRAMEBUFFER_INCOMPLETE_FORMATS = EXTFramebufferObject.GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT; + FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER = GL30.GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER; + FRAMEBUFFER_INCOMPLETE_READ_BUFFER = GL30.GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER; + FRAMEBUFFER_UNSUPPORTED = GL30.GL_FRAMEBUFFER_UNSUPPORTED; + + FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE = GL30.GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE; + FRAMEBUFFER_ATTACHMENT_OBJECT_NAME = GL30.GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME; + FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL = GL30.GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL; + FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE = GL30.GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE; + + RENDERBUFFER_WIDTH = GL30.GL_RENDERBUFFER_WIDTH; + RENDERBUFFER_HEIGHT = GL30.GL_RENDERBUFFER_HEIGHT; + RENDERBUFFER_RED_SIZE = GL30.GL_RENDERBUFFER_RED_SIZE; + RENDERBUFFER_GREEN_SIZE = GL30.GL_RENDERBUFFER_GREEN_SIZE; + RENDERBUFFER_BLUE_SIZE = GL30.GL_RENDERBUFFER_BLUE_SIZE; + RENDERBUFFER_ALPHA_SIZE = GL30.GL_RENDERBUFFER_ALPHA_SIZE; + RENDERBUFFER_DEPTH_SIZE = GL30.GL_RENDERBUFFER_DEPTH_SIZE; + RENDERBUFFER_STENCIL_SIZE = GL30.GL_RENDERBUFFER_STENCIL_SIZE; + RENDERBUFFER_INTERNAL_FORMAT = GL30.GL_RENDERBUFFER_INTERNAL_FORMAT; + + MULTISAMPLE = GL13.GL_MULTISAMPLE; + POINT_SMOOTH = GL11.GL_POINT_SMOOTH; + LINE_SMOOTH = GL11.GL_LINE_SMOOTH; + POLYGON_SMOOTH = GL11.GL_POLYGON_SMOOTH; + } + + /////////////////////////////////////////////////////////// + + // Special Functions + + public void flush() { + GL11.glFlush(); + } + + public void finish() { + GL11.glFinish(); + } + + public void hint(int target, int hint) { + GL11.glHint(target, hint); + } + + /////////////////////////////////////////////////////////// + + // State and State Requests + + public void enable(int value) { + if (-1 < value) { + GL11.glEnable(value); + } + } + + public void disable(int value) { + if (-1 < value) { + GL11.glDisable(value); + } + } + + public void getBooleanv(int value, IntBuffer data) { + if (-1 < value) { + if (byteBuffer.capacity() < data.capacity()) { + byteBuffer = allocateDirectByteBuffer(data.capacity()); + } + GL11.glGetBoolean(value, byteBuffer); + for (int i = 0; i < data.capacity(); i++) { + data.put(i, byteBuffer.get(i)); + } + } else { + fillIntBuffer(data, 0, data.capacity() - 1, 0); + } + } + + public void getIntegerv(int value, IntBuffer data) { + if (-1 < value) { + GL11.glGetInteger(value, data); + } else { + fillIntBuffer(data, 0, data.capacity() - 1, 0); + } + } + + public void getFloatv(int value, FloatBuffer data) { + if (-1 < value) { + GL11.glGetFloat(value, data); + } else { + fillFloatBuffer(data, 0, data.capacity() - 1, 0); + } + } + + public boolean isEnabled(int value) { + return GL11.glIsEnabled(value); + } + + public String getString(int name) { + return GL11.glGetString(name); + } + + /////////////////////////////////////////////////////////// + + // Error Handling + + public int getError() { + return GL11.glGetError(); + } + + public String errorString(int err) { + return GLU.gluErrorString(err); + } + + ////////////////////////////////////////////////////////////////////////////// + + // Buffer Objects + + public void genBuffers(int n, IntBuffer buffers) { + GL15.glGenBuffers(buffers); + } + + public void deleteBuffers(int n, IntBuffer buffers) { + GL15.glDeleteBuffers(buffers); + } + + public void bindBuffer(int target, int buffer) { + GL15.glBindBuffer(target, buffer); + } + + public void bufferData(int target, int size, Buffer data, int usage) { + if (data == null) { + FloatBuffer empty = BufferUtils.createFloatBuffer(size); + GL15.glBufferData(target, empty, usage); + } else { + if (data instanceof ByteBuffer) { + GL15.glBufferData(target, (ByteBuffer)data, usage); + } else if (data instanceof ShortBuffer) { + GL15.glBufferData(target, (ShortBuffer)data, usage); + } else if (data instanceof IntBuffer) { + GL15.glBufferData(target, (IntBuffer)data, usage); + } else if (data instanceof FloatBuffer) { + GL15.glBufferData(target, (FloatBuffer)data, usage); + } + } + } + + public void bufferSubData(int target, int offset, int size, Buffer data) { + if (data instanceof ByteBuffer) { + GL15.glBufferSubData(target, offset, (ByteBuffer)data); + } else if (data instanceof ShortBuffer) { + GL15.glBufferSubData(target, offset, (ShortBuffer)data); + } else if (data instanceof IntBuffer) { + GL15.glBufferSubData(target, offset, (IntBuffer)data); + } else if (data instanceof FloatBuffer) { + GL15.glBufferSubData(target, offset, (FloatBuffer)data); + } + } + + public void isBuffer(int buffer) { + GL15.glIsBuffer(buffer); + } + + public void getBufferParameteriv(int target, int value, IntBuffer data) { + if (-1 < value) { + int res = GL15.glGetBufferParameteri(target, value); + data.put(0, res); + } else { + data.put(0, 0); + } + } + + public ByteBuffer mapBuffer(int target, int access) { + return GL15.glMapBuffer(target, access, null); + } + + public ByteBuffer mapBufferRange(int target, int offset, int length, int access) { + return GL30.glMapBufferRange(target, offset, length, access, null); + } + + public void unmapBuffer(int target) { + GL15.glUnmapBuffer(target); + } + + ////////////////////////////////////////////////////////////////////////////// + + // Viewport and Clipping + + public void depthRangef(float n, float f) { + GL11.glDepthRange(n, f); + } + + public void viewport(int x, int y, int w, int h) { + GL11.glViewport(x, y, w, h); + } + + ////////////////////////////////////////////////////////////////////////////// + + // Reading Pixels + + protected void readPixelsImpl(int x, int y, int width, int height, int format, int type, Buffer buffer) { + GL11.glReadPixels(x, y, width, height, format, type, (IntBuffer)buffer); + } + + ////////////////////////////////////////////////////////////////////////////// + + // Vertices + + public void vertexAttrib1f(int index, float value) { + GL20.glVertexAttrib1f(index, value); + } + + public void vertexAttrib2f(int index, float value0, float value1) { + GL20.glVertexAttrib2f(index, value0, value1); + } + + public void vertexAttrib3f(int index, float value0, float value1, float value2) { + GL20.glVertexAttrib3f(index, value0, value1, value2); + } + + public void vertexAttrib4f(int index, float value0, float value1, float value2, float value3) { + GL20.glVertexAttrib4f(index, value0, value1, value2, value3); + } + + public void vertexAttrib1fv(int index, FloatBuffer values) { + GL20.glVertexAttrib1f(index, values.get()); + } + + public void vertexAttrib2fv(int index, FloatBuffer values) { + GL20.glVertexAttrib2f(index, values.get(), values.get()); + } + + public void vertexAttrib3fv(int index, FloatBuffer values) { + GL20.glVertexAttrib3f(index, values.get(), values.get(), values.get()); + } + + public void vertexAttri4fv(int index, FloatBuffer values) { + GL20.glVertexAttrib4f(index, values.get(), values.get(), values.get(), values.get()); + } + + public void vertexAttribPointer(int index, int size, int type, boolean normalized, int stride, int offset) { + GL20.glVertexAttribPointer(index, size, type, normalized, stride, offset); + } + + public void vertexAttribPointer(int index, int size, int type, boolean normalized, int stride, Buffer data) { + if (type == UNSIGNED_INT) { + GL20.glVertexAttribPointer(index, size, true, normalized, stride, (IntBuffer)data); + } else if (type == UNSIGNED_BYTE) { + GL20.glVertexAttribPointer(index, size, true, normalized, stride, (ByteBuffer)data); + } else if (type == UNSIGNED_SHORT) { + GL20.glVertexAttribPointer(index, size, true, normalized, stride, (ShortBuffer)data); + } else if (type == FLOAT) { + GL20.glVertexAttribPointer(index, size, normalized, stride, (FloatBuffer)data); + } + } + + public void enableVertexAttribArray(int index) { + GL20.glEnableVertexAttribArray(index); + } + + public void disableVertexAttribArray(int index) { + GL20.glDisableVertexAttribArray(index); + } + + public void drawArrays(int mode, int first, int count) { + GL11.glDrawArrays(mode, first, count); + } + + public void drawElements(int mode, int count, int type, int offset) { + GL11.glDrawElements(mode, count, type, offset); + } + + public void drawElements(int mode, int count, int type, Buffer indices) { + if (type == UNSIGNED_INT) { + GL11.glDrawElements(mode, (IntBuffer)indices); + } else if (type == UNSIGNED_BYTE) { + GL11.glDrawElements(mode, (ByteBuffer)indices); + } else if (type == UNSIGNED_SHORT) { + GL11.glDrawElements(mode, (ShortBuffer)indices); + } + } + + ////////////////////////////////////////////////////////////////////////////// + + // Rasterization + + public void lineWidth(float width) { + GL11.glLineWidth(width); + } + + public void frontFace(int dir) { + GL11.glFrontFace(dir); + } + + public void cullFace(int mode) { + GL11.glCullFace(mode); + } + + public void polygonOffset(float factor, float units) { + GL11.glPolygonOffset(factor, units); + } + + ////////////////////////////////////////////////////////////////////////////// + + // Pixel Rectangles + + public void pixelStorei(int pname, int param) { + GL11.glPixelStorei(pname, param); + } + + /////////////////////////////////////////////////////////// + + // Texturing + + public void texImage2D(int target, int level, int internalFormat, int width, int height, int border, int format, int type, Buffer data) { + GL11.glTexImage2D(target, level, internalFormat, width, height, border, format, type, (IntBuffer)data); + } + + public void copyTexImage2D(int target, int level, int internalFormat, int x, int y, int width, int height, int border) { + GL11.glCopyTexImage2D(target, level, internalFormat, x, y, width, height, border); + } + + public void texSubImage2D(int target, int level, int xOffset, int yOffset, int width, int height, int format, int type, Buffer data) { + GL11.glTexSubImage2D(target, level, xOffset, yOffset, width, height, format, type, (IntBuffer)data); + } + + public void copyTexSubImage2D(int target, int level, int xOffset, int yOffset, int x, int y, int width, int height) { + GL11.glCopyTexSubImage2D(target, level, x, y, xOffset, xOffset, width, height); + } + + public void compressedTexImage2D(int target, int level, int internalFormat, int width, int height, int border, int imageSize, Buffer data) { + GL13.glCompressedTexImage2D(target, level, internalFormat, width, height, border, (ByteBuffer)data); + } + + public void compressedTexSubImage2D(int target, int level, int xOffset, int yOffset, int width, int height, int format, int imageSize, Buffer data) { + GL13.glCompressedTexSubImage2D(target, level, xOffset, yOffset, width, height, format, (ByteBuffer)data); + } + + public void texParameteri(int target, int pname, int param) { + GL11.glTexParameteri(target, pname, param); + } + + public void texParameterf(int target, int pname, float param) { + GL11.glTexParameterf(target, pname, param); + } + + public void texParameteriv(int target, int pname, IntBuffer params) { + GL11.glTexParameteri(target, pname, params.get()); + } + + public void texParameterfv(int target, int pname, FloatBuffer params) { + GL11.glTexParameterf(target, pname, params.get()); + } + + public void generateMipmap(int target) { + GL30.glGenerateMipmap(target); + } + + public void genTextures(int n, IntBuffer textures) { + GL11.glGenTextures(textures); + } + + public void deleteTextures(int n, IntBuffer textures) { + GL11.glDeleteTextures(textures); + } + + public void getTexParameteriv(int target, int pname, IntBuffer params) { + GL11.glGetTexParameter(target, pname, params); + } + + public void getTexParameterfv(int target, int pname, FloatBuffer params) { + GL11.glGetTexParameter(target, pname, params); + } + + public boolean isTexture(int texture) { + return GL11.glIsTexture(texture); + } + + protected void activeTextureImpl(int texture) { + GL13.glActiveTexture(texture); + } + + protected void bindTextureImpl(int target, int texture) { + GL11.glBindTexture(target, texture); + } + + /////////////////////////////////////////////////////////// + + // Shaders and Programs + + public int createShader(int type) { + return GL20.glCreateShader(type); + } + + public void shaderSource(int shader, String source) { + GL20.glShaderSource(shader, source); + } + + public void compileShader(int shader) { + GL20.glCompileShader(shader); + } + + public void releaseShaderCompiler() { + throw new RuntimeException(String.format(MISSING_GLFUNC_ERROR, "glReleaseShaderCompiler()")); + } + + public void deleteShader(int shader) { + GL20.glDeleteShader(shader); + } + + public void shaderBinary(int count, IntBuffer shaders, int binaryFormat, Buffer binary, int length) { + throw new RuntimeException(String.format(MISSING_GLFUNC_ERROR, "glShaderBinary()")); + } + + public int createProgram() { + return GL20.glCreateProgram(); + } + + public void attachShader(int program, int shader) { + GL20.glAttachShader(program, shader); + } + + public void detachShader(int program, int shader) { + GL20.glDetachShader(program, shader); + } + + public void linkProgram(int program) { + GL20.glLinkProgram(program); + } + + public void useProgram(int program) { + GL20.glUseProgram(program); + } + + public void deleteProgram(int program) { + GL20.glDeleteProgram(program); + } + + public String getActiveAttrib (int program, int index, IntBuffer size, IntBuffer type) { + IntBuffer typeTmp = BufferUtils.createIntBuffer(2); + String name = GL20.glGetActiveAttrib(program, index, 256, typeTmp); + size.put(typeTmp.get(0)); + type.put(typeTmp.get(1)); + return name; + } + + public int getAttribLocation(int program, String name) { + return GL20.glGetAttribLocation(program, name); + } + + public void bindAttribLocation(int program, int index, String name) { + GL20.glBindAttribLocation(program, index, name); + } + + public int getUniformLocation(int program, String name) { + return GL20.glGetUniformLocation(program, name); + } + + public String getActiveUniform(int program, int index, IntBuffer size, IntBuffer type) { + IntBuffer typeTmp = BufferUtils.createIntBuffer(2); + String name = GL20.glGetActiveUniform(program, index, 256, typeTmp); + type.put(typeTmp.get(0)); + return name; + } + + public void uniform1i(int location, int value) { + GL20.glUniform1i(location, value); + } + + public void uniform2i(int location, int value0, int value1) { + GL20.glUniform2i(location, value0, value1); + } + + public void uniform3i(int location, int value0, int value1, int value2) { + GL20.glUniform3i(location, value0, value1, value2); + } + + public void uniform4i(int location, int value0, int value1, int value2, int value3) { + GL20.glUniform4i(location, value0, value1, value2, value3); + } + + public void uniform1f(int location, float value) { + GL20.glUniform1f(location, value); + } + + public void uniform2f(int location, float value0, float value1) { + GL20.glUniform2f(location, value0, value1); + } + + public void uniform3f(int location, float value0, float value1, float value2) { + GL20.glUniform3f(location, value0, value1, value2); + } + + public void uniform4f(int location, float value0, float value1, float value2, float value3) { + GL20.glUniform4f(location, value0, value1, value2, value3); + } + + public void uniform1iv(int location, int count, IntBuffer v) { + v.limit(count); + GL20.glUniform1(location, v); + v.clear(); + } + + public void uniform2iv(int location, int count, IntBuffer v) { + v.limit(count); + GL20.glUniform2(location, v); + v.clear(); + } + + public void uniform3iv(int location, int count, IntBuffer v) { + v.limit(count); + GL20.glUniform3(location, v); + v.clear(); + } + + public void uniform4iv(int location, int count, IntBuffer v) { + v.limit(count); + GL20.glUniform4(location, v); + v.clear(); + } + + public void uniform1fv(int location, int count, FloatBuffer v) { + v.limit(count); + GL20.glUniform1(location, v); + v.clear(); + } + + public void uniform2fv(int location, int count, FloatBuffer v) { + v.limit(count); + GL20.glUniform2(location, v); + v.clear(); + } + + public void uniform3fv(int location, int count, FloatBuffer v) { + v.limit(count); + GL20.glUniform3(location, v); + v.clear(); + } + + public void uniform4fv(int location, int count, FloatBuffer v) { + v.limit(count); + GL20.glUniform4(location, v); + v.clear(); + } + + public void uniformMatrix2fv(int location, int count, boolean transpose, FloatBuffer mat) { + mat.limit(4); + GL20.glUniformMatrix2(location, transpose, mat); + mat.clear(); + } + + public void uniformMatrix3fv(int location, int count, boolean transpose, FloatBuffer mat) { + mat.limit(9); + GL20.glUniformMatrix3(location, transpose, mat); + mat.clear(); + } + + public void uniformMatrix4fv(int location, int count, boolean transpose, FloatBuffer mat) { + mat.limit(16); + GL20.glUniformMatrix4(location, transpose, mat); + mat.clear(); + } + + public void validateProgram(int program) { + GL20.glValidateProgram(program); + } + + public boolean isShader(int shader) { + return GL20.glIsShader(shader); + } + + public void getShaderiv(int shader, int pname, IntBuffer params) { + GL20.glGetShader(shader, pname, params); + } + + public void getAttachedShaders(int program, int maxCount, IntBuffer count, IntBuffer shaders) { + GL20.glGetAttachedShaders(program, count, shaders); + } + + public String getShaderInfoLog(int shader) { + int len = GL20.glGetShaderi(shader, GL20.GL_INFO_LOG_LENGTH); + return GL20.glGetShaderInfoLog(shader, len); + } + + public String getShaderSource(int shader) { + int len = GL20.glGetShaderi(shader, GL20.GL_SHADER_SOURCE_LENGTH); + return GL20.glGetShaderSource(shader, len); + } + + public void getShaderPrecisionFormat(int shaderType, int precisionType, IntBuffer range, IntBuffer precision) { + throw new RuntimeException(String.format(MISSING_GLFUNC_ERROR, "glGetShaderPrecisionFormat()")); + } + + public void getVertexAttribfv(int index, int pname, FloatBuffer params) { + GL20.glGetVertexAttrib(index, pname, params); + } + + public void getVertexAttribiv(int index, int pname, IntBuffer params) { + GL20.glGetVertexAttrib(index, pname, params); + } + + public void getVertexAttribPointerv(int index, int pname, ByteBuffer data) { + int len = data.capacity(); + ByteBuffer res = GL20.glGetVertexAttribPointer(index, pname, len); + data.put(res); + } + + public void getUniformfv(int program, int location, FloatBuffer params) { + GL20.glGetUniform(program, location, params); + } + + public void getUniformiv(int program, int location, IntBuffer params) { + GL20.glGetUniform(program, location, params); + } + + public boolean isProgram(int program) { + return GL20.glIsProgram(program); + } + + public void getProgramiv(int program, int pname, IntBuffer params) { + GL20.glGetProgram(program, pname, params); + } + + public String getProgramInfoLog(int program) { + int len = GL20.glGetProgrami(program, GL20.GL_INFO_LOG_LENGTH); + return GL20.glGetProgramInfoLog(program, len); + } + + /////////////////////////////////////////////////////////// + + // Per-Fragment Operations + + public void scissor(int x, int y, int w, int h) { + GL11.glScissor(x, y, w, h); + } + + public void sampleCoverage(float value, boolean invert) { + GL13.glSampleCoverage(value, invert); + } + + public void stencilFunc(int func, int ref, int mask) { + GL11.glStencilFunc(func, ref, mask); + } + + public void stencilFuncSeparate(int face, int func, int ref, int mask) { + GL20.glStencilFuncSeparate(face, func, ref, mask); + } + + public void stencilOp(int sfail, int dpfail, int dppass) { + GL11.glStencilOp(sfail, dpfail, dppass); + } + + public void stencilOpSeparate(int face, int sfail, int dpfail, int dppass) { + GL20.glStencilOpSeparate(face, sfail, dpfail, dppass); + } + + public void depthFunc(int func) { + GL11.glDepthFunc(func); + } + + public void blendEquation(int mode) { + GL14.glBlendEquation(mode); + } + + public void blendEquationSeparate(int modeRGB, int modeAlpha) { + GL20.glBlendEquationSeparate(modeRGB, modeAlpha); + } + + public void blendFunc(int src, int dst) { + GL11.glBlendFunc(src, dst); + } + + public void blendFuncSeparate(int srcRGB, int dstRGB, int srcAlpha, int dstAlpha) { + GL14.glBlendFuncSeparate(srcRGB, dstRGB, srcAlpha, dstAlpha); + } + + public void blendColor(float red, float green, float blue, float alpha) { + GL14.glBlendColor(red, green, blue, alpha); + } + + public void alphaFunc(int func, float ref) { + GL11.glAlphaFunc(func, ref); + } + + /////////////////////////////////////////////////////////// + + // Whole Framebuffer Operations + + public void colorMask(boolean r, boolean g, boolean b, boolean a) { + GL11.glColorMask(r, g, b, a); + } + + public void depthMask(boolean mask) { + GL11.glDepthMask(mask); + } + + public void stencilMask(int mask) { + GL11.glStencilMask(mask); + } + + public void stencilMaskSeparate(int face, int mask) { + GL20.glStencilMaskSeparate(face, mask); + } + + public void clear(int buf) { + GL11.glClear(buf); + } + + public void clearColor(float r, float g, float b, float a) { + GL11.glClearColor(r, g, b, a); + } + + public void clearDepth(float d) { + GL11.glClearDepth(d); + } + + public void clearStencil(int s) { + GL11.glClearStencil(s); + } + + /////////////////////////////////////////////////////////// + + // Framebuffers Objects + + protected void bindFramebufferImpl(int target, int framebuffer) { + GL30.glBindFramebuffer(target, framebuffer); + } + + public void deleteFramebuffers(int n, IntBuffer framebuffers) { + GL30.glDeleteFramebuffers(framebuffers); + } + + public void genFramebuffers(int n, IntBuffer framebuffers) { + GL30.glGenFramebuffers(framebuffers); + } + + public void bindRenderbuffer(int target, int renderbuffer) { + GL30.glBindRenderbuffer(target, renderbuffer); + } + + public void deleteRenderbuffers(int n, IntBuffer renderbuffers) { + GL30.glDeleteRenderbuffers(renderbuffers); + } + + public void genRenderbuffers(int n, IntBuffer renderbuffers) { + GL30.glGenRenderbuffers(renderbuffers); + } + + public void renderbufferStorage(int target, int internalFormat, int width, int height) { + GL30.glRenderbufferStorage(target, internalFormat, width, height); + } + + public void framebufferRenderbuffer(int target, int attachment, int rendbuferfTarget, int renderbuffer) { + GL30.glFramebufferRenderbuffer(target, attachment, rendbuferfTarget, renderbuffer); + } + + public void framebufferTexture2D(int target, int attachment, int texTarget, int texture, int level) { + GL30.glFramebufferTexture2D(target, attachment, texTarget, texture, level); + } + + public int checkFramebufferStatus(int target) { + return GL30.glCheckFramebufferStatus(target); + } + + public boolean isFramebuffer(int framebuffer) { + return GL30.glIsFramebuffer(framebuffer); + } + + public void getFramebufferAttachmentParameteriv(int target, int attachment, int pname, IntBuffer params) { + GL30.glGetFramebufferAttachmentParameter(target, attachment, pname, params); + } + + public boolean isRenderbuffer(int renderbuffer) { + return GL30.glIsRenderbuffer(renderbuffer); + } + + public void getRenderbufferParameteriv(int target, int pname, IntBuffer params) { + GL30.glGetRenderbufferParameter(target, pname, params); + } + + public void blitFramebuffer(int srcX0, int srcY0, int srcX1, int srcY1, int dstX0, int dstY0, int dstX1, int dstY1, int mask, int filter) { + GL30.glBlitFramebuffer(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter); + } + + public void renderbufferStorageMultisample(int target, int samples, int format, int width, int height) { + GL30.glRenderbufferStorageMultisample(target, samples, format, width, height); + } + + public void readBuffer(int buf) { + GL11.glReadBuffer(buf); + } + + public void drawBuffer(int buf) { + GL11.glDrawBuffer(buf); + } + + + @Override + protected void getGL(PGL pgl) { + } +} diff --git a/java/libraries/minim/examples/AnalyzeSound/AnalyzeSound.pde b/java/libraries/minim/examples/AnalyzeSound/AnalyzeSound.pde index d2122efb7..46bdec13a 100644 --- a/java/libraries/minim/examples/AnalyzeSound/AnalyzeSound.pde +++ b/java/libraries/minim/examples/AnalyzeSound/AnalyzeSound.pde @@ -7,6 +7,9 @@ * the frequency content of a signal. You've seen * visualizations like this before in music players * and car stereos. + *

    + * For more information about Minim and additional features, + * visit http://code.compartmental.net/minim/ */ import ddf.minim.analysis.*; diff --git a/java/libraries/minim/examples/CreateAnInstrument/CreateAnInstrument.pde b/java/libraries/minim/examples/CreateAnInstrument/CreateAnInstrument.pde new file mode 100644 index 000000000..e43c3f339 --- /dev/null +++ b/java/libraries/minim/examples/CreateAnInstrument/CreateAnInstrument.pde @@ -0,0 +1,85 @@ +/** + * This sketch demonstrates how to create synthesized sound with Minim using an AudioOutput and + * an Instrument we define. By using the playNote method you can schedule notes to played + * at some point in the future, essentially allowing to you create musical scores with code. + * Because they are constructed with code, they can be either deterministic or different every time. + * This sketch creates a deterministic score, meaning it is the same every time you run the sketch. + *

    + * For more complex examples of using playNote check out algorithmicCompExample and compositionExample + * in the Synthesis folder. + *

    + * For more information about Minim and additional features, visit http://code.compartmental.net/minim/ + */ + +import ddf.minim.*; +import ddf.minim.ugens.*; + +Minim minim; +AudioOutput out; + +// to make an Instrument we must define a class +// that implements the Instrument interface. +class SineInstrument implements Instrument +{ + Oscil wave; + Line ampEnv; + + SineInstrument( float frequency ) + { + // make a sine wave oscillator + // the amplitude is zero because + // we are going to patch a Line to it anyway + wave = new Oscil( frequency, 0, Waves.SINE ); + ampEnv = new Line(); + ampEnv.patch( wave.amplitude ); + } + + // this is called by the sequencer when this instrument + // should start making sound. the duration is expressed in seconds. + void noteOn( float duration ) + { + // start the amplitude envelope + ampEnv.activate( duration, 0.5f, 0 ); + // attach the oscil to the output so it makes sound + wave.patch( out ); + } + + // this is called by the sequencer when the instrument should + // stop making sound + void noteOff() + { + wave.unpatch( out ); + } +} + +void setup() +{ + size(512, 200, P3D); + + minim = new Minim(this); + + // use the getLineOut method of the Minim object to get an AudioOutput object + out = minim.getLineOut(); + + // when providing an Instrument, we always specify start time and duration + out.playNote( 0.0, 0.9, new SineInstrument( 97.99 ) ); + out.playNote( 1.0, 0.9, new SineInstrument( 123.47 ) ); + + // we can use the Frequency class to create frequencies from pitch names + out.playNote( 2.0, 2.9, new SineInstrument( Frequency.ofPitch( "C3" ).asHz() ) ); + out.playNote( 3.0, 1.9, new SineInstrument( Frequency.ofPitch( "E3" ).asHz() ) ); + out.playNote( 4.0, 0.9, new SineInstrument( Frequency.ofPitch( "G3" ).asHz() ) ); +} + +void draw() +{ + background(0); + stroke(255); + + // draw the waveforms + for(int i = 0; i < out.bufferSize() - 1; i++) + { + line( i, 50 + out.left.get(i)*50, i+1, 50 + out.left.get(i+1)*50 ); + line( i, 150 + out.right.get(i)*50, i+1, 150 + out.right.get(i+1)*50 ); + } +} diff --git a/java/libraries/minim/examples/GetMetaData/GetMetaData.pde b/java/libraries/minim/examples/GetMetaData/GetMetaData.pde index fe47ea429..717152ba2 100644 --- a/java/libraries/minim/examples/GetMetaData/GetMetaData.pde +++ b/java/libraries/minim/examples/GetMetaData/GetMetaData.pde @@ -10,6 +10,9 @@ * If you load WAV file or other non-tagged file, most of the metadata * will be empty, but you will still have information like the filename * and the length. + *

    + * For more information about Minim and additional features, + * visit http://code.compartmental.net/minim/ */ import ddf.minim.*; @@ -52,14 +55,3 @@ void draw() text("Publisher: " + meta.publisher(), 5, y+=yi); text("Encoded: " + meta.encoded(), 5, y+=yi); } - - -void stop() -{ - // always close Minim audio classes when you are done with them - groove.close(); - // always stop Minim before exiting - minim.stop(); - - super.stop(); -} diff --git a/java/libraries/minim/examples/GetMetaData/data/groove.mp3 b/java/libraries/minim/examples/GetMetaData/data/groove.mp3 index abfd3c811..22fd64fd4 100644 Binary files a/java/libraries/minim/examples/GetMetaData/data/groove.mp3 and b/java/libraries/minim/examples/GetMetaData/data/groove.mp3 differ diff --git a/java/libraries/minim/examples/MonitorInput/MonitorInput.pde b/java/libraries/minim/examples/MonitorInput/MonitorInput.pde index 3ec94cc6f..86679683b 100644 --- a/java/libraries/minim/examples/MonitorInput/MonitorInput.pde +++ b/java/libraries/minim/examples/MonitorInput/MonitorInput.pde @@ -1,12 +1,17 @@ /** * This sketch demonstrates how to monitor the currently active audio input - * of the computer using an AudioInput. What you will actually + * of the computer using an AudioInput. What you will actually * be monitoring depends on the current settings of the machine the sketch is running on. * Typically, you will be monitoring the built-in microphone, but if running on a desktop - * its feasible that the user may have the actual audio output of the computer + * it's feasible that the user may have the actual audio output of the computer * as the active audio input, or something else entirely. *

    - * When you run your sketch as an applet you will need to sign it in order to get an input. + * Press 'm' to toggle monitoring on and off. + *

    + * When you run your sketch as an applet you will need to sign it in order to get an input. + *

    + * For more information about Minim and additional features, + * visit http://code.compartmental.net/minim/ */ import ddf.minim.*; @@ -22,9 +27,6 @@ void setup() // use the getLineIn method of the Minim object to get an AudioInput in = minim.getLineIn(); - - // uncomment this line to *hear* what is being monitored, in addition to seeing it - in.enableMonitoring(); } void draw() @@ -38,4 +40,22 @@ void draw() line( i, 50 + in.left.get(i)*50, i+1, 50 + in.left.get(i+1)*50 ); line( i, 150 + in.right.get(i)*50, i+1, 150 + in.right.get(i+1)*50 ); } + + String monitoringState = in.isMonitoring() ? "enabled" : "disabled"; + text( "Input monitoring is currently " + monitoringState + ".", 5, 15 ); +} + +void keyPressed() +{ + if ( key == 'm' || key == 'M' ) + { + if ( in.isMonitoring() ) + { + in.disableMonitoring(); + } + else + { + in.enableMonitoring(); + } + } } diff --git a/java/libraries/minim/examples/PatchingAnInput/PatchingAnInput.pde b/java/libraries/minim/examples/PatchingAnInput/PatchingAnInput.pde index 54b8719aa..e756a6d17 100644 --- a/java/libraries/minim/examples/PatchingAnInput/PatchingAnInput.pde +++ b/java/libraries/minim/examples/PatchingAnInput/PatchingAnInput.pde @@ -4,6 +4,9 @@ * a UGen. In this case, we patch an Oscil generating a sine wave into * the amplitude input of an Oscil generating a square wave. The result * is known as amplitude modulation. + *

    + * For more information about Minim and additional features, + * visit http://code.compartmental.net/minim/ */ import ddf.minim.*; diff --git a/java/libraries/minim/examples/PlayAFile/PlayAFile.pde b/java/libraries/minim/examples/PlayAFile/PlayAFile.pde index a906820ea..c95eb3956 100644 --- a/java/libraries/minim/examples/PlayAFile/PlayAFile.pde +++ b/java/libraries/minim/examples/PlayAFile/PlayAFile.pde @@ -1,6 +1,9 @@ /** * This sketch demonstrates how to play a file with Minim using an AudioPlayer.
    * It's also a good example of how to draw the waveform of the audio. + *

    + * For more information about Minim and additional features, + * visit http://code.compartmental.net/minim/ */ import ddf.minim.*; @@ -20,7 +23,9 @@ void setup() // sketch folder. you can also pass an absolute path, or a URL. player = minim.loadFile("marcus_kellis_theme.mp3"); - // play the file + // play the file from start to finish. + // if you want to play the file again, + // you need to call rewind() first. player.play(); } diff --git a/java/libraries/minim/examples/RecordAudioInput/RecordAudioInput.pde b/java/libraries/minim/examples/RecordAudioInput/RecordAudioInput.pde index 296d23814..b5983fde7 100644 --- a/java/libraries/minim/examples/RecordAudioInput/RecordAudioInput.pde +++ b/java/libraries/minim/examples/RecordAudioInput/RecordAudioInput.pde @@ -1,8 +1,13 @@ /** * This sketch demonstrates how to an AudioRecorder to record audio to disk. - * To use this sketch you need to have something plugged into the line-in on your computer, or else be working on a - * laptop with an active built-in microphone. Press 'r' to toggle recording on and off and the press 's' to save to disk. + * To use this sketch you need to have something plugged into the line-in on your computer, + * or else be working on a laptop with an active built-in microphone. + *

    + * Press 'r' to toggle recording on and off and the press 's' to save to disk. * The recorded file will be placed in the sketch folder of the sketch. + *

    + * For more information about Minim and additional features, + * visit http://code.compartmental.net/minim/ */ import ddf.minim.*; @@ -18,11 +23,9 @@ void setup() minim = new Minim(this); in = minim.getLineIn(); - // create a recorder that will record from the input to the filename specified, using buffered recording - // buffered recording means that all captured audio will be written into a sample buffer - // then when save() is called, the contents of the buffer will actually be written to a file + // create a recorder that will record from the input to the filename specified // the file will be located in the sketch's root folder. - recorder = minim.createRecorder(in, "myrecording.wav", true); + recorder = minim.createRecorder(in, "myrecording.wav"); textFont(createFont("Arial", 12)); } diff --git a/java/libraries/minim/examples/RecordAudioOutput/RecordAudioOutput.pde b/java/libraries/minim/examples/RecordAudioOutput/RecordAudioOutput.pde index ae7cefdbf..c34d03f1e 100644 --- a/java/libraries/minim/examples/RecordAudioOutput/RecordAudioOutput.pde +++ b/java/libraries/minim/examples/RecordAudioOutput/RecordAudioOutput.pde @@ -2,6 +2,9 @@ * This sketch demonstrates how to use an AudioRecorder to record audio to disk. * Press 'r' to toggle recording on and off and the press 's' to save to disk. * The recorded file will be placed in the sketch folder of the sketch. + *

    + * For more information about Minim and additional features, + * visit http://code.compartmental.net/minim/ */ import ddf.minim.*; @@ -19,11 +22,9 @@ void setup() out = minim.getLineOut(); - // create a recorder that will record from the input to the filename specified, using buffered recording - // buffered recording means that all captured audio will be written into a sample buffer - // then when save() is called, the contents of the buffer will actually be written to a file + // create a recorder that will record from the output to the filename specified // the file will be located in the sketch's root folder. - recorder = minim.createRecorder(out, "myrecording.wav", true); + recorder = minim.createRecorder(out, "myrecording.wav"); // patch some sound into the output so we have something to record Oscil wave = new Oscil( 440.f, 1.0f ); diff --git a/java/libraries/minim/examples/Scrubbing/Scrubbing.pde b/java/libraries/minim/examples/Scrubbing/Scrubbing.pde index dc3c5fedd..23f24bdb1 100644 --- a/java/libraries/minim/examples/Scrubbing/Scrubbing.pde +++ b/java/libraries/minim/examples/Scrubbing/Scrubbing.pde @@ -5,6 +5,9 @@ * But the end result is convincing enough. *

    * The positioning code is inside of the Play, Rewind, and Forward classes, which are in button.pde. + *

    + * For more information about Minim and additional features, + * visit http://code.compartmental.net/minim/ */ import ddf.minim.*; diff --git a/java/libraries/minim/examples/SequenceSound/SequenceSound.pde b/java/libraries/minim/examples/SequenceSound/SequenceSound.pde index 9d4878e94..c23c8fa36 100644 --- a/java/libraries/minim/examples/SequenceSound/SequenceSound.pde +++ b/java/libraries/minim/examples/SequenceSound/SequenceSound.pde @@ -6,8 +6,11 @@ * sketch creates a deterministic score, meaning it is the same every time you run the sketch. It also demonstrates * a couple different versions of the playNote method. *

    - * For more complex examples of using playNote check out algorithmicCompExample and compositionExample - * in the Synthesis folder. + * For more complex examples of using playNote check out + * algorithmicCompExample and compositionExample in the Synthesis folder. + *

    + * For more information about Minim and additional features, + * visit http://code.compartmental.net/minim/ */ import ddf.minim.*; @@ -25,6 +28,20 @@ void setup() // use the getLineOut method of the Minim object to get an AudioOutput object out = minim.getLineOut(); + // set the tempo of the sequencer + // this makes the first argument of playNote + // specify the start time in quarter notes + // and the duration becomes relative to the length of a quarter note + // by default the tempo is 60 BPM (beats per minute). + // at 60 BPM both start time and duration can be interpreted as seconds. + // to retrieve the current tempo, use getTempo(). + out.setTempo( 80 ); + + // pause the sequencer so our note play back will be rock solid + // if you don't do this, then tiny bits of error can occur since + // the sequencer is running in parallel with you note queueing. + out.pauseNotes(); + // given start time, duration, and frequency out.playNote( 0.0, 0.9, 97.99 ); out.playNote( 1.0, 0.9, 123.47 ); @@ -41,15 +58,17 @@ void setup() out.playNote( 7.0, "G4" ); // the note offset is simply added into the start time of - // every subsequenct call to playNote. It's expressed in beats, - // but since the default tempo of an AudioOuput is 60 beats per minute, - // this particular call translates to 8.1 seconds, as you might expect. + // every subsequenct call to playNote. It's expressed in beats. + // to get the current note offset, use getNoteOffset(). out.setNoteOffset( 8.1 ); // because only given a note name or frequency // starttime defaults to 0.0 and duration defaults to 1.0 out.playNote( "G5" ); out.playNote( 987.77 ); + + // now we can start the sequencer again to hear our sequence + out.resumeNotes(); } void draw() diff --git a/java/libraries/minim/examples/SoundSpectrum/SoundSpectrum.pde b/java/libraries/minim/examples/SoundSpectrum/SoundSpectrum.pde index c9095d953..73de17a0c 100644 --- a/java/libraries/minim/examples/SoundSpectrum/SoundSpectrum.pde +++ b/java/libraries/minim/examples/SoundSpectrum/SoundSpectrum.pde @@ -6,8 +6,8 @@ * these can be calculated: Linearly, by grouping equal numbers of adjacent frequency bands, or * Logarithmically, by grouping frequency bands by octave, which is more akin to how humans hear sound. *
    - * This sketch illustrates the difference between viewing the full spectrum, linearly spaced averaged bands, - * and logarithmically spaced averaged bands. + * This sketch illustrates the difference between viewing the full spectrum, + * linearly spaced averaged bands, and logarithmically spaced averaged bands. *

    * From top to bottom: *

      @@ -19,6 +19,8 @@ * Moving the mouse across the sketch will highlight a band in each spectrum and display what the center * frequency of that band is. The averaged bands are drawn so that they line up with full spectrum bands they * are averages of. In this way, you can clearly see how logarithmic averages differ from linear averages. + *

      + * For more information about Minim and additional features, visit http://code.compartmental.net/minim/ */ import ddf.minim.analysis.*; diff --git a/java/libraries/minim/examples/SynthesizeSound/SynthesizeSound.pde b/java/libraries/minim/examples/SynthesizeSound/SynthesizeSound.pde index 4a836e1e2..64b8d915d 100644 --- a/java/libraries/minim/examples/SynthesizeSound/SynthesizeSound.pde +++ b/java/libraries/minim/examples/SynthesizeSound/SynthesizeSound.pde @@ -1,9 +1,16 @@ /** * This sketch demonstrates how to create synthesized sound with Minim * using an AudioOutput and an Oscil. An Oscil is a UGen object, - * one of many different types included with Minim. For many more examples - * of UGens included with Minim, have a look in the Synthesis - * folder of the Minim examples. + * one of many different types included with Minim. By using + * the numbers 1 thru 5, you can change the waveform being used + * by the Oscil to make sound. These basic waveforms are the + * basis of much audio synthesis. + * + * For many more examples of UGens included with Minim, + * have a look in the Synthesis folder of the Minim examples. + *

      + * For more information about Minim and additional features, + * visit http://code.compartmental.net/minim/ */ import ddf.minim.*; @@ -32,11 +39,62 @@ void draw() { background(0); stroke(255); + strokeWeight(1); - // draw the waveforms + // draw the waveform of the output for(int i = 0; i < out.bufferSize() - 1; i++) { - line( i, 50 + out.left.get(i)*50, i+1, 50 + out.left.get(i+1)*50 ); - line( i, 150 + out.right.get(i)*50, i+1, 150 + out.right.get(i+1)*50 ); + line( i, 50 - out.left.get(i)*50, i+1, 50 - out.left.get(i+1)*50 ); + line( i, 150 - out.right.get(i)*50, i+1, 150 - out.right.get(i+1)*50 ); + } + + // draw the waveform we are using in the oscillator + stroke( 128, 0, 0 ); + strokeWeight(4); + for( int i = 0; i < width-1; ++i ) + { + point( i, height/2 - (height*0.49) * wave.getWaveform().value( (float)i / width ) ); + } +} + +void mouseMoved() +{ + // usually when setting the amplitude and frequency of an Oscil + // you will want to patch something to the amplitude and frequency inputs + // but this is a quick and easy way to turn the screen into + // an x-y control for them. + + float amp = map( mouseY, 0, height, 1, 0 ); + wave.setAmplitude( amp ); + + float freq = map( mouseX, 0, width, 110, 880 ); + wave.setFrequency( freq ); +} + +void keyPressed() +{ + switch( key ) + { + case '1': + wave.setWaveform( Waves.SINE ); + break; + + case '2': + wave.setWaveform( Waves.TRIANGLE ); + break; + + case '3': + wave.setWaveform( Waves.SAW ); + break; + + case '4': + wave.setWaveform( Waves.SQUARE ); + break; + + case '5': + wave.setWaveform( Waves.QUARTERPULSE ); + break; + + default: break; } } diff --git a/java/libraries/minim/examples/TriggerASample/TriggerASample.pde b/java/libraries/minim/examples/TriggerASample/TriggerASample.pde index fa4a0c878..dc39a4091 100644 --- a/java/libraries/minim/examples/TriggerASample/TriggerASample.pde +++ b/java/libraries/minim/examples/TriggerASample/TriggerASample.pde @@ -22,6 +22,9 @@ *

      * Use 'k' and 's' to trigger a kick drum sample and a snare sample, respectively. * You will see their waveforms drawn when they are played back. + *

      + * For more information about Minim and additional features, + * visit http://code.compartmental.net/minim/ */ import ddf.minim.*; diff --git a/java/libraries/minim/examples/delayExample/delayExample.pde b/java/libraries/minim/examples/delayExample/delayExample.pde index dbf69a4ac..6c2b7941b 100644 --- a/java/libraries/minim/examples/delayExample/delayExample.pde +++ b/java/libraries/minim/examples/delayExample/delayExample.pde @@ -1,10 +1,12 @@ -/* delayExample - is an example of using the Delay UGen in a continuous sound example. - Use the mouse to control the delay time and the amount of feedback - in the delay unit. - author: Anderson Mills - Anderson Mills's work was supported by numediart (www.numediart.org) -*/ +/* delayExample
      + * is an example of using the Delay UGen in a continuous sound example. + *

      + * For more information about Minim and additional features, + * visit http://code.compartmental.net/minim/ + *

      + * author: Anderson Mills
      + * Anderson Mills's work was supported by numediart (www.numediart.org) + */ // import everything necessary to make sound. import ddf.minim.*; @@ -14,47 +16,44 @@ import ddf.minim.ugens.*; // more than one methods (setup(), draw(), stop()). Minim minim; AudioOutput out; -Delay myDelay1; +Delay myDelay; // setup is run once at the beginning void setup() { // initialize the drawing window - size( 512, 200, P2D ); + size( 512, 200 ); // initialize the minim and out objects minim = new Minim(this); - out = minim.getLineOut( Minim.MONO, 2048 ); + out = minim.getLineOut(); - // initialize myDelay1 with continual feedback and no audio passthrough - myDelay1 = new Delay( 0.6, 0.9, true, false ); + // initialize myDelay with continual feedback and audio passthrough + myDelay = new Delay( 0.4, 0.5, true, true ); + + // sawh will create a Sawtooth wave with the requested number of harmonics. + // like with Waves.randomNHarms for sine waves, + // you can create a richer sounding sawtooth this way. + Waveform saw = Waves.sawh( 15 ); // create the Blip that will be used - Oscil myBlip = new Oscil( 245.0, 0.3, Waves.saw( 15 ) ); + Oscil myBlip = new Oscil( 245.0, 0.3, saw ); + // Waves.square will create a square wave with an uneven duty-cycle, + // also known as a pulse wave. a square wave has only two values, + // either -1 or 1 and the duty cycle indicates how much of the wave + // should -1 and how much 1. in this case, we are asking for a square + // wave that is -1 90% of the time, and 1 10% of the time. + Waveform square = Waves.square( 0.9 ); // create an LFO to be used for an amplitude envelope - Oscil myLFO = new Oscil( 0.5, 0.3, Waves.square( 0.005 ) ); - // our LFO will operate on a base amplitude - Constant baseAmp = new Constant(0.3); - // we get the final amplitude by summing the two - Summer ampSum = new Summer(); - - Summer sum = new Summer(); - - // patch everything together - // the LFO is patched into a summer along with a constant value - // and that sum is used to drive the amplitude of myBlip - baseAmp.patch( ampSum ); - myLFO.patch( ampSum ); - ampSum.patch( myBlip.amplitude ); + Oscil myLFO = new Oscil( 1, 0.3, square ); + // offset the center value of the LFO so that it outputs 0 + // for the long portion of the duty cycle + myLFO.offset.setLastValue( 0.3 ); - // the Blip is patched directly into the sum - myBlip.patch( sum ); + myLFO.patch( myBlip.amplitude ); - // and the Blip is patched through the delay into the sum. - myBlip.patch( myDelay1 ).patch( sum ); - - // patch the sum into the output - sum.patch( out ); + // and the Blip is patched through the delay into the output + myBlip.patch( myDelay ).patch( out ); } // draw is run many times @@ -73,7 +72,10 @@ void draw() // draw a line from one buffer position to the next for both channels line( x1, 50 + out.left.get(i)*50, x2, 50 + out.left.get(i+1)*50); line( x1, 150 + out.right.get(i)*50, x2, 150 + out.right.get(i+1)*50); - } + } + + text( "Delay time is " + myDelay.delTime.getLastValue(), 5, 15 ); + text( "Delay amplitude (feedback) is " + myDelay.delAmp.getLastValue(), 5, 30 ); } // when the mouse is moved, change the delay parameters @@ -81,8 +83,8 @@ void mouseMoved() { // set the delay time by the horizontal location float delayTime = map( mouseX, 0, width, 0.0001, 0.5 ); - myDelay1.setDelTime( delayTime ); + myDelay.setDelTime( delayTime ); // set the feedback factor by the vertical location - float feedbackFactor = map( mouseY, 0, height, 0.0, 0.99 ); - myDelay1.setDelAmp( feedbackFactor ); + float feedbackFactor = map( mouseY, 0, height, 0.99, 0.0 ); + myDelay.setDelAmp( feedbackFactor ); } diff --git a/java/libraries/minim/examples/filterExample/filterExample.pde b/java/libraries/minim/examples/filterExample/filterExample.pde index 3035fc5f1..2b82c52ba 100644 --- a/java/libraries/minim/examples/filterExample/filterExample.pde +++ b/java/libraries/minim/examples/filterExample/filterExample.pde @@ -1,10 +1,13 @@ -/* filterExample - is an example of using the different filters - in continuous sound. - - author: Damien Di Fede, Anderson Mills - Anderson Mills's work was supported by numediart (www.numediart.org) -*/ +/* filterExample
      + * is an example of using the different filters + * in continuous sound. + *

      + * For more information about Minim and additional features, + * visit http://code.compartmental.net/minim/ + *

      + * author: Damien Di Fede, Anderson Mills
      + * Anderson Mills's work was supported by numediart (www.numediart.org) + */ // import everything necessary to make sound. import ddf.minim.*; diff --git a/java/libraries/minim/examples/frequencyModulation/frequencyModulation.pde b/java/libraries/minim/examples/frequencyModulation/frequencyModulation.pde index 8338ea098..7fcb7645a 100644 --- a/java/libraries/minim/examples/frequencyModulation/frequencyModulation.pde +++ b/java/libraries/minim/examples/frequencyModulation/frequencyModulation.pde @@ -1,10 +1,12 @@ /* frequencyModulation -

      - A simple example for doing FM (frequency modulation) using two Oscils. - Use the mouse to control the speed and range of the frequency modulation. -

      - Author: Damien Di Fede -*/ +

      + A simple example for doing FM (frequency modulation) using two Oscils. +

      + For more information about Minim and additional features, + visit http://code.compartmental.net/minim/ +

      + Author: Damien Di Fede + */ // import everything necessary to make sound. import ddf.minim.*; @@ -62,6 +64,9 @@ void draw() line( x1, 50 + out.left.get(i)*50, x2, 50 + out.left.get(i+1)*50); line( x1, 150 + out.right.get(i)*50, x2, 150 + out.right.get(i+1)*50); } + + text( "Modulation frequency: " + fm.frequency.getLastValue(), 5, 15 ); + text( "Modulation amplitude: " + fm.amplitude.getLastValue(), 5, 30 ); } // we can change the parameters of the frequency modulation Oscil @@ -71,6 +76,6 @@ void mouseMoved() float modulateAmount = map( mouseY, 0, height, 220, 1 ); float modulateFrequency = map( mouseX, 0, width, 0.1, 100 ); - fm.frequency.setLastValue( modulateFrequency ); - fm.amplitude.setLastValue( modulateAmount ); + fm.setFrequency( modulateFrequency ); + fm.setAmplitude( modulateAmount ); } diff --git a/java/libraries/minim/examples/loadFileIntoBuffer/loadFileIntoBuffer.pde b/java/libraries/minim/examples/loadFileIntoBuffer/loadFileIntoBuffer.pde index ec5c6d1b4..32db474e4 100644 --- a/java/libraries/minim/examples/loadFileIntoBuffer/loadFileIntoBuffer.pde +++ b/java/libraries/minim/examples/loadFileIntoBuffer/loadFileIntoBuffer.pde @@ -4,6 +4,9 @@ * a file from the data folder into a MultiChannelBuffer and then modifies that sample data before * using it to create a Sampler UGen. You can hear the result of this modification by hitting * the space bar. + *

      + * For more information about Minim and additional features, + * visit http://code.compartmental.net/minim/ */ import ddf.minim.*; diff --git a/java/libraries/net/build.xml b/java/libraries/net/build.xml old mode 100644 new mode 100755 index 1766599ed..61adc6a05 --- a/java/libraries/net/build.xml +++ b/java/libraries/net/build.xml @@ -18,7 +18,11 @@ srcdir="src" destdir="bin" encoding="UTF-8" includeAntRuntime="false" - classpath="../../../core/library/core.jar" /> + classpath="../../../core/library/core.jar" + nowarn="true" + compiler="org.eclipse.jdt.core.JDTCompilerAdapter"> + + diff --git a/java/libraries/net/src/processing/net/Client.java b/java/libraries/net/src/processing/net/Client.java index 18867e5dc..de39b6e9f 100644 --- a/java/libraries/net/src/processing/net/Client.java +++ b/java/libraries/net/src/processing/net/Client.java @@ -118,6 +118,7 @@ public class Client implements Runnable { * @throws IOException */ public Client(PApplet parent, Socket socket) throws IOException { + this.parent = parent; this.socket = socket; input = socket.getInputStream(); @@ -125,6 +126,16 @@ public class Client implements Runnable { thread = new Thread(this); thread.start(); + + // reflection to check whether host sketch has a call for + // public void disconnectEvent(processing.net.Client) + try { + disconnectEventMethod = + parent.getClass().getMethod("disconnectEvent", + new Class[] { Client.class }); + } catch (Exception e) { + // no such method, or an error.. which is fine, just ignore + } } @@ -140,7 +151,7 @@ public class Client implements Runnable { * @usage application */ public void stop() { - if (disconnectEventMethod != null) { + if (disconnectEventMethod != null && thread != null){ try { disconnectEventMethod.invoke(parent, new Object[] { this }); } catch (Exception e) { diff --git a/java/libraries/net/src/processing/net/Server.java b/java/libraries/net/src/processing/net/Server.java index 12b73a71b..4cfeb784d 100644 --- a/java/libraries/net/src/processing/net/Server.java +++ b/java/libraries/net/src/processing/net/Server.java @@ -24,12 +24,14 @@ */ package processing.net; + import processing.core.*; import java.io.*; import java.lang.reflect.*; import java.net.*; + /** * ( begin auto-generated from Server.xml ) * @@ -65,11 +67,25 @@ public class Server implements Runnable { * @param port port used to transfer data */ public Server(PApplet parent, int port) { + this(parent, port, null); + } + + + /** + * @param parent typically use "this" + * @param port port used to transfer data + * @param host when multiple NICs are in use, the ip (or name) to bind from + */ + public Server(PApplet parent, int port, String host) { this.parent = parent; this.port = port; try { - server = new ServerSocket(this.port); + if (host == null) { + server = new ServerSocket(this.port); + } else { + server = new ServerSocket(this.port, 10, InetAddress.getByName(host)); + } //clients = new Vector(); clients = new Client[10]; @@ -109,8 +125,7 @@ public class Server implements Runnable { * @param client the client to disconnect */ public void disconnect(Client client) { - //client.stop(); - client.dispose(); + client.stop(); int index = clientIndex(client); if (index != -1) { removeIndex(index); @@ -129,6 +144,21 @@ public class Server implements Runnable { } + protected void disconnectAll() { + synchronized (clients) { + for (int i = 0; i < clientCount; i++) { + try { + clients[i].stop(); + } catch (Exception e) { + // ignore + } + clients[i] = null; + } + clientCount = 0; + } + } + + protected void addClient(Client client) { if (clientCount == clients.length) { clients = (Client[]) PApplet.expand(clients); @@ -152,8 +182,8 @@ public class Server implements Runnable { return InetAddress.getLocalHost().getHostAddress(); } catch (UnknownHostException e) { e.printStackTrace(); + return null; } - return null; } @@ -216,9 +246,7 @@ public class Server implements Runnable { thread = null; if (clients != null) { - for (int i = 0; i < clientCount; i++) { - disconnect(clients[i]); - } + disconnectAll(); clientCount = 0; clients = null; } @@ -251,6 +279,10 @@ public class Server implements Runnable { } } } + } catch (SocketException e) { + //thrown when server.close() is called and server is waiting on accept + System.err.println("Server SocketException: " + e.getMessage()); + thread = null; } catch (IOException e) { //errorMessage("run", e); e.printStackTrace(); @@ -311,15 +343,4 @@ public class Server implements Runnable { } } } - - - /** - * General error reporting, all corralled here just in case - * I think of something slightly more intelligent to do. - */ -// public void errorMessage(String where, Exception e) { -// parent.die("Error inside Server." + where + "()", e); -// //System.err.println("Error inside Server." + where + "()"); -// //e.printStackTrace(System.err); -// } } diff --git a/java/libraries/pdf/README.md b/java/libraries/pdf/README.md new file mode 100644 index 000000000..a5fe4967c --- /dev/null +++ b/java/libraries/pdf/README.md @@ -0,0 +1,11 @@ +This library uses iText 2.1.7, which is the last LGPL/MPL version of the iText project. + +We've used iText for several years. The license for iText has changed for subsequent versions and is no longer compatible with Processing, so we're stuck at 2.x. + +At the iText site, there's also some [vague wording](http://lowagie.com/itext2) about legal liability for commercial projects using the 2.x series. It's not clear where this leaves us. + +Bruno Lowagie did an enormous amount of (free) work with the iText project, and we certainly don't fault him for the new commercial license. + +We're using iText in a very limited way--drawing to it like it's a Java Graphics2D object. There might be other options for us in this space, but it's not much of a priority. + +Ben Fry, 12 October 2013 diff --git a/java/libraries/pdf/build.xml b/java/libraries/pdf/build.xml old mode 100644 new mode 100755 index 322002993..e10313534 --- a/java/libraries/pdf/build.xml +++ b/java/libraries/pdf/build.xml @@ -3,7 +3,7 @@ - + @@ -18,7 +18,11 @@ srcdir="src" destdir="bin" encoding="UTF-8" includeAntRuntime="false" - classpath="../../../core/library/core.jar; library/itext.jar" /> + classpath="../../../core/library/core.jar; library/itext.jar" + nowarn="true" + compiler="org.eclipse.jdt.core.JDTCompilerAdapter"> + + diff --git a/java/libraries/pdf/src/processing/pdf/PGraphicsPDF.java b/java/libraries/pdf/src/processing/pdf/PGraphicsPDF.java index 2c6e8ae88..69e02d1f8 100644 --- a/java/libraries/pdf/src/processing/pdf/PGraphicsPDF.java +++ b/java/libraries/pdf/src/processing/pdf/PGraphicsPDF.java @@ -22,16 +22,13 @@ package processing.pdf; import java.awt.Font; import java.awt.Graphics2D; +import java.awt.Image; import java.io.*; import java.util.*; import com.lowagie.text.*; import com.lowagie.text.pdf.*; -// Tried iText 5, but it was too slow -//import com.itextpdf.text.*; -//import com.itextpdf.text.pdf.*; - import processing.core.*; @@ -404,22 +401,23 @@ public class PGraphicsPDF extends PGraphicsJava2D { ////////////////////////////////////////////////////////////// - /* - protected void imageImplAWT(java.awt.Image awtImage, - float x1, float y1, float x2, float y2, - int u1, int v1, int u2, int v2) { + protected void imageImpl(PImage image, + float x1, float y1, float x2, float y2, + int u1, int v1, int u2, int v2) { pushMatrix(); translate(x1, y1); - int awtImageWidth = awtImage.getWidth(null); - int awtImageHeight = awtImage.getHeight(null); - scale((x2 - x1) / (float)awtImageWidth, - (y2 - y1) / (float)awtImageHeight); - g2.drawImage(awtImage, - 0, 0, awtImageWidth, awtImageHeight, - u1, v1, u2, v2, null); + int imageWidth = image.width; + int imageHeight = image.height; + scale((x2 - x1) / imageWidth, + (y2 - y1) / imageHeight); + if (u2-u1 == imageWidth && v2-v1 == imageHeight) { + g2.drawImage((Image) image.getNative(), 0, 0, null); + } else { + PImage tmp = image.get(u1, v1, u2-u1, v2-v1); + g2.drawImage((Image) tmp.getNative(), 0, 0, null); + } popMatrix(); } - */ ////////////////////////////////////////////////////////////// diff --git a/java/libraries/serial/.classpath b/java/libraries/serial/.classpath index fb88820a0..d8894f2e3 100644 --- a/java/libraries/serial/.classpath +++ b/java/libraries/serial/.classpath @@ -2,7 +2,7 @@ - + diff --git a/java/libraries/serial/build.xml b/java/libraries/serial/build.xml old mode 100644 new mode 100755 index 887db84a0..d6b18410e --- a/java/libraries/serial/build.xml +++ b/java/libraries/serial/build.xml @@ -3,7 +3,7 @@ - + @@ -18,7 +18,11 @@ srcdir="src" destdir="bin" encoding="UTF-8" includeAntRuntime="false" - classpath="../../../core/library/core.jar; library/RXTXcomm.jar" /> + classpath="../../../core/library/core.jar; library/jssc.jar" + nowarn="true" + compiler="org.eclipse.jdt.core.JDTCompilerAdapter"> + + diff --git a/java/libraries/serial/examples/SerialCallResponse/SerialCallResponse.pde b/java/libraries/serial/examples/SerialCallResponse/SerialCallResponse.pde index cae1e6a1d..627b27a75 100644 --- a/java/libraries/serial/examples/SerialCallResponse/SerialCallResponse.pde +++ b/java/libraries/serial/examples/SerialCallResponse/SerialCallResponse.pde @@ -33,7 +33,7 @@ void setup() { ypos = height/2; // Print a list of the serial ports, for debugging purposes: - println(Serial.list()); + printArray(Serial.list()); // I know that the first port in the serial list on my mac // is always my FTDI adaptor, so I open Serial.list()[0]. @@ -133,15 +133,15 @@ void loop() // so that you're sending 100 or 255: thirdSensor = 100 + (155 * digitalRead(2)); // send sensor values: - Serial.print(firstSensor, BYTE); - Serial.print(secondSensor, BYTE); - Serial.print(thirdSensor, BYTE); + Serial.write(firstSensor); + Serial.write(secondSensor); + Serial.write(thirdSensor); } } void establishContact() { while (Serial.available() <= 0) { - Serial.print('A', BYTE); // send a capital A + Serial.write('A'); // send a capital A delay(300); } } diff --git a/java/libraries/serial/examples/SerialDuplex/SerialDuplex.pde b/java/libraries/serial/examples/SerialDuplex/SerialDuplex.pde index 1a012a206..38dc2f204 100644 --- a/java/libraries/serial/examples/SerialDuplex/SerialDuplex.pde +++ b/java/libraries/serial/examples/SerialDuplex/SerialDuplex.pde @@ -22,7 +22,7 @@ void setup() { textFont(myFont); // List all the available serial ports: - println(Serial.list()); + printArray(Serial.list()); // I know that the first port in the serial list on my mac // is always my FTDI adaptor, so I open Serial.list()[0]. diff --git a/java/libraries/serial/examples/SerialMultiple/SerialMultiple.pde b/java/libraries/serial/examples/SerialMultiple/SerialMultiple.pde index 4334e1a2b..0646a4dc9 100644 --- a/java/libraries/serial/examples/SerialMultiple/SerialMultiple.pde +++ b/java/libraries/serial/examples/SerialMultiple/SerialMultiple.pde @@ -13,7 +13,7 @@ int[] dataIn = new int[2]; // a list to hold data from the serial ports void setup() { size(400, 300); // print a list of the serial ports: - println(Serial.list()); + printArray(Serial.list()); // On my machine, the first and third ports in the list // were the serial ports that my microcontrollers were // attached to. @@ -77,7 +77,7 @@ void setup() void loop() { // read analog input, divide by 4 to make the range 0-255: int analogValue = analogRead(0)/4; - Serial.print(analogValue, BYTE); + Serial.write(analogValue); // pause for 10 milliseconds: delay(10); } diff --git a/java/libraries/serial/examples/SimpleRead/SimpleRead.pde b/java/libraries/serial/examples/SimpleRead/SimpleRead.pde index b5b455e13..856da8f17 100644 --- a/java/libraries/serial/examples/SimpleRead/SimpleRead.pde +++ b/java/libraries/serial/examples/SimpleRead/SimpleRead.pde @@ -54,9 +54,9 @@ void setup() { void loop() { if (digitalRead(switchPin) == HIGH) { // If switch is ON, - Serial.print(1, BYTE); // send 1 to Processing + Serial.write(1); // send 1 to Processing } else { // If the switch is not ON, - Serial.print(0, BYTE); // send 0 to Processing + Serial.write(0); // send 0 to Processing } delay(100); // Wait 100 milliseconds } diff --git a/java/libraries/serial/library/.cvsignore b/java/libraries/serial/library/.cvsignore deleted file mode 100644 index 718e029bc..000000000 --- a/java/libraries/serial/library/.cvsignore +++ /dev/null @@ -1 +0,0 @@ -serial.jar diff --git a/java/libraries/serial/library/jssc.txt b/java/libraries/serial/library/jssc.txt new file mode 100644 index 000000000..7174ece29 --- /dev/null +++ b/java/libraries/serial/library/jssc.txt @@ -0,0 +1,4 @@ +This is using a modified version of Java Simple Serial Connector by Alexey Sokolov. See https://github.com/gohai/java-simple-serial-connector for details on the modifications. + +To compile the C++ portion of the library on OS X: +g++ -shared [or: -dynamiclib?] -arch i386 -arch x86_64 -I/System/Library/Frameworks/IOKit.framework/Versions/A/Headers -I$JAVA_HOME/include -I$JAVA_HOME/include/darwin -framework IOKit -framework CoreFoundation -o libjSSC-2.6.jnilib jssc.cpp diff --git a/java/libraries/serial/src/processing/serial/Serial.java b/java/libraries/serial/src/processing/serial/Serial.java index fd3595fc1..f473f863d 100644 --- a/java/libraries/serial/src/processing/serial/Serial.java +++ b/java/libraries/serial/src/processing/serial/Serial.java @@ -5,6 +5,7 @@ Part of the Processing project - http://processing.org Copyright (c) 2004-05 Ben Fry & Casey Reas + Reworked by Gottfried Haider as part of GSOC 2013 This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public @@ -23,729 +24,453 @@ */ package processing.serial; + import processing.core.*; -import gnu.io.*; - -import java.io.*; -import java.util.*; import java.lang.reflect.*; +import java.util.Map; + +import jssc.*; + -/** - * @generate Serial.xml - * @webref net - * @usage application - */ public class Serial implements SerialPortEventListener { - PApplet parent; + public SerialPort port; + Method serialAvailableMethod; Method serialEventMethod; - // properties can be passed in for default values - // otherwise defaults to 9600 N81 + byte[] buffer = new byte[32768]; + int inBuffer = 0; + int readOffset = 0; - // these could be made static, which might be a solution - // for the classloading problem.. because if code ran again, - // the static class would have an object that could be closed + int bufferUntilSize = 1; + byte bufferUntilByte = 0; - public SerialPort port; + volatile boolean invokeSerialAvailable = false; - public int rate; - public int parity; - public int databits; - public int stopbits; + // Things we are currently not exposing: + // * hardware flow control + // * state of the RING, RLSD line + // * sending breaks - - // read buffer and streams - - public InputStream input; - public OutputStream output; - - byte buffer[] = new byte[32768]; - int bufferIndex; - int bufferLast; - - //boolean bufferUntil = false; - int bufferSize = 1; // how big before reset or event firing - boolean bufferUntil; - byte bufferUntilByte; - - - // defaults - - static String dname = "COM1"; - static int drate = 9600; - static char dparity = 'N'; - static int ddatabits = 8; - static float dstopbits = 1; - - - public void setProperties(Properties props) { - dname = - props.getProperty("serial.port", dname); - drate = - Integer.parseInt(props.getProperty("serial.rate", "9600")); - dparity = - props.getProperty("serial.parity", "N").charAt(0); - ddatabits = - Integer.parseInt(props.getProperty("serial.databits", "8")); - dstopbits = - new Float(props.getProperty("serial.stopbits", "1")).floatValue(); - } - -/** - * @param parent typically use "this" - */ + public Serial(PApplet parent) { - this(parent, dname, drate, dparity, ddatabits, dstopbits); + this(parent, "COM1", 9600, 'N', 8, 1); } - -/** - * @param irate 9600 is the default - */ - public Serial(PApplet parent, int irate) { - this(parent, dname, irate, dparity, ddatabits, dstopbits); + + public Serial(PApplet parent, int baudRate) { + this(parent, "COM1", baudRate, 'N', 8, 1); } - -/** - * @param iname name of the port (COM1 is the default) - */ - public Serial(PApplet parent, String iname, int irate) { - this(parent, iname, irate, dparity, ddatabits, dstopbits); + + public Serial(PApplet parent, String portName) { + this(parent, portName, 9600, 'N', 8, 1); } - public Serial(PApplet parent, String iname) { - this(parent, iname, drate, dparity, ddatabits, dstopbits); + + public Serial(PApplet parent, String portName, int baudRate) { + this(parent, portName, baudRate, 'N', 8, 1); } - -/** - * @param iparity 'N' for none, 'E' for even, 'O' for odd ('N' is the default) - * @param idatabits 8 is the default - * @param istopbits 1.0, 1.5, or 2.0 (1.0 is the default) - */ - public Serial(PApplet parent, String iname, int irate, - char iparity, int idatabits, float istopbits) { - //if (port != null) port.close(); + + public Serial(PApplet parent, String portName, int baudRate, char parity, int dataBits, float stopBits) { this.parent = parent; - //parent.attach(this); - - // On OS X, make sure the lock folder needed by RXTX is present - if (PApplet.platform == PConstants.MACOSX) { - File lockFolder = new File("/var/lock"); - if (!lockFolder.exists() || - !lockFolder.canRead() || - !lockFolder.canWrite() || - !lockFolder.canExecute()) { - final String MESSAGE = - "To use the serial library, first open\n" + - "Applications -> Utilities -> Terminal.app\n" + - "and enter the following:\n" + - "sudo mkdir -p /var/lock\n" + - "sudo chmod 777 /var/lock"; - System.err.println(MESSAGE); - //throw new RuntimeException("Additional installation required to " + - // "use serial, read the console below."); - final String msg = - "Please use Tools \u2192 Fix the Serial Library."; - throw new RuntimeException(msg); - } - } - - this.rate = irate; - - parity = SerialPort.PARITY_NONE; - if (iparity == 'E') parity = SerialPort.PARITY_EVEN; - if (iparity == 'O') parity = SerialPort.PARITY_ODD; - - this.databits = idatabits; - - stopbits = SerialPort.STOPBITS_1; - if (istopbits == 1.5f) stopbits = SerialPort.STOPBITS_1_5; - if (istopbits == 2) stopbits = SerialPort.STOPBITS_2; - - try { - Enumeration portList = CommPortIdentifier.getPortIdentifiers(); - while (portList.hasMoreElements()) { - CommPortIdentifier portId = - (CommPortIdentifier) portList.nextElement(); - - if (portId.getPortType() == CommPortIdentifier.PORT_SERIAL) { - //System.out.println("found " + portId.getName()); - if (portId.getName().equals(iname)) { - port = (SerialPort)portId.open("serial madness", 2000); - input = port.getInputStream(); - output = port.getOutputStream(); - port.setSerialPortParams(rate, databits, stopbits, parity); - port.addEventListener(this); - port.notifyOnDataAvailable(true); - //System.out.println("opening, ready to roll"); - } - } - } - - } catch (Exception e) { - errorMessage("", e); - //exception = e; - //e.printStackTrace(); - port = null; - input = null; - output = null; - } - parent.registerMethod("dispose", this); + parent.registerMethod("pre", this); - // reflection to check whether host applet has a call for - // public void serialEvent(processing.serial.Serial) - // which would be called each time an event comes in - try { - serialEventMethod = - parent.getClass().getMethod("serialEvent", - new Class[] { Serial.class }); - } catch (Exception e) { - // no such method, or an error.. which is fine, just ignore + // setup parity + if (parity == 'O') { + parity = SerialPort.PARITY_ODD; + } else if (parity == 'E') { + parity = SerialPort.PARITY_EVEN; + } else if (parity == 'M') { + parity = SerialPort.PARITY_MARK; + } else if (parity == 'S') { + parity = SerialPort.PARITY_SPACE; + } else { + parity = SerialPort.PARITY_NONE; } + + // setup stop bits + int stopBitsIdx = SerialPort.STOPBITS_1; + if (stopBits == 1.5f) { + stopBitsIdx = SerialPort.STOPBITS_1_5; + } else if (stopBits == 2) { + stopBitsIdx = SerialPort.STOPBITS_2; + } + + port = new SerialPort(portName); + try { + // the native open() call is not using O_NONBLOCK, so this might block for certain operations (see write()) + port.openPort(); + port.setParams(baudRate, dataBits, stopBitsIdx, parity); + // we could register more events here + port.addEventListener(this, SerialPort.MASK_RXCHAR); + } catch (SerialPortException e) { + // this used to be a RuntimeException before, so stick with it + throw new RuntimeException("Error opening serial port " + e.getPortName() + ": " + e.getExceptionType()); + } + + serialEventMethod = findCallback("serialEvent"); + serialAvailableMethod = findCallback("serialAvailable"); } - - /** - * @generate Serial_stop.xml - * @webref serial:serial - * @usage web_application - */ - public void stop() { - dispose(); + private Method findCallback(final String name) { + try { + return parent.getClass().getMethod(name, this.getClass()); + } catch (Exception e) { + } + // Permit callback(Object) as alternative to callback(Serial). + try { + return parent.getClass().getMethod(name, Object.class); + } catch (Exception e) { + } + return null; } - - /** - * Used by PApplet to shut things down. - */ + public void dispose() { - try { - if (input != null) { - input.close(); - input = null; - } - } catch (Exception e) { - e.printStackTrace(); - } - - try { - if (output != null) { - output.close(); - output = null; - } - } catch (Exception e) { - e.printStackTrace(); - } - - try { - if (port != null) { - port.close(); - port = null; - } - } catch (Exception e) { - e.printStackTrace(); - } + stop(); } - - /** - * Set the DTR line. Addition from Tom Hulbert. - */ - public void setDTR(boolean state) { - port.setDTR(state); - } - - - /** - * @generate serialEvent.xml - * @webref serial:events - * @usage web_application - * @param serialEvent the port where new data is available - */ - synchronized public void serialEvent(SerialPortEvent serialEvent) { - if (serialEvent.getEventType() == SerialPortEvent.DATA_AVAILABLE) { + + public void pre() { + if (serialAvailableMethod != null && invokeSerialAvailable) { + invokeSerialAvailable = false; try { - while (input.available() > 0) { - synchronized (buffer) { - if (bufferLast == buffer.length) { - byte temp[] = new byte[bufferLast << 1]; - System.arraycopy(buffer, 0, temp, 0, bufferLast); - buffer = temp; - } - buffer[bufferLast++] = (byte) input.read(); - if (serialEventMethod != null) { - if ((bufferUntil && - (buffer[bufferLast-1] == bufferUntilByte)) || - (!bufferUntil && - ((bufferLast - bufferIndex) >= bufferSize))) { - try { - serialEventMethod.invoke(parent, new Object[] { this }); - } catch (Exception e) { - String msg = "error, disabling serialEvent() for " + port; - System.err.println(msg); - e.printStackTrace(); - serialEventMethod = null; - } - } - } - } - } - - } catch (IOException e) { - errorMessage("serialEvent", e); + serialAvailableMethod.invoke(parent, this); + } catch (Exception e) { + System.err.println("Error, disabling serialAvailable() for "+port.getPortName()); + System.err.println(e.getLocalizedMessage()); + serialAvailableMethod = null; } } } - /** - * @generate Serial_buffer.xml - * @webref serial:serial - * @usage web_application - * @param count number of bytes to buffer - */ - public void buffer(int count) { - bufferUntil = false; - bufferSize = count; - } - - - /** - * @generate Serial_bufferUntil.xml - * @webref serial:serial - * @usage web_application - * @param what the value to buffer until - */ - public void bufferUntil(int what) { - bufferUntil = true; - bufferUntilByte = (byte) what; - } - - - /** - * @generate Serial_available.xml - * @webref serial:serial - * @usage web_application - */ public int available() { - return (bufferLast - bufferIndex); + return (inBuffer-readOffset); } + + public void buffer(int size) { + bufferUntilSize = size; + } - /** - * @generate Serial_clear.xml - * @webref serial:serial - * @usage web_application - */ + + public void bufferUntil(int inByte) { + bufferUntilSize = 0; + bufferUntilByte = (byte)inByte; + } + + public void clear() { - bufferLast = 0; - bufferIndex = 0; - } - - - /** - * @generate Serial_read.xml - * @webref serial:serial - * @usage web_application - */ - public int read() { - if (bufferIndex == bufferLast) return -1; - synchronized (buffer) { - int outgoing = buffer[bufferIndex++] & 0xff; - if (bufferIndex == bufferLast) { // rewind - bufferIndex = 0; - bufferLast = 0; - } - return outgoing; + inBuffer = 0; + readOffset = 0; } } - /** - * @generate Serial_last.xml - *

      Advanced

      - * Same as read() but returns the very last value received - * and clears the buffer. Useful when you just want the most - * recent value sent over the port. - * @webref serial:serial - * @usage web_application - */ + public boolean getCTS() { + try { + return port.isCTS(); + } catch (SerialPortException e) { + throw new RuntimeException("Error reading the CTS line: " + e.getExceptionType()); + } + } + + + public boolean getDSR() { + try { + return port.isDSR(); + } catch (SerialPortException e) { + throw new RuntimeException("Error reading the DSR line: " + e.getExceptionType()); + } + } + + + public static Map getProperties(String portName) { + return SerialPortList.getPortProperties(portName); + } + + public int last() { - if (bufferIndex == bufferLast) return -1; + if (inBuffer == readOffset) { + return -1; + } + synchronized (buffer) { - int outgoing = buffer[bufferLast-1]; - bufferIndex = 0; - bufferLast = 0; - return outgoing; + int ret = buffer[inBuffer-1] & 0xFF; + inBuffer = 0; + readOffset = 0; + return ret; } } + + public char lastChar() { + return (char)last(); + } - /** - * @generate Serial_readChar.xml - * @webref serial:serial - * @usage web_application - */ + + public static String[] list() { + // returns list sorted alphabetically, thus cu.* comes before tty.* + // this was different with RXTX + return SerialPortList.getPortNames(); + } + + + public int read() { + if (inBuffer == readOffset) { + return -1; + } + + synchronized (buffer) { + int ret = buffer[readOffset++] & 0xFF; + if (inBuffer == readOffset) { + inBuffer = 0; + readOffset = 0; + } + return ret; + } + } + + + public byte[] readBytes() { + if (inBuffer == readOffset) { + return null; + } + + synchronized (buffer) { + byte[] ret = new byte[inBuffer-readOffset]; + System.arraycopy(buffer, readOffset, ret, 0, ret.length); + inBuffer = 0; + readOffset = 0; + return ret; + } + } + + + public int readBytes(byte[] dest) { + if (inBuffer == readOffset) { + return 0; + } + + synchronized (buffer) { + int toCopy = inBuffer-readOffset; + if (dest.length < toCopy) { + toCopy = dest.length; + } + System.arraycopy(buffer, readOffset, dest, 0, toCopy); + readOffset += toCopy; + if (inBuffer == readOffset) { + inBuffer = 0; + readOffset = 0; + } + return toCopy; + } + } + + + public byte[] readBytesUntil(int inByte) { + if (inBuffer == readOffset) { + return null; + } + + synchronized (buffer) { + // look for needle in buffer + int found = -1; + for (int i=readOffset; i < inBuffer; i++) { + if (buffer[i] == (byte)inByte) { + found = i; + break; + } + } + if (found == -1) { + return null; + } + + int toCopy = found-readOffset+1; + byte[] dest = new byte[toCopy]; + System.arraycopy(buffer, readOffset, dest, 0, toCopy); + readOffset += toCopy; + if (inBuffer == readOffset) { + inBuffer = 0; + readOffset = 0; + } + return dest; + } + } + + + public int readBytesUntil(int inByte, byte[] dest) { + if (inBuffer == readOffset) { + return 0; + } + + synchronized (buffer) { + // look for needle in buffer + int found = -1; + for (int i=readOffset; i < inBuffer; i++) { + if (buffer[i] == (byte)inByte) { + found = i; + break; + } + } + if (found == -1) { + return 0; + } + + // check if bytes to copy fit in dest + int toCopy = found-readOffset+1; + if (dest.length < toCopy) { + System.err.println( "The buffer passed to readBytesUntil() is to small " + + "to contain " + toCopy + " bytes up to and including " + + "char " + (byte)inByte); + return -1; + } + System.arraycopy(buffer, readOffset, dest, 0, toCopy); + readOffset += toCopy; + if (inBuffer == readOffset) { + inBuffer = 0; + readOffset = 0; + } + return toCopy; + } + } + + public char readChar() { - if (bufferIndex == bufferLast) return (char)(-1); return (char) read(); } - - /** - * @generate Serial_lastChar.xml - * @webref serial:serial - * @usage web_application - */ - public char lastChar() { - if (bufferIndex == bufferLast) return (char)(-1); - return (char) last(); - } - - - /** - * @generate Serial_readBytes.xml - * @webref serial:serial - * @usage web_application - */ - public byte[] readBytes() { - if (bufferIndex == bufferLast) return null; - - synchronized (buffer) { - int length = bufferLast - bufferIndex; - byte outgoing[] = new byte[length]; - System.arraycopy(buffer, bufferIndex, outgoing, 0, length); - - bufferIndex = 0; // rewind - bufferLast = 0; - return outgoing; - } - } - - - /** - *

      Advanced

      - * Grab whatever is in the serial buffer, and stuff it into a - * byte buffer passed in by the user. This is more memory/time - * efficient than readBytes() returning a byte[] array. - * - * Returns an int for how many bytes were read. If more bytes - * are available than can fit into the byte array, only those - * that will fit are read. - */ - public int readBytes(byte outgoing[]) { - if (bufferIndex == bufferLast) return 0; - - synchronized (buffer) { - int length = bufferLast - bufferIndex; - if (length > outgoing.length) length = outgoing.length; - System.arraycopy(buffer, bufferIndex, outgoing, 0, length); - - bufferIndex += length; - if (bufferIndex == bufferLast) { - bufferIndex = 0; // rewind - bufferLast = 0; - } - return length; - } - } - - - /** - * @generate Serial_readBytesUntil.xml - * @webref serial:serial - * @usage web_application - * @param interesting character designated to mark the end of the data - */ - public byte[] readBytesUntil(int interesting) { - if (bufferIndex == bufferLast) return null; - byte what = (byte)interesting; - - synchronized (buffer) { - int found = -1; - for (int k = bufferIndex; k < bufferLast; k++) { - if (buffer[k] == what) { - found = k; - break; - } - } - if (found == -1) return null; - - int length = found - bufferIndex + 1; - byte outgoing[] = new byte[length]; - System.arraycopy(buffer, bufferIndex, outgoing, 0, length); - - bufferIndex += length; - if (bufferIndex == bufferLast) { - bufferIndex = 0; // rewind - bufferLast = 0; - } - return outgoing; - } - } - - - /** - *

      Advanced

      - * If outgoing[] is not big enough, then -1 is returned, - * and an error message is printed on the console. - * If nothing is in the buffer, zero is returned. - * If 'interesting' byte is not in the buffer, then 0 is returned. - * @param outgoing passed in byte array to be altered - */ - public int readBytesUntil(int interesting, byte outgoing[]) { - if (bufferIndex == bufferLast) return 0; - byte what = (byte)interesting; - - synchronized (buffer) { - int found = -1; - for (int k = bufferIndex; k < bufferLast; k++) { - if (buffer[k] == what) { - found = k; - break; - } - } - if (found == -1) return 0; - - int length = found - bufferIndex + 1; - if (length > outgoing.length) { - System.err.println("readBytesUntil() byte buffer is" + - " too small for the " + length + - " bytes up to and including char " + interesting); - return -1; - } - //byte outgoing[] = new byte[length]; - System.arraycopy(buffer, bufferIndex, outgoing, 0, length); - - bufferIndex += length; - if (bufferIndex == bufferLast) { - bufferIndex = 0; // rewind - bufferLast = 0; - } - return length; - } - } - - - /** - * @generate Serial_readString.xml - * @webref serial:serial - * @usage web_application - */ + public String readString() { - if (bufferIndex == bufferLast) return null; + if (inBuffer == readOffset) { + return null; + } return new String(readBytes()); } - - /** - * @generate Serial_readStringUntil.xml - *

      Advanced

      - * If you want to move Unicode data, you can first convert the - * String to a byte stream in the representation of your choice - * (i.e. UTF8 or two-byte Unicode data), and send it as a byte array. - * - * @webref serial:serial - * @usage web_application - * @param interesting character designated to mark the end of the data - */ - public String readStringUntil(int interesting) { - byte b[] = readBytesUntil(interesting); - if (b == null) return null; - return new String(b); - } - - - /** - *

      Advanced

      - * This will handle both ints, bytes and chars transparently. - * @param what data to write - */ - public void write(int what) { // will also cover char - try { - output.write(what & 0xff); // for good measure do the & - output.flush(); // hmm, not sure if a good idea - - } catch (Exception e) { // null pointer or serial port dead - errorMessage("write", e); - } - } - - /** - * @param bytes[] data to write - */ - public void write(byte bytes[]) { - try { - output.write(bytes); - output.flush(); // hmm, not sure if a good idea - - } catch (Exception e) { // null pointer or serial port dead - //errorMessage("write", e); - e.printStackTrace(); + + public String readStringUntil(int inByte) { + byte temp[] = readBytesUntil(inByte); + if (temp == null) { + return null; + } else { + return new String(temp); } } - /** - * @generate Serial_write.xml - *

      Advanced

      - * Write a String to the output. Note that this doesn't account - * for Unicode (two bytes per char), nor will it send UTF8 - * characters.. It assumes that you mean to send a byte buffer - * (most often the case for networking and serial i/o) and - * will only use the bottom 8 bits of each char in the string. - * (Meaning that internally it uses String.getBytes) - * - * If you want to move Unicode data, you can first convert the - * String to a byte stream in the representation of your choice - * (i.e. UTF8 or two-byte Unicode data), and send it as a byte array. - * - * @webref serial:serial - * @usage web_application - * @param what data to write - */ - public void write(String what) { - write(what.getBytes()); - } - - - /** - * @generate Serial_list.xml - *

      Advanced

      - * If this just hangs and never completes on Windows, - * it may be because the DLL doesn't have its exec bit set. - * Why the hell that'd be the case, who knows. - * - * @webref serial - * @usage web_application - */ - static public String[] list() { - Vector list = new Vector(); - try { - //System.err.println("trying"); - Enumeration portList = CommPortIdentifier.getPortIdentifiers(); - //System.err.println("got port list"); - while (portList.hasMoreElements()) { - CommPortIdentifier portId = - (CommPortIdentifier) portList.nextElement(); - //System.out.println(portId); - - if (portId.getPortType() == CommPortIdentifier.PORT_SERIAL) { - String name = portId.getName(); - list.addElement(name); + public void serialEvent(SerialPortEvent event) { + if (event.getEventType() == SerialPortEvent.RXCHAR) { + int toRead; + try { + while (0 < (toRead = port.getInputBufferBytesCount())) { + // this method can be called from the context of another thread + synchronized (buffer) { + // read one byte at a time if the sketch is using serialEvent + if (serialEventMethod != null) { + toRead = 1; + } + // enlarge buffer if necessary + if (buffer.length < inBuffer+toRead) { + byte temp[] = new byte[buffer.length<<1]; + System.arraycopy(buffer, 0, temp, 0, inBuffer); + buffer = temp; + } + // read an array of bytes and copy it into our buffer + byte[] read = port.readBytes(toRead); + System.arraycopy(read, 0, buffer, inBuffer, read.length); + inBuffer += read.length; + } + if (serialEventMethod != null) { + if ((0 < bufferUntilSize && bufferUntilSize <= inBuffer-readOffset) || + (0 == bufferUntilSize && bufferUntilByte == buffer[inBuffer-1])) { + try { + // serialEvent() is invoked in the context of the current (serial) thread + // which means that serialization and atomic variables need to be used to + // guarantee reliable operation (and better not draw() etc..) + // serialAvailable() does not provide any real benefits over using + // available() and read() inside draw - but this function has no + // thread-safety issues since it's being invoked during pre in the context + // of the Processing applet + serialEventMethod.invoke(parent, this); + } catch (Exception e) { + System.err.println("Error, disabling serialEvent() for "+port.getPortName()); + System.err.println(e.getLocalizedMessage()); + serialEventMethod = null; + } + } + } + invokeSerialAvailable = true; } + } catch (SerialPortException e) { + throw new RuntimeException("Error reading from serial port " + e.getPortName() + ": " + e.getExceptionType()); } - - } catch (UnsatisfiedLinkError e) { - //System.err.println("1"); - errorMessage("ports", e); - - } catch (Exception e) { - //System.err.println("2"); - errorMessage("ports", e); } - //System.err.println("move out"); - String outgoing[] = new String[list.size()]; - list.copyInto(outgoing); - return outgoing; } + + public void setDTR(boolean state) { + // there is no way to influence the behavior of the DTR line when opening the serial port + // this means that at least on Linux and OS X, Arduino devices are always reset + try { + port.setDTR(state); + } catch (SerialPortException e) { + throw new RuntimeException("Error setting the DTR line: " + e.getExceptionType()); + } + } - /** - * General error reporting, all corraled here just in case - * I think of something slightly more intelligent to do. - */ - static public void errorMessage(String where, Throwable e) { - e.printStackTrace(); - throw new RuntimeException("Error inside Serial." + where + "()"); + + public void setRTS(boolean state) { + try { + port.setRTS(state); + } catch (SerialPortException e) { + throw new RuntimeException("Error setting the RTS line: " + e.getExceptionType()); + } + } + + + public void stop() { + try { + port.closePort(); + } catch (SerialPortException e) { + // ignored + } + inBuffer = 0; + readOffset = 0; + } + + + public void write(byte[] src) { + try { + // this might block if the serial device is not yet ready (esp. tty devices under OS X) + port.writeBytes(src); + // we used to call flush() here + } catch (SerialPortException e) { + throw new RuntimeException("Error writing to serial port " + e.getPortName() + ": " + e.getExceptionType()); + } + } + + + public void write(int src) { + try { + port.writeInt(src); + } catch (SerialPortException e) { + throw new RuntimeException("Error writing to serial port " + e.getPortName() + ": " + e.getExceptionType()); + } + } + + + public void write(String src) { + try { + port.writeString(src); + } catch (SerialPortException e) { + throw new RuntimeException("Error writing to serial port " + e.getPortName() + ": " + e.getExceptionType()); + } } } - - - /* - class SerialMenuListener implements ItemListener { - //public SerialMenuListener() { } - - public void itemStateChanged(ItemEvent e) { - int count = serialMenu.getItemCount(); - for (int i = 0; i < count; i++) { - ((CheckboxMenuItem)serialMenu.getItem(i)).setState(false); - } - CheckboxMenuItem item = (CheckboxMenuItem)e.getSource(); - item.setState(true); - String name = item.getLabel(); - //System.out.println(item.getLabel()); - PdeBase.properties.put("serial.port", name); - //System.out.println("set to " + get("serial.port")); - } - } - */ - - - /* - protected Vector buildPortList() { - // get list of names for serial ports - // have the default port checked (if present) - Vector list = new Vector(); - - //SerialMenuListener listener = new SerialMenuListener(); - boolean problem = false; - - // if this is failing, it may be because - // lib/javax.comm.properties is missing. - // java is weird about how it searches for java.comm.properties - // so it tends to be very fragile. i.e. quotes in the CLASSPATH - // environment variable will hose things. - try { - //System.out.println("building port list"); - Enumeration portList = CommPortIdentifier.getPortIdentifiers(); - while (portList.hasMoreElements()) { - CommPortIdentifier portId = - (CommPortIdentifier) portList.nextElement(); - //System.out.println(portId); - - if (portId.getPortType() == CommPortIdentifier.PORT_SERIAL) { - //if (portId.getName().equals(port)) { - String name = portId.getName(); - //CheckboxMenuItem mi = - //new CheckboxMenuItem(name, name.equals(defaultName)); - - //mi.addItemListener(listener); - //serialMenu.add(mi); - list.addElement(name); - } - } - } catch (UnsatisfiedLinkError e) { - e.printStackTrace(); - problem = true; - - } catch (Exception e) { - System.out.println("exception building serial menu"); - e.printStackTrace(); - } - - //if (serialMenu.getItemCount() == 0) { - //System.out.println("dimming serial menu"); - //serialMenu.setEnabled(false); - //} - - // only warn them if this is the first time - if (problem && PdeBase.firstTime) { - JOptionPane.showMessageDialog(this, //frame, - "Serial port support not installed.\n" + - "Check the readme for instructions\n" + - "if you need to use the serial port. ", - "Serial Port Warning", - JOptionPane.WARNING_MESSAGE); - } - return list; - } - */ - - diff --git a/java/libraries/serial/test/serial_latency/arduino/serial_latency.ino b/java/libraries/serial/test/serial_latency/arduino/serial_latency.ino new file mode 100644 index 000000000..d4711fe64 --- /dev/null +++ b/java/libraries/serial/test/serial_latency/arduino/serial_latency.ino @@ -0,0 +1,12 @@ +void setup() { + Serial.begin(115200); +} + +void loop() { + while (true) { + int in = Serial.read(); + if (in != -1) { + Serial.write(in); + } + } +} diff --git a/java/libraries/serial/test/serial_latency/serial_latency.pde b/java/libraries/serial/test/serial_latency/serial_latency.pde new file mode 100644 index 000000000..17e0a2756 --- /dev/null +++ b/java/libraries/serial/test/serial_latency/serial_latency.pde @@ -0,0 +1,53 @@ +// Arduino Duemilanove (168) on OS X 10.9 +// with either 115200 or 38400 bps +// on Processing 2.0.3 (cu & tty): 24 ms avg, 35 ms max +// on Processing 2.1b1 (cu & tty): 18 ms avg, 35 ms max + +import processing.serial.*; +Serial serial; +int start; +byte out = '@'; +int last_send = 0; +byte[] in = new byte[32768]; +long num_fail = 0; +long num_recv = 0; +int max_latency = 0; + +void setup() { + println(serial.list()); + // change this accordingly + serial = new Serial(this, serial.list()[0], 115200); + start = millis(); +} + +void draw() { + background(255); + if (0 < serial.available()) { + int recv = serial.readBytes(in); + for (int i=0; i < recv; i++) { + if (in[i] == out) { + num_recv++; + int now = millis(); + if (max_latency < now-last_send) { + max_latency = now-last_send; + } + last_send = 0; + } + } + } + if (last_send != 0 && 1000 < millis()-last_send) { + num_fail++; + last_send = 0; + println(num_fail+" bytes timed out"); + } + if (last_send == 0) { + if (out == 'Z') { + out = '@'; + } + serial.write(++out); + last_send = millis(); + } + fill(0); + text(((millis()-start)/(float)num_recv+" ms avg"), 0, height/2); + text(max_latency+" ms max", 0, height/2+20); +} diff --git a/java/libraries/serial/test/serial_throughput/arduino/serial_thoughput.ino b/java/libraries/serial/test/serial_throughput/arduino/serial_thoughput.ino new file mode 100644 index 000000000..dc950ce5a --- /dev/null +++ b/java/libraries/serial/test/serial_throughput/arduino/serial_thoughput.ino @@ -0,0 +1,9 @@ +void setup() { + Serial.begin(115200); +} + +void loop() { + while (true) { + Serial.write('.'); + } +} diff --git a/java/libraries/serial/test/serial_throughput/serial_throughput.pde b/java/libraries/serial/test/serial_throughput/serial_throughput.pde new file mode 100644 index 000000000..477fdf3bc --- /dev/null +++ b/java/libraries/serial/test/serial_throughput/serial_throughput.pde @@ -0,0 +1,32 @@ +import processing.serial.*; +Serial serial; +int start; +byte[] in = new byte[32768]; +long num_ok = 0; +long num_fail = 0; +long num_recv = 0; + +void setup() { + println(serial.list()); + // change this accordingly + serial = new Serial(this, serial.list()[0], 115200); + start = millis(); +} + +void draw() { + background(255); + if (0 < serial.available()) { + int recv = serial.readBytes(in); + for (int i=0; i < recv; i++) { + if (in[i] == '.') { + num_ok++; + } else { + num_fail++; + println("Received "+num_fail+" unexpected bytes"); + } + num_recv++; + } + } + fill(0); + text(num_recv/((millis()-start)/1000.0), 0, height/2); +} diff --git a/java/libraries/video/build.xml b/java/libraries/video/build.xml old mode 100644 new mode 100755 index 1bf2e5899..202242085 --- a/java/libraries/video/build.xml +++ b/java/libraries/video/build.xml @@ -18,7 +18,11 @@ srcdir="src" destdir="bin" encoding="UTF-8" includeAntRuntime="false" - classpath="../../../core/library/core.jar; library/gstreamer-java.jar; library/jna.jar" /> + classpath="../../../core/library/core.jar; library/gstreamer-java.jar; library/jna.jar" + nowarn="true" + compiler="org.eclipse.jdt.core.JDTCompilerAdapter"> + + diff --git a/java/libraries/video/examples/Capture/GettingStartedCapture/GettingStartedCapture.pde b/java/libraries/video/examples/Capture/GettingStartedCapture/GettingStartedCapture.pde index 33163dc0e..517ba8786 100644 --- a/java/libraries/video/examples/Capture/GettingStartedCapture/GettingStartedCapture.pde +++ b/java/libraries/video/examples/Capture/GettingStartedCapture/GettingStartedCapture.pde @@ -13,11 +13,13 @@ void setup() { String[] cameras = Capture.list(); - if (cameras.length == 0) { + if (cameras == null) { + println("Failed to retrieve the list of available cameras, will try the default..."); + cam = new Capture(this, 640, 480); + } if (cameras.length == 0) { println("There are no cameras available for capture."); exit(); - } - else { + } else { println("Available cameras:"); for (int i = 0; i < cameras.length; i++) { println(cameras[i]); diff --git a/java/libraries/video/examples/Capture/HsvSpace/HsvSpace.pde b/java/libraries/video/examples/Capture/HsvSpace/HsvSpace.pde index 9f72fe96f..c77208410 100644 --- a/java/libraries/video/examples/Capture/HsvSpace/HsvSpace.pde +++ b/java/libraries/video/examples/Capture/HsvSpace/HsvSpace.pde @@ -80,8 +80,9 @@ void draw() { rotateY(radians(36 + leftRightAngle)); //, 0, 1, 0); rotateX(radians(-228 + upDownAngle)); //, 1, 0, 0); - if (blobby) { - stroke(0.35, 0.35, 0.25, 0.15); + strokeWeight(0.1); + if (blobby) { + stroke(0.35, 0.35, 0.25, 0.15); wireCone(MAX_RADIUS, MAX_RADIUS * CONE_HEIGHT, 18, 18); } else { diff --git a/java/libraries/video/src/processing/video/Capture.java b/java/libraries/video/src/processing/video/Capture.java index 6e50442f1..5d4221fb5 100644 --- a/java/libraries/video/src/processing/video/Capture.java +++ b/java/libraries/video/src/processing/video/Capture.java @@ -107,11 +107,12 @@ public class Capture extends PImage implements PConstants { protected int reqHeight; protected boolean useBufferSink = false; + protected boolean outdatedPixels = true; protected Object bufferSink; protected Method sinkCopyMethod; protected Method sinkSetMethod; protected Method sinkDisposeMethod; - protected Method sinkGetMethod; + protected Method sinkGetMethod; protected String copyMask; protected Buffer natBuffer = null; protected BufferDataAppSink natSink = null; @@ -238,7 +239,7 @@ public class Capture extends PImage implements PConstants { /** * Disposes all the native resources associated to this capture device. - * + * * NOTE: This is not official API and may/will be removed at any time. */ public void dispose() { @@ -270,6 +271,10 @@ public class Capture extends PImage implements PConstants { pipeline.dispose(); pipeline = null; + + parent.g.removeCache(this); + parent.unregisterMethod("dispose", this); + parent.unregisterMethod("post", this); } } @@ -303,11 +308,11 @@ public class Capture extends PImage implements PConstants { /** * ( begin auto-generated from Capture_start.xml ) - * + * * Starts capturing frames from the selected device. - * + * * ( end auto-generated ) - * + * * @webref capture * @brief Starts capturing frames from the selected device */ @@ -371,6 +376,7 @@ public class Capture extends PImage implements PConstants { } if (useBufferSink) { // The native buffer from gstreamer is copied to the buffer sink. + outdatedPixels = true; if (natBuffer == null) { return; } @@ -419,10 +425,10 @@ public class Capture extends PImage implements PConstants { newFrame = true; } - + public synchronized void loadPixels() { super.loadPixels(); - if (useBufferSink) { + if (useBufferSink) { if (natBuffer != null) { // This means that the OpenGL texture hasn't been created so far (the // video frame not drawn using image()), but the user wants to use the @@ -430,18 +436,38 @@ public class Capture extends PImage implements PConstants { IntBuffer buf = natBuffer.getByteBuffer().asIntBuffer(); buf.rewind(); buf.get(pixels); - Video.convertToARGB(pixels, width, height); + Video.convertToARGB(pixels, width, height); } else if (sinkGetMethod != null) { try { + // sinkGetMethod will copy the latest buffer to the pixels array, + // and the pixels will be copied to the texture when the OpenGL + // renderer needs to draw it. sinkGetMethod.invoke(bufferSink, new Object[] { pixels }); } catch (Exception e) { e.printStackTrace(); - } - } + } + } + + outdatedPixels = false; } } - - + + + public int get(int x, int y) { + if (outdatedPixels) loadPixels(); + return super.get(x, y); + } + + + protected void getImpl(int sourceX, int sourceY, + int sourceWidth, int sourceHeight, + PImage target, int targetX, int targetY) { + if (outdatedPixels) loadPixels(); + super.getImpl(sourceX, sourceY, sourceWidth, sourceHeight, + target, targetX, targetY); + } + + //////////////////////////////////////////////////////////// // List methods. @@ -940,7 +966,7 @@ public class Capture extends PImage implements PConstants { /** * Uses a generic object as handler of the capture. This object should have a - * movieEvent method that receives a GSMovie argument. This method will + * captureEvent method that receives a Capture argument. This method will * be called upon a new frame read event. * */ @@ -948,8 +974,17 @@ public class Capture extends PImage implements PConstants { eventHandler = obj; try { - captureEventMethod = parent.getClass().getMethod("captureEvent", - new Class[] { Capture.class }); + captureEventMethod = obj.getClass().getMethod("captureEvent", Capture.class); + return; + } catch (Exception e) { + // no such method, or an error.. which is fine, just ignore + } + + // The captureEvent method may be declared as receiving Object, rather + // than Capture. + try { + captureEventMethod = obj.getClass().getMethod("captureEvent", Object.class); + return; } catch (Exception e) { // no such method, or an error.. which is fine, just ignore } @@ -982,18 +1017,7 @@ public class Capture extends PImage implements PConstants { copyPixels = null; return; } - - // Creates a movieEvent. - if (captureEventMethod != null) { - try { - captureEventMethod.invoke(eventHandler, new Object[] { this }); - } catch (Exception e) { - System.err.println( - "error, disabling captureEvent() for capture object"); - e.printStackTrace(); - captureEventMethod = null; - } - } + fireCaptureEvent(); } @@ -1002,20 +1026,23 @@ public class Capture extends PImage implements PConstants { bufWidth = w; bufHeight = h; if (natBuffer != null) { - // To handle the situation where read() is not called in the sketch, so - // that the native buffers are not being sent to the sinke, and therefore, not disposed - // by it. - natBuffer.dispose(); - } + // To handle the situation where read() is not called in the sketch, + // so that the native buffers are not being sent to the sink, + // and therefore, not disposed by it. + natBuffer.dispose(); + } natBuffer = buffer; + fireCaptureEvent(); + } - // Creates a movieEvent. + + private void fireCaptureEvent() { if (captureEventMethod != null) { try { - captureEventMethod.invoke(eventHandler, new Object[] { this }); + captureEventMethod.invoke(eventHandler, this); + } catch (Exception e) { - System.err.println( - "error, disabling captureEvent() for capture object"); + System.err.println("error, disabling captureEvent()"); e.printStackTrace(); captureEventMethod = null; } @@ -1104,7 +1131,7 @@ public class Capture extends PImage implements PConstants { * copy the frames to OpenGL. * * NOTE: This is not official API and may/will be removed at any time. - * + * * @param Object dest */ public void setBufferSink(Object sink) { @@ -1115,7 +1142,7 @@ public class Capture extends PImage implements PConstants { /** * Sets the object to use as destination for the frames read from the stream. - * + * * NOTE: This is not official API and may/will be removed at any time. * * @param Object dest @@ -1160,22 +1187,22 @@ public class Capture extends PImage implements PConstants { throw new RuntimeException("Capture: provided sink object doesn't have "+ "a setBufferSource method."); } - + try { - sinkDisposeMethod = bufferSink.getClass().getMethod("disposeSourceBuffer", + sinkDisposeMethod = bufferSink.getClass().getMethod("disposeSourceBuffer", new Class[] { }); } catch (Exception e) { throw new RuntimeException("Capture: provided sink object doesn't have " + "a disposeSourceBuffer method."); } - + try { - sinkGetMethod = bufferSink.getClass().getMethod("getBufferPixels", + sinkGetMethod = bufferSink.getClass().getMethod("getBufferPixels", new Class[] { int[].class }); } catch (Exception e) { throw new RuntimeException("Capture: provided sink object doesn't have " + "a getBufferPixels method."); - } + } } @@ -1186,8 +1213,8 @@ public class Capture extends PImage implements PConstants { copyMask = "red_mask=(int)0xFF, green_mask=(int)0xFF00, blue_mask=(int)0xFF0000"; } } - - + + public synchronized void post() { if (useBufferSink && sinkDisposeMethod != null) { try { @@ -1196,5 +1223,5 @@ public class Capture extends PImage implements PConstants { e.printStackTrace(); } } - } -} \ No newline at end of file + } +} diff --git a/java/libraries/video/src/processing/video/Movie.java b/java/libraries/video/src/processing/video/Movie.java index 60dbb64c4..958be1109 100644 --- a/java/libraries/video/src/processing/video/Movie.java +++ b/java/libraries/video/src/processing/video/Movie.java @@ -79,6 +79,7 @@ public class Movie extends PImage implements PConstants { protected boolean seeking = false; protected boolean useBufferSink = false; + protected boolean outdatedPixels = true; protected Object bufferSink; protected Method sinkCopyMethod; protected Method sinkSetMethod; @@ -135,6 +136,10 @@ public class Movie extends PImage implements PConstants { playbin.dispose(); playbin = null; + + parent.g.removeCache(this); + parent.unregisterMethod("dispose", this); + parent.unregisterMethod("post", this); } } @@ -318,7 +323,41 @@ public class Movie extends PImage implements PConstants { // (like seek in this case) has completed seeking = true; playbin.getState(); - seeking = false; + seeking = false; + /* + if (seeking) return; // don't seek again until the current seek operation is done. + + if (!sinkReady) { + initSink(); + } + + // Round the time to a multiple of the source framerate, in + // order to eliminate stutter. Suggested by Daniel Shiffman + float fps = getSourceFrameRate(); + int frame = (int)(where * fps); + final float seconds = frame / fps; + + // Put the seek operation inside a thread to avoid blocking the main + // animation thread + Thread seeker = new Thread() { + @Override + public void run() { + long pos = Video.secToNanoLong(seconds); + boolean res = playbin.seek(rate, Format.TIME, SeekFlags.FLUSH, + SeekType.SET, pos, SeekType.NONE, -1); + if (!res) { + PGraphics.showWarning("Seek operation failed."); + } + + // getState() will wait until any async state change + // (like seek in this case) has completed + seeking = true; + playbin.getState(); + seeking = false; + } + }; + seeker.start(); + */ } @@ -483,6 +522,7 @@ public class Movie extends PImage implements PConstants { } if (useBufferSink) { // The native buffer from gstreamer is copied to the buffer sink. + outdatedPixels = true; if (natBuffer == null) { return; } @@ -557,15 +597,35 @@ public class Movie extends PImage implements PConstants { Video.convertToARGB(pixels, width, height); } else if (sinkGetMethod != null) { try { + // sinkGetMethod will copy the latest buffer to the pixels array, + // and the pixels will be copied to the texture when the OpenGL + // renderer needs to draw it. sinkGetMethod.invoke(bufferSink, new Object[] { pixels }); } catch (Exception e) { e.printStackTrace(); } - } + } + + outdatedPixels = false; } } + public int get(int x, int y) { + if (outdatedPixels) loadPixels(); + return super.get(x, y); + } + + + protected void getImpl(int sourceX, int sourceY, + int sourceWidth, int sourceHeight, + PImage target, int targetX, int targetY) { + if (outdatedPixels) loadPixels(); + super.getImpl(sourceX, sourceY, sourceWidth, sourceHeight, + target, targetX, targetY); + } + + //////////////////////////////////////////////////////////// // Initialization methods. @@ -662,10 +722,19 @@ public class Movie extends PImage implements PConstants { eventHandler = obj; try { - movieEventMethod = eventHandler.getClass().getMethod("movieEvent", - new Class[] { Movie.class }); + movieEventMethod = eventHandler.getClass().getMethod("movieEvent", Movie.class); + return; } catch (Exception e) { - // no such method, or an error.. which is fine, just ignore + // no such method, or an error... which is fine, just ignore + } + + // movieEvent can alternatively be defined as receiving an Object, to allow + // Processing mode implementors to support the video library without linking + // to it at build-time. + try { + movieEventMethod = eventHandler.getClass().getMethod("movieEvent", Object.class); + } catch (Exception e) { + // no such method, or an error... which is fine, just ignore } } @@ -748,20 +817,10 @@ public class Movie extends PImage implements PConstants { } if (playing) { - // Creates a movieEvent. - if (movieEventMethod != null) { - try { - movieEventMethod.invoke(eventHandler, new Object[] { this }); - } catch (Exception e) { - System.err.println("error, disabling movieEvent() for " + filename); - e.printStackTrace(); - movieEventMethod = null; - } - } + fireMovieEvent(); } } - protected synchronized void invokeEvent(int w, int h, Buffer buffer) { available = true; bufWidth = w; @@ -775,19 +834,22 @@ public class Movie extends PImage implements PConstants { natBuffer = buffer; if (playing) { - // Creates a movieEvent. - if (movieEventMethod != null) { - try { - movieEventMethod.invoke(eventHandler, new Object[] { this }); - } catch (Exception e) { - System.err.println("error, disabling movieEvent() for " + filename); - e.printStackTrace(); - movieEventMethod = null; - } - } + fireMovieEvent(); } } + private void fireMovieEvent() { + // Creates a movieEvent. + if (movieEventMethod != null) { + try { + movieEventMethod.invoke(eventHandler, this); + } catch (Exception e) { + System.err.println("error, disabling movieEvent() for " + filename); + e.printStackTrace(); + movieEventMethod = null; + } + } + } protected void eosEvent() { if (repeat) { diff --git a/todo.txt b/todo.txt index 9312d89df..340c441f5 100644 --- a/todo.txt +++ b/todo.txt @@ -1,100 +1,89 @@ -0221 pde -X add double quotes to readlink call, fixes issue w/ paths and spaces -X https://github.com/processing/processing/pull/2027 -X fix submitted by hamoid +0228 pde -_ changing modes brings the PDE back on the second screen -_ the Find window (also the save windows) also have the same problem - - -high -_ MovieMaker needs to be compiling as 1.6 -_ move old Google Code SVN back to processing.org -_ then cull out the old branches/tags from the Github repo -_ figure out Android build w/o javac so we can remove tools.jar and javac -_ also to the p5 repo with just a JRE -_ remove initRequirements from Base (no longer need JDI) -_ move this into Android mode? -_ "String index out of range" error -_ https://github.com/processing/processing/issues/1940 -_ freeze after splash screen on OS X (looks like core.jar in the path) -_ https://github.com/processing/processing/issues/1872 medium +_ possible to open a sketch multiple times +_ by double-clicking one of its files instead of the main pde file +_ https://github.com/processing/processing/issues/2506 +_ closing the color selector makes things freeze (only Linux and Windows?) +_ https://github.com/processing/processing/issues/2381 +_ check on why 2x core.jar inside the Java folder +_ maybe OS X Java can't look in subfolders? (just auto-adds things) +_ display "1" is not correct in 2.1.2 +_ https://github.com/processing/processing/issues/2502 +_ re/move things from Google Code downloads +_ https://code.google.com/p/support/wiki/DownloadsFAQ +_ clean out the repo +_ https://github.com/processing/processing/issues/1898 +_ requires re-forking, so still a ton of work +_ remove non-web stuff from web +_ remove non-android stuff from android +_ remove web and android from the main repo +_ add font fixes to the rest of the API +_ https://github.com/processing/processing/commit/eaff673d173b2d27f276cf5c59e3abf6c0fab86b +_ g2.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, +_ RenderingHints.VALUE_FRACTIONALMETRICS_ON); +_ should default to the local Java on Windows and Linux +_ have export apps default to the local JRE +_ Linux is probably using the system JRE if available +_ launch4j may be all set, but double-check +_ try installing 10.7.3 on Mac Mini and check whether things run +_ make sure it's only running on 64-bit machines? +_ use platformDelete() to remove untitled sketches? +_ would allow us to use the /tmp folder _ change to using platformDelete() instead of Base.removeDir() where possible _ verify that the OS X version uses the real call _ and doesn't just look for .Trash _ getCoreLibrary() is breaking OpenGL _ "new Library()" constructor needs to go back to private _ add .bat file to lib on windows so that we can get better debugging info +_ changing modes brings the PDE back on the second screen +_ the Find window (also the save windows) also have the same problem +_ move old Google Code SVN back to processing.org +_ then cull out the old branches/tags from the Github repo +_ and/or start bundling separate source downloads +_ "String index out of range" error +_ https://github.com/processing/processing/issues/1940 +_ look through all isPopupTrigger() code +_ make sure both press/release are implemented +_ emacs style errors in commander aren't quite right +_ https://github.com/processing/processing/issues/2158 +_ add documentation for how to run mode development from Eclipse +_ implementation/changes from JDF +_ modes are being loaded multiple times, which can cause trouble +_ add minimum version required (or max version?) to libraries/modes/etc +_ no high-res display support for the PDE +_ PDE and sketches are 2x smaller on high-res Windows 8 machines +_ https://github.com/processing/processing/issues/2411 -7u40 switch -_ change how export is handled -_ remove ability to export cross-platform apps -_ add ability to embed the current JRE -_ appbundler fixes/changes -_ icon location uses path, even when embedded -_ add indents to the Info.plist output file -_ inside writeInfoPlist from AppBundlerTask.java -_ use Contents/Resources/Java instead of Contents/Java? -_ this is in main.m. why the change? -_ any missing args from our app (copyrights/versions?) -_ make sure it's only running on 64-bit machines? -_ add MinimumSystemVersion? -_ the "Classes" folder is included -_ appears to be line 138 of main.m -_ maybe this is a holdover from OS X Java? -_ bring back the splash screen? -direct link for download (replace .md5 with .dmg on that page) -http://www.java.net/download/jdk7u40/archive/b38/binaries/jre-7u40-ea-bin-b38-macosx-x86_64-07_aug_2013.dmg -http://www.java.net/download/jdk7u40/archive/b40/binaries/jre-7u40-fcs-bin-b40-macosx-x86_64-16_aug_2013.dmg +pulls +_ may need a progress bar for "save as" +_ or just the file copy function in general +_ since it may take a long time (i.e. 1000s of screen grabs) +_ http://code.google.com/p/processing/issues/detail?id=31 +_ https://github.com/processing/processing/issues/70 +_ https://github.com/processing/processing/pull/2370 -needs cookie for proper download -http://download.oracle.com/otn-pub/java/jdk/7u25-b15/jre-7u25-linux-i586.tar.gz -http://ivan-site.com/2012/05/download-oracle-java-jre-jdk-using-a-script/ -wget --no-cookies --no-check-certificate --header "Cookie: gpw_e24=http%3A%2F%2Fwww.oracle.com%2F" http://download.oracle.com/otn-pub/java/jdk/7u25-b15/jre-7u25-linux-i586.tar.gz +post 2.1 cleaning +_ remove the prefs for 32/64-bit from Preferences +_ remove the extra OS X cruft inside Runner.java +_ exclude 'fonts' folder from build (since it's going into the JRE) -http://download.oracle.com/otn-pub/java/jdk/7u25-b15/jre-7u25-linux-i586.tar.gz -http://download.oracle.com/otn-pub/java/jdk/7u25-b15/jre-7u25-linux-x64.tar.gz -http://download.oracle.com/otn-pub/java/jdk/7u25-b17/jre-7u25-windows-i586.tar.gz -http://download.oracle.com/otn-pub/java/jdk/7u25-b17/jre-7u25-windows-x64.tar.gz - -or a direct download from http://www.java.com/en/download/manual.jsp -http://javadl.sun.com/webapps/download/AutoDL?BundleId=78695 Linux i386 -http://javadl.sun.com/webapps/download/AutoDL?BundleId=78697 Linux x64 -http://javadl.sun.com/webapps/download/AutoDL?BundleId=78698 OS X (64-bit) -http://javadl.sun.com/webapps/download/AutoDL?BundleId=79063 Windows (32?) -http://javadl.sun.com/webapps/download/AutoDL?BundleId=79065 Windows (64-bit) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . -DOC / Mess +DOC / Misc _ find in reference for copy() (on image) tries to open PVector.copy() _ might need disambiguation pages? _ if a reference page is missing, throws a bunch of exceptions _ i.e. PVector.copy() not in the reference -_ import p5 reference into the javadoc _ local web server to run reference from .zip? _ no more gazillion file nastiness _ yahoo search example is out of date (included in the examples? the book?) -_ document the move of the auto format menu -_ in the book(s)? in the reference? -_ improve documentation of the pdf stuff -_ be clearer about the font setup stuff -_ fonts by default not working that well? -_ jer: opengl2 tutorial -_ jer: android tutorial -_ probably later: examples into categories based on difficulty -_ add ratings/difficult level to examples online and in the pde -_ go through examples, figure out how to do many on the site w/ js instead - - -DOC / Write Me - _ stop() to shut down a sketch (but not quit/close window) _ actually pause/resume _ MIN_FLOAT, MAX_FLOAT, also the difference from the Java functions @@ -111,8 +100,6 @@ _ and that java2d should complain if people try it _ method to go from function name to the included examples where used? _ encourage use of set() instead of point() in the drawing api _ http://processing.org/discourse/yabb_beta/YaBB.cgi?board=SoftwareBugs;action=display;num=1114204116 -_ moviemaker video sizes / usability -_ http://processing.org/discourse/yabb_beta/YaBB.cgi?board=Exhibition;action=display;num=1211318862 _ other projects on which p5 is built _ no longer oro matcher and jikes _ add: quaqua, jna, registry stuff, .. ? @@ -156,6 +143,9 @@ _ 3) use createFont() (which you already are) _ this will convert all the shape data from the fonts for writing. _ it will be *extremely* slow, which is part of why it's not documented yet. _ but it will work with beginRaw(). +_ improve documentation of the pdf stuff +_ be clearer about the font setup stuff +_ fonts by default not working that well? DOC / Other @@ -259,15 +249,17 @@ PDE - Processing Development Environment PDE / Dialogs -_ proper dialog support -_ http://javagraphics.blogspot.com/2008/06/joptionpane-making-alternative.html +_ type cut off in dialog boxes on OS X retina machines +_ https://github.com/processing/processing/issues/2116 +_ add spaces to the end of the text? +_ dialog box icon is fuzzy on OS X retina machines +_ https://github.com/processing/processing/issues/2117 +_ solution might be our own dialog boxes _ two-tiered dialogs for everything - use big font/little font style throughout _ http://javagraphics.blogspot.com/2008/06/joptionpane-making-alternative.html _ option to suppress warning dialogs _ starting with the one about modifying the sketch name for spaces _ http://code.google.com/p/processing/issues/detail?id=7 -_ the first time someone hides a tab, put up a msg explaining what it does -_ "don't warn me about this anymore" _ add "don't warn me about this" for sketch renaming _ make sure renamed version doesn't exist already _ prompt user before nuking applet or application folders @@ -307,7 +299,8 @@ _ missing brackets, unmatched brackets _ examples added to the bug report _ http://code.google.com/p/processing/issues/detail?id=6 _ enums not supported properly -_ http://code.google.com/p/processing/issues/detail?id=1352 +_ https://github.com/processing/processing/issues/1390 +X http://code.google.com/p/processing/issues/detail?id=1352 low (features) _ combining char/int/etc casts in one statement causes preproc trouble @@ -365,8 +358,6 @@ _ active editor not being set null _ in Base.nextEditorLocation(), changed to "editors.size() == 0" _ instead of (activeEditor == null), but that's papering over a problem _ where the active editor is not being set null -_ the PDE uses 15% of CPU while just sitting idle -_ https://github.com/processing/processing/issues/1561 _ renaming RGB (.pde) to Rgb.java says "a file named RGB.pde already exists" _ improve update check message "a new release (1.0.1) is available" _ be more descriptive, use a second line in latest.txt @@ -376,10 +367,9 @@ _ deal with isManagingFocus() warning in the editor src _ strange NullPointerException problem prevents launch _ some kind of NPE in handleOpenInternal and friends _ appears to be a synchronization problem with the loading -_ when opening from double-click on the mac, doesn't replace untitled -_ or in general, issues between opening new window and another launching -_ probably need to synchronize the file open methods inside Base -_ (could in fact cause nastiness with editors[] access) +_ blank sketch opened even if another opened by double-click +_ add a 150 ms or more lag before opening the untitled window +_ https://github.com/processing/processing/issues/218 _ https://github.com/processing/processing/issues/1745 _ editors opening up at the same time on load? _ either synchronize the open (at a minimum) @@ -439,9 +429,6 @@ _ http://code.google.com/p/processing/issues/detail?id=746 PDE / Export -_ use launch4j for export and p5 app itself -_ perhaps even calling it through an ant task -_ windows exported exe problems (pcho) _ if the lib folder goes missing from export, give an error _ also any .jar files that are missing, give an error _ showing more debug messages (command line?) @@ -471,16 +458,17 @@ _ mark examples as untitled (rather than read-only) _ maybe even pull these directly from the zip file? _ load examples from zip files _ http://code.google.com/p/processing/issues/detail?id=143 +_ don't make examples read-only +_ just do them from psk files _ disallow add file to sketch, export, export application _ pretty much anything inside the sketch? _ but don't do this with untitled, cuz it kinda stinks _ this is too weird--just put examples into individual zip files _ mark example files as untitled _ though will that require the sketch to be saved before export? +_ https://github.com/processing/processing/issues/2459 _ examples window sketches should load in proper environment _ write build.xml file to automatically update the examples -_ don't make examples read-only -_ just do them from psk files _ sketch.isReadOnly returns false for examples coming from multiple modes _ http://code.google.com/p/processing/issues/detail?id=734 _ see how library installation goes, then possibly do same w/ examples @@ -489,6 +477,8 @@ _ see how library installation goes, then possibly do same w/ examples PDE / Libraries +_ Add a means to specify packages to import in library.properties +_ https://github.com/processing/processing/issues/2134 _ need to deal with classpath conflicts _ avoid garbage that people have installed on their machines _ antlr.jar in the classpath will cause trouble.. @@ -533,7 +523,8 @@ _ String osName = System.getProperty("os.name"); _ String osArch = System.getProperty("os.arch"); _ http://stackoverflow.com/questions/1611357/how-to-make-a-jar-file-that-include-dll-files _ add control for dependencies (i.e. svg needs xml), needed for export -_ http://code.google.com/p/processing/issues/detail?id=70 +X http://code.google.com/p/processing/issues/detail?id=70 +_ https://github.com/processing/processing/issues/109 _ need better platform designation setup for libs _ library installation should use the sketchbook folder, not the p5 folder _ actually enforce this, give users a warning about other libs @@ -552,6 +543,7 @@ _ https://github.com/processing/processing/issues/943 PDE / Manager +_ ability to cancel a download/install _ we shouldn't use .properties extension for modes, et al _ because a .properties file is iso8859-1 _ make note that .properties file *must* be utf-8 @@ -567,8 +559,6 @@ _ classpath conflicts.. _ getPackageList.. from Library... maybe others? _ really need to make sure that a weird core.jar isn't being imported _ coffeescript was doing this and breaking the pde -_ init() not called on tools until later -_ https://github.com/processing/processing/issues/1859 _ contrib library examples are not read-only _ another point for doing .zip files to prevent overwriting _ add BookContribution? @@ -615,12 +605,6 @@ _ clear up prefs so that multiple editors don't trash each other's prefs _ when are prefs saved? could instead save whenever changes are made _ and then if the file gets modified, it'll put up an error message _ also, this may be part of why other sketches aren't reloading properly -_ control text size in console -o why aren't prefs from theme.txt showing up in preferences.txt? hrm -o or rather, why can't they be overridden? -X because theme.txt data is a different animal / that's part of the point -_ should fonts at least be in prefs.txt? -_ http://code.google.com/p/processing/issues/detail?id=226 _ simple prefs implementation to set key/value pairs using a JTable _ separate prefs and sketch state info? _ this would mean prefs being rewritten far less @@ -635,10 +619,7 @@ _ preferences window has been hit with the ugly stick _ redo panel to use proper Box layout etc _ also needs to look good across all platforms _ http://code.google.com/p/processing/issues/detail?id=28 -_ make available the background colors for present mode, stop button color -_ isolate color chooser into a simpler/smaller class outside tools -_ then can also use from inside processing applications as well -_ http://code.google.com/p/processing/issues/detail?id=30 +_ https://github.com/processing/processing/issues/67 PDE / Runner @@ -667,10 +648,6 @@ _ because there's a dash in the name _ and instead only loads StemCell.pde _ show progress dialog during export and save _ hitting ESC on "create this, move file, continue" opened anyway -_ may need a progress bar for "save as" -_ or just the file copy function in general -_ since it may take a long time (i.e. 1000s of screen grabs) -_ http://code.google.com/p/processing/issues/detail?id=31 _ some type of sketch archive format for posting examples (.psk?) _ would be nice to open a sketch directly from a zip file _ http://code.google.com/p/processing/issues/detail?id=34 @@ -690,20 +667,12 @@ TOOLS / General _ create default tools folder (just like libraries) _ for tools, maybe don't run on event thread? (makes the gui hang) _ but instead, things that affect gui need to be called w/ invokeLater? -_ bad tool brings down the environment -_ http://code.google.com/p/processing/issues/detail?id=798 _ need a proper means to handle command keys for tools (?) _ http://code.google.com/p/processing/issues/detail?id=44 _ handle native code for tools menu (?) _ http://code.google.com/p/processing/issues/detail?id=109 -TOOLS / Movie Maker - -_ move Movie Maker out to its own separate tool package (with separate build) -_ http://code.google.com/p/processing/issues/detail?id=837 - - TOOLS / Ideas _ eclipse import/export @@ -723,10 +692,12 @@ _ update will update classes from shared in the current folder TOOLS / Auto Format +_ for() loop with nothing inside parens crashes Auto Format +_ https://github.com/processing/processing/issues/2141 _ extra indent found -_ http://code.google.com/p/processing/issues/detail?id=1003 +_ https://github.com/processing/processing/issues/1041 _ Switch block cases not indented -_ http://code.google.com/p/processing/issues/detail?id=1004 +_ https://github.com/processing/processing/issues/1042 _ do a better job of maintaining cursor _ only auto-format a particular section of code _ set the 'tabs' var based on how many spaces on previous line @@ -745,16 +716,11 @@ _ need to unpack InvocationTargetException in xxxxxxEvent calls _ http://processing.org/discourse/yabb_beta/YaBB.cgi?board=VideoCamera;action=display;num=1116850328#3 -LIBRARIES / Serial +LIBRARIES / Net -_ need 64-bit version of RXTX library -_ http://code.google.com/p/processing/issues/detail?id=1237 -_ serial still causing problems on OS X -_ http://code.google.com/p/processing/issues/detail?id=52 -_ serial ports missing from list (OS X) -_ http://code.google.com/p/processing/issues/detail?id=52 -_ Serial.write problem with Bluetooth SPP virtual serial port -_ http://code.google.com/p/processing/issues/detail?id=318 +_ modernize Client/Server code to use synchronized lists +_ do we let people use the public vars in Server and Client? +_ are they documented? @@ -775,6 +741,7 @@ _ https://github.com/processing/processing/issues/1840 _ need to have ecj.jar accessible to ant, then modify build.xml to use this: _ +_ http://help.eclipse.org/juno/index.jsp?topic=%2Forg.eclipse.jdt.doc.user%2Ftasks%2Ftask-ant_javac_adapter.htm _ line ending issues _ doesn't really help on Windows since we use Cygwin _ but it would be helpful for people not using it (ant/other LF issues) @@ -812,13 +779,8 @@ DIST / Windows _ processing-java output as UTF-8 makes Windows unhappy _ https://github.com/processing/processing/issues/1633 -_ updated launch4j 3.1 beta -_ http://sourceforge.net/projects/launch4j/files/launch4j-3/ _ does launching p5 from inside the .zip folder cause it to quit immediately? _ how can we provide an error message here? -_ exe instead of bat to make exported apps run in 64-bit -_ http://code.google.com/p/processing/issues/detail?id=885 -_ might not be necessary with new launch4j! _ how to handle double-clicked files on windows? _ big deal for psk and others _ this may already work with SingleInstance stuff @@ -828,23 +790,13 @@ _ http://code.google.com/p/processing/issues/detail?id=632 DIST / Mac OS X -_ package Java 7u40 version of the app -_ docs.oracle.com/javase/7/docs/technotes/guides/jweb/packagingAppsForMac.html -_ http://www.intransitione.com/blog/take-java-to-app-store/ -X retina support http://bugs.sun.com/view_bug.do?bug_id=8009754 -_ useful retina digging/findings for Oracle Java -_ http://bulenkov.com/2013/06/23/retina-support-in-oracle-jdk-1-7/ -_ 7u40 target release is "late August" -_ http://openjdk.java.net/projects/jdk7u/releases/7u40.html -_ Contents/Java/Classes folder is added to java.class.path -_ so the folder must exist otherwise the ECJ compiler will crash -_ once fixed, remove notes from JavaBuild.java -_ "Are you sure you want to quit?" when switching modes on Oracle JVM -_ default menu bar is still broken -_ http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=8007267 -_ blank sketch opened even if another opened by double-click -_ add a 150 ms or more lag before opening the untitled window (on os x) -_ https://github.com/processing/processing/issues/218 +_ possible better option for doing retina? +_ g.getFontRenderContext().getTransform().equals(AffineTransform.getScaleInstance(2.0, 2.0)) +_ appbundler improvements +_ don't re-copy JRE into work folder if already exists +_ implement a splash screen +_ remove default menu bar hack when 7u60 arrives +_ http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=8022667 _ OS X not opening a sketch at all on pde double-click? (though opening the app) _ LWJGL forum discussion _ http://lwjgl.org/forum/index.php/topic,4711.225.html @@ -953,9 +905,6 @@ _ write up code guidelines for project _ make proper Eclipse style prefs to reinforce _ write up guidelines for modes _ i.e. don't mess with Sketch menu, put it in the mode menu -_ add notes to build instructions re: building core with eclipse -_ update the build instructions page -_ http://code.google.com/p/processing/wiki/BuildInstructions _ p5 assets need to be licensed differently from the source